aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/script_task.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/script_task.rs')
-rw-r--r--components/script/script_task.rs854
1 files changed, 475 insertions, 379 deletions
diff --git a/components/script/script_task.rs b/components/script/script_task.rs
index fd608fe4284..238763f3035 100644
--- a/components/script/script_task.rs
+++ b/components/script/script_task.rs
@@ -3,7 +3,19 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! The script task is the task that owns the DOM in memory, runs JavaScript, and spawns parsing
-//! and layout tasks.
+//! and layout tasks. It's in charge of processing events for all same-origin pages in a frame
+//! tree, and manages the entire lifetime of pages in the frame tree from initial request to
+//! teardown.
+//!
+//! Page loads follow a two-step process. When a request for a new page load is received, the
+//! network request is initiated and the relevant data pertaining to the new page is stashed.
+//! While the non-blocking request is ongoing, the script task is free to process further events,
+//! noting when they pertain to ongoing loads (such as resizes/viewport adjustments). When the
+//! initial response is received for an ongoing load, the second phase starts - the frame tree
+//! entry is created, along with the Window and Document objects, and the appropriate parser
+//! takes over the response body. Once parsing is complete, the document lifecycle for loading
+//! a page runs its course and the script task returns to processing events in the main event
+//! loop.
#![allow(unsafe_blocks)]
@@ -20,13 +32,12 @@ use dom::bindings::trace::JSTraceable;
use dom::bindings::utils::{wrap_for_same_compartment, pre_wrap};
use dom::document::{Document, IsHTMLDocument, DocumentHelpers, DocumentProgressHandler, DocumentProgressTask, DocumentSource};
use dom::element::{Element, AttributeHandlers};
-use dom::event::{Event, EventHelpers};
+use dom::event::{Event, EventHelpers, EventBubbles, EventCancelable};
+use dom::htmliframeelement::HTMLIFrameElementHelpers;
use dom::uievent::UIEvent;
use dom::eventtarget::EventTarget;
-use dom::mouseevent::MouseEvent;
-use dom::node::{self, Node, NodeHelpers, NodeDamage};
-use dom::window::{Window, WindowHelpers, ScriptHelpers};
-use dom::worker::{Worker, TrustedWorkerAddress};
+use dom::node::{self, Node, NodeHelpers, NodeDamage, window_from_node};
+use dom::window::{Window, WindowHelpers, ScriptHelpers, ReflowReason};
use parse::html::{HTMLInput, parse_html};
use layout_interface::{ScriptLayoutChan, LayoutChan, ReflowGoal, ReflowQueryType};
use layout_interface;
@@ -50,20 +61,21 @@ use msg::constellation_msg::{LoadData, PipelineId, SubpageId};
use msg::constellation_msg::{Failure, Msg, WindowSizeData, PipelineExitType};
use msg::constellation_msg::Msg as ConstellationMsg;
use net::image_cache_task::ImageCacheTask;
-use net::resource_task::{ResourceTask, ControlMsg};
+use net::resource_task::{ResourceTask, ControlMsg, LoadResponse};
use net::resource_task::LoadData as NetLoadData;
use net::storage_task::StorageTask;
use string_cache::Atom;
use util::geometry::to_frac_px;
use util::smallvec::SmallVec;
use util::str::DOMString;
-use util::task::spawn_named_with_send_on_failure;
+use util::task::{spawn_named, spawn_named_with_send_on_failure};
use util::task_state;
+use geom::Rect;
use geom::point::Point2D;
use hyper::header::{LastModified, Headers};
-use js::jsapi::{JS_SetWrapObjectCallbacks, JS_SetGCZeal, JS_DEFAULT_ZEAL_FREQ, JS_GC};
-use js::jsapi::{JSContext, JSRuntime, JSObject};
+use js::jsapi::{JS_SetWrapObjectCallbacks, JS_SetGCZeal, JS_SetExtraGCRootsTracer, JS_DEFAULT_ZEAL_FREQ};
+use js::jsapi::{JSContext, JSRuntime, JSObject, JSTracer};
use js::jsapi::{JS_SetGCParameter, JSGC_MAX_BYTES};
use js::jsapi::{JS_SetGCCallback, JSGCStatus, JSGC_BEGIN, JSGC_END};
use js::rust::{Cx, RtUtils};
@@ -71,11 +83,13 @@ use js;
use url::Url;
use libc;
+use std::ascii::AsciiExt;
use std::any::Any;
use std::borrow::ToOwned;
-use std::cell::Cell;
-use std::mem::replace;
+use std::cell::{Cell, RefCell};
use std::num::ToPrimitive;
+use std::option::Option;
+use std::ptr;
use std::rc::Rc;
use std::result::Result;
use std::sync::mpsc::{channel, Sender, Receiver, Select};
@@ -83,6 +97,53 @@ use std::u32;
use time::Tm;
thread_local!(pub static STACK_ROOTS: Cell<Option<RootCollectionPtr>> = Cell::new(None));
+thread_local!(static SCRIPT_TASK_ROOT: RefCell<Option<*const ScriptTask>> = RefCell::new(None));
+
+unsafe extern fn trace_script_tasks(tr: *mut JSTracer, _data: *mut libc::c_void) {
+ SCRIPT_TASK_ROOT.with(|root| {
+ if let Some(script_task) = *root.borrow() {
+ (*script_task).trace(tr);
+ }
+ });
+}
+
+/// A document load that is in the process of fetching the requested resource. Contains
+/// 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]
+struct InProgressLoad {
+ /// The pipeline which requested this load.
+ pipeline_id: PipelineId,
+ /// The parent pipeline and child subpage associated with this load, if any.
+ subpage_id: Option<(PipelineId, SubpageId)>,
+ /// The current window size associated with this pipeline.
+ 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.
+ clip_rect: Option<Rect<f32>>,
+ /// The requested URL of the load.
+ url: Url,
+}
+
+impl InProgressLoad {
+ /// Create a new InProgressLoad object.
+ fn new(id: PipelineId,
+ subpage_id: Option<(PipelineId, SubpageId)>,
+ layout_chan: LayoutChan,
+ window_size: Option<WindowSizeData>,
+ url: Url) -> InProgressLoad {
+ InProgressLoad {
+ pipeline_id: id,
+ subpage_id: subpage_id,
+ layout_chan: layout_chan,
+ window_size: window_size,
+ clip_rect: None,
+ url: url,
+ }
+ }
+}
#[derive(Copy)]
pub enum TimerSource {
@@ -102,7 +163,7 @@ pub enum ScriptMsg {
TriggerFragment(PipelineId, String),
/// Begins a content-initiated load on the specified pipeline (only
/// dispatched to ScriptTask).
- TriggerLoad(PipelineId, LoadData),
+ Navigate(PipelineId, LoadData),
/// Fires a JavaScript timeout
/// TimerSource must be FromWindow when dispatched to ScriptTask and
/// must be FromWorker when dispatched to a DedicatedGlobalWorkerScope
@@ -113,12 +174,12 @@ pub enum ScriptMsg {
/// Message sent through Worker.postMessage (only dispatched to
/// DedicatedWorkerGlobalScope).
DOMMessage(StructuredCloneData),
- /// Sends a message to the Worker object (dispatched to all tasks) regarding error.
- WorkerDispatchErrorEvent(TrustedWorkerAddress, DOMString, DOMString, u32, u32),
/// Generic message that encapsulates event handling.
RunnableMsg(Box<Runnable+Send>),
/// A DOM object's last pinned reference was removed (dispatched to all tasks).
RefcountCleanup(TrustedReference),
+ /// The final network response for a page has arrived.
+ PageFetchComplete(PipelineId, Option<SubpageId>, LoadResponse),
}
/// A cloneable interface for communicating with an event loop.
@@ -174,13 +235,18 @@ impl Drop for StackRootTLS {
/// frames.
///
/// FIXME: Rename to `Page`, following WebKit?
+#[jstraceable]
pub struct ScriptTask {
/// A handle to the information pertaining to page layout
- page: DOMRefCell<Rc<Page>>,
+ page: DOMRefCell<Option<Rc<Page>>>,
+ /// A list of data pertaining to loads that have not yet received a network response
+ 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 storage task.
+ storage_task: StorageTask,
/// The port on which the script task receives messages (load URL, exit, etc.)
port: Receiver<ScriptMsg>,
@@ -243,7 +309,8 @@ impl<'a> Drop for ScriptMemoryFailsafe<'a> {
unsafe {
let page = owner.page.borrow_for_script_deallocation();
for page in page.iter() {
- *page.unsafe_mut_js_info() = None;
+ let window = page.window().root();
+ window.r().clear_js_context_for_script_deallocation();
}
*owner.js_context.borrow_for_script_deallocation() = None;
}
@@ -275,15 +342,14 @@ impl ScriptTaskFactory for ScriptTask {
storage_task: StorageTask,
image_cache_task: ImageCacheTask,
devtools_chan: Option<DevtoolsControlChan>,
- window_size: WindowSizeData)
+ window_size: Option<WindowSizeData>,
+ load_data: LoadData)
where C: ScriptListener + Send + 'static {
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("ScriptTask", task_state::SCRIPT, move || {
- let script_task = ScriptTask::new(id,
- box compositor as Box<ScriptListener>,
- layout_chan,
+ let script_task = ScriptTask::new(box compositor as Box<ScriptListener>,
script_port,
NonWorkerScriptChan(script_chan),
control_chan,
@@ -292,9 +358,17 @@ impl ScriptTaskFactory for ScriptTask {
resource_task,
storage_task,
image_cache_task,
- devtools_chan,
- window_size);
+ devtools_chan);
+
+ SCRIPT_TASK_ROOT.with(|root| {
+ *root.borrow_mut() = Some(&script_task as *const _);
+ });
let mut failsafe = ScriptMemoryFailsafe::new(&script_task);
+
+ let new_load = InProgressLoad::new(id, None, layout_chan, window_size,
+ load_data.url.clone());
+ script_task.start_page_load(new_load, load_data);
+
script_task.start();
// This must always be the very last operation performed before the task completes
@@ -313,9 +387,7 @@ unsafe extern "C" fn debug_gc_callback(_rt: *mut JSRuntime, status: JSGCStatus)
impl ScriptTask {
/// Creates a new script task.
- pub fn new(id: PipelineId,
- compositor: Box<ScriptListener+'static>,
- layout_chan: LayoutChan,
+ pub fn new(compositor: Box<ScriptListener+'static>,
port: Receiver<ScriptMsg>,
chan: NonWorkerScriptChan,
control_chan: ScriptControlChan,
@@ -324,8 +396,7 @@ impl ScriptTask {
resource_task: ResourceTask,
storage_task: StorageTask,
img_cache_task: ImageCacheTask,
- devtools_chan: Option<DevtoolsControlChan>,
- window_size: WindowSizeData)
+ devtools_chan: Option<DevtoolsControlChan>)
-> ScriptTask {
let (js_runtime, js_context) = ScriptTask::new_rt_and_cx();
let wrap_for_same_compartment = wrap_for_same_compartment as
@@ -349,19 +420,14 @@ impl ScriptTask {
Some(pre_wrap));
}
- let page = Page::new(id, None, layout_chan, window_size,
- resource_task.clone(),
- storage_task,
- constellation_chan.clone(),
- js_context.clone(),
- devtools_chan.clone());
-
let (devtools_sender, devtools_receiver) = channel();
ScriptTask {
- page: DOMRefCell::new(Rc::new(page)),
+ page: DOMRefCell::new(None),
+ incomplete_loads: DOMRefCell::new(vec!()),
image_cache_task: img_cache_task,
resource_task: resource_task,
+ storage_task: storage_task,
port: port,
chan: chan,
@@ -405,6 +471,7 @@ impl ScriptTask {
js_context.set_logging_error_reporter();
unsafe {
JS_SetGCZeal((*js_context).ptr, 0, JS_DEFAULT_ZEAL_FREQ);
+ JS_SetExtraGCRootsTracer((*js_runtime).ptr, Some(trace_script_tasks), ptr::null_mut());
}
// Needed for debug assertions about whether GC is running.
@@ -418,6 +485,11 @@ impl ScriptTask {
(js_runtime, js_context)
}
+ // Return the root page in the frame tree. Panics if it doesn't exist.
+ fn root_page(&self) -> Rc<Page> {
+ self.page.borrow().as_ref().unwrap().clone()
+ }
+
pub fn get_cx(&self) -> *mut JSContext {
(**self.js_context.borrow().as_ref().unwrap()).ptr
}
@@ -440,17 +512,18 @@ impl ScriptTask {
let mut resizes = vec!();
{
- let page = self.page.borrow_mut();
- for page in page.iter() {
- // Only process a resize if layout is idle.
- let layout_join_port = page.layout_join_port.borrow();
- if layout_join_port.is_none() {
- let mut resize_event = page.resize_event.get();
- match resize_event.take() {
- Some(size) => resizes.push((page.id, size)),
- None => ()
+ let page = self.page.borrow();
+ if let Some(page) = page.as_ref() {
+ for page in page.iter() {
+ // Only process a resize if layout is idle.
+ let window = page.window().root();
+ if window.r().layout_is_idle() {
+ let resize_event = window.r().steal_resize_event();
+ match resize_event {
+ Some(size) => resizes.push((window.r().pipeline(), size)),
+ None => ()
+ }
}
- page.resize_event.set(None);
}
}
}
@@ -503,17 +576,10 @@ impl ScriptTask {
self.handle_new_layout(new_layout_info);
}
MixedMessage::FromConstellation(ConstellationControlMsg::Resize(id, size)) => {
- let page = self.page.borrow_mut();
- let page = page.find(id).expect("resize sent to nonexistent pipeline");
- page.resize_event.set(Some(size));
+ self.handle_resize(id, size);
}
MixedMessage::FromConstellation(ConstellationControlMsg::Viewport(id, rect)) => {
- let page = self.page.borrow_mut();
- let inner_page = page.find(id).expect("Page rect message sent to nonexistent pipeline");
- if inner_page.set_page_clip_rect_with_new_viewport(rect) {
- let page = get_page(&*page, id);
- self.force_reflow(&*page);
- }
+ self.handle_viewport(id, rect);
}
_ => {
sequential.push(event);
@@ -556,8 +622,8 @@ impl ScriptTask {
match msg {
ConstellationControlMsg::AttachLayout(_) =>
panic!("should have handled AttachLayout already"),
- ConstellationControlMsg::Load(id, parent, load_data) =>
- self.load(id, parent, load_data),
+ ConstellationControlMsg::Navigate(pipeline_id, subpage_id, load_data) =>
+ self.handle_navigate(pipeline_id, Some(subpage_id), load_data),
ConstellationControlMsg::SendEvent(id, event) =>
self.handle_event(id, event),
ConstellationControlMsg::ReflowComplete(id, reflow_id) =>
@@ -581,8 +647,8 @@ impl ScriptTask {
fn handle_msg_from_script(&self, msg: ScriptMsg) {
match msg {
- ScriptMsg::TriggerLoad(id, load_data) =>
- self.trigger_load(id, load_data),
+ ScriptMsg::Navigate(id, load_data) =>
+ self.handle_navigate(id, None, load_data),
ScriptMsg::TriggerFragment(id, fragment) =>
self.trigger_fragment(id, fragment),
ScriptMsg::FireTimer(TimerSource::FromWindow(id), timer_id) =>
@@ -593,104 +659,141 @@ impl ScriptTask {
self.handle_exit_window_msg(id),
ScriptMsg::DOMMessage(..) =>
panic!("unexpected message"),
- ScriptMsg::WorkerDispatchErrorEvent(addr, msg, file_name,line_num, col_num) =>
- Worker::handle_error_message(addr, msg, file_name, line_num, col_num),
ScriptMsg::RunnableMsg(runnable) =>
runnable.handler(),
ScriptMsg::RefcountCleanup(addr) =>
LiveDOMReferences::cleanup(self.get_cx(), addr),
+ ScriptMsg::PageFetchComplete(id, subpage, response) =>
+ self.handle_page_fetch_complete(id, subpage, response),
}
}
fn handle_msg_from_devtools(&self, msg: DevtoolScriptControlMsg) {
+ let page = self.root_page();
match msg {
DevtoolScriptControlMsg::EvaluateJS(id, s, reply) =>
- devtools::handle_evaluate_js(&*self.page.borrow(), id, s, reply),
+ devtools::handle_evaluate_js(&page, id, s, reply),
DevtoolScriptControlMsg::GetRootNode(id, reply) =>
- devtools::handle_get_root_node(&*self.page.borrow(), id, reply),
+ devtools::handle_get_root_node(&page, id, reply),
DevtoolScriptControlMsg::GetDocumentElement(id, reply) =>
- devtools::handle_get_document_element(&*self.page.borrow(), id, reply),
+ devtools::handle_get_document_element(&page, id, reply),
DevtoolScriptControlMsg::GetChildren(id, node_id, reply) =>
- devtools::handle_get_children(&*self.page.borrow(), id, node_id, reply),
+ devtools::handle_get_children(&page, id, node_id, reply),
DevtoolScriptControlMsg::GetLayout(id, node_id, reply) =>
- devtools::handle_get_layout(&*self.page.borrow(), id, node_id, reply),
+ devtools::handle_get_layout(&page, id, node_id, reply),
DevtoolScriptControlMsg::ModifyAttribute(id, node_id, modifications) =>
- devtools::handle_modify_attribute(&*self.page.borrow(), id, node_id, modifications),
+ devtools::handle_modify_attribute(&page, id, node_id, modifications),
DevtoolScriptControlMsg::WantsLiveNotifications(pipeline_id, to_send) =>
- devtools::handle_wants_live_notifications(&*self.page.borrow(), pipeline_id, to_send),
+ devtools::handle_wants_live_notifications(&page, pipeline_id, to_send),
}
}
+ fn handle_resize(&self, id: PipelineId, size: WindowSizeData) {
+ let page = self.page.borrow();
+ if let Some(ref page) = page.as_ref() {
+ if let Some(ref page) = page.find(id) {
+ let window = page.window().root();
+ window.r().set_resize_event(size);
+ return;
+ }
+ }
+ let mut loads = self.incomplete_loads.borrow_mut();
+ if let Some(ref mut load) = loads.iter_mut().find(|load| load.pipeline_id == id) {
+ load.window_size = Some(size);
+ return;
+ }
+ panic!("resize sent to nonexistent pipeline");
+ }
+
+ fn handle_viewport(&self, id: PipelineId, rect: Rect<f32>) {
+ let page = self.page.borrow();
+ if let Some(page) = page.as_ref() {
+ if let Some(ref inner_page) = page.find(id) {
+ let window = inner_page.window().root();
+ if window.r().set_page_clip_rect_with_new_viewport(rect) {
+ let page = get_page(page, id);
+ self.force_reflow(&*page, ReflowReason::Viewport);
+ }
+ return;
+ }
+ }
+ let mut loads = self.incomplete_loads.borrow_mut();
+ if let Some(ref mut load) = loads.iter_mut().find(|load| load.pipeline_id == id) {
+ load.clip_rect = Some(rect);
+ 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) {
let NewLayoutInfo {
- old_pipeline_id,
+ containing_pipeline_id,
new_pipeline_id,
subpage_id,
- layout_chan
+ layout_chan,
+ load_data,
} = new_layout_info;
- let page = self.page.borrow_mut();
- let parent_page = page.find(old_pipeline_id).expect("ScriptTask: received a layout
+ 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 new_page = {
- let window_size = parent_page.window_size.get();
- Page::new(new_pipeline_id, Some(subpage_id),
- LayoutChan(layout_chan.downcast_ref::<Sender<layout_interface::Msg>>().unwrap().clone()),
- window_size,
- parent_page.resource_task.clone(),
- parent_page.storage_task.clone(),
- self.constellation_chan.clone(),
- self.js_context.borrow().as_ref().unwrap().clone(),
- self.devtools_chan.clone())
- };
- parent_page.children.borrow_mut().push(Rc::new(new_page));
+
+ let parent_window = parent_page.window().root();
+ let chan = layout_chan.downcast_ref::<Sender<layout_interface::Msg>>().unwrap();
+ let layout_chan = LayoutChan(chan.clone());
+ // 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(),
+ load_data.url.clone());
+ self.start_page_load(new_load, load_data);
}
/// Handles a timer that fired.
fn handle_fire_timer_msg(&self, id: PipelineId, timer_id: TimerId) {
- let page = self.page.borrow_mut();
+ let page = self.root_page();
let page = page.find(id).expect("ScriptTask: received fire timer msg for a
pipeline ID not associated with this script task. This is a bug.");
- let frame = page.frame();
- let window = frame.as_ref().unwrap().window.root();
+ let window = page.window().root();
window.r().handle_fire_timer(timer_id);
}
/// Handles freeze message
fn handle_freeze_msg(&self, id: PipelineId) {
- let page = self.page.borrow_mut();
+ let page = self.root_page();
let page = page.find(id).expect("ScriptTask: received freeze msg for a
pipeline ID not associated with this script task. This is a bug.");
- let frame = page.frame();
- let window = frame.as_ref().unwrap().window.root();
+ let window = page.window().root();
window.r().freeze();
}
/// Handles thaw message
fn handle_thaw_msg(&self, id: PipelineId) {
- let page = self.page.borrow_mut();
- let page = page.find(id).expect("ScriptTask: received thaw msg for a
- pipeline ID not associated with this script task. This is a bug.");
- let frame = page.frame();
- let window = frame.as_ref().unwrap().window.root();
+ // We should only get this message when moving in history, so all pages requested
+ // should exist.
+ let page = self.root_page().find(id).unwrap();
+
+ let needed_reflow = page.set_reflow_status(false);
+ if needed_reflow {
+ self.force_reflow(&*page, ReflowReason::CachedPageNeededReflow);
+ }
+
+ let window = page.window().root();
window.r().thaw();
}
/// Handles a notification that reflow completed.
fn handle_reflow_complete_msg(&self, pipeline_id: PipelineId, reflow_id: uint) {
debug!("Script: Reflow {:?} complete for {:?}", reflow_id, pipeline_id);
- let page = self.page.borrow_mut();
+ let page = self.root_page();
let page = page.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.");
- let last_reflow_id = page.last_reflow_id.get();
- if last_reflow_id == reflow_id {
- let mut layout_join_port = page.layout_join_port.borrow_mut();
- *layout_join_port = None;
- }
+ let window = page.window().root();
+ window.r().handle_reflow_complete_msg(reflow_id);
- let doc = page.frame().as_ref().unwrap().document.root();
+ let doc = page.document().root();
let html_element = doc.r().GetDocumentElement().root();
let reftest_wait = html_element.r().map_or(false, |elem| elem.has_class(&Atom::from_slice("reftest-wait")));
@@ -701,14 +804,12 @@ impl ScriptTask {
/// Window was resized, but this script was not active, so don't reflow yet
fn handle_resize_inactive_msg(&self, id: PipelineId, new_size: WindowSizeData) {
- let page = self.page.borrow_mut();
+ let page = self.root_page();
let page = page.find(id).expect("Received resize message for PipelineId not associated
with a page in the page tree. This is a bug.");
- page.window_size.set(new_size);
- match &mut *page.mut_url() {
- &mut Some((_, ref mut needs_reflow)) => *needs_reflow = true,
- &mut None => (),
- }
+ let window = page.window().root();
+ window.r().set_window_size(new_size);
+ page.set_reflow_status(true);
}
/// We have gotten a window.close from script, which we pass on to the compositor.
@@ -725,182 +826,206 @@ impl ScriptTask {
self.compositor.borrow_mut().close();
}
+ /// We have received notification that the response associated with a load has completed.
+ /// Kick off the document and frame tree creation process using the result.
+ fn handle_page_fetch_complete(&self, id: PipelineId, subpage: Option<SubpageId>,
+ response: LoadResponse) {
+ // Any notification received should refer to an existing, in-progress load that is tracked.
+ let idx = self.incomplete_loads.borrow().iter().position(|load| {
+ load.pipeline_id == id && load.subpage_id.map(|sub| sub.1) == subpage
+ }).unwrap();
+ let load = self.incomplete_loads.borrow_mut().remove(idx);
+ self.load(response, load);
+ }
+
/// Handles a request for the window title.
fn handle_get_title_msg(&self, pipeline_id: PipelineId) {
- get_page(&*self.page.borrow(), pipeline_id).send_title_to_compositor();
+ let page = get_page(&self.root_page(), pipeline_id);
+ let document = page.document().root();
+ document.r().send_title_to_compositor();
}
/// Handles a request to exit the script task and shut down layout.
/// Returns true if the script task should shut down and false otherwise.
fn handle_exit_pipeline_msg(&self, id: PipelineId, exit_type: PipelineExitType) -> bool {
// If root is being exited, shut down all pages
- let page = self.page.borrow_mut();
- if page.id == id {
+ let page = self.root_page();
+ let window = page.window().root();
+ if window.r().pipeline() == id {
debug!("shutting down layout for root page {:?}", id);
+ // To ensure the elements of the DOM tree remain usable (such as the window global),
+ // don't free the JS context until all interactions with it are finished.
+ shut_down_layout(&page, exit_type);
*self.js_context.borrow_mut() = None;
- shut_down_layout(&*page, (*self.js_runtime).ptr, exit_type);
return true
}
// otherwise find just the matching page and exit all sub-pages
- match page.find(id) {
- Some(ref mut page) => {
- shut_down_layout(&*page, (*self.js_runtime).ptr, exit_type);
- page.remove(id);
- false
- }
- // TODO(tkuehn): pipeline closing is currently duplicated across
- // script and constellation, which can cause this to happen. Constellation
- // needs to be smarter about exiting pipelines.
- None => false,
+ if let Some(ref mut child_page) = page.remove(id) {
+ shut_down_layout(&*child_page, exit_type);
}
-
+ return false;
}
/// 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(&self, pipeline_id: PipelineId,
- parent: Option<(PipelineId, SubpageId)>, load_data: LoadData) {
- let url = load_data.url.clone();
- debug!("ScriptTask: loading {} on page {:?}", url.serialize(), pipeline_id);
-
- let borrowed_page = self.page.borrow_mut();
-
- let frame_element = parent.and_then(|(parent_id, subpage_id)| {
- // In the case a parent id exists but the matching page
- // cannot be found, this means the page exists in a different
- // script task (due to origin) so it shouldn't be returned.
- // TODO: window.parent will continue to return self in that
- // case, which is wrong. We should be returning an object that
- // denies access to most properties (per
- // https://github.com/servo/servo/issues/3939#issuecomment-62287025).
- borrowed_page.find(parent_id).and_then(|page| {
- let doc = page.frame().as_ref().unwrap().document.root();
- let doc: JSRef<Node> = NodeCast::from_ref(doc.r());
-
- doc.traverse_preorder()
- .filter_map(HTMLIFrameElementCast::to_ref)
- .find(|node| node.subpage_id() == Some(subpage_id))
- .map(ElementCast::from_ref)
- .map(Temporary::from_rooted)
- })
+ fn load(&self, response: LoadResponse, incomplete: InProgressLoad) {
+ let final_url = response.metadata.final_url.clone();
+ debug!("ScriptTask: loading {} on page {:?}", incomplete.url.serialize(), incomplete.pipeline_id);
+
+ // We should either be initializing a root page or loading a child page of an
+ // existing one.
+ let root_page_exists = self.page.borrow().is_some();
+ assert!(incomplete.subpage_id.is_none() || root_page_exists);
+
+ let frame_element = incomplete.subpage_id.and_then(|(parent_id, subpage_id)| {
+ let borrowed_page = self.root_page();
+ // In the case a parent id exists but the matching page
+ // cannot be found, this means the page exists in a different
+ // script task (due to origin) so it shouldn't be returned.
+ // TODO: window.parent will continue to return self in that
+ // case, which is wrong. We should be returning an object that
+ // denies access to most properties (per
+ // https://github.com/servo/servo/issues/3939#issuecomment-62287025).
+ borrowed_page.find(parent_id).and_then(|page| {
+ let doc = page.document().root();
+ let doc: JSRef<Node> = NodeCast::from_ref(doc.r());
+
+ doc.traverse_preorder()
+ .filter_map(HTMLIFrameElementCast::to_ref)
+ .find(|node| node.subpage_id() == Some(subpage_id))
+ .map(ElementCast::from_ref)
+ .map(Temporary::from_rooted)
+ })
}).root();
- let page = borrowed_page.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.");
+ self.compositor.borrow_mut().set_ready_state(incomplete.pipeline_id, Loading);
- // Are we reloading?
- let reloading = match *page.url() {
- Some((ref loaded, _)) => *loaded == url,
- _ => false,
- };
- if reloading {
- // Pull out the `needs_reflow` flag explicitly because `reflow` can ask for the page's
- // URL, and we can't be holding a borrow on that URL (#4402).
- let needed_reflow = match &mut *page.mut_url() {
- &mut Some((_, ref mut needs_reflow)) => replace(needs_reflow, false),
- _ => panic!("can't reload a page with no URL!")
- };
- if needed_reflow {
- self.force_reflow(&*page);
- }
- return
- }
- let last_url = replace(&mut *page.mut_url(), None).map(|(last_url, _)| last_url);
-
- let is_javascript = url.scheme.as_slice() == "javascript";
-
- self.compositor.borrow_mut().set_ready_state(pipeline_id, Loading);
-
- let (mut parser_input, final_url, last_modified) = if !is_javascript {
- // Wait for the LoadResponse so that the parser knows the final URL.
- let (input_chan, input_port) = channel();
- self.resource_task.send(ControlMsg::Load(NetLoadData {
- url: url,
- method: load_data.method,
- headers: Headers::new(),
- preserved_headers: load_data.headers,
- data: load_data.data,
- cors: None,
- consumer: input_chan,
- })).unwrap();
+ let cx = self.js_context.borrow();
+ let cx = cx.as_ref().unwrap();
- let load_response = input_port.recv().unwrap();
+ // Create a new frame tree entry.
+ let page = Rc::new(Page::new(incomplete.pipeline_id, final_url.clone()));
+ if !root_page_exists {
+ // We have a new root frame tree.
+ *self.page.borrow_mut() = Some(page.clone());
+ } else if let Some((parent, _)) = incomplete.subpage_id {
+ // We have a new child frame.
+ let parent_page = self.root_page();
+ parent_page.find(parent).expect("received load for subpage with missing parent");
+ parent_page.children.borrow_mut().push(page.clone());
+ }
- let last_modified = load_response.metadata.headers.as_ref().and_then(|headers| {
- headers.get().map(|&LastModified(ref tm)| tm.clone())
- });
+ enum PageToRemove {
+ Root,
+ Child(PipelineId),
+ }
+ struct AutoPageRemover<'a> {
+ page: PageToRemove,
+ script_task: &'a ScriptTask,
+ neutered: bool,
+ }
+ impl<'a> AutoPageRemover<'a> {
+ fn new(script_task: &'a ScriptTask, page: PageToRemove) -> AutoPageRemover<'a> {
+ AutoPageRemover {
+ page: page,
+ script_task: script_task,
+ neutered: false,
+ }
+ }
- let final_url = load_response.metadata.final_url.clone();
+ fn neuter(&mut self) {
+ self.neutered = true;
+ }
+ }
+ #[unsafe_destructor]
+ impl<'a> Drop for AutoPageRemover<'a> {
+ fn drop(&mut self) {
+ if !self.neutered {
+ match self.page {
+ PageToRemove::Root => *self.script_task.page.borrow_mut() = None,
+ PageToRemove::Child(id) => {
+ let _ = self.script_task.root_page().remove(id);
+ }
+ }
+ }
+ }
+ }
- (Some(HTMLInput::InputUrl(load_response)), final_url, last_modified)
+ let page_to_remove = if !root_page_exists {
+ PageToRemove::Root
} else {
- let doc_url = last_url.unwrap_or_else(|| {
- Url::parse("about:blank").unwrap()
- });
- (None, doc_url, None)
+ PageToRemove::Child(incomplete.pipeline_id)
};
+ let mut page_remover = AutoPageRemover::new(self, page_to_remove);
- // Store the final URL before we start parsing, so that DOM routines
- // (e.g. HTMLImageElement::update_image) can resolve relative URLs
- // correctly.
- {
- *page.mut_url() = Some((final_url.clone(), true));
- }
-
- let cx = self.js_context.borrow();
- let cx = cx.as_ref().unwrap();
// Create the window and document objects.
- let window = Window::new(cx.ptr,
+ let window = Window::new(cx.clone(),
page.clone(),
self.chan.clone(),
self.control_chan.clone(),
self.compositor.borrow_mut().dup(),
- self.image_cache_task.clone()).root();
+ self.image_cache_task.clone(),
+ self.resource_task.clone(),
+ self.storage_task.clone(),
+ self.devtools_chan.clone(),
+ self.constellation_chan.clone(),
+ incomplete.layout_chan,
+ incomplete.pipeline_id,
+ incomplete.subpage_id.map(|s| s.1),
+ incomplete.window_size).root();
+
+ let last_modified: Option<DOMString> = response.metadata.headers.as_ref().and_then(|headers| {
+ headers.get().map(|&LastModified(ref tm)| dom_last_modified(tm))
+ });
- let document = Document::new(window.r(), Some(final_url.clone()),
- IsHTMLDocument::HTMLDocument, None,
+ let content_type = match response.metadata.content_type {
+ Some((ref t, ref st)) if t.as_slice().eq_ignore_ascii_case("text") &&
+ st.as_slice().eq_ignore_ascii_case("plain") => {
+ Some("text/plain".to_owned())
+ }
+ _ => None
+ };
+
+ let document = Document::new(window.r(),
+ Some(final_url.clone()),
+ IsHTMLDocument::HTMLDocument,
+ content_type,
+ last_modified,
DocumentSource::FromParser).root();
- if let Some(tm) = last_modified {
- document.r().set_last_modified(dom_last_modified(&tm));
- }
- window.r().init_browser_context(document.r(), frame_element.r());
+ window.r().init_browser_context(document.r(), frame_element.r());
- {
- // Create the root frame.
- let mut frame = page.mut_frame();
- *frame = Some(Frame {
- document: JS::from_rooted(document.r()),
- window: JS::from_rooted(window.r()),
- });
- }
+ // Create the root frame
+ page.set_frame(Some(Frame {
+ document: JS::from_rooted(document.r()),
+ window: JS::from_rooted(window.r()),
+ }));
- if is_javascript {
- let evalstr = load_data.url.non_relative_scheme_data().unwrap();
+ let is_javascript = incomplete.url.scheme.as_slice() == "javascript";
+ let parse_input = if is_javascript {
+ let evalstr = incomplete.url.non_relative_scheme_data().unwrap();
let jsval = window.r().evaluate_js_on_global_with_result(evalstr);
let strval = FromJSValConvertible::from_jsval(self.get_cx(), jsval,
StringificationBehavior::Empty);
- parser_input = Some(HTMLInput::InputString(strval.unwrap_or("".to_owned())));
+ HTMLInput::InputString(strval.unwrap_or("".to_owned()))
+ } else {
+ HTMLInput::InputUrl(response)
};
- parse_html(document.r(), parser_input.unwrap(), &final_url);
+ parse_html(document.r(), parse_input, &final_url);
document.r().set_ready_state(DocumentReadyState::Interactive);
- self.compositor.borrow_mut().set_ready_state(pipeline_id, PerformingLayout);
+ self.compositor.borrow_mut().set_ready_state(incomplete.pipeline_id, PerformingLayout);
// Kick off the initial reflow of the page.
debug!("kicking off initial reflow of {:?}", final_url);
document.r().content_changed(NodeCast::from_ref(document.r()),
NodeDamage::OtherNodeDamage);
- window.r().flush_layout(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery);
+ window.r().reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, ReflowReason::FirstLoad);
- {
- // No more reflow required
- let mut page_url = page.mut_url();
- *page_url = Some((final_url.clone(), false));
- }
+ // No more reflow required
+ page.set_reflow_status(false);
// https://html.spec.whatwg.org/multipage/#the-end step 4
let addr: Trusted<Document> = Trusted::new(self.get_cx(), document.r(), self.chan.clone());
@@ -915,7 +1040,7 @@ impl ScriptTask {
let handler = Box::new(DocumentProgressHandler::new(addr, DocumentProgressTask::Load));
self.chan.send(ScriptMsg::RunnableMsg(handler)).unwrap();
- *page.fragment_name.borrow_mut() = final_url.fragment.clone();
+ window.r().set_fragment_name(final_url.fragment.clone());
let ConstellationChan(ref chan) = self.constellation_chan;
chan.send(ConstellationMsg::LoadComplete).unwrap();
@@ -928,11 +1053,13 @@ impl ScriptTask {
title: document.r().Title(),
url: final_url
};
- chan.send(DevtoolsControlMsg::NewGlobal(pipeline_id,
+ chan.send(DevtoolsControlMsg::NewGlobal(incomplete.pipeline_id,
self.devtools_sender.clone(),
page_info)).unwrap();
}
}
+
+ page_remover.neuter();
}
fn scroll_fragment_point(&self, pipeline_id: PipelineId, node: JSRef<Element>) {
@@ -948,12 +1075,11 @@ impl ScriptTask {
}
/// Reflows non-incrementally.
- fn force_reflow(&self, page: &Page) {
- page.dirty_all_nodes();
- page.reflow(ReflowGoal::ForDisplay,
- self.control_chan.clone(),
- &mut **self.compositor.borrow_mut(),
- ReflowQueryType::NoQuery);
+ fn force_reflow(&self, page: &Page, reason: ReflowReason) {
+ let document = page.document().root();
+ document.r().dirty_all_nodes();
+ let window = window_from_node(document.r()).root();
+ window.r().reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, reason);
}
/// This is the main entry point for receiving and dispatching DOM events.
@@ -979,9 +1105,8 @@ impl ScriptTask {
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.page.borrow(), pipeline_id);
- let frame = page.frame();
- let document = frame.as_ref().unwrap().document.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);
}
@@ -990,40 +1115,65 @@ impl ScriptTask {
}
ClickEvent(_button, point) => {
- let page = get_page(&*self.page.borrow(), pipeline_id);
- let frame = page.frame();
- let document = frame.as_ref().unwrap().document.root();
+ 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);
}
MouseDownEvent(..) => {}
MouseUpEvent(..) => {}
MouseMoveEvent(point) => {
- self.handle_mouse_move_event(pipeline_id, point);
+ let page = get_page(&self.root_page(), pipeline_id);
+ let document = page.document().root();
+ let mouse_over_targets = &mut *self.mouse_over_targets.borrow_mut();
+
+ if document.r().handle_mouse_move_event(self.js_runtime.ptr, point, mouse_over_targets) {
+ self.force_reflow(&page, ReflowReason::MouseEvent)
+ }
}
KeyEvent(key, state, modifiers) => {
- let page = get_page(&*self.page.borrow(), pipeline_id);
- let frame = page.frame();
- let document = frame.as_ref().unwrap().document.root();
+ let page = get_page(&self.root_page(), pipeline_id);
+ let document = page.document().root();
document.r().dispatch_key_event(
key, state, modifiers, &mut *self.compositor.borrow_mut());
}
}
}
+ /// https://html.spec.whatwg.org/multipage/browsers.html#navigating-across-documents
/// The entry point for content to notify that a new load has been requested
- /// for the given pipeline.
- fn trigger_load(&self, pipeline_id: PipelineId, load_data: LoadData) {
- let ConstellationChan(ref const_chan) = self.constellation_chan;
- const_chan.send(ConstellationMsg::LoadUrl(pipeline_id, load_data)).unwrap();
+ /// for the given pipeline (specifically the "navigate" algorithm).
+ fn handle_navigate(&self, pipeline_id: PipelineId, subpage_id: Option<SubpageId>, load_data: LoadData) {
+ match subpage_id {
+ Some(subpage_id) => {
+ let borrowed_page = self.root_page();
+ let iframe = borrowed_page.find(pipeline_id).and_then(|page| {
+ let doc = page.document().root();
+ let doc: JSRef<Node> = NodeCast::from_ref(doc.r());
+
+ doc.traverse_preorder()
+ .filter_map(HTMLIFrameElementCast::to_ref)
+ .find(|node| node.subpage_id() == Some(subpage_id))
+ .map(Temporary::from_rooted)
+ }).root();
+ if let Some(iframe) = iframe.r() {
+ iframe.navigate_child_browsing_context(load_data.url);
+ }
+ }
+ None => {
+ let ConstellationChan(ref const_chan) = self.constellation_chan;
+ const_chan.send(ConstellationMsg::LoadUrl(pipeline_id, load_data)).unwrap();
+ }
+ }
}
/// The entry point for content to notify that a fragment url has been requested
/// for the given pipeline.
fn trigger_fragment(&self, pipeline_id: PipelineId, fragment: String) {
- let page = get_page(&*self.page.borrow(), pipeline_id);
- match page.find_fragment_node(fragment).root() {
+ let page = get_page(&self.root_page(), pipeline_id);
+ let document = page.document().root();
+ match document.r().find_fragment_node(fragment).root() {
Some(node) => {
self.scroll_fragment_point(pipeline_id, node.r());
}
@@ -1033,159 +1183,105 @@ impl ScriptTask {
fn handle_resize_event(&self, pipeline_id: PipelineId, new_size: WindowSizeData) {
- let window = {
- let page = get_page(&*self.page.borrow(), pipeline_id);
- page.window_size.set(new_size);
-
- let frame = page.frame();
- if frame.is_some() {
- self.force_reflow(&*page);
- }
-
- let fragment_node =
- page.fragment_name
- .borrow_mut()
- .take()
- .and_then(|name| page.find_fragment_node(name))
- .root();
- match fragment_node {
- Some(node) => self.scroll_fragment_point(pipeline_id, node.r()),
- None => {}
- }
+ let page = get_page(&self.root_page(), pipeline_id);
+ let window = page.window().root();
+ window.r().set_window_size(new_size);
+ self.force_reflow(&*page, ReflowReason::WindowResize);
+
+ let document = page.document().root();
+ let fragment_node = window.r().steal_fragment_name()
+ .and_then(|name| document.r().find_fragment_node(name))
+ .root();
+ match fragment_node {
+ Some(node) => self.scroll_fragment_point(pipeline_id, node.r()),
+ None => {}
+ }
- frame.as_ref().map(|frame| Temporary::new(frame.window.clone()))
- };
+ // http://dev.w3.org/csswg/cssom-view/#resizing-viewports
+ // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#event-type-resize
+ let uievent = UIEvent::new(window.r(),
+ "resize".to_owned(), EventBubbles::DoesNotBubble,
+ EventCancelable::NotCancelable, Some(window.r()),
+ 0i32).root();
+ let event: JSRef<Event> = EventCast::from_ref(uievent.r());
- match window.root() {
- Some(window) => {
- // http://dev.w3.org/csswg/cssom-view/#resizing-viewports
- // https://dvcs.w3.org/hg/dom3events/raw-file/tip/html/DOM3-Events.html#event-type-resize
- let uievent = UIEvent::new(window.r(),
- "resize".to_owned(), false,
- false, Some(window.r()),
- 0i32).root();
- let event: JSRef<Event> = EventCast::from_ref(uievent.r());
-
- let wintarget: JSRef<EventTarget> = EventTargetCast::from_ref(window.r());
- event.fire(wintarget);
- }
- None => ()
- }
+ let wintarget: JSRef<EventTarget> = EventTargetCast::from_ref(window.r());
+ event.fire(wintarget);
}
fn handle_reflow_event(&self, pipeline_id: PipelineId) {
debug!("script got reflow event");
- let page = get_page(&*self.page.borrow(), pipeline_id);
- let frame = page.frame();
- if frame.is_some() {
- self.force_reflow(&*page);
- }
+ let page = get_page(&self.root_page(), pipeline_id);
+ self.force_reflow(&*page, 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) {
+ let id = incomplete.pipeline_id.clone();
+ let subpage = incomplete.subpage_id.clone().map(|p| p.1);
- fn handle_mouse_move_event(&self, pipeline_id: PipelineId, point: Point2D<f32>) {
- let page = get_page(&*self.page.borrow(), pipeline_id);
- let mut needs_reflow = false;
-
- // Build a list of elements that are currently under the mouse.
- let mouse_over_addresses = page.get_nodes_under_mouse(&point);
- let mouse_over_targets: Vec<JS<Node>> = mouse_over_addresses.iter()
- .filter_map(|node_address| {
- let node = node::from_untrusted_node_address(self.js_runtime.ptr, *node_address);
- node.root().r().inclusive_ancestors().find(|node| node.is_element()).map(JS::from_rooted)
- }).collect();
-
- // Remove hover from any elements in the previous list that are no longer
- // under the mouse.
- let prev_mouse_over_targets = &mut *self.mouse_over_targets.borrow_mut();
- for target in prev_mouse_over_targets.iter() {
- if !mouse_over_targets.contains(target) {
- target.root().r().set_hover_state(false);
- needs_reflow = true;
- }
- }
+ let script_chan = self.chan.clone();
+ let resource_task = self.resource_task.clone();
- // Set hover state for any elements in the current mouse over list.
- // Check if any of them changed state to determine whether to
- // force a reflow below.
- for target in mouse_over_targets.iter() {
- let target = target.root();
- let target_ref = target.r();
- if !target_ref.get_hover_state() {
- target_ref.set_hover_state(true);
- needs_reflow = true;
+ spawn_named(format!("fetch for {:?}", load_data.url.serialize()), move || {
+ if load_data.url.scheme.as_slice() == "javascript" {
+ load_data.url = Url::parse("about:blank").unwrap();
}
- }
- // Send mousemove event to topmost target
- if mouse_over_addresses.len() > 0 {
- let top_most_node =
- node::from_untrusted_node_address(self.js_runtime.ptr, mouse_over_addresses[0]).root();
-
- if let Some(ref frame) = *page.frame() {
- let window = frame.window.root();
-
- let x = point.x.to_i32().unwrap_or(0);
- let y = point.y.to_i32().unwrap_or(0);
-
- let mouse_event = MouseEvent::new(window.r(),
- "mousemove".to_owned(),
- true,
- true,
- Some(window.r()),
- 0i32,
- x, y, x, y,
- false, false, false, false,
- 0i16,
- None).root();
-
- let event: JSRef<Event> = EventCast::from_ref(mouse_event.r());
- let target: JSRef<EventTarget> = EventTargetCast::from_ref(top_most_node.r());
- event.fire(target);
- }
- }
+ let (input_chan, input_port) = channel();
+ resource_task.send(ControlMsg::Load(NetLoadData {
+ url: load_data.url,
+ method: load_data.method,
+ headers: Headers::new(),
+ preserved_headers: load_data.headers,
+ data: load_data.data,
+ cors: None,
+ consumer: input_chan,
+ })).unwrap();
- // Store the current mouse over targets for next frame
- *prev_mouse_over_targets = mouse_over_targets;
+ let load_response = input_port.recv().unwrap();
+ script_chan.send(ScriptMsg::PageFetchComplete(id, subpage, load_response)).unwrap();
+ });
- // Reflow if hover state changed
- if needs_reflow {
- self.force_reflow(&*page);
- }
+ self.incomplete_loads.borrow_mut().push(incomplete);
+ }
+}
+
+impl Drop for ScriptTask {
+ fn drop(&mut self) {
+ SCRIPT_TASK_ROOT.with(|root| {
+ *root.borrow_mut() = None;
+ });
}
}
/// Shuts down layout for the given page tree.
-fn shut_down_layout(page_tree: &Rc<Page>, rt: *mut JSRuntime, exit_type: PipelineExitType) {
+fn shut_down_layout(page_tree: &Rc<Page>, exit_type: PipelineExitType) {
+ let mut channels = vec!();
+
for page in page_tree.iter() {
// Tell the layout task to begin shutting down, and wait until it
// processed this message.
let (response_chan, response_port) = channel();
- let LayoutChan(ref chan) = page.layout_chan;
+ let window = page.window().root();
+ let LayoutChan(chan) = window.r().layout_chan();
if chan.send(layout_interface::Msg::PrepareToExit(response_chan)).is_ok() {
- response_port.recv().unwrap();
+ channels.push(chan);
+ response_port.recv().unwrap();
}
}
- // Remove our references to the DOM objects in this page tree.
- for page in page_tree.iter() {
- *page.mut_frame() = None;
- }
-
- // Drop our references to the JSContext, potentially triggering a GC.
+ // Drop our references to the JSContext and DOM objects.
for page in page_tree.iter() {
- *page.mut_js_info() = None;
- }
-
- // Force a GC to make sure that our DOM reflectors are released before we tell
- // layout to exit.
- unsafe {
- JS_GC(rt);
+ let window = page.window().root();
+ window.r().clear_js_context();
+ // Sever the connection between the global and the DOM tree
+ page.set_frame(None);
}
// Destroy the layout task. If there were node leaks, layout will now crash safely.
- for page in page_tree.iter() {
- let LayoutChan(ref chan) = page.layout_chan;
+ for chan in channels.into_iter() {
chan.send(layout_interface::Msg::ExitNow(exit_type)).ok();
}
}