aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/gfx/render_task.rs5
-rw-r--r--src/components/main/compositing/mod.rs62
-rw-r--r--src/components/main/constellation.rs584
-rw-r--r--src/components/main/layout/layout_task.rs63
-rw-r--r--src/components/main/pipeline.rs103
-rwxr-xr-xsrc/components/main/servo.rc4
-rw-r--r--src/components/msg/compositor_msg.rs3
-rw-r--r--src/components/msg/constellation_msg.rs16
-rw-r--r--src/components/script/dom/bindings/codegen/CodegenRust.py28
-rw-r--r--src/components/script/dom/bindings/element.rs7
-rw-r--r--src/components/script/dom/bindings/utils.rs6
-rw-r--r--src/components/script/dom/blob.rs6
-rw-r--r--src/components/script/dom/clientrect.rs17
-rw-r--r--src/components/script/dom/clientrectlist.rs17
-rw-r--r--src/components/script/dom/document.rs36
-rw-r--r--src/components/script/dom/domparser.rs6
-rw-r--r--src/components/script/dom/element.rs55
-rw-r--r--src/components/script/dom/event.rs14
-rw-r--r--src/components/script/dom/eventtarget.rs14
-rw-r--r--src/components/script/dom/formdata.rs13
-rw-r--r--src/components/script/dom/htmlcollection.rs18
-rw-r--r--src/components/script/dom/mouseevent.rs10
-rw-r--r--src/components/script/dom/node.rs5
-rw-r--r--src/components/script/dom/uievent.rs9
-rw-r--r--src/components/script/dom/window.rs26
-rw-r--r--src/components/script/dom/windowproxy.rs13
-rw-r--r--src/components/script/html/hubbub_html_parser.rs147
-rw-r--r--src/components/script/layout_interface.rs6
-rw-r--r--src/components/script/script_task.rs771
-rw-r--r--src/components/util/time.rs3
30 files changed, 1324 insertions, 743 deletions
diff --git a/src/components/gfx/render_task.rs b/src/components/gfx/render_task.rs
index 4bb2b2a45a2..af80d384b69 100644
--- a/src/components/gfx/render_task.rs
+++ b/src/components/gfx/render_task.rs
@@ -9,6 +9,7 @@ use azure::azure_hl::{B8G8R8A8, DrawTarget};
use display_list::DisplayList;
use servo_msg::compositor_msg::{RenderListener, IdleRenderState, RenderingRenderState, LayerBuffer};
use servo_msg::compositor_msg::{LayerBufferSet};
+use servo_msg::constellation_msg::PipelineId;
use font_context::FontContext;
use geom::matrix2d::Matrix2D;
use geom::size::Size2D;
@@ -70,7 +71,7 @@ impl RenderChan {
}
priv struct RenderTask<C> {
- id: uint,
+ id: PipelineId,
port: Port<Msg>,
compositor: C,
font_ctx: @mut FontContext,
@@ -90,7 +91,7 @@ priv struct RenderTask<C> {
}
impl<C: RenderListener + Send> RenderTask<C> {
- pub fn create(id: uint,
+ pub fn create(id: PipelineId,
port: Port<Msg>,
compositor: C,
opts: Opts,
diff --git a/src/components/main/compositing/mod.rs b/src/components/main/compositing/mod.rs
index f274ba835d0..ebb18f7441d 100644
--- a/src/components/main/compositing/mod.rs
+++ b/src/components/main/compositing/mod.rs
@@ -5,7 +5,6 @@
use platform::{Application, Window};
use script::dom::event::{Event, ClickEvent, MouseDownEvent, MouseUpEvent, ResizeEvent};
use script::script_task::{LoadMsg, NavigateMsg, SendEventMsg};
-use script::layout_interface::{LayoutChan, RouteScriptMsg};
use windowing::{ApplicationMethods, WindowEvent, WindowMethods};
use windowing::{IdleWindowEvent, ResizeWindowEvent, LoadUrlWindowEvent, MouseWindowEventClass};
@@ -14,9 +13,9 @@ use windowing::{QuitWindowEvent, MouseWindowClickEvent, MouseWindowMouseDownEven
use servo_msg::compositor_msg::{RenderListener, LayerBuffer, LayerBufferSet, RenderState};
use servo_msg::compositor_msg::{ReadyState, ScriptListener};
-use servo_msg::constellation_msg::{CompositorAck, ConstellationChan};
+use servo_msg::constellation_msg::{CompositorAck, ConstellationChan, PipelineId};
use servo_msg::constellation_msg;
-use gfx::render_task::{RenderChan, ReRenderMsg};
+use gfx::render_task::ReRenderMsg;
use gfx::opts::Opts;
use azure::azure_hl::{DataSourceSurface, DrawTarget, SourceSurfaceMethods, current_gl_context};
@@ -50,6 +49,8 @@ pub use windowing;
use extra::time::precise_time_s;
use compositing::quadtree::Quadtree;
+use constellation::SendableFrameTree;
+use pipeline::Pipeline;
mod quadtree;
/// The implementation of the layers-based compositor.
@@ -78,7 +79,7 @@ impl RenderListener for CompositorChan {
port.recv()
}
- fn paint(&self, id: uint, layer_buffer_set: arc::ARC<LayerBufferSet>, new_size: Size2D<uint>) {
+ fn paint(&self, id: PipelineId, layer_buffer_set: arc::ARC<LayerBufferSet>, new_size: Size2D<uint>) {
self.chan.send(Paint(id, layer_buffer_set, new_size))
}
@@ -134,13 +135,13 @@ pub enum Msg {
DeleteLayer,
/// Requests that the compositor paint the given layer buffer set for the given page size.
- Paint(uint, arc::ARC<LayerBufferSet>, Size2D<uint>),
+ Paint(PipelineId, arc::ARC<LayerBufferSet>, Size2D<uint>),
/// Alerts the compositor to the current status of page loading.
ChangeReadyState(ReadyState),
/// Alerts the compositor to the current status of rendering.
ChangeRenderState(RenderState),
/// Sets the channel to the current layout and render tasks, along with their id
- SetLayoutRenderChans(LayoutChan, RenderChan , uint, ConstellationChan)
+ SetIds(SendableFrameTree, ConstellationChan)
}
/// Azure surface wrapping to work with the layers infrastructure.
@@ -234,12 +235,12 @@ impl CompositorTask {
let mut world_zoom = 1f32;
// Keeps track of local zoom factor. Reset to 1 after a rerender event.
let mut local_zoom = 1f32;
- // Channel to the current renderer.
- // FIXME: This probably shouldn't be stored like this.
-
- let mut render_chan: Option<RenderChan> = None;
- let mut pipeline_id: Option<uint> = None;
- let mut layout_chan: Option<LayoutChan> = None;
+ // Channel to the outermost frame's pipeline.
+ // FIXME: Compositor currently only asks for tiles to composite from this pipeline,
+ // Subframes need to be handled, as well. Additionally, events are only forwarded
+ // to this pipeline, but they should be routed to the appropriate pipeline via
+ // the constellation.
+ let mut pipeline: Option<Pipeline> = None;
// Quadtree for this layer
// FIXME: This should be one-per-layer
@@ -317,9 +318,9 @@ impl CompositorTask {
window_size), world_zoom);
if !tile_request.is_empty() {
- match render_chan {
- Some(ref chan) => {
- chan.send(ReRenderMsg(tile_request, world_zoom));
+ match pipeline {
+ Some(ref pipeline) => {
+ pipeline.render_chan.send(ReRenderMsg(tile_request, world_zoom));
}
_ => {
println("Warning: Compositor: Cannot send tile request, no render chan initialized");
@@ -344,14 +345,9 @@ impl CompositorTask {
ChangeReadyState(ready_state) => window.set_ready_state(ready_state),
ChangeRenderState(render_state) => window.set_render_state(render_state),
- SetLayoutRenderChans(new_layout_chan,
- new_render_chan,
- new_pipeline_id,
- response_chan) => {
- layout_chan = Some(new_layout_chan);
- render_chan = Some(new_render_chan);
- pipeline_id = Some(new_pipeline_id);
- response_chan.send(CompositorAck(new_pipeline_id));
+ SetIds(frame_tree, response_chan) => {
+ pipeline = Some(frame_tree.pipeline);
+ response_chan.send(CompositorAck(pipeline.get_ref().id.clone()));
}
GetSize(chan) => {
@@ -378,8 +374,8 @@ impl CompositorTask {
}
Paint(id, new_layer_buffer_set, new_size) => {
- match pipeline_id {
- Some(pipeline_id) => if id != pipeline_id { loop; },
+ match pipeline {
+ Some(ref pipeline) => if id != pipeline.id { loop; },
None => { loop; },
}
@@ -417,8 +413,8 @@ impl CompositorTask {
if window_size != new_size {
debug!("osmain: window resized to %ux%u", width, height);
window_size = new_size;
- match layout_chan {
- Some(ref chan) => chan.send(RouteScriptMsg(SendEventMsg(ResizeEvent(width, height)))),
+ match pipeline {
+ Some(ref pipeline) => pipeline.script_chan.send(SendEventMsg(pipeline.id.clone(), ResizeEvent(width, height))),
None => error!("Compositor: Recieved resize event without initialized layout chan"),
}
} else {
@@ -428,8 +424,8 @@ impl CompositorTask {
LoadUrlWindowEvent(url_string) => {
debug!("osmain: loading URL `%s`", url_string);
- match layout_chan {
- Some(ref chan) => chan.send(RouteScriptMsg(LoadMsg(url::make_url(url_string.to_str(), None)))),
+ match pipeline {
+ Some(ref pipeline) => pipeline.script_chan.send(LoadMsg(pipeline.id.clone(), url::make_url(url_string.to_str(), None))),
None => error!("Compositor: Recieved loadurl event without initialized layout chan"),
}
}
@@ -450,8 +446,8 @@ impl CompositorTask {
event = MouseUpEvent(button, world_mouse_point(layer_mouse_point));
}
}
- match layout_chan {
- Some(ref chan) => chan.send(RouteScriptMsg(SendEventMsg(event))),
+ match pipeline {
+ Some(ref pipeline) => pipeline.script_chan.send(SendEventMsg(pipeline.id.clone(), event)),
None => error!("Compositor: Recieved mouse event without initialized layout chan"),
}
}
@@ -530,8 +526,8 @@ impl CompositorTask {
windowing::Forward => constellation_msg::Forward,
windowing::Back => constellation_msg::Back,
};
- match layout_chan {
- Some(ref chan) => chan.send(RouteScriptMsg(NavigateMsg(direction))),
+ match pipeline {
+ Some(ref pipeline) => pipeline.script_chan.send(NavigateMsg(pipeline.id.clone(), direction)),
None => error!("Compositor: Recieved navigation event without initialized layout chan"),
}
}
diff --git a/src/components/main/constellation.rs b/src/components/main/constellation.rs
index 88edb160404..a86b2453844 100644
--- a/src/components/main/constellation.rs
+++ b/src/components/main/constellation.rs
@@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use compositing::{CompositorChan, SetLayoutRenderChans};
+use compositing::{CompositorChan, SetIds};
use extra::net::url;
@@ -10,11 +10,13 @@ use std::cell::Cell;
use std::comm;
use std::comm::Port;
use std::task;
+use geom::size::Size2D;
use gfx::opts::Opts;
-use gfx::render_task::{PaintPermissionGranted, PaintPermissionRevoked};
use pipeline::Pipeline;
-use servo_msg::constellation_msg::{CompositorAck, ConstellationChan, ExitMsg, LoadUrlMsg};
-use servo_msg::constellation_msg::{Msg, NavigateMsg, RendererReadyMsg, ResizedWindowBroadcast};
+use servo_msg::constellation_msg::{CompositorAck, ConstellationChan, ExitMsg};
+use servo_msg::constellation_msg::{InitLoadUrlMsg, LoadIframeUrlMsg, LoadUrlMsg};
+use servo_msg::constellation_msg::{Msg, NavigateMsg};
+use servo_msg::constellation_msg::{PipelineId, RendererReadyMsg, ResizedWindowBroadcast};
use servo_msg::constellation_msg;
use script::script_task::{ResizeInactiveMsg, ExecuteMsg};
use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient};
@@ -23,6 +25,7 @@ use servo_net::resource_task;
use servo_util::time::ProfilerChan;
use std::hashmap::HashMap;
use std::util::replace;
+use extra::future::from_value;
/// Maintains the pipelines and navigation context and grants permission to composite
pub struct Constellation {
@@ -31,20 +34,121 @@ pub struct Constellation {
compositor_chan: CompositorChan,
resource_task: ResourceTask,
image_cache_task: ImageCacheTask,
- pipelines: HashMap<uint, Pipeline>,
+ pipelines: HashMap<PipelineId, @mut Pipeline>,
navigation_context: NavigationContext,
- next_id: uint,
- current_painter: Option<uint>,
- next_painter: Option<uint>,
+ priv next_pipeline_id: PipelineId,
+ pending_frames: ~[FrameChange],
profiler_chan: ProfilerChan,
opts: Opts,
}
-/// Stores the ID's of the pipelines previous and next in the browser's history
-pub struct NavigationContext {
- previous: ~[uint],
- next: ~[uint],
- current: Option<uint>,
+/// Stores the Id of the outermost frame's pipeline, along with a vector of children frames
+#[deriving(Clone)]
+struct FrameTree {
+ pipeline: @mut Pipeline,
+ parent: Option<@mut Pipeline>,
+ children: ~[@mut FrameTree],
+}
+
+pub struct SendableFrameTree {
+ pipeline: Pipeline,
+ children: ~[SendableFrameTree],
+}
+
+impl SendableFrameTree {
+ fn contains(&self, id: PipelineId) -> bool {
+ self.pipeline.id == id ||
+ do self.children.iter().any |frame_tree| {
+ frame_tree.contains(id)
+ }
+ }
+}
+
+impl FrameTree {
+ fn contains(&self, id: PipelineId) -> bool {
+ self.pipeline.id == id ||
+ do self.children.iter().any |frame_tree| {
+ frame_tree.contains(id)
+ }
+ }
+
+ /// Returns the frame tree whose key is id
+ fn find_mut(@mut self, id: PipelineId) -> Option<@mut FrameTree> {
+ if self.pipeline.id == id { return Some(self); }
+ for self.children.mut_iter().advance |frame_tree| {
+ let found = frame_tree.find_mut(id);
+ if found.is_some() { return found; }
+ }
+ None
+ }
+
+ /// Replaces a node of the frame tree in place. Returns the node that was removed or the original node
+ /// if the node to replace could not be found.
+ fn replace_child(&mut self, id: PipelineId, new_child: @mut FrameTree) -> Result<@mut FrameTree, @mut FrameTree> {
+ let new_child_cell = Cell::new(new_child);
+ for self.children.mut_iter().advance |child| {
+ let new_child = new_child_cell.take();
+ if child.pipeline.id == id {
+ new_child.parent = child.parent;
+ return Ok(replace(child, new_child));
+ }
+ let replaced = child.replace_child(id, new_child);
+ if replaced.is_ok() {
+ return replaced;
+ }
+ new_child_cell.put_back(replaced.get_err());
+ }
+ Err(new_child_cell.take())
+ }
+
+ fn to_sendable(&self) -> SendableFrameTree {
+ let mut sendable_frame_tree = SendableFrameTree {
+ pipeline: (*self.pipeline).clone(),
+ children: ~[],
+ };
+
+ for self.children.iter().advance |frame_tree| {
+ sendable_frame_tree.children.push(frame_tree.to_sendable());
+ }
+ sendable_frame_tree
+ }
+
+ pub fn iter(@mut self) -> FrameTreeIterator {
+ FrameTreeIterator {
+ stack: ~[self],
+ }
+ }
+}
+
+pub struct FrameTreeIterator {
+ priv stack: ~[@mut FrameTree],
+}
+
+impl Iterator<@mut FrameTree> for FrameTreeIterator {
+ fn next(&mut self) -> Option<@mut FrameTree> {
+ if !self.stack.is_empty() {
+ let next = self.stack.pop();
+ for next.children.iter().advance |&child| {
+ self.stack.push(child);
+ }
+ Some(next)
+ } else {
+ None
+ }
+ }
+}
+
+/// Represents the portion of a page that is changing in navigating.
+struct FrameChange {
+ before: Option<PipelineId>,
+ after: @mut FrameTree,
+}
+
+/// Stores the Id's of the pipelines previous and next in the browser's history
+struct NavigationContext {
+ previous: ~[@mut FrameTree],
+ next: ~[@mut FrameTree],
+ current: Option<@mut FrameTree>,
}
impl NavigationContext {
@@ -57,32 +161,61 @@ impl NavigationContext {
}
/* Note that the following two methods can fail. They should only be called *
- * when it is known that, e.g., there exists a previous page or a next page. */
+ * when it is known that there exists either a previous page or a next page. */
- pub fn back(&mut self) -> uint {
- self.next.push(self.current.get());
+ pub fn back(&mut self) -> @mut FrameTree {
+ self.next.push(self.current.swap_unwrap());
self.current = Some(self.previous.pop());
- debug!("previous: %? next: %? current: %u", self.previous, self.next, self.current.get());
+ debug!("previous: %? next: %? current: %?", self.previous, self.next, *self.current.get_ref());
self.current.get()
}
- pub fn forward(&mut self) -> uint {
- self.previous.push(self.current.get());
+ pub fn forward(&mut self) -> @mut FrameTree {
+ self.previous.push(self.current.swap_unwrap());
self.current = Some(self.next.pop());
- debug!("previous: %? next: %? current: %u", self.previous, self.next, self.current.get());
+ debug!("previous: %? next: %? current: %?", self.previous, self.next, *self.current.get_ref());
self.current.get()
}
- /// Navigates to a new id, returning all id's evicted from next
- pub fn navigate(&mut self, id: uint) -> ~[uint] {
- debug!("navigating to %u", id);
+ /// Navigates to a new set of page frames, returning all evicted frame trees
+ pub fn navigate(&mut self, frame_tree: @mut FrameTree) -> ~[@mut FrameTree] {
+ debug!("navigating to %?", frame_tree);
let evicted = replace(&mut self.next, ~[]);
- do self.current.mutate_default(id) |cur_id| {
- self.previous.push(cur_id);
- id
+ if self.current.is_some() {
+ self.previous.push(self.current.swap_unwrap());
}
+ self.current = Some(frame_tree);
evicted
}
+
+ /// Returns the frame tree whose key is pipeline_id.
+ pub fn find(&mut self, pipeline_id: PipelineId) -> Option<@mut FrameTree> {
+ for self.current.mut_iter().advance |frame_tree| {
+ let found = frame_tree.find_mut(pipeline_id);
+ if found.is_some() { return found; }
+ }
+ let mut forward = self.next.mut_rev_iter();
+ let mut backward = self.previous.mut_rev_iter();
+ loop {
+ match (forward.next(), backward.next()) {
+ (None, None) => {
+ return None;
+ }
+ (next_forward, next_backward) => {
+ let mut next_forward = next_forward;
+ let mut next_backward = next_backward;
+ for next_forward.mut_iter().advance |frame_tree| {
+ let found = frame_tree.find_mut(pipeline_id);
+ if found.is_some() { return found; }
+ }
+ for next_backward.mut_iter().advance |frame_tree| {
+ let found = frame_tree.find_mut(pipeline_id);
+ if found.is_some() { return found; }
+ }
+ }
+ }
+ }
+ }
}
impl Constellation {
@@ -114,9 +247,8 @@ impl Constellation {
image_cache_task: image_cache_task.take(),
pipelines: HashMap::new(),
navigation_context: NavigationContext::new(),
- next_id: 0,
- current_painter: None,
- next_painter: None,
+ next_pipeline_id: PipelineId(0),
+ pending_frames: ~[],
profiler_chan: profiler_chan.take(),
opts: opts.take(),
};
@@ -134,41 +266,216 @@ impl Constellation {
}
}
- /// Helper function for getting a unique pipeline ID
- fn get_next_id(&mut self) -> uint {
- let id = self.next_id;
- self.next_id = id + 1;
+ /// Helper function for getting a unique pipeline Id
+ fn get_next_pipeline_id(&mut self) -> PipelineId {
+ let id = self.next_pipeline_id;
+ self.next_pipeline_id = PipelineId(*id + 1);
id
}
+
+ /// Convenience function for getting the currently active frame tree.
+ /// The currently active frame tree should always be the current painter
+ fn current_frame<'a>(&'a self) -> &'a Option<@mut FrameTree> {
+ &self.navigation_context.current
+ }
/// Handles loading pages, navigation, and granting access to the compositor
fn handle_request(&mut self, request: Msg) -> bool {
match request {
- // Load a new page, usually either from a mouse click or typed url
- LoadUrlMsg(url) => {
+
+ // Acknowledgement from the compositor that it has updated its active pipeline id
+ CompositorAck(id) => {
+ let pending_index = do self.pending_frames.rposition |frame_change| {
+ frame_change.after.pipeline.id == id
+ }.expect("Constellation: received compositor ack for frame tree not currently
+ pending compositor ack. This is a bug.");
+ let frame_tree = self.pending_frames.swap_remove(pending_index).after;
+ self.grant_paint_permission(frame_tree);
+ }
+
+ ExitMsg(sender) => {
+ for self.pipelines.iter().advance |(_id, ref pipeline)| {
+ pipeline.exit();
+ }
+ self.image_cache_task.exit();
+ self.resource_task.send(resource_task::Exit);
+
+ sender.send(());
+ return false
+ }
+
+ // This should only be called once per constellation, and only by the browser
+ InitLoadUrlMsg(url) => {
+ let pipeline = @mut Pipeline::create(self.get_next_pipeline_id(),
+ self.chan.clone(),
+ self.compositor_chan.clone(),
+ self.image_cache_task.clone(),
+ self.resource_task.clone(),
+ self.profiler_chan.clone(),
+ copy self.opts,
+ {
+ let size = self.compositor_chan.get_size();
+ from_value(Size2D(size.width as uint, size.height as uint))
+ });
+ if url.path.ends_with(".js") {
+ pipeline.script_chan.send(ExecuteMsg(pipeline.id, url));
+ } else {
+ pipeline.load(url);
+ pipeline.navigation_type = Some(constellation_msg::Load);
+
+ self.pending_frames.push(FrameChange{
+ before: None,
+ after: @mut FrameTree {
+ pipeline: pipeline,
+ parent: None,
+ children: ~[],
+ },
+ });
+ }
+ self.pipelines.insert(pipeline.id, pipeline);
+ }
+
+ LoadIframeUrlMsg(url, source_pipeline_id, size_future) => {
+ // A message from the script associated with pipeline_id that it has
+ // parsed an iframe during html parsing. This iframe will result in a
+ // new pipeline being spawned and a frame tree being added to pipeline_id's
+ // frame tree's children. This message is never the result of a link clicked
+ // or a new url entered.
+ // Start by finding the frame tree matching the pipeline id,
+ // and add the new pipeline to its sub frames. The frame tree
+ // could already be in the navigation context, or it could still
+ // be loading, in which case it is a new id in a constellation.pending_frames
+ // frame change, because pages aren't added to the navi context until they finish
+ // loading (excluding iframes). Which is checked first is seemingly arbitrary
+ let next_pipeline_id = self.get_next_pipeline_id();
+ let frame_tree = {
+ let frame_tree = self.navigation_context.find(source_pipeline_id);
+ match frame_tree {
+ Some(frame_tree) => frame_tree,
+ None => {
+ let frame_change = do self.pending_frames.mut_iter().find_ |frame_change| {
+ frame_change.after.pipeline.id == source_pipeline_id
+ };
+ let frame_change = frame_change.expect("Constellation: source pipeline id
+ of LoadIframeUrlMsg is not in navigation context nor a pending painter.
+ This should be impossible.");
+ frame_change.after
+ }
+ }
+ };
+
+ // Compare the pipeline's url to the new url. If the origin is the same,
+ // then reuse the script task in creating the new pipeline
+ let source_pipeline = *self.pipelines.find(&source_pipeline_id).expect("Constellation:
+ source Id of LoadIframeUrlMsg does have an associated pipeline in
+ constellation. This should be impossible.");
+
+ let source_url = source_pipeline.url.clone().expect("Constellation: LoadUrlIframeMsg's
+ source's Url is None. There should never be a LoadUrlIframeMsg from a pipeline
+ that was never given a url to load.");
+
+ let pipeline = @mut if (source_url.host == url.host &&
+ source_url.port == url.port) {
+ // Reuse the script task if same-origin url's
+ Pipeline::with_script(next_pipeline_id,
+ self.chan.clone(),
+ self.compositor_chan.clone(),
+ self.image_cache_task.clone(),
+ self.profiler_chan.clone(),
+ copy self.opts,
+ source_pipeline,
+ size_future)
+ } else {
+ // Create a new script task if not same-origin url's
+ Pipeline::create(next_pipeline_id,
+ self.chan.clone(),
+ self.compositor_chan.clone(),
+ self.image_cache_task.clone(),
+ self.resource_task.clone(),
+ self.profiler_chan.clone(),
+ copy self.opts,
+ size_future)
+ };
+
+ if url.path.ends_with(".js") {
+ pipeline.execute(url);
+ } else {
+ pipeline.load(url);
+ pipeline.navigation_type = None;
+ }
+ frame_tree.children.push(@mut FrameTree {
+ pipeline: pipeline,
+ parent: Some(source_pipeline),
+ children: ~[],
+ });
+ self.pipelines.insert(pipeline.id, pipeline);
+ }
+
+ // Load a new page, usually -- but not always -- from a mouse click or typed url
+ // If there is already a pending page (self.pending_frames), it will not be overridden;
+ // However, if the id is not encompassed by another change, it will be.
+ LoadUrlMsg(source_id, url, size_future) => {
debug!("received message to load %s", url::to_str(&url));
- let pipeline_id = self.get_next_id();
- let mut pipeline = Pipeline::create(pipeline_id,
- self.chan.clone(),
- self.compositor_chan.clone(),
- self.image_cache_task.clone(),
- self.resource_task.clone(),
- self.profiler_chan.clone(),
- copy self.opts);
+ // Make sure no pending page would be overridden.
+ for self.pending_frames.iter().advance |frame_change| {
+ let old_id = frame_change.before.expect("Constellation: Received load msg
+ from pipeline, but there is no currently active page. This should
+ be impossible.");
+ let changing_frame = self.current_frame().get_ref().find_mut(old_id).expect("Constellation:
+ Pending change has non-active source pipeline. This should be
+ impossible.");
+ if changing_frame.contains(source_id) {
+ // id that sent load msg is being changed already; abort
+ return true;
+ }
+ }
+ // Being here means either there are no pending frames, or none of the pending
+ // changes would be overriden by changing the subframe associated with source_id.
+
+ let next_pipeline_id = self.get_next_pipeline_id();
+ let pipeline = @mut Pipeline::create(next_pipeline_id,
+ self.chan.clone(),
+ self.compositor_chan.clone(),
+ self.image_cache_task.clone(),
+ self.resource_task.clone(),
+ self.profiler_chan.clone(),
+ copy self.opts,
+ size_future);
+
if url.path.ends_with(".js") {
- pipeline.script_chan.send(ExecuteMsg(url));
+ pipeline.script_chan.send(ExecuteMsg(pipeline.id, url));
} else {
pipeline.load(url);
pipeline.navigation_type = Some(constellation_msg::Load);
- self.next_painter = Some(pipeline_id);
+
+ let parent = if self.current_frame().get_ref().pipeline.id == source_id {
+ // source_id is the root of the current frame tree; replace whole tree
+ None
+ } else {
+ // id is not the root of the current frame tree, but is in the frame tree;
+ // replace only the subtree
+ let source_frame = self.current_frame().get_ref().find_mut(source_id).expect(
+ "Constellation: received a LoadUrlMsg from a pipeline_id associated
+ with a pipeline not in the active frame tree. This should be
+ impossible.");
+ source_frame.parent
+ };
+ self.pending_frames.push(FrameChange{
+ before: Some(source_id),
+ after: @mut FrameTree {
+ pipeline: pipeline,
+ parent: parent,
+ children: ~[],
+ },
+ });
}
- self.pipelines.insert(pipeline_id, pipeline);
+ self.pipelines.insert(pipeline.id, pipeline);
}
// Handle a forward or back request
NavigateMsg(direction) => {
debug!("received message to navigate %?", direction);
- let destination_id = match direction {
+ let destination_frame = match direction {
constellation_msg::Forward => {
if self.navigation_context.next.is_empty() {
debug!("no next page to navigate to");
@@ -184,79 +491,152 @@ impl Constellation {
self.navigation_context.back()
}
};
- let mut pipeline = self.pipelines.pop(&destination_id).unwrap();
- pipeline.navigation_type = Some(constellation_msg::Navigate);
- pipeline.reload();
- self.pipelines.insert(destination_id, pipeline);
- self.next_painter = Some(destination_id);
- self.update_painter();
+
+ for destination_frame.iter().advance |frame| {
+ let pipeline = &frame.pipeline;
+ pipeline.navigation_type = Some(constellation_msg::Navigate);
+ pipeline.reload();
+ }
+ self.compositor_chan.send(SetIds(destination_frame.to_sendable(), self.chan.clone()));
+
+ let before_id = self.current_frame().get_ref().pipeline.id.clone();
+ self.pending_frames.clear();
+ self.pending_frames.push(FrameChange {
+ before: Some(before_id),
+ after: destination_frame,
+ });
+
+ // TODO(tkuehn): what is the "critical point" beyond which pending frames
+ // should not be cleared? Currently, the behavior is that forward/back
+ // navigation always has navigation priority, and after that new page loading is
+ // first come, first served.
+ let old = self.current_frame().get_ref();
+ for old.iter().advance |frame| {
+ frame.pipeline.revoke_paint_permission();
+ }
}
// Notification that rendering has finished and is requesting permission to paint.
RendererReadyMsg(pipeline_id) => {
- let next_painter = self.next_painter;
- for next_painter.iter().advance |&id| {
- if pipeline_id == id {
- self.update_painter();
+ // This message could originate from a pipeline in the navigation context or
+ // from a pending frame. The only time that we will grant paint permission is
+ // when the message originates from a pending frame or the current frame.
+
+ for self.current_frame().iter().advance |current_frame| {
+ // Messages originating in the current frame are not navigations;
+ // TODO(tkuehn): In fact, this kind of message might be provably
+ // impossible to occur.
+ if current_frame.contains(pipeline_id) {
+ for current_frame.iter().advance |frame| {
+ frame.pipeline.grant_paint_permission();
+ }
+ return true;
}
}
- }
- ResizedWindowBroadcast(new_size) => match self.current_painter {
- Some(current_painter_id) => for self.pipelines.iter().advance |(&id, pipeline)| {
- if current_painter_id != id {
- pipeline.script_chan.send(ResizeInactiveMsg(new_size));
- }
- },
- None => for self.pipelines.iter().advance |(_, pipeline)| {
- pipeline.script_chan.send(ResizeInactiveMsg(new_size));
- },
- },
+ // Find the pending frame change whose new pipeline id is pipeline_id.
+ // If it is not found, it simply means that this pipeline will not receive
+ // permission to paint.
+ let pending_index = do self.pending_frames.rposition |frame_change| {
+ frame_change.after.pipeline.id == pipeline_id
+ };
+ for pending_index.iter().advance |&pending_index| {
+ let frame_change = self.pending_frames.swap_remove(pending_index);
+ let to_add = frame_change.after;
- // Acknowledgement from the compositor that it has updated its active pipeline id
- CompositorAck(id) => {
- self.grant_paint_permission(id);
- }
+ // Create the next frame tree that will be given to the compositor
+ let next_frame_tree = match to_add.parent {
+ None => to_add, // to_add is the root
+ Some(_parent) => self.current_frame().get(),
+ };
- ExitMsg(sender) => {
- for self.pipelines.iter().advance |(_, pipeline)| {
- pipeline.exit();
+ // If there are frames to revoke permission from, do so now.
+ match frame_change.before {
+ Some(revoke_id) => {
+ let current_frame = self.current_frame().get();
+
+ let to_revoke = current_frame.find_mut(revoke_id).expect(
+ "Constellation: pending frame change refers to an old
+ frame not contained in the current frame. This is a bug");
+
+ for to_revoke.iter().advance |frame| {
+ frame.pipeline.revoke_paint_permission();
+ }
+
+ // If to_add is not the root frame, then replace revoked_frame with it
+ if to_add.parent.is_some() {
+ next_frame_tree.replace_child(revoke_id, to_add);
+ }
+ self.pending_frames.push(FrameChange {
+ before: Some(revoke_id),
+ after: next_frame_tree
+ });
+ }
+
+ None => {
+ // Add to_add to parent's children, if it is not the root
+ let parent = &to_add.parent;
+ let to_add = Cell::new(to_add);
+ for parent.iter().advance |parent| {
+ let parent = next_frame_tree.find_mut(parent.id).expect(
+ "Constellation: pending frame has a parent frame that is not
+ active. This is a bug.");
+ parent.children.push(to_add.take());
+ }
+ self.pending_frames.push(FrameChange {
+ before: None,
+ after: next_frame_tree
+ });
+ }
+ }
+ self.compositor_chan.send(SetIds(next_frame_tree.to_sendable(), self.chan.clone()));
}
- self.image_cache_task.exit();
- self.resource_task.send(resource_task::Exit);
+ }
- sender.send(());
- return false
+ ResizedWindowBroadcast(new_size) => match *self.current_frame() {
+ Some(ref current_frame) => {
+ let current_frame_id = current_frame.pipeline.id.clone();
+ for self.navigation_context.previous.iter().advance |frame_tree| {
+ let pipeline = &frame_tree.pipeline;
+ if current_frame_id != pipeline.id {
+ pipeline.script_chan.send(ResizeInactiveMsg(new_size));
+ }
+ }
+ for self.navigation_context.next.iter().advance |frame_tree| {
+ let pipeline = &frame_tree.pipeline;
+ if current_frame_id != pipeline.id {
+ pipeline.script_chan.send(ResizeInactiveMsg(new_size));
+ }
+ }
+ }
+ None => {
+ for self.navigation_context.previous.iter().advance |frame_tree| {
+ frame_tree.pipeline.script_chan.send(ResizeInactiveMsg(new_size));
+ }
+ for self.navigation_context.next.iter().advance |frame_tree| {
+ frame_tree.pipeline.script_chan.send(ResizeInactiveMsg(new_size));
+ }
+ }
}
+
}
true
}
- fn update_painter(&mut self) {
- let current_painter = replace(&mut self.current_painter, None);
- for current_painter.iter().advance |id| {
- self.pipelines.get(id).render_chan.send(PaintPermissionRevoked);
+ // Grants a frame tree permission to paint; optionally updates navigation to reflect a new page
+ fn grant_paint_permission(&mut self, frame_tree: @mut FrameTree) {
+ // Give permission to paint to the new frame and all child frames
+ for frame_tree.iter().advance |frame| {
+ frame.pipeline.grant_paint_permission();
}
- let id = replace(&mut self.next_painter, None);
- let id = id.expect("constellation: called update painter when there was no next painter");
- let pipeline = self.pipelines.get(&id);
- self.compositor_chan.send(SetLayoutRenderChans(pipeline.layout_chan.clone(),
- pipeline.render_chan.clone(),
- id,
- self.chan.clone()));
- }
- // Grants a renderer permission to paint; optionally updates navigation to reflect a new page
- fn grant_paint_permission(&mut self, id: uint) {
- let pipeline = self.pipelines.get(&id);
- pipeline.render_chan.send(PaintPermissionGranted);
- self.current_painter = Some(id);
- // Don't navigate on Navigate type, because that is handled by forward/back
- match pipeline.navigation_type.get() {
- constellation_msg::Load => {
- let evicted = self.navigation_context.navigate(id);
- for evicted.iter().advance |id| {
- self.pipelines.get(id).exit();
+ // Don't navigate on Navigate type (or None, as in the case of parsed iframes that finish
+ // loading)
+ match frame_tree.pipeline.navigation_type {
+ Some(constellation_msg::Load) => {
+ let evicted = self.navigation_context.navigate(frame_tree);
+ for evicted.iter().advance |frame_tree| {
+ frame_tree.pipeline.exit();
}
}
_ => {}
diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs
index 1943191c89a..68ba371d110 100644
--- a/src/components/main/layout/layout_task.rs
+++ b/src/components/main/layout/layout_task.rs
@@ -34,9 +34,10 @@ use script::layout_interface::{AddStylesheetMsg, ContentBoxQuery};
use script::layout_interface::{HitTestQuery, ContentBoxResponse, HitTestResponse};
use script::layout_interface::{ContentBoxesQuery, ContentBoxesResponse, ExitMsg, LayoutQuery};
use script::layout_interface::{MatchSelectorsDocumentDamage, Msg};
-use script::layout_interface::{QueryMsg, RouteScriptMsg, Reflow, ReflowDocumentDamage};
+use script::layout_interface::{QueryMsg, Reflow, ReflowDocumentDamage};
use script::layout_interface::{ReflowForDisplay, ReflowMsg};
-use script::script_task::{ReflowCompleteMsg, ScriptChan, ScriptMsg, SendEventMsg};
+use script::script_task::{ReflowCompleteMsg, ScriptChan, SendEventMsg};
+use servo_msg::constellation_msg::{ConstellationChan, PipelineId};
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
use servo_net::local_image_cache::LocalImageCache;
use servo_util::tree::{TreeNodeRef, TreeUtils};
@@ -45,7 +46,9 @@ use servo_util::time;
use extra::net::url::Url;
struct LayoutTask {
+ id: PipelineId,
port: Port<Msg>,
+ constellation_chan: ConstellationChan,
script_chan: ScriptChan,
render_chan: RenderChan,
image_cache_task: ImageCacheTask,
@@ -62,25 +65,38 @@ struct LayoutTask {
}
impl LayoutTask {
- pub fn create(port: Port<Msg>,
- script_chan: ScriptChan,
- render_chan: RenderChan,
- img_cache_task: ImageCacheTask,
- opts: Opts,
- profiler_chan: ProfilerChan) {
+ pub fn create(id: PipelineId,
+ port: Port<Msg>,
+ constellation_chan: ConstellationChan,
+ script_chan: ScriptChan,
+ render_chan: RenderChan,
+ img_cache_task: ImageCacheTask,
+ opts: Opts,
+ profiler_chan: ProfilerChan) {
+
let port = Cell::new(port);
+ let constellation_chan = Cell::new(constellation_chan);
+ let script_chan = Cell::new(script_chan);
+ let render_chan = Cell::new(render_chan);
+ let img_cache_task = Cell::new(img_cache_task);
+ let profiler_chan = Cell::new(profiler_chan);
+
do spawn {
- let mut layout = LayoutTask::new(port.take(),
- script_chan.clone(),
- render_chan.clone(),
- img_cache_task.clone(),
- &opts,
- profiler_chan.clone());
+ let mut layout = LayoutTask::new(id,
+ port.take(),
+ constellation_chan.take(),
+ script_chan.take(),
+ render_chan.take(),
+ img_cache_task.take(),
+ &opts,
+ profiler_chan.take());
layout.start();
};
}
- fn new(port: Port<Msg>,
+ fn new(id: PipelineId,
+ port: Port<Msg>,
+ constellation_chan: ConstellationChan,
script_chan: ScriptChan,
render_chan: RenderChan,
image_cache_task: ImageCacheTask,
@@ -90,7 +106,9 @@ impl LayoutTask {
let fctx = @mut FontContext::new(opts.render_backend, true, profiler_chan.clone());
LayoutTask {
+ id: id,
port: port,
+ constellation_chan: constellation_chan,
script_chan: script_chan,
render_chan: render_chan,
image_cache_task: image_cache_task.clone(),
@@ -140,10 +158,6 @@ impl LayoutTask {
self.handle_query(query.take());
}
}
- RouteScriptMsg(script_msg) => {
- debug!("layout: routing %? to script task", script_msg);
- self.route_script_msg(script_msg);
- }
ExitMsg => {
debug!("layout: ExitMsg received");
return false
@@ -265,7 +279,7 @@ impl LayoutTask {
// FIXME(pcwalton): This should probably be *one* channel, but we can't fix this without
// either select or a filtered recv() that only looks for messages of a given type.
data.script_join_chan.send(());
- data.script_chan.send(ReflowCompleteMsg);
+ data.script_chan.send(ReflowCompleteMsg(self.id));
}
/// Handles a query from the script task. This is the main routine that DOM functions like
@@ -381,12 +395,6 @@ impl LayoutTask {
}
}
- // TODO(tkuehn): once there are multiple script tasks, this is where the layout task will
- // determine which script task should receive the message. The prototype will need to change
- fn route_script_msg(&self, script_msg: ScriptMsg) {
- self.script_chan.send(script_msg);
- }
-
// When images can't be loaded in time to display they trigger
// this callback in some task somewhere. This will send a message
// to the script task, and ultimately cause the image to be
@@ -398,10 +406,11 @@ impl LayoutTask {
// make multiple copies of the callback, and the dom event
// channel is not a copyable type, so this is actually a
// little factory to produce callbacks
+ let id = self.id.clone();
let f: @fn() -> ~fn(ImageResponseMsg) = || {
let script_chan = script_chan.clone();
let f: ~fn(ImageResponseMsg) = |_| {
- script_chan.send(SendEventMsg(ReflowEvent))
+ script_chan.send(SendEventMsg(id.clone(), ReflowEvent))
};
f
};
diff --git a/src/components/main/pipeline.rs b/src/components/main/pipeline.rs
index 45852132bbf..645fb051b7d 100644
--- a/src/components/main/pipeline.rs
+++ b/src/components/main/pipeline.rs
@@ -5,22 +5,26 @@
use extra::net::url::Url;
use compositing::CompositorChan;
use gfx::render_task::{RenderChan, RenderTask};
+use gfx::render_task::{PaintPermissionGranted, PaintPermissionRevoked};
use gfx::render_task;
use gfx::opts::Opts;
use layout::layout_task::LayoutTask;
use script::layout_interface::LayoutChan;
-use script::script_task::LoadMsg;
-use servo_msg::constellation_msg::{ConstellationChan, NavigationType};
-use script::script_task::{ScriptTask, ScriptChan};
+use script::script_task::{ExecuteMsg, LoadMsg};
+use servo_msg::constellation_msg::{ConstellationChan, NavigationType, PipelineId};
+use script::script_task::{AttachLayoutMsg, NewLayoutInfo, ScriptTask, ScriptChan};
use script::script_task;
use servo_net::image_cache_task::ImageCacheTask;
use servo_net::resource_task::ResourceTask;
use servo_util::time::ProfilerChan;
+use geom::size::Size2D;
+use extra::future::Future;
use std::comm;
/// A uniquely-identifiable pipeline of stript task, layout task, and render task.
+#[deriving(Clone)]
pub struct Pipeline {
- id: uint,
+ id: PipelineId,
script_chan: ScriptChan,
layout_chan: LayoutChan,
render_chan: RenderChan,
@@ -31,15 +35,15 @@ pub struct Pipeline {
impl Pipeline {
/// Starts a render task, layout task, and script task. Returns the channels wrapped in a struct.
- pub fn create(id: uint,
- constellation_chan: ConstellationChan,
- compositor_chan: CompositorChan,
- image_cache_task: ImageCacheTask,
- resource_task: ResourceTask,
- profiler_chan: ProfilerChan,
- opts: Opts) -> Pipeline {
+ pub fn with_script(id: PipelineId,
+ constellation_chan: ConstellationChan,
+ compositor_chan: CompositorChan,
+ image_cache_task: ImageCacheTask,
+ profiler_chan: ProfilerChan,
+ opts: Opts,
+ script_pipeline: &Pipeline,
+ size_future: Future<Size2D<uint>>) -> Pipeline {
- let (script_port, script_chan) = special_stream!(ScriptChan);
let (layout_port, layout_chan) = special_stream!(LayoutChan);
let (render_port, render_chan) = special_stream!(RenderChan);
@@ -49,30 +53,76 @@ impl Pipeline {
copy opts,
profiler_chan.clone());
- LayoutTask::create(layout_port,
- script_chan.clone(),
+ LayoutTask::create(id,
+ layout_port,
+ constellation_chan,
+ script_pipeline.script_chan.clone(),
render_chan.clone(),
image_cache_task.clone(),
copy opts,
profiler_chan);
+ let new_layout_info = NewLayoutInfo {
+ old_id: script_pipeline.id.clone(),
+ new_id: id,
+ layout_chan: layout_chan.clone(),
+ size_future: size_future,
+ };
+
+ script_pipeline.script_chan.send(AttachLayoutMsg(new_layout_info));
+
+ Pipeline::new(id,
+ script_pipeline.script_chan.clone(),
+ layout_chan,
+ render_chan)
+
+ }
+
+ pub fn create(id: PipelineId,
+ constellation_chan: ConstellationChan,
+ compositor_chan: CompositorChan,
+ image_cache_task: ImageCacheTask,
+ resource_task: ResourceTask,
+ profiler_chan: ProfilerChan,
+ opts: Opts,
+ size: Future<Size2D<uint>>) -> Pipeline {
+
+ let (script_port, script_chan) = special_stream!(ScriptChan);
+ let (layout_port, layout_chan) = special_stream!(LayoutChan);
+ let (render_port, render_chan) = special_stream!(RenderChan);
+
ScriptTask::create(id,
compositor_chan.clone(),
layout_chan.clone(),
script_port,
script_chan.clone(),
- constellation_chan,
+ constellation_chan.clone(),
resource_task,
- image_cache_task,
- compositor_chan.get_size());
+ image_cache_task.clone(),
+ size);
+
+
+ RenderTask::create(id,
+ render_port,
+ compositor_chan.clone(),
+ copy opts,
+ profiler_chan.clone());
+ LayoutTask::create(id,
+ layout_port,
+ constellation_chan,
+ script_chan.clone(),
+ render_chan.clone(),
+ image_cache_task,
+ copy opts,
+ profiler_chan);
Pipeline::new(id,
script_chan,
layout_chan,
render_chan)
}
- pub fn new(id: uint,
+ pub fn new(id: PipelineId,
script_chan: ScriptChan,
layout_chan: LayoutChan,
render_chan: RenderChan)
@@ -89,12 +139,25 @@ impl Pipeline {
pub fn load(&mut self, url: Url) {
self.url = Some(url.clone());
- self.script_chan.send(LoadMsg(url));
+ self.script_chan.send(LoadMsg(self.id, url));
+ }
+
+ pub fn execute(&mut self, url: Url) {
+ self.url = Some(url.clone());
+ self.script_chan.send(ExecuteMsg(self.id, url));
+ }
+
+ pub fn grant_paint_permission(&self) {
+ self.render_chan.send(PaintPermissionGranted);
+ }
+
+ pub fn revoke_paint_permission(&self) {
+ self.render_chan.send(PaintPermissionRevoked);
}
pub fn reload(&self) {
for self.url.iter().advance |url| {
- self.script_chan.send(LoadMsg(url.clone()));
+ self.script_chan.send(LoadMsg(self.id, url.clone()));
}
}
diff --git a/src/components/main/servo.rc b/src/components/main/servo.rc
index 1f660764c93..ea66cc356d9 100755
--- a/src/components/main/servo.rc
+++ b/src/components/main/servo.rc
@@ -37,7 +37,7 @@ extern mod core_text;
use compositing::{CompositorChan, CompositorTask};
use constellation::Constellation;
-use servo_msg::constellation_msg::{ExitMsg, LoadUrlMsg};
+use servo_msg::constellation_msg::{ExitMsg, InitLoadUrlMsg};
use gfx::opts;
use servo_net::image_cache_task::ImageCacheTask;
@@ -133,7 +133,7 @@ fn run(opts: &Opts) {
// Send the URL command to the constellation.
for opts.urls.iter().advance |filename| {
- constellation_chan.send(LoadUrlMsg(make_url(copy *filename, None)))
+ constellation_chan.send(InitLoadUrlMsg(make_url(copy *filename, None)))
}
// Wait for the compositor to shut down.
diff --git a/src/components/msg/compositor_msg.rs b/src/components/msg/compositor_msg.rs
index 3ec0ddcb684..5da0ddf204f 100644
--- a/src/components/msg/compositor_msg.rs
+++ b/src/components/msg/compositor_msg.rs
@@ -7,6 +7,7 @@ use azure::azure::AzGLContext;
use geom::rect::Rect;
use geom::size::Size2D;
+use constellation_msg::PipelineId;
use extra::arc;
@@ -60,7 +61,7 @@ pub trait RenderListener {
fn new_layer(&self, Size2D<uint>, uint);
fn resize_layer(&self, Size2D<uint>);
fn delete_layer(&self);
- fn paint(&self, id: uint, layer_buffer_set: arc::ARC<LayerBufferSet>, new_size: Size2D<uint>);
+ fn paint(&self, id: PipelineId, layer_buffer_set: arc::ARC<LayerBufferSet>, new_size: Size2D<uint>);
fn set_render_state(&self, render_state: RenderState);
}
diff --git a/src/components/msg/constellation_msg.rs b/src/components/msg/constellation_msg.rs
index 57accdddc63..c261f94dc42 100644
--- a/src/components/msg/constellation_msg.rs
+++ b/src/components/msg/constellation_msg.rs
@@ -7,6 +7,7 @@
use std::comm::{Chan, SharedChan};
use extra::net::url::Url;
+use extra::future::Future;
use geom::size::Size2D;
#[deriving(Clone)]
@@ -26,21 +27,28 @@ impl ConstellationChan {
}
pub enum Msg {
- LoadUrlMsg(Url),
- NavigateMsg(NavigationDirection),
+ CompositorAck(PipelineId),
ExitMsg(Chan<()>),
- RendererReadyMsg(uint),
- CompositorAck(uint),
+ InitLoadUrlMsg(Url),
+ LoadUrlMsg(PipelineId, Url, Future<Size2D<uint>>),
+ LoadIframeUrlMsg(Url, PipelineId, Future<Size2D<uint>>),
+ NavigateMsg(NavigationDirection),
+ RendererReadyMsg(PipelineId),
ResizedWindowBroadcast(Size2D<uint>),
}
/// Represents the two different ways to which a page can be navigated
+#[deriving(Clone, Eq, IterBytes)]
enum NavigationType {
Load, // entered or clicked on a url
Navigate, // browser forward/back buttons
}
+#[deriving(Clone, Eq, IterBytes)]
pub enum NavigationDirection {
Forward,
Back,
}
+
+#[deriving(Clone, Eq, IterBytes)]
+pub struct PipelineId(uint);
diff --git a/src/components/script/dom/bindings/codegen/CodegenRust.py b/src/components/script/dom/bindings/codegen/CodegenRust.py
index f20a4de0729..44113ad9e50 100644
--- a/src/components/script/dom/bindings/codegen/CodegenRust.py
+++ b/src/components/script/dom/bindings/codegen/CodegenRust.py
@@ -2458,8 +2458,8 @@ def CreateBindingJSObject(descriptor, parent):
if descriptor.proxy:
handler = """ //let cache = ptr::to_unsafe_ptr(aObject.get_wrappercache());
- let script_context = task_from_context(aCx);
- let handler = (*script_context).dom_static.proxy_handlers.get(&(PrototypeList::id::%s as uint));
+ let page = page_from_context(aCx);
+ let handler = (*page).js_info.get_ref().dom_static.proxy_handlers.get(&(PrototypeList::id::%s as uint));
""" % descriptor.name
create = handler + """ let obj = NewProxyObject(aCx, *handler,
ptr::to_unsafe_ptr(&RUST_PRIVATE_TO_JSVAL(squirrel_away(aObject) as *libc::c_void)),
@@ -2629,8 +2629,8 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
elif props.hasNonChromeOnly():
idsToInit.append(props.variableName(False))
if len(idsToInit) > 0:
- setup = CGList([CGGeneric("let script_context = task_from_context(aCx);"),
- CGList([CGGeneric("let %s_ids_mut = (*script_context).dom_static.attribute_ids.get(&(PrototypeList::id::%s as uint));" % (varname, self.descriptor.name)) for varname in idsToInit], '\n')], '\n')
+ setup = CGList([CGGeneric("let page = page_from_context(aCx);"),
+ CGList([CGGeneric("let %s_ids_mut = (*page).js_info.get_ref().dom_static.attribute_ids.get(&(PrototypeList::id::%s as uint));" % (varname, self.descriptor.name)) for varname in idsToInit], '\n')], '\n')
initIds = CGList(
[CGGeneric("!InitIds(aCx, %s, *%s_ids_mut)" % (varname, varname)) for
varname in idsToInit], ' ||\n')
@@ -2818,7 +2818,7 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod):
else:
getter = "GetConstructorObject"
- body = " let script_context = task_from_context(aCx);\n"
+ body = "let page = page_from_context(aCx);"
#XXXjdm This self.descriptor.concrete check shouldn't be necessary
if not self.descriptor.concrete or self.descriptor.proxy:
body += """ let traps = ProxyTraps {
@@ -2851,12 +2851,12 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod):
getElementIfPresent: ptr::null(),
getPrototypeOf: ptr::null()
};
- (*script_context).dom_static.proxy_handlers.insert(PrototypeList::id::%s as uint,
+ (*page).js_info.get_mut_ref().dom_static.proxy_handlers.insert(PrototypeList::id::%s as uint,
CreateProxyHandler(ptr::to_unsafe_ptr(&traps), ptr::to_unsafe_ptr(&Class) as *libc::c_void));
""" % self.descriptor.name
else:
- body += """ (*script_context).dom_static.attribute_ids.insert(PrototypeList::id::%s as uint,
+ body += """ (*page).js_info.get_ref().dom_static.attribute_ids.insert(PrototypeList::id::%s as uint,
vec::cast_to_mut(vec::from_slice(sAttributes_ids)));
""" % self.descriptor.name
body = "" #XXXjdm xray stuff isn't necessary yet
@@ -3445,12 +3445,12 @@ class CGXrayHelper(CGAbstractExternMethod):
def definition_body(self):
varNames = self.properties.variableNames(True)
- setup = "let script_context = task_from_context(cx);\n"
+ setup = "let page = page_from_context(cx);\n"
methods = self.properties.methods
if methods.hasNonChromeOnly() or methods.hasChromeOnly():
methodArgs = "Some(vec::zip_slice(%(methods)s, *method_ids))" % varNames
- setup += "let method_ids = (*script_context).dom_static.method_ids.get(&(PrototypeList::id::ClientRect as uint));\n"
+ setup += "let method_ids = (*page).js_info.get_ref().dom_static.method_ids.get(&(PrototypeList::id::ClientRect as uint));\n"
else:
methodArgs = "None"
methodArgs = CGGeneric(methodArgs)
@@ -3458,7 +3458,7 @@ class CGXrayHelper(CGAbstractExternMethod):
attrs = self.properties.attrs
if attrs.hasNonChromeOnly() or attrs.hasChromeOnly():
attrArgs = "Some(vec::zip_slice(%(attrs)s, *attr_ids))" % varNames
- setup += "let attr_ids = (*script_context).dom_static.attribute_ids.get(&(PrototypeList::id::ClientRect as uint));\n"
+ setup += "let attr_ids = (*page).js_info.get_ref().dom_static.attribute_ids.get(&(PrototypeList::id::ClientRect as uint));\n"
else:
attrArgs = "None"
attrArgs = CGGeneric(attrArgs)
@@ -3466,7 +3466,7 @@ class CGXrayHelper(CGAbstractExternMethod):
consts = self.properties.consts
if consts.hasNonChromeOnly() or consts.hasChromeOnly():
constArgs = "Some(vec::zip_slice(%(consts)s, *const_ids))" % varNames
- setup += "let const_ids = (*script_context).dom_static.constant_ids.get(&(PrototypeList::id::ClientRect as uint));\n"
+ setup += "let const_ids = (*page).js_info.get_ref().dom_static.constant_ids.get(&(PrototypeList::id::ClientRect as uint));\n"
else:
constArgs = "None"
constArgs = CGGeneric(constArgs)
@@ -3751,8 +3751,8 @@ class CGClassConstructHook(CGAbstractExternMethod):
//XXXjdm Gecko obtains a GlobalObject from the global (maybe from the private value,
// or through unwrapping a slot or something). We'll punt and get the Window
// from the context for now.
- let script_context = task_from_context(cx);
- let global = (*script_context).root_frame.get_ref().window;
+ let page = page_from_context(cx);
+ let global = (*page).frame.get_ref().window;
let obj = global.get_wrappercache().get_wrapper();
"""
preArgs = ["global"]
@@ -4365,7 +4365,7 @@ class CGBindingRoot(CGThing):
'dom::uievent::*', #XXXjdm
'dom::windowproxy::*', #XXXjdm
'dom::bindings::codegen::*', #XXXjdm
- 'script_task::task_from_context',
+ 'script_task::{JSPageInfo, page_from_context}',
'dom::bindings::utils::EnumEntry',
'dom::node::ScriptView',
'std::cast',
diff --git a/src/components/script/dom/bindings/element.rs b/src/components/script/dom/bindings/element.rs
index e186e8c97ae..98663ba9de9 100644
--- a/src/components/script/dom/bindings/element.rs
+++ b/src/components/script/dom/bindings/element.rs
@@ -11,7 +11,7 @@ use dom::element::{HTMLImageElementTypeId, HTMLHeadElementTypeId, HTMLScriptElem
HTMLDivElementTypeId};
use dom::node::{AbstractNode, ScriptView, ElementNodeTypeId};
use layout_interface::{ContentBoxQuery, ContentBoxResponse};
-use script_task::task_from_context;
+use script_task::page_from_context;
use super::utils;
use std::cast;
@@ -230,9 +230,10 @@ extern fn HTMLImageElement_getWidth(cx: *JSContext, _argc: c_uint, vp: *mut JSVa
let node = unwrap(obj);
let width = match node.type_id() {
ElementNodeTypeId(HTMLImageElementTypeId) => {
- let script_context = task_from_context(cx);
+ let page = page_from_context(cx);
let (port, chan) = comm::stream();
- match (*script_context).query_layout(ContentBoxQuery(node, chan), port) {
+ // TODO(tkuehn): currently this just queries top-level page's layout. Need to handle subframes.
+ match (*page).query_layout(ContentBoxQuery(node, chan), port) {
Ok(ContentBoxResponse(rect)) => rect.size.width.to_px(),
Err(()) => 0
}
diff --git a/src/components/script/dom/bindings/utils.rs b/src/components/script/dom/bindings/utils.rs
index 67419b26dfc..c2a0aad6633 100644
--- a/src/components/script/dom/bindings/utils.rs
+++ b/src/components/script/dom/bindings/utils.rs
@@ -5,7 +5,7 @@
use dom::bindings::codegen::PrototypeList;
use dom::bindings::node;
use dom::node::{AbstractNode, ScriptView};
-use script_task::task_from_context;
+use script_task::page_from_context;
use std::cast;
use std::hashmap::HashMap;
@@ -217,8 +217,8 @@ pub unsafe fn domstring_to_jsval(cx: *JSContext, string: &DOMString) -> JSVal {
pub fn get_compartment(cx: *JSContext) -> @mut Compartment {
unsafe {
- let script_context = task_from_context(cx);
- let compartment = (*script_context).js_compartment;
+ let page = page_from_context(cx);
+ let compartment = (*page).js_info.get_ref().js_compartment;
assert!(cx == compartment.cx.ptr);
compartment
}
diff --git a/src/components/script/dom/blob.rs b/src/components/script/dom/blob.rs
index 2f9d90c2b1c..e8760d985d0 100644
--- a/src/components/script/dom/blob.rs
+++ b/src/components/script/dom/blob.rs
@@ -4,7 +4,7 @@
use dom::bindings::utils::{WrapperCache, BindingObject, CacheableWrapper};
use dom::bindings::codegen::BlobBinding;
-use script_task::{task_from_context};
+use script_task::{page_from_context};
use js::jsapi::{JSContext, JSObject};
@@ -35,9 +35,9 @@ impl CacheableWrapper for Blob {
impl BindingObject for Blob {
fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper {
- let script_context = task_from_context(cx);
+ let page = page_from_context(cx);
unsafe {
- (*script_context).root_frame.get_ref().window as @mut CacheableWrapper
+ (*page).frame.get_ref().window as @mut CacheableWrapper
}
}
}
diff --git a/src/components/script/dom/clientrect.rs b/src/components/script/dom/clientrect.rs
index cd3f9f344e6..9ec68d1e06b 100644
--- a/src/components/script/dom/clientrect.rs
+++ b/src/components/script/dom/clientrect.rs
@@ -4,7 +4,7 @@
use dom::bindings::utils::{CacheableWrapper, WrapperCache, BindingObject, DerivedWrapper};
use dom::bindings::codegen::ClientRectBinding;
-use script_task::{task_from_context, global_script_context};
+use script_task::page_from_context;
use js::jsapi::{JSObject, JSContext, JSVal};
use js::glue::RUST_OBJECT_TO_JSVAL;
@@ -21,7 +21,7 @@ pub struct ClientRect {
}
impl ClientRect {
- pub fn new(top: f32, bottom: f32, left: f32, right: f32) -> @mut ClientRect {
+ pub fn new(top: f32, bottom: f32, left: f32, right: f32, cx: *JSContext, scope: *JSObject) -> @mut ClientRect {
let rect = @mut ClientRect {
top: top,
bottom: bottom,
@@ -29,16 +29,11 @@ impl ClientRect {
right: right,
wrapper: WrapperCache::new()
};
- rect.init_wrapper();
+ rect.init_wrapper(cx, scope);
rect
}
- pub fn init_wrapper(@mut self) {
- let script_context = global_script_context();
- let cx = script_context.js_compartment.cx.ptr;
- let owner = script_context.root_frame.get_ref().window;
- let cache = owner.get_wrappercache();
- let scope = cache.get_wrapper();
+ pub fn init_wrapper(@mut self, cx: *JSContext, scope: *JSObject) {
self.wrap_object_shared(cx, scope);
}
@@ -82,9 +77,9 @@ impl CacheableWrapper for ClientRect {
impl BindingObject for ClientRect {
fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper {
- let script_context = task_from_context(cx);
+ let page = page_from_context(cx);
unsafe {
- (*script_context).root_frame.get_ref().window as @mut CacheableWrapper
+ (*page).frame.get_ref().window as @mut CacheableWrapper
}
}
}
diff --git a/src/components/script/dom/clientrectlist.rs b/src/components/script/dom/clientrectlist.rs
index 2fdbc108f36..023b6ac37a6 100644
--- a/src/components/script/dom/clientrectlist.rs
+++ b/src/components/script/dom/clientrectlist.rs
@@ -5,7 +5,7 @@
use dom::bindings::codegen::ClientRectListBinding;
use dom::bindings::utils::{WrapperCache, CacheableWrapper, BindingObject};
use dom::clientrect::ClientRect;
-use script_task::{task_from_context, global_script_context};
+use script_task::page_from_context;
use js::jsapi::{JSObject, JSContext};
@@ -17,21 +17,16 @@ pub struct ClientRectList {
}
impl ClientRectList {
- pub fn new(rects: ~[@mut ClientRect]) -> @mut ClientRectList {
+ pub fn new(rects: ~[@mut ClientRect], cx: *JSContext, scope: *JSObject) -> @mut ClientRectList {
let list = @mut ClientRectList {
wrapper: WrapperCache::new(),
rects: rects
};
- list.init_wrapper();
+ list.init_wrapper(cx, scope);
list
}
- pub fn init_wrapper(@mut self) {
- let script_context = global_script_context();
- let cx = script_context.js_compartment.cx.ptr;
- let owner = script_context.root_frame.get_ref().window;
- let cache = owner.get_wrappercache();
- let scope = cache.get_wrapper();
+ pub fn init_wrapper(@mut self, cx: *JSContext, scope: *JSObject) {
self.wrap_object_shared(cx, scope);
}
@@ -68,9 +63,9 @@ impl CacheableWrapper for ClientRectList {
impl BindingObject for ClientRectList {
fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper {
- let script_context = task_from_context(cx);
+ let page = page_from_context(cx);
unsafe {
- (*script_context).root_frame.get_ref().window as @mut CacheableWrapper
+ (*page).frame.get_ref().window as @mut CacheableWrapper
}
}
}
diff --git a/src/components/script/dom/document.rs b/src/components/script/dom/document.rs
index f497e533fba..80f677e35cf 100644
--- a/src/components/script/dom/document.rs
+++ b/src/components/script/dom/document.rs
@@ -13,7 +13,6 @@ use dom::htmlcollection::HTMLCollection;
use dom::node::{AbstractNode, ScriptView, Node};
use dom::window::Window;
use dom::windowproxy::WindowProxy;
-use script_task::global_script_context;
use js::JSPROP_ENUMERATE;
use js::glue::*;
@@ -37,20 +36,20 @@ pub fn Document(root: AbstractNode<ScriptView>, window: Option<@mut Window>) ->
wrapper: WrapperCache::new(),
window: window
};
- let compartment = global_script_context().js_compartment;
+ let compartment = (*window.get_ref().page).js_info.get_ref().js_compartment;
do root.with_base |base| {
assert!(base.wrapper.get_wrapper().is_not_null());
let rootable = base.wrapper.get_rootable();
JS_AddObjectRoot(compartment.cx.ptr, rootable);
}
- let cx = global_script_context().js_compartment.cx.ptr;
+ let cx = (*window.get_ref().page).js_info.get_ref().js_compartment.cx.ptr;
doc.wrap_object_shared(cx, ptr::null()); //XXXjdm a proper scope would be nice
match window {
Some(win) => {
//FIXME: This is a hack until Window is autogenerated
- let compartment = (*win.script_task).js_compartment;
+ let compartment = (*win.page).js_info.get_ref().js_compartment;
compartment.define_property(~"document",
RUST_OBJECT_TO_JSVAL(doc.wrapper.wrapper),
GetJSClassHookStubPointer(PROPERTY_STUB) as *u8,
@@ -89,7 +88,8 @@ impl Document {
parent: Element::new(HTMLHtmlElementTypeId, ~"html")
};
- let root = unsafe { Node::as_abstract_node(root) };
+ let cx = unsafe {(*_owner.page).js_info.get_ref().js_compartment.cx.ptr};
+ let root = unsafe { Node::as_abstract_node(cx, root) };
Document(root, None)
}
@@ -129,15 +129,27 @@ impl Document {
}
}
};
- HTMLCollection::new(elements)
+ let win = self.window.get_ref();
+ let cx = unsafe {(*win.page).js_info.get_ref().js_compartment.cx.ptr};
+ let cache = win.get_wrappercache();
+ let scope = cache.get_wrapper();
+ HTMLCollection::new(elements, cx, scope)
}
pub fn GetElementsByTagNameNS(&self, _ns: DOMString, _tag: DOMString) -> @mut HTMLCollection {
- HTMLCollection::new(~[])
+ let win = self.window.get_ref();
+ let cx = unsafe {(*win.page).js_info.get_ref().js_compartment.cx.ptr};
+ let cache = win.get_wrappercache();
+ let scope = cache.get_wrapper();
+ HTMLCollection::new(~[], cx, scope)
}
pub fn GetElementsByClassName(&self, _class: DOMString) -> @mut HTMLCollection {
- HTMLCollection::new(~[])
+ let win = self.window.get_ref();
+ let cx = unsafe {(*win.page).js_info.get_ref().js_compartment.cx.ptr};
+ let cache = win.get_wrappercache();
+ let scope = cache.get_wrapper();
+ HTMLCollection::new(~[], cx, scope)
}
@@ -276,7 +288,11 @@ impl Document {
}
}
};
- HTMLCollection::new(elements)
+ let win = self.window.get_ref();
+ let cx = unsafe {(*win.page).js_info.get_ref().js_compartment.cx.ptr};
+ let cache = win.get_wrappercache();
+ let scope = cache.get_wrapper();
+ HTMLCollection::new(elements, cx, scope)
}
pub fn content_changed(&self) {
@@ -287,7 +303,7 @@ impl Document {
pub fn teardown(&self) {
unsafe {
- let compartment = global_script_context().js_compartment;
+ let compartment = (*self.window.get_ref().page).js_info.get_ref().js_compartment;
do self.root.with_base |node| {
assert!(node.wrapper.get_wrapper().is_not_null());
let rootable = node.wrapper.get_rootable();
diff --git a/src/components/script/dom/domparser.rs b/src/components/script/dom/domparser.rs
index e5609e60f49..0b8ba1f0326 100644
--- a/src/components/script/dom/domparser.rs
+++ b/src/components/script/dom/domparser.rs
@@ -8,7 +8,6 @@ use dom::document::Document;
use dom::element::{Element, HTMLHtmlElement, HTMLHtmlElementTypeId};
use dom::node::Node;
use dom::window::Window;
-use script_task::global_script_context;
pub struct DOMParser {
owner: @mut Window, //XXXjdm Document instead?
@@ -22,7 +21,8 @@ impl DOMParser {
wrapper: WrapperCache::new()
};
- let cx = global_script_context().js_compartment.cx.ptr;
+ // TODO(tkuehn): This just handles the top-level page. Need to handle subframes.
+ let cx = unsafe {(*owner.page).js_info.get_ref().js_compartment.cx.ptr};
let cache = owner.get_wrappercache();
let scope = cache.get_wrapper();
parser.wrap_object_shared(cx, scope);
@@ -43,7 +43,7 @@ impl DOMParser {
parent: Element::new(HTMLHtmlElementTypeId, ~"html")
};
- let root = Node::as_abstract_node(root);
+ let root = Node::as_abstract_node((*self.owner.page).js_info.get_ref().js_compartment.cx.ptr, root);
Document(root, None)
}
}
diff --git a/src/components/script/dom/element.rs b/src/components/script/dom/element.rs
index 3983d1b83f2..b83d81d0700 100644
--- a/src/components/script/dom/element.rs
+++ b/src/components/script/dom/element.rs
@@ -4,19 +4,20 @@
//! Element nodes.
-use dom::bindings::utils::DOMString;
+use dom::bindings::utils::{DOMString, CacheableWrapper};
use dom::clientrect::ClientRect;
use dom::clientrectlist::ClientRectList;
use dom::node::{ElementNodeTypeId, Node, ScriptView};
-use html::hubbub_html_parser::HtmlParserResult;
use layout_interface::{ContentBoxQuery, ContentBoxResponse, ContentBoxesQuery};
use layout_interface::{ContentBoxesResponse};
use std::cell::Cell;
+use std::comm::ChanOne;
use std::comm;
use std::uint;
use std::str::eq_slice;
use extra::net::url::Url;
+use geom::size::Size2D;
pub struct Element {
parent: Node<ScriptView>,
@@ -112,7 +113,7 @@ pub struct HTMLHeadingElement {
pub struct HTMLIframeElement {
parent: Element,
frame: Option<Url>,
- parse_result: Option<Port<HtmlParserResult>>
+ size_future_chan: Option<ChanOne<Size2D<uint>>>,
}
pub struct HTMLImageElement {
@@ -168,44 +169,53 @@ impl<'self> Element {
}
pub fn getClientRects(&self) -> Option<@mut ClientRectList> {
- let rects = match self.parent.owner_doc {
+ let (rects, cx, scope) = match self.parent.owner_doc {
Some(doc) => {
match doc.window {
Some(win) => {
let node = self.parent.abstract.get();
assert!(node.is_element());
- let script_task = unsafe {
- &mut *win.script_task
+ let page = unsafe {
+ &mut *win.page
};
let (port, chan) = comm::stream();
- match script_task.query_layout(ContentBoxesQuery(node, chan), port) {
+ // TODO(tkuehn): currently just queries top-level page layout. Needs to query
+ // subframe layout if this element is in a subframe. Probably need an ID field.
+ match page.query_layout(ContentBoxesQuery(node, chan), port) {
Ok(ContentBoxesResponse(rects)) => {
- do rects.map |r| {
+ let cx = (*page).js_info.get_ref().js_compartment.cx.ptr;
+ let cache = win.get_wrappercache();
+ let scope = cache.get_wrapper();
+ let rects = do rects.map |r| {
ClientRect::new(
r.origin.y.to_f32(),
(r.origin.y + r.size.height).to_f32(),
r.origin.x.to_f32(),
- (r.origin.x + r.size.width).to_f32())
- }
+ (r.origin.x + r.size.width).to_f32(),
+ cx,
+ scope)
+ };
+ Some((rects, cx, scope))
},
Err(()) => {
debug!("layout query error");
- ~[]
+ None
}
}
}
None => {
debug!("no window");
- ~[]
+ None
}
}
}
None => {
debug!("no document");
- ~[]
+ None
}
- };
- Some(ClientRectList::new(rects))
+ }.get();
+
+ Some(ClientRectList::new(rects, cx, scope))
}
pub fn getBoundingClientRect(&self) -> Option<@mut ClientRect> {
@@ -213,17 +223,26 @@ impl<'self> Element {
Some(doc) => {
match doc.window {
Some(win) => {
+ let page = unsafe {
+ &mut *win.page
+ };
let node = self.parent.abstract.get();
assert!(node.is_element());
- let script_task = unsafe { &mut *win.script_task };
let (port, chan) = comm::stream();
- match script_task.query_layout(ContentBoxQuery(node, chan), port) {
+ // TODO(tkuehn): currently just queries top-level page layout. Needs to query
+ // subframe layout if this element is in a subframe. Probably need an ID field.
+ match page.query_layout(ContentBoxQuery(node, chan), port) {
Ok(ContentBoxResponse(rect)) => {
+ let cx = (*page).js_info.get_ref().js_compartment.cx.ptr;
+ let cache = win.get_wrappercache();
+ let scope = cache.get_wrapper();
Some(ClientRect::new(
rect.origin.y.to_f32(),
(rect.origin.y + rect.size.height).to_f32(),
rect.origin.x.to_f32(),
- (rect.origin.x + rect.size.width).to_f32()))
+ (rect.origin.x + rect.size.width).to_f32(),
+ cx,
+ scope))
},
Err(()) => {
debug!("error querying layout");
diff --git a/src/components/script/dom/event.rs b/src/components/script/dom/event.rs
index 952a8a227cd..9f9135d23e9 100644
--- a/src/components/script/dom/event.rs
+++ b/src/components/script/dom/event.rs
@@ -7,12 +7,13 @@ use dom::window::Window;
use dom::bindings::codegen::EventBinding;
use dom::bindings::utils::{CacheableWrapper, BindingObject, DerivedWrapper};
use dom::bindings::utils::{DOMString, ErrorResult, WrapperCache};
-use script_task::{task_from_context, global_script_context};
use geom::point::Point2D;
use js::glue::RUST_OBJECT_TO_JSVAL;
use js::jsapi::{JSObject, JSContext, JSVal};
+use script_task::page_from_context;
+
use std::cast;
@@ -45,12 +46,7 @@ impl Event_ {
}
}
- pub fn init_wrapper(@mut self) {
- let script_context = global_script_context();
- let cx = script_context.js_compartment.cx.ptr;
- let owner = script_context.root_frame.get_ref().window;
- let cache = owner.get_wrappercache();
- let scope = cache.get_wrapper();
+ pub fn init_wrapper(@mut self, cx: *JSContext, scope: *JSObject) {
self.wrap_object_shared(cx, scope);
}
@@ -131,9 +127,9 @@ impl CacheableWrapper for Event_ {
impl BindingObject for Event_ {
fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper {
- let script_context = task_from_context(cx);
+ let page = page_from_context(cx);
unsafe {
- (*script_context).root_frame.get_ref().window as @mut CacheableWrapper
+ (*page).frame.get_ref().window as @mut CacheableWrapper
}
}
}
diff --git a/src/components/script/dom/eventtarget.rs b/src/components/script/dom/eventtarget.rs
index d5f3ff309e8..dbb972fc14e 100644
--- a/src/components/script/dom/eventtarget.rs
+++ b/src/components/script/dom/eventtarget.rs
@@ -4,7 +4,7 @@
use dom::bindings::codegen::EventTargetBinding;
use dom::bindings::utils::{CacheableWrapper, WrapperCache, BindingObject, DerivedWrapper};
-use script_task::{task_from_context, global_script_context};
+use script_task::page_from_context;
use js::glue::RUST_OBJECT_TO_JSVAL;
use js::jsapi::{JSObject, JSContext, JSVal};
@@ -22,12 +22,7 @@ impl EventTarget {
}
}
- pub fn init_wrapper(@mut self) {
- let script_context = global_script_context();
- let cx = script_context.js_compartment.cx.ptr;
- let owner = script_context.root_frame.get_ref().window;
- let cache = owner.get_wrappercache();
- let scope = cache.get_wrapper();
+ pub fn init_wrapper(@mut self, cx: *JSContext, scope: *JSObject) {
self.wrap_object_shared(cx, scope);
}
}
@@ -45,9 +40,10 @@ impl CacheableWrapper for EventTarget {
impl BindingObject for EventTarget {
fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper {
- let script_context = task_from_context(cx);
+ let page = page_from_context(cx);
+ // TODO(tkuehn): This only handles top-level pages. Needs to handle subframes.
unsafe {
- (*script_context).root_frame.get_ref().window as @mut CacheableWrapper
+ (*page).frame.get_ref().window as @mut CacheableWrapper
}
}
}
diff --git a/src/components/script/dom/formdata.rs b/src/components/script/dom/formdata.rs
index 5c3d49e1371..71a96b49f89 100644
--- a/src/components/script/dom/formdata.rs
+++ b/src/components/script/dom/formdata.rs
@@ -6,7 +6,7 @@ use dom::bindings::utils::{CacheableWrapper, BindingObject, DerivedWrapper};
use dom::bindings::utils::{WrapperCache, DOMString, str};
use dom::bindings::codegen::FormDataBinding;
use dom::blob::Blob;
-use script_task::{task_from_context, global_script_context};
+use script_task::{page_from_context};
use js::jsapi::{JSObject, JSContext, JSVal};
use js::glue::RUST_OBJECT_TO_JSVAL;
@@ -32,12 +32,7 @@ impl FormData {
}
}
- pub fn init_wrapper(@mut self) {
- let script_context = global_script_context();
- let cx = script_context.js_compartment.cx.ptr;
- let owner = script_context.root_frame.get_ref().window;
- let cache = owner.get_wrappercache();
- let scope = cache.get_wrapper();
+ pub fn init_wrapper(@mut self, cx: *JSContext, scope: *JSObject) {
self.wrap_object_shared(cx, scope);
}
@@ -69,9 +64,9 @@ impl CacheableWrapper for FormData {
impl BindingObject for FormData {
fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper {
- let script_context = task_from_context(cx);
+ let page = page_from_context(cx);
unsafe {
- (*script_context).root_frame.get_ref().window as @mut CacheableWrapper
+ (*page).frame.get_ref().window as @mut CacheableWrapper
}
}
}
diff --git a/src/components/script/dom/htmlcollection.rs b/src/components/script/dom/htmlcollection.rs
index da0b29e4e40..a19ed5f669d 100644
--- a/src/components/script/dom/htmlcollection.rs
+++ b/src/components/script/dom/htmlcollection.rs
@@ -6,7 +6,7 @@ use dom::bindings::codegen::HTMLCollectionBinding;
use dom::bindings::utils::{CacheableWrapper, BindingObject, WrapperCache};
use dom::bindings::utils::{DOMString, ErrorResult};
use dom::node::{AbstractNode, ScriptView};
-use script_task::{task_from_context, global_script_context};
+use script_task::page_from_context;
use js::jsapi::{JSObject, JSContext};
@@ -19,21 +19,16 @@ pub struct HTMLCollection {
}
impl HTMLCollection {
- pub fn new(elements: ~[AbstractNode<ScriptView>]) -> @mut HTMLCollection {
+ pub fn new(elements: ~[AbstractNode<ScriptView>], cx: *JSContext, scope: *JSObject) -> @mut HTMLCollection {
let collection = @mut HTMLCollection {
elements: elements,
wrapper: WrapperCache::new()
};
- collection.init_wrapper();
+ collection.init_wrapper(cx, scope);
collection
}
- pub fn init_wrapper(@mut self) {
- let script_context = global_script_context();
- let cx = script_context.js_compartment.cx.ptr;
- let owner = script_context.root_frame.get_ref().window;
- let cache = owner.get_wrappercache();
- let scope = cache.get_wrapper();
+ pub fn init_wrapper(@mut self, cx: *JSContext, scope: *JSObject) {
self.wrap_object_shared(cx, scope);
}
@@ -62,9 +57,10 @@ impl HTMLCollection {
impl BindingObject for HTMLCollection {
fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper {
- let script_context = task_from_context(cx);
+ let page = page_from_context(cx);
+ // TODO(tkuehn): This only handles the top-level frame. Need to grab subframes.
unsafe {
- (*script_context).root_frame.get_ref().window as @mut CacheableWrapper
+ (*page).frame.get_ref().window as @mut CacheableWrapper
}
}
}
diff --git a/src/components/script/dom/mouseevent.rs b/src/components/script/dom/mouseevent.rs
index 7ea73d27429..175f35a5833 100644
--- a/src/components/script/dom/mouseevent.rs
+++ b/src/components/script/dom/mouseevent.rs
@@ -9,7 +9,6 @@ use dom::eventtarget::EventTarget;
use dom::uievent::UIEvent;
use dom::window::Window;
use dom::windowproxy::WindowProxy;
-use script_task::{global_script_context};
use js::glue::RUST_OBJECT_TO_JSVAL;
use js::jsapi::{JSObject, JSContext, JSVal};
@@ -49,12 +48,7 @@ impl MouseEvent {
}
}
- pub fn init_wrapper(@mut self) {
- let script_context = global_script_context();
- let cx = script_context.js_compartment.cx.ptr;
- let owner = script_context.root_frame.get_ref().window;
- let cache = owner.get_wrappercache();
- let scope = cache.get_wrapper();
+ pub fn init_wrapper(@mut self, cx: *JSContext, scope: *JSObject) {
self.wrap_object_shared(cx, scope);
}
@@ -181,4 +175,4 @@ impl DerivedWrapper for MouseEvent {
}
}
-} \ No newline at end of file
+}
diff --git a/src/components/script/dom/node.rs b/src/components/script/dom/node.rs
index e7cd5f01d33..ab2023151ed 100644
--- a/src/components/script/dom/node.rs
+++ b/src/components/script/dom/node.rs
@@ -11,13 +11,13 @@ use dom::characterdata::CharacterData;
use dom::document::Document;
use dom::element::{Element, ElementTypeId, HTMLImageElement, HTMLImageElementTypeId, HTMLIframeElementTypeId, HTMLIframeElement};
use dom::element::{HTMLStyleElementTypeId};
-use script_task::global_script_context;
use std::cast;
use std::cast::transmute;
use std::libc::c_void;
use std::uint;
use js::rust::Compartment;
+use js::jsapi::{JSContext};
use netsurfcss::util::VoidPtrLike;
use servo_util::tree::{TreeNode, TreeNodeRef, TreeUtils};
@@ -424,12 +424,11 @@ impl<View> Iterator<AbstractNode<View>> for AbstractNodeChildrenIterator<View> {
}
impl Node<ScriptView> {
- pub unsafe fn as_abstract_node<N>(node: ~N) -> AbstractNode<ScriptView> {
+ pub unsafe fn as_abstract_node<N>(cx: *JSContext, node: ~N) -> AbstractNode<ScriptView> {
// This surrenders memory management of the node!
let mut node = AbstractNode {
obj: transmute(node),
};
- let cx = global_script_context().js_compartment.cx.ptr;
node::create(cx, &mut node);
node
}
diff --git a/src/components/script/dom/uievent.rs b/src/components/script/dom/uievent.rs
index 65bf468b0bb..417eccaaebe 100644
--- a/src/components/script/dom/uievent.rs
+++ b/src/components/script/dom/uievent.rs
@@ -10,8 +10,6 @@ use dom::event::Event_;
use dom::window::Window;
use dom::windowproxy::WindowProxy;
-use script_task::global_script_context;
-
use js::glue::RUST_OBJECT_TO_JSVAL;
use js::jsapi::{JSObject, JSContext, JSVal};
@@ -35,12 +33,7 @@ impl UIEvent {
}
}
- pub fn init_wrapper(@mut self) {
- let script_context = global_script_context();
- let cx = script_context.js_compartment.cx.ptr;
- let owner = script_context.root_frame.get_ref().window;
- let cache = owner.get_wrappercache();
- let scope = cache.get_wrapper();
+ pub fn init_wrapper(@mut self, cx: *JSContext, scope: *JSObject) {
self.wrap_object_shared(cx, scope);
}
diff --git a/src/components/script/dom/window.rs b/src/components/script/dom/window.rs
index 7c69ea5aee5..213b211ed91 100644
--- a/src/components/script/dom/window.rs
+++ b/src/components/script/dom/window.rs
@@ -6,7 +6,8 @@ use dom::bindings::utils::WrapperCache;
use dom::bindings::window;
use layout_interface::ReflowForScriptQuery;
-use script_task::{ExitMsg, FireTimerMsg, ScriptChan, ScriptTask};
+use script_task::{ExitMsg, FireTimerMsg, Page, ScriptChan};
+use servo_msg::compositor_msg::ScriptListener;
use std::comm;
use std::comm::Chan;
@@ -27,12 +28,14 @@ pub enum TimerControlMsg {
//FIXME If we're going to store the script task, find a way to do so safely. Currently it's
// only used for querying layout from arbitrary script.
pub struct Window {
- timer_chan: Chan<TimerControlMsg>,
+ page: *mut Page,
script_chan: ScriptChan,
- script_task: *mut ScriptTask,
- wrapper: WrapperCache
+ compositor: @ScriptListener,
+ wrapper: WrapperCache,
+ timer_chan: Chan<TimerControlMsg>,
}
+#[unsafe_destructor]
impl Drop for Window {
fn drop(&self) {
self.timer_chan.send(TimerMessage_Close);
@@ -89,34 +92,37 @@ impl Window {
pub fn content_changed(&self) {
unsafe {
- (*self.script_task).reflow_all(ReflowForScriptQuery)
+ // TODO(tkuehn): currently reflow top-level, but want to reflow only the associated frame
+ (*self.page).reflow_all(ReflowForScriptQuery, self.script_chan.clone(), self.compositor);
}
}
- pub fn new(script_chan: ScriptChan, script_task: *mut ScriptTask)
+ pub fn new(page: *mut Page, script_chan: ScriptChan, compositor: @ScriptListener)
-> @mut Window {
let script_chan_clone = script_chan.clone();
let win = @mut Window {
- wrapper: WrapperCache::new(),
+ page: page,
script_chan: script_chan,
+ compositor: compositor,
+ wrapper: WrapperCache::new(),
timer_chan: {
let (timer_port, timer_chan) = comm::stream::<TimerControlMsg>();
do spawn {
loop {
match timer_port.recv() {
TimerMessage_Close => break,
- TimerMessage_Fire(td) => script_chan_clone.chan.send(FireTimerMsg(td)),
+ TimerMessage_Fire(td) => unsafe {script_chan_clone.chan.send(FireTimerMsg((*page).id.clone(), td))},
TimerMessage_TriggerExit => script_chan_clone.chan.send(ExitMsg),
}
}
}
timer_chan
},
- script_task: script_task,
};
unsafe {
- let compartment = (*script_task).js_compartment;
+ // TODO(tkuehn): This just grabs the top-level page. Need to handle subframes.
+ let compartment = (*page).js_info.get_ref().js_compartment;
window::create(compartment, win);
}
win
diff --git a/src/components/script/dom/windowproxy.rs b/src/components/script/dom/windowproxy.rs
index 89b373c0716..350a9e87df6 100644
--- a/src/components/script/dom/windowproxy.rs
+++ b/src/components/script/dom/windowproxy.rs
@@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::bindings::utils::{CacheableWrapper, WrapperCache, BindingObject};
-use script_task::{global_script_context, task_from_context};
+use script_task::page_from_context;
use js::jsapi::{JSContext, JSObject};
@@ -18,21 +18,16 @@ impl WindowProxy {
}
}
- pub fn init_wrapper(@mut self) {
- let script_context = global_script_context();
- let cx = script_context.js_compartment.cx.ptr;
- let owner = script_context.root_frame.get_ref().window;
- let cache = owner.get_wrappercache();
- let scope = cache.get_wrapper();
+ pub fn init_wrapper(@mut self, cx: *JSContext, scope: *JSObject) {
self.wrap_object_shared(cx, scope);
}
}
impl BindingObject for WindowProxy {
fn GetParentObject(&self, cx: *JSContext) -> @mut CacheableWrapper {
- let script_context = task_from_context(cx);
+ let page = page_from_context(cx);
unsafe {
- (*script_context).root_frame.get_ref().window as @mut CacheableWrapper
+ (*page).frame.get_ref().window as @mut CacheableWrapper
}
}
}
diff --git a/src/components/script/html/hubbub_html_parser.rs b/src/components/script/html/hubbub_html_parser.rs
index 2f5591464cf..e1ad1c28d34 100644
--- a/src/components/script/html/hubbub_html_parser.rs
+++ b/src/components/script/html/hubbub_html_parser.rs
@@ -31,6 +31,7 @@ use dom::element::{Element, Attr};
use dom::node::{AbstractNode, Comment, Doctype, ElementNodeTypeId, Node, ScriptView};
use dom::node::{Text};
use html::cssparse::{InlineProvenance, StylesheetProvenance, UrlProvenance, spawn_css_parser};
+use js::jsapi::JSContext;
use newcss::stylesheet::Stylesheet;
use std::cast;
@@ -48,9 +49,11 @@ use servo_util::tree::TreeUtils;
use servo_util::url::make_url;
use extra::net::url::Url;
use extra::net::url;
+use extra::future::{Future, from_port};
+use geom::size::Size2D;
macro_rules! handle_element(
- ($tag:expr, $string:expr, $type_id:expr, $ctor:ident, [ $(($field:ident : $field_init:expr)),* ]) => (
+ ($cx: expr, $tag:expr, $string:expr, $type_id:expr, $ctor:ident, [ $(($field:ident : $field_init:expr)),* ]) => (
if eq_slice($tag, $string) {
let _element = ~$ctor {
parent: Element::new($type_id, ($tag).to_str()),
@@ -59,7 +62,7 @@ macro_rules! handle_element(
)*
};
unsafe {
- return Node::as_abstract_node(_element);
+ return Node::as_abstract_node(cx, _element);
}
}
)
@@ -79,7 +82,8 @@ enum JSMessage {
pub struct HtmlParserResult {
root: AbstractNode<ScriptView>,
- style_port: Port<Option<Stylesheet>>,
+ style_port: Port<Stylesheet>,
+ iframe_port: Port<(Url, Future<Size2D<uint>>)>,
js_port: Port<JSResult>,
}
@@ -112,7 +116,7 @@ spawned, collates them, and sends them to the given result channel.
* `from_parent` - A port on which to receive new links.
*/
-fn css_link_listener(to_parent: Chan<Option<Stylesheet>>,
+fn css_link_listener(to_parent: Chan<Stylesheet>,
from_parent: Port<CSSMessage>,
resource_task: ResourceTask) {
let mut result_vec = ~[];
@@ -131,9 +135,8 @@ fn css_link_listener(to_parent: Chan<Option<Stylesheet>>,
// Send the sheets back in order
// FIXME: Shouldn't wait until after we've recieved CSSTaskExit to start sending these
for result_vec.iter().advance |port| {
- to_parent.send(Some(port.recv()));
+ to_parent.send(port.recv());
}
- to_parent.send(None);
}
fn js_script_listener(to_parent: Chan<~[~[u8]]>,
@@ -184,63 +187,62 @@ fn js_script_listener(to_parent: Chan<~[~[u8]]>,
// Silly macros to handle constructing DOM nodes. This produces bad code and should be optimized
// via atomization (issue #85).
-fn build_element_from_tag(tag: &str) -> AbstractNode<ScriptView> {
+fn build_element_from_tag(cx: *JSContext, tag: &str) -> AbstractNode<ScriptView> {
// TODO (Issue #85): use atoms
- handle_element!(tag, "a", HTMLAnchorElementTypeId, HTMLAnchorElement, []);
- handle_element!(tag, "aside", HTMLAsideElementTypeId, HTMLAsideElement, []);
- handle_element!(tag, "br", HTMLBRElementTypeId, HTMLBRElement, []);
- handle_element!(tag, "body", HTMLBodyElementTypeId, HTMLBodyElement, []);
- handle_element!(tag, "bold", HTMLBoldElementTypeId, HTMLBoldElement, []);
- handle_element!(tag, "div", HTMLDivElementTypeId, HTMLDivElement, []);
- handle_element!(tag, "font", HTMLFontElementTypeId, HTMLFontElement, []);
- handle_element!(tag, "form", HTMLFormElementTypeId, HTMLFormElement, []);
- handle_element!(tag, "hr", HTMLHRElementTypeId, HTMLHRElement, []);
- handle_element!(tag, "head", HTMLHeadElementTypeId, HTMLHeadElement, []);
- handle_element!(tag, "html", HTMLHtmlElementTypeId, HTMLHtmlElement, []);
- handle_element!(tag, "input", HTMLInputElementTypeId, HTMLInputElement, []);
- handle_element!(tag, "i", HTMLItalicElementTypeId, HTMLItalicElement, []);
- handle_element!(tag, "link", HTMLLinkElementTypeId, HTMLLinkElement, []);
- handle_element!(tag, "li", HTMLListItemElementTypeId, HTMLListItemElement, []);
- handle_element!(tag, "meta", HTMLMetaElementTypeId, HTMLMetaElement, []);
- handle_element!(tag, "ol", HTMLOListElementTypeId, HTMLOListElement, []);
- handle_element!(tag, "option", HTMLOptionElementTypeId, HTMLOptionElement, []);
- handle_element!(tag, "p", HTMLParagraphElementTypeId, HTMLParagraphElement, []);
- handle_element!(tag, "script", HTMLScriptElementTypeId, HTMLScriptElement, []);
- handle_element!(tag, "section", HTMLSectionElementTypeId, HTMLSectionElement, []);
- handle_element!(tag, "select", HTMLSelectElementTypeId, HTMLSelectElement, []);
- handle_element!(tag, "small", HTMLSmallElementTypeId, HTMLSmallElement, []);
- handle_element!(tag, "span", HTMLSpanElementTypeId, HTMLSpanElement, []);
- handle_element!(tag, "style", HTMLStyleElementTypeId, HTMLStyleElement, []);
- handle_element!(tag, "tbody", HTMLTableBodyElementTypeId, HTMLTableBodyElement, []);
- handle_element!(tag, "td", HTMLTableCellElementTypeId, HTMLTableCellElement, []);
- handle_element!(tag, "table", HTMLTableElementTypeId, HTMLTableElement, []);
- handle_element!(tag, "tr", HTMLTableRowElementTypeId, HTMLTableRowElement, []);
- handle_element!(tag, "title", HTMLTitleElementTypeId, HTMLTitleElement, []);
- handle_element!(tag, "ul", HTMLUListElementTypeId, HTMLUListElement, []);
-
- handle_element!(tag, "img", HTMLImageElementTypeId, HTMLImageElement, [(image: None)]);
- handle_element!(tag, "iframe", HTMLIframeElementTypeId, HTMLIframeElement,
- [(frame: None), (parse_result: None)]);
-
- handle_element!(tag, "h1", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading1)]);
- handle_element!(tag, "h2", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading2)]);
- handle_element!(tag, "h3", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading3)]);
- handle_element!(tag, "h4", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading4)]);
- handle_element!(tag, "h5", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading5)]);
- handle_element!(tag, "h6", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading6)]);
+ handle_element!(cx, tag, "a", HTMLAnchorElementTypeId, HTMLAnchorElement, []);
+ handle_element!(cx, tag, "aside", HTMLAsideElementTypeId, HTMLAsideElement, []);
+ handle_element!(cx, tag, "br", HTMLBRElementTypeId, HTMLBRElement, []);
+ handle_element!(cx, tag, "body", HTMLBodyElementTypeId, HTMLBodyElement, []);
+ handle_element!(cx, tag, "bold", HTMLBoldElementTypeId, HTMLBoldElement, []);
+ handle_element!(cx, tag, "div", HTMLDivElementTypeId, HTMLDivElement, []);
+ handle_element!(cx, tag, "font", HTMLFontElementTypeId, HTMLFontElement, []);
+ handle_element!(cx, tag, "form", HTMLFormElementTypeId, HTMLFormElement, []);
+ handle_element!(cx, tag, "hr", HTMLHRElementTypeId, HTMLHRElement, []);
+ handle_element!(cx, tag, "head", HTMLHeadElementTypeId, HTMLHeadElement, []);
+ handle_element!(cx, tag, "html", HTMLHtmlElementTypeId, HTMLHtmlElement, []);
+ handle_element!(cx, tag, "input", HTMLInputElementTypeId, HTMLInputElement, []);
+ handle_element!(cx, tag, "i", HTMLItalicElementTypeId, HTMLItalicElement, []);
+ handle_element!(cx, tag, "link", HTMLLinkElementTypeId, HTMLLinkElement, []);
+ handle_element!(cx, tag, "li", HTMLListItemElementTypeId, HTMLListItemElement, []);
+ handle_element!(cx, tag, "meta", HTMLMetaElementTypeId, HTMLMetaElement, []);
+ handle_element!(cx, tag, "ol", HTMLOListElementTypeId, HTMLOListElement, []);
+ handle_element!(cx, tag, "option", HTMLOptionElementTypeId, HTMLOptionElement, []);
+ handle_element!(cx, tag, "p", HTMLParagraphElementTypeId, HTMLParagraphElement, []);
+ handle_element!(cx, tag, "script", HTMLScriptElementTypeId, HTMLScriptElement, []);
+ handle_element!(cx, tag, "section", HTMLSectionElementTypeId, HTMLSectionElement, []);
+ handle_element!(cx, tag, "select", HTMLSelectElementTypeId, HTMLSelectElement, []);
+ handle_element!(cx, tag, "small", HTMLSmallElementTypeId, HTMLSmallElement, []);
+ handle_element!(cx, tag, "span", HTMLSpanElementTypeId, HTMLSpanElement, []);
+ handle_element!(cx, tag, "style", HTMLStyleElementTypeId, HTMLStyleElement, []);
+ handle_element!(cx, tag, "tbody", HTMLTableBodyElementTypeId, HTMLTableBodyElement, []);
+ handle_element!(cx, tag, "td", HTMLTableCellElementTypeId, HTMLTableCellElement, []);
+ handle_element!(cx, tag, "table", HTMLTableElementTypeId, HTMLTableElement, []);
+ handle_element!(cx, tag, "tr", HTMLTableRowElementTypeId, HTMLTableRowElement, []);
+ handle_element!(cx, tag, "title", HTMLTitleElementTypeId, HTMLTitleElement, []);
+ handle_element!(cx, tag, "ul", HTMLUListElementTypeId, HTMLUListElement, []);
+
+ handle_element!(cx, tag, "img", HTMLImageElementTypeId, HTMLImageElement, [(image: None)]);
+ handle_element!(cx, tag, "iframe", HTMLIframeElementTypeId, HTMLIframeElement, [(frame: None), (size_future_chan: None)]);
+
+ handle_element!(cx, tag, "h1", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading1)]);
+ handle_element!(cx, tag, "h2", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading2)]);
+ handle_element!(cx, tag, "h3", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading3)]);
+ handle_element!(cx, tag, "h4", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading4)]);
+ handle_element!(cx, tag, "h5", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading5)]);
+ handle_element!(cx, tag, "h6", HTMLHeadingElementTypeId, HTMLHeadingElement, [(level: Heading6)]);
unsafe {
- Node::as_abstract_node(~Element::new(UnknownElementTypeId, tag.to_str()))
+ Node::as_abstract_node(cx, ~Element::new(UnknownElementTypeId, tag.to_str()))
}
}
#[allow(non_implicitly_copyable_typarams)]
-pub fn parse_html(url: Url,
+pub fn parse_html(cx: *JSContext,
+ url: Url,
resource_task: ResourceTask,
image_cache_task: ImageCacheTask) -> HtmlParserResult {
// Spawn a CSS parser to receive links to CSS style sheets.
let resource_task2 = resource_task.clone();
- let resource_task3 = resource_task.clone();
let (stylesheet_port, stylesheet_chan) = comm::stream();
let stylesheet_chan = Cell::new(stylesheet_chan);
@@ -268,7 +270,7 @@ pub fn parse_html(url: Url,
// Build the root node.
let root = ~HTMLHtmlElement { parent: Element::new(HTMLHtmlElementTypeId, ~"html") };
- let root = unsafe { Node::as_abstract_node(root) };
+ let root = unsafe { Node::as_abstract_node(cx, root) };
debug!("created new node");
let mut parser = hubbub::Parser("UTF-8", false);
debug!("created parser");
@@ -277,11 +279,12 @@ pub fn parse_html(url: Url,
parser.enable_styling(true);
let (css_chan2, css_chan3, js_chan2) = (css_chan.clone(), css_chan.clone(), js_chan.clone());
+ let (iframe_port, iframe_chan) = comm::stream();
parser.set_tree_handler(~hubbub::TreeHandler {
create_comment: |data: ~str| {
debug!("create comment");
unsafe {
- Node::as_abstract_node(~Comment::new(data)).to_hubbub_node()
+ Node::as_abstract_node(cx, ~Comment::new(data)).to_hubbub_node()
}
},
create_doctype: |doctype: ~hubbub::Doctype| {
@@ -295,12 +298,12 @@ pub fn parse_html(url: Url,
system_id,
force_quirks);
unsafe {
- Node::as_abstract_node(node).to_hubbub_node()
+ Node::as_abstract_node(cx, node).to_hubbub_node()
}
},
create_element: |tag: ~hubbub::Tag| {
debug!("create element");
- let node = build_element_from_tag(tag.name);
+ let node = build_element_from_tag(cx, tag.name);
debug!("-- attach attrs");
do node.as_mut_element |element| {
@@ -325,30 +328,22 @@ pub fn parse_html(url: Url,
_ => {}
}
}
- },
+ }
+
ElementNodeTypeId(HTMLIframeElementTypeId) => {
do node.with_mut_iframe_element |iframe_element| {
let src_opt = iframe_element.parent.get_attr("src").map(|x| x.to_str());
- match src_opt {
- None => {}
- Some(src) => {
- let iframe_url = make_url(src, Some(url2.clone()));
- iframe_element.frame = Some(iframe_url.clone());
- let (parse_port, parse_chan) = comm::stream();
- iframe_element.parse_result = Some(parse_port);
- let image_cache_task2 = Cell::new(image_cache_task.clone());
- let resource_task3 = Cell::new(resource_task3.clone());
- let iframe_url = Cell::new(iframe_url);
- do task::spawn {
- let result = parse_html(iframe_url.take(),
- resource_task3.take(),
- image_cache_task2.take());
- parse_chan.send(result);
- }
- }
+ for src_opt.iter().advance |src| {
+ let iframe_url = make_url(src.clone(), Some(url2.clone()));
+ iframe_element.frame = Some(iframe_url.clone());
+ let (port, chan) = comm::oneshot();
+ iframe_element.size_future_chan = Some(chan);
+ let size_future = from_port(port);
+ iframe_chan.send((iframe_url, size_future));
}
}
}
+
ElementNodeTypeId(HTMLImageElementTypeId) => {
do node.with_mut_image_element |image_element| {
let src_opt = image_element.parent.get_attr("src").map(|x| x.to_str());
@@ -365,6 +360,7 @@ pub fn parse_html(url: Url,
}
}
}
+
//TODO (Issue #86): handle inline styles ('style' attr)
_ => {}
}
@@ -374,7 +370,7 @@ pub fn parse_html(url: Url,
create_text: |data: ~str| {
debug!("create text");
unsafe {
- Node::as_abstract_node(~Text::new(data)).to_hubbub_node()
+ Node::as_abstract_node(cx, ~Text::new(data)).to_hubbub_node()
}
},
ref_node: |_| {},
@@ -496,6 +492,7 @@ pub fn parse_html(url: Url,
HtmlParserResult {
root: root,
style_port: stylesheet_port,
+ iframe_port: iframe_port,
js_port: js_result_port,
}
}
diff --git a/src/components/script/layout_interface.rs b/src/components/script/layout_interface.rs
index 766c728067b..9c9f27328c6 100644
--- a/src/components/script/layout_interface.rs
+++ b/src/components/script/layout_interface.rs
@@ -7,8 +7,7 @@
/// from layout.
use dom::node::{AbstractNode, ScriptView, LayoutView};
-use script_task::{ScriptMsg, ScriptChan};
-
+use script_task::{ScriptChan};
use std::comm::{Chan, SharedChan};
use geom::rect::Rect;
use geom::size::Size2D;
@@ -32,9 +31,6 @@ pub enum Msg {
/// FIXME(pcwalton): As noted below, this isn't very type safe.
QueryMsg(LayoutQuery),
- /// Routes a message (usually from the compositor) to the appropriate script task
- RouteScriptMsg(ScriptMsg),
-
/// Requests that the layout task shut down and exit.
ExitMsg,
}
diff --git a/src/components/script/script_task.rs b/src/components/script/script_task.rs
index b0d9af20cfc..bd52d3f8e20 100644
--- a/src/components/script/script_task.rs
+++ b/src/components/script/script_task.rs
@@ -11,7 +11,7 @@ use dom::bindings::utils::GlobalStaticData;
use dom::document::Document;
use dom::element::Element;
use dom::event::{Event, ResizeEvent, ReflowEvent, ClickEvent, MouseDownEvent, MouseUpEvent};
-use dom::node::{AbstractNode, ScriptView, define_bindings};
+use dom::node::define_bindings;
use dom::window::Window;
use layout_interface::{AddStylesheetMsg, DocumentDamage};
use layout_interface::{DocumentDamageLevel, HitTestQuery, HitTestResponse, LayoutQuery};
@@ -20,20 +20,22 @@ use layout_interface::{ReflowDocumentDamage, ReflowForDisplay, ReflowGoal};
use layout_interface::ReflowMsg;
use layout_interface;
use servo_msg::constellation_msg::{ConstellationChan, LoadUrlMsg, NavigationDirection};
-use servo_msg::constellation_msg::{RendererReadyMsg, ResizedWindowBroadcast};
+use servo_msg::constellation_msg::{PipelineId, RendererReadyMsg, ResizedWindowBroadcast};
+use servo_msg::constellation_msg::{LoadIframeUrlMsg};
use servo_msg::constellation_msg;
-use std::cast::transmute;
+use newcss::stylesheet::Stylesheet;
+
use std::cell::Cell;
use std::comm;
-use std::comm::{Port, SharedChan};
+use std::comm::{Port, SharedChan, Select2};
use std::io::read_whole_file;
-use std::local_data;
use std::ptr::null;
use std::task::{SingleThreaded, task};
use std::util::replace;
use dom::window::TimerData;
use geom::size::Size2D;
+use html::hubbub_html_parser::HtmlParserResult;
use html::hubbub_html_parser;
use js::JSVAL_NULL;
use js::global::{global_class, debug_fns};
@@ -48,27 +50,37 @@ use servo_util::tree::TreeNodeRef;
use servo_util::url::make_url;
use extra::net::url::Url;
use extra::net::url;
+use extra::future::{from_value, Future};
/// Messages used to control the script task.
pub enum ScriptMsg {
- /// Loads a new URL.
- LoadMsg(Url),
+ /// Loads a new URL on the specified pipeline.
+ LoadMsg(PipelineId, Url),
+ /// Gives a channel and ID to a layout task, as well as the ID of that layout's parent
+ AttachLayoutMsg(NewLayoutInfo),
/// Executes a standalone script.
- ExecuteMsg(Url),
+ ExecuteMsg(PipelineId, Url),
/// Instructs the script task to send a navigate message to the constellation.
- NavigateMsg(NavigationDirection),
+ NavigateMsg(PipelineId, NavigationDirection),
/// Sends a DOM event.
- SendEventMsg(Event),
+ SendEventMsg(PipelineId, Event),
/// Fires a JavaScript timeout.
- FireTimerMsg(~TimerData),
+ FireTimerMsg(PipelineId, ~TimerData),
/// Notifies script that reflow is finished.
- ReflowCompleteMsg,
+ ReflowCompleteMsg(PipelineId),
/// Notifies script that window has been resized but to not take immediate action.
ResizeInactiveMsg(Size2D<uint>),
/// Exits the constellation.
ExitMsg,
}
+pub struct NewLayoutInfo {
+ old_id: PipelineId,
+ new_id: PipelineId,
+ layout_chan: LayoutChan,
+ size_future: Future<Size2D<uint>>,
+}
+
/// Encapsulates external communication with the script task.
#[deriving(Clone)]
pub struct ScriptChan {
@@ -77,7 +89,7 @@ pub struct ScriptChan {
}
impl ScriptChan {
- /// Creates a new script task.
+ /// Creates a new script chan.
pub fn new(chan: Chan<ScriptMsg>) -> ScriptChan {
ScriptChan {
chan: SharedChan::new(chan)
@@ -88,11 +100,242 @@ impl ScriptChan {
}
}
+/// Encapsulates a handle to a frame and its associate layout information
+pub struct Page {
+ /// Pipeline id associated with this page.
+ id: PipelineId,
+
+ /// The outermost frame containing the document, window, and page URL.
+ frame: Option<Frame>,
+
+ /// A handle for communicating messages to the layout task.
+ layout_chan: LayoutChan,
+
+ /// The port that we will use to join layout. If this is `None`, then layout is not running.
+ layout_join_port: Option<Port<()>>,
+
+ /// What parts of the document are dirty, if any.
+ damage: Option<DocumentDamage>,
+
+ /// The current size of the window, in pixels.
+ window_size: Future<Size2D<uint>>,
+
+ js_info: Option<JSPageInfo>,
+
+ /// Cached copy of the most recent url loaded by the script
+ /// 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: Option<(Url, bool)>,
+}
+
+pub struct PageTree {
+ page: @mut Page,
+ inner: ~[PageTree],
+}
+
+pub struct PageTreeIterator<'self> {
+ priv stack: ~[&'self mut PageTree],
+}
+
+impl PageTree {
+ fn new(id: PipelineId, layout_chan: LayoutChan, size_future: Future<Size2D<uint>>) -> PageTree {
+ PageTree {
+ page: @mut Page {
+ id: id,
+ frame: None,
+ layout_chan: layout_chan,
+ layout_join_port: None,
+ damage: None,
+ window_size: size_future,
+ js_info: None,
+ url: None,
+ },
+ inner: ~[],
+ }
+ }
+
+ pub fn find<'a> (&'a mut self, id: PipelineId) -> Option<&'a mut PageTree> {
+ if self.page.id == id { return Some(self); }
+ for self.inner.mut_iter().advance |page_tree| {
+ let found = page_tree.find(id);
+ if found.is_some() { return found; }
+ }
+ None
+ }
+
+ pub fn iter<'a>(&'a mut self) -> PageTreeIterator<'a> {
+ PageTreeIterator {
+ stack: ~[self],
+ }
+ }
+}
+
+impl<'self> Iterator<@mut Page> for PageTreeIterator<'self> {
+ fn next(&mut self) -> Option<@mut Page> {
+ if !self.stack.is_empty() {
+ let next = self.stack.pop();
+ {
+ for next.inner.mut_iter().advance |child| {
+ self.stack.push(child);
+ }
+ }
+ Some(next.page)
+ } else {
+ None
+ }
+ }
+}
+
+impl Page {
+ /// Adds the given damage.
+ fn damage(&mut self, level: DocumentDamageLevel) {
+ match self.damage {
+ None => {}
+ Some(ref mut damage) => {
+ // FIXME(pcwalton): This is wrong. We should trace up to the nearest ancestor.
+ damage.root = self.frame.get_ref().document.root;
+ damage.level.add(level);
+ return
+ }
+ }
+
+ self.damage = Some(DocumentDamage {
+ root: self.frame.get_ref().document.root,
+ level: level,
+ })
+ }
+
+ /// 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(&mut self) {
+ if self.layout_join_port.is_some() {
+ let join_port = replace(&mut self.layout_join_port, None);
+ match join_port {
+ Some(ref join_port) => {
+ if !join_port.peek() {
+ info!("script: waiting on layout");
+ }
+
+ join_port.recv();
+
+ debug!("script: layout joined")
+ }
+ None => fail!(~"reader forked but no join port?"),
+ }
+ }
+ }
+
+ /// Sends the given query to layout.
+ pub fn query_layout<T: Send>(&mut self,
+ query: LayoutQuery,
+ response_port: Port<Result<T, ()>>)
+ -> Result<T,()> {
+ self.join_layout();
+ self.layout_chan.send(QueryMsg(query));
+ response_port.recv()
+ }
+
+ /// This method will wait until the layout task has completed its current action, join the
+ /// layout task, and then request a new layout run. It won't wait for the new layout
+ /// computation to finish.
+ ///
+ /// This function fails if there is no root frame.
+ fn reflow(&mut self, goal: ReflowGoal, script_chan: ScriptChan, compositor: @ScriptListener) {
+
+ debug!("script: performing reflow for goal %?", goal);
+
+ // Now, join the layout so that they will see the latest changes we have made.
+ self.join_layout();
+
+ // Tell the user that we're performing layout.
+ compositor.set_ready_state(PerformingLayout);
+
+ // Layout will let us know when it's done.
+ let (join_port, join_chan) = comm::stream();
+ self.layout_join_port = Some(join_port);
+
+ match self.frame {
+ None => fail!(~"Tried to relayout with no root frame!"),
+ Some(ref frame) => {
+ // Send new document and relevant styles to layout.
+ let reflow = ~Reflow {
+ document_root: frame.document.root,
+ url: copy self.url.get_ref().first(),
+ goal: goal,
+ window_size: self.window_size.get(),
+ script_chan: script_chan,
+ script_join_chan: join_chan,
+ damage: replace(&mut self.damage, None).unwrap(),
+ };
+
+ self.layout_chan.send(ReflowMsg(reflow))
+ }
+ }
+
+ debug!("script: layout forked")
+ }
+
+ /// Reflows the entire document.
+ ///
+ /// FIXME: This should basically never be used.
+ pub fn reflow_all(&mut self, goal: ReflowGoal, script_chan: ScriptChan, compositor: @ScriptListener) {
+ if self.frame.is_some() {
+ self.damage(MatchSelectorsDocumentDamage);
+ }
+
+ self.reflow(goal, script_chan, compositor)
+ }
+
+ pub fn initialize_js_info(&mut self, js_context: @Cx) {
+ // Note that the order that these variables are initialized is _not_ arbitrary. Switching them around
+ // can -- and likely will -- lead to things breaking.
+
+ js_context.set_default_options_and_version();
+ js_context.set_logging_error_reporter();
+
+ let compartment = match js_context.new_compartment(global_class) {
+ Ok(c) => c,
+ Err(()) => fail!("Failed to create a compartment"),
+ };
+
+ // Indirection for Rust Issue #6248, dynamic freeze scope artifically extended
+ let page_ptr = {
+ let borrowed_page = &mut *self;
+ borrowed_page as *mut Page
+ };
+
+ unsafe {
+ js_context.set_cx_private(page_ptr as *());
+ }
+
+ self.js_info = Some(JSPageInfo {
+ dom_static: GlobalStaticData(),
+ bindings_initialized: false,
+ js_compartment: compartment,
+ js_context: js_context,
+ });
+ }
+
+}
+
/// Information for one frame in the browsing context.
pub struct Frame {
document: @mut Document,
window: @mut Window,
- url: Url,
+
+}
+
+/// Encapsulation of the javascript information associated with each frame.
+pub struct JSPageInfo {
+ /// Global static data related to the DOM.
+ dom_static: GlobalStaticData,
+ /// Flag indicating if the JS bindings have been initialized.
+ bindings_initialized: bool,
+ /// The JavaScript compartment for the origin associated with the script task.
+ js_compartment: @mut Compartment,
+ /// The JavaScript context.
+ js_context: @Cx,
}
/// Information for an entire page. Pages are top-level browsing contexts and can contain multiple
@@ -100,135 +343,64 @@ pub struct Frame {
///
/// FIXME: Rename to `Page`, following WebKit?
pub struct ScriptTask {
- /// A unique identifier to the script's pipeline
- id: uint,
- /// A handle to the layout task.
- layout_chan: LayoutChan,
+ /// A handle to the information pertaining to page layout
+ page_tree: PageTree,
/// A handle to the image cache task.
image_cache_task: ImageCacheTask,
/// A handle to the resource task.
resource_task: ResourceTask,
- /// The port that we will use to join layout. If this is `None`, then layout is not currently
- /// running.
- layout_join_port: Option<Port<()>>,
- /// The port on which we receive messages (load URL, exit, etc.)
- script_port: Port<ScriptMsg>,
- /// A channel for us to hand out when we want some other task to be able to send us script
- /// messages.
- script_chan: ScriptChan,
+ /// The port on which the script task receives messages (load URL, exit, etc.)
+ port: Port<ScriptMsg>,
+ /// A channel to hand out when some other task needs to be able to respond to a message from
+ /// the script task.
+ chan: ScriptChan,
/// For communicating load url messages to the constellation
constellation_chan: ConstellationChan,
- /// For permission to communicate ready state messages to the compositor
+ /// A handle to the compositor for communicating ready state messages.
compositor: @ScriptListener,
/// The JavaScript runtime.
js_runtime: js::rust::rt,
- /// The JavaScript context.
- js_context: @Cx,
- /// The JavaScript compartment.
- js_compartment: @mut Compartment,
-
- /// Global static data related to the DOM.
- dom_static: GlobalStaticData,
- /// Whether the JS bindings have been initialized.
- bindings_initialized: bool,
-
- /// The outermost frame. This frame contains the document, window, and page URL.
- root_frame: Option<Frame>,
-
- /// The current size of the window, in pixels.
- window_size: Size2D<uint>,
- /// What parts of the document are dirty, if any.
- damage: Option<DocumentDamage>,
-
- /// Cached copy of the most recent url loaded by the script
- /// TODO(tkuehn): this currently does not follow any particular caching policy
- /// and simply caches pages forever (!). The bool indicates if reflow is required
- last_loaded_url: Option<(Url, bool)>,
}
-fn global_script_context_key(_: @ScriptTask) {}
-
-/// Returns this task's script context singleton.
-pub fn global_script_context() -> @ScriptTask {
+/// Returns the relevant page from the associated JS Context.
+pub fn page_from_context(js_context: *JSContext) -> *mut Page {
unsafe {
- local_data::local_data_get(global_script_context_key).get()
- }
-}
-
-/// Returns the script task from the JS Context.
-///
-/// FIXME: Rename to `script_context_from_js_context`.
-pub fn task_from_context(js_context: *JSContext) -> *mut ScriptTask {
- unsafe {
- JS_GetContextPrivate(js_context) as *mut ScriptTask
+ JS_GetContextPrivate(js_context) as *mut Page
}
}
impl ScriptTask {
/// Creates a new script task.
- pub fn new(id: uint,
+ pub fn new(id: PipelineId,
compositor: @ScriptListener,
layout_chan: LayoutChan,
- script_port: Port<ScriptMsg>,
- script_chan: ScriptChan,
+ port: Port<ScriptMsg>,
+ chan: ScriptChan,
constellation_chan: ConstellationChan,
resource_task: ResourceTask,
img_cache_task: ImageCacheTask,
- initial_size: Size2D<int>)
+ initial_size: Future<Size2D<uint>>)
-> @mut ScriptTask {
let js_runtime = js::rust::rt();
- let js_context = js_runtime.cx();
-
- js_context.set_default_options_and_version();
- js_context.set_logging_error_reporter();
-
- let compartment = match js_context.new_compartment(global_class) {
- Ok(c) => c,
- Err(()) => fail!("Failed to create a compartment"),
- };
let script_task = @mut ScriptTask {
- id: id,
- compositor: compositor,
+ page_tree: PageTree::new(id, layout_chan, initial_size),
- layout_chan: layout_chan,
image_cache_task: img_cache_task,
resource_task: resource_task,
- layout_join_port: None,
- script_port: script_port,
- script_chan: script_chan,
-
+ port: port,
+ chan: chan,
constellation_chan: constellation_chan,
+ compositor: compositor,
js_runtime: js_runtime,
- js_context: js_context,
- js_compartment: compartment,
-
- dom_static: GlobalStaticData(),
- bindings_initialized: false,
-
- root_frame: None,
-
- window_size: Size2D(initial_size.width as uint, initial_size.height as uint),
- damage: None,
-
- last_loaded_url: None,
};
- // Indirection for Rust Issue #6248, dynamic freeze scope artifically extended
- let script_task_ptr = {
- let borrowed_ctx= &mut *script_task;
- borrowed_ctx as *mut ScriptTask
- };
-
- unsafe {
- js_context.set_cx_private(script_task_ptr as *());
- local_data::local_data_set(global_script_context_key, transmute(script_task))
- }
+ script_task.page_tree.page.initialize_js_info(script_task.js_runtime.cx());
script_task
}
@@ -240,17 +412,18 @@ impl ScriptTask {
}
}
- pub fn create<C: ScriptListener + Send>(id: uint,
- compositor: C,
- layout_chan: LayoutChan,
- script_port: Port<ScriptMsg>,
- script_chan: ScriptChan,
- constellation_chan: ConstellationChan,
- resource_task: ResourceTask,
- image_cache_task: ImageCacheTask,
- initial_size: Size2D<int>) {
+ pub fn create<C: ScriptListener + Send>(id: PipelineId,
+ compositor: C,
+ layout_chan: LayoutChan,
+ port: Port<ScriptMsg>,
+ chan: ScriptChan,
+ constellation_chan: ConstellationChan,
+ resource_task: ResourceTask,
+ image_cache_task: ImageCacheTask,
+ initial_size: Future<Size2D<uint>>) {
let compositor = Cell::new(compositor);
- let script_port = Cell::new(script_port);
+ let port = Cell::new(port);
+ let initial_size = Cell::new(initial_size);
// FIXME: rust#6399
let mut the_task = task();
the_task.sched_mode(SingleThreaded);
@@ -258,25 +431,27 @@ impl ScriptTask {
let script_task = ScriptTask::new(id,
@compositor.take() as @ScriptListener,
layout_chan.clone(),
- script_port.take(),
- script_chan.clone(),
+ port.take(),
+ chan.clone(),
constellation_chan.clone(),
resource_task.clone(),
image_cache_task.clone(),
- initial_size);
+ initial_size.take());
script_task.start();
}
}
/// Handles an incoming control message.
fn handle_msg(&mut self) -> bool {
- match self.script_port.recv() {
- LoadMsg(url) => self.load(url),
- ExecuteMsg(url) => self.handle_execute_msg(url),
- SendEventMsg(event) => self.handle_event(event),
- FireTimerMsg(timer_data) => self.handle_fire_timer_msg(timer_data),
- NavigateMsg(direction) => self.handle_navigate_msg(direction),
- ReflowCompleteMsg => self.handle_reflow_complete_msg(),
+ match self.port.recv() {
+ // TODO(tkuehn) need to handle auxiliary layouts for iframes
+ AttachLayoutMsg(new_layout_info) => self.handle_new_layout(new_layout_info),
+ LoadMsg(id, url) => self.load(id, url),
+ ExecuteMsg(id, url) => self.handle_execute_msg(id, url),
+ SendEventMsg(id, event) => self.handle_event(id, event),
+ FireTimerMsg(id, timer_data) => self.handle_fire_timer_msg(id, timer_data),
+ NavigateMsg(id, direction) => self.handle_navigate_msg(id, direction),
+ ReflowCompleteMsg(id) => self.handle_reflow_complete_msg(id),
ResizeInactiveMsg(new_size) => self.handle_resize_inactive_msg(new_size),
ExitMsg => {
self.handle_exit_msg();
@@ -286,309 +461,267 @@ impl ScriptTask {
true
}
+ fn handle_new_layout(&mut self, new_layout_info: NewLayoutInfo) {
+ let NewLayoutInfo {
+ old_id,
+ new_id,
+ layout_chan,
+ size_future
+ } = new_layout_info;
+
+ let parent_page_tree = self.page_tree.find(old_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_tree = PageTree::new(new_id, layout_chan, size_future);
+ new_page_tree.page.initialize_js_info(self.js_runtime.cx());
+
+ parent_page_tree.inner.push(new_page_tree);
+ }
+
/// Handles a request to execute a script.
- fn handle_execute_msg(&self, url: Url) {
+ fn handle_execute_msg(&mut self, id: PipelineId, url: Url) {
debug!("script: Received url `%s` to execute", url::to_str(&url));
+ let page_tree = self.page_tree.find(id).expect("ScriptTask: received fire timer msg for a
+ pipeline ID not associated with this script task. This is a bug.");
+ let js_info = page_tree.page.js_info.get_ref();
+
match read_whole_file(&Path(url.path)) {
Err(msg) => println(fmt!("Error opening %s: %s", url::to_str(&url), msg)),
Ok(bytes) => {
- self.js_compartment.define_functions(debug_fns);
- let _ = self.js_context.evaluate_script(self.js_compartment.global_obj,
- bytes,
- copy url.path,
- 1);
+ js_info.js_compartment.define_functions(debug_fns);
+ js_info.js_context.evaluate_script(js_info.js_compartment.global_obj,
+ bytes,
+ copy url.path,
+ 1);
}
}
}
/// Handles a timer that fired.
- fn handle_fire_timer_msg(&mut self, timer_data: ~TimerData) {
+ fn handle_fire_timer_msg(&mut self, id: PipelineId, timer_data: ~TimerData) {
+ let page = self.page_tree.find(id).expect("ScriptTask: received fire timer msg for a
+ pipeline ID not associated with this script task. This is a bug.").page;
unsafe {
+ let js_info = page.js_info.get_ref();
let this_value = if timer_data.args.len() > 0 {
RUST_JSVAL_TO_OBJECT(timer_data.args[0])
} else {
- self.js_compartment.global_obj.ptr
+ js_info.js_compartment.global_obj.ptr
};
// TODO: Support extra arguments. This requires passing a `*JSVal` array as `argv`.
let rval = JSVAL_NULL;
- JS_CallFunctionValue(self.js_context.ptr,
+ JS_CallFunctionValue(js_info.js_context.ptr,
this_value,
timer_data.funval,
0,
null(),
&rval);
- // We don't know what the script changed, so for now we will do a total redisplay.
- self.reflow_all(ReflowForDisplay)
}
+ // We don't know what the script changed, so for now we will do a total redisplay.
+ page.reflow_all(ReflowForDisplay, self.chan.clone(), self.compositor);
}
/// Handles a notification that reflow completed.
- fn handle_reflow_complete_msg(&mut self) {
- self.layout_join_port = None;
- self.constellation_chan.send(RendererReadyMsg(self.id));
+ fn handle_reflow_complete_msg(&mut self, pipeline_id: PipelineId) {
+ self.page_tree.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.").page.layout_join_port = None;
+ self.constellation_chan.send(RendererReadyMsg(pipeline_id));
self.compositor.set_ready_state(FinishedLoading);
}
/// Handles a navigate forward or backward message.
- fn handle_navigate_msg(&self, direction: NavigationDirection) {
+ fn handle_navigate_msg(&self, pipeline_id: PipelineId, direction: NavigationDirection) {
self.constellation_chan.send(constellation_msg::NavigateMsg(direction));
}
/// Window was resized, but this script was not active, so don't reflow yet
fn handle_resize_inactive_msg(&mut self, new_size: Size2D<uint>) {
- self.window_size = new_size;
- let last_loaded_url = replace(&mut self.last_loaded_url, None);
+ self.page_tree.page.window_size = from_value(new_size);
+ let last_loaded_url = replace(&mut self.page_tree.page.url, None);
for last_loaded_url.iter().advance |last_loaded_url| {
- self.last_loaded_url = Some((last_loaded_url.first(), true));
+ self.page_tree.page.url = Some((last_loaded_url.first(), true));
}
}
/// Handles a request to exit the script task and shut down layout.
fn handle_exit_msg(&mut self) {
- self.join_layout();
- for self.root_frame.iter().advance |frame| {
- frame.document.teardown();
- }
-
- self.layout_chan.send(layout_interface::ExitMsg);
-
- unsafe {
- let _ = local_data::local_data_pop(global_script_context_key);
+ for self.page_tree.iter().advance |page| {
+ page.join_layout();
+ page.frame.get().document.teardown();
+ page.layout_chan.send(layout_interface::ExitMsg);
}
}
/// 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(&mut self, url: Url) {
- let last_loaded_url = replace(&mut self.last_loaded_url, None);
+ fn load(&mut self, pipeline_id: PipelineId, url: Url) {
+ let page = self.page_tree.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.").page;
+ let last_loaded_url = replace(&mut page.url, None);
for last_loaded_url.iter().advance |last_loaded_url| {
let (ref last_loaded_url, needs_reflow) = *last_loaded_url;
if *last_loaded_url == url {
if needs_reflow {
- self.reflow_all(ReflowForDisplay);
- self.last_loaded_url = Some((last_loaded_url.clone(), false));
+ page.reflow_all(ReflowForDisplay, self.chan.clone(), self.compositor);
+ page.url = Some((last_loaded_url.clone(), false));
}
return;
}
}
- // Define the script DOM bindings.
- //
- // FIXME: Can this be done earlier, to save the flag?
- if !self.bindings_initialized {
- define_bindings(self.js_compartment);
- self.bindings_initialized = true
+ {
+ let js_info = page.js_info.get_mut_ref();
+ // Define the script DOM bindings.
+ //
+ // FIXME: Can this be done earlier, to save the flag?
+ //let js_info = page.js_info.get_ref();
+ if !js_info.bindings_initialized {
+ define_bindings(js_info.js_compartment);
+ js_info.bindings_initialized = true;
+ }
}
self.compositor.set_ready_state(Loading);
// Parse HTML.
//
// Note: We can parse the next document in parallel with any previous documents.
- let html_parsing_result = hubbub_html_parser::parse_html(url.clone(),
+ let html_parsing_result = hubbub_html_parser::parse_html(page.js_info.get_ref().js_compartment.cx.ptr,
+ url.clone(),
self.resource_task.clone(),
self.image_cache_task.clone());
- let root_node = html_parsing_result.root;
-
- // Send style sheets over to layout.
- //
- // FIXME: These should be streamed to layout as they're parsed. We don't need to stop here
- // in the script task.
- loop {
- match html_parsing_result.style_port.recv() {
- Some(sheet) => self.layout_chan.send(AddStylesheetMsg(sheet)),
- None => break,
- }
- }
-
- // Receive the JavaScript scripts.
- let js_scripts = html_parsing_result.js_port.recv();
- debug!("js_scripts: %?", js_scripts);
+ let HtmlParserResult {root, js_port, style_port, iframe_port} = html_parsing_result;
// Create the window and document objects.
- let window = Window::new(self.script_chan.clone(), &mut *self);
- let document = Document(root_node, Some(window));
+ let window = Window::new(&mut *page, self.chan.clone(), self.compositor);
+ let document = Document(root, Some(window));
// Tie the root into the document.
- do root_node.with_mut_base |base| {
+ do root.with_mut_base |base| {
base.add_to_doc(document)
}
// Create the root frame.
- self.root_frame = Some(Frame {
+ page.frame = Some(Frame {
document: document,
window: window,
- url: url.clone(),
});
+ page.url = Some((url.clone(), true));
- // Perform the initial reflow.
- self.damage = Some(DocumentDamage {
- root: root_node,
- level: MatchSelectorsDocumentDamage,
- });
- self.reflow(ReflowForDisplay);
-
- // Define debug functions.
- self.js_compartment.define_functions(debug_fns);
-
- // Evaluate every script in the document.
- for js_scripts.iter().advance |bytes| {
- let _ = self.js_context.evaluate_script(self.js_compartment.global_obj,
- bytes.clone(),
- ~"???",
- 1);
- }
- self.last_loaded_url = Some((url, false));
- }
-
- /// 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(&mut self) {
- if self.layout_join_port.is_some() {
- let join_port = replace(&mut self.layout_join_port, None);
- match join_port {
- Some(ref join_port) => {
- if !join_port.peek() {
- info!("script: waiting on layout");
- }
-
- join_port.recv();
+ // Send style sheets over to layout.
+ //
+ // FIXME: These should be streamed to layout as they're parsed. We don't need to stop here
+ // in the script task.
- debug!("script: layout joined")
+ let get_iframes = |iframe_port: &Port<(Url, Future<Size2D<uint>>)>| loop {
+ match iframe_port.try_recv() {
+ None => break,
+ Some((iframe_url, size_future)) => {
+ self.constellation_chan.send(LoadIframeUrlMsg(iframe_url,
+ pipeline_id,
+ size_future));
}
- None => fail!(~"reader forked but no join port?"),
}
- }
- }
-
- /// This method will wait until the layout task has completed its current action, join the
- /// layout task, and then request a new layout run. It won't wait for the new layout
- /// computation to finish.
- ///
- /// This function fails if there is no root frame.
- fn reflow(&mut self, goal: ReflowGoal) {
- debug!("script: performing reflow for goal %?", goal);
-
- // Now, join the layout so that they will see the latest changes we have made.
- self.join_layout();
-
- // Tell the user that we're performing layout.
- self.compositor.set_ready_state(PerformingLayout);
-
- // Layout will let us know when it's done.
- let (join_port, join_chan) = comm::stream();
- self.layout_join_port = Some(join_port);
-
- match self.root_frame {
- None => fail!(~"Tried to relayout with no root frame!"),
- Some(ref root_frame) => {
- // Send new document and relevant styles to layout.
- let reflow = ~Reflow {
- document_root: root_frame.document.root,
- url: copy root_frame.url,
- goal: goal,
- window_size: self.window_size,
- script_chan: self.script_chan.clone(),
- script_join_chan: join_chan,
- damage: replace(&mut self.damage, None).unwrap(),
- };
+ };
- self.layout_chan.send(ReflowMsg(reflow))
+ let get_stylesheets = |style_port: &Port<Stylesheet>| loop {
+ match style_port.try_recv() {
+ None => break,
+ Some(sheet) => page.layout_chan.send(AddStylesheetMsg(sheet)),
+ }
+ };
+
+ let mut select_ports = (style_port, iframe_port);
+ loop {
+ match select_ports.try_select() {
+ Left(None) => {
+ get_iframes(select_ports.second_ref());
+ break;
+ }
+ Left(Some(sheet)) => {
+ page.layout_chan.send(AddStylesheetMsg(sheet));
+ }
+ Right(Some((iframe_url, size_future))) => {
+ self.constellation_chan.send(LoadIframeUrlMsg(iframe_url,
+ pipeline_id,
+ size_future));
+ }
+ Right(None) => {
+ get_stylesheets(select_ports.first_ref());
+ break;
+ }
}
}
- debug!("script: layout forked")
- }
-
- /// Reflows the entire document.
- ///
- /// FIXME: This should basically never be used.
- pub fn reflow_all(&mut self, goal: ReflowGoal) {
- for self.root_frame.iter().advance |root_frame| {
- ScriptTask::damage(&mut self.damage,
- root_frame.document.root,
- MatchSelectorsDocumentDamage)
- }
+ // Receive the JavaScript scripts.
+ let js_scripts = js_port.recv();
+ debug!("js_scripts: %?", js_scripts);
- self.reflow(goal)
- }
+ // Perform the initial reflow.
+ page.damage = Some(DocumentDamage {
+ root: root,
+ level: MatchSelectorsDocumentDamage,
+ });
+ page.reflow(ReflowForDisplay, self.chan.clone(), self.compositor);
+ page.url = Some((url, false));
- /// Sends the given query to layout.
- pub fn query_layout<T: Send>(&mut self, query: LayoutQuery, response_port: Port<Result<T, ()>>) -> Result<T,()> {
- self.join_layout();
- self.layout_chan.send(QueryMsg(query));
- response_port.recv()
- }
+ // Define debug functions.
+ let js_info = page.js_info.get_ref();
+ js_info.js_compartment.define_functions(debug_fns);
- /// Adds the given damage.
- fn damage(damage: &mut Option<DocumentDamage>,
- root: AbstractNode<ScriptView>,
- level: DocumentDamageLevel) {
- match *damage {
- None => {}
- Some(ref mut damage) => {
- // FIXME(pcwalton): This is wrong. We should trace up to the nearest ancestor.
- damage.root = root;
- damage.level.add(level);
- return
- }
+ // Evaluate every script in the document.
+ for js_scripts.iter().advance |bytes| {
+ let _ = js_info.js_context.evaluate_script(js_info.js_compartment.global_obj,
+ bytes.clone(),
+ ~"???",
+ 1);
}
-
- *damage = Some(DocumentDamage {
- root: root,
- level: level,
- })
}
/// This is the main entry point for receiving and dispatching DOM events.
///
/// TODO: Actually perform DOM event dispatch.
- fn handle_event(&mut self, event: Event) {
+ fn handle_event(&mut self, pipeline_id: PipelineId, event: Event) {
+ let page = self.page_tree.find(pipeline_id).expect("ScriptTask: received an event
+ message for a layout channel that is not associated with this script task. This
+ is a bug.").page;
+
match event {
ResizeEvent(new_width, new_height) => {
debug!("script got resize event: %u, %u", new_width, new_height);
- self.window_size = Size2D(new_width, new_height);
+ page.window_size = from_value(Size2D(new_width, new_height));
- for self.root_frame.iter().advance |root_frame| {
- ScriptTask::damage(&mut self.damage,
- root_frame.document.root,
- ReflowDocumentDamage);
+ if page.frame.is_some() {
+ page.damage(ReflowDocumentDamage);
+ page.reflow(ReflowForDisplay, self.chan.clone(), self.compositor)
}
- if self.root_frame.is_some() {
- self.reflow(ReflowForDisplay)
- }
- self.constellation_chan.send(ResizedWindowBroadcast(self.window_size));
+ self.constellation_chan.send(ResizedWindowBroadcast(page.window_size.get().clone()));
}
// FIXME(pcwalton): This reflows the entire document and is not incremental-y.
ReflowEvent => {
debug!("script got reflow event");
- for self.root_frame.iter().advance |root_frame| {
- ScriptTask::damage(&mut self.damage,
- root_frame.document.root,
- MatchSelectorsDocumentDamage);
- }
-
- if self.root_frame.is_some() {
- self.reflow(ReflowForDisplay)
+ if page.frame.is_some() {
+ page.damage(MatchSelectorsDocumentDamage);
+ page.reflow(ReflowForDisplay, self.chan.clone(), self.compositor)
}
}
- ClickEvent(_button, point) => {
+ ClickEvent(page_button, point) => {
debug!("ClickEvent: clicked at %?", point);
- let root = match self.root_frame {
- Some(ref frame) => frame.document.root,
- None => fail!("root frame is None")
- };
+
+ let root = page.frame.expect("root frame is None").document.root;
let (port, chan) = comm::stream();
- match self.query_layout(HitTestQuery(root, point, chan), port) {
+ match page.query_layout(HitTestQuery(root, point, chan), port) {
Ok(node) => match node {
HitTestResponse(node) => {
debug!("clicked on %s", node.debug_str());
@@ -605,7 +738,7 @@ impl ScriptTask {
if node.is_element() {
do node.with_imm_element |element| {
if "a" == element.tag_name {
- self.load_url_from_element(element)
+ self.load_url_from_element(page, element)
}
}
}
@@ -621,16 +754,16 @@ impl ScriptTask {
}
}
- priv fn load_url_from_element(&self, element: &Element) {
+ priv fn load_url_from_element(&self, page: @mut Page, element: &Element) {
// if the node's element is "a," load url from href attr
let href = element.get_attr("href");
for href.iter().advance |href| {
debug!("clicked on link to %s", *href);
- let current_url = do self.root_frame.map |frame| {
- frame.url.clone()
+ let current_url = do page.url.map |&(ref url, _)| {
+ url.clone()
};
let url = make_url(href.to_owned(), current_url);
- self.constellation_chan.send(LoadUrlMsg(url));
+ self.constellation_chan.send(LoadUrlMsg(page.id, url, from_value(page.window_size.get())));
}
}
}
diff --git a/src/components/util/time.rs b/src/components/util/time.rs
index 34de480b99c..db3963da08b 100644
--- a/src/components/util/time.rs
+++ b/src/components/util/time.rs
@@ -7,6 +7,7 @@ use extra::time::precise_time_ns;
use std::cell::Cell;
use std::comm::{Port, SharedChan};
use extra::sort::tim_sort;
+use std::iterator::AdditiveIterator;
// front-end representation of the profiler used to communicate with the profiler
#[deriving(Clone)]
@@ -162,7 +163,7 @@ impl Profiler {
let data_len = data.len();
if data_len > 0 {
let (mean, median, &min, &max) =
- (data.iter().fold(0f, |a, b| a + *b) / (data_len as float),
+ (data.iter().transform(|&x|x).sum() / (data_len as float),
data[data_len / 2],
data.iter().min().unwrap(),
data.iter().max().unwrap());