diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index cf303102ba3..02461adb15d 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -274,8 +274,8 @@ impl IOCompositor { self.change_page_title(pipeline_id, title); } - (Msg::ChangePageLoadData(frame_id, load_data), ShutdownState::NotShuttingDown) => { - self.change_page_load_data(frame_id, load_data); + (Msg::ChangePageUrl(frame_id, url), ShutdownState::NotShuttingDown) => { + self.change_page_url(frame_id, url); } (Msg::PaintMsgDiscarded, ShutdownState::NotShuttingDown) => { @@ -441,8 +441,8 @@ impl IOCompositor { } } - fn change_page_load_data(&mut self, _: FrameId, load_data: LoadData) { - self.window.set_page_load_data(load_data); + fn change_page_url(&mut self, _: FrameId, url: Url) { + self.window.set_page_url(url); } fn all_pipelines_in_idle_paint_state(&self) -> bool { diff --git a/components/compositing/compositor_task.rs b/components/compositing/compositor_task.rs index e3c1a5102d1..9458c7fa150 100644 --- a/components/compositing/compositor_task.rs +++ b/components/compositing/compositor_task.rs @@ -20,8 +20,9 @@ use layers::layers::LayerBufferSet; use pipeline::CompositionPipeline; use msg::compositor_msg::{Epoch, LayerId, LayerMetadata, ReadyState}; use msg::compositor_msg::{PaintListener, PaintState, ScriptListener, ScrollPolicy}; -use msg::constellation_msg::{ConstellationChan, LoadData, PipelineId}; +use msg::constellation_msg::{ConstellationChan, PipelineId}; use msg::constellation_msg::{Key, KeyState, KeyModifiers}; +use url::Url; use util::cursor::Cursor; use util::geometry::PagePx; use util::memory::MemoryProfilerChan; @@ -200,8 +201,8 @@ pub enum Msg { ChangePaintState(PipelineId, PaintState), /// Alerts the compositor that the current page has changed its title. ChangePageTitle(PipelineId, Option), - /// Alerts the compositor that the current page has changed its load data (including URL). - ChangePageLoadData(FrameId, LoadData), + /// Alerts the compositor that the current page has changed its URL. + ChangePageUrl(FrameId, Url), /// Alerts the compositor that a `PaintMsg` has been discarded. PaintMsgDiscarded, /// Replaces the current frame tree, typically called during main frame navigation. @@ -237,7 +238,7 @@ impl Debug for Msg { Msg::ChangeReadyState(..) => write!(f, "ChangeReadyState"), Msg::ChangePaintState(..) => write!(f, "ChangePaintState"), Msg::ChangePageTitle(..) => write!(f, "ChangePageTitle"), - Msg::ChangePageLoadData(..) => write!(f, "ChangePageLoadData"), + Msg::ChangePageUrl(..) => write!(f, "ChangePageUrl"), Msg::PaintMsgDiscarded(..) => write!(f, "PaintMsgDiscarded"), Msg::SetFrameTree(..) => write!(f, "SetFrameTree"), Msg::CreateRootLayerForPipeline(..) => write!(f, "CreateRootLayerForPipeline"), diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs index 2d370eca4e5..aff40db788d 100644 --- a/components/compositing/constellation.rs +++ b/components/compositing/constellation.rs @@ -334,9 +334,9 @@ impl NavigationContext { /// compositor of the new URLs. fn set_current(&mut self, new_frame: Rc, compositor_proxy: &mut CompositorProxy) { self.current = Some(new_frame.clone()); - compositor_proxy.send(CompositorMsg::ChangePageLoadData( + compositor_proxy.send(CompositorMsg::ChangePageUrl( new_frame.id, - new_frame.pipeline.borrow().load_data.clone())); + new_frame.pipeline.borrow().url.clone())); } } @@ -407,8 +407,7 @@ impl Constellation { self.time_profiler_chan.clone(), self.window_size, script_pipeline, - load_data.clone()); - pipe.load(); + load_data); Rc::new(pipe) } @@ -763,7 +762,7 @@ impl Constellation { source Id of ScriptLoadedURLInIFrameMsg does have an associated pipeline in constellation. This should be impossible.").clone(); - let source_url = source_pipeline.load_data.url.clone(); + let source_url = source_pipeline.url.clone(); let same_script = (source_url.host() == url.host() && source_url.port() == url.port()) && @@ -876,7 +875,7 @@ impl Constellation { }; for frame in destination_frame.iter() { - frame.pipeline.borrow().load(); + frame.pipeline.borrow().activate(); frame.pipeline.borrow().thaw(); } self.send_frame_tree_and_grant_paint_permission(destination_frame); diff --git a/components/compositing/headless.rs b/components/compositing/headless.rs index 0c38846d1d0..9957cb067cc 100644 --- a/components/compositing/headless.rs +++ b/components/compositing/headless.rs @@ -113,7 +113,7 @@ impl CompositorEventListener for NullCompositor { Msg::PaintMsgDiscarded(..) | Msg::ScrollTimeout(..) | Msg::ChangePageTitle(..) | - Msg::ChangePageLoadData(..) | + Msg::ChangePageUrl(..) | Msg::KeyEvent(..) | Msg::SetCursor(..) => {} Msg::PaintTaskExited(..) => {} diff --git a/components/compositing/pipeline.rs b/components/compositing/pipeline.rs index 27ee5eba150..c09f4ea06d5 100644 --- a/components/compositing/pipeline.rs +++ b/components/compositing/pipeline.rs @@ -16,6 +16,7 @@ use msg::constellation_msg::{LoadData, WindowSizeData, PipelineExitType}; use net::image_cache_task::ImageCacheTask; use net::resource_task::ResourceTask; use net::storage_task::StorageTask; +use url::Url; use util::time::TimeProfilerChan; use std::rc::Rc; use std::sync::mpsc::{Receiver, channel}; @@ -29,8 +30,8 @@ pub struct Pipeline { pub paint_chan: PaintChan, pub layout_shutdown_port: Receiver<()>, pub paint_shutdown_port: Receiver<()>, - /// Load data corresponding to the most recently-loaded page. - pub load_data: LoadData, + /// URL corresponding to the most recently-loaded page. + pub url: Url, /// The title of the most recently-loaded page. pub title: Option, } @@ -88,7 +89,8 @@ impl Pipeline { storage_task.clone(), image_cache_task.clone(), devtools_chan, - window_size); + window_size, + load_data.clone()); ScriptControlChan(script_chan) } Some(spipe) => { @@ -97,6 +99,7 @@ impl Pipeline { new_pipeline_id: id, subpage_id: parent.expect("script_pipeline != None but subpage_id == None").1, layout_chan: ScriptTaskFactory::clone_layout_channel(None::<&mut STF>, &layout_pair), + load_data: load_data.clone(), }; let ScriptControlChan(ref chan) = spipe.script_chan; @@ -135,7 +138,7 @@ impl Pipeline { paint_chan, layout_shutdown_port, paint_shutdown_port, - load_data) + load_data.url) } pub fn new(id: PipelineId, @@ -145,7 +148,7 @@ impl Pipeline { paint_chan: PaintChan, layout_shutdown_port: Receiver<()>, paint_shutdown_port: Receiver<()>, - load_data: LoadData) + url: Url) -> Pipeline { Pipeline { id: id, @@ -155,16 +158,14 @@ impl Pipeline { paint_chan: paint_chan, layout_shutdown_port: layout_shutdown_port, paint_shutdown_port: paint_shutdown_port, - load_data: load_data, + url: url, title: None, } } - pub fn load(&self) { + pub fn activate(&self) { let ScriptControlChan(ref chan) = self.script_chan; - chan.send(ConstellationControlMsg::Load(self.id, - self.parent, - self.load_data.clone())).unwrap(); + chan.send(ConstellationControlMsg::Activate(self.id)).unwrap(); } pub fn grant_paint_permission(&self) { diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs index 8c20aedd8e5..1990523dd2f 100644 --- a/components/compositing/windowing.rs +++ b/components/compositing/windowing.rs @@ -12,7 +12,8 @@ use geom::size::TypedSize2D; use layers::geometry::DevicePixel; use layers::platform::surface::NativeGraphicsMetadata; use msg::compositor_msg::{PaintState, ReadyState}; -use msg::constellation_msg::{Key, KeyState, KeyModifiers, LoadData}; +use msg::constellation_msg::{Key, KeyState, KeyModifiers}; +use url::Url; use util::cursor::Cursor; use util::geometry::ScreenPx; use std::fmt::{Error, Formatter, Debug}; @@ -105,7 +106,7 @@ pub trait WindowMethods { /// Sets the page title for the current page. fn set_page_title(&self, title: Option); /// Sets the load data for the current page. - fn set_page_load_data(&self, load_data: LoadData); + fn set_page_url(&self, url: Url); /// Called when the browser is done loading a frame. fn load_end(&self); diff --git a/components/gfx/display_list/mod.rs b/components/gfx/display_list/mod.rs index 9ba4d93e9e9..fde17c0aed5 100644 --- a/components/gfx/display_list/mod.rs +++ b/components/gfx/display_list/mod.rs @@ -42,8 +42,9 @@ use util::smallvec::{SmallVec, SmallVec8}; use std::fmt; use std::slice::Iter; use std::sync::Arc; +use style::computed_values::{border_style, cursor, filter, image_rendering, mix_blend_mode}; +use style::computed_values::{pointer_events}; use style::properties::ComputedValues; -use style::computed_values::{border_style, cursor, filter, mix_blend_mode, pointer_events}; // It seems cleaner to have layout code not mention Azure directly, so let's just reexport this for // layout to use. @@ -763,6 +764,10 @@ pub struct ImageDisplayItem { /// the bounds of this display item, then the image will be repeated in the appropriate /// direction to tile the entire bounds. pub stretch_size: Size2D, + + /// The algorithm we should use to stretch the image. See `image_rendering` in CSS-IMAGES-3 § + /// 5.3. + pub image_rendering: image_rendering::T, } /// Paints a gradient. @@ -937,7 +942,9 @@ impl DisplayItem { bounds.origin.y = bounds.origin.y + y_offset; bounds.size = image_item.stretch_size; - paint_context.draw_image(&bounds, image_item.image.clone()); + paint_context.draw_image(&bounds, + image_item.image.clone(), + image_item.image_rendering.clone()); x_offset = x_offset + image_item.stretch_size.width; } diff --git a/components/gfx/paint_context.rs b/components/gfx/paint_context.rs index 82184bf06de..442e13f7333 100644 --- a/components/gfx/paint_context.rs +++ b/components/gfx/paint_context.rs @@ -37,7 +37,7 @@ use std::mem; use std::num::Float; use std::ptr; use std::sync::Arc; -use style::computed_values::{border_style, filter, mix_blend_mode}; +use style::computed_values::{border_style, filter, image_rendering, mix_blend_mode}; use util::geometry::{self, Au, MAX_RECT, ZERO_RECT}; use util::opts; use util::range::Range; @@ -127,7 +127,10 @@ impl<'a> PaintContext<'a> { self.draw_target.pop_clip(); } - pub fn draw_image(&self, bounds: &Rect, image: Arc>) { + pub fn draw_image(&self, + bounds: &Rect, + image: Arc>, + image_rendering: image_rendering::T) { let size = Size2D(image.width as i32, image.height as i32); let (pixel_width, pixels, source_format) = match image.pixels { PixelsByColorType::RGBA8(ref pixels) => (4, pixels.as_slice(), SurfaceFormat::B8G8R8A8), @@ -146,7 +149,17 @@ impl<'a> PaintContext<'a> { let source_rect = Rect(Point2D(0.0, 0.0), Size2D(image.width as AzFloat, image.height as AzFloat)); let dest_rect = bounds.to_azure_rect(); - let draw_surface_options = DrawSurfaceOptions::new(Filter::Linear, true); + + // TODO(pcwalton): According to CSS-IMAGES-3 § 5.3, nearest-neighbor interpolation is a + // conforming implementation of `crisp-edges`, but it is not the best we could do. + // Something like Scale2x would be ideal. + let draw_surface_options = match image_rendering { + image_rendering::T::Auto => DrawSurfaceOptions::new(Filter::Linear, true), + image_rendering::T::CrispEdges | image_rendering::T::Pixelated => { + DrawSurfaceOptions::new(Filter::Point, true) + } + }; + let draw_options = DrawOptions::new(1.0, 0); draw_target_ref.draw_surface(azure_surface, dest_rect, diff --git a/components/layout/block.rs b/components/layout/block.rs index e26fb5f8b8e..fadba8af411 100644 --- a/components/layout/block.rs +++ b/components/layout/block.rs @@ -52,14 +52,16 @@ use geom::{Point2D, Rect, Size2D}; use gfx::display_list::{ClippingRegion, DisplayList}; use rustc_serialize::{Encoder, Encodable}; use msg::compositor_msg::LayerId; +use msg::constellation_msg::ConstellationChan; use servo_util::geometry::{Au, MAX_AU}; use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize}; use servo_util::opts; use std::cmp::{max, min}; use std::fmt; +use style::computed_values::{overflow_x, overflow_y, position, box_sizing, display, float}; use style::properties::ComputedValues; -use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto, LengthOrPercentageOrNone}; -use style::computed_values::{overflow, position, box_sizing, display, float}; +use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto}; +use style::values::computed::{LengthOrPercentageOrNone}; use std::sync::Arc; /// Information specific to floated blocks. @@ -1431,7 +1433,10 @@ impl BlockFlow { display::T::inline_block => { FormattingContextType::Other } - _ if style.get_box().overflow != overflow::T::visible => FormattingContextType::Block, + _ if style.get_box().overflow_x != overflow_x::T::visible || + style.get_box().overflow_y != overflow_y::T(overflow_x::T::visible) => { + FormattingContextType::Block + } _ => FormattingContextType::None, } } @@ -1915,6 +1920,10 @@ impl Flow for BlockFlow { CoordinateSystem::Parent) .translate(stacking_context_position)); } + + fn remove_compositor_layers(&self, constellation_chan: ConstellationChan) { + self.fragment.remove_compositor_layers(constellation_chan); + } } impl fmt::Debug for BlockFlow { diff --git a/components/layout/construct.rs b/components/layout/construct.rs index 7b6f4c63ebe..b263c438149 100644 --- a/components/layout/construct.rs +++ b/components/layout/construct.rs @@ -233,6 +233,20 @@ impl<'a> FlowConstructor<'a> { } } + #[inline] + fn set_flow_construction_result(&self, node: &ThreadSafeLayoutNode, result: ConstructionResult) { + match result { + ConstructionResult::None => { + let mut layout_data_ref = node.mutate_layout_data(); + let layout_data = layout_data_ref.as_mut().expect("no layout data"); + layout_data.remove_compositor_layers(self.layout_context.shared.constellation_chan.clone()); + } + _ => {} + } + + node.set_flow_construction_result(result); + } + /// Builds the `ImageFragmentInfo` for the given image. This is out of line to guide inlining. fn build_fragment_info_for_image(&mut self, node: &ThreadSafeLayoutNode, url: Option) -> SpecificFragmentInfo { @@ -381,7 +395,7 @@ impl<'a> FlowConstructor<'a> { // If kid_flow is TableCaptionFlow, kid_flow should be added under // TableWrapperFlow. if flow.is_table() && kid_flow.is_table_caption() { - kid.set_flow_construction_result(ConstructionResult::Flow(kid_flow, + self.set_flow_construction_result(&kid, ConstructionResult::Flow(kid_flow, Descendants::new())) } else if flow.need_anonymous_flow(&*kid_flow) { consecutive_siblings.push(kid_flow) @@ -562,7 +576,7 @@ impl<'a> FlowConstructor<'a> { // box, so don't construct them. if node.type_id() == Some(NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement))) { for kid in node.children() { - kid.set_flow_construction_result(ConstructionResult::None) + self.set_flow_construction_result(&kid, ConstructionResult::None) } } Some(Fragment::new_from_specific_info( @@ -692,7 +706,7 @@ impl<'a> FlowConstructor<'a> { fn build_fragments_for_replaced_inline_content(&mut self, node: &ThreadSafeLayoutNode) -> ConstructionResult { for kid in node.children() { - kid.set_flow_construction_result(ConstructionResult::None) + self.set_flow_construction_result(&kid, ConstructionResult::None) } // If this node is ignorable whitespace, bail out now. @@ -1046,7 +1060,7 @@ impl<'a> FlowConstructor<'a> { -> ConstructionResult { // CSS 2.1 § 17.2.1. Treat all child fragments of a `table-column` as `display: none`. for kid in node.children() { - kid.set_flow_construction_result(ConstructionResult::None) + self.set_flow_construction_result(&kid, ConstructionResult::None) } let specific = SpecificFragmentInfo::TableColumn(TableColumnFragmentInfo::new(node)); @@ -1174,15 +1188,15 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> { // results of children. (display::T::none, _, _) => { for child in node.children() { - child.set_flow_construction_result(ConstructionResult::None); + self.set_flow_construction_result(&child, ConstructionResult::None); } - node.set_flow_construction_result(ConstructionResult::None); + self.set_flow_construction_result(node, ConstructionResult::None); } // Table items contribute table flow construction results. (display::T::table, float_value, _) => { let construction_result = self.build_flow_for_table_wrapper(node, float_value); - node.set_flow_construction_result(construction_result) + self.set_flow_construction_result(node, construction_result) } // Absolutely positioned elements will have computed value of @@ -1193,13 +1207,14 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> { // below. (display::T::block, _, position::T::absolute) | (_, _, position::T::fixed) => { - node.set_flow_construction_result(self.build_flow_for_nonfloated_block(node)) + let construction_result = self.build_flow_for_nonfloated_block(node); + self.set_flow_construction_result(node, construction_result) } // List items contribute their own special flows. (display::T::list_item, float_value, _) => { - node.set_flow_construction_result(self.build_flow_for_list_item(node, - float_value)) + let construction_result = self.build_flow_for_list_item(node, float_value); + self.set_flow_construction_result(node, construction_result) } // Inline items that are absolutely-positioned contribute inline fragment construction @@ -1207,7 +1222,7 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> { (display::T::inline, _, position::T::absolute) => { let construction_result = self.build_fragment_for_absolutely_positioned_inline(node); - node.set_flow_construction_result(construction_result) + self.set_flow_construction_result(node, construction_result) } // Inline items contribute inline fragment construction results. @@ -1215,31 +1230,31 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> { // FIXME(pcwalton, #3307): This is not sufficient to handle floated generated content. (display::T::inline, float::T::none, _) => { let construction_result = self.build_fragments_for_inline(node); - node.set_flow_construction_result(construction_result) + self.set_flow_construction_result(node, construction_result) } // Inline-block items contribute inline fragment construction results. (display::T::inline_block, float::T::none, _) => { let construction_result = self.build_fragment_for_inline_block(node); - node.set_flow_construction_result(construction_result) + self.set_flow_construction_result(node, construction_result) } // Table items contribute table flow construction results. (display::T::table_caption, _, _) => { let construction_result = self.build_flow_for_table_caption(node); - node.set_flow_construction_result(construction_result) + self.set_flow_construction_result(node, construction_result) } // Table items contribute table flow construction results. (display::T::table_column_group, _, _) => { let construction_result = self.build_flow_for_table_colgroup(node); - node.set_flow_construction_result(construction_result) + self.set_flow_construction_result(node, construction_result) } // Table items contribute table flow construction results. (display::T::table_column, _, _) => { let construction_result = self.build_fragments_for_table_column(node); - node.set_flow_construction_result(construction_result) + self.set_flow_construction_result(node, construction_result) } // Table items contribute table flow construction results. @@ -1247,19 +1262,19 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> { (display::T::table_header_group, _, _) | (display::T::table_footer_group, _, _) => { let construction_result = self.build_flow_for_table_rowgroup(node); - node.set_flow_construction_result(construction_result) + self.set_flow_construction_result(node, construction_result) } // Table items contribute table flow construction results. (display::T::table_row, _, _) => { let construction_result = self.build_flow_for_table_row(node); - node.set_flow_construction_result(construction_result) + self.set_flow_construction_result(node, construction_result) } // Table items contribute table flow construction results. (display::T::table_cell, _, _) => { let construction_result = self.build_flow_for_table_cell(node); - node.set_flow_construction_result(construction_result) + self.set_flow_construction_result(node, construction_result) } // Block flows that are not floated contribute block flow construction results. @@ -1268,14 +1283,15 @@ impl<'a> PostorderNodeMutTraversal for FlowConstructor<'a> { // properties separately. (_, float::T::none, _) => { - node.set_flow_construction_result(self.build_flow_for_nonfloated_block(node)) + let construction_result = self.build_flow_for_nonfloated_block(node); + self.set_flow_construction_result(node, construction_result) } // Floated flows contribute float flow construction results. (_, float_value, _) => { let float_kind = FloatKind::from_property(float_value); - node.set_flow_construction_result( - self.build_flow_for_floated_block(node, float_kind)) + let construction_result = self.build_flow_for_floated_block(node, float_kind); + self.set_flow_construction_result(node, construction_result) } } @@ -1330,13 +1346,6 @@ impl<'ln> NodeUtils for ThreadSafeLayoutNode<'ln> { let mut layout_data_ref = self.mutate_layout_data(); let layout_data = layout_data_ref.as_mut().expect("no layout data"); - match result { - ConstructionResult::None => { - layout_data.clear(); - } - _ => {} - } - let dst = self.get_construction_result(layout_data); *dst = result; diff --git a/components/layout/data.rs b/components/layout/data.rs index d8a2c3f38b9..da24d662485 100644 --- a/components/layout/data.rs +++ b/components/layout/data.rs @@ -4,17 +4,17 @@ #![allow(unsafe_blocks)] -use construct::ConstructionResult; +use construct::{ConstructionItem, ConstructionResult}; use incremental::RestyleDamage; +use msg::constellation_msg::ConstellationChan; use parallel::DomParallelInfo; -use wrapper::{LayoutNode, TLayoutNode}; - use script::dom::node::SharedLayoutData; use script::layout_interface::LayoutChan; -use std::mem; use std::cell::{Ref, RefMut}; -use style::properties::ComputedValues; +use std::mem; use std::sync::Arc; +use style::properties::ComputedValues; +use wrapper::{LayoutNode, TLayoutNode}; /// Data that layout associates with a node. pub struct PrivateLayoutData { @@ -72,8 +72,26 @@ pub struct LayoutDataWrapper { } impl LayoutDataWrapper { - pub fn clear(&self) { - // TODO: Clear items related to this node, e.g. compositor layers + pub fn remove_compositor_layers(&self, constellation_chan: ConstellationChan) { + match self.data.flow_construction_result { + ConstructionResult::None => {} + ConstructionResult::Flow(ref flow_ref, _) => { + flow_ref.remove_compositor_layers(constellation_chan); + } + ConstructionResult::ConstructionItem(ref construction_item) => { + match construction_item { + &ConstructionItem::InlineFragments(ref inline_fragments) => { + for fragment in inline_fragments.fragments.iter() { + fragment.remove_compositor_layers(constellation_chan.clone()); + } + } + &ConstructionItem::Whitespace(..) => {} + &ConstructionItem::TableColumnFragment(ref fragment) => { + fragment.remove_compositor_layers(constellation_chan.clone()); + } + } + } + } } } diff --git a/components/layout/display_list_builder.rs b/components/layout/display_list_builder.rs index 10585964543..b4fb0001a80 100644 --- a/components/layout/display_list_builder.rs +++ b/components/layout/display_list_builder.rs @@ -19,7 +19,7 @@ use fragment::{CoordinateSystem, Fragment, IframeFragmentInfo, ImageFragmentInfo use fragment::{ScannedTextFragmentInfo, SpecificFragmentInfo}; use inline::InlineFlow; use list_item::ListItemFlow; -use model; +use model::{self, MaybeAuto}; use opaque_node::OpaqueNodeMethods; use geom::{Point2D, Rect, Size2D, SideOffsets2D}; @@ -32,8 +32,7 @@ use gfx::display_list::{GradientStop, ImageDisplayItem, LineDisplayItem}; use gfx::display_list::{OpaqueNode, SolidColorDisplayItem}; use gfx::display_list::{StackingContext, TextDisplayItem, TextOrientation}; use gfx::paint_task::{PaintLayer, THREAD_TINT_COLORS}; -use png; -use png::PixelsByColorType; +use png::{self, PixelsByColorType}; use msg::compositor_msg::ScrollPolicy; use msg::constellation_msg::Msg as ConstellationMsg; use msg::constellation_msg::ConstellationChan; @@ -42,15 +41,16 @@ use servo_util::cursor::Cursor; use servo_util::geometry::{self, Au, ZERO_POINT, to_px, to_frac_px}; use servo_util::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize}; use servo_util::opts; +use std::cmp; use std::default::Default; use std::iter::repeat; use std::num::Float; use style::values::specified::{AngleOrCorner, HorizontalDirection, VerticalDirection}; -use style::values::computed::{Image, LinearGradient, LengthOrPercentage}; +use style::values::computed::{Image, LinearGradient, LengthOrPercentage, LengthOrPercentageOrAuto}; use style::values::RGBA; use style::computed_values::filter::Filter; -use style::computed_values::{background_attachment, background_repeat, border_style, overflow}; -use style::computed_values::{position, visibility}; +use style::computed_values::{background_attachment, background_repeat, background_size}; +use style::computed_values::{border_style, image_rendering, overflow_x, position, visibility}; use style::properties::style_structs::Border; use style::properties::ComputedValues; use std::num::ToPrimitive; @@ -93,6 +93,14 @@ pub trait FragmentDisplayListBuilding { absolute_bounds: &Rect, clip: &ClippingRegion); + /// Computes the background size for an image with the given background area according to the + /// rules in CSS-BACKGROUNDS § 3.9. + fn compute_background_image_size(&self, + style: &ComputedValues, + bounds: &Rect, + image: &png::Image) + -> Size2D; + /// Adds the display items necessary to paint the background image of this fragment to the /// display list at the appropriate stacking level. fn build_display_list_for_background_image(&self, @@ -326,6 +334,59 @@ impl FragmentDisplayListBuilding for Fragment { } } + fn compute_background_image_size(&self, + style: &ComputedValues, + bounds: &Rect, + image: &png::Image) + -> Size2D { + // If `image_aspect_ratio` < `bounds_aspect_ratio`, the image is tall; otherwise, it is + // wide. + let image_aspect_ratio = (image.width as f64) / (image.height as f64); + let bounds_aspect_ratio = bounds.size.width.to_subpx() / bounds.size.height.to_subpx(); + let intrinsic_size = Size2D(Au::from_px(image.width as int), + Au::from_px(image.height as int)); + match (style.get_background().background_size.clone(), + image_aspect_ratio < bounds_aspect_ratio) { + (background_size::T::Contain, false) | (background_size::T::Cover, true) => { + Size2D(bounds.size.width, + Au::from_frac_px(bounds.size.width.to_subpx() / image_aspect_ratio)) + } + + (background_size::T::Contain, true) | (background_size::T::Cover, false) => { + Size2D(Au::from_frac_px(bounds.size.height.to_subpx() * image_aspect_ratio), + bounds.size.height) + } + + (background_size::T::Explicit(background_size::ExplicitSize { + width, + height: LengthOrPercentageOrAuto::Auto, + }), _) => { + let width = MaybeAuto::from_style(width, bounds.size.width) + .specified_or_default(intrinsic_size.width); + Size2D(width, Au::from_frac_px(width.to_subpx() / image_aspect_ratio)) + } + + (background_size::T::Explicit(background_size::ExplicitSize { + width: LengthOrPercentageOrAuto::Auto, + height + }), _) => { + let height = MaybeAuto::from_style(height, bounds.size.height) + .specified_or_default(intrinsic_size.height); + Size2D(Au::from_frac_px(height.to_subpx() * image_aspect_ratio), height) + } + + (background_size::T::Explicit(background_size::ExplicitSize { + width, + height + }), _) => { + Size2D(MaybeAuto::from_style(width, bounds.size.width) + .specified_or_default(intrinsic_size.width), + MaybeAuto::from_style(height, bounds.size.height) + .specified_or_default(intrinsic_size.height)) + } + } + } + fn build_display_list_for_background_image(&self, style: &ComputedValues, display_list: &mut DisplayList, @@ -349,16 +410,16 @@ impl FragmentDisplayListBuilding for Fragment { }; debug!("(building display list) building background image"); - let image_width = Au::from_px(image.width as int); - let image_height = Au::from_px(image.height as int); + // Use `background-size` to get the size. let mut bounds = *absolute_bounds; + let image_size = self.compute_background_image_size(style, &bounds, &**image); // Clip. // // TODO: Check the bounds to see if a clip item is actually required. let clip = clip.clone().intersect_rect(&bounds); - // Use background-attachment to get the initial virtual origin + // Use `background-attachment` to get the initial virtual origin let (virtual_origin_x, virtual_origin_y) = match background.background_attachment { background_attachment::T::scroll => { (absolute_bounds.origin.x, absolute_bounds.origin.y) @@ -368,11 +429,11 @@ impl FragmentDisplayListBuilding for Fragment { } }; - // Use background-position to get the offset + // Use `background-position` to get the offset. let horizontal_position = model::specified(background.background_position.horizontal, - bounds.size.width - image_width); + bounds.size.width - image_size.width); let vertical_position = model::specified(background.background_position.vertical, - bounds.size.height - image_height); + bounds.size.height - image_size.height); let abs_x = virtual_origin_x + horizontal_position; let abs_y = virtual_origin_y + vertical_position; @@ -382,26 +443,34 @@ impl FragmentDisplayListBuilding for Fragment { background_repeat::T::no_repeat => { bounds.origin.x = abs_x; bounds.origin.y = abs_y; - bounds.size.width = image_width; - bounds.size.height = image_height; + bounds.size.width = image_size.width; + bounds.size.height = image_size.height; } background_repeat::T::repeat_x => { bounds.origin.y = abs_y; - bounds.size.height = image_height; - ImageFragmentInfo::tile_image(&mut bounds.origin.x, &mut bounds.size.width, - abs_x, image.width); + bounds.size.height = image_size.height; + ImageFragmentInfo::tile_image(&mut bounds.origin.x, + &mut bounds.size.width, + abs_x, + image_size.width.to_nearest_px() as u32); } background_repeat::T::repeat_y => { bounds.origin.x = abs_x; - bounds.size.width = image_width; - ImageFragmentInfo::tile_image(&mut bounds.origin.y, &mut bounds.size.height, - abs_y, image.height); + bounds.size.width = image_size.width; + ImageFragmentInfo::tile_image(&mut bounds.origin.y, + &mut bounds.size.height, + abs_y, + image_size.height.to_nearest_px() as u32); } background_repeat::T::repeat => { - ImageFragmentInfo::tile_image(&mut bounds.origin.x, &mut bounds.size.width, - abs_x, image.width); - ImageFragmentInfo::tile_image(&mut bounds.origin.y, &mut bounds.size.height, - abs_y, image.height); + ImageFragmentInfo::tile_image(&mut bounds.origin.x, + &mut bounds.size.width, + abs_x, + image_size.width.to_nearest_px() as u32); + ImageFragmentInfo::tile_image(&mut bounds.origin.y, + &mut bounds.size.height, + abs_y, + image_size.height.to_nearest_px() as u32); } }; @@ -413,8 +482,8 @@ impl FragmentDisplayListBuilding for Fragment { Cursor::DefaultCursor), clip), image: image.clone(), - stretch_size: Size2D(Au::from_px(image.width as int), - Au::from_px(image.height as int)), + stretch_size: Size2D(image_size.width, image_size.height), + image_rendering: style.get_effects().image_rendering.clone(), }), level); } @@ -912,6 +981,7 @@ impl FragmentDisplayListBuilding for Fragment { (*clip).clone()), image: image.clone(), stretch_size: stacking_relative_content_box.size, + image_rendering: self.style.get_effects().image_rendering.clone(), })); } else { // No image data at all? Do nothing. @@ -947,6 +1017,7 @@ impl FragmentDisplayListBuilding for Fragment { pixels: PixelsByColorType::RGBA8(canvas_data), }), stretch_size: stacking_relative_content_box.size, + image_rendering: image_rendering::T::Auto, }; display_list.content.push_back(DisplayItem::ImageClass(canvas_display_item)); @@ -991,17 +1062,37 @@ impl FragmentDisplayListBuilding for Fragment { } // Account for style-specified `clip`. - let current_clip = self.calculate_style_specified_clip(current_clip, - stacking_relative_border_box); + let mut current_clip = self.calculate_style_specified_clip(current_clip, + stacking_relative_border_box); - // Only clip if `overflow` tells us to. - match self.style.get_box().overflow { - overflow::T::hidden | overflow::T::auto | overflow::T::scroll => { - // Create a new clip rect. - current_clip.intersect_rect(stacking_relative_border_box) + // Clip according to the values of `overflow-x` and `overflow-y`. + // + // TODO(pcwalton): Support scrolling. + // FIXME(pcwalton): This may be more complex than it needs to be, since it seems to be + // impossible with the computed value rules as they are to have `overflow-x: visible` with + // `overflow-y: ` or vice versa! + match self.style.get_box().overflow_x { + overflow_x::T::hidden | overflow_x::T::auto | overflow_x::T::scroll => { + let mut bounds = current_clip.bounding_rect(); + let max_x = cmp::min(bounds.max_x(), stacking_relative_border_box.max_x()); + bounds.origin.x = cmp::max(bounds.origin.x, stacking_relative_border_box.origin.x); + bounds.size.width = max_x - bounds.origin.x; + current_clip = current_clip.intersect_rect(&bounds) } - _ => current_clip, + _ => {} } + match self.style.get_box().overflow_y.0 { + overflow_x::T::hidden | overflow_x::T::auto | overflow_x::T::scroll => { + let mut bounds = current_clip.bounding_rect(); + let max_y = cmp::min(bounds.max_y(), stacking_relative_border_box.max_y()); + bounds.origin.y = cmp::max(bounds.origin.y, stacking_relative_border_box.origin.y); + bounds.size.height = max_y - bounds.origin.y; + current_clip = current_clip.intersect_rect(&bounds) + } + _ => {} + } + + current_clip } fn build_display_list_for_text_fragment(&self, diff --git a/components/layout/flow.rs b/components/layout/flow.rs index 19f1df86c3a..ec618e03847 100644 --- a/components/layout/flow.rs +++ b/components/layout/flow.rs @@ -49,6 +49,7 @@ use wrapper::ThreadSafeLayoutNode; use geom::{Point2D, Rect, Size2D}; use gfx::display_list::ClippingRegion; use rustc_serialize::{Encoder, Encodable}; +use msg::constellation_msg::ConstellationChan; use msg::compositor_msg::LayerId; use servo_util::geometry::{Au, ZERO_RECT}; use servo_util::logical_geometry::{LogicalRect, LogicalSize, WritingMode}; @@ -312,6 +313,9 @@ pub trait Flow: fmt::Debug + Sync { /// Attempts to perform incremental fixup of this flow by replacing its fragment's style with /// the new style. This can only succeed if the flow has exactly one fragment. fn repair_style(&mut self, new_style: &Arc); + + /// Remove any compositor layers associated with this flow + fn remove_compositor_layers(&self, _: ConstellationChan) {} } // Base access diff --git a/components/layout/fragment.rs b/components/layout/fragment.rs index ed6e3920f88..205e7324d12 100644 --- a/components/layout/fragment.rs +++ b/components/layout/fragment.rs @@ -30,7 +30,7 @@ use gfx::text::glyph::CharIndex; use gfx::text::text_run::{TextRun, TextRunSlice}; use script_traits::UntrustedNodeAddress; use rustc_serialize::{Encodable, Encoder}; -use msg::constellation_msg::{PipelineId, SubpageId}; +use msg::constellation_msg::{ConstellationChan, Msg, PipelineId, SubpageId}; use net::image::holder::ImageHolder; use net::local_image_cache::LocalImageCache; use servo_util::geometry::{self, Au, ZERO_POINT}; @@ -982,10 +982,11 @@ impl Fragment { fn style_specified_intrinsic_inline_size(&self) -> IntrinsicISizesContribution { let flags = self.quantities_included_in_intrinsic_inline_size(); let style = self.style(); - let specified = if flags.contains(INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED) { - MaybeAuto::from_style(style.content_inline_size(), Au(0)).specified_or_zero() + let (min_inline_size, specified) = if flags.contains(INTRINSIC_INLINE_SIZE_INCLUDES_SPECIFIED) { + (model::specified(style.min_inline_size(), Au(0)), + MaybeAuto::from_style(style.content_inline_size(), Au(0)).specified_or_zero()) } else { - Au(0) + (Au(0), Au(0)) }; // FIXME(#2261, pcwalton): This won't work well for inlines: is this OK? @@ -993,7 +994,7 @@ impl Fragment { IntrinsicISizesContribution { content_intrinsic_sizes: IntrinsicISizes { - minimum_inline_size: specified, + minimum_inline_size: min_inline_size, preferred_inline_size: specified, }, surrounding_size: surrounding_inline_size, @@ -1721,7 +1722,8 @@ impl Fragment { SpecificFragmentInfo::InlineBlock(ref mut info) => { let block_flow = info.flow_ref.as_block(); self.border_box.size.inline = - block_flow.base.intrinsic_inline_sizes.preferred_inline_size; + max(block_flow.base.intrinsic_inline_sizes.minimum_inline_size, + block_flow.base.intrinsic_inline_sizes.preferred_inline_size); block_flow.base.block_container_inline_size = self.border_box.size.inline; } SpecificFragmentInfo::ScannedText(ref info) => { @@ -2058,6 +2060,23 @@ impl Fragment { // box too. overflow } + + /// Remove any compositor layers associated with this fragment - it is being + /// removed from the tree or had its display property set to none. + /// TODO(gw): This just hides the compositor layer for now. In the future + /// it probably makes sense to provide a hint to the compositor whether + /// the layers should be destroyed to free memory. + pub fn remove_compositor_layers(&self, constellation_chan: ConstellationChan) { + match self.specific { + SpecificFragmentInfo::Iframe(ref iframe_info) => { + let ConstellationChan(ref chan) = constellation_chan; + chan.send(Msg::FrameRect(iframe_info.pipeline_id, + iframe_info.subpage_id, + Rect::zero())).unwrap(); + } + _ => {} + } + } } impl fmt::Debug for Fragment { diff --git a/components/layout/inline.rs b/components/layout/inline.rs index a5ca5038c30..0a4b575c3c4 100644 --- a/components/layout/inline.rs +++ b/components/layout/inline.rs @@ -34,7 +34,7 @@ use std::mem; use std::num::ToPrimitive; use std::ops::{Add, Sub, Mul, Div, Rem, Neg, Shl, Shr, Not, BitOr, BitAnd, BitXor}; use std::u16; -use style::computed_values::{overflow, text_align, text_justify, text_overflow, vertical_align}; +use style::computed_values::{overflow_x, text_align, text_justify, text_overflow, vertical_align}; use style::computed_values::{white_space}; use style::properties::ComputedValues; use std::sync::Arc; @@ -653,8 +653,8 @@ impl LineBreaker { let available_inline_size = self.pending_line.green_zone.inline - self.pending_line.bounds.size.inline - indentation; match (fragment.style().get_inheritedtext().text_overflow, - fragment.style().get_box().overflow) { - (text_overflow::T::clip, _) | (_, overflow::T::visible) => {} + fragment.style().get_box().overflow_x) { + (text_overflow::T::clip, _) | (_, overflow_x::T::visible) => {} (text_overflow::T::ellipsis, _) => { need_ellipsis = fragment.border_box.size.inline > available_inline_size; } diff --git a/components/layout/layout_task.rs b/components/layout/layout_task.rs index 046d79b1805..e55805486b3 100644 --- a/components/layout/layout_task.rs +++ b/components/layout/layout_task.rs @@ -126,6 +126,9 @@ pub struct LayoutTask { //// The channel to send messages to ourself. pub chan: LayoutChan, + /// The channel on which messages can be sent to the constellation. + pub constellation_chan: ConstellationChan, + /// The channel on which messages can be sent to the script task. pub script_chan: ScriptControlChan, @@ -274,6 +277,7 @@ impl LayoutTask { pipeline_port: pipeline_port, chan: chan, script_chan: script_chan, + constellation_chan: constellation_chan.clone(), paint_chan: paint_chan, time_profiler_chan: time_profiler_chan, resource_task: resource_task, @@ -414,7 +418,7 @@ impl LayoutTask { }, Msg::ReapLayoutData(dead_layout_data) => { unsafe { - LayoutTask::handle_reap_layout_data(dead_layout_data) + self.handle_reap_layout_data(dead_layout_data) } }, Msg::PrepareToExit(response_chan) => { @@ -443,7 +447,7 @@ impl LayoutTask { match self.port.recv().unwrap() { Msg::ReapLayoutData(dead_layout_data) => { unsafe { - LayoutTask::handle_reap_layout_data(dead_layout_data) + self.handle_reap_layout_data(dead_layout_data) } } Msg::ExitNow(exit_type) => { @@ -940,9 +944,9 @@ impl LayoutTask { /// Handles a message to destroy layout data. Layout data must be destroyed on *this* task /// because the struct type is transmuted to a different type on the script side. - unsafe fn handle_reap_layout_data(layout_data: LayoutData) { + unsafe fn handle_reap_layout_data(&self, layout_data: LayoutData) { let layout_data_wrapper: LayoutDataWrapper = mem::transmute(layout_data); - layout_data_wrapper.clear(); + layout_data_wrapper.remove_compositor_layers(self.constellation_chan.clone()); } /// Returns profiling information which is passed to the time profiler. diff --git a/components/script/devtools.rs b/components/script/devtools.rs index 5333b9a4cee..051002c7478 100644 --- a/components/script/devtools.rs +++ b/components/script/devtools.rs @@ -11,7 +11,7 @@ use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::Bindings::DOMRectBinding::{DOMRectMethods}; use dom::bindings::codegen::Bindings::ElementBinding::{ElementMethods}; use dom::node::{Node, NodeHelpers}; -use dom::window::{ScriptHelpers}; +use dom::window::{WindowHelpers, ScriptHelpers}; use dom::element::Element; use dom::document::DocumentHelpers; use page::Page; @@ -24,8 +24,7 @@ use std::rc::Rc; pub fn handle_evaluate_js(page: &Rc, pipeline: PipelineId, eval: String, reply: Sender){ let page = get_page(&*page, pipeline); - let frame = page.frame(); - let window = frame.as_ref().unwrap().window.root(); + let window = page.window().root(); let cx = window.r().get_cx(); let rval = window.r().evaluate_js_on_global_with_result(eval.as_slice()); @@ -49,8 +48,7 @@ pub fn handle_evaluate_js(page: &Rc, pipeline: PipelineId, eval: String, r pub fn handle_get_root_node(page: &Rc, pipeline: PipelineId, reply: Sender) { let page = get_page(&*page, pipeline); - let frame = page.frame(); - let document = frame.as_ref().unwrap().document.root(); + let document = page.document().root(); let node: JSRef = NodeCast::from_ref(document.r()); reply.send(node.summarize()).unwrap(); @@ -58,8 +56,7 @@ pub fn handle_get_root_node(page: &Rc, pipeline: PipelineId, reply: Sender pub fn handle_get_document_element(page: &Rc, pipeline: PipelineId, reply: Sender) { let page = get_page(&*page, pipeline); - let frame = page.frame(); - let document = frame.as_ref().unwrap().document.root(); + let document = page.document().root(); let document_element = document.r().GetDocumentElement().root().unwrap(); let node: JSRef = NodeCast::from_ref(document_element.r()); @@ -68,8 +65,7 @@ pub fn handle_get_document_element(page: &Rc, pipeline: PipelineId, reply: fn find_node_by_unique_id(page: &Rc, pipeline: PipelineId, node_id: String) -> Temporary { let page = get_page(&*page, pipeline); - let frame = page.frame(); - let document = frame.as_ref().unwrap().document.root(); + let document = page.document().root(); let node: JSRef = NodeCast::from_ref(document.r()); for candidate in node.traverse_preorder() { @@ -110,5 +106,6 @@ pub fn handle_modify_attribute(page: &Rc, pipeline: PipelineId, node_id: S pub fn handle_wants_live_notifications(page: &Rc, pipeline_id: PipelineId, send_notifications: bool) { let page = get_page(&*page, pipeline_id); - page.devtools_wants_updates.set(send_notifications); + let window = page.window().root(); + window.r().set_devtools_wants_updates(send_notifications); } diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 203bf60f340..9274c6cafb7 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -4643,7 +4643,6 @@ class CGBindingRoot(CGThing): 'dom::bindings::proxyhandler::{fill_property_descriptor, get_expando_object}', 'dom::bindings::proxyhandler::{get_property_descriptor}', 'dom::bindings::str::ByteString', - 'page::JSPageInfo', 'libc', 'util::str::DOMString', 'std::borrow::ToOwned', diff --git a/components/script/dom/bindings/global.rs b/components/script/dom/bindings/global.rs index dc42d0da6e0..3d17778c6b1 100644 --- a/components/script/dom/bindings/global.rs +++ b/components/script/dom/bindings/global.rs @@ -11,7 +11,7 @@ use dom::bindings::conversions::FromJSValConvertible; use dom::bindings::js::{JS, JSRef, Root, Unrooted}; use dom::bindings::utils::{Reflectable, Reflector}; use dom::workerglobalscope::{WorkerGlobalScope, WorkerGlobalScopeHelpers}; -use dom::window; +use dom::window::{self, WindowHelpers}; use script_task::ScriptChan; use net::resource_task::ResourceTask; @@ -84,7 +84,7 @@ impl<'a> GlobalRef<'a> { /// Get the `ResourceTask` for this global scope. pub fn resource_task(&self) -> ResourceTask { match *self { - GlobalRef::Window(ref window) => window.page().resource_task.clone(), + GlobalRef::Window(ref window) => window.resource_task().clone(), GlobalRef::Worker(ref worker) => worker.resource_task().clone(), } } diff --git a/components/script/dom/bindings/js.rs b/components/script/dom/bindings/js.rs index b370b3a073f..7ada9f45d14 100644 --- a/components/script/dom/bindings/js.rs +++ b/components/script/dom/bindings/js.rs @@ -727,7 +727,7 @@ impl<'a, T> Clone for JSRef<'a, T> { } } -impl<'a, T> PartialEq for JSRef<'a, T> { +impl<'a, 'b, T> PartialEq> for JSRef<'a, T> { fn eq(&self, other: &JSRef) -> bool { self.ptr == other.ptr } diff --git a/components/script/dom/browsercontext.rs b/components/script/dom/browsercontext.rs index e21533bbfe0..a3af0a8b02a 100644 --- a/components/script/dom/browsercontext.rs +++ b/components/script/dom/browsercontext.rs @@ -71,14 +71,12 @@ impl BrowserContext { fn create_window_proxy(&mut self) { let win = self.active_window().root(); let win = win.r(); - let page = win.page(); - let js_info = page.js_info(); - let WindowProxyHandler(handler) = js_info.as_ref().unwrap().dom_static.windowproxy_handler; + let WindowProxyHandler(handler) = win.windowproxy_handler(); assert!(!handler.is_null()); let parent = win.reflector().get_jsobject(); - let cx = js_info.as_ref().unwrap().js_context.ptr; + let cx = win.get_cx(); let wrapper = with_compartment(cx, parent, || unsafe { WrapperNew(cx, parent, handler) }); diff --git a/components/script/dom/canvasrenderingcontext2d.rs b/components/script/dom/canvasrenderingcontext2d.rs index d0ae869d847..8367e27d05e 100644 --- a/components/script/dom/canvasrenderingcontext2d.rs +++ b/components/script/dom/canvasrenderingcontext2d.rs @@ -151,7 +151,7 @@ impl<'a> CanvasRenderingContext2DMethods for JSRef<'a, CanvasRenderingContext2D> } fn LineTo(self, x: f64, y: f64) { - self.renderer.send(CanvasMsg::LineTo(Point2D(x as f32, y as f32))); + self.renderer.send(CanvasMsg::LineTo(Point2D(x as f32, y as f32))).unwrap(); } fn BezierCurveTo(self, cp1x: f64, cp1y: f64, cp2x: f64, cp2y: f64, x: f64, y: f64) { diff --git a/components/script/dom/console.rs b/components/script/dom/console.rs index 4dec1c3bd45..6308556ef2e 100644 --- a/components/script/dom/console.rs +++ b/components/script/dom/console.rs @@ -7,6 +7,7 @@ use dom::bindings::codegen::Bindings::ConsoleBinding::ConsoleMethods; use dom::bindings::global::{GlobalRef, GlobalField}; use dom::bindings::js::{JSRef, Temporary}; use dom::bindings::utils::{Reflector, reflect_dom_object}; +use dom::window::WindowHelpers; use devtools_traits::{DevtoolsControlMsg, ConsoleMessage}; use util::str::DOMString; @@ -66,8 +67,8 @@ fn propagate_console_msg(console: &JSRef, console_message: ConsoleMessa let global = console.global.root(); match global.r() { GlobalRef::Window(window_ref) => { - let pipelineId = window_ref.page().id; - console.global.root().r().as_window().page().devtools_chan.as_ref().map(|chan| { + let pipelineId = window_ref.pipeline(); + console.global.root().r().as_window().devtools_chan().as_ref().map(|chan| { chan.send(DevtoolsControlMsg::SendConsoleMessage( pipelineId, console_message.clone())).unwrap(); }); diff --git a/components/script/dom/cssstyledeclaration.rs b/components/script/dom/cssstyledeclaration.rs index 2337e6c061a..b41adcb8b06 100644 --- a/components/script/dom/cssstyledeclaration.rs +++ b/components/script/dom/cssstyledeclaration.rs @@ -14,7 +14,7 @@ use dom::document::DocumentHelpers; use dom::element::{Element, ElementHelpers, StylePriority}; use dom::htmlelement::HTMLElement; use dom::node::{window_from_node, document_from_node, NodeDamage, Node}; -use dom::window::Window; +use dom::window::{Window, WindowHelpers}; use util::str::DOMString; use string_cache::Atom; use style::properties::{is_supported_property, longhands_from_shorthand, parse_style_attribute}; @@ -221,10 +221,8 @@ impl<'a> CSSStyleDeclarationMethods for JSRef<'a, CSSStyleDeclaration> { let owner = self.owner.root(); let window = window_from_node(owner.r()).root(); - let window = window.r(); - let page = window.page(); let decl_block = parse_style_attribute(synthesized_declaration.as_slice(), - &page.get_url()); + &window.r().get_url()); // Step 7 if decl_block.normal.len() == 0 { @@ -270,10 +268,8 @@ impl<'a> CSSStyleDeclarationMethods for JSRef<'a, CSSStyleDeclaration> { let owner = self.owner.root(); let window = window_from_node(owner.r()).root(); - let window = window.r(); - let page = window.page(); let decl_block = parse_style_attribute(property.as_slice(), - &page.get_url()); + &window.r().get_url()); let element: JSRef = ElementCast::from_ref(owner.r()); // Step 5 diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 6e5413ebdc8..ffa22a48182 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -11,7 +11,6 @@ use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use dom::bindings::codegen::Bindings::NodeFilterBinding::NodeFilter; -use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::codegen::InheritTypes::{DocumentDerived, EventCast, HTMLElementCast}; use dom::bindings::codegen::InheritTypes::{HTMLHeadElementCast, TextCast, ElementCast}; use dom::bindings::codegen::InheritTypes::{DocumentTypeCast, HTMLHtmlElementCast, NodeCast}; @@ -59,12 +58,14 @@ use dom::treewalker::TreeWalker; use dom::uievent::UIEvent; use dom::window::{Window, WindowHelpers}; +use layout_interface::{HitTestResponse, MouseOverResponse}; use msg::compositor_msg::ScriptListener; use msg::constellation_msg::{Key, KeyState, KeyModifiers}; use msg::constellation_msg::{SUPER, ALT, SHIFT, CONTROL}; use net::resource_task::ControlMsg::{SetCookiesForUrl, GetCookiesForUrl}; use net::cookie_storage::CookieSource::NonHTTP; use script_task::Runnable; +use script_traits::UntrustedNodeAddress; use util::namespace; use util::str::{DOMString, split_html_space_chars}; use layout_interface::{ReflowGoal, ReflowQueryType}; @@ -201,6 +202,8 @@ pub trait DocumentHelpers<'a> { fn register_named_element(self, element: JSRef, id: Atom); fn load_anchor_href(self, href: DOMString); fn find_fragment_node(self, fragid: DOMString) -> Option>; + fn hit_test(self, point: &Point2D) -> Option; + fn get_nodes_under_mouse(self, point: &Point2D) -> Vec; fn set_ready_state(self, state: DocumentReadyState); fn get_focused_element(self) -> Option>; fn begin_focus_transaction(self); @@ -241,7 +244,7 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { let browser_context = browser_context.as_ref().unwrap(); let active_document = browser_context.active_document().root(); - if self.clone() != active_document.r() { + if self != active_document.r() { return false; } // FIXME: It should also check whether the browser context is top-level or not @@ -264,7 +267,7 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { Quirks => { let window = self.window.root(); let window = window.r(); - let LayoutChan(ref layout_chan) = window.page().layout_chan; + let LayoutChan(ref layout_chan) = window.layout_chan(); layout_chan.send(Msg::SetQuirksMode).unwrap(); } NoQuirks | LimitedQuirks => {} @@ -377,6 +380,44 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { }) } + fn hit_test(self, point: &Point2D) -> Option { + let root = self.GetDocumentElement().root(); + let root = match root.r() { + Some(root) => root, + None => return None, + }; + let root = NodeCast::from_ref(root); + let win = self.window.root(); + let address = match win.r().layout().hit_test(root.to_trusted_node_address(), *point) { + Ok(HitTestResponse(node_address)) => { + Some(node_address) + } + Err(()) => { + debug!("layout query error"); + None + } + }; + address + } + + fn get_nodes_under_mouse(self, point: &Point2D) -> Vec { + let root = self.GetDocumentElement().root(); + let root = match root.r() { + Some(root) => root, + None => return vec!(), + }; + let root: JSRef = NodeCast::from_ref(root); + let win = self.window.root(); + match win.r().layout().mouse_over(root.to_trusted_node_address(), *point) { + Ok(MouseOverResponse(node_address)) => { + node_address + } + Err(()) => { + vec!() + } + } + } + // https://html.spec.whatwg.org/multipage/dom.html#current-document-readiness fn set_ready_state(self, state: DocumentReadyState) { self.ready_state.set(state); @@ -416,7 +457,7 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { /// Sends this document's title to the compositor. fn send_title_to_compositor(self) { let window = self.window().root(); - window.r().page().send_title_to_compositor(); + window.r().compositor().set_title(window.r().pipeline(), Some(self.Title())); } fn dirty_all_nodes(self) { @@ -428,10 +469,7 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { fn handle_click_event(self, js_runtime: *mut JSRuntime, _button: uint, point: Point2D) { debug!("ClickEvent: clicked at {:?}", point); - let window = self.window.root(); - let window = window.r(); - let page = window.page(); - let node = match page.hit_test(&point) { + let node = match self.hit_test(&point) { Some(node_address) => { debug!("node address is {:?}", node_address.0); node::from_untrusted_node_address(js_runtime, node_address) @@ -460,48 +498,40 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { return; } - match *page.frame() { - Some(ref frame) => { - let window = frame.window.root(); - let doc = window.r().Document().root(); - doc.r().begin_focus_transaction(); + self.begin_focus_transaction(); - // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#event-type-click - let x = point.x as i32; - let y = point.y as i32; - let event = MouseEvent::new(window.r(), - "click".to_owned(), - true, - true, - Some(window.r()), - 0i32, - x, y, x, y, - false, false, false, false, - 0i16, - None).root(); - let event: JSRef = EventCast::from_ref(event.r()); + let window = self.window.root(); - // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#trusted-events - event.set_trusted(true); - // https://html.spec.whatwg.org/multipage/interaction.html#run-authentic-click-activation-steps - el.authentic_click_activation(event); + // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#event-type-click + let x = point.x as i32; + let y = point.y as i32; + let event = MouseEvent::new(window.r(), + "click".to_owned(), + true, + true, + Some(window.r()), + 0i32, + x, y, x, y, + false, false, false, false, + 0i16, + None).root(); + let event: JSRef = EventCast::from_ref(event.r()); - doc.r().commit_focus_transaction(); - window.r().flush_layout(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); - } - None => {} - } + // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#trusted-events + event.set_trusted(true); + // https://html.spec.whatwg.org/multipage/interaction.html#run-authentic-click-activation-steps + el.authentic_click_activation(event); + + self.commit_focus_transaction(); + window.r().reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); } /// Return need force reflow or not fn handle_mouse_move_event(self, js_runtime: *mut JSRuntime, point: Point2D, prev_mouse_over_targets: &mut Vec>) -> bool { - let window = self.window.root(); - let window = window.r(); - let page = window.page(); let mut needs_reflow = false; // Build a list of elements that are currently under the mouse. - let mouse_over_addresses = page.get_nodes_under_mouse(&point); + let mouse_over_addresses = self.get_nodes_under_mouse(&point); let mouse_over_targets: Vec> = mouse_over_addresses.iter() .filter_map(|node_address| { let node = node::from_untrusted_node_address(js_runtime, *node_address); @@ -534,27 +564,24 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { let top_most_node = node::from_untrusted_node_address(js_runtime, mouse_over_addresses[0]).root(); - if let Some(ref frame) = *page.frame() { - let window = frame.window.root(); + let x = point.x.to_i32().unwrap_or(0); + let y = point.y.to_i32().unwrap_or(0); - let x = point.x.to_i32().unwrap_or(0); - let y = point.y.to_i32().unwrap_or(0); + let window = self.window.root(); + let mouse_event = MouseEvent::new(window.r(), + "mousemove".to_owned(), + true, + true, + Some(window.r()), + 0i32, + x, y, x, y, + false, false, false, false, + 0i16, + None).root(); - let mouse_event = MouseEvent::new(window.r(), - "mousemove".to_owned(), - true, - true, - Some(window.r()), - 0i32, - x, y, x, y, - false, false, false, false, - 0i16, - None).root(); - - let event: JSRef = EventCast::from_ref(mouse_event.r()); - let target: JSRef = EventTargetCast::from_ref(top_most_node.r()); - event.fire(target); - } + let event: JSRef = EventCast::from_ref(mouse_event.r()); + let target: JSRef = EventTargetCast::from_ref(top_most_node.r()); + event.fire(target); } // Store the current mouse over targets for next frame @@ -637,7 +664,7 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { _ => () } - window.r().flush_layout(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); + window.r().reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); } fn set_current_script(self, script: Option>) { @@ -1259,7 +1286,7 @@ impl<'a> DocumentMethods for JSRef<'a, Document> { fn Location(self) -> Temporary { let window = self.window.root(); let window = window.r(); - self.location.or_init(|| Location::new(window, window.page_clone())) + self.location.or_init(|| Location::new(window)) } // http://dom.spec.whatwg.org/#dom-parentnode-children @@ -1298,10 +1325,8 @@ impl<'a> DocumentMethods for JSRef<'a, Document> { return Err(Security); } let window = self.window.root(); - let window = window.r(); - let page = window.page(); let (tx, rx) = channel(); - let _ = page.resource_task.send(GetCookiesForUrl(url, tx, NonHTTP)); + let _ = window.r().resource_task().send(GetCookiesForUrl(url, tx, NonHTTP)); let cookies = rx.recv().unwrap(); Ok(cookies.unwrap_or("".to_owned())) } @@ -1314,9 +1339,7 @@ impl<'a> DocumentMethods for JSRef<'a, Document> { return Err(Security); } let window = self.window.root(); - let window = window.r(); - let page = window.page(); - let _ = page.resource_task.send(SetCookiesForUrl(url, cookie, NonHTTP)); + let _ = window.r().resource_task().send(SetCookiesForUrl(url, cookie, NonHTTP)); Ok(()) } diff --git a/components/script/dom/domparser.rs b/components/script/dom/domparser.rs index 91d5d96a615..88429277f19 100644 --- a/components/script/dom/domparser.rs +++ b/components/script/dom/domparser.rs @@ -12,7 +12,7 @@ use dom::bindings::js::{JS, JSRef, Temporary}; use dom::bindings::utils::{Reflector, reflect_dom_object}; use dom::document::{Document, DocumentHelpers, IsHTMLDocument}; use dom::document::DocumentSource; -use dom::window::Window; +use dom::window::{Window, WindowHelpers}; use parse::html::{HTMLInput, parse_html}; use util::str::DOMString; diff --git a/components/script/dom/htmlbodyelement.rs b/components/script/dom/htmlbodyelement.rs index e609169ff05..eaf683ce3bd 100644 --- a/components/script/dom/htmlbodyelement.rs +++ b/components/script/dom/htmlbodyelement.rs @@ -16,6 +16,7 @@ use dom::eventtarget::{EventTarget, EventTargetTypeId, EventTargetHelpers}; use dom::htmlelement::{HTMLElement, HTMLElementTypeId}; use dom::node::{Node, NodeTypeId, window_from_node}; use dom::virtualmethods::VirtualMethods; +use dom::window::WindowHelpers; use cssparser::RGBA; use util::str::{self, DOMString}; diff --git a/components/script/dom/htmlelement.rs b/components/script/dom/htmlelement.rs index dd5cd1cb7e0..f3528185211 100644 --- a/components/script/dom/htmlelement.rs +++ b/components/script/dom/htmlelement.rs @@ -26,6 +26,7 @@ use dom::htmlmediaelement::HTMLMediaElementTypeId; use dom::htmltablecellelement::HTMLTableCellElementTypeId; use dom::node::{Node, NodeTypeId, window_from_node}; use dom::virtualmethods::VirtualMethods; +use dom::window::WindowHelpers; use util::str::DOMString; diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs index ddc8d42b661..9c2148334e2 100644 --- a/components/script/dom/htmlformelement.rs +++ b/components/script/dom/htmlformelement.rs @@ -215,7 +215,7 @@ impl<'a> HTMLFormElementHelpers for JSRef<'a, HTMLFormElement> { } // This is wrong. https://html.spec.whatwg.org/multipage/forms.html#planned-navigation - win.r().script_chan().send(ScriptMsg::TriggerLoad(win.r().page().id, load_data)).unwrap(); + win.r().script_chan().send(ScriptMsg::TriggerLoad(win.r().pipeline(), load_data)).unwrap(); } fn get_form_dataset<'b>(self, submitter: Option>) -> Vec { diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index b4f9b577b7b..35cc68c3da5 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -20,8 +20,8 @@ use dom::htmlelement::{HTMLElement, HTMLElementTypeId}; use dom::node::{Node, NodeHelpers, NodeTypeId, window_from_node}; use dom::urlhelper::UrlHelper; use dom::virtualmethods::VirtualMethods; -use dom::window::Window; -use page::{IterablePage, Page}; +use dom::window::{Window, WindowHelpers}; +use page::IterablePage; use msg::constellation_msg::{PipelineId, SubpageId, ConstellationChan}; use msg::constellation_msg::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed}; @@ -62,7 +62,7 @@ pub trait HTMLIFrameElementHelpers { fn get_url(self) -> Option; /// http://www.whatwg.org/html/#process-the-iframe-attributes fn process_the_iframe_attributes(self); - fn generate_new_subpage_id(self, page: &Page) -> (SubpageId, Option); + fn generate_new_subpage_id(self) -> (SubpageId, Option); } impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> { @@ -78,15 +78,16 @@ impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> { None } else { let window = window_from_node(self).root(); - UrlParser::new().base_url(&window.r().page().get_url()) + UrlParser::new().base_url(&window.r().get_url()) .parse(url.as_slice()).ok() } }) } - fn generate_new_subpage_id(self, page: &Page) -> (SubpageId, Option) { + fn generate_new_subpage_id(self) -> (SubpageId, Option) { let old_subpage_id = self.subpage_id.get(); - let subpage_id = page.get_next_subpage_id(); + let win = window_from_node(self).root(); + let subpage_id = win.r().get_next_subpage_id(); self.subpage_id.set(Some(subpage_id)); (subpage_id, old_subpage_id) } @@ -105,14 +106,13 @@ impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> { let window = window_from_node(self).root(); let window = window.r(); - let page = window.page(); - let (new_subpage_id, old_subpage_id) = self.generate_new_subpage_id(page); + let (new_subpage_id, old_subpage_id) = self.generate_new_subpage_id(); - self.containing_page_pipeline_id.set(Some(page.id)); + self.containing_page_pipeline_id.set(Some(window.pipeline())); - let ConstellationChan(ref chan) = page.constellation_chan; + let ConstellationChan(ref chan) = window.constellation_chan(); chan.send(ConstellationMsg::ScriptLoadedURLInIFrame(url, - page.id, + window.pipeline(), new_subpage_id, old_subpage_id, sandboxed)).unwrap(); @@ -172,14 +172,10 @@ impl<'a> HTMLIFrameElementMethods for JSRef<'a, HTMLIFrameElement> { let window = window_from_node(self).root(); let window = window.r(); let children = window.page().children.borrow(); - let child = children.iter().find(|child| { - child.subpage_id.unwrap() == subpage_id - }); - child.and_then(|page| { - page.frame.borrow().as_ref().map(|frame| { - Temporary::new(frame.window.clone()) - }) - }) + children.iter().find(|page| { + let window = page.window().root(); + window.r().subpage() == Some(subpage_id) + }).map(|page| page.window()) }) } @@ -189,7 +185,7 @@ impl<'a> HTMLIFrameElementMethods for JSRef<'a, HTMLIFrameElement> { Some(self_url) => self_url, None => return None, }; - let win_url = window_from_node(self).root().r().page().get_url(); + let win_url = window_from_node(self).root().r().get_url(); if UrlHelper::SameOrigin(&self_url, &win_url) { Some(window.r().Document()) diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index 2f8d09bb759..6b2f0b3dc2f 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -17,6 +17,7 @@ use dom::element::ElementTypeId; use dom::htmlelement::{HTMLElement, HTMLElementTypeId}; use dom::node::{Node, NodeTypeId, NodeHelpers, NodeDamage, window_from_node}; use dom::virtualmethods::VirtualMethods; +use dom::window::WindowHelpers; use net::image_cache_task; use util::geometry::to_px; use util::str::DOMString; diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs index 4f8f2aa14b8..ab0c45a22d5 100644 --- a/components/script/dom/htmllinkelement.rs +++ b/components/script/dom/htmllinkelement.rs @@ -17,6 +17,7 @@ use dom::element::ElementTypeId; use dom::htmlelement::{HTMLElement, HTMLElementTypeId}; use dom::node::{Node, NodeHelpers, NodeTypeId, window_from_node}; use dom::virtualmethods::VirtualMethods; +use dom::window::WindowHelpers; use layout_interface::{LayoutChan, Msg}; use util::str::{DOMString, HTML_SPACE_CHARACTERS}; @@ -130,9 +131,9 @@ impl<'a> PrivateHTMLLinkElementHelpers for JSRef<'a, HTMLLinkElement> { fn handle_stylesheet_url(self, href: &str) { let window = window_from_node(self).root(); let window = window.r(); - match UrlParser::new().base_url(&window.page().get_url()).parse(href) { + match UrlParser::new().base_url(&window.get_url()).parse(href) { Ok(url) => { - let LayoutChan(ref layout_chan) = window.page().layout_chan; + let LayoutChan(ref layout_chan) = window.layout_chan(); layout_chan.send(Msg::LoadStylesheet(url)).unwrap(); } Err(e) => debug!("Parsing url {} failed: {}", href, e) diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index 853d6709fcb..e9809a6e2cb 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -25,7 +25,7 @@ use dom::element::ElementTypeId; use dom::htmlelement::{HTMLElement, HTMLElementTypeId}; use dom::node::{Node, NodeHelpers, NodeTypeId, document_from_node, window_from_node, CloneChildrenFlag}; use dom::virtualmethods::VirtualMethods; -use dom::window::ScriptHelpers; +use dom::window::{WindowHelpers, ScriptHelpers}; use script_task::{ScriptMsg, Runnable}; use encoding::all::UTF_8; @@ -214,8 +214,7 @@ impl<'a> HTMLScriptElementHelpers for JSRef<'a, HTMLScriptElement> { // Step 14. let window = window_from_node(self).root(); let window = window.r(); - let page = window.page(); - let base_url = page.get_url(); + let base_url = window.get_url(); let load = match element.get_attribute(ns!(""), &atom!("src")).root() { // Step 14. @@ -243,7 +242,7 @@ impl<'a> HTMLScriptElementHelpers for JSRef<'a, HTMLScriptElement> { // state of the element's `crossorigin` content attribute, the origin being // the origin of the script element's node document, and the default origin // behaviour set to taint. - ScriptOrigin::External(load_whole_resource(&page.resource_task, url)) + ScriptOrigin::External(load_whole_resource(&window.resource_task(), url)) } } }, diff --git a/components/script/dom/htmlstyleelement.rs b/components/script/dom/htmlstyleelement.rs index 6fc3e1a2039..945831a35b1 100644 --- a/components/script/dom/htmlstyleelement.rs +++ b/components/script/dom/htmlstyleelement.rs @@ -12,6 +12,7 @@ use dom::element::ElementTypeId; use dom::htmlelement::{HTMLElement, HTMLElementTypeId}; use dom::node::{Node, NodeHelpers, NodeTypeId, window_from_node}; use dom::virtualmethods::VirtualMethods; +use dom::window::WindowHelpers; use layout_interface::{LayoutChan, Msg}; use util::str::DOMString; use style::stylesheets::{Origin, Stylesheet}; @@ -52,11 +53,11 @@ impl<'a> StyleElementHelpers for JSRef<'a, HTMLStyleElement> { let win = window_from_node(node).root(); let win = win.r(); - let url = win.page().get_url(); + let url = win.get_url(); let data = node.GetTextContent().expect("Element.textContent must be a string"); let sheet = Stylesheet::from_str(data.as_slice(), url, Origin::Author); - let LayoutChan(ref layout_chan) = win.page().layout_chan; + let LayoutChan(ref layout_chan) = win.layout_chan(); layout_chan.send(Msg::AddStylesheet(sheet)).unwrap(); } } diff --git a/components/script/dom/location.rs b/components/script/dom/location.rs index 614b623e5b9..13cc1a05c65 100644 --- a/components/script/dom/location.rs +++ b/components/script/dom/location.rs @@ -5,33 +5,31 @@ use dom::bindings::codegen::Bindings::LocationBinding; use dom::bindings::codegen::Bindings::LocationBinding::LocationMethods; use dom::bindings::global::GlobalRef; -use dom::bindings::js::{JSRef, Temporary}; +use dom::bindings::js::{JS, JSRef, Temporary}; use dom::bindings::utils::{Reflector, reflect_dom_object}; use dom::urlhelper::UrlHelper; use dom::window::Window; use dom::window::WindowHelpers; -use page::Page; use util::str::DOMString; - -use std::rc::Rc; +use url::Url; #[dom_struct] pub struct Location { reflector_: Reflector, - page: Rc, + window: JS, } impl Location { - fn new_inherited(page: Rc) -> Location { + fn new_inherited(window: JSRef) -> Location { Location { reflector_: Reflector::new(), - page: page + window: JS::from_rooted(window) } } - pub fn new(window: JSRef, page: Rc) -> Temporary { - reflect_dom_object(box Location::new_inherited(page), + pub fn new(window: JSRef) -> Temporary { + reflect_dom_object(box Location::new_inherited(window), GlobalRef::Window(window), LocationBinding::Wrap) } @@ -40,11 +38,11 @@ impl Location { impl<'a> LocationMethods for JSRef<'a, Location> { // https://html.spec.whatwg.org/multipage/browsers.html#dom-location-assign fn Assign(self, url: DOMString) { - self.page.frame().as_ref().unwrap().window.root().r().load_url(url); + self.window.root().r().load_url(url); } fn Href(self) -> DOMString { - UrlHelper::Href(&self.page.get_url()) + UrlHelper::Href(&self.get_url()) } fn Stringify(self) -> DOMString { @@ -52,11 +50,21 @@ impl<'a> LocationMethods for JSRef<'a, Location> { } fn Search(self) -> DOMString { - UrlHelper::Search(&self.page.get_url()) + UrlHelper::Search(&self.get_url()) } fn Hash(self) -> DOMString { - UrlHelper::Hash(&self.page.get_url()) + UrlHelper::Hash(&self.get_url()) } } +trait PrivateLocationHelpers { + fn get_url(self) -> Url; +} + +impl<'a> PrivateLocationHelpers for JSRef<'a, Location> { + fn get_url(self) -> Url { + let window = self.window.root(); + window.r().get_url() + } +} diff --git a/components/script/dom/macros.rs b/components/script/dom/macros.rs index d00aa9961b1..b1cba9ccd3e 100644 --- a/components/script/dom/macros.rs +++ b/components/script/dom/macros.rs @@ -80,6 +80,7 @@ macro_rules! make_url_or_base_getter( fn $attr(self) -> DOMString { use dom::element::{Element, AttributeHandlers}; use dom::bindings::codegen::InheritTypes::ElementCast; + use dom::window::WindowHelpers; #[allow(unused_imports)] use std::ascii::AsciiExt; let element: JSRef = ElementCast::from_ref(self); diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index a7b5e5484d6..b6f4e86307b 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -41,7 +41,7 @@ use dom::nodelist::NodeList; use dom::processinginstruction::ProcessingInstruction; use dom::text::Text; use dom::virtualmethods::{VirtualMethods, vtable_for}; -use dom::window::Window; +use dom::window::{Window, WindowHelpers}; use geom::rect::Rect; use layout_interface::{LayoutChan, Msg}; use devtools_traits::NodeInfo; @@ -409,7 +409,7 @@ pub trait NodeHelpers<'a> { fn child_elements(self) -> ChildElementIterator<'a>; fn following_siblings(self) -> NodeChildrenIterator<'a>; fn is_in_doc(self) -> bool; - fn is_inclusive_ancestor_of(self, parent: JSRef<'a, Node>) -> bool; // FIXME: See #3960 + fn is_inclusive_ancestor_of(self, parent: JSRef) -> bool; fn is_parent_of(self, child: JSRef) -> bool; fn type_id(self) -> NodeTypeId; @@ -490,9 +490,18 @@ pub trait NodeHelpers<'a> { fn get_unique_id(self) -> String; fn summarize(self) -> NodeInfo; + + fn teardown(self); } impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { + fn teardown(self) { + self.layout_data.dispose(); + for kid in self.children() { + kid.teardown(); + } + } + /// Dumps the subtree rooted at this node, for debugging. fn dump(self) { self.dump_indent(0); @@ -715,7 +724,7 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { } } - fn is_inclusive_ancestor_of(self, parent: JSRef<'a, Node>) -> bool { + fn is_inclusive_ancestor_of(self, parent: JSRef) -> bool { self == parent || parent.ancestors().any(|ancestor| ancestor == self) } @@ -737,11 +746,11 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> { } fn get_bounding_content_box(self) -> Rect { - window_from_node(self).root().r().page().content_box_query(self.to_trusted_node_address()) + window_from_node(self).root().r().content_box_query(self.to_trusted_node_address()) } fn get_content_boxes(self) -> Vec> { - window_from_node(self).root().r().page().content_boxes_query(self.to_trusted_node_address()) + window_from_node(self).root().r().content_boxes_query(self.to_trusted_node_address()) } // http://dom.spec.whatwg.org/#dom-parentnode-queryselector @@ -1376,7 +1385,7 @@ impl Node { // Step 7-8. let reference_child = match child { - Some(child) if child.clone() == node => node.next_sibling().map(|node| node.root().get_unsound_ref_forever()), + Some(child) if child == node => node.next_sibling().map(|node| node.root().get_unsound_ref_forever()), _ => child }; @@ -1946,7 +1955,7 @@ impl<'a> NodeMethods for JSRef<'a, Node> { } // Ok if not caught by previous error checks. - if node.clone() == child { + if node == child { return Ok(Temporary::from_rooted(child)); } @@ -2106,7 +2115,7 @@ impl<'a> NodeMethods for JSRef<'a, Node> { // http://dom.spec.whatwg.org/#dom-node-comparedocumentposition fn CompareDocumentPosition(self, other: JSRef) -> u16 { - if self.clone() == other { // FIXME: See issue #3960 + if self == other { // step 2. 0 } else { diff --git a/components/script/dom/webidls/CSSStyleDeclaration.webidl b/components/script/dom/webidls/CSSStyleDeclaration.webidl index 1d642acf17c..46ca8424b70 100644 --- a/components/script/dom/webidls/CSSStyleDeclaration.webidl +++ b/components/script/dom/webidls/CSSStyleDeclaration.webidl @@ -38,6 +38,7 @@ partial interface CSSStyleDeclaration { [TreatNullAs=EmptyString] attribute DOMString backgroundRepeat; [TreatNullAs=EmptyString] attribute DOMString backgroundImage; [TreatNullAs=EmptyString] attribute DOMString backgroundAttachment; + [TreatNullAs=EmptyString] attribute DOMString backgroundSize; [TreatNullAs=EmptyString] attribute DOMString border; [TreatNullAs=EmptyString] attribute DOMString borderColor; @@ -103,6 +104,8 @@ partial interface CSSStyleDeclaration { [TreatNullAs=EmptyString] attribute DOMString listStyleImage; [TreatNullAs=EmptyString] attribute DOMString overflow; + [TreatNullAs=EmptyString] attribute DOMString overflowX; + [TreatNullAs=EmptyString] attribute DOMString overflowY; [TreatNullAs=EmptyString] attribute DOMString overflowWrap; [TreatNullAs=EmptyString] attribute DOMString tableLayout; @@ -171,4 +174,6 @@ partial interface CSSStyleDeclaration { [TreatNullAs=EmptyString] attribute DOMString maxWidth; [TreatNullAs=EmptyString] attribute DOMString zIndex; + + [TreatNullAs=EmptyString] attribute DOMString imageRendering; }; diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 27b297e21f2..29551114315 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -8,50 +8,59 @@ use dom::bindings::codegen::Bindings::FunctionBinding::Function; use dom::bindings::codegen::Bindings::WindowBinding; use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; -use dom::bindings::codegen::InheritTypes::EventTargetCast; +use dom::bindings::codegen::InheritTypes::{NodeCast, EventTargetCast}; use dom::bindings::global::global_object_for_js_object; use dom::bindings::error::{report_pending_exception, Fallible}; use dom::bindings::error::Error::InvalidCharacter; use dom::bindings::global::GlobalRef; -use dom::bindings::js::{MutNullableJS, JSRef, Temporary}; -use dom::bindings::utils::Reflectable; +use dom::bindings::js::{MutNullableJS, JSRef, Temporary, OptionalRootable, RootedReference}; +use dom::bindings::utils::{GlobalStaticData, Reflectable, WindowProxyHandler}; use dom::browsercontext::BrowserContext; use dom::console::Console; -use dom::document::Document; +use dom::document::{Document, DocumentHelpers}; use dom::element::Element; use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId}; use dom::location::Location; use dom::navigator::Navigator; -use dom::node::window_from_node; +use dom::node::{window_from_node, Node, TrustedNodeAddress, NodeHelpers}; use dom::performance::Performance; use dom::screen::Screen; use dom::storage::Storage; -use layout_interface::{ReflowGoal, ReflowQueryType}; +use layout_interface::{ReflowGoal, ReflowQueryType, LayoutRPC, LayoutChan, Reflow, Msg}; +use layout_interface::{ContentBoxResponse, ContentBoxesResponse}; use page::Page; use script_task::{TimerSource, ScriptChan}; use script_task::ScriptMsg; use script_traits::ScriptControlChan; use timers::{IsInterval, TimerId, TimerManager, TimerCallback}; +use devtools_traits::DevtoolsControlChan; use msg::compositor_msg::ScriptListener; -use msg::constellation_msg::LoadData; +use msg::constellation_msg::{LoadData, PipelineId, SubpageId, ConstellationChan, WindowSizeData}; use net::image_cache_task::ImageCacheTask; +use net::resource_task::ResourceTask; use net::storage_task::StorageTask; +use util::geometry::{self, Au, MAX_RECT}; use util::str::{DOMString,HTML_SPACE_CHARACTERS}; +use geom::{Point2D, Rect, Size2D}; use js::jsapi::JS_EvaluateUCScript; use js::jsapi::JSContext; use js::jsapi::{JS_GC, JS_GetRuntime}; use js::jsval::{JSVal, UndefinedValue}; -use js::rust::with_compartment; +use js::rust::{Cx, with_compartment}; use url::{Url, UrlParser}; use libc; use rustc_serialize::base64::{FromBase64, ToBase64, STANDARD}; -use std::cell::{Ref, RefMut}; +use std::cell::{Cell, Ref, RefMut}; use std::default::Default; use std::ffi::CString; +use std::mem; +use std::num::Float; use std::rc::Rc; +use std::sync::mpsc::{channel, Receiver}; +use std::sync::mpsc::TryRecvError::{Empty, Disconnected}; use time; #[dom_struct] @@ -71,18 +80,80 @@ pub struct Window { screen: MutNullableJS, session_storage: MutNullableJS, timers: TimerManager, + + /// For providing instructions to an optional devtools server. + devtools_chan: Option, + + /// A flag to indicate whether the developer tools have requested live updates of + /// page changes. + devtools_wants_updates: Cell, + + next_subpage_id: Cell, + + /// Pending resize event, if any. + resize_event: Cell>, + + /// Pipeline id associated with this page. + id: PipelineId, + + /// Subpage id associated with this page, if any. + subpage_id: Option, + + /// Unique id for last reflow request; used for confirming completion reply. + last_reflow_id: Cell, + + /// Global static data related to the DOM. + dom_static: GlobalStaticData, + + /// The JavaScript context. + js_context: DOMRefCell>>, + + /// A handle for communicating messages to the layout task. + layout_chan: LayoutChan, + + /// A handle to perform RPC calls into the layout, quickly. + layout_rpc: Box, + + /// The port that we will use to join layout. If this is `None`, then layout is not running. + layout_join_port: DOMRefCell>>, + + /// The current size of the window, in pixels. + window_size: Cell, + + /// Associated resource task for use by DOM objects like XMLHttpRequest + resource_task: ResourceTask, + + /// A handle for communicating messages to the storage task. + storage_task: StorageTask, + + /// A handle for communicating messages to the constellation task. + constellation_chan: ConstellationChan, + + /// Pending scroll to fragment event, if any + fragment_name: DOMRefCell>, + + /// An enlarged rectangle around the page contents visible in the viewport, used + /// to prevent creating display list items for content that is far away from the viewport. + page_clip_rect: Cell>, } impl Window { pub fn get_cx(&self) -> *mut JSContext { - let js_info = self.page().js_info(); - (*js_info.as_ref().unwrap().js_context).ptr + self.js_context.borrow().as_ref().unwrap().ptr } pub fn script_chan(&self) -> Box { self.script_chan.clone() } + pub fn pipeline(&self) -> PipelineId { + self.id.clone() + } + + pub fn subpage(&self) -> Option { + self.subpage_id.clone() + } + pub fn control_chan<'a>(&'a self) -> &'a ScriptControlChan { &self.control_chan } @@ -103,16 +174,8 @@ impl Window { &*self.page } - pub fn page_clone(&self) -> Rc { - self.page.clone() - } - - pub fn get_url(&self) -> Url { - self.page().get_url() - } - pub fn storage_task(&self) -> StorageTask { - self.page().storage_task.clone() + self.storage_task.clone() } } @@ -197,12 +260,11 @@ impl<'a> WindowMethods for JSRef<'a, Window> { } fn Close(self) { - self.script_chan.send(ScriptMsg::ExitWindow(self.page.id.clone())).unwrap(); + self.script_chan.send(ScriptMsg::ExitWindow(self.id.clone())).unwrap(); } fn Document(self) -> Temporary { - let frame = self.page().frame(); - Temporary::new(frame.as_ref().unwrap().document.clone()) + self.browser_context().as_ref().unwrap().active_document() } fn Location(self) -> Temporary { @@ -230,7 +292,7 @@ impl<'a> WindowMethods for JSRef<'a, Window> { args, timeout, IsInterval::NonInterval, - TimerSource::FromWindow(self.page.id.clone()), + TimerSource::FromWindow(self.id.clone()), self.script_chan.clone()) } @@ -239,7 +301,7 @@ impl<'a> WindowMethods for JSRef<'a, Window> { args, timeout, IsInterval::NonInterval, - TimerSource::FromWindow(self.page.id.clone()), + TimerSource::FromWindow(self.id.clone()), self.script_chan.clone()) } @@ -252,7 +314,7 @@ impl<'a> WindowMethods for JSRef<'a, Window> { args, timeout, IsInterval::Interval, - TimerSource::FromWindow(self.page.id.clone()), + TimerSource::FromWindow(self.id.clone()), self.script_chan.clone()) } @@ -261,7 +323,7 @@ impl<'a> WindowMethods for JSRef<'a, Window> { args, timeout, IsInterval::Interval, - TimerSource::FromWindow(self.page.id.clone()), + TimerSource::FromWindow(self.id.clone()), self.script_chan.clone()) } @@ -330,10 +392,34 @@ impl<'a> WindowMethods for JSRef<'a, Window> { } pub trait WindowHelpers { - fn flush_layout(self, goal: ReflowGoal, query: ReflowQueryType); + fn clear_js_context(self); + fn clear_js_context_for_script_deallocation(self); fn init_browser_context(self, doc: JSRef, frame_element: Option>); fn load_url(self, href: DOMString); fn handle_fire_timer(self, timer_id: TimerId); + fn reflow(self, goal: ReflowGoal, query_type: ReflowQueryType); + fn join_layout(self); + fn layout(&self) -> &LayoutRPC; + fn content_box_query(self, content_box_request: TrustedNodeAddress) -> Rect; + fn content_boxes_query(self, content_boxes_request: TrustedNodeAddress) -> Vec>; + fn handle_reflow_complete_msg(self, reflow_id: uint); + fn handle_resize_inactive_msg(self, new_size: WindowSizeData); + fn set_fragment_name(self, fragment: Option); + fn steal_fragment_name(self) -> Option; + fn set_window_size(self, size: WindowSizeData); + fn window_size(self) -> WindowSizeData; + fn get_url(self) -> Url; + fn resource_task(self) -> ResourceTask; + fn devtools_chan(self) -> Option; + fn layout_chan(self) -> LayoutChan; + fn constellation_chan(self) -> ConstellationChan; + fn windowproxy_handler(self) -> WindowProxyHandler; + fn get_next_subpage_id(self) -> SubpageId; + fn layout_is_idle(self) -> bool; + fn set_resize_event(self, event: WindowSizeData); + fn steal_resize_event(self) -> Option; + fn set_page_clip_rect_with_new_viewport(self, viewport: Rect) -> bool; + fn set_devtools_wants_updates(self, value: bool); fn IndexedGetter(self, _index: u32, _found: &mut bool) -> Option>; fn thaw(self); fn freeze(self); @@ -373,8 +459,131 @@ impl<'a, T: Reflectable> ScriptHelpers for JSRef<'a, T> { } impl<'a> WindowHelpers for JSRef<'a, Window> { - fn flush_layout(self, goal: ReflowGoal, query: ReflowQueryType) { - self.page().flush_layout(goal, query); + fn clear_js_context(self) { + let document = self.Document().root(); + NodeCast::from_ref(document.r()).teardown(); + + *self.js_context.borrow_mut() = None; + *self.browser_context.borrow_mut() = None; + } + + #[allow(unsafe_blocks)] + fn clear_js_context_for_script_deallocation(self) { + unsafe { + *self.js_context.borrow_for_script_deallocation() = None; + *self.browser_context.borrow_for_script_deallocation() = None; + } + } + + /// Reflows the page if it's possible to do so and the page is dirty. This method will wait + /// for the layout thread to complete (but see the `TODO` below). If there is no window size + /// yet, the page is presumed invisible and no reflow is performed. + /// + /// TODO(pcwalton): Only wait for style recalc, since we have off-main-thread layout. + fn reflow(self, goal: ReflowGoal, query_type: ReflowQueryType) { + let document = self.Document().root(); + let root = document.r().GetDocumentElement().root(); + let root = match root.r() { + Some(root) => root, + None => return, + }; + + debug!("script: performing reflow for goal {:?}", goal); + + let root: JSRef = NodeCast::from_ref(root); + if !root.get_has_dirty_descendants() { + debug!("root has no dirty descendants; avoiding reflow"); + return + } + + debug!("script: performing reflow for goal {:?}", goal); + + // Layout will let us know when it's done. + let (join_chan, join_port) = channel(); + + { + let mut layout_join_port = self.layout_join_port.borrow_mut(); + *layout_join_port = Some(join_port); + } + + let last_reflow_id = &self.last_reflow_id; + last_reflow_id.set(last_reflow_id.get() + 1); + + let window_size = self.window_size.get(); + + // Send new document and relevant styles to layout. + let reflow = box Reflow { + document_root: root.to_trusted_node_address(), + url: self.get_url(), + iframe: self.subpage_id.is_some(), + goal: goal, + window_size: window_size, + script_chan: self.control_chan.clone(), + script_join_chan: join_chan, + id: last_reflow_id.get(), + query_type: query_type, + page_clip_rect: self.page_clip_rect.get(), + }; + + let LayoutChan(ref chan) = self.layout_chan; + chan.send(Msg::Reflow(reflow)).unwrap(); + + debug!("script: layout forked"); + + self.join_layout(); + } + + // FIXME(cgaebel): join_layout is racey. What if the compositor triggers a + // reflow between the "join complete" message and returning from this + // function? + + /// Sends a ping to layout and waits for the response. The response will arrive when the + /// layout task has finished any pending request messages. + fn join_layout(self) { + let mut layout_join_port = self.layout_join_port.borrow_mut(); + if let Some(join_port) = mem::replace(&mut *layout_join_port, None) { + match join_port.try_recv() { + Err(Empty) => { + info!("script: waiting on layout"); + join_port.recv().unwrap(); + } + Ok(_) => {} + Err(Disconnected) => { + panic!("Layout task failed while script was waiting for a result."); + } + } + + debug!("script: layout joined") + } + } + + fn layout(&self) -> &LayoutRPC { + &*self.layout_rpc + } + + fn content_box_query(self, content_box_request: TrustedNodeAddress) -> Rect { + self.reflow(ReflowGoal::ForScriptQuery, ReflowQueryType::ContentBoxQuery(content_box_request)); + self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough? + let ContentBoxResponse(rect) = self.layout_rpc.content_box(); + rect + } + + fn content_boxes_query(self, content_boxes_request: TrustedNodeAddress) -> Vec> { + self.reflow(ReflowGoal::ForScriptQuery, ReflowQueryType::ContentBoxesQuery(content_boxes_request)); + self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough? + let ContentBoxesResponse(rects) = self.layout_rpc.content_boxes(); + rects + } + + fn handle_reflow_complete_msg(self, reflow_id: uint) { + let last_reflow_id = self.last_reflow_id.get(); + if last_reflow_id == reflow_id { + *self.layout_join_port.borrow_mut() = None; + } + } + + fn handle_resize_inactive_msg(self, new_size: WindowSizeData) { + self.window_size.set(new_size); } fn init_browser_context(self, doc: JSRef, frame_element: Option>) { @@ -383,26 +592,117 @@ impl<'a> WindowHelpers for JSRef<'a, Window> { /// Commence a new URL load which will either replace this window or scroll to a fragment. fn load_url(self, href: DOMString) { - let base_url = self.page().get_url(); + let base_url = self.get_url(); debug!("current page url is {}", base_url); let url = UrlParser::new().base_url(&base_url).parse(href.as_slice()); // FIXME: handle URL parse errors more gracefully. let url = url.unwrap(); match url.fragment { Some(fragment) => { - self.script_chan.send(ScriptMsg::TriggerFragment(self.page.id, fragment)).unwrap(); + self.script_chan.send(ScriptMsg::TriggerFragment(self.id, fragment)).unwrap(); }, None => { - self.script_chan.send(ScriptMsg::TriggerLoad(self.page.id, LoadData::new(url))).unwrap(); + self.script_chan.send(ScriptMsg::TriggerLoad(self.id, LoadData::new(url))).unwrap(); } } } fn handle_fire_timer(self, timer_id: TimerId) { self.timers.fire_timer(timer_id, self); - self.flush_layout(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); + self.reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); } + fn set_fragment_name(self, fragment: Option) { + *self.fragment_name.borrow_mut() = fragment; + } + + fn steal_fragment_name(self) -> Option { + self.fragment_name.borrow_mut().take() + } + + fn set_window_size(self, size: WindowSizeData) { + self.window_size.set(size); + } + + fn window_size(self) -> WindowSizeData { + self.window_size.get() + } + + fn get_url(self) -> Url { + let doc = self.Document().root(); + doc.r().url() + } + + fn resource_task(self) -> ResourceTask { + self.resource_task.clone() + } + + fn devtools_chan(self) -> Option { + self.devtools_chan.clone() + } + + fn layout_chan(self) -> LayoutChan { + self.layout_chan.clone() + } + + fn constellation_chan(self) -> ConstellationChan { + self.constellation_chan.clone() + } + + fn windowproxy_handler(self) -> WindowProxyHandler { + WindowProxyHandler(self.dom_static.windowproxy_handler.0) + } + + fn get_next_subpage_id(self) -> SubpageId { + let subpage_id = self.next_subpage_id.get(); + let SubpageId(id_num) = subpage_id; + self.next_subpage_id.set(SubpageId(id_num + 1)); + subpage_id + } + + fn layout_is_idle(self) -> bool { + self.layout_join_port.borrow().is_none() + } + + fn set_resize_event(self, event: WindowSizeData) { + self.resize_event.set(Some(event)); + } + + fn steal_resize_event(self) -> Option { + let event = self.resize_event.get(); + self.resize_event.set(None); + event + } + + fn set_page_clip_rect_with_new_viewport(self, viewport: Rect) -> bool { + // We use a clipping rectangle that is five times the size of the of the viewport, + // so that we don't collect display list items for areas too far outside the viewport, + // but also don't trigger reflows every time the viewport changes. + static VIEWPORT_EXPANSION: f32 = 2.0; // 2 lengths on each side plus original length is 5 total. + let proposed_clip_rect = geometry::f32_rect_to_au_rect( + viewport.inflate(viewport.size.width * VIEWPORT_EXPANSION, + viewport.size.height * VIEWPORT_EXPANSION)); + let clip_rect = self.page_clip_rect.get(); + if proposed_clip_rect == clip_rect { + return false; + } + + let had_clip_rect = clip_rect != MAX_RECT; + if had_clip_rect && !should_move_clip_rect(clip_rect, viewport) { + return false; + } + + self.page_clip_rect.set(proposed_clip_rect); + + // If we didn't have a clip rect, the previous display doesn't need rebuilding + // because it was built for infinite clip (MAX_RECT). + had_clip_rect + } + + fn set_devtools_wants_updates(self, value: bool) { + self.devtools_wants_updates.set(value); + } + // https://html.spec.whatwg.org/multipage/browsers.html#accessing-other-browsing-contexts fn IndexedGetter(self, _index: u32, _found: &mut bool) -> Option> { None @@ -419,13 +719,28 @@ impl<'a> WindowHelpers for JSRef<'a, Window> { } impl Window { - pub fn new(cx: *mut JSContext, + pub fn new(js_context: Rc, page: Rc, script_chan: Box, control_chan: ScriptControlChan, compositor: Box, - image_cache_task: ImageCacheTask) + image_cache_task: ImageCacheTask, + resource_task: ResourceTask, + storage_task: StorageTask, + devtools_chan: Option, + constellation_chan: ConstellationChan, + layout_chan: LayoutChan, + id: PipelineId, + subpage_id: Option, + window_size: WindowSizeData) -> Temporary { + let layout_rpc: Box = { + let (rpc_send, rpc_recv) = channel(); + let LayoutChan(ref lchan) = layout_chan; + lchan.send(Msg::GetRPC(rpc_send)).unwrap(); + rpc_recv.recv().unwrap() + }; + let win = box Window { eventtarget: EventTarget::new_inherited(EventTargetTypeId::Window), script_chan: script_chan, @@ -435,6 +750,7 @@ impl Window { page: page, navigator: Default::default(), image_cache_task: image_cache_task, + devtools_chan: devtools_chan, browser_context: DOMRefCell::new(None), performance: Default::default(), navigation_start: time::get_time().sec as u64, @@ -442,8 +758,43 @@ impl Window { screen: Default::default(), session_storage: Default::default(), timers: TimerManager::new(), + id: id, + subpage_id: subpage_id, + dom_static: GlobalStaticData::new(), + js_context: DOMRefCell::new(Some(js_context.clone())), + resource_task: resource_task, + storage_task: storage_task, + constellation_chan: constellation_chan, + page_clip_rect: Cell::new(MAX_RECT), + fragment_name: DOMRefCell::new(None), + last_reflow_id: Cell::new(0), + resize_event: Cell::new(None), + next_subpage_id: Cell::new(SubpageId(0)), + layout_chan: layout_chan, + layout_rpc: layout_rpc, + layout_join_port: DOMRefCell::new(None), + window_size: Cell::new(window_size), + devtools_wants_updates: Cell::new(false), }; - WindowBinding::Wrap(cx, win) + WindowBinding::Wrap(js_context.ptr, win) } } + +fn should_move_clip_rect(clip_rect: Rect, new_viewport: Rect) -> bool{ + let clip_rect = Rect(Point2D(geometry::to_frac_px(clip_rect.origin.x) as f32, + geometry::to_frac_px(clip_rect.origin.y) as f32), + Size2D(geometry::to_frac_px(clip_rect.size.width) as f32, + geometry::to_frac_px(clip_rect.size.height) as f32)); + + // We only need to move the clip rect if the viewport is getting near the edge of + // our preexisting clip rect. We use half of the size of the viewport as a heuristic + // for "close." + static VIEWPORT_SCROLL_MARGIN_SIZE: f32 = 0.5; + let viewport_scroll_margin = new_viewport.size * VIEWPORT_SCROLL_MARGIN_SIZE; + + (clip_rect.origin.x - new_viewport.origin.x).abs() <= viewport_scroll_margin.width || + (clip_rect.max_x() - new_viewport.max_x()).abs() <= viewport_scroll_margin.width || + (clip_rect.origin.y - new_viewport.origin.y).abs() <= viewport_scroll_margin.height || + (clip_rect.max_y() - new_viewport.max_y()).abs() <= viewport_scroll_margin.height +} diff --git a/components/script/page.rs b/components/script/page.rs index c7ea2e44bec..ffaa25c9e14 100644 --- a/components/script/page.rs +++ b/components/script/page.rs @@ -3,107 +3,39 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use dom::bindings::cell::DOMRefCell; -use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; -use dom::bindings::codegen::InheritTypes::NodeCast; -use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootable}; -use dom::bindings::utils::GlobalStaticData; +use dom::bindings::js::{JS, Temporary, Unrooted}; use dom::document::{Document, DocumentHelpers}; -use dom::element::Element; -use dom::node::{Node, NodeHelpers}; +use dom::node::NodeHelpers; use dom::window::Window; -use devtools_traits::DevtoolsControlChan; -use layout_interface::{ - ContentBoxResponse, ContentBoxesResponse, - HitTestResponse, LayoutChan, LayoutRPC, MouseOverResponse, Msg, Reflow, - ReflowGoal, ReflowQueryType, - TrustedNodeAddress -}; -use script_traits::{UntrustedNodeAddress, ScriptControlChan}; -use geom::{Point2D, Rect, Size2D}; -use js::rust::Cx; -use msg::compositor_msg::ScriptListener; -use msg::constellation_msg::{ConstellationChan, WindowSizeData}; use msg::constellation_msg::{PipelineId, SubpageId}; -use net::resource_task::ResourceTask; -use net::storage_task::StorageTask; -use util::geometry::{Au, MAX_RECT}; -use util::geometry; -use util::str::DOMString; use util::smallvec::SmallVec; -use std::cell::{Cell, Ref, RefMut}; -use std::sync::mpsc::{channel, Receiver}; -use std::sync::mpsc::TryRecvError::{Empty, Disconnected}; -use std::mem::replace; -use std::num::Float; +use std::cell::Cell; use std::rc::Rc; use url::Url; -/// Encapsulates a handle to a frame and its associated layout information. +/// Encapsulates a handle to a frame in a frame tree. #[jstraceable] pub struct Page { /// Pipeline id associated with this page. - pub id: PipelineId, + id: PipelineId, /// Subpage id associated with this page, if any. - pub subpage_id: Option, + subpage_id: Option, - /// Unique id for last reflow request; used for confirming completion reply. - pub last_reflow_id: Cell, + /// The outermost frame containing the document and window. + frame: DOMRefCell>, - /// The outermost frame containing the document, window, and page URL. - pub frame: DOMRefCell>, - - /// A handle for communicating messages to the layout task. - pub layout_chan: LayoutChan, - - /// A handle to perform RPC calls into the layout, quickly. - layout_rpc: Box, - - /// The port that we will use to join layout. If this is `None`, then layout is not running. - pub layout_join_port: DOMRefCell>>, - - /// The current size of the window, in pixels. - pub window_size: Cell, - - js_info: DOMRefCell>, - - /// Cached copy of the most recent url loaded by the script + /// Cached copy of the most recent url loaded by the script, after all redirections. /// TODO(tkuehn): this currently does not follow any particular caching policy - /// and simply caches pages forever (!). The bool indicates if reflow is required - /// when reloading. - url: DOMRefCell>, + /// and simply caches pages forever (!). + url: Url, - next_subpage_id: Cell, - - /// Pending resize event, if any. - pub resize_event: Cell>, - - /// Pending scroll to fragment event, if any - pub fragment_name: DOMRefCell>, - - /// Associated resource task for use by DOM objects like XMLHttpRequest - pub resource_task: ResourceTask, - - /// A handle for communicating messages to the storage task. - pub storage_task: StorageTask, - - /// A handle for communicating messages to the constellation task. - pub constellation_chan: ConstellationChan, + /// Indicates if reflow is required when reloading. + needs_reflow: Cell, // Child Pages. pub children: DOMRefCell>>, - - /// An enlarged rectangle around the page contents visible in the viewport, used - /// to prevent creating display list items for content that is far away from the viewport. - pub page_clip_rect: Cell>, - - /// A flag to indicate whether the developer tools have requested live updates of - /// page changes. - pub devtools_wants_updates: Cell, - - /// For providing instructions to an optional devtools server. - pub devtools_chan: Option, } pub struct PageIterator { @@ -133,70 +65,27 @@ impl IterablePage for Rc { } impl Page { - pub fn new(id: PipelineId, subpage_id: Option, - layout_chan: LayoutChan, - window_size: WindowSizeData, - resource_task: ResourceTask, - storage_task: StorageTask, - constellation_chan: ConstellationChan, - js_context: Rc, - devtools_chan: Option) -> Page { - let js_info = JSPageInfo { - dom_static: GlobalStaticData::new(), - js_context: js_context, - }; - let layout_rpc: Box = { - let (rpc_send, rpc_recv) = channel(); - let LayoutChan(ref lchan) = layout_chan; - lchan.send(Msg::GetRPC(rpc_send)).unwrap(); - rpc_recv.recv().unwrap() - }; + pub fn new(id: PipelineId, subpage_id: Option, url: Url) -> Page { Page { id: id, subpage_id: subpage_id, frame: DOMRefCell::new(None), - layout_chan: layout_chan, - layout_rpc: layout_rpc, - layout_join_port: DOMRefCell::new(None), - window_size: Cell::new(window_size), - js_info: DOMRefCell::new(Some(js_info)), - url: DOMRefCell::new(None), - next_subpage_id: Cell::new(SubpageId(0)), - resize_event: Cell::new(None), - fragment_name: DOMRefCell::new(None), - last_reflow_id: Cell::new(0), - resource_task: resource_task, - storage_task: storage_task, - constellation_chan: constellation_chan, + url: url, + needs_reflow: Cell::new(true), children: DOMRefCell::new(vec!()), - page_clip_rect: Cell::new(MAX_RECT), - devtools_wants_updates: Cell::new(false), - devtools_chan: devtools_chan, } } - pub fn flush_layout(&self, goal: ReflowGoal, query: ReflowQueryType) { - let frame = self.frame(); - let window = frame.as_ref().unwrap().window.root(); - self.reflow(goal, window.r().control_chan().clone(), &mut **window.r().compositor(), query); + pub fn window(&self) -> Temporary { + Temporary::new(self.frame.borrow().as_ref().unwrap().window.clone()) } - pub fn layout(&self) -> &LayoutRPC { - &*self.layout_rpc + pub fn window_for_script_dealloation(&self) -> Unrooted { + Unrooted::from_js(self.frame.borrow().as_ref().unwrap().window) } - pub fn content_box_query(&self, content_box_request: TrustedNodeAddress) -> Rect { - self.flush_layout(ReflowGoal::ForScriptQuery, ReflowQueryType::ContentBoxQuery(content_box_request)); - self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough? - let ContentBoxResponse(rect) = self.layout_rpc.content_box(); - rect - } - - pub fn content_boxes_query(&self, content_boxes_request: TrustedNodeAddress) -> Vec> { - self.flush_layout(ReflowGoal::ForScriptQuery, ReflowQueryType::ContentBoxesQuery(content_boxes_request)); - self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough? - let ContentBoxesResponse(rects) = self.layout_rpc.content_boxes(); - rects + pub fn document(&self) -> Temporary { + Temporary::new(self.frame.borrow().as_ref().unwrap().document.clone()) } // must handle root case separately @@ -218,49 +107,6 @@ impl Page { } } } - - pub fn set_page_clip_rect_with_new_viewport(&self, viewport: Rect) -> bool { - // We use a clipping rectangle that is five times the size of the of the viewport, - // so that we don't collect display list items for areas too far outside the viewport, - // but also don't trigger reflows every time the viewport changes. - static VIEWPORT_EXPANSION: f32 = 2.0; // 2 lengths on each side plus original length is 5 total. - let proposed_clip_rect = geometry::f32_rect_to_au_rect( - viewport.inflate(viewport.size.width * VIEWPORT_EXPANSION, - viewport.size.height * VIEWPORT_EXPANSION)); - let clip_rect = self.page_clip_rect.get(); - if proposed_clip_rect == clip_rect { - return false; - } - - let had_clip_rect = clip_rect != MAX_RECT; - if had_clip_rect && !should_move_clip_rect(clip_rect, viewport) { - return false; - } - - self.page_clip_rect.set(proposed_clip_rect); - - // If we didn't have a clip rect, the previous display doesn't need rebuilding - // because it was built for infinite clip (MAX_RECT). - had_clip_rect - } - - pub fn send_title_to_compositor(&self) { - match *self.frame() { - None => {} - Some(ref frame) => { - let window = frame.window.root(); - let document = frame.document.root(); - window.r().compositor().set_title(self.id, Some(document.r().Title())); - } - } - } - - pub fn dirty_all_nodes(&self) { - match *self.frame.borrow() { - None => {} - Some(ref frame) => frame.document.root().r().dirty_all_nodes(), - } - } } impl Iterator for PageIterator { @@ -280,198 +126,15 @@ impl Iterator for PageIterator { } impl Page { - pub fn mut_js_info<'a>(&'a self) -> RefMut<'a, Option> { - self.js_info.borrow_mut() + pub fn set_reflow_status(&self, status: bool) -> bool { + let old = self.needs_reflow.get(); + self.needs_reflow.set(status); + old } - pub unsafe fn unsafe_mut_js_info<'a>(&'a self) -> &'a mut Option { - self.js_info.borrow_for_script_deallocation() + pub fn set_frame(&self, frame: Option) { + *self.frame.borrow_mut() = frame; } - - pub fn js_info<'a>(&'a self) -> Ref<'a, Option> { - self.js_info.borrow() - } - - pub fn url<'a>(&'a self) -> Ref<'a, Option<(Url, bool)>> { - self.url.borrow() - } - - pub fn mut_url<'a>(&'a self) -> RefMut<'a, Option<(Url, bool)>> { - self.url.borrow_mut() - } - - pub fn frame<'a>(&'a self) -> Ref<'a, Option> { - self.frame.borrow() - } - - pub fn mut_frame<'a>(&'a self) -> RefMut<'a, Option> { - self.frame.borrow_mut() - } - - pub fn get_next_subpage_id(&self) -> SubpageId { - let subpage_id = self.next_subpage_id.get(); - let SubpageId(id_num) = subpage_id; - self.next_subpage_id.set(SubpageId(id_num + 1)); - subpage_id - } - - pub fn get_url(&self) -> Url { - self.url().as_ref().unwrap().0.clone() - } - - // FIXME(cgaebel): join_layout is racey. What if the compositor triggers a - // reflow between the "join complete" message and returning from this - // function? - - /// Sends a ping to layout and waits for the response. The response will arrive when the - /// layout task has finished any pending request messages. - fn join_layout(&self) { - let mut layout_join_port = self.layout_join_port.borrow_mut(); - if let Some(join_port) = replace(&mut *layout_join_port, None) { - match join_port.try_recv() { - Err(Empty) => { - info!("script: waiting on layout"); - join_port.recv().unwrap(); - } - Ok(_) => {} - Err(Disconnected) => { - panic!("Layout task failed while script was waiting for a result."); - } - } - - debug!("script: layout joined") - } - } - - /// Reflows the page if it's possible to do so and the page is dirty. This method will wait - /// for the layout thread to complete (but see the `TODO` below). If there is no window size - /// yet, the page is presumed invisible and no reflow is performed. - /// - /// TODO(pcwalton): Only wait for style recalc, since we have off-main-thread layout. - pub fn reflow(&self, - goal: ReflowGoal, - script_chan: ScriptControlChan, - _: &mut ScriptListener, - query_type: ReflowQueryType) { - let root = match *self.frame() { - None => return, - Some(ref frame) => { - frame.document.root().r().GetDocumentElement() - } - }; - - let root = match root.root() { - None => return, - Some(root) => root, - }; - - debug!("script: performing reflow for goal {:?}", goal); - - let root: JSRef = NodeCast::from_ref(root.r()); - if !root.get_has_dirty_descendants() { - debug!("root has no dirty descendants; avoiding reflow"); - return - } - - debug!("script: performing reflow for goal {:?}", goal); - - // Layout will let us know when it's done. - let (join_chan, join_port) = channel(); - - { - let mut layout_join_port = self.layout_join_port.borrow_mut(); - *layout_join_port = Some(join_port); - } - - let last_reflow_id = &self.last_reflow_id; - last_reflow_id.set(last_reflow_id.get() + 1); - - let window_size = self.window_size.get(); - - // Send new document and relevant styles to layout. - let reflow = box Reflow { - document_root: root.to_trusted_node_address(), - url: self.get_url(), - iframe: self.subpage_id.is_some(), - goal: goal, - window_size: window_size, - script_chan: script_chan, - script_join_chan: join_chan, - id: last_reflow_id.get(), - query_type: query_type, - page_clip_rect: self.page_clip_rect.get(), - }; - - let LayoutChan(ref chan) = self.layout_chan; - chan.send(Msg::Reflow(reflow)).unwrap(); - - debug!("script: layout forked"); - - self.join_layout(); - } - - /// Attempt to find a named element in this page's document. - pub fn find_fragment_node(&self, fragid: DOMString) -> Option> { - let document = self.frame().as_ref().unwrap().document.root(); - document.r().find_fragment_node(fragid) - } - - pub fn hit_test(&self, point: &Point2D) -> Option { - let frame = self.frame(); - let document = frame.as_ref().unwrap().document.root(); - let root = match document.r().GetDocumentElement().root() { - None => return None, - Some(root) => root, - }; - let root: JSRef = NodeCast::from_ref(root.r()); - let address = match self.layout().hit_test(root.to_trusted_node_address(), *point) { - Ok(HitTestResponse(node_address)) => { - Some(node_address) - } - Err(()) => { - debug!("layout query error"); - None - } - }; - address - } - - pub fn get_nodes_under_mouse(&self, point: &Point2D) -> Vec { - let mut results = vec!(); - let frame = self.frame(); - let document = frame.as_ref().unwrap().document.root(); - match document.r().GetDocumentElement().root() { - Some(root) => { - let root: JSRef = NodeCast::from_ref(root.r()); - match self.layout().mouse_over(root.to_trusted_node_address(), *point) { - Ok(MouseOverResponse(node_addresses)) => { - results = node_addresses; - } - Err(()) => {} - }; - } - None => {} - } - results - } -} - -fn should_move_clip_rect(clip_rect: Rect, new_viewport: Rect) -> bool{ - let clip_rect = Rect(Point2D(geometry::to_frac_px(clip_rect.origin.x) as f32, - geometry::to_frac_px(clip_rect.origin.y) as f32), - Size2D(geometry::to_frac_px(clip_rect.size.width) as f32, - geometry::to_frac_px(clip_rect.size.height) as f32)); - - // We only need to move the clip rect if the viewport is getting near the edge of - // our preexisting clip rect. We use half of the size of the viewport as a heuristic - // for "close." - static VIEWPORT_SCROLL_MARGIN_SIZE: f32 = 0.5; - let viewport_scroll_margin = new_viewport.size * VIEWPORT_SCROLL_MARGIN_SIZE; - - (clip_rect.origin.x - new_viewport.origin.x).abs() <= viewport_scroll_margin.width || - (clip_rect.max_x() - new_viewport.max_x()).abs() <= viewport_scroll_margin.width || - (clip_rect.origin.y - new_viewport.origin.y).abs() <= viewport_scroll_margin.height || - (clip_rect.max_y() - new_viewport.max_y()).abs() <= viewport_scroll_margin.height } /// Information for one frame in the browsing context. @@ -483,12 +146,3 @@ pub struct Frame { /// The window object for this frame. pub window: JS, } - -/// Encapsulation of the javascript information associated with each frame. -#[jstraceable] -pub struct JSPageInfo { - /// Global static data related to the DOM. - pub dom_static: GlobalStaticData, - /// The JavaScript context. - pub js_context: Rc, -} diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 51361bd1436..673dc3510ef 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -3,7 +3,19 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ //! The script task is the task that owns the DOM in memory, runs JavaScript, and spawns parsing -//! and layout tasks. +//! and layout tasks. It's in charge of processing events for all same-origin pages in a frame +//! tree, and manages the entire lifetime of pages in the frame tree from initial request to +//! teardown. +//! +//! Page loads follow a two-step process. When a request for a new page load is received, the +//! network request is initiated and the relevant data pertaining to the new page is stashed. +//! While the non-blocking request is ongoing, the script task is free to process further events, +//! noting when they pertain to ongoing loads (such as resizes/viewport adjustments). When the +//! initial response is received for an ongoing load, the second phase starts - the frame tree +//! entry is created, along with the Window and Document objects, and the appropriate parser +//! takes over the response body. Once parsing is complete, the document lifecycle for loading +//! a page runs its course and the script task returns to processing events in the main event +//! loop. #![allow(unsafe_blocks)] @@ -23,7 +35,7 @@ use dom::element::{Element, AttributeHandlers}; use dom::event::{Event, EventHelpers}; use dom::uievent::UIEvent; use dom::eventtarget::EventTarget; -use dom::node::{self, Node, NodeHelpers, NodeDamage}; +use dom::node::{self, Node, NodeHelpers, NodeDamage, window_from_node}; use dom::window::{Window, WindowHelpers, ScriptHelpers}; use dom::worker::{Worker, TrustedWorkerAddress}; use parse::html::{HTMLInput, parse_html}; @@ -49,19 +61,20 @@ use msg::constellation_msg::{LoadData, PipelineId, SubpageId}; use msg::constellation_msg::{Failure, Msg, WindowSizeData, PipelineExitType}; use msg::constellation_msg::Msg as ConstellationMsg; use net::image_cache_task::ImageCacheTask; -use net::resource_task::{ResourceTask, ControlMsg}; +use net::resource_task::{ResourceTask, ControlMsg, LoadResponse}; use net::resource_task::LoadData as NetLoadData; use net::storage_task::StorageTask; use string_cache::Atom; use util::geometry::to_frac_px; use util::smallvec::SmallVec; use util::str::DOMString; -use util::task::spawn_named_with_send_on_failure; +use util::task::{spawn_named, spawn_named_with_send_on_failure}; use util::task_state; +use geom::Rect; use geom::point::Point2D; use hyper::header::{LastModified, Headers}; -use js::jsapi::{JS_SetWrapObjectCallbacks, JS_SetGCZeal, JS_DEFAULT_ZEAL_FREQ, JS_GC}; +use js::jsapi::{JS_SetWrapObjectCallbacks, JS_SetGCZeal, JS_DEFAULT_ZEAL_FREQ}; use js::jsapi::{JSContext, JSRuntime, JSObject}; use js::jsapi::{JS_SetGCParameter, JSGC_MAX_BYTES}; use js::jsapi::{JS_SetGCCallback, JSGCStatus, JSGC_BEGIN, JSGC_END}; @@ -73,7 +86,6 @@ use libc; use std::any::Any; use std::borrow::ToOwned; use std::cell::Cell; -use std::mem::replace; use std::num::ToPrimitive; use std::rc::Rc; use std::result::Result; @@ -83,6 +95,43 @@ use time::Tm; thread_local!(pub static STACK_ROOTS: Cell> = Cell::new(None)); +/// A document load that is in the process of fetching the requested resource. Contains +/// data that will need to be present when the document and frame tree entry are created, +/// but is only easily available at initiation of the load and on a push basis (so some +/// data will be updated according to future resize events, viewport changes, etc.) +struct InProgressLoad { + /// The pipeline which requested this load. + pipeline_id: PipelineId, + /// The parent pipeline and child subpage associated with this load, if any. + subpage_id: Option<(PipelineId, SubpageId)>, + /// The current window size associated with this pipeline. + window_size: WindowSizeData, + /// Channel to the layout task associated with this pipeline. + layout_chan: LayoutChan, + /// The current viewport clipping rectangle applying to this pipelie, if any. + clip_rect: Option>, + /// The requested URL of the load. + url: Url, +} + +impl InProgressLoad { + /// Create a new InProgressLoad object. + fn new(id: PipelineId, + subpage_id: Option<(PipelineId, SubpageId)>, + layout_chan: LayoutChan, + window_size: WindowSizeData, + url: Url) -> InProgressLoad { + InProgressLoad { + pipeline_id: id, + subpage_id: subpage_id, + layout_chan: layout_chan, + window_size: window_size, + clip_rect: None, + url: url, + } + } +} + #[derive(Copy)] pub enum TimerSource { FromWindow(PipelineId), @@ -118,6 +167,8 @@ pub enum ScriptMsg { RunnableMsg(Box), /// A DOM object's last pinned reference was removed (dispatched to all tasks). RefcountCleanup(TrustedReference), + /// The final network response for a page has arrived. + PageFetchComplete(PipelineId, Option, LoadResponse), } /// A cloneable interface for communicating with an event loop. @@ -175,11 +226,15 @@ impl Drop for StackRootTLS { /// FIXME: Rename to `Page`, following WebKit? pub struct ScriptTask { /// A handle to the information pertaining to page layout - page: DOMRefCell>, + page: DOMRefCell>>, + /// A list of data pertaining to loads that have not yet received a network response + incomplete_loads: DOMRefCell>, /// A handle to the image cache task. image_cache_task: ImageCacheTask, /// A handle to the resource task. resource_task: ResourceTask, + /// A handle to the storage task. + storage_task: StorageTask, /// The port on which the script task receives messages (load URL, exit, etc.) port: Receiver, @@ -242,7 +297,8 @@ impl<'a> Drop for ScriptMemoryFailsafe<'a> { unsafe { let page = owner.page.borrow_for_script_deallocation(); for page in page.iter() { - *page.unsafe_mut_js_info() = None; + let window = page.window().root(); + window.r().clear_js_context_for_script_deallocation(); } *owner.js_context.borrow_for_script_deallocation() = None; } @@ -274,15 +330,14 @@ impl ScriptTaskFactory for ScriptTask { storage_task: StorageTask, image_cache_task: ImageCacheTask, devtools_chan: Option, - window_size: WindowSizeData) + window_size: WindowSizeData, + load_data: LoadData) where C: ScriptListener + Send + 'static { let ConstellationChan(const_chan) = constellation_chan.clone(); let (script_chan, script_port) = channel(); let layout_chan = LayoutChan(layout_chan.sender()); spawn_named_with_send_on_failure("ScriptTask", task_state::SCRIPT, move || { - let script_task = ScriptTask::new(id, - box compositor as Box, - layout_chan, + let script_task = ScriptTask::new(box compositor as Box, script_port, NonWorkerScriptChan(script_chan), control_chan, @@ -291,9 +346,13 @@ impl ScriptTaskFactory for ScriptTask { resource_task, storage_task, image_cache_task, - devtools_chan, - window_size); + devtools_chan); let mut failsafe = ScriptMemoryFailsafe::new(&script_task); + + let new_load = InProgressLoad::new(id, None, layout_chan, window_size, + load_data.url.clone()); + script_task.start_page_load(new_load, load_data); + script_task.start(); // This must always be the very last operation performed before the task completes @@ -312,9 +371,7 @@ unsafe extern "C" fn debug_gc_callback(_rt: *mut JSRuntime, status: JSGCStatus) impl ScriptTask { /// Creates a new script task. - pub fn new(id: PipelineId, - compositor: Box, - layout_chan: LayoutChan, + pub fn new(compositor: Box, port: Receiver, chan: NonWorkerScriptChan, control_chan: ScriptControlChan, @@ -323,8 +380,7 @@ impl ScriptTask { resource_task: ResourceTask, storage_task: StorageTask, img_cache_task: ImageCacheTask, - devtools_chan: Option, - window_size: WindowSizeData) + devtools_chan: Option) -> ScriptTask { let (js_runtime, js_context) = ScriptTask::new_rt_and_cx(); let wrap_for_same_compartment = wrap_for_same_compartment as @@ -348,19 +404,14 @@ impl ScriptTask { Some(pre_wrap)); } - let page = Page::new(id, None, layout_chan, window_size, - resource_task.clone(), - storage_task, - constellation_chan.clone(), - js_context.clone(), - devtools_chan.clone()); - let (devtools_sender, devtools_receiver) = channel(); ScriptTask { - page: DOMRefCell::new(Rc::new(page)), + page: DOMRefCell::new(None), + incomplete_loads: DOMRefCell::new(vec!()), image_cache_task: img_cache_task, resource_task: resource_task, + storage_task: storage_task, port: port, chan: chan, @@ -417,6 +468,11 @@ impl ScriptTask { (js_runtime, js_context) } + // Return the root page in the frame tree. Panics if it doesn't exist. + fn root_page(&self) -> Rc { + self.page.borrow().as_ref().unwrap().clone() + } + pub fn get_cx(&self) -> *mut JSContext { (**self.js_context.borrow().as_ref().unwrap()).ptr } @@ -439,17 +495,18 @@ impl ScriptTask { let mut resizes = vec!(); { - let page = self.page.borrow_mut(); - for page in page.iter() { - // Only process a resize if layout is idle. - let layout_join_port = page.layout_join_port.borrow(); - if layout_join_port.is_none() { - let mut resize_event = page.resize_event.get(); - match resize_event.take() { - Some(size) => resizes.push((page.id, size)), - None => () + let page = self.page.borrow(); + if let Some(page) = page.as_ref() { + for page in page.iter() { + // Only process a resize if layout is idle. + let window = page.window().root(); + if window.r().layout_is_idle() { + let resize_event = window.r().steal_resize_event(); + match resize_event { + Some(size) => resizes.push((window.r().pipeline(), size)), + None => () + } } - page.resize_event.set(None); } } } @@ -502,17 +559,10 @@ impl ScriptTask { self.handle_new_layout(new_layout_info); } MixedMessage::FromConstellation(ConstellationControlMsg::Resize(id, size)) => { - let page = self.page.borrow_mut(); - let page = page.find(id).expect("resize sent to nonexistent pipeline"); - page.resize_event.set(Some(size)); + self.handle_resize(id, size); } MixedMessage::FromConstellation(ConstellationControlMsg::Viewport(id, rect)) => { - let page = self.page.borrow_mut(); - let inner_page = page.find(id).expect("Page rect message sent to nonexistent pipeline"); - if inner_page.set_page_clip_rect_with_new_viewport(rect) { - let page = get_page(&*page, id); - self.force_reflow(&*page); - } + self.handle_viewport(id, rect); } _ => { sequential.push(event); @@ -555,8 +605,8 @@ impl ScriptTask { match msg { ConstellationControlMsg::AttachLayout(_) => panic!("should have handled AttachLayout already"), - ConstellationControlMsg::Load(id, parent, load_data) => - self.load(id, parent, load_data), + ConstellationControlMsg::Activate(id) => + self.handle_activate(id), ConstellationControlMsg::SendEvent(id, event) => self.handle_event(id, event), ConstellationControlMsg::ReflowComplete(id, reflow_id) => @@ -598,98 +648,144 @@ impl ScriptTask { runnable.handler(), ScriptMsg::RefcountCleanup(addr) => LiveDOMReferences::cleanup(self.get_cx(), addr), + ScriptMsg::PageFetchComplete(id, subpage, response) => + self.handle_page_fetch_complete(id, subpage, response), } } fn handle_msg_from_devtools(&self, msg: DevtoolScriptControlMsg) { + let page = self.root_page(); match msg { DevtoolScriptControlMsg::EvaluateJS(id, s, reply) => - devtools::handle_evaluate_js(&*self.page.borrow(), id, s, reply), + devtools::handle_evaluate_js(&page, id, s, reply), DevtoolScriptControlMsg::GetRootNode(id, reply) => - devtools::handle_get_root_node(&*self.page.borrow(), id, reply), + devtools::handle_get_root_node(&page, id, reply), DevtoolScriptControlMsg::GetDocumentElement(id, reply) => - devtools::handle_get_document_element(&*self.page.borrow(), id, reply), + devtools::handle_get_document_element(&page, id, reply), DevtoolScriptControlMsg::GetChildren(id, node_id, reply) => - devtools::handle_get_children(&*self.page.borrow(), id, node_id, reply), + devtools::handle_get_children(&page, id, node_id, reply), DevtoolScriptControlMsg::GetLayout(id, node_id, reply) => - devtools::handle_get_layout(&*self.page.borrow(), id, node_id, reply), + devtools::handle_get_layout(&page, id, node_id, reply), DevtoolScriptControlMsg::ModifyAttribute(id, node_id, modifications) => - devtools::handle_modify_attribute(&*self.page.borrow(), id, node_id, modifications), + devtools::handle_modify_attribute(&page, id, node_id, modifications), DevtoolScriptControlMsg::WantsLiveNotifications(pipeline_id, to_send) => - devtools::handle_wants_live_notifications(&*self.page.borrow(), pipeline_id, to_send), + devtools::handle_wants_live_notifications(&page, pipeline_id, to_send), } } + fn handle_resize(&self, id: PipelineId, size: WindowSizeData) { + let page = self.page.borrow(); + if let Some(ref page) = page.as_ref() { + if let Some(ref page) = page.find(id) { + let window = page.window().root(); + window.r().set_resize_event(size); + return; + } + } + let mut loads = self.incomplete_loads.borrow_mut(); + if let Some(ref mut load) = loads.iter_mut().find(|load| load.pipeline_id == id) { + load.window_size = size; + return; + } + panic!("resize sent to nonexistent pipeline"); + } + + fn handle_viewport(&self, id: PipelineId, rect: Rect) { + let page = self.page.borrow(); + if let Some(page) = page.as_ref() { + if let Some(ref inner_page) = page.find(id) { + let window = inner_page.window().root(); + if window.r().set_page_clip_rect_with_new_viewport(rect) { + let page = get_page(page, id); + self.force_reflow(&*page); + } + return; + } + } + let mut loads = self.incomplete_loads.borrow_mut(); + if let Some(ref mut load) = loads.iter_mut().find(|load| load.pipeline_id == id) { + load.clip_rect = Some(rect); + return; + } + panic!("Page rect message sent to nonexistent pipeline"); + } + + /// Handle a request to load a page in a new child frame of an existing page. fn handle_new_layout(&self, new_layout_info: NewLayoutInfo) { let NewLayoutInfo { old_pipeline_id, new_pipeline_id, subpage_id, - layout_chan + layout_chan, + load_data, } = new_layout_info; - let page = self.page.borrow_mut(); + let page = self.root_page(); let parent_page = page.find(old_pipeline_id).expect("ScriptTask: received a layout whose parent has a PipelineId which does not correspond to a pipeline in the script task's page tree. This is a bug."); - let new_page = { - let window_size = parent_page.window_size.get(); - Page::new(new_pipeline_id, Some(subpage_id), - LayoutChan(layout_chan.downcast_ref::>().unwrap().clone()), - window_size, - parent_page.resource_task.clone(), - parent_page.storage_task.clone(), - self.constellation_chan.clone(), - self.js_context.borrow().as_ref().unwrap().clone(), - self.devtools_chan.clone()) - }; - parent_page.children.borrow_mut().push(Rc::new(new_page)); + + let parent_window = parent_page.window().root(); + let chan = layout_chan.downcast_ref::>().unwrap(); + let layout_chan = LayoutChan(chan.clone()); + // Kick off the fetch for the new resource. + let new_load = InProgressLoad::new(new_pipeline_id, Some((old_pipeline_id, subpage_id)), + layout_chan, parent_window.r().window_size(), + load_data.url.clone()); + self.start_page_load(new_load, load_data); } /// Handles a timer that fired. fn handle_fire_timer_msg(&self, id: PipelineId, timer_id: TimerId) { - let page = self.page.borrow_mut(); + let page = self.root_page(); let page = page.find(id).expect("ScriptTask: received fire timer msg for a pipeline ID not associated with this script task. This is a bug."); - let frame = page.frame(); - let window = frame.as_ref().unwrap().window.root(); + let window = page.window().root(); window.r().handle_fire_timer(timer_id); } /// Handles freeze message fn handle_freeze_msg(&self, id: PipelineId) { - let page = self.page.borrow_mut(); + let page = self.root_page(); let page = page.find(id).expect("ScriptTask: received freeze msg for a pipeline ID not associated with this script task. This is a bug."); - let frame = page.frame(); - let window = frame.as_ref().unwrap().window.root(); + let window = page.window().root(); window.r().freeze(); } /// Handles thaw message fn handle_thaw_msg(&self, id: PipelineId) { - let page = self.page.borrow_mut(); + let page = self.root_page(); let page = page.find(id).expect("ScriptTask: received thaw msg for a pipeline ID not associated with this script task. This is a bug."); - let frame = page.frame(); - let window = frame.as_ref().unwrap().window.root(); + let window = page.window().root(); window.r().thaw(); } + /// Handle a request to make a previously-created pipeline active. + //TODO: unsuspend JS and timers, etc. when we support such things. + fn handle_activate(&self, pipeline_id: PipelineId) { + // We should only get this message when moving in history, so all pages requested + // should exist. + let page = self.root_page().find(pipeline_id).unwrap(); + + let needed_reflow = page.set_reflow_status(false); + if needed_reflow { + self.force_reflow(&*page); + } + } + /// Handles a notification that reflow completed. fn handle_reflow_complete_msg(&self, pipeline_id: PipelineId, reflow_id: uint) { debug!("Script: Reflow {:?} complete for {:?}", reflow_id, pipeline_id); - let page = self.page.borrow_mut(); + let page = self.root_page(); let page = page.find(pipeline_id).expect( "ScriptTask: received a load message for a layout channel that is not associated \ with this script task. This is a bug."); - let last_reflow_id = page.last_reflow_id.get(); - if last_reflow_id == reflow_id { - let mut layout_join_port = page.layout_join_port.borrow_mut(); - *layout_join_port = None; - } + let window = page.window().root(); + window.r().handle_reflow_complete_msg(reflow_id); - let doc = page.frame().as_ref().unwrap().document.root(); + let doc = page.document().root(); let html_element = doc.r().GetDocumentElement().root(); let reftest_wait = html_element.r().map_or(false, |elem| elem.has_class(&Atom::from_slice("reftest-wait"))); @@ -700,14 +796,12 @@ impl ScriptTask { /// Window was resized, but this script was not active, so don't reflow yet fn handle_resize_inactive_msg(&self, id: PipelineId, new_size: WindowSizeData) { - let page = self.page.borrow_mut(); + let page = self.root_page(); let page = page.find(id).expect("Received resize message for PipelineId not associated with a page in the page tree. This is a bug."); - page.window_size.set(new_size); - match &mut *page.mut_url() { - &mut Some((_, ref mut needs_reflow)) => *needs_reflow = true, - &mut None => (), - } + let window = page.window().root(); + window.r().set_window_size(new_size); + page.set_reflow_status(true); } /// We have gotten a window.close from script, which we pass on to the compositor. @@ -724,182 +818,199 @@ impl ScriptTask { self.compositor.borrow_mut().close(); } + /// We have received notification that the response associated with a load has completed. + /// Kick off the document and frame tree creation process using the result. + fn handle_page_fetch_complete(&self, id: PipelineId, subpage: Option, + response: LoadResponse) { + // Any notification received should refer to an existing, in-progress load that is tracked. + let idx = self.incomplete_loads.borrow().iter().position(|&:load| { + load.pipeline_id == id && load.subpage_id.map(|sub| sub.1) == subpage + }).unwrap(); + let load = self.incomplete_loads.borrow_mut().remove(idx); + self.load(response, load); + } + /// Handles a request for the window title. fn handle_get_title_msg(&self, pipeline_id: PipelineId) { - get_page(&*self.page.borrow(), pipeline_id).send_title_to_compositor(); + let page = get_page(&self.root_page(), pipeline_id); + let document = page.document().root(); + document.r().send_title_to_compositor(); } /// Handles a request to exit the script task and shut down layout. /// Returns true if the script task should shut down and false otherwise. fn handle_exit_pipeline_msg(&self, id: PipelineId, exit_type: PipelineExitType) -> bool { // If root is being exited, shut down all pages - let page = self.page.borrow_mut(); - if page.id == id { + let page = self.root_page(); + let window = page.window().root(); + if window.r().pipeline() == id { debug!("shutting down layout for root page {:?}", id); + // To ensure the elements of the DOM tree remain usable (such as the window global), + // don't free the JS context until all interactions with it are finished. + shut_down_layout(&page, exit_type); *self.js_context.borrow_mut() = None; - shut_down_layout(&*page, (*self.js_runtime).ptr, exit_type); return true } // otherwise find just the matching page and exit all sub-pages - match page.find(id) { - Some(ref mut page) => { - shut_down_layout(&*page, (*self.js_runtime).ptr, exit_type); - page.remove(id); - false - } - // TODO(tkuehn): pipeline closing is currently duplicated across - // script and constellation, which can cause this to happen. Constellation - // needs to be smarter about exiting pipelines. - None => false, + if let Some(ref mut child_page) = page.remove(id) { + shut_down_layout(&*child_page, exit_type); } - + return false; } /// The entry point to document loading. Defines bindings, sets up the window and document /// objects, parses HTML and CSS, and kicks off initial layout. - fn load(&self, pipeline_id: PipelineId, - parent: Option<(PipelineId, SubpageId)>, load_data: LoadData) { - let url = load_data.url.clone(); - debug!("ScriptTask: loading {} on page {:?}", url.serialize(), pipeline_id); + fn load(&self, response: LoadResponse, incomplete: InProgressLoad) { + let final_url = response.metadata.final_url.clone(); + debug!("ScriptTask: loading {} on page {:?}", incomplete.url.serialize(), incomplete.pipeline_id); - let borrowed_page = self.page.borrow_mut(); + // We should either be initializing a root page or loading a child page of an + // existing one. + let root_page_exists = self.page.borrow().is_some(); + assert!(incomplete.subpage_id.is_none() || root_page_exists); - let frame_element = parent.and_then(|(parent_id, subpage_id)| { - // In the case a parent id exists but the matching page - // cannot be found, this means the page exists in a different - // script task (due to origin) so it shouldn't be returned. - // TODO: window.parent will continue to return self in that - // case, which is wrong. We should be returning an object that - // denies access to most properties (per - // https://github.com/servo/servo/issues/3939#issuecomment-62287025). - borrowed_page.find(parent_id).and_then(|page| { - let doc = page.frame().as_ref().unwrap().document.root(); - let doc: JSRef = NodeCast::from_ref(doc.r()); + let frame_element = incomplete.subpage_id.and_then(|(parent_id, subpage_id)| { + let borrowed_page = self.root_page(); + // In the case a parent id exists but the matching page + // cannot be found, this means the page exists in a different + // script task (due to origin) so it shouldn't be returned. + // TODO: window.parent will continue to return self in that + // case, which is wrong. We should be returning an object that + // denies access to most properties (per + // https://github.com/servo/servo/issues/3939#issuecomment-62287025). + borrowed_page.find(parent_id).and_then(|page| { + let doc = page.document().root(); + let doc: JSRef = NodeCast::from_ref(doc.r()); - doc.traverse_preorder() - .filter_map(HTMLIFrameElementCast::to_ref) - .find(|node| node.subpage_id() == Some(subpage_id)) - .map(ElementCast::from_ref) - .map(Temporary::from_rooted) - }) + doc.traverse_preorder() + .filter_map(HTMLIFrameElementCast::to_ref) + .find(|node| node.subpage_id() == Some(subpage_id)) + .map(ElementCast::from_ref) + .map(Temporary::from_rooted) + }) }).root(); - let page = borrowed_page.find(pipeline_id).expect("ScriptTask: received a load - message for a layout channel that is not associated with this script task. This - is a bug."); - - // Are we reloading? - let reloading = match *page.url() { - Some((ref loaded, _)) => *loaded == url, - _ => false, - }; - if reloading { - // Pull out the `needs_reflow` flag explicitly because `reflow` can ask for the page's - // URL, and we can't be holding a borrow on that URL (#4402). - let needed_reflow = match &mut *page.mut_url() { - &mut Some((_, ref mut needs_reflow)) => replace(needs_reflow, false), - _ => panic!("can't reload a page with no URL!") - }; - if needed_reflow { - self.force_reflow(&*page); - } - return - } - let last_url = replace(&mut *page.mut_url(), None).map(|(last_url, _)| last_url); - - let is_javascript = url.scheme.as_slice() == "javascript"; - - self.compositor.borrow_mut().set_ready_state(pipeline_id, Loading); - - let (mut parser_input, final_url, last_modified) = if !is_javascript { - // Wait for the LoadResponse so that the parser knows the final URL. - let (input_chan, input_port) = channel(); - self.resource_task.send(ControlMsg::Load(NetLoadData { - url: url, - method: load_data.method, - headers: Headers::new(), - preserved_headers: load_data.headers, - data: load_data.data, - cors: None, - consumer: input_chan, - })).unwrap(); - - let load_response = input_port.recv().unwrap(); - - let last_modified = load_response.metadata.headers.as_ref().and_then(|headers| { - headers.get().map(|&LastModified(ref tm)| tm.clone()) - }); - - let final_url = load_response.metadata.final_url.clone(); - - (Some(HTMLInput::InputUrl(load_response)), final_url, last_modified) - } else { - let doc_url = last_url.unwrap_or_else(|| { - Url::parse("about:blank").unwrap() - }); - (None, doc_url, None) - }; - - // Store the final URL before we start parsing, so that DOM routines - // (e.g. HTMLImageElement::update_image) can resolve relative URLs - // correctly. - { - *page.mut_url() = Some((final_url.clone(), true)); - } + self.compositor.borrow_mut().set_ready_state(incomplete.pipeline_id, Loading); let cx = self.js_context.borrow(); let cx = cx.as_ref().unwrap(); + + // Create a new frame tree entry. + let page = Rc::new(Page::new(incomplete.pipeline_id, incomplete.subpage_id.map(|p| p.1), + final_url.clone())); + if !root_page_exists { + // We have a new root frame tree. + *self.page.borrow_mut() = Some(page.clone()); + } else if let Some((parent, _)) = incomplete.subpage_id { + // We have a new child frame. + let parent_page = self.root_page(); + parent_page.find(parent).expect("received load for subpage with missing parent"); + parent_page.children.borrow_mut().push(page.clone()); + } + + enum PageToRemove { + Root, + Child(PipelineId), + } + struct AutoPageRemover<'a> { + page: PageToRemove, + script_task: &'a ScriptTask, + neutered: bool, + } + impl<'a> AutoPageRemover<'a> { + fn new(script_task: &'a ScriptTask, page: PageToRemove) -> AutoPageRemover<'a> { + AutoPageRemover { + page: page, + script_task: script_task, + neutered: false, + } + } + + fn neuter(&mut self) { + self.neutered = true; + } + } + #[unsafe_destructor] + impl<'a> Drop for AutoPageRemover<'a> { + fn drop(&mut self) { + if !self.neutered { + match self.page { + PageToRemove::Root => *self.script_task.page.borrow_mut() = None, + PageToRemove::Child(id) => { + let _ = self.script_task.root_page().remove(id); + } + } + } + } + } + + let page_to_remove = if !root_page_exists { + PageToRemove::Root + } else { + PageToRemove::Child(incomplete.pipeline_id) + }; + let mut page_remover = AutoPageRemover::new(self, page_to_remove); + // Create the window and document objects. - let window = Window::new(cx.ptr, + let window = Window::new(cx.clone(), page.clone(), self.chan.clone(), self.control_chan.clone(), self.compositor.borrow_mut().dup(), - self.image_cache_task.clone()).root(); + self.image_cache_task.clone(), + self.resource_task.clone(), + self.storage_task.clone(), + self.devtools_chan.clone(), + self.constellation_chan.clone(), + incomplete.layout_chan, + incomplete.pipeline_id, + incomplete.subpage_id.map(|s| s.1), + incomplete.window_size).root(); let document = Document::new(window.r(), Some(final_url.clone()), IsHTMLDocument::HTMLDocument, None, DocumentSource::FromParser).root(); + + window.r().init_browser_context(document.r(), frame_element.r()); + + let last_modified = response.metadata.headers.as_ref().and_then(|headers| { + headers.get().map(|&LastModified(ref tm)| tm.clone()) + }); if let Some(tm) = last_modified { document.r().set_last_modified(dom_last_modified(&tm)); } - window.r().init_browser_context(document.r(), frame_element.r()); + // Create the root frame + page.set_frame(Some(Frame { + document: JS::from_rooted(document.r()), + window: JS::from_rooted(window.r()), + })); - { - // Create the root frame. - let mut frame = page.mut_frame(); - *frame = Some(Frame { - document: JS::from_rooted(document.r()), - window: JS::from_rooted(window.r()), - }); - } - - if is_javascript { - let evalstr = load_data.url.non_relative_scheme_data().unwrap(); + let is_javascript = incomplete.url.scheme.as_slice() == "javascript"; + let parse_input = if is_javascript { + let evalstr = incomplete.url.non_relative_scheme_data().unwrap(); let jsval = window.r().evaluate_js_on_global_with_result(evalstr); let strval = FromJSValConvertible::from_jsval(self.get_cx(), jsval, StringificationBehavior::Empty); - parser_input = Some(HTMLInput::InputString(strval.unwrap_or("".to_owned()))); + HTMLInput::InputString(strval.unwrap_or("".to_owned())) + } else { + HTMLInput::InputUrl(response) }; - parse_html(document.r(), parser_input.unwrap(), &final_url); + parse_html(document.r(), parse_input, &final_url); document.r().set_ready_state(DocumentReadyState::Interactive); - self.compositor.borrow_mut().set_ready_state(pipeline_id, PerformingLayout); + self.compositor.borrow_mut().set_ready_state(incomplete.pipeline_id, PerformingLayout); // Kick off the initial reflow of the page. debug!("kicking off initial reflow of {:?}", final_url); document.r().content_changed(NodeCast::from_ref(document.r()), NodeDamage::OtherNodeDamage); - window.r().flush_layout(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); + window.r().reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); - { - // No more reflow required - let mut page_url = page.mut_url(); - *page_url = Some((final_url.clone(), false)); - } + // No more reflow required + page.set_reflow_status(false); // https://html.spec.whatwg.org/multipage/#the-end step 4 let addr: Trusted = Trusted::new(self.get_cx(), document.r(), self.chan.clone()); @@ -914,7 +1025,7 @@ impl ScriptTask { let handler = Box::new(DocumentProgressHandler::new(addr, DocumentProgressTask::Load)); self.chan.send(ScriptMsg::RunnableMsg(handler)).unwrap(); - *page.fragment_name.borrow_mut() = final_url.fragment.clone(); + window.r().set_fragment_name(final_url.fragment.clone()); let ConstellationChan(ref chan) = self.constellation_chan; chan.send(ConstellationMsg::LoadComplete).unwrap(); @@ -927,11 +1038,13 @@ impl ScriptTask { title: document.r().Title(), url: final_url }; - chan.send(DevtoolsControlMsg::NewGlobal(pipeline_id, + chan.send(DevtoolsControlMsg::NewGlobal(incomplete.pipeline_id, self.devtools_sender.clone(), page_info)).unwrap(); } } + + page_remover.neuter(); } fn scroll_fragment_point(&self, pipeline_id: PipelineId, node: JSRef) { @@ -948,11 +1061,10 @@ impl ScriptTask { /// Reflows non-incrementally. fn force_reflow(&self, page: &Page) { - page.dirty_all_nodes(); - page.reflow(ReflowGoal::ForDisplay, - self.control_chan.clone(), - &mut **self.compositor.borrow_mut(), - ReflowQueryType::NoQuery); + let document = page.document().root(); + document.r().dirty_all_nodes(); + let window = window_from_node(document.r()).root(); + window.r().reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery); } /// This is the main entry point for receiving and dispatching DOM events. @@ -978,9 +1090,8 @@ impl ScriptTask { for node in nodes.iter() { let node_to_dirty = node::from_untrusted_node_address(self.js_runtime.ptr, *node).root(); - let page = get_page(&*self.page.borrow(), pipeline_id); - let frame = page.frame(); - let document = frame.as_ref().unwrap().document.root(); + let page = get_page(&self.root_page(), pipeline_id); + let document = page.document().root(); document.r().content_changed(node_to_dirty.r(), NodeDamage::OtherNodeDamage); } @@ -989,18 +1100,16 @@ impl ScriptTask { } ClickEvent(_button, point) => { - let page = get_page(&*self.page.borrow(), pipeline_id); - let frame = page.frame(); - let document = frame.as_ref().unwrap().document.root(); + let page = get_page(&self.root_page(), pipeline_id); + let document = page.document().root(); document.r().handle_click_event(self.js_runtime.ptr, _button, point); } MouseDownEvent(..) => {} MouseUpEvent(..) => {} MouseMoveEvent(point) => { - let page = get_page(&*self.page.borrow(), pipeline_id); - let frame = page.frame(); - let document = frame.as_ref().unwrap().document.root(); + let page = get_page(&self.root_page(), pipeline_id); + let document = page.document().root(); let mouse_over_targets = &mut *self.mouse_over_targets.borrow_mut(); if document.r().handle_mouse_move_event(self.js_runtime.ptr, point, mouse_over_targets) { @@ -1009,9 +1118,8 @@ impl ScriptTask { } KeyEvent(key, state, modifiers) => { - let page = get_page(&*self.page.borrow(), pipeline_id); - let frame = page.frame(); - let document = frame.as_ref().unwrap().document.root(); + let page = get_page(&self.root_page(), pipeline_id); + let document = page.document().root(); document.r().dispatch_key_event( key, state, modifiers, &mut *self.compositor.borrow_mut()); } @@ -1028,8 +1136,9 @@ impl ScriptTask { /// The entry point for content to notify that a fragment url has been requested /// for the given pipeline. fn trigger_fragment(&self, pipeline_id: PipelineId, fragment: String) { - let page = get_page(&*self.page.borrow(), pipeline_id); - match page.find_fragment_node(fragment).root() { + let page = get_page(&self.root_page(), pipeline_id); + let document = page.document().root(); + match document.r().find_fragment_node(fragment).root() { Some(node) => { self.scroll_fragment_point(pipeline_id, node.r()); } @@ -1039,87 +1148,97 @@ impl ScriptTask { fn handle_resize_event(&self, pipeline_id: PipelineId, new_size: WindowSizeData) { - let window = { - let page = get_page(&*self.page.borrow(), pipeline_id); - page.window_size.set(new_size); + let page = get_page(&self.root_page(), pipeline_id); + let window = page.window().root(); + window.r().set_window_size(new_size); + self.force_reflow(&*page); - let frame = page.frame(); - if frame.is_some() { - self.force_reflow(&*page); - } - - let fragment_node = - page.fragment_name - .borrow_mut() - .take() - .and_then(|name| page.find_fragment_node(name)) - .root(); - match fragment_node { - Some(node) => self.scroll_fragment_point(pipeline_id, node.r()), - None => {} - } - - frame.as_ref().map(|frame| Temporary::new(frame.window.clone())) - }; - - match window.root() { - Some(window) => { - // http://dev.w3.org/csswg/cssom-view/#resizing-viewports - // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#event-type-resize - let uievent = UIEvent::new(window.r(), - "resize".to_owned(), false, - false, Some(window.r()), - 0i32).root(); - let event: JSRef = EventCast::from_ref(uievent.r()); - - let wintarget: JSRef = EventTargetCast::from_ref(window.r()); - event.fire(wintarget); - } - None => () + let document = page.document().root(); + let fragment_node = window.r().steal_fragment_name() + .and_then(|name| document.r().find_fragment_node(name)) + .root(); + match fragment_node { + Some(node) => self.scroll_fragment_point(pipeline_id, node.r()), + None => {} } + + // http://dev.w3.org/csswg/cssom-view/#resizing-viewports + // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#event-type-resize + let uievent = UIEvent::new(window.r(), + "resize".to_owned(), false, + false, Some(window.r()), + 0i32).root(); + let event: JSRef = EventCast::from_ref(uievent.r()); + + let wintarget: JSRef = EventTargetCast::from_ref(window.r()); + event.fire(wintarget); } fn handle_reflow_event(&self, pipeline_id: PipelineId) { debug!("script got reflow event"); - let page = get_page(&*self.page.borrow(), pipeline_id); - let frame = page.frame(); - if frame.is_some() { - self.force_reflow(&*page); - } + let page = get_page(&self.root_page(), pipeline_id); + self.force_reflow(&*page); + } + + /// Initiate a non-blocking fetch for a specified resource. Stores the InProgressLoad + /// argument until a notification is received that the fetch is complete. + fn start_page_load(&self, incomplete: InProgressLoad, mut load_data: LoadData) { + let id = incomplete.pipeline_id.clone(); + let subpage = incomplete.subpage_id.clone().map(|p| p.1); + + let script_chan = self.chan.clone(); + let resource_task = self.resource_task.clone(); + + spawn_named(format!("fetch for {:?}", load_data.url), move || { + if load_data.url.scheme.as_slice() == "javascript" { + load_data.url = Url::parse("about:blank").unwrap(); + } + + let (input_chan, input_port) = channel(); + resource_task.send(ControlMsg::Load(NetLoadData { + url: load_data.url, + method: load_data.method, + headers: Headers::new(), + preserved_headers: load_data.headers, + data: load_data.data, + cors: None, + consumer: input_chan, + })).unwrap(); + + let load_response = input_port.recv().unwrap(); + script_chan.send(ScriptMsg::PageFetchComplete(id, subpage, load_response)).unwrap(); + }); + + self.incomplete_loads.borrow_mut().push(incomplete); } } /// Shuts down layout for the given page tree. -fn shut_down_layout(page_tree: &Rc, rt: *mut JSRuntime, exit_type: PipelineExitType) { +fn shut_down_layout(page_tree: &Rc, exit_type: PipelineExitType) { + let mut channels = vec!(); + for page in page_tree.iter() { // Tell the layout task to begin shutting down, and wait until it // processed this message. let (response_chan, response_port) = channel(); - let LayoutChan(ref chan) = page.layout_chan; + let window = page.window().root(); + let LayoutChan(chan) = window.r().layout_chan(); if chan.send(layout_interface::Msg::PrepareToExit(response_chan)).is_ok() { - response_port.recv().unwrap(); + channels.push(chan); + response_port.recv().unwrap(); } } - // Remove our references to the DOM objects in this page tree. + // Drop our references to the JSContext and DOM objects. for page in page_tree.iter() { - *page.mut_frame() = None; - } - - // Drop our references to the JSContext, potentially triggering a GC. - for page in page_tree.iter() { - *page.mut_js_info() = None; - } - - // Force a GC to make sure that our DOM reflectors are released before we tell - // layout to exit. - unsafe { - JS_GC(rt); + let window = page.window().root(); + window.r().clear_js_context(); + // Sever the connection between the global and the DOM tree + page.set_frame(None); } // Destroy the layout task. If there were node leaks, layout will now crash safely. - for page in page_tree.iter() { - let LayoutChan(ref chan) = page.layout_chan; + for chan in channels.into_iter() { chan.send(layout_interface::Msg::ExitNow(exit_type)).ok(); } } diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index 7df28d29108..deb5ba30770 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -48,12 +48,13 @@ pub struct NewLayoutInfo { pub new_pipeline_id: PipelineId, pub subpage_id: SubpageId, pub layout_chan: Box, // opaque reference to a LayoutChannel + pub load_data: LoadData, } /// Messages sent from the constellation to the script task pub enum ConstellationControlMsg { - /// Loads a new URL on the specified pipeline. - Load(PipelineId, Option<(PipelineId, SubpageId)>, LoadData), + /// Reactivate an existing pipeline. + Activate(PipelineId), /// Gives a channel and ID to a layout task, as well as the ID of that layout's parent AttachLayout(NewLayoutInfo), /// Window resized. Sends a DOM event eventually, but first we combine events. @@ -111,7 +112,8 @@ pub trait ScriptTaskFactory { storage_task: StorageTask, image_cache_task: ImageCacheTask, devtools_chan: Option, - window_size: WindowSizeData) + window_size: WindowSizeData, + load_data: LoadData) where C: ScriptListener + Send; fn create_layout_channel(_phantom: Option<&mut Self>) -> OpaqueScriptLayoutChannel; fn clone_layout_channel(_phantom: Option<&mut Self>, pair: &OpaqueScriptLayoutChannel) diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index a52a5ec3191..d94dfcade90 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -389,6 +389,7 @@ dependencies = [ "libc 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", "time 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "url 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)", "util 0.0.1", ] diff --git a/components/style/properties.mako.rs b/components/style/properties.mako.rs index 2ff84b2107c..11089b165ce 100644 --- a/components/style/properties.mako.rs +++ b/components/style/properties.mako.rs @@ -676,8 +676,77 @@ pub mod longhands { // CSS 2.1, Section 11 - Visual effects - // FIXME: Implement scrolling for `scroll` and `auto` (#2742). - ${single_keyword("overflow", "visible hidden scroll auto")} + + // FIXME(pcwalton, #2742): Implement scrolling for `scroll` and `auto`. + <%self:single_keyword_computed name="overflow-x" values="visible hidden scroll auto"> + use values::computed::{Context, ToComputedValue}; + + pub fn compute_with_other_overflow_direction(value: SpecifiedValue, + other_direction: SpecifiedValue) + -> computed_value::T { + // CSS-OVERFLOW 3 states "Otherwise, if one cascaded values is one of the scrolling + // values and the other is `visible`, then computed values are the cascaded values with + // `visible` changed to `auto`." + match (value, other_direction) { + (SpecifiedValue::visible, SpecifiedValue::hidden) | + (SpecifiedValue::visible, SpecifiedValue::scroll) | + (SpecifiedValue::visible, SpecifiedValue::auto) => computed_value::T::auto, + _ => value, + } + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value(&self, context: &Context) -> computed_value::T { + compute_with_other_overflow_direction(*self, context.overflow_y.0) + } + } + + + // FIXME(pcwalton, #2742): Implement scrolling for `scroll` and `auto`. + <%self:longhand name="overflow-y"> + use super::overflow_x; + use values::computed::{Context, ToComputedValue}; + + use cssparser::ToCss; + use text_writer::{self, TextWriter}; + + pub use self::computed_value::T as SpecifiedValue; + + impl ToCss for SpecifiedValue { + fn to_css(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { + self.0.to_css(dest) + } + } + + pub mod computed_value { + #[derive(Clone, Copy, PartialEq)] + pub struct T(pub super::super::overflow_x::computed_value::T); + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value(&self, context: &Context) -> computed_value::T { + let computed_value::T(this) = *self; + computed_value::T(overflow_x::compute_with_other_overflow_direction( + this, + context.overflow_x)) + } + } + + pub fn get_initial_value() -> computed_value::T { + computed_value::T(overflow_x::get_initial_value()) + } + + pub fn parse(context: &ParserContext, input: &mut Parser) -> Result { + overflow_x::parse(context, input).map(|value| SpecifiedValue(value)) + } + + ${switch_to_style_struct("InheritedBox")} @@ -1004,6 +1073,126 @@ pub mod longhands { ${single_keyword("background-attachment", "scroll fixed")} + <%self:longhand name="background-size"> + use cssparser::{ToCss, Token}; + use std::ascii::AsciiExt; + use text_writer::{self, TextWriter}; + use values::computed::{Context, ToComputedValue}; + + pub mod computed_value { + use values::computed::LengthOrPercentageOrAuto; + + #[derive(PartialEq, Clone, Debug)] + pub struct ExplicitSize { + pub width: LengthOrPercentageOrAuto, + pub height: LengthOrPercentageOrAuto, + } + + #[derive(PartialEq, Clone, Debug)] + pub enum T { + Explicit(ExplicitSize), + Cover, + Contain, + } + } + + #[derive(Clone, PartialEq, Debug)] + pub struct SpecifiedExplicitSize { + pub width: specified::LengthOrPercentageOrAuto, + pub height: specified::LengthOrPercentageOrAuto, + } + + impl ToCss for SpecifiedExplicitSize { + fn to_css(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { + try!(self.width.to_css(dest)); + try!(dest.write_str(" ")); + self.height.to_css(dest) + } + } + + #[derive(Clone, PartialEq, Debug)] + pub enum SpecifiedValue { + Explicit(SpecifiedExplicitSize), + Cover, + Contain, + } + + impl ToCss for SpecifiedValue { + fn to_css(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { + match *self { + SpecifiedValue::Explicit(ref size) => size.to_css(dest), + SpecifiedValue::Cover => dest.write_str("cover"), + SpecifiedValue::Contain => dest.write_str("contain"), + } + } + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value(&self, context: &computed::Context) -> computed_value::T { + match *self { + SpecifiedValue::Explicit(ref size) => { + computed_value::T::Explicit(computed_value::ExplicitSize { + width: size.width.to_computed_value(context), + height: size.height.to_computed_value(context), + }) + } + SpecifiedValue::Cover => computed_value::T::Cover, + SpecifiedValue::Contain => computed_value::T::Contain, + } + } + } + + #[inline] + pub fn get_initial_value() -> computed_value::T { + computed_value::T::Explicit(computed_value::ExplicitSize { + width: computed::LengthOrPercentageOrAuto::Auto, + height: computed::LengthOrPercentageOrAuto::Auto, + }) + } + + pub fn parse(_: &ParserContext, input: &mut Parser) -> Result { + let width; + if let Ok(value) = input.try(|input| { + match input.next() { + Err(_) => Err(()), + Ok(Token::Ident(ref ident)) if ident.as_slice() + .eq_ignore_ascii_case("cover") => { + Ok(SpecifiedValue::Cover) + } + Ok(Token::Ident(ref ident)) if ident.as_slice() + .eq_ignore_ascii_case("contain") => { + Ok(SpecifiedValue::Contain) + } + Ok(_) => Err(()), + } + }) { + return Ok(value) + } else { + width = try!(specified::LengthOrPercentageOrAuto::parse(input)) + } + + let height; + if let Ok(value) = input.try(|input| { + match input.next() { + Err(_) => Ok(specified::LengthOrPercentageOrAuto::Auto), + Ok(_) => Err(()), + } + }) { + height = value + } else { + height = try!(specified::LengthOrPercentageOrAuto::parse(input)); + } + + Ok(SpecifiedValue::Explicit(SpecifiedExplicitSize { + width: width, + height: height, + })) + } + + ${new_style_struct("Color", is_inherited=True)} <%self:raw_longhand name="color"> @@ -2346,6 +2535,62 @@ pub mod longhands { """normal multiply screen overlay darken lighten color-dodge color-burn hard-light soft-light difference exclusion hue saturation color luminosity""")} + + <%self:longhand name="image-rendering"> + use values::computed::{Context, ToComputedValue}; + + pub mod computed_value { + use cssparser::ToCss; + use text_writer::{self, TextWriter}; + + #[derive(Copy, Clone, Debug, PartialEq)] + pub enum T { + Auto, + CrispEdges, + Pixelated, + } + + impl ToCss for T { + fn to_css(&self, dest: &mut W) -> text_writer::Result where W: TextWriter { + match *self { + T::Auto => dest.write_str("auto"), + T::CrispEdges => dest.write_str("crisp-edges"), + T::Pixelated => dest.write_str("pixelated"), + } + } + } + } + + pub type SpecifiedValue = computed_value::T; + + #[inline] + pub fn get_initial_value() -> computed_value::T { + computed_value::T::Auto + } + + pub fn parse(_: &ParserContext, input: &mut Parser) -> Result { + // According to to CSS-IMAGES-3, `optimizespeed` and `optimizequality` are synonyms for + // `auto`. + match_ignore_ascii_case! { + try!(input.expect_ident()), + "auto" => Ok(computed_value::T::Auto), + "optimizespeed" => Ok(computed_value::T::Auto), + "optimizequality" => Ok(computed_value::T::Auto), + "crisp-edges" => Ok(computed_value::T::CrispEdges), + "pixelated" => Ok(computed_value::T::Pixelated) + _ => Err(()) + } + } + + impl ToComputedValue for SpecifiedValue { + type ComputedValue = computed_value::T; + + #[inline] + fn to_computed_value(&self, _: &Context) -> computed_value::T { + *self + } + } + } @@ -2438,14 +2683,15 @@ pub mod shorthands { // TODO: other background-* properties <%self:shorthand name="background" - sub_properties="background-color background-position background-repeat background-attachment background-image"> - use properties::longhands::{background_color, background_position, background_repeat, - background_attachment, background_image}; + sub_properties="background-color background-position background-repeat background-attachment background-image background-size"> + use properties::longhands::{background_color, background_position, background_repeat}; + use properties::longhands::{background_attachment, background_image, background_size}; let mut color = None; let mut image = None; let mut position = None; let mut repeat = None; + let mut size = None; let mut attachment = None; let mut any = false; @@ -2454,6 +2700,13 @@ pub mod shorthands { if let Ok(value) = input.try(|input| background_position::parse(context, input)) { position = Some(value); any = true; + + // Parse background size, if applicable. + size = input.try(|input| { + try!(input.expect_delim('/')); + background_size::parse(context, input) + }).ok(); + continue } } @@ -2495,6 +2748,7 @@ pub mod shorthands { background_position: position, background_repeat: repeat, background_attachment: attachment, + background_size: size, }) } else { Err(()) @@ -2830,6 +3084,16 @@ pub mod shorthands { _ => Err(()), } + + <%self:shorthand name="overflow" sub_properties="overflow-x overflow-y"> + use properties::longhands::{overflow_x, overflow_y}; + + let overflow = try!(overflow_x::parse(context, input)); + Ok(Longhands { + overflow_x: Some(overflow), + overflow_y: Some(overflow_y::SpecifiedValue(overflow)), + }) + } @@ -3474,6 +3738,8 @@ pub fn cascade(applicable_declarations: &[DeclarationBlock false, } } + PropertyDeclaration::OverflowX(ref value) => { + context.overflow_x = get_specified!(get_box, overflow_x, value); + } + PropertyDeclaration::OverflowY(ref value) => { + context.overflow_y = get_specified!(get_box, overflow_y, value); + } PropertyDeclaration::Float(ref value) => { context.floated = get_specified!(get_box, float, value) != longhands::float::SpecifiedValue::none; diff --git a/components/style/stylesheets.rs b/components/style/stylesheets.rs index c0efee55d8a..1e7e78ac9cc 100644 --- a/components/style/stylesheets.rs +++ b/components/style/stylesheets.rs @@ -435,6 +435,7 @@ fn test_parse_stylesheet() { ], declarations: PropertyDeclarationBlock { normal: Arc::new(vec![ + PropertyDeclaration::BackgroundSize(DeclaredValue::Initial), PropertyDeclaration::BackgroundImage(DeclaredValue::Initial), PropertyDeclaration::BackgroundAttachment(DeclaredValue::Initial), PropertyDeclaration::BackgroundRepeat(DeclaredValue::Initial), diff --git a/components/style/values.rs b/components/style/values.rs index 450e82cfa61..ae1e4bc2600 100644 --- a/components/style/values.rs +++ b/components/style/values.rs @@ -690,6 +690,8 @@ pub mod computed { pub font_size: longhands::font_size::computed_value::T, pub root_font_size: longhands::font_size::computed_value::T, pub display: longhands::display::computed_value::T, + pub overflow_x: longhands::overflow_x::computed_value::T, + pub overflow_y: longhands::overflow_y::computed_value::T, pub positioned: bool, pub floated: bool, pub border_top_present: bool, diff --git a/components/util/memory.rs b/components/util/memory.rs index c309cdbb4ed..6327a6e01f9 100644 --- a/components/util/memory.rs +++ b/components/util/memory.rs @@ -7,6 +7,7 @@ use libc::{c_char,c_int,c_void,size_t}; use std::borrow::ToOwned; use std::ffi::CString; +#[cfg(target_os = "linux")] use std::iter::AdditiveIterator; use std::old_io::timer::sleep; #[cfg(target_os="linux")] diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index c67a240d535..423e2be2246 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -392,6 +392,7 @@ dependencies = [ "libc 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "msg 0.0.1", "time 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "url 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)", "util 0.0.1", ] diff --git a/ports/cef/window.rs b/ports/cef/window.rs index 1b133e14d27..c485792005b 100644 --- a/ports/cef/window.rs +++ b/ports/cef/window.rs @@ -23,7 +23,7 @@ use layers::platform::surface::NativeGraphicsMetadata; use libc::{c_char, c_void}; use msg::constellation_msg::{Key, KeyModifiers}; use msg::compositor_msg::{ReadyState, PaintState}; -use msg::constellation_msg::LoadData; +use std_url::Url; use util::cursor::Cursor; use util::geometry::ScreenPx; use std::cell::RefCell; @@ -286,7 +286,7 @@ impl WindowMethods for Window { } } - fn set_page_load_data(&self, load_data: LoadData) { + fn set_page_url(&self, url: Url) { let browser = self.cef_browser.borrow(); let browser = match *browser { None => return, @@ -294,7 +294,7 @@ impl WindowMethods for Window { }; let frame = browser.get_main_frame(); let frame = frame.downcast(); - *frame.url.borrow_mut() = load_data.url.to_string() + *frame.url.borrow_mut() = url.to_string() } fn handle_key(&self, _: Key, _: KeyModifiers) { diff --git a/ports/glutin/Cargo.toml b/ports/glutin/Cargo.toml index d58ed4a32bd..7c3cb6f13d5 100644 --- a/ports/glutin/Cargo.toml +++ b/ports/glutin/Cargo.toml @@ -43,3 +43,4 @@ git = "https://github.com/servo/rust-egl" time = "0.1.12" bitflags = "*" libc = "*" +url = "*" \ No newline at end of file diff --git a/ports/glutin/lib.rs b/ports/glutin/lib.rs index b644483adb2..476ed457dea 100644 --- a/ports/glutin/lib.rs +++ b/ports/glutin/lib.rs @@ -22,6 +22,7 @@ extern crate msg; extern crate time; extern crate util; extern crate egl; +extern crate url; use compositing::windowing::WindowEvent; use geom::scale_factor::ScaleFactor; diff --git a/ports/glutin/window.rs b/ports/glutin/window.rs index 2256d34b998..bf83b445e6a 100644 --- a/ports/glutin/window.rs +++ b/ports/glutin/window.rs @@ -15,10 +15,10 @@ use layers::platform::surface::NativeGraphicsMetadata; use msg::constellation_msg; use msg::constellation_msg::Key; use msg::compositor_msg::{PaintState, ReadyState}; -use msg::constellation_msg::LoadData; use NestedEventLoopListener; use std::rc::Rc; use std::sync::mpsc::{channel, Sender}; +use url::Url; use util::cursor::Cursor; use util::geometry::ScreenPx; @@ -462,7 +462,7 @@ impl WindowMethods for Window { self.window.set_title(&title); } - fn set_page_load_data(&self, _: LoadData) { + fn set_page_url(&self, _: Url) { } fn load_end(&self) { @@ -657,7 +657,7 @@ impl WindowMethods for Window { fn set_page_title(&self, _: Option) { } - fn set_page_load_data(&self, _: LoadData) { + fn set_page_url(&self, _: Url) { } fn load_end(&self) { diff --git a/ports/gonk/src/main.rs b/ports/gonk/src/main.rs index c074ce632cd..fc53e3b7900 100644 --- a/ports/gonk/src/main.rs +++ b/ports/gonk/src/main.rs @@ -22,6 +22,7 @@ extern crate msg; extern crate gleam; extern crate layers; extern crate egl; +extern crate url; use util::opts; use servo::Browser; diff --git a/ports/gonk/src/window.rs b/ports/gonk/src/window.rs index 818e62c6835..0e8bf8451a1 100644 --- a/ports/gonk/src/window.rs +++ b/ports/gonk/src/window.rs @@ -13,7 +13,6 @@ use layers::platform::surface::NativeGraphicsMetadata; use libc::c_int; use msg::compositor_msg::{ReadyState, PaintState}; use msg::constellation_msg::{Key, KeyModifiers}; -use msg::constellation_msg::LoadData; use std::cell::Cell; use std::sync::mpsc::{channel, Sender, Receiver}; use std::rc::Rc; @@ -22,6 +21,7 @@ use std::mem::size_of; use std::mem::zeroed; use std::ptr; use std::ffi::CString; +use url::Url; use util::cursor::Cursor; use util::geometry::ScreenPx; use gleam::gl; @@ -804,7 +804,7 @@ impl WindowMethods for Window { fn set_page_title(&self, _: Option) { } - fn set_page_load_data(&self, _: LoadData) { + fn set_page_url(&self, _: Url) { } fn load_end(&self) { diff --git a/python/servo/command_base.py b/python/servo/command_base.py index 20ee3fb6527..4bcae97c11b 100644 --- a/python/servo/command_base.py +++ b/python/servo/command_base.py @@ -202,7 +202,7 @@ class CommandBase(object): if self.context.bootstrapped: return - subprocess.check_call(["git", "submodule", "sync"]) + subprocess.check_call(["git", "submodule", "--quiet", "sync", "--recursive"]) submodules = subprocess.check_output(["git", "submodule", "status"]) for line in submodules.split('\n'): components = line.strip().split(' ') diff --git a/tests/ref/2x4.png b/tests/ref/2x4.png new file mode 100644 index 00000000000..7efc762459b Binary files /dev/null and b/tests/ref/2x4.png differ diff --git a/tests/ref/4x2.png b/tests/ref/4x2.png new file mode 100644 index 00000000000..eb0c5cda971 Binary files /dev/null and b/tests/ref/4x2.png differ diff --git a/tests/ref/background_size.png b/tests/ref/background_size.png new file mode 100644 index 00000000000..8d5824940c0 Binary files /dev/null and b/tests/ref/background_size.png differ diff --git a/tests/ref/background_size_a.html b/tests/ref/background_size_a.html new file mode 100644 index 00000000000..aa2df4fb8b2 --- /dev/null +++ b/tests/ref/background_size_a.html @@ -0,0 +1,62 @@ + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + diff --git a/tests/ref/background_size_ref.html b/tests/ref/background_size_ref.html new file mode 100644 index 00000000000..7967b10ae09 --- /dev/null +++ b/tests/ref/background_size_ref.html @@ -0,0 +1,19 @@ + + + + + + + + + + + diff --git a/tests/ref/background_size_shorthand_a.html b/tests/ref/background_size_shorthand_a.html new file mode 100644 index 00000000000..30ce3a9cef2 --- /dev/null +++ b/tests/ref/background_size_shorthand_a.html @@ -0,0 +1,28 @@ + + + + + + + +
 
+
 
+
 
+ + + diff --git a/tests/ref/background_size_shorthand_ref.html b/tests/ref/background_size_shorthand_ref.html new file mode 100644 index 00000000000..48766310e1c --- /dev/null +++ b/tests/ref/background_size_shorthand_ref.html @@ -0,0 +1,23 @@ + + + + + + + +
 
+
 
+
 
+ + + diff --git a/tests/ref/basic.list b/tests/ref/basic.list index 0d83cc16d1f..8dc421956a4 100644 --- a/tests/ref/basic.list +++ b/tests/ref/basic.list @@ -139,6 +139,8 @@ fragment=top != ../html/acid2.html acid2_ref.html == iframe/overflow.html iframe/overflow_ref.html == iframe/positioning_margin.html iframe/positioning_margin_ref.html == iframe/hide_and_show.html iframe/hide_and_show_ref.html +== iframe/hide_layers1.html iframe/hide_layers_ref.html +== iframe/hide_layers2.html iframe/hide_layers_ref.html == floated_generated_content_a.html floated_generated_content_b.html == inline_block_margin_a.html inline_block_margin_ref.html @@ -146,6 +148,7 @@ fragment=top != ../html/acid2.html acid2_ref.html == inline_block_baseline_a.html inline_block_baseline_ref.html == inline_block_parent_width.html inline_block_parent_width_ref.html == inline_block_parent_width_percentage.html inline_block_parent_width_ref.html +== inline_block_overflow.html inline_block_overflow_ref.html == float_table_a.html float_table_ref.html == table_containing_block_a.html table_containing_block_ref.html == link_style_order.html link_style_order_ref.html @@ -157,6 +160,7 @@ fragment=top != ../html/acid2.html acid2_ref.html == img_block_display_a.html img_block_display_ref.html == after_block_iteration.html after_block_iteration_ref.html == inline_block_percentage_height_a.html inline_block_percentage_height_ref.html +== inline_block_min_width.html inline_block_min_width_ref.html == percentage_height_float_a.html percentage_height_float_ref.html == img_block_maxwidth_a.html img_block_maxwidth_ref.html == img_block_maxwidth_b.html img_block_maxwidth_ref.html @@ -255,6 +259,11 @@ fragment=top != ../html/acid2.html acid2_ref.html == canvas_lineto_a.html canvas_lineto_ref.html != text_decoration_smoke_a.html text_decoration_smoke_ref.html == hide_after_create.html hide_after_create_ref.html +== overflow_xy_a.html overflow_xy_ref.html == text_shadow_simple_a.html text_shadow_simple_ref.html == text_shadow_decorations_a.html text_shadow_decorations_ref.html == text_shadow_blur_a.html text_shadow_blur_ref.html +!= image_rendering_auto_a.html image_rendering_pixelated_a.html +== image_rendering_pixelated_a.html image_rendering_pixelated_ref.html +== background_size_a.html background_size_ref.html +== background_size_shorthand_a.html background_size_shorthand_ref.html diff --git a/tests/ref/iframe/hide_layers1.html b/tests/ref/iframe/hide_layers1.html new file mode 100644 index 00000000000..159723211de --- /dev/null +++ b/tests/ref/iframe/hide_layers1.html @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/tests/ref/iframe/hide_layers2.html b/tests/ref/iframe/hide_layers2.html new file mode 100644 index 00000000000..1bc79c654da --- /dev/null +++ b/tests/ref/iframe/hide_layers2.html @@ -0,0 +1,18 @@ + + + + + + + + diff --git a/tests/ref/iframe/hide_layers_ref.html b/tests/ref/iframe/hide_layers_ref.html new file mode 100644 index 00000000000..cb61f63d447 --- /dev/null +++ b/tests/ref/iframe/hide_layers_ref.html @@ -0,0 +1,10 @@ + + + + + + diff --git a/tests/ref/image_rendering_auto_a.html b/tests/ref/image_rendering_auto_a.html new file mode 100644 index 00000000000..3b7282e5ff9 --- /dev/null +++ b/tests/ref/image_rendering_auto_a.html @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/tests/ref/image_rendering_pixelated_a.html b/tests/ref/image_rendering_pixelated_a.html new file mode 100644 index 00000000000..b4086e2fd33 --- /dev/null +++ b/tests/ref/image_rendering_pixelated_a.html @@ -0,0 +1,20 @@ + + + + + + + + + + + + diff --git a/tests/ref/image_rendering_pixelated_ref.html b/tests/ref/image_rendering_pixelated_ref.html new file mode 100644 index 00000000000..06b2f3249f6 --- /dev/null +++ b/tests/ref/image_rendering_pixelated_ref.html @@ -0,0 +1,40 @@ + + + + + + + +
+
+
+
+ + + + + diff --git a/tests/ref/inline_block_min_width.html b/tests/ref/inline_block_min_width.html new file mode 100644 index 00000000000..c677fb21d94 --- /dev/null +++ b/tests/ref/inline_block_min_width.html @@ -0,0 +1,26 @@ + + + + + + +
+ + diff --git a/tests/ref/inline_block_min_width_ref.html b/tests/ref/inline_block_min_width_ref.html new file mode 100644 index 00000000000..d0577dd80df --- /dev/null +++ b/tests/ref/inline_block_min_width_ref.html @@ -0,0 +1,25 @@ + + + + + + +
+ + diff --git a/tests/ref/inline_block_overflow.html b/tests/ref/inline_block_overflow.html new file mode 100644 index 00000000000..5b5aa9d0130 --- /dev/null +++ b/tests/ref/inline_block_overflow.html @@ -0,0 +1,6 @@ + + + + ab + + diff --git a/tests/ref/inline_block_overflow_ref.html b/tests/ref/inline_block_overflow_ref.html new file mode 100644 index 00000000000..63d14407d59 --- /dev/null +++ b/tests/ref/inline_block_overflow_ref.html @@ -0,0 +1,6 @@ + + + + ab + + diff --git a/tests/ref/overflow_xy_a.html b/tests/ref/overflow_xy_a.html new file mode 100644 index 00000000000..eee0c79eec8 --- /dev/null +++ b/tests/ref/overflow_xy_a.html @@ -0,0 +1,36 @@ + + + + + + + +
+
+ + + diff --git a/tests/ref/overflow_xy_ref.html b/tests/ref/overflow_xy_ref.html new file mode 100644 index 00000000000..777f18bbebe --- /dev/null +++ b/tests/ref/overflow_xy_ref.html @@ -0,0 +1,29 @@ + + + + + + + +
+
+ + + + diff --git a/tests/reftest.rs b/tests/reftest.rs index c718e827969..3908217c556 100644 --- a/tests/reftest.rs +++ b/tests/reftest.rs @@ -130,7 +130,7 @@ struct Reftest { name: String, kind: ReftestKind, files: [Path; 2], - id: uint, + id: usize, servo_args: Vec, render_mode: RenderMode, is_flaky: bool, @@ -145,7 +145,7 @@ struct TestLine<'a> { file_right: &'a str, } -fn parse_lists(file: &Path, servo_args: &[String], render_mode: RenderMode, id_offset: uint) -> Vec { +fn parse_lists(file: &Path, servo_args: &[String], render_mode: RenderMode, id_offset: usize) -> Vec { let mut tests = Vec::new(); let contents = File::open_mode(file, io::Open, io::Read) .and_then(|mut f| f.read_to_string()) @@ -240,7 +240,7 @@ fn make_test(reftest: Reftest) -> TestDescAndFn { } } -fn capture(reftest: &Reftest, side: uint) -> (u32, u32, Vec) { +fn capture(reftest: &Reftest, side: usize) -> (u32, u32, Vec) { let png_filename = format!("/tmp/servo-reftest-{:06}-{}.png", reftest.id, side); let mut command = Command::new(os::self_exe_path().unwrap().join("servo")); command diff --git a/tests/wpt/metadata/html/semantics/document-metadata/the-base-element/base_multiple.html.ini b/tests/wpt/metadata/html/semantics/document-metadata/the-base-element/base_multiple.html.ini index 956ccc14150..df76225f1fe 100644 --- a/tests/wpt/metadata/html/semantics/document-metadata/the-base-element/base_multiple.html.ini +++ b/tests/wpt/metadata/html/semantics/document-metadata/the-base-element/base_multiple.html.ini @@ -1,5 +1,5 @@ [base_multiple.html] type: testharness + expected: ERROR [The attributes of the a element must be affected by the first base element] expected: FAIL - diff --git a/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/move_iframe_in_dom_03.html.ini b/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/move_iframe_in_dom_03.html.ini new file mode 100644 index 00000000000..0ec075cd574 --- /dev/null +++ b/tests/wpt/metadata/html/semantics/embedded-content/the-iframe-element/move_iframe_in_dom_03.html.ini @@ -0,0 +1,3 @@ +[move_iframe_in_dom_03.html] + type: testharness + expected: TIMEOUT