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.rs199
1 files changed, 147 insertions, 52 deletions
diff --git a/components/script/script_task.rs b/components/script/script_task.rs
index 3b17c49e45c..2db2ef24a80 100644
--- a/components/script/script_task.rs
+++ b/components/script/script_task.rs
@@ -17,7 +17,7 @@
//! a page runs its course and the script task returns to processing events in the main event
//! loop.
-#![allow(unsafe_blocks)]
+#![allow(unsafe_code)]
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyState};
@@ -25,10 +25,10 @@ use dom::bindings::codegen::InheritTypes::{ElementCast, EventTargetCast, HTMLIFr
use dom::bindings::conversions::FromJSValConvertible;
use dom::bindings::conversions::StringificationBehavior;
use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootable, RootedReference};
-use dom::bindings::js::{RootCollection, RootCollectionPtr};
+use dom::bindings::js::{RootCollection, RootCollectionPtr, Unrooted};
use dom::bindings::refcounted::{LiveDOMReferences, Trusted, TrustedReference};
use dom::bindings::structuredclone::StructuredCloneData;
-use dom::bindings::trace::JSTraceable;
+use dom::bindings::trace::{JSTraceable, trace_collections};
use dom::bindings::utils::{wrap_for_same_compartment, pre_wrap};
use dom::document::{Document, IsHTMLDocument, DocumentHelpers, DocumentProgressHandler, DocumentProgressTask, DocumentSource};
use dom::element::{Element, AttributeHandlers};
@@ -57,8 +57,8 @@ use script_traits::ScriptTaskFactory;
use msg::compositor_msg::ReadyState::{FinishedLoading, Loading, PerformingLayout};
use msg::compositor_msg::{LayerId, ScriptListener};
use msg::constellation_msg::{ConstellationChan};
-use msg::constellation_msg::{LoadData, PipelineId, SubpageId};
-use msg::constellation_msg::{Failure, Msg, WindowSizeData, PipelineExitType};
+use msg::constellation_msg::{LoadData, PipelineId, SubpageId, MozBrowserEvent, WorkerId};
+use msg::constellation_msg::{Failure, WindowSizeData, PipelineExitType};
use msg::constellation_msg::Msg as ConstellationMsg;
use net::image_cache_task::ImageCacheTask;
use net::resource_task::{ResourceTask, ControlMsg, LoadResponse};
@@ -116,7 +116,7 @@ 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)>,
+ parent_info: Option<(PipelineId, SubpageId)>,
/// The current window size associated with this pipeline.
window_size: Option<WindowSizeData>,
/// Channel to the layout task associated with this pipeline.
@@ -130,13 +130,13 @@ struct InProgressLoad {
impl InProgressLoad {
/// Create a new InProgressLoad object.
fn new(id: PipelineId,
- subpage_id: Option<(PipelineId, SubpageId)>,
+ parent_info: Option<(PipelineId, SubpageId)>,
layout_chan: LayoutChan,
window_size: Option<WindowSizeData>,
url: Url) -> InProgressLoad {
InProgressLoad {
pipeline_id: id,
- subpage_id: subpage_id,
+ parent_info: parent_info,
layout_chan: layout_chan,
window_size: window_size,
clip_rect: None,
@@ -155,6 +155,10 @@ pub trait Runnable {
fn handler(self: Box<Self>);
}
+pub trait MainThreadRunnable {
+ fn handler(self: Box<Self>, script_task: &ScriptTask);
+}
+
/// Messages used to control script event loops, such as ScriptTask and
/// DedicatedWorkerGlobalScope.
pub enum ScriptMsg {
@@ -176,6 +180,8 @@ pub enum ScriptMsg {
DOMMessage(StructuredCloneData),
/// Generic message that encapsulates event handling.
RunnableMsg(Box<Runnable+Send>),
+ /// Generic message for running tasks in the ScriptTask
+ MainThreadRunnableMsg(Box<MainThreadRunnable+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.
@@ -303,14 +309,15 @@ impl<'a> ScriptMemoryFailsafe<'a> {
#[unsafe_destructor]
impl<'a> Drop for ScriptMemoryFailsafe<'a> {
+ #[allow(unrooted_must_root)]
fn drop(&mut self) {
match self.owner {
Some(owner) => {
unsafe {
let page = owner.page.borrow_for_script_deallocation();
for page in page.iter() {
- let window = page.window().root();
- window.r().clear_js_context_for_script_deallocation();
+ let window = Unrooted::from_temporary(page.window());
+ (*window.unsafe_get()).clear_js_context_for_script_deallocation();
}
*owner.js_context.borrow_for_script_deallocation() = None;
}
@@ -332,6 +339,7 @@ impl ScriptTaskFactory for ScriptTask {
fn create<C>(_phantom: Option<&mut ScriptTask>,
id: PipelineId,
+ parent_info: Option<(PipelineId, SubpageId)>,
compositor: C,
layout_chan: &OpaqueScriptLayoutChannel,
control_chan: ScriptControlChan,
@@ -365,7 +373,7 @@ impl ScriptTaskFactory for ScriptTask {
});
let mut failsafe = ScriptMemoryFailsafe::new(&script_task);
- let new_load = InProgressLoad::new(id, None, layout_chan, window_size,
+ let new_load = InProgressLoad::new(id, parent_info, layout_chan, window_size,
load_data.url.clone());
script_task.start_page_load(new_load, load_data);
@@ -453,6 +461,10 @@ impl ScriptTask {
!ptr.is_null()
});
+
+ unsafe {
+ JS_SetExtraGCRootsTracer((*js_runtime).ptr, Some(trace_collections), ptr::null_mut());
+ }
// Unconstrain the runtime's threshold on nominal heap size, to avoid
// triggering GC too often if operating continuously near an arbitrary
// finite threshold. This leaves the maximum-JS_malloc-bytes threshold
@@ -566,13 +578,15 @@ impl ScriptTask {
}
};
- // Squash any pending resize and reflow events in the queue.
+ // Squash any pending resize, reflow, and mouse-move events in the queue.
+ let mut mouse_move_event_index = None;
loop {
match event {
// This has to be handled before the ResizeMsg below,
// otherwise the page may not have been added to the
// child list yet, causing the find() to fail.
- MixedMessage::FromConstellation(ConstellationControlMsg::AttachLayout(new_layout_info)) => {
+ MixedMessage::FromConstellation(ConstellationControlMsg::AttachLayout(
+ new_layout_info)) => {
self.handle_new_layout(new_layout_info);
}
MixedMessage::FromConstellation(ConstellationControlMsg::Resize(id, size)) => {
@@ -581,6 +595,19 @@ impl ScriptTask {
MixedMessage::FromConstellation(ConstellationControlMsg::Viewport(id, rect)) => {
self.handle_viewport(id, rect);
}
+ MixedMessage::FromConstellation(ConstellationControlMsg::SendEvent(
+ _,
+ MouseMoveEvent(_))) => {
+ match mouse_move_event_index {
+ None => {
+ mouse_move_event_index = Some(sequential.len());
+ sequential.push(event);
+ }
+ Some(index) => {
+ sequential[index] = event
+ }
+ }
+ }
_ => {
sequential.push(event);
}
@@ -641,7 +668,17 @@ impl ScriptTask {
ConstellationControlMsg::Freeze(pipeline_id) =>
self.handle_freeze_msg(pipeline_id),
ConstellationControlMsg::Thaw(pipeline_id) =>
- self.handle_thaw_msg(pipeline_id)
+ self.handle_thaw_msg(pipeline_id),
+ ConstellationControlMsg::MozBrowserEventMsg(parent_pipeline_id,
+ subpage_id,
+ event) =>
+ self.handle_mozbrowser_event_msg(parent_pipeline_id,
+ subpage_id,
+ event),
+ ConstellationControlMsg::UpdateSubpageId(containing_pipeline_id,
+ old_subpage_id,
+ new_subpage_id) =>
+ self.handle_update_subpage_id(containing_pipeline_id, old_subpage_id, new_subpage_id),
}
}
@@ -661,6 +698,8 @@ impl ScriptTask {
panic!("unexpected message"),
ScriptMsg::RunnableMsg(runnable) =>
runnable.handler(),
+ ScriptMsg::MainThreadRunnableMsg(runnable) =>
+ runnable.handler(self),
ScriptMsg::RefcountCleanup(addr) =>
LiveDOMReferences::cleanup(self.get_cx(), addr),
ScriptMsg::PageFetchComplete(id, subpage, response) =>
@@ -783,8 +822,50 @@ impl ScriptTask {
window.r().thaw();
}
+ /// Handles a mozbrowser event, for example see:
+ /// https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowserloadstart
+ fn handle_mozbrowser_event_msg(&self,
+ parent_pipeline_id: PipelineId,
+ subpage_id: SubpageId,
+ event: MozBrowserEvent) {
+ let borrowed_page = self.root_page();
+
+ let frame_element = borrowed_page.find(parent_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(frame_element) = frame_element {
+ frame_element.r().dispatch_mozbrowser_event(event);
+ }
+ }
+
+ fn handle_update_subpage_id(&self,
+ containing_pipeline_id: PipelineId,
+ old_subpage_id: SubpageId,
+ new_subpage_id: SubpageId) {
+ let borrowed_page = self.root_page();
+
+ let frame_element = borrowed_page.find(containing_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(old_subpage_id))
+ .map(Temporary::from_rooted)
+ }).root();
+
+ frame_element.unwrap().r().update_subpage_id(new_subpage_id);
+ }
+
/// Handles a notification that reflow completed.
- fn handle_reflow_complete_msg(&self, pipeline_id: PipelineId, reflow_id: uint) {
+ fn handle_reflow_complete_msg(&self, pipeline_id: PipelineId, reflow_id: u32) {
debug!("Script: Reflow {:?} complete for {:?}", reflow_id, pipeline_id);
let page = self.root_page();
let page = page.find(pipeline_id).expect(
@@ -831,8 +912,8 @@ impl ScriptTask {
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
+ let idx = self.incomplete_loads.borrow().iter().position(|load| {
+ load.pipeline_id == id && load.parent_info.map(|info| info.1) == subpage
}).unwrap();
let load = self.incomplete_loads.borrow_mut().remove(idx);
self.load(response, load);
@@ -876,27 +957,31 @@ impl ScriptTask {
// 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)
- })
+ let frame_element = incomplete.parent_info.and_then(|(parent_id, subpage_id)| {
+ // The root page may not exist yet, if the parent of this frame
+ // exists in a different script task.
+ let borrowed_page = self.page.borrow();
+
+ // 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.as_ref().and_then(|borrowed_page| {
+ 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();
self.compositor.borrow_mut().set_ready_state(incomplete.pipeline_id, Loading);
@@ -909,9 +994,11 @@ impl ScriptTask {
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 {
+ } else if let Some((parent, _)) = incomplete.parent_info {
// We have a new child frame.
let parent_page = self.root_page();
+ // TODO(gw): This find will fail when we are sharing script tasks
+ // between cross origin iframes in the same TLD.
parent_page.find(parent).expect("received load for subpage with missing parent");
parent_page.children.borrow_mut().push(page.clone());
}
@@ -972,7 +1059,7 @@ impl ScriptTask {
self.constellation_chan.clone(),
incomplete.layout_chan,
incomplete.pipeline_id,
- incomplete.subpage_id.map(|s| s.1),
+ incomplete.parent_info,
incomplete.window_size).root();
let last_modified: Option<DOMString> = response.metadata.headers.as_ref().and_then(|headers| {
@@ -1002,7 +1089,7 @@ impl ScriptTask {
window: JS::from_rooted(window.r()),
}));
- let is_javascript = incomplete.url.scheme.as_slice() == "javascript";
+ let is_javascript = incomplete.url.scheme == "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);
@@ -1013,7 +1100,7 @@ impl ScriptTask {
HTMLInput::InputUrl(response)
};
- parse_html(document.r(), parse_input, &final_url);
+ parse_html(document.r(), parse_input, &final_url, None);
document.r().set_ready_state(DocumentReadyState::Interactive);
self.compositor.borrow_mut().set_ready_state(incomplete.pipeline_id, PerformingLayout);
@@ -1029,7 +1116,7 @@ impl ScriptTask {
// https://html.spec.whatwg.org/multipage/#the-end step 4
let addr: Trusted<Document> = Trusted::new(self.get_cx(), document.r(), self.chan.clone());
- let handler = Box::new(DocumentProgressHandler::new(addr.clone(), DocumentProgressTask::DOMContentLoaded));
+ let handler = box DocumentProgressHandler::new(addr.clone(), DocumentProgressTask::DOMContentLoaded);
self.chan.send(ScriptMsg::RunnableMsg(handler)).unwrap();
// We have no concept of a document loader right now, so just dispatch the
@@ -1037,7 +1124,7 @@ impl ScriptTask {
// the initial load.
// https://html.spec.whatwg.org/multipage/#the-end step 7
- let handler = Box::new(DocumentProgressHandler::new(addr, DocumentProgressTask::Load));
+ let handler = box DocumentProgressHandler::new(addr, DocumentProgressTask::Load);
self.chan.send(ScriptMsg::RunnableMsg(handler)).unwrap();
window.r().set_fragment_name(final_url.fragment.clone());
@@ -1046,20 +1133,24 @@ impl ScriptTask {
chan.send(ConstellationMsg::LoadComplete).unwrap();
// Notify devtools that a new script global exists.
+ self.notify_devtools(document.r().Title(), final_url, (incomplete.pipeline_id, None));
+
+ page_remover.neuter();
+ }
+
+ fn notify_devtools(&self, title: DOMString, url: Url, ids: (PipelineId, Option<WorkerId>)) {
match self.devtools_chan {
None => {}
Some(ref chan) => {
let page_info = DevtoolsPageInfo {
- title: document.r().Title(),
- url: final_url
+ title: title,
+ url: url,
};
- chan.send(DevtoolsControlMsg::NewGlobal(incomplete.pipeline_id,
+ chan.send(DevtoolsControlMsg::NewGlobal(ids,
self.devtools_sender.clone(),
page_info)).unwrap();
}
}
-
- page_remover.neuter();
}
fn scroll_fragment_point(&self, pipeline_id: PipelineId, node: JSRef<Element>) {
@@ -1212,20 +1303,24 @@ impl ScriptTask {
fn handle_reflow_event(&self, pipeline_id: PipelineId) {
debug!("script got reflow event");
let page = get_page(&self.root_page(), pipeline_id);
- self.force_reflow(&*page, ReflowReason::ReceivedReflowEvent);
+ 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) {
let id = incomplete.pipeline_id.clone();
- let subpage = incomplete.subpage_id.clone().map(|p| p.1);
+ let subpage = incomplete.parent_info.clone().map(|p| p.1);
let script_chan = self.chan.clone();
let resource_task = self.resource_task.clone();
spawn_named(format!("fetch for {:?}", load_data.url.serialize()), move || {
- if load_data.url.scheme.as_slice() == "javascript" {
+ if load_data.url.scheme == "javascript" {
load_data.url = Url::parse("about:blank").unwrap();
}
@@ -1294,5 +1389,5 @@ pub fn get_page(page: &Rc<Page>, pipeline_id: PipelineId) -> Rc<Page> {
}
fn dom_last_modified(tm: &Tm) -> String {
- format!("{}", tm.to_local().strftime("%m/%d/%Y %H:%M:%S").unwrap())
+ tm.to_local().strftime("%m/%d/%Y %H:%M:%S").unwrap().to_string()
}