mirror of
https://github.com/servo/servo.git
synced 2025-06-25 17:44:33 +01:00
Implement JSManaged for DOM objects.
This commit is contained in:
parent
061269f963
commit
625325434b
137 changed files with 3644 additions and 2778 deletions
|
@ -6,14 +6,16 @@
|
|||
//! and layout tasks.
|
||||
|
||||
use dom::bindings::codegen::RegisterBindings;
|
||||
use dom::bindings::utils::{Reflectable, GlobalStaticData};
|
||||
use dom::document::AbstractDocument;
|
||||
use dom::bindings::codegen::InheritTypes::{EventTargetCast, NodeCast, DocumentCast, ElementCast};
|
||||
use dom::bindings::js::JS;
|
||||
use dom::bindings::utils::{Reflectable, GlobalStaticData, with_gc_enabled};
|
||||
use dom::document::Document;
|
||||
use dom::element::Element;
|
||||
use dom::event::{Event_, ResizeEvent, ReflowEvent, ClickEvent, MouseDownEvent, MouseMoveEvent, MouseUpEvent};
|
||||
use dom::event::Event;
|
||||
use dom::eventtarget::AbstractEventTarget;
|
||||
use dom::eventtarget::EventTarget;
|
||||
use dom::htmldocument::HTMLDocument;
|
||||
use dom::node::AbstractNode;
|
||||
use dom::node::{Node, NodeHelpers};
|
||||
use dom::window::{TimerData, TimerHandle, Window};
|
||||
use html::hubbub_html_parser::HtmlParserResult;
|
||||
use html::hubbub_html_parser::{HtmlDiscoveredStyle, HtmlDiscoveredIFrame, HtmlDiscoveredScript};
|
||||
|
@ -32,7 +34,7 @@ use geom::size::Size2D;
|
|||
use js::JSVAL_NULL;
|
||||
use js::global::debug_fns;
|
||||
use js::glue::RUST_JSVAL_TO_OBJECT;
|
||||
use js::jsapi::{JSContext, JSObject};
|
||||
use js::jsapi::{JSContext, JSObject, JS_InhibitGC, JS_AllowGC};
|
||||
use js::jsapi::{JS_CallFunctionValue, JS_GetContextPrivate};
|
||||
use js::rust::{Compartment, Cx};
|
||||
use js;
|
||||
|
@ -52,6 +54,8 @@ use std::ptr;
|
|||
use std::task;
|
||||
use std::util::replace;
|
||||
|
||||
use extra::serialize::{Encoder, Encodable};
|
||||
|
||||
/// Messages used to control the script task.
|
||||
pub enum ScriptMsg {
|
||||
/// Loads a new URL on the specified pipeline.
|
||||
|
@ -86,6 +90,11 @@ pub struct NewLayoutInfo {
|
|||
#[deriving(Clone)]
|
||||
pub struct ScriptChan(SharedChan<ScriptMsg>);
|
||||
|
||||
impl<S: Encoder> Encodable<S> for ScriptChan {
|
||||
fn encode(&self, _s: &mut S) {
|
||||
}
|
||||
}
|
||||
|
||||
impl ScriptChan {
|
||||
/// Creates a new script chan.
|
||||
pub fn new() -> (Port<ScriptMsg>, ScriptChan) {
|
||||
|
@ -131,7 +140,13 @@ pub struct Page {
|
|||
resize_event: Option<Size2D<uint>>,
|
||||
|
||||
/// Pending scroll to fragment event, if any
|
||||
fragment_node: Option<AbstractNode>
|
||||
fragment_node: Option<JS<Element>>
|
||||
}
|
||||
|
||||
impl<S: Encoder> Encodable<S> for Page {
|
||||
fn encode(&self, s: &mut S) {
|
||||
self.fragment_node.encode(s);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PageTree {
|
||||
|
@ -223,23 +238,24 @@ impl Page {
|
|||
pub fn damage(&mut self, level: DocumentDamageLevel) {
|
||||
let root = match self.frame {
|
||||
None => return,
|
||||
Some(ref frame) => frame.document.document().GetDocumentElement()
|
||||
Some(ref frame) => frame.document.get().GetDocumentElement()
|
||||
};
|
||||
match root {
|
||||
None => {},
|
||||
Some(root) => {
|
||||
let root: JS<Node> = NodeCast::from(&root);
|
||||
match self.damage {
|
||||
None => {}
|
||||
Some(ref mut damage) => {
|
||||
// FIXME(pcwalton): This is wrong. We should trace up to the nearest ancestor.
|
||||
damage.root = root;
|
||||
damage.root = root.to_trusted_node_address();
|
||||
damage.level.add(level);
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
self.damage = Some(DocumentDamage {
|
||||
root: root,
|
||||
root: root.to_trusted_node_address(),
|
||||
level: level,
|
||||
})
|
||||
}
|
||||
|
@ -292,7 +308,7 @@ impl Page {
|
|||
let root = match self.frame {
|
||||
None => return,
|
||||
Some(ref frame) => {
|
||||
frame.document.document().GetDocumentElement()
|
||||
frame.document.get().GetDocumentElement()
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -314,8 +330,9 @@ impl Page {
|
|||
self.last_reflow_id += 1;
|
||||
|
||||
// Send new document and relevant styles to layout.
|
||||
let root: JS<Node> = NodeCast::from(&root);
|
||||
let reflow = ~Reflow {
|
||||
document_root: root,
|
||||
document_root: root.to_trusted_node_address(),
|
||||
url: self.url.get_ref().first().clone(),
|
||||
goal: goal,
|
||||
window_size: self.window_size,
|
||||
|
@ -333,6 +350,8 @@ impl Page {
|
|||
}
|
||||
|
||||
pub fn initialize_js_info(&mut self, js_context: @Cx, global: *JSObject) {
|
||||
assert!(global.is_not_null());
|
||||
|
||||
// Note that the order that these variables are initialized is _not_ arbitrary. Switching
|
||||
// them around can -- and likely will -- lead to things breaking.
|
||||
|
||||
|
@ -352,6 +371,8 @@ impl Page {
|
|||
|
||||
unsafe {
|
||||
js_context.set_cx_private(page_ptr as *());
|
||||
|
||||
JS_InhibitGC(js_context.ptr);
|
||||
}
|
||||
|
||||
self.js_info = Some(JSPageInfo {
|
||||
|
@ -363,11 +384,12 @@ impl Page {
|
|||
}
|
||||
|
||||
/// Information for one frame in the browsing context.
|
||||
#[deriving(Encodable)]
|
||||
pub struct Frame {
|
||||
/// The document for this frame.
|
||||
document: AbstractDocument,
|
||||
document: JS<Document>,
|
||||
/// The window object for this frame.
|
||||
window: @mut Window,
|
||||
window: JS<Window>,
|
||||
}
|
||||
|
||||
/// Encapsulation of the javascript information associated with each frame.
|
||||
|
@ -406,7 +428,7 @@ pub struct ScriptTask {
|
|||
/// The JavaScript runtime.
|
||||
js_runtime: js::rust::rt,
|
||||
|
||||
mouse_over_targets:Option<~[AbstractNode]>
|
||||
mouse_over_targets: Option<~[JS<Node>]>
|
||||
}
|
||||
|
||||
/// Returns the relevant page from the associated JS Context.
|
||||
|
@ -565,28 +587,27 @@ impl ScriptTask {
|
|||
fn handle_fire_timer_msg(&mut self, id: PipelineId, timer_data: ~TimerData) {
|
||||
let page = self.page_tree.find(id).expect("ScriptTask: received fire timer msg for a
|
||||
pipeline ID not associated with this script task. This is a bug.").page;
|
||||
let window = page.frame.expect("ScriptTask: Expect a timeout to have a document").window;
|
||||
if !window.active_timers.contains(&TimerHandle { handle: timer_data.handle, cancel_chan: None }) {
|
||||
let mut window = page.frame.get_ref().window.clone();
|
||||
if !window.get().active_timers.contains(&TimerHandle { handle: timer_data.handle, cancel_chan: None }) {
|
||||
return;
|
||||
}
|
||||
window.active_timers.remove(&TimerHandle { handle: timer_data.handle, cancel_chan: None });
|
||||
unsafe {
|
||||
let this_value = if timer_data.args.len() > 0 {
|
||||
window.get_mut().active_timers.remove(&TimerHandle { handle: timer_data.handle, cancel_chan: None });
|
||||
let this_value = if timer_data.args.len() > 0 {
|
||||
unsafe {
|
||||
RUST_JSVAL_TO_OBJECT(timer_data.args[0])
|
||||
} else {
|
||||
page.js_info.get_ref().js_compartment.global_obj.ptr
|
||||
};
|
||||
}
|
||||
} else {
|
||||
page.js_info.get_ref().js_compartment.global_obj.ptr
|
||||
};
|
||||
|
||||
// TODO: Support extra arguments. This requires passing a `*JSVal` array as `argv`.
|
||||
let rval = JSVAL_NULL;
|
||||
JS_CallFunctionValue(page.js_info.get_ref().js_context.ptr,
|
||||
this_value,
|
||||
timer_data.funval,
|
||||
0,
|
||||
ptr::null(),
|
||||
&rval);
|
||||
|
||||
}
|
||||
// TODO: Support extra arguments. This requires passing a `*JSVal` array as `argv`.
|
||||
let rval = JSVAL_NULL;
|
||||
let cx = page.js_info.get_ref().js_context.ptr;
|
||||
with_gc_enabled(cx, || {
|
||||
unsafe {
|
||||
JS_CallFunctionValue(cx, this_value, timer_data.funval, 0, ptr::null(), &rval);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Handles a notification that reflow completed.
|
||||
|
@ -697,9 +718,9 @@ impl ScriptTask {
|
|||
// Parse HTML.
|
||||
//
|
||||
// Note: We can parse the next document in parallel with any previous documents.
|
||||
let document = HTMLDocument::new(window, Some(url.clone()));
|
||||
let mut document = DocumentCast::from(&HTMLDocument::new(&window, Some(url.clone())));
|
||||
let html_parsing_result = hubbub_html_parser::parse_html(cx.ptr,
|
||||
document,
|
||||
&mut document,
|
||||
url.clone(),
|
||||
self.resource_task.clone(),
|
||||
self.image_cache_task.clone(),
|
||||
|
@ -711,8 +732,8 @@ impl ScriptTask {
|
|||
|
||||
// Create the root frame.
|
||||
page.frame = Some(Frame {
|
||||
document: document,
|
||||
window: window,
|
||||
document: document.clone(),
|
||||
window: window.clone(),
|
||||
});
|
||||
|
||||
// Send style sheets over to layout.
|
||||
|
@ -747,7 +768,7 @@ impl ScriptTask {
|
|||
}
|
||||
|
||||
// Kick off the initial reflow of the page.
|
||||
document.document().content_changed();
|
||||
document.get().content_changed();
|
||||
|
||||
let fragment = url.fragment.as_ref().map(|ref fragment| fragment.to_owned());
|
||||
|
||||
|
@ -766,47 +787,50 @@ impl ScriptTask {
|
|||
|
||||
// Evaluate every script in the document.
|
||||
for file in js_scripts.iter() {
|
||||
let _ = cx.evaluate_script(compartment.global_obj,
|
||||
file.data.clone(),
|
||||
file.url.to_str(),
|
||||
1);
|
||||
with_gc_enabled(cx.ptr, || {
|
||||
cx.evaluate_script(compartment.global_obj,
|
||||
file.data.clone(),
|
||||
file.url.to_str(),
|
||||
1);
|
||||
});
|
||||
}
|
||||
|
||||
// We have no concept of a document loader right now, so just dispatch the
|
||||
// "load" event as soon as we've finished executing all scripts parsed during
|
||||
// the initial load.
|
||||
let event = Event::new(window);
|
||||
event.mut_event().InitEvent(~"load", false, false);
|
||||
let doctarget = AbstractEventTarget::from_document(document);
|
||||
let wintarget = AbstractEventTarget::from_window(window);
|
||||
window.eventtarget.dispatch_event_with_target(wintarget, Some(doctarget), event);
|
||||
let mut event = Event::new(&window);
|
||||
event.get_mut().InitEvent(~"load", false, false);
|
||||
let doctarget = EventTargetCast::from(&document);
|
||||
let mut wintarget: JS<EventTarget> = EventTargetCast::from(&window);
|
||||
let winclone = wintarget.clone();
|
||||
wintarget.get_mut().dispatch_event_with_target(&winclone, Some(doctarget), &mut event);
|
||||
|
||||
page.fragment_node = fragment.map_default(None, |fragid| self.find_fragment_node(page, fragid));
|
||||
|
||||
self.constellation_chan.send(LoadCompleteMsg(page.id, url));
|
||||
}
|
||||
|
||||
fn find_fragment_node(&self, page: &mut Page, fragid: ~str) -> Option<AbstractNode> {
|
||||
let document = page.frame.expect("root frame is None").document;
|
||||
match document.document().GetElementById(fragid.to_owned()) {
|
||||
fn find_fragment_node(&self, page: &mut Page, fragid: ~str) -> Option<JS<Element>> {
|
||||
let document = page.frame.get_ref().document.clone();
|
||||
match document.get().GetElementById(fragid.to_owned()) {
|
||||
Some(node) => Some(node),
|
||||
None => {
|
||||
let doc_node = AbstractNode::from_document(document);
|
||||
let doc_node: JS<Node> = NodeCast::from(&document);
|
||||
let mut anchors = doc_node.traverse_preorder().filter(|node| node.is_anchor_element());
|
||||
anchors.find(|node| {
|
||||
node.with_imm_element(|elem| {
|
||||
elem.get_attribute(Null, "name").map_default(false, |attr| {
|
||||
attr.value_ref() == fragid
|
||||
})
|
||||
let elem: JS<Element> = ElementCast::to(node);
|
||||
elem.get().get_attribute(Null, "name").map_default(false, |attr| {
|
||||
attr.get().value_ref() == fragid
|
||||
})
|
||||
})
|
||||
}).map(|node| ElementCast::to(&node))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn scroll_fragment_point(&self, pipeline_id: PipelineId, page: &mut Page, node: AbstractNode) {
|
||||
fn scroll_fragment_point(&self, pipeline_id: PipelineId, page: &mut Page, node: JS<Element>) {
|
||||
let (port, chan) = Chan::new();
|
||||
match page.query_layout(ContentBoxQuery(node, chan), port) {
|
||||
let node: JS<Node> = NodeCast::from(&node);
|
||||
match page.query_layout(ContentBoxQuery(node.to_trusted_node_address(), chan), port) {
|
||||
ContentBoxResponse(rect) => {
|
||||
let point = Point2D(to_frac_px(rect.origin.x).to_f32().unwrap(),
|
||||
to_frac_px(rect.origin.y).to_f32().unwrap());
|
||||
|
@ -852,18 +876,19 @@ impl ScriptTask {
|
|||
ClickEvent(_button, point) => {
|
||||
debug!("ClickEvent: clicked at {:?}", point);
|
||||
|
||||
let document = page.frame.expect("root frame is None").document;
|
||||
let root = document.document().GetDocumentElement();
|
||||
let document = page.frame.get_ref().document.clone();
|
||||
let root = document.get().GetDocumentElement();
|
||||
if root.is_none() {
|
||||
return;
|
||||
}
|
||||
let (port, chan) = Chan::new();
|
||||
match page.query_layout(HitTestQuery(root.unwrap(), point, chan), port) {
|
||||
let root: JS<Node> = NodeCast::from(&root.unwrap());
|
||||
match page.query_layout(HitTestQuery(root.to_trusted_node_address(), point, chan), port) {
|
||||
Ok(HitTestResponse(node_address)) => {
|
||||
debug!("node address is {:?}", node_address);
|
||||
let mut node = AbstractNode::from_untrusted_node_address(self.js_runtime
|
||||
.ptr,
|
||||
node_address);
|
||||
let mut node: JS<Node> =
|
||||
NodeHelpers::from_untrusted_node_address(self.js_runtime.ptr,
|
||||
node_address);
|
||||
debug!("clicked on {:s}", node.debug_str());
|
||||
|
||||
// Traverse node generations until a node that is an element is
|
||||
|
@ -876,11 +901,10 @@ impl ScriptTask {
|
|||
}
|
||||
|
||||
if node.is_element() {
|
||||
node.with_imm_element(|element| {
|
||||
if "a" == element.tag_name {
|
||||
self.load_url_from_element(page, element)
|
||||
}
|
||||
})
|
||||
let element: JS<Element> = ElementCast::to(&node);
|
||||
if "a" == element.get().tag_name {
|
||||
self.load_url_from_element(page, element.get())
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(()) => debug!("layout query error"),
|
||||
|
@ -889,21 +913,22 @@ impl ScriptTask {
|
|||
MouseDownEvent(..) => {}
|
||||
MouseUpEvent(..) => {}
|
||||
MouseMoveEvent(point) => {
|
||||
let document = page.frame.expect("root frame is None").document;
|
||||
let root = document.document().GetDocumentElement();
|
||||
let document = page.frame.get_ref().document.clone();
|
||||
let root = document.get().GetDocumentElement();
|
||||
if root.is_none() {
|
||||
return;
|
||||
}
|
||||
let root: JS<Node> = NodeCast::from(&root.unwrap());
|
||||
let (port, chan) = Chan::new();
|
||||
match page.query_layout(MouseOverQuery(root.unwrap(), point, chan), port) {
|
||||
match page.query_layout(MouseOverQuery(root.to_trusted_node_address(), point, chan), port) {
|
||||
Ok(MouseOverResponse(node_address)) => {
|
||||
|
||||
let mut target_list:~[AbstractNode] = ~[];
|
||||
let mut target_list: ~[JS<Node>] = ~[];
|
||||
let mut target_compare = false;
|
||||
|
||||
match self.mouse_over_targets {
|
||||
Some(ref mut mouse_over_targets) => {
|
||||
for node in mouse_over_targets.iter() {
|
||||
for node in mouse_over_targets.mut_iter() {
|
||||
node.set_hover_state(false);
|
||||
}
|
||||
}
|
||||
|
@ -911,9 +936,9 @@ impl ScriptTask {
|
|||
}
|
||||
|
||||
for node_address in node_address.iter() {
|
||||
let mut node = AbstractNode::from_untrusted_node_address(self.js_runtime
|
||||
.ptr,
|
||||
*node_address);
|
||||
let mut node: JS<Node> =
|
||||
NodeHelpers::from_untrusted_node_address(
|
||||
self.js_runtime.ptr, *node_address);
|
||||
// Traverse node generations until a node that is an element is
|
||||
// found.
|
||||
while !node.is_element() {
|
||||
|
@ -964,13 +989,13 @@ impl ScriptTask {
|
|||
// if the node's element is "a," load url from href attr
|
||||
let attr = element.get_attribute(Null, "href");
|
||||
for href in attr.iter() {
|
||||
debug!("ScriptTask: clicked on link to {:s}", href.Value());
|
||||
let click_frag = href.value_ref().starts_with("#");
|
||||
debug!("ScriptTask: clicked on link to {:s}", href.get().Value());
|
||||
let click_frag = href.get().value_ref().starts_with("#");
|
||||
let base_url = page.url.as_ref().map(|&(ref url, _)| {
|
||||
url.clone()
|
||||
});
|
||||
debug!("ScriptTask: current url is {:?}", base_url);
|
||||
let url = parse_url(href.value_ref(), base_url);
|
||||
let url = parse_url(href.get().value_ref(), base_url);
|
||||
|
||||
if click_frag {
|
||||
match self.find_fragment_node(page, url.fragment.unwrap()) {
|
||||
|
@ -995,6 +1020,11 @@ fn shut_down_layout(page: @mut Page) {
|
|||
|
||||
// Destroy all nodes. Setting frame and js_info to None will trigger our
|
||||
// compartment to shutdown, run GC, etc.
|
||||
|
||||
unsafe {
|
||||
JS_AllowGC(page.js_info.get_ref().js_context.ptr);
|
||||
}
|
||||
|
||||
page.frame = None;
|
||||
page.js_info = None;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue