From 5b684659ffa224e15ddbd91d28c054a7f893d2af Mon Sep 17 00:00:00 2001 From: Keegan McAllister Date: Thu, 12 Sep 2013 15:15:32 -0700 Subject: [PATCH] Combine resize events for each pipeline and process when layout is idle --- src/components/main/constellation.rs | 14 +++-- src/components/script/script_task.rs | 81 ++++++++++++++++++++++------ 2 files changed, 71 insertions(+), 24 deletions(-) diff --git a/src/components/main/constellation.rs b/src/components/main/constellation.rs index 271b72526bb..40e10b6cbd6 100644 --- a/src/components/main/constellation.rs +++ b/src/components/main/constellation.rs @@ -3,7 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use compositing::{CompositorChan, SetIds, SetLayerClipRect}; -use script::dom::event::ResizeEvent; use std::cell::Cell; use std::comm; @@ -18,7 +17,7 @@ use servo_msg::constellation_msg::{InitLoadUrlMsg, LoadIframeUrlMsg, LoadUrlMsg} use servo_msg::constellation_msg::{Msg, NavigateMsg, NavigationType, IFrameUnsandboxed}; use servo_msg::constellation_msg::{PipelineId, RendererReadyMsg, ResizedWindowMsg, SubpageId}; use servo_msg::constellation_msg; -use script::script_task::{SendEventMsg, ResizeInactiveMsg, ExecuteMsg}; +use script::script_task::{ResizeMsg, ResizeInactiveMsg, ExecuteMsg}; use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient}; use servo_net::resource_task::ResourceTask; use servo_net::resource_task; @@ -409,9 +408,10 @@ impl Constellation { subpage id. This should not be possible.") == subpage_id { child_frame_tree.rect = Some(rect.clone()); let Rect { size: Size2D { width, height }, _ } = rect; - pipeline.script_chan.send(SendEventMsg(pipeline.id.clone(), - ResizeEvent(width as uint, - height as uint))); + pipeline.script_chan.send(ResizeMsg(pipeline.id.clone(), Size2D { + width: width as uint, + height: height as uint + })); self.compositor_chan.send(SetLayerClipRect(pipeline.id, rect)); already_sent.insert(pipeline.id.clone()); break; @@ -720,9 +720,7 @@ impl Constellation { fn handle_resized_window_msg(&mut self, new_size: Size2D) { let mut already_seen = HashSet::new(); for &@FrameTree { pipeline: pipeline, _ } in self.current_frame().iter() { - let Size2D { width, height } = new_size; - pipeline.script_chan.send(SendEventMsg(pipeline.id.clone(), - ResizeEvent(width, height))); + pipeline.script_chan.send(ResizeMsg(pipeline.id.clone(), new_size)); already_seen.insert(pipeline.id.clone()); } for frame_tree in self.navigation_context.previous.iter() diff --git a/src/components/script/script_task.rs b/src/components/script/script_task.rs index 4025de24bb4..33fdecee0bf 100644 --- a/src/components/script/script_task.rs +++ b/src/components/script/script_task.rs @@ -63,6 +63,8 @@ pub enum ScriptMsg { NavigateMsg(NavigationDirection), /// Sends a DOM event. SendEventMsg(PipelineId, Event_), + /// Window resized. Sends a DOM event eventually, but first we combine events. + ResizeMsg(PipelineId, Size2D), /// Fires a JavaScript timeout. FireTimerMsg(PipelineId, ~TimerData), /// Notifies script that reflow is finished. @@ -128,6 +130,9 @@ pub struct Page { url: Option<(Url, bool)>, next_subpage_id: SubpageId, + + /// Pending resize event, if any. + resize_event: Option> } pub struct PageTree { @@ -152,6 +157,7 @@ impl PageTree { js_info: None, url: None, next_subpage_id: SubpageId(0), + resize_event: None, }, inner: ~[], } @@ -406,7 +412,7 @@ impl ScriptTask { /// Starts the script task. After calling this method, the script task will loop receiving /// messages on its port. pub fn start(&mut self) { - while self.handle_msg() { + while self.handle_msgs() { // Go on... } } @@ -440,23 +446,66 @@ impl ScriptTask { } } - /// Handles an incoming control message. - fn handle_msg(&mut self) -> bool { - 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(direction) => self.handle_navigate_msg(direction), - ReflowCompleteMsg(id) => self.handle_reflow_complete_msg(id), - ResizeInactiveMsg(id, new_size) => self.handle_resize_inactive_msg(id, new_size), - ExitMsg => { - self.handle_exit_msg(); - return false + /// Handle incoming control messages. + fn handle_msgs(&mut self) -> bool { + // Handle pending resize events. + // Gather them first to avoid a double mut borrow on self. + let mut resizes = ~[]; + for page in self.page_tree.iter() { + // Only process a resize if layout is idle. + if page.layout_join_port.is_none() { + match page.resize_event.take() { + Some(size) => resizes.push((page.id, size)), + None => () + } } } + + for (id, Size2D { width, height }) in resizes.move_iter() { + self.handle_event(id, ResizeEvent(width, height)); + } + + // Store new resizes, and gather all other events. + let mut sequential = ~[]; + loop { + // Receive at least one message so we don't spinloop. + let event = self.port.recv(); + match event { + ResizeMsg(id, size) => { + let page = self.page_tree.find(id).expect("resize sent to nonexistent pipeline").page; + page.resize_event = Some(size); + } + _ => { + sequential.push(event); + } + } + + // Break if there are no more messages. + if !self.port.peek() { + break; + } + } + + // Process the gathered events. + for msg in sequential.move_iter() { + match msg { + // 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(direction) => self.handle_navigate_msg(direction), + ReflowCompleteMsg(id) => self.handle_reflow_complete_msg(id), + ResizeInactiveMsg(id, new_size) => self.handle_resize_inactive_msg(id, new_size), + ExitMsg => { + self.handle_exit_msg(); + return false + }, + ResizeMsg(*) => fail!("should have handled ResizeMsg already"), + } + } + true }