diff options
Diffstat (limited to 'components/script/script_task.rs')
-rw-r--r-- | components/script/script_task.rs | 116 |
1 files changed, 60 insertions, 56 deletions
diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 92a5dccb7d4..6886137e48c 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -36,7 +36,7 @@ use dom::event::{Event, EventHelpers, EventBubbles, EventCancelable}; use dom::htmliframeelement::{HTMLIFrameElement, HTMLIFrameElementHelpers}; use dom::uievent::UIEvent; use dom::eventtarget::EventTarget; -use dom::node::{self, Node, NodeHelpers, NodeDamage, window_from_node}; +use dom::node::{Node, NodeHelpers, NodeDamage, window_from_node}; use dom::window::{Window, WindowHelpers, ScriptHelpers, ReflowReason}; use dom::worker::TrustedWorkerAddress; use parse::html::{HTMLInput, parse_html}; @@ -50,7 +50,7 @@ use devtools_traits::{DevtoolsControlChan, DevtoolsControlPort, DevtoolsPageInfo use devtools_traits::{DevtoolsControlMsg, DevtoolScriptControlMsg}; use devtools_traits::{TimelineMarker, TimelineMarkerType, TracingMetadata}; use script_traits::CompositorEvent; -use script_traits::CompositorEvent::{ResizeEvent, ReflowEvent, ClickEvent}; +use script_traits::CompositorEvent::{ResizeEvent, ClickEvent}; use script_traits::CompositorEvent::{MouseDownEvent, MouseUpEvent}; use script_traits::CompositorEvent::{MouseMoveEvent, KeyEvent}; use script_traits::{NewLayoutInfo, OpaqueScriptLayoutChannel}; @@ -64,7 +64,7 @@ use msg::constellation_msg::{Failure, WindowSizeData, PipelineExitType}; use msg::constellation_msg::Msg as ConstellationMsg; use net_traits::{ResourceTask, ControlMsg, LoadResponse, LoadConsumer}; use net_traits::LoadData as NetLoadData; -use net_traits::image_cache_task::ImageCacheTask; +use net_traits::image_cache_task::{ImageCacheChan, ImageCacheTask, ImageCacheResult}; use net_traits::storage_task::StorageTask; use string_cache::Atom; use util::geometry::to_frac_px; @@ -79,8 +79,7 @@ use hyper::header::{LastModified, Headers}; use js::jsapi::{JS_SetWrapObjectCallbacks, JS_SetExtraGCRootsTracer}; use js::jsapi::{JSContext, JSRuntime, JSObject, JSTracer}; use js::jsapi::{JS_SetGCCallback, JSGCStatus, JSGC_BEGIN, JSGC_END}; -use js::rust::{Runtime, Cx, RtUtils}; -use js; +use js::rust::{Runtime, RtUtils}; use url::Url; use libc; @@ -297,6 +296,12 @@ pub struct ScriptTask { /// A handle to the compositor for communicating ready state messages. compositor: DOMRefCell<Box<ScriptListener+'static>>, + /// The port on which we receive messages from the image cache + image_cache_port: Receiver<ImageCacheResult>, + + /// The channel on which the image cache can send messages to ourself. + image_cache_channel: ImageCacheChan, + /// For providing instructions to an optional devtools server. devtools_chan: Option<DevtoolsControlChan>, /// For receiving commands from an optional devtools server. Will be ignored if @@ -309,9 +314,7 @@ pub struct ScriptTask { devtools_marker_sender: RefCell<Option<Sender<TimelineMarker>>>, /// The JavaScript runtime. - js_runtime: js::rust::rt, - /// The JSContext. - js_context: Rc<Cx>, + js_runtime: Runtime, mouse_over_targets: DOMRefCell<Vec<JS<Node>>> } @@ -440,10 +443,10 @@ impl ScriptTask { constellation_chan: ConstellationChan, resource_task: ResourceTask, storage_task: StorageTask, - img_cache_task: ImageCacheTask, + image_cache_task: ImageCacheTask, devtools_chan: Option<DevtoolsControlChan>) -> ScriptTask { - let (js_runtime, js_context) = ScriptTask::new_rt_and_cx(); + let runtime = ScriptTask::new_rt_and_cx(); let wrap_for_same_compartment = wrap_for_same_compartment as unsafe extern "C" fn(*mut JSContext, *mut JSObject) -> *mut JSObject; let pre_wrap = pre_wrap as @@ -455,22 +458,27 @@ impl ScriptTask { // and JSCompartment::wrap crashes if that happens. The only way // to retrieve the default callback is as the result of // JS_SetWrapObjectCallbacks, which is why we call it twice. - let callback = JS_SetWrapObjectCallbacks((*js_runtime).ptr, + let callback = JS_SetWrapObjectCallbacks(runtime.rt(), None, Some(wrap_for_same_compartment), None); - JS_SetWrapObjectCallbacks((*js_runtime).ptr, + JS_SetWrapObjectCallbacks(runtime.rt(), callback, Some(wrap_for_same_compartment), Some(pre_wrap)); } let (devtools_sender, devtools_receiver) = channel(); + let (image_cache_channel, image_cache_port) = channel(); + ScriptTask { page: DOMRefCell::new(None), incomplete_loads: DOMRefCell::new(vec!()), - image_cache_task: img_cache_task, + image_cache_task: image_cache_task, + image_cache_channel: ImageCacheChan(image_cache_channel), + image_cache_port: image_cache_port, + resource_task: resource_task, storage_task: storage_task, @@ -486,13 +494,12 @@ impl ScriptTask { devtools_markers: RefCell::new(HashSet::new()), devtools_marker_sender: RefCell::new(None), - js_runtime: js_runtime, - js_context: js_context, + js_runtime: runtime, mouse_over_targets: DOMRefCell::new(vec!()) } } - pub fn new_rt_and_cx() -> (js::rust::rt, Rc<Cx>) { + pub fn new_rt_and_cx() -> Runtime { LiveDOMReferences::initialize(); let runtime = Runtime::new(); @@ -508,7 +515,7 @@ impl ScriptTask { } } - (runtime.rt, runtime.cx) + runtime } // Return the root page in the frame tree. Panics if it doesn't exist. @@ -517,7 +524,7 @@ impl ScriptTask { } pub fn get_cx(&self) -> *mut JSContext { - self.js_context.ptr + self.js_runtime.cx() } /// Starts the script task. After calling this method, the script task will loop receiving @@ -562,6 +569,7 @@ impl ScriptTask { FromConstellation(ConstellationControlMsg), FromScript(ScriptMsg), FromDevtools(DevtoolScriptControlMsg), + FromImageCache(ImageCacheResult), } // Store new resizes, and gather all other events. @@ -573,12 +581,14 @@ impl ScriptTask { let mut port1 = sel.handle(&self.port); let mut port2 = sel.handle(&self.control_port); let mut port3 = sel.handle(&self.devtools_port); + let mut port4 = sel.handle(&self.image_cache_port); unsafe { port1.add(); port2.add(); if self.devtools_chan.is_some() { port3.add(); } + port4.add(); } let ret = sel.wait(); if ret == port1.id() { @@ -587,6 +597,8 @@ impl ScriptTask { MixedMessage::FromConstellation(self.control_port.recv().unwrap()) } else if ret == port3.id() { MixedMessage::FromDevtools(self.devtools_port.recv().unwrap()) + } else if ret == port4.id() { + MixedMessage::FromImageCache(self.image_cache_port.recv().unwrap()) } else { panic!("unexpected select result") } @@ -633,7 +645,10 @@ impl ScriptTask { match self.control_port.try_recv() { Err(_) => match self.port.try_recv() { Err(_) => match self.devtools_port.try_recv() { - Err(_) => break, + Err(_) => match self.image_cache_port.try_recv() { + Err(_) => break, + Ok(ev) => event = MixedMessage::FromImageCache(ev), + }, Ok(ev) => event = MixedMessage::FromDevtools(ev), }, Ok(ev) => event = MixedMessage::FromScript(ev), @@ -653,6 +668,23 @@ impl ScriptTask { MixedMessage::FromConstellation(inner_msg) => self.handle_msg_from_constellation(inner_msg), MixedMessage::FromScript(inner_msg) => self.handle_msg_from_script(inner_msg), MixedMessage::FromDevtools(inner_msg) => self.handle_msg_from_devtools(inner_msg), + MixedMessage::FromImageCache(inner_msg) => self.handle_msg_from_image_cache(inner_msg), + } + } + + // Issue batched reflows on any pages that require it (e.g. if images loaded) + // TODO(gw): In the future we could probably batch other types of reflows + // into this loop too, but for now it's only images. + let page = self.page.borrow(); + if let Some(page) = page.as_ref() { + for page in page.iter() { + let window = page.window().root(); + let pending_reflows = window.r().get_pending_reflow_count(); + if pending_reflows > 0 { + window.r().reflow(ReflowGoal::ForDisplay, + ReflowQueryType::NoQuery, + ReflowReason::ImageLoaded); + } } } @@ -747,6 +779,10 @@ impl ScriptTask { } } + fn handle_msg_from_image_cache(&self, msg: ImageCacheResult) { + msg.responder.unwrap().respond(msg.image); + } + fn handle_resize(&self, id: PipelineId, size: WindowSizeData) { let page = self.page.borrow(); if let Some(ref page) = page.as_ref() { @@ -782,7 +818,7 @@ impl ScriptTask { return; } panic!("Page rect message sent to nonexistent pipeline"); - } + } /// Handle a request to load a page in a new child frame of an existing page. fn handle_new_layout(&self, new_layout_info: NewLayoutInfo) { @@ -1062,9 +1098,10 @@ impl ScriptTask { let mut page_remover = AutoPageRemover::new(self, page_to_remove); // Create the window and document objects. - let window = Window::new(self.js_context.clone(), + let window = Window::new(self.js_runtime.cx.clone(), page.clone(), self.chan.clone(), + self.image_cache_channel.clone(), self.control_chan.clone(), self.compositor.borrow_mut().dup(), self.image_cache_task.clone(), @@ -1211,29 +1248,6 @@ impl ScriptTask { self.handle_resize_event(pipeline_id, new_size); } - ReflowEvent(nodes) => { - // FIXME(pcwalton): This event seems to only be used by the image cache task, and - // the interaction between it and the image holder is really racy. I think that, in - // order to fix this race, we need to rewrite the image cache task to make the - // image holder responsible for the lifecycle of image loading instead of having - // the image holder and layout task both be observers. Then we can have the DOM - // image element observe the state of the image holder and have it send reflows - // via the normal dirtying mechanism, and ultimately remove this event. - // - // See the implementation of `Width()` and `Height()` in `HTMLImageElement` for - // fallout of this problem. - for node in nodes.iter() { - let node_to_dirty = node::from_untrusted_node_address(self.js_runtime.ptr, - *node).root(); - let page = get_page(&self.root_page(), pipeline_id); - let document = page.document().root(); - document.r().content_changed(node_to_dirty.r(), - NodeDamage::OtherNodeDamage); - } - - self.handle_reflow_event(pipeline_id); - } - ClickEvent(button, point) => { let _marker; if self.need_emit_timeline_marker(TimelineMarkerType::DOMEvent) { @@ -1241,7 +1255,7 @@ impl ScriptTask { } let page = get_page(&self.root_page(), pipeline_id); let document = page.document().root(); - document.r().handle_click_event(self.js_runtime.ptr, button, point); + document.r().handle_click_event(self.js_runtime.rt(), button, point); } MouseDownEvent(..) => {} @@ -1256,7 +1270,7 @@ impl ScriptTask { let mut mouse_over_targets = RootedVec::new(); mouse_over_targets.append(&mut *self.mouse_over_targets.borrow_mut()); - document.r().handle_mouse_move_event(self.js_runtime.ptr, point, &mut mouse_over_targets); + document.r().handle_mouse_move_event(self.js_runtime.rt(), point, &mut mouse_over_targets); *self.mouse_over_targets.borrow_mut() = mouse_over_targets.clone(); } @@ -1336,16 +1350,6 @@ impl ScriptTask { event.fire(wintarget); } - fn handle_reflow_event(&self, pipeline_id: PipelineId) { - debug!("script got reflow event"); - let page = get_page(&self.root_page(), pipeline_id); - let document = page.document().root(); - let window = window_from_node(document.r()).root(); - window.r().reflow(ReflowGoal::ForDisplay, - ReflowQueryType::NoQuery, - ReflowReason::ReceivedReflowEvent); - } - /// Initiate a non-blocking fetch for a specified resource. Stores the InProgressLoad /// argument until a notification is received that the fetch is complete. fn start_page_load(&self, incomplete: InProgressLoad, mut load_data: LoadData) { |