diff options
Diffstat (limited to 'components/script/script_task.rs')
-rw-r--r-- | components/script/script_task.rs | 380 |
1 files changed, 293 insertions, 87 deletions
diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 9a5f01084cf..832024a1c03 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -21,10 +21,12 @@ use document_loader::{LoadType, DocumentLoader, NotifierData}; use dom::bindings::cell::DOMRefCell; +use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods; use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyState}; -use dom::bindings::codegen::InheritTypes::{ElementCast, EventTargetCast, HTMLIFrameElementCast, NodeCast, EventCast}; +use dom::bindings::codegen::InheritTypes::{ElementCast, EventTargetCast, NodeCast, EventCast}; use dom::bindings::conversions::FromJSValConvertible; use dom::bindings::conversions::StringificationBehavior; +use dom::bindings::global::GlobalRef; use dom::bindings::js::{JS, RootCollection, trace_roots}; use dom::bindings::js::{RootCollectionPtr, Root, RootedReference}; use dom::bindings::refcounted::{LiveDOMReferences, Trusted, TrustedReference, trace_refcounted_objects}; @@ -35,30 +37,31 @@ use dom::document::{Document, IsHTMLDocument, DocumentHelpers, DocumentProgressH DocumentProgressTask, DocumentSource, MouseEventType}; use dom::element::{Element, AttributeHandlers}; use dom::event::{EventHelpers, EventBubbles, EventCancelable}; -use dom::htmliframeelement::{HTMLIFrameElement, HTMLIFrameElementHelpers}; +use dom::htmliframeelement::HTMLIFrameElementHelpers; use dom::uievent::UIEvent; use dom::node::{Node, NodeHelpers, NodeDamage, window_from_node}; use dom::servohtmlparser::{ServoHTMLParser, ParserContext}; use dom::window::{Window, WindowHelpers, ScriptHelpers, ReflowReason}; use dom::worker::TrustedWorkerAddress; use parse::html::{ParseContext, parse_html}; -use layout_interface::{ScriptLayoutChan, LayoutChan, ReflowGoal, ReflowQueryType}; -use layout_interface; +use layout_interface::{self, NewLayoutTaskInfo, ScriptLayoutChan, LayoutChan, ReflowGoal}; +use layout_interface::{ReflowQueryType}; +use mem::heap_size_of_eventtarget; use network_listener::NetworkListener; use page::{Page, IterablePage, Frame}; use timers::TimerId; use devtools; use webdriver_handlers; -use devtools_traits::{DevtoolsControlChan, DevtoolsControlPort, DevtoolsPageInfo}; -use devtools_traits::{DevtoolsControlMsg, DevtoolScriptControlMsg}; -use devtools_traits::{TimelineMarker, TimelineMarkerType, TracingMetadata}; -use script_traits::{CompositorEvent, MouseButton}; -use script_traits::CompositorEvent::{ResizeEvent, ClickEvent}; +use devtools_traits::{DevtoolsControlPort, DevtoolsPageInfo, DevtoolScriptControlMsg}; +use devtools_traits::{ScriptToDevtoolsControlMsg, TimelineMarker, TimelineMarkerType}; +use devtools_traits::{TracingMetadata}; use script_traits::CompositorEvent::{MouseDownEvent, MouseUpEvent}; use script_traits::CompositorEvent::{MouseMoveEvent, KeyEvent}; -use script_traits::{NewLayoutInfo, OpaqueScriptLayoutChannel}; +use script_traits::CompositorEvent::{ResizeEvent, ClickEvent}; +use script_traits::{CompositorEvent, MouseButton}; use script_traits::{ConstellationControlMsg, ScriptControlChan}; +use script_traits::{NewLayoutInfo, OpaqueScriptLayoutChannel}; use script_traits::{ScriptState, ScriptTaskFactory}; use msg::compositor_msg::{LayerId, ScriptListener}; use msg::constellation_msg::{ConstellationChan, FocusType}; @@ -66,10 +69,11 @@ use msg::constellation_msg::{LoadData, PipelineId, SubpageId, MozBrowserEvent, W use msg::constellation_msg::{Failure, WindowSizeData, PipelineExitType}; use msg::constellation_msg::Msg as ConstellationMsg; use msg::webdriver_msg::WebDriverScriptCommand; -use net_traits::{ResourceTask, LoadConsumer, ControlMsg, Metadata}; use net_traits::LoadData as NetLoadData; +use net_traits::{AsyncResponseTarget, ResourceTask, LoadConsumer, ControlMsg, Metadata}; use net_traits::image_cache_task::{ImageCacheChan, ImageCacheTask, ImageCacheResult}; use net_traits::storage_task::StorageTask; +use profile_traits::mem::{self, Report, Reporter, ReporterRequest, ReportKind, ReportsChan}; use string_cache::Atom; use util::str::DOMString; use util::task::spawn_named_with_send_on_failure; @@ -78,20 +82,23 @@ use util::task_state; use euclid::Rect; use euclid::point::Point2D; use hyper::header::{LastModified, Headers}; +use ipc_channel::ipc::{self, IpcSender}; +use ipc_channel::router::ROUTER; +use js::glue::CollectServoSizes; use js::jsapi::{JS_SetWrapObjectCallbacks, JS_AddExtraGCRootsTracer, DisableIncrementalGC}; use js::jsapi::{JSContext, JSRuntime, JSTracer}; -use js::jsapi::{JS_SetGCCallback, JSGCStatus, JSAutoRequest, SetDOMCallbacks}; +use js::jsapi::{JS_GetRuntime, JS_SetGCCallback, JSGCStatus, JSAutoRequest, SetDOMCallbacks}; use js::jsapi::{SetDOMProxyInformation, DOMProxyShadowsResult, HandleObject, HandleId, RootedValue}; use js::jsval::UndefinedValue; use js::rust::Runtime; -use url::Url; +use url::{Url, UrlParser}; use libc; use std::any::Any; use std::borrow::ToOwned; use std::cell::{Cell, RefCell}; use std::collections::HashSet; -use std::mem; +use std::mem as std_mem; use std::option::Option; use std::ptr; use std::rc::Rc; @@ -121,7 +128,7 @@ unsafe extern fn trace_rust_roots(tr: *mut JSTracer, _data: *mut libc::c_void) { /// data that will need to be present when the document and frame tree entry are created, /// but is only easily available at initiation of the load and on a push basis (so some /// data will be updated according to future resize events, viewport changes, etc.) -#[jstraceable] +#[derive(JSTraceable)] struct InProgressLoad { /// The pipeline which requested this load. pipeline_id: PipelineId, @@ -131,7 +138,7 @@ struct InProgressLoad { window_size: Option<WindowSizeData>, /// Channel to the layout task associated with this pipeline. layout_chan: LayoutChan, - /// The current viewport clipping rectangle applying to this pipelie, if any. + /// The current viewport clipping rectangle applying to this pipeline, if any. clip_rect: Option<Rect<f32>>, /// The requested URL of the load. url: Url, @@ -196,6 +203,9 @@ pub enum ScriptMsg { RefcountCleanup(TrustedReference), /// Notify a document that all pending loads are complete. DocumentLoadsComplete(PipelineId), + /// Requests that the script task measure its memory usage. The results are sent back via the + /// supplied channel. + CollectReports(ReportsChan), } /// A cloneable interface for communicating with an event loop. @@ -226,7 +236,7 @@ impl ScriptPort for Receiver<(TrustedWorkerAddress, ScriptMsg)> { } /// Encapsulates internal communication within the script task. -#[jstraceable] +#[derive(JSTraceable)] pub struct NonWorkerScriptChan(pub Sender<ScriptMsg>); impl ScriptChan for NonWorkerScriptChan { @@ -269,7 +279,9 @@ impl Drop for StackRootTLS { /// Information for an entire page. Pages are top-level browsing contexts and can contain multiple /// frames. -#[jstraceable] +#[derive(JSTraceable)] +// ScriptTask instances are rooted on creation, so this is okay +#[allow(unrooted_must_root)] pub struct ScriptTask { /// A handle to the information pertaining to page layout page: DOMRefCell<Option<Rc<Page>>>, @@ -277,8 +289,9 @@ pub struct ScriptTask { incomplete_loads: DOMRefCell<Vec<InProgressLoad>>, /// A handle to the image cache task. image_cache_task: ImageCacheTask, - /// A handle to the resource task. - resource_task: ResourceTask, + /// A handle to the resource task. This is an `Arc` to avoid running out of file descriptors if + /// there are many iframes. + resource_task: Arc<ResourceTask>, /// A handle to the storage task. storage_task: StorageTask, @@ -297,8 +310,9 @@ pub struct ScriptTask { /// For communicating load url messages to the constellation constellation_chan: ConstellationChan, + /// A handle to the compositor for communicating ready state messages. - compositor: DOMRefCell<Box<ScriptListener+'static>>, + compositor: DOMRefCell<ScriptListener>, /// The port on which we receive messages from the image cache image_cache_port: Receiver<ImageCacheResult>, @@ -306,16 +320,19 @@ pub struct ScriptTask { /// The channel on which the image cache can send messages to ourself. image_cache_channel: ImageCacheChan, + /// For providing contact with the memory profiler. + mem_profiler_chan: mem::ProfilerChan, + /// For providing instructions to an optional devtools server. - devtools_chan: Option<DevtoolsControlChan>, + devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>, /// For receiving commands from an optional devtools server. Will be ignored if /// no such server exists. devtools_port: DevtoolsControlPort, - devtools_sender: Sender<DevtoolScriptControlMsg>, + devtools_sender: IpcSender<DevtoolScriptControlMsg>, /// For sending timeline markers. Will be ignored if /// no devtools server devtools_markers: RefCell<HashSet<TimelineMarkerType>>, - devtools_marker_sender: RefCell<Option<Sender<TimelineMarker>>>, + devtools_marker_sender: RefCell<Option<IpcSender<TimelineMarker>>>, /// The JavaScript runtime. js_runtime: Rc<Runtime>, @@ -374,37 +391,40 @@ impl ScriptTaskFactory for ScriptTask { box pair.sender() as Box<Any+Send> } - fn create<C>(_phantom: Option<&mut ScriptTask>, - id: PipelineId, - parent_info: Option<(PipelineId, SubpageId)>, - compositor: C, - layout_chan: &OpaqueScriptLayoutChannel, - control_chan: ScriptControlChan, - control_port: Receiver<ConstellationControlMsg>, - constellation_chan: ConstellationChan, - failure_msg: Failure, - resource_task: ResourceTask, - storage_task: StorageTask, - image_cache_task: ImageCacheTask, - devtools_chan: Option<DevtoolsControlChan>, - window_size: Option<WindowSizeData>, - load_data: LoadData) - where C: ScriptListener + Send + 'static { + fn create(_phantom: Option<&mut ScriptTask>, + id: PipelineId, + parent_info: Option<(PipelineId, SubpageId)>, + compositor: ScriptListener, + layout_chan: &OpaqueScriptLayoutChannel, + control_chan: ScriptControlChan, + control_port: Receiver<ConstellationControlMsg>, + constellation_chan: ConstellationChan, + failure_msg: Failure, + resource_task: ResourceTask, + storage_task: StorageTask, + image_cache_task: ImageCacheTask, + mem_profiler_chan: mem::ProfilerChan, + devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>, + window_size: Option<WindowSizeData>, + load_data: LoadData) { let ConstellationChan(const_chan) = constellation_chan.clone(); let (script_chan, script_port) = channel(); let layout_chan = LayoutChan(layout_chan.sender()); spawn_named_with_send_on_failure(format!("ScriptTask {:?}", id), task_state::SCRIPT, move || { let roots = RootCollection::new(); let _stack_roots_tls = StackRootTLS::new(&roots); - let script_task = ScriptTask::new(box compositor as Box<ScriptListener>, + let chan = NonWorkerScriptChan(script_chan); + let channel_for_reporter = chan.clone(); + let script_task = ScriptTask::new(compositor, script_port, - NonWorkerScriptChan(script_chan), + chan, control_chan, control_port, constellation_chan, - resource_task, + Arc::new(resource_task), storage_task, image_cache_task, + mem_profiler_chan.clone(), devtools_chan); SCRIPT_TASK_ROOT.with(|root| { @@ -417,8 +437,25 @@ impl ScriptTaskFactory for ScriptTask { load_data.url.clone()); script_task.start_page_load(new_load, load_data); + // Register this task as a memory reporter. + let reporter_name = format!("script-reporter-{}", id.0); + let (reporter_sender, reporter_receiver) = ipc::channel().unwrap(); + ROUTER.add_route(reporter_receiver.to_opaque(), box move |reporter_request| { + // Just injects an appropriate event into the worker task's queue. + let reporter_request: ReporterRequest = reporter_request.to().unwrap(); + channel_for_reporter.send(ScriptMsg::CollectReports( + reporter_request.reports_channel)).unwrap() + }); + let reporter = Reporter(reporter_sender); + let msg = mem::ProfilerMsg::RegisterReporter(reporter_name.clone(), reporter); + mem_profiler_chan.send(msg); + script_task.start(); + // Unregister this task as a memory reporter. + let msg = mem::ProfilerMsg::UnregisterReporter(reporter_name); + mem_profiler_chan.send(msg); + // This must always be the very last operation performed before the task completes failsafe.neuter(); }, ConstellationMsg::Failure(failure_msg), const_chan); @@ -464,16 +501,17 @@ impl ScriptTask { } /// Creates a new script task. - pub fn new(compositor: Box<ScriptListener+'static>, + pub fn new(compositor: ScriptListener, port: Receiver<ScriptMsg>, chan: NonWorkerScriptChan, control_chan: ScriptControlChan, control_port: Receiver<ConstellationControlMsg>, constellation_chan: ConstellationChan, - resource_task: ResourceTask, + resource_task: Arc<ResourceTask>, storage_task: StorageTask, image_cache_task: ImageCacheTask, - devtools_chan: Option<DevtoolsControlChan>) + mem_profiler_chan: mem::ProfilerChan, + devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>) -> ScriptTask { let runtime = ScriptTask::new_rt_and_cx(); @@ -482,15 +520,21 @@ impl ScriptTask { &WRAP_CALLBACKS); } - let (devtools_sender, devtools_receiver) = channel(); - let (image_cache_channel, image_cache_port) = channel(); + // Ask the router to proxy IPC messages from the devtools to us. + let (ipc_devtools_sender, ipc_devtools_receiver) = ipc::channel().unwrap(); + let devtools_port = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_devtools_receiver); + + // Ask the router to proxy IPC messages from the image cache task to us. + let (ipc_image_cache_channel, ipc_image_cache_port) = ipc::channel().unwrap(); + let image_cache_port = + ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_image_cache_port); ScriptTask { page: DOMRefCell::new(None), incomplete_loads: DOMRefCell::new(vec!()), image_cache_task: image_cache_task, - image_cache_channel: ImageCacheChan(image_cache_channel), + image_cache_channel: ImageCacheChan(ipc_image_cache_channel), image_cache_port: image_cache_port, resource_task: resource_task, @@ -502,9 +546,11 @@ impl ScriptTask { control_port: control_port, constellation_chan: constellation_chan, compositor: DOMRefCell::new(compositor), + mem_profiler_chan: mem_profiler_chan, + devtools_chan: devtools_chan, - devtools_port: devtools_receiver, - devtools_sender: devtools_sender, + devtools_port: devtools_port, + devtools_sender: ipc_devtools_sender, devtools_markers: RefCell::new(HashSet::new()), devtools_marker_sender: RefCell::new(None), @@ -623,8 +669,9 @@ impl ScriptTask { } }; - // Squash any pending resize, reflow, and mouse-move events in the queue. + // Squash any pending resize, reflow, animation tick, and mouse-move events in the queue. let mut mouse_move_event_index = None; + let mut animation_ticks = HashSet::new(); loop { match event { // This has to be handled before the ResizeMsg below, @@ -640,6 +687,13 @@ impl ScriptTask { MixedMessage::FromConstellation(ConstellationControlMsg::Viewport(id, rect)) => { self.handle_viewport(id, rect); } + MixedMessage::FromConstellation(ConstellationControlMsg::TickAllAnimations( + pipeline_id)) => { + if !animation_ticks.contains(&pipeline_id) { + animation_ticks.insert(pipeline_id); + sequential.push(event); + } + } MixedMessage::FromConstellation(ConstellationControlMsg::SendEvent( _, MouseMoveEvent(_))) => { @@ -783,14 +837,19 @@ impl ScriptTask { LiveDOMReferences::cleanup(addr), ScriptMsg::DocumentLoadsComplete(id) => self.handle_loads_complete(id), + ScriptMsg::CollectReports(reports_chan) => + self.collect_reports(reports_chan), } } fn handle_msg_from_devtools(&self, msg: DevtoolScriptControlMsg) { let page = self.root_page(); match msg { - DevtoolScriptControlMsg::EvaluateJS(id, s, reply) => - devtools::handle_evaluate_js(&page, id, s, reply), + DevtoolScriptControlMsg::EvaluateJS(id, s, reply) => { + let window = get_page(&page, id).window(); + let global_ref = GlobalRef::Window(window.r()); + devtools::handle_evaluate_js(&global_ref, s, reply) + }, DevtoolScriptControlMsg::GetRootNode(id, reply) => devtools::handle_get_root_node(&page, id, reply), DevtoolScriptControlMsg::GetDocumentElement(id, reply) => @@ -803,8 +862,11 @@ impl ScriptTask { devtools::handle_get_cached_messages(pipeline_id, message_types, reply), DevtoolScriptControlMsg::ModifyAttribute(id, node_id, modifications) => devtools::handle_modify_attribute(&page, id, node_id, modifications), - DevtoolScriptControlMsg::WantsLiveNotifications(pipeline_id, to_send) => - devtools::handle_wants_live_notifications(&page, pipeline_id, to_send), + DevtoolScriptControlMsg::WantsLiveNotifications(id, to_send) => { + let window = get_page(&page, id).window(); + let global_ref = GlobalRef::Window(window.r()); + devtools::handle_wants_live_notifications(&global_ref, to_send) + }, DevtoolScriptControlMsg::SetTimelineMarkers(_pipeline_id, marker_types, reply) => devtools::handle_set_timeline_markers(&page, self, marker_types, reply), DevtoolScriptControlMsg::DropTimelineMarkers(_pipeline_id, marker_types) => @@ -835,6 +897,8 @@ impl ScriptTask { webdriver_handlers::handle_get_text(&page, pipeline_id, node_id, reply), WebDriverScriptCommand::GetFrameId(frame_id, reply) => webdriver_handlers::handle_get_frame_id(&page, pipeline_id, frame_id, reply), + WebDriverScriptCommand::GetUrl(reply) => + webdriver_handlers::handle_get_url(&page, pipeline_id, reply), WebDriverScriptCommand::GetTitle(reply) => webdriver_handlers::handle_get_title(&page, pipeline_id, reply), WebDriverScriptCommand::ExecuteAsyncScript(script, reply) => @@ -925,18 +989,44 @@ impl ScriptTask { containing_pipeline_id, new_pipeline_id, subpage_id, - layout_chan, load_data, + paint_chan, + failure, + pipeline_port, + layout_shutdown_chan, } = new_layout_info; + let layout_pair = ScriptTask::create_layout_channel(None::<&mut ScriptTask>); + let layout_chan = LayoutChan(*ScriptTask::clone_layout_channel( + None::<&mut ScriptTask>, + &layout_pair).downcast::<Sender<layout_interface::Msg>>().unwrap()); + + let layout_creation_info = NewLayoutTaskInfo { + id: new_pipeline_id, + url: load_data.url.clone(), + is_parent: false, + layout_pair: layout_pair, + pipeline_port: pipeline_port, + constellation_chan: self.constellation_chan.clone(), + failure: failure, + paint_chan: paint_chan, + script_chan: self.control_chan.0.clone(), + image_cache_task: self.image_cache_task.clone(), + layout_shutdown_chan: layout_shutdown_chan, + }; + let page = self.root_page(); let parent_page = page.find(containing_pipeline_id).expect("ScriptTask: received a layout whose parent has a PipelineId which does not correspond to a pipeline in the script task's page tree. This is a bug."); - let parent_window = parent_page.window(); - let chan = layout_chan.downcast_ref::<Sender<layout_interface::Msg>>().unwrap(); - let layout_chan = LayoutChan(chan.clone()); + + // Tell layout to actually spawn the task. + parent_window.layout_chan() + .0 + .send(layout_interface::Msg::CreateLayoutTask(layout_creation_info)) + .unwrap(); + // Kick off the fetch for the new resource. let new_load = InProgressLoad::new(new_pipeline_id, Some((containing_pipeline_id, subpage_id)), layout_chan, parent_window.r().window_size(), @@ -963,6 +1053,83 @@ impl ScriptTask { chan.send(ConstellationMsg::LoadComplete(pipeline)).unwrap(); } + pub fn get_reports(cx: *mut JSContext, path_seg: String) -> Vec<Report> { + let mut reports = vec![]; + + unsafe { + let rt = JS_GetRuntime(cx); + let mut stats = ::std::mem::zeroed(); + if CollectServoSizes(rt, &mut stats) { + let mut report = |mut path_suffix, kind, size| { + let mut path = path![path_seg, "js"]; + path.append(&mut path_suffix); + reports.push(Report { + path: path, + kind: kind, + size: size as usize, + }) + }; + + // A note about possibly confusing terminology: the JS GC "heap" is allocated via + // mmap/VirtualAlloc, which means it's not on the malloc "heap", so we use + // `ExplicitNonHeapSize` as its kind. + + report(path!["gc-heap", "used"], + ReportKind::ExplicitNonHeapSize, + stats.gcHeapUsed); + + report(path!["gc-heap", "unused"], + ReportKind::ExplicitNonHeapSize, + stats.gcHeapUnused); + + report(path!["gc-heap", "admin"], + ReportKind::ExplicitNonHeapSize, + stats.gcHeapAdmin); + + report(path!["gc-heap", "decommitted"], + ReportKind::ExplicitNonHeapSize, + stats.gcHeapDecommitted); + + // SpiderMonkey uses the system heap, not jemalloc. + report(path!["malloc-heap"], + ReportKind::ExplicitSystemHeapSize, + stats.mallocHeap); + + report(path!["non-heap"], + ReportKind::ExplicitNonHeapSize, + stats.nonHeap); + } + } + reports + } + + fn collect_reports(&self, reports_chan: ReportsChan) { + let mut urls = vec![]; + let mut dom_tree_size = 0; + let mut reports = vec![]; + for it_page in self.root_page().iter() { + let current_url = it_page.document().url().serialize(); + urls.push(current_url.clone()); + + for child in NodeCast::from_ref(&*it_page.document()).traverse_preorder() { + let target = EventTargetCast::from_ref(&*child); + dom_tree_size += heap_size_of_eventtarget(target); + } + let window = it_page.window(); + let target = EventTargetCast::from_ref(&*window); + dom_tree_size += heap_size_of_eventtarget(target); + + reports.push(Report { + path: path![format!("url({})", current_url), "dom-tree"], + kind: ReportKind::ExplicitJemallocHeapSize, + size: dom_tree_size, + }) + } + let path_seg = format!("url({})", urls.join(", ")); + reports.extend(ScriptTask::get_reports(self.get_cx(), path_seg)); + reports_chan.send(reports); + } + /// Handles a timer that fired. fn handle_fire_timer_msg(&self, id: PipelineId, timer_id: TimerId) { let page = self.root_page(); @@ -1003,7 +1170,7 @@ impl ScriptTask { let page = borrowed_page.find(parent_pipeline_id).unwrap(); let doc = page.document(); - let frame_element = self.find_iframe(doc.r(), subpage_id); + let frame_element = doc.find_iframe(subpage_id); if let Some(ref frame_element) = frame_element { let element = ElementCast::from_ref(frame_element.r()); @@ -1023,7 +1190,7 @@ impl ScriptTask { let frame_element = borrowed_page.find(parent_pipeline_id).and_then(|page| { let doc = page.document(); - self.find_iframe(doc.r(), subpage_id) + doc.find_iframe(subpage_id) }); if let Some(ref frame_element) = frame_element { @@ -1039,7 +1206,7 @@ impl ScriptTask { let frame_element = borrowed_page.find(containing_pipeline_id).and_then(|page| { let doc = page.document(); - self.find_iframe(doc.r(), old_subpage_id) + doc.find_iframe(old_subpage_id) }); frame_element.r().unwrap().update_subpage_id(new_subpage_id); @@ -1162,7 +1329,7 @@ impl ScriptTask { fn handle_tick_all_animations(&self, id: PipelineId) { let page = get_page(&self.root_page(), id); let document = page.document(); - document.r().invoke_animation_callbacks(); + document.r().run_the_animation_frame_callbacks(); } /// The entry point to document loading. Defines bindings, sets up the window and document @@ -1190,13 +1357,13 @@ impl ScriptTask { borrowed_page.as_ref().and_then(|borrowed_page| { borrowed_page.find(parent_id).and_then(|page| { let doc = page.document(); - self.find_iframe(doc.r(), subpage_id) + doc.find_iframe(subpage_id) }) }) }); // Create a new frame tree entry. - let page = Rc::new(Page::new(incomplete.pipeline_id, final_url.clone())); + let page = Rc::new(Page::new(incomplete.pipeline_id)); if !root_page_exists { // We have a new root frame tree. *self.page.borrow_mut() = Some(page.clone()); @@ -1261,6 +1428,7 @@ impl ScriptTask { self.image_cache_task.clone(), self.resource_task.clone(), self.storage_task.clone(), + self.mem_profiler_chan.clone(), self.devtools_chan.clone(), self.constellation_chan.clone(), incomplete.layout_chan, @@ -1273,7 +1441,9 @@ impl ScriptTask { }); let content_type = match metadata.content_type { - Some(ContentType(Mime(TopLevel::Text, SubLevel::Plain, _))) => Some("text/plain".to_owned()), + Some(ContentType(Mime(TopLevel::Text, SubLevel::Plain, _))) => { + Some("text/plain".to_owned()) + } _ => None }; @@ -1293,7 +1463,7 @@ impl ScriptTask { loader); let frame_element = frame_element.r().map(|elem| ElementCast::from_ref(elem)); - window.r().init_browser_context(document.r(), frame_element); + window.r().init_browsing_context(document.r(), frame_element); // Create the root frame page.set_frame(Some(Frame { @@ -1330,9 +1500,10 @@ impl ScriptTask { title: title, url: url, }; - chan.send(DevtoolsControlMsg::NewGlobal(ids, - self.devtools_sender.clone(), - page_info)).unwrap(); + chan.send(ScriptToDevtoolsControlMsg::NewGlobal( + ids, + self.devtools_sender.clone(), + page_info)).unwrap(); } } } @@ -1356,16 +1527,6 @@ impl ScriptTask { window.r().reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, reason); } - /// Find an iframe element in a provided document. - fn find_iframe(&self, doc: &Document, subpage_id: SubpageId) - -> Option<Root<HTMLIFrameElement>> { - let doc = NodeCast::from_ref(doc); - - doc.traverse_preorder() - .filter_map(HTMLIFrameElementCast::to_root) - .find(|node| node.r().subpage_id() == Some(subpage_id)) - } - /// This is the main entry point for receiving and dispatching DOM events. /// /// TODO: Actually perform DOM event dispatch. @@ -1400,12 +1561,49 @@ impl ScriptTask { } let page = get_page(&self.root_page(), pipeline_id); let document = page.document(); + + let mut prev_mouse_over_targets: RootedVec<JS<Node>> = RootedVec::new(); + for target in self.mouse_over_targets.borrow_mut().iter() { + prev_mouse_over_targets.push(target.clone()); + } + // We temporarily steal the list of targets over which the mouse is to pass it to // handle_mouse_move_event() in a safe RootedVec container. let mut mouse_over_targets = RootedVec::new(); - mem::swap(&mut *self.mouse_over_targets.borrow_mut(), &mut *mouse_over_targets); + std_mem::swap(&mut *self.mouse_over_targets.borrow_mut(), &mut *mouse_over_targets); document.r().handle_mouse_move_event(self.js_runtime.rt(), point, &mut mouse_over_targets); - mem::swap(&mut *self.mouse_over_targets.borrow_mut(), &mut *mouse_over_targets); + + // Notify Constellation about anchors that are no longer mouse over targets. + for target in prev_mouse_over_targets.iter() { + if !mouse_over_targets.contains(target) { + if target.root().r().is_anchor_element() { + let event = ConstellationMsg::NodeStatus(None); + let ConstellationChan(ref chan) = self.constellation_chan; + chan.send(event).unwrap(); + break; + } + } + } + + // Notify Constellation about the topmost anchor mouse over target. + for target in mouse_over_targets.iter() { + let target = target.root(); + if target.r().is_anchor_element() { + let element = ElementCast::to_ref(target.r()).unwrap(); + let status = element.get_attribute(&ns!(""), &atom!("href")) + .and_then(|href| { + let value = href.r().Value(); + let url = document.r().url(); + UrlParser::new().base_url(&url).parse(&value).map(|url| url.serialize()).ok() + }); + let event = ConstellationMsg::NodeStatus(status); + let ConstellationChan(ref chan) = self.constellation_chan; + chan.send(event).unwrap(); + break; + } + } + + std_mem::swap(&mut *self.mouse_over_targets.borrow_mut(), &mut *mouse_over_targets); } KeyEvent(key, state, modifiers) => { @@ -1444,7 +1642,7 @@ impl ScriptTask { let borrowed_page = self.root_page(); let iframe = borrowed_page.find(pipeline_id).and_then(|page| { let doc = page.document(); - self.find_iframe(doc.r(), subpage_id) + doc.find_iframe(subpage_id) }); if let Some(iframe) = iframe.r() { iframe.navigate_child_browsing_context(load_data.url); @@ -1510,10 +1708,17 @@ impl ScriptTask { let context = Arc::new(Mutex::new(ParserContext::new(id, subpage, script_chan.clone(), load_data.url.clone()))); + let (action_sender, action_receiver) = ipc::channel().unwrap(); let listener = box NetworkListener { context: context, script_chan: script_chan.clone(), }; + ROUTER.add_route(action_receiver.to_opaque(), box move |message| { + listener.notify(message.to().unwrap()); + }); + let response_target = AsyncResponseTarget { + sender: action_sender, + }; if load_data.url.scheme == "javascript" { load_data.url = Url::parse("about:blank").unwrap(); @@ -1527,7 +1732,7 @@ impl ScriptTask { data: load_data.data, cors: None, pipeline_id: Some(id), - }, LoadConsumer::Listener(listener))).unwrap(); + }, LoadConsumer::Listener(response_target))).unwrap(); self.incomplete_loads.borrow_mut().push(incomplete); } @@ -1542,7 +1747,9 @@ impl ScriptTask { sender.send(marker).unwrap(); } - pub fn set_devtools_timeline_marker(&self, marker: TimelineMarkerType, reply: Sender<TimelineMarker>) { + pub fn set_devtools_timeline_marker(&self, + marker: TimelineMarkerType, + reply: IpcSender<TimelineMarker>) { *self.devtools_marker_sender.borrow_mut() = Some(reply); self.devtools_markers.borrow_mut().insert(marker); } @@ -1646,7 +1853,6 @@ fn shut_down_layout(page_tree: &Rc<Page>, exit_type: PipelineExitType) { } } - pub fn get_page(page: &Rc<Page>, pipeline_id: PipelineId) -> Rc<Page> { page.find(pipeline_id).expect("ScriptTask: received an event \ message for a layout channel that is not associated with this script task.\ |