forward/back navigation with shift+backspace and backspace

script caches last loaded url -- currently no caching policy
naive caching of render layers for near-instant forward/back
handling evicted pipelines is currently broken
This commit is contained in:
Tim Kuehn 2013-06-28 16:45:56 -07:00
parent d17a1f2ad7
commit a6eaffcd93
16 changed files with 216 additions and 100 deletions

View file

@ -21,7 +21,6 @@ use render_context::RenderContext;
use std::cell::Cell; use std::cell::Cell;
use std::comm::{Chan, Port, SharedChan}; use std::comm::{Chan, Port, SharedChan};
use std::uint; use std::uint;
use std::util::replace;
use servo_util::time::{ProfilerChan, profile}; use servo_util::time::{ProfilerChan, profile};
use servo_util::time; use servo_util::time;
@ -73,7 +72,7 @@ priv struct RenderTask<C> {
/// A token that grants permission to send paint messages to compositor /// A token that grants permission to send paint messages to compositor
compositor_token: Option<~CompositorToken>, compositor_token: Option<~CompositorToken>,
/// Cached copy of last layers rendered /// Cached copy of last layers rendered
next_paint_msg: Option<(LayerBufferSet, Size2D<uint>)>, last_paint_msg: Option<(LayerBufferSet, Size2D<uint>)>,
} }
impl<C: RenderListener + Owned> RenderTask<C> { impl<C: RenderListener + Owned> RenderTask<C> {
@ -108,7 +107,7 @@ impl<C: RenderListener + Owned> RenderTask<C> {
constellation_chan: constellation_chan.take(), constellation_chan: constellation_chan.take(),
compositor_token: None, compositor_token: None,
next_paint_msg: None, last_paint_msg: None,
}; };
render_task.start(); render_task.start();
@ -129,11 +128,9 @@ impl<C: RenderListener + Owned> RenderTask<C> {
} }
TokenBestowMsg(token) => { TokenBestowMsg(token) => {
self.compositor_token = Some(token); self.compositor_token = Some(token);
let next_paint_msg = replace(&mut self.next_paint_msg, None); match self.last_paint_msg {
match next_paint_msg { Some((ref layer_buffer_set, ref layer_size)) => {
Some((layer_buffer_set, layer_size)) => { self.compositor.paint(layer_buffer_set.clone(), *layer_size);
println("retrieving cached paint msg");
self.compositor.paint(layer_buffer_set, layer_size);
self.compositor.set_render_state(IdleRenderState); self.compositor.set_render_state(IdleRenderState);
} }
None => {} None => {}
@ -162,7 +159,7 @@ impl<C: RenderListener + Owned> RenderTask<C> {
} }
self.compositor.set_render_state(RenderingRenderState); self.compositor.set_render_state(RenderingRenderState);
do profile(time::RenderingCategory, self.profiler_chan.clone()) { do time::profile(time::RenderingCategory, self.profiler_chan.clone()) {
let tile_size = self.opts.tile_size; let tile_size = self.opts.tile_size;
// FIXME: Try not to create a new array here. // FIXME: Try not to create a new array here.
@ -235,12 +232,10 @@ impl<C: RenderListener + Owned> RenderTask<C> {
debug!("render_task: returning surface"); debug!("render_task: returning surface");
if self.compositor_token.is_some() { if self.compositor_token.is_some() {
self.compositor.paint(layer_buffer_set, render_layer.size); self.compositor.paint(layer_buffer_set.clone(), render_layer.size);
}
else {
println("caching paint msg");
self.next_paint_msg = Some((layer_buffer_set, render_layer.size));
} }
debug!("caching paint msg");
self.last_paint_msg = Some((layer_buffer_set, render_layer.size));
self.compositor.set_render_state(IdleRenderState); self.compositor.set_render_state(IdleRenderState);
} }
} }

View file

@ -4,12 +4,14 @@
use platform::{Application, Window}; use platform::{Application, Window};
use script::dom::event::{Event, ClickEvent, MouseDownEvent, MouseUpEvent, ResizeEvent}; use script::dom::event::{Event, ClickEvent, MouseDownEvent, MouseUpEvent, ResizeEvent};
use script::script_task::{LoadMsg, SendEventMsg}; use script::script_task::{LoadMsg, NavigateMsg, SendEventMsg};
use script::layout_interface::{LayoutChan, RouteScriptMsg}; use script::layout_interface::{LayoutChan, RouteScriptMsg};
use windowing::{ApplicationMethods, WindowMethods, WindowMouseEvent, WindowClickEvent}; use windowing::{ApplicationMethods, WindowMethods, WindowMouseEvent, WindowClickEvent};
use windowing::{WindowMouseDownEvent, WindowMouseUpEvent}; use windowing::{WindowMouseDownEvent, WindowMouseUpEvent};
use servo_msg::compositor::{RenderListener, LayerBufferSet, RenderState}; use servo_msg::compositor::{RenderListener, LayerBufferSet, RenderState};
use servo_msg::compositor::{ReadyState, ScriptListener}; use servo_msg::compositor::{ReadyState, ScriptListener};
use servo_msg::constellation;
use gfx::render_task::{RenderChan, ReRenderMsg}; use gfx::render_task::{RenderChan, ReRenderMsg};
use azure::azure_hl::{DataSourceSurface, DrawTarget, SourceSurfaceMethods, current_gl_context}; use azure::azure_hl::{DataSourceSurface, DrawTarget, SourceSurfaceMethods, current_gl_context};
@ -32,6 +34,7 @@ use servo_util::{time, url};
use servo_util::time::profile; use servo_util::time::profile;
use servo_util::time::ProfilerChan; use servo_util::time::ProfilerChan;
pub use windowing;
/// The implementation of the layers-based compositor. /// The implementation of the layers-based compositor.
#[deriving(Clone)] #[deriving(Clone)]
@ -179,6 +182,15 @@ impl CompositorTask {
let render_chan: @mut Option<RenderChan> = @mut None; let render_chan: @mut Option<RenderChan> = @mut None;
let update_layout_callbacks: @fn(LayoutChan) = |layout_chan: LayoutChan| { let update_layout_callbacks: @fn(LayoutChan) = |layout_chan: LayoutChan| {
let layout_chan_clone = layout_chan.clone();
do window.set_navigation_callback |direction| {
let direction = match direction {
windowing::Forward => constellation::Forward,
windowing::Back => constellation::Back,
};
layout_chan_clone.send(RouteScriptMsg(NavigateMsg(direction)));
}
let layout_chan_clone = layout_chan.clone(); let layout_chan_clone = layout_chan.clone();
// Hook the windowing system's resize callback up to the resize rate limiter. // Hook the windowing system's resize callback up to the resize rate limiter.
do window.set_resize_callback |width, height| { do window.set_resize_callback |width, height| {
@ -186,7 +198,7 @@ impl CompositorTask {
if *window_size != new_size { if *window_size != new_size {
debug!("osmain: window resized to %ux%u", width, height); debug!("osmain: window resized to %ux%u", width, height);
*window_size = new_size; *window_size = new_size;
layout_chan_clone.chan.send(RouteScriptMsg(SendEventMsg(ResizeEvent(width, height)))); layout_chan_clone.send(RouteScriptMsg(SendEventMsg(ResizeEvent(width, height))));
} else { } else {
debug!("osmain: dropping window resize since size is still %ux%u", width, height); debug!("osmain: dropping window resize since size is still %ux%u", width, height);
} }
@ -197,7 +209,7 @@ impl CompositorTask {
// When the user enters a new URL, load it. // When the user enters a new URL, load it.
do window.set_load_url_callback |url_string| { do window.set_load_url_callback |url_string| {
debug!("osmain: loading URL `%s`", url_string); debug!("osmain: loading URL `%s`", url_string);
layout_chan_clone.chan.send(RouteScriptMsg(LoadMsg(url::make_url(url_string.to_str(), None)))); layout_chan_clone.send(RouteScriptMsg(LoadMsg(url::make_url(url_string.to_str(), None))));
} }
let layout_chan_clone = layout_chan.clone(); let layout_chan_clone = layout_chan.clone();
@ -229,7 +241,7 @@ impl CompositorTask {
event = MouseUpEvent(button, world_mouse_point(layer_mouse_point)); event = MouseUpEvent(button, world_mouse_point(layer_mouse_point));
} }
} }
layout_chan_clone.chan.send(RouteScriptMsg(SendEventMsg(event))); layout_chan_clone.send(RouteScriptMsg(SendEventMsg(event)));
} }
}; };
@ -410,6 +422,7 @@ impl CompositorTask {
window.set_needs_display() window.set_needs_display()
} }
// Enter the main event loop. // Enter the main event loop.
while !*done { while !*done {
// Check for new messages coming from the rendering task. // Check for new messages coming from the rendering task.

View file

@ -12,14 +12,15 @@ use gfx::opts::Opts;
use gfx::render_task::{TokenBestowMsg, TokenProcureMsg}; use gfx::render_task::{TokenBestowMsg, TokenProcureMsg};
use pipeline::Pipeline; use pipeline::Pipeline;
use servo_msg::compositor::{CompositorToken}; use servo_msg::compositor::{CompositorToken};
use servo_msg::constellation::{ConstellationChan, ExitMsg, LoadUrlMsg, Msg, RendererReadyMsg}; use servo_msg::constellation::{ConstellationChan, ExitMsg, LoadUrlMsg, Msg, NavigateMsg};
use servo_msg::constellation::TokenSurrenderMsg; use servo_msg::constellation::{Forward, Back, RendererReadyMsg, TokenSurrenderMsg};
use script::script_task::{ExecuteMsg, LoadMsg}; use script::script_task::ExecuteMsg;
use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient}; use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient};
use servo_net::resource_task::ResourceTask; use servo_net::resource_task::ResourceTask;
use servo_net::resource_task; use servo_net::resource_task;
use servo_util::time::ProfilerChan; use servo_util::time::ProfilerChan;
use std::hashmap::HashMap; use std::hashmap::HashMap;
use std::util::replace;
pub struct Constellation { pub struct Constellation {
chan: ConstellationChan, chan: ConstellationChan,
@ -30,12 +31,20 @@ pub struct Constellation {
pipelines: HashMap<uint, Pipeline>, pipelines: HashMap<uint, Pipeline>,
navigation_context: NavigationContext, navigation_context: NavigationContext,
next_id: uint, next_id: uint,
current_token_holder: Option<uint>, current_token_bearer: Option<uint>,
loading: Option<uint>, next_token_bearer: Option<(uint, NavigationType)>,
compositor_token: Option<~CompositorToken>,
profiler_chan: ProfilerChan, profiler_chan: ProfilerChan,
opts: Opts, opts: Opts,
} }
/// Represents the two different ways to which a page can be navigated
enum NavigationType {
Load, // entered or clicked on a url
Navigate, // browser forward/back buttons
}
/// Stores the ID's of the pipelines previous and next in the browser's history
pub struct NavigationContext { pub struct NavigationContext {
previous: ~[uint], previous: ~[uint],
next: ~[uint], next: ~[uint],
@ -52,18 +61,16 @@ impl NavigationContext {
} }
pub fn back(&mut self) -> uint { pub fn back(&mut self) -> uint {
do self.current.mutate |id| { self.next.push(self.current.get());
self.next.push(id); self.current = Some(self.previous.pop());
self.previous.pop() debug!("previous: %? next: %? current: %?", self.previous, self.next, self.current);
}
self.current.get() self.current.get()
} }
pub fn forward(&mut self) -> uint { pub fn forward(&mut self) -> uint {
do self.current.mutate |id| { self.previous.push(self.current.get());
self.previous.push(id); self.current = Some(self.next.pop());
self.next.pop() debug!("previous: %? next: %? current: %?", self.previous, self.next, self.current);
}
self.current.get() self.current.get()
} }
@ -102,8 +109,9 @@ impl Constellation {
pipelines: HashMap::new(), pipelines: HashMap::new(),
navigation_context: NavigationContext::new(), navigation_context: NavigationContext::new(),
next_id: 0, next_id: 0,
current_token_holder: None, current_token_bearer: None,
loading: None, next_token_bearer: None,
compositor_token: Some(~CompositorToken::new()),
profiler_chan: profiler_chan.clone(), profiler_chan: profiler_chan.clone(),
opts: opts.take(), opts: opts.take(),
}; };
@ -122,17 +130,19 @@ impl Constellation {
} }
} }
/// Helper function for getting a unique pipeline ID
fn get_next_id(&mut self) -> uint { fn get_next_id(&mut self) -> uint {
let id = self.next_id; let id = self.next_id;
self.next_id = id + 1; self.next_id = id + 1;
id id
} }
/// Handles loading pages, navigation, and granting access to the compositor
fn handle_request(&mut self, request: Msg) -> bool { fn handle_request(&mut self, request: Msg) -> bool {
match request { match request {
LoadUrlMsg(url) => { LoadUrlMsg(url) => {
let pipeline_id = self.get_next_id(); let pipeline_id = self.get_next_id();
let pipeline = Pipeline::create(pipeline_id, let mut pipeline = Pipeline::create(pipeline_id,
self.chan.clone(), self.chan.clone(),
self.compositor_chan.clone(), self.compositor_chan.clone(),
self.image_cache_task.clone(), self.image_cache_task.clone(),
@ -142,23 +152,41 @@ impl Constellation {
if url.path.ends_with(".js") { if url.path.ends_with(".js") {
pipeline.script_chan.send(ExecuteMsg(url)); pipeline.script_chan.send(ExecuteMsg(url));
} else { } else {
pipeline.script_chan.send(LoadMsg(url)); pipeline.load(url);
self.loading = Some(pipeline_id); self.next_token_bearer = Some((pipeline_id, Load));
} }
self.pipelines.insert(pipeline_id, pipeline); self.pipelines.insert(pipeline_id, pipeline);
} }
RendererReadyMsg(pipeline_id) => { NavigateMsg(direction) => {
let loading = self.loading.clone(); debug!("received message to navigate %?", direction);
do loading.map() |&id| { let destination_id = match direction {
if pipeline_id == id { Forward => {
match self.current_token_holder { if self.navigation_context.next.is_empty() {
Some(ref id) => { debug!("no next page to navigate to");
let current_holder = self.pipelines.get(id); return true
current_holder.render_chan.send(TokenProcureMsg);
}
None => self.bestow_compositor_token(id, ~CompositorToken::new())
} }
self.navigation_context.forward()
}
Back => {
if self.navigation_context.previous.is_empty() {
debug!("no previous page to navigate to");
return true
}
self.navigation_context.back()
}
};
debug!("navigating to pipeline %u", destination_id);
self.pipelines.get(&destination_id).reload();
self.next_token_bearer = Some((destination_id, Navigate));
self.procure_or_bestow();
}
RendererReadyMsg(pipeline_id) => {
let next_token_bearer = self.next_token_bearer;
for next_token_bearer.iter().advance |&(id, _)| {
if pipeline_id == id {
self.procure_or_bestow();
} }
}; };
} }
@ -166,9 +194,9 @@ impl Constellation {
TokenSurrenderMsg(token) => { TokenSurrenderMsg(token) => {
self.remove_active_pipeline(); self.remove_active_pipeline();
let token = Cell::new(token); let token = Cell::new(token);
let loading = self.loading; let next_token_bearer = self.next_token_bearer;
do loading.map |&id| { for next_token_bearer.iter().advance |&(id, nav_type)| {
self.bestow_compositor_token(id, token.take()); self.bestow_compositor_token(id, token.take(), nav_type);
}; };
} }
@ -186,23 +214,45 @@ impl Constellation {
true true
} }
/// Either procures the token, sends the token to next bearer, or does nothing if waiting for token surrender.
fn procure_or_bestow(&mut self) {
let current_token_bearer = replace(&mut self.current_token_bearer, None);
match current_token_bearer {
Some(ref id) => {
let pipeline = self.pipelines.get(id);
pipeline.render_chan.send(TokenProcureMsg);
}
None => {
let compositor_token = replace(&mut self.compositor_token, None);
for compositor_token.iter().advance |&token| {
let (id, nav_type) = self.next_token_bearer.get();
self.bestow_compositor_token(id, token, nav_type);
}
}
};
}
fn remove_active_pipeline(&mut self) { fn remove_active_pipeline(&mut self) {
// FIXME(tkuehn): currently, pipelines are not removed at all // FIXME(tkuehn): currently, pipelines are not removed at all
// do self.current_token_holder.map |id| { // do self.current_token_bearer.map |id| {
// self.pipelines.pop(id).unwrap().exit(); // self.pipelines.pop(id).unwrap().exit();
// }; // };
self.current_token_holder = None; self.current_token_bearer = None;
} }
fn bestow_compositor_token(&mut self, id: uint, compositor_token: ~CompositorToken) { fn bestow_compositor_token(&mut self, id: uint, compositor_token: ~CompositorToken, navigation_type: NavigationType) {
let pipeline = self.pipelines.get(&id); let pipeline = self.pipelines.get(&id);
pipeline.render_chan.send(TokenBestowMsg(compositor_token)); pipeline.render_chan.send(TokenBestowMsg(compositor_token));
self.compositor_chan.send(SetLayoutChan(pipeline.layout_chan.clone())); self.compositor_chan.send(SetLayoutChan(pipeline.layout_chan.clone()));
self.compositor_chan.send(SetRenderChan(pipeline.render_chan.clone())); self.compositor_chan.send(SetRenderChan(pipeline.render_chan.clone()));
self.current_token_holder = Some(id); self.current_token_bearer = Some(id);
self.loading = None; self.next_token_bearer = None;
self.navigation_context.navigate(id); // Don't navigate on Navigate type, because that is handled by forward/back
match navigation_type {
Load => self.navigation_context.navigate(id),
_ => {}
}
} }
} }

View file

@ -141,6 +141,7 @@ impl LayoutTask {
} }
} }
RouteScriptMsg(script_msg) => { RouteScriptMsg(script_msg) => {
debug!("layout: routing %? to script task", script_msg);
self.route_script_msg(script_msg); self.route_script_msg(script_msg);
} }
ExitMsg => { ExitMsg => {

View file

@ -234,7 +234,7 @@ impl TextRunScanner {
for clump.eachi |i| { for clump.eachi |i| {
let range = new_ranges[i - self.clump.begin()]; let range = new_ranges[i - self.clump.begin()];
if range.length() == 0 { if range.length() == 0 {
error!("Elided an `UnscannedTextbox` because it was zero-length after \ debug!("Elided an `UnscannedTextbox` because it was zero-length after \
compression; %s", compression; %s",
in_boxes[i].debug_str()); in_boxes[i].debug_str());
loop loop

View file

@ -2,13 +2,14 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use extra::net::url::Url;
use compositing::CompositorChan; use compositing::CompositorChan;
use gfx::render_task::{RenderChan, RenderTask}; use gfx::render_task::{RenderChan, RenderTask};
use gfx::render_task; use gfx::render_task;
use gfx::opts::Opts; use gfx::opts::Opts;
use layout::layout_task::LayoutTask; use layout::layout_task::LayoutTask;
use script::layout_interface::LayoutChan; use script::layout_interface::LayoutChan;
use script::layout_interface; use script::script_task::LoadMsg;
use servo_msg::constellation::{ConstellationChan}; use servo_msg::constellation::{ConstellationChan};
use script::script_task::{ScriptTask, ScriptChan, ScriptMsg}; use script::script_task::{ScriptTask, ScriptChan, ScriptMsg};
use script::script_task; use script::script_task;
@ -23,6 +24,8 @@ pub struct Pipeline {
script_chan: ScriptChan, script_chan: ScriptChan,
layout_chan: LayoutChan, layout_chan: LayoutChan,
render_chan: RenderChan, render_chan: RenderChan,
/// The most recently loaded url
url: Option<Url>,
} }
impl Pipeline { impl Pipeline {
@ -90,12 +93,24 @@ impl Pipeline {
script_chan: script_chan, script_chan: script_chan,
layout_chan: layout_chan, layout_chan: layout_chan,
render_chan: render_chan, render_chan: render_chan,
url: None,
}
}
pub fn load(&mut self, url: Url) {
self.url = Some(url.clone());
self.script_chan.send(LoadMsg(url));
}
pub fn reload(&self) {
for self.url.iter().advance |&url| {
self.script_chan.send(LoadMsg(url));
} }
} }
pub fn exit(&self) { pub fn exit(&self) {
// Script task handles shutting down layout, as well
self.script_chan.send(script_task::ExitMsg); self.script_chan.send(script_task::ExitMsg);
self.layout_chan.send(layout_interface::ExitMsg);
let (response_port, response_chan) = comm::stream(); let (response_port, response_chan) = comm::stream();
self.render_chan.send(render_task::ExitMsg(response_chan)); self.render_chan.send(render_task::ExitMsg(response_chan));

View file

@ -9,7 +9,7 @@
use windowing::{ApplicationMethods, CompositeCallback, LoadUrlCallback, MouseCallback}; use windowing::{ApplicationMethods, CompositeCallback, LoadUrlCallback, MouseCallback};
use windowing::{ResizeCallback, ScrollCallback, WindowMethods, WindowMouseEvent, WindowClickEvent}; use windowing::{ResizeCallback, ScrollCallback, WindowMethods, WindowMouseEvent, WindowClickEvent};
use windowing::{WindowMouseDownEvent, WindowMouseUpEvent, ZoomCallback}; use windowing::{WindowMouseDownEvent, WindowMouseUpEvent, ZoomCallback, Forward, Back, NavigationCallback};
use alert::{Alert, AlertMethods}; use alert::{Alert, AlertMethods};
use std::libc::c_int; use std::libc::c_int;
@ -17,7 +17,8 @@ use geom::point::Point2D;
use geom::size::Size2D; use geom::size::Size2D;
use servo_msg::compositor::{IdleRenderState, RenderState, RenderingRenderState}; use servo_msg::compositor::{IdleRenderState, RenderState, RenderingRenderState};
use servo_msg::compositor::{FinishedLoading, Loading, PerformingLayout, ReadyState}; use servo_msg::compositor::{FinishedLoading, Loading, PerformingLayout, ReadyState};
use glut::glut::{ACTIVE_CTRL, DOUBLE, HAVE_PRECISE_MOUSE_WHEEL, WindowHeight, WindowWidth}; use glut::glut::{ACTIVE_CTRL, ACTIVE_SHIFT, DOUBLE, HAVE_PRECISE_MOUSE_WHEEL, WindowHeight};
use glut::glut::WindowWidth;
use glut::glut; use glut::glut;
use glut::machack; use glut::machack;
@ -44,6 +45,7 @@ pub struct Window {
mouse_callback: Option<MouseCallback>, mouse_callback: Option<MouseCallback>,
scroll_callback: Option<ScrollCallback>, scroll_callback: Option<ScrollCallback>,
zoom_callback: Option<ZoomCallback>, zoom_callback: Option<ZoomCallback>,
navigation_callback: Option<NavigationCallback>,
drag_origin: Point2D<c_int>, drag_origin: Point2D<c_int>,
@ -72,6 +74,7 @@ impl WindowMethods<Application> for Window {
mouse_callback: None, mouse_callback: None,
scroll_callback: None, scroll_callback: None,
zoom_callback: None, zoom_callback: None,
navigation_callback: None,
drag_origin: Point2D(0 as c_int, 0), drag_origin: Point2D(0 as c_int, 0),
@ -177,6 +180,11 @@ impl WindowMethods<Application> for Window {
self.zoom_callback = Some(new_zoom_callback) self.zoom_callback = Some(new_zoom_callback)
} }
/// Registers a callback to be run when backspace or shift-backspace is pressed.
pub fn set_navigation_callback(&mut self, new_navigation_callback: NavigationCallback) {
self.navigation_callback = Some(new_navigation_callback)
}
/// Spins the event loop. /// Spins the event loop.
pub fn check_loop(@mut self) { pub fn check_loop(@mut self) {
glut::check_loop() glut::check_loop()
@ -226,20 +234,33 @@ impl Window {
/// Helper function to handle keyboard events. /// Helper function to handle keyboard events.
fn handle_key(&self, key: u8) { fn handle_key(&self, key: u8) {
debug!("got key: %d", key as int); debug!("got key: %?", key);
let modifiers = glut::get_modifiers();
match key { match key {
12 => self.load_url(), // Ctrl+L 12 => self.load_url(), // Ctrl+L
k if k == ('=' as u8) && (glut::get_modifiers() & ACTIVE_CTRL) != 0 => { // Ctrl++ 31 if (modifiers & ACTIVE_CTRL) != 0 => { // Ctrl+-
for self.zoom_callback.iter().advance |&callback| {
callback(0.1);
}
}
k if k == 31 && (glut::get_modifiers() & ACTIVE_CTRL) != 0 => { // Ctrl+-
for self.zoom_callback.iter().advance |&callback| { for self.zoom_callback.iter().advance |&callback| {
callback(-0.1); callback(-0.1);
} }
} }
_ => {} 127 => {
for self.navigation_callback.iter().advance |&callback| {
if (modifiers & ACTIVE_SHIFT) != 0 { // Shift+Backspace
callback(Forward);
}
else {
callback(Back);
}
}
}
c => match c as char {
'=' if (modifiers & ACTIVE_CTRL) != 0 => { // Ctrl++
for self.zoom_callback.iter().advance |&callback| {
callback(0.1);
}
}
_ => {}
}
} }
} }

View file

@ -14,6 +14,11 @@ pub enum WindowMouseEvent {
WindowMouseUpEvent(uint, Point2D<f32>), WindowMouseUpEvent(uint, Point2D<f32>),
} }
pub enum WindowNavigateMsg {
Forward,
Back,
}
/// Type of the function that is called when the screen is to be redisplayed. /// Type of the function that is called when the screen is to be redisplayed.
pub type CompositeCallback = @fn(); pub type CompositeCallback = @fn();
@ -29,9 +34,12 @@ pub type MouseCallback = @fn(WindowMouseEvent);
/// Type of the function that is called when the user scrolls. /// Type of the function that is called when the user scrolls.
pub type ScrollCallback = @fn(Point2D<f32>); pub type ScrollCallback = @fn(Point2D<f32>);
///Type of the function that is called when the user zooms. /// Type of the function that is called when the user zooms.
pub type ZoomCallback = @fn(f32); pub type ZoomCallback = @fn(f32);
/// Type of the function that is called when the user clicks backspace or shift-backspace
pub type NavigationCallback = @fn(WindowNavigateMsg);
/// Methods for an abstract Application. /// Methods for an abstract Application.
pub trait ApplicationMethods { pub trait ApplicationMethods {
fn new() -> Self; fn new() -> Self;
@ -57,6 +65,8 @@ pub trait WindowMethods<A> {
pub fn set_scroll_callback(&mut self, new_scroll_callback: ScrollCallback); pub fn set_scroll_callback(&mut self, new_scroll_callback: ScrollCallback);
/// Registers a callback to run when the user zooms. /// Registers a callback to run when the user zooms.
pub fn set_zoom_callback(&mut self, new_zoom_callback: ZoomCallback); pub fn set_zoom_callback(&mut self, new_zoom_callback: ZoomCallback);
/// Registers a callback to run when the user presses backspace or shift-backspace.
pub fn set_navigation_callback(&mut self, new_navigation_callback: NavigationCallback);
/// Spins the event loop. /// Spins the event loop.
pub fn check_loop(@mut self); pub fn check_loop(@mut self);

View file

@ -8,6 +8,7 @@ use geom::rect::Rect;
use geom::size::Size2D; use geom::size::Size2D;
use std::util::NonCopyable; use std::util::NonCopyable;
#[deriving(Clone)]
pub struct LayerBuffer { pub struct LayerBuffer {
draw_target: DrawTarget, draw_target: DrawTarget,
@ -23,6 +24,7 @@ pub struct LayerBuffer {
/// A set of layer buffers. This is an atomic unit used to switch between the front and back /// A set of layer buffers. This is an atomic unit used to switch between the front and back
/// buffers. /// buffers.
#[deriving(Clone)]
pub struct LayerBufferSet { pub struct LayerBufferSet {
buffers: ~[LayerBuffer] buffers: ~[LayerBuffer]
} }

View file

@ -27,8 +27,13 @@ impl ConstellationChan {
pub enum Msg { pub enum Msg {
LoadUrlMsg(Url), LoadUrlMsg(Url),
NavigateMsg(NavigationDirection),
ExitMsg(Chan<()>), ExitMsg(Chan<()>),
RendererReadyMsg(uint), RendererReadyMsg(uint),
TokenSurrenderMsg(~CompositorToken), TokenSurrenderMsg(~CompositorToken),
} }
pub enum NavigationDirection {
Forward,
Back,
}

View file

@ -7,8 +7,6 @@
use dom::bindings::utils::{DOMString, null_string, str}; use dom::bindings::utils::{DOMString, null_string, str};
use dom::node::{Node, NodeTypeId, ScriptView}; use dom::node::{Node, NodeTypeId, ScriptView};
use std::str;
pub struct CharacterData { pub struct CharacterData {
parent: Node<ScriptView>, parent: Node<ScriptView>,
data: DOMString data: DOMString

View file

@ -367,7 +367,7 @@ impl<View> AbstractNode<View> {
pub fn dump_indent(&self, indent: uint) { pub fn dump_indent(&self, indent: uint) {
let mut s = ~""; let mut s = ~"";
for uint::range(0u, indent) |_i| { for uint::range(0u, indent) |_i| {
s += ~" "; s += " ";
} }
s += self.debug_str(); s += self.debug_str();

View file

@ -8,7 +8,6 @@ use std::cell::Cell;
use std::comm; use std::comm;
use std::comm::Port; use std::comm::Port;
use std::task; use std::task;
use std::str;
use newcss::stylesheet::Stylesheet; use newcss::stylesheet::Stylesheet;
use newcss::util::DataStream; use newcss::util::DataStream;
use servo_net::resource_task::{ResourceTask, ProgressMsg, Load, Payload, Done}; use servo_net::resource_task::{ResourceTask, ProgressMsg, Load, Payload, Done};

View file

@ -19,7 +19,9 @@ use layout_interface::{LayoutChan, MatchSelectorsDocumentDamage, QueryMsg, Reflo
use layout_interface::{ReflowDocumentDamage, ReflowForDisplay, ReflowForScriptQuery, ReflowGoal}; use layout_interface::{ReflowDocumentDamage, ReflowForDisplay, ReflowForScriptQuery, ReflowGoal};
use layout_interface::ReflowMsg; use layout_interface::ReflowMsg;
use layout_interface; use layout_interface;
use servo_msg::constellation::{ConstellationChan, LoadUrlMsg, RendererReadyMsg}; use servo_msg::constellation::{ConstellationChan, LoadUrlMsg, NavigationDirection};
use servo_msg::constellation::RendererReadyMsg;
use servo_msg::constellation;
use std::cast::transmute; use std::cast::transmute;
use std::cell::Cell; use std::cell::Cell;
@ -53,6 +55,8 @@ pub enum ScriptMsg {
LoadMsg(Url), LoadMsg(Url),
/// Executes a standalone script. /// Executes a standalone script.
ExecuteMsg(Url), ExecuteMsg(Url),
/// Instructs the script task to send a navigate message to the constellation.
NavigateMsg(NavigationDirection),
/// Sends a DOM event. /// Sends a DOM event.
SendEventMsg(Event), SendEventMsg(Event),
/// Fires a JavaScript timeout. /// Fires a JavaScript timeout.
@ -136,6 +140,11 @@ pub struct ScriptTask {
window_size: Size2D<uint>, window_size: Size2D<uint>,
/// What parts of the document are dirty, if any. /// What parts of the document are dirty, if any.
damage: Option<DocumentDamage>, 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 (!).
last_loaded_url: Option<Url>,
} }
fn global_script_context_key(_: @ScriptTask) {} fn global_script_context_key(_: @ScriptTask) {}
@ -212,6 +221,8 @@ impl ScriptTask {
window_size: Size2D(800u, 600), window_size: Size2D(800u, 600),
damage: None, damage: None,
last_loaded_url: None,
}; };
// Indirection for Rust Issue #6248, dynamic freeze scope artifically extended // Indirection for Rust Issue #6248, dynamic freeze scope artifically extended
let script_task_ptr = { let script_task_ptr = {
@ -264,31 +275,18 @@ impl ScriptTask {
/// Handles an incoming control message. /// Handles an incoming control message.
fn handle_msg(&mut self) -> bool { fn handle_msg(&mut self) -> bool {
match self.script_port.recv() { match self.script_port.recv() {
LoadMsg(url) => { LoadMsg(url) => self.load(url),
self.load(url); ExecuteMsg(url) => self.handle_execute_msg(url),
true SendEventMsg(event) => self.handle_event(event),
} FireTimerMsg(timer_data) => self.handle_fire_timer_msg(timer_data),
ExecuteMsg(url) => { NavigateMsg(direction) => self.handle_navigate_msg(direction),
self.handle_execute_msg(url); ReflowCompleteMsg => self.handle_reflow_complete_msg(),
true
}
SendEventMsg(event) => {
self.handle_event(event);
true
}
FireTimerMsg(timer_data) => {
self.handle_fire_timer_msg(timer_data);
true
}
ReflowCompleteMsg => {
self.handle_reflow_complete_msg();
true
}
ExitMsg => { ExitMsg => {
self.handle_exit_msg(); self.handle_exit_msg();
false return false
} }
} }
true
} }
/// Handles a request to execute a script. /// Handles a request to execute a script.
@ -337,6 +335,11 @@ impl ScriptTask {
self.compositor.set_ready_state(FinishedLoading); self.compositor.set_ready_state(FinishedLoading);
} }
/// Handles a navigate forward or backward message.
fn handle_navigate_msg(&self, direction: NavigationDirection) {
self.constellation_chan.send(constellation::NavigateMsg(direction));
}
/// Handles a request to exit the script task and shut down layout. /// Handles a request to exit the script task and shut down layout.
fn handle_exit_msg(&mut self) { fn handle_exit_msg(&mut self) {
self.join_layout(); self.join_layout();
@ -350,6 +353,9 @@ impl ScriptTask {
/// The entry point to document loading. Defines bindings, sets up the window and document /// The entry point to document loading. Defines bindings, sets up the window and document
/// objects, parses HTML and CSS, and kicks off initial layout. /// objects, parses HTML and CSS, and kicks off initial layout.
fn load(&mut self, url: Url) { fn load(&mut self, url: Url) {
for self.last_loaded_url.iter().advance |&last_loaded_url| {
if url == last_loaded_url { return; }
}
// Define the script DOM bindings. // Define the script DOM bindings.
// //
// FIXME: Can this be done earlier, to save the flag? // FIXME: Can this be done earlier, to save the flag?
@ -362,7 +368,7 @@ impl ScriptTask {
// Parse HTML. // Parse HTML.
// //
// Note: We can parse the next document in parallel with any previous documents. // Note: We can parse the next document in parallel with any previous documents.
let html_parsing_result = hubbub_html_parser::parse_html(copy url, let html_parsing_result = hubbub_html_parser::parse_html(url.clone(),
self.resource_task.clone(), self.resource_task.clone(),
self.image_cache_task.clone()); self.image_cache_task.clone());
@ -396,7 +402,7 @@ impl ScriptTask {
self.root_frame = Some(Frame { self.root_frame = Some(Frame {
document: document, document: document,
window: window, window: window,
url: url url: url.clone(),
}); });
// Perform the initial reflow. // Perform the initial reflow.
@ -416,6 +422,7 @@ impl ScriptTask {
~"???", ~"???",
1); 1);
} }
self.last_loaded_url = Some(url);
} }
/// Sends a ping to layout and waits for the response. The response will arrive when the /// Sends a ping to layout and waits for the response. The response will arrive when the

View file

@ -152,7 +152,7 @@ impl Profiler {
priv fn print_buckets(&mut self) { priv fn print_buckets(&mut self) {
println(fmt!("%31s %15s %15s %15s %15s %15s", println(fmt!("%31s %15s %15s %15s %15s %15s",
"_category (ms)_", "_mean (ms)_", "_median (ms)_", "_category_", "_mean (ms)_", "_median (ms)_",
"_min (ms)_", "_max (ms)_", "_bucket size_")); "_min (ms)_", "_max (ms)_", "_bucket size_"));
for self.buckets.mut_iter().advance |bucket| { for self.buckets.mut_iter().advance |bucket| {
match *bucket { match *bucket {

@ -1 +1 @@
Subproject commit 502ec156da38860a4dff911cf8f33f388a0a1883 Subproject commit 68875af396cb583e670dd5caad99431dac62f8db