aboutsummaryrefslogtreecommitdiffstats
path: root/components/script
diff options
context:
space:
mode:
Diffstat (limited to 'components/script')
-rw-r--r--components/script/dom/bindings/js.rs27
-rw-r--r--components/script/dom/bindings/trace.rs6
-rw-r--r--components/script/dom/eventtarget.rs5
-rw-r--r--components/script/dom/htmltabledatacellelement.rs10
-rw-r--r--components/script/dom/htmltableelement.rs10
-rw-r--r--components/script/dom/htmltablerowelement.rs8
-rw-r--r--components/script/dom/node.rs92
-rw-r--r--components/script/dom/window.rs8
-rw-r--r--components/script/layout_interface.rs15
-rw-r--r--components/script/lib.rs2
-rw-r--r--components/script/page.rs55
-rw-r--r--components/script/script_task.rs8
12 files changed, 151 insertions, 95 deletions
diff --git a/components/script/dom/bindings/js.rs b/components/script/dom/bindings/js.rs
index 14f717f7455..2cd327397ad 100644
--- a/components/script/dom/bindings/js.rs
+++ b/components/script/dom/bindings/js.rs
@@ -53,7 +53,8 @@ use js::jsapi::JSObject;
use layout_interface::TrustedNodeAddress;
use script_task::StackRoots;
-use std::cell::{Cell, RefCell};
+use servo_util::smallvec::{SmallVec, SmallVec16};
+use std::cell::{Cell, UnsafeCell};
use std::default::Default;
use std::kinds::marker::ContravariantLifetime;
use std::mem;
@@ -419,14 +420,14 @@ impl<T: Assignable<U>, U: Reflectable> TemporaryPushable<T> for Vec<JS<U>> {
/// An opaque, LIFO rooting mechanism.
pub struct RootCollection {
- roots: RefCell<Vec<*mut JSObject>>,
+ roots: UnsafeCell<SmallVec16<*mut JSObject>>,
}
impl RootCollection {
/// Create an empty collection of roots
pub fn new() -> RootCollection {
RootCollection {
- roots: RefCell::new(vec!()),
+ roots: UnsafeCell::new(SmallVec16::new()),
}
}
@@ -438,17 +439,23 @@ impl RootCollection {
/// Track a stack-based root to ensure LIFO root ordering
fn root<'a, 'b, T: Reflectable>(&self, untracked: &Root<'a, 'b, T>) {
- let mut roots = self.roots.borrow_mut();
- roots.push(untracked.js_ptr);
- debug!(" rooting {:?}", untracked.js_ptr);
+ unsafe {
+ let roots = self.roots.get();
+ (*roots).push(untracked.js_ptr);
+ debug!(" rooting {:?}", untracked.js_ptr);
+ }
}
/// Stop tracking a stack-based root, asserting if LIFO root ordering has been violated
fn unroot<'a, 'b, T: Reflectable>(&self, rooted: &Root<'a, 'b, T>) {
- let mut roots = self.roots.borrow_mut();
- debug!("unrooting {:?} (expecting {:?}", roots.last().unwrap(), rooted.js_ptr);
- assert!(*roots.last().unwrap() == rooted.js_ptr);
- roots.pop().unwrap();
+ unsafe {
+ let roots = self.roots.get();
+ debug!("unrooting {:?} (expecting {:?}",
+ (*roots).as_slice().last().unwrap(),
+ rooted.js_ptr);
+ assert!(*(*roots).as_slice().last().unwrap() == rooted.js_ptr);
+ (*roots).pop().unwrap();
+ }
}
}
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
index 930fd59edbe..c4f59491c57 100644
--- a/components/script/dom/bindings/trace.rs
+++ b/components/script/dom/bindings/trace.rs
@@ -39,7 +39,7 @@ use msg::constellation_msg::{PipelineId, SubpageId, WindowSizeData};
use net::image_cache_task::ImageCacheTask;
use script_traits::ScriptControlChan;
use std::collections::hashmap::HashMap;
-use collections::hash::Hash;
+use collections::hash::{Hash, Hasher};
use style::PropertyDeclarationBlock;
use std::comm::{Receiver, Sender};
use string_cache::{Atom, Namespace};
@@ -170,7 +170,9 @@ impl<T: JSTraceable> JSTraceable for Option<T> {
}
}
-impl<K: Eq+Hash+JSTraceable, V: JSTraceable> JSTraceable for HashMap<K, V> {
+impl<K,V,S,H> JSTraceable for HashMap<K, V, H> where K: Eq + Hash<S> + JSTraceable,
+ V: JSTraceable,
+ H: Hasher<S> {
#[inline]
fn trace(&self, trc: *mut JSTracer) {
for e in self.iter() {
diff --git a/components/script/dom/eventtarget.rs b/components/script/dom/eventtarget.rs
index 233047691ea..0ced9c9c41e 100644
--- a/components/script/dom/eventtarget.rs
+++ b/components/script/dom/eventtarget.rs
@@ -18,6 +18,7 @@ use dom::xmlhttprequest::XMLHttpRequestId;
use dom::virtualmethods::VirtualMethods;
use js::jsapi::{JS_CompileUCFunction, JS_GetFunctionObject, JS_CloneFunctionObject};
use js::jsapi::{JSContext, JSObject};
+use servo_util::fnv::FnvHasher;
use servo_util::str::DOMString;
use libc::{c_char, size_t};
use std::ptr;
@@ -69,7 +70,7 @@ pub struct EventListenerEntry {
pub struct EventTarget {
type_id: EventTargetTypeId,
reflector_: Reflector,
- handlers: DOMRefCell<HashMap<DOMString, Vec<EventListenerEntry>>>,
+ handlers: DOMRefCell<HashMap<DOMString, Vec<EventListenerEntry>, FnvHasher>>,
}
impl EventTarget {
@@ -77,7 +78,7 @@ impl EventTarget {
EventTarget {
type_id: type_id,
reflector_: Reflector::new(),
- handlers: DOMRefCell::new(HashMap::new()),
+ handlers: DOMRefCell::new(HashMap::with_hasher(FnvHasher)),
}
}
diff --git a/components/script/dom/htmltabledatacellelement.rs b/components/script/dom/htmltabledatacellelement.rs
index 535f2e4ffd5..a2a73bd86f8 100644
--- a/components/script/dom/htmltabledatacellelement.rs
+++ b/components/script/dom/htmltabledatacellelement.rs
@@ -32,9 +32,13 @@ impl HTMLTableDataCellElement {
}
#[allow(unrooted_must_root)]
- pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> Temporary<HTMLTableDataCellElement> {
- let element = HTMLTableDataCellElement::new_inherited(localName, prefix, document);
- Node::reflect_node(box element, document, HTMLTableDataCellElementBinding::Wrap)
+ pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>)
+ -> Temporary<HTMLTableDataCellElement> {
+ Node::reflect_node(box HTMLTableDataCellElement::new_inherited(localName,
+ prefix,
+ document),
+ document,
+ HTMLTableDataCellElementBinding::Wrap)
}
}
diff --git a/components/script/dom/htmltableelement.rs b/components/script/dom/htmltableelement.rs
index b39c8ebb3e3..7dddf98bdc4 100644
--- a/components/script/dom/htmltableelement.rs
+++ b/components/script/dom/htmltableelement.rs
@@ -9,7 +9,6 @@ use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::js::{JSRef, Temporary};
use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::Document;
-use dom::element::HTMLTableCaptionElementTypeId;
use dom::element::HTMLTableElementTypeId;
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
@@ -52,11 +51,10 @@ impl<'a> HTMLTableElementMethods for JSRef<'a, HTMLTableElement> {
// http://www.whatwg.org/html/#dom-table-caption
fn GetCaption(self) -> Option<Temporary<HTMLTableCaptionElement>> {
let node: JSRef<Node> = NodeCast::from_ref(self);
- node.children().find(|child| {
- child.type_id() == ElementNodeTypeId(HTMLTableCaptionElementTypeId)
- }).map(|node| {
- Temporary::from_rooted(HTMLTableCaptionElementCast::to_ref(node).unwrap())
- })
+ node.children()
+ .filter_map::<JSRef<HTMLTableCaptionElement>>(HTMLTableCaptionElementCast::to_ref)
+ .next()
+ .map(Temporary::from_rooted)
}
// http://www.whatwg.org/html/#dom-table-caption
diff --git a/components/script/dom/htmltablerowelement.rs b/components/script/dom/htmltablerowelement.rs
index 64bd1e66c11..3edf4a0e64f 100644
--- a/components/script/dom/htmltablerowelement.rs
+++ b/components/script/dom/htmltablerowelement.rs
@@ -32,9 +32,11 @@ impl HTMLTableRowElement {
}
#[allow(unrooted_must_root)]
- pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> Temporary<HTMLTableRowElement> {
- let element = HTMLTableRowElement::new_inherited(localName, prefix, document);
- Node::reflect_node(box element, document, HTMLTableRowElementBinding::Wrap)
+ pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>)
+ -> Temporary<HTMLTableRowElement> {
+ Node::reflect_node(box HTMLTableRowElement::new_inherited(localName, prefix, document),
+ document,
+ HTMLTableRowElementBinding::Wrap)
}
}
diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs
index af1afccc3b1..938404bdc37 100644
--- a/components/script/dom/node.rs
+++ b/components/script/dom/node.rs
@@ -45,8 +45,7 @@ use dom::text::Text;
use dom::virtualmethods::{VirtualMethods, vtable_for};
use dom::window::Window;
use geom::rect::Rect;
-use layout_interface::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC,
- LayoutChan, ReapLayoutDataMsg};
+use layout_interface::{LayoutChan, ReapLayoutDataMsg};
use devtools_traits::NodeInfo;
use script_traits::UntrustedNodeAddress;
use servo_util::geometry::Au;
@@ -621,6 +620,10 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> {
// 1. Dirty self.
self.set_has_changed(true);
+ if self.get_is_dirty() {
+ return
+ }
+
// 2. Dirty descendants.
fn dirty_subtree(node: JSRef<Node>) {
// Stop if this subtree is already dirty.
@@ -689,20 +692,11 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> {
}
fn get_bounding_content_box(self) -> Rect<Au> {
- let window = window_from_node(self).root();
- let page = window.page();
- let addr = self.to_trusted_node_address();
-
- let ContentBoxResponse(rect) = page.layout().content_box(addr);
- rect
+ window_from_node(self).root().page().content_box_query(self.to_trusted_node_address())
}
fn get_content_boxes(self) -> Vec<Rect<Au>> {
- let window = window_from_node(self).root();
- let page = window.page();
- let addr = self.to_trusted_node_address();
- let ContentBoxesResponse(rects) = page.layout().content_boxes(addr);
- rects
+ window_from_node(self).root().page().content_boxes_query(self.to_trusted_node_address())
}
// http://dom.spec.whatwg.org/#dom-parentnode-queryselector
@@ -1151,7 +1145,7 @@ impl Node {
layout_data: LayoutDataRef::new(),
- unique_id: DOMRefCell::new("".to_string()),
+ unique_id: DOMRefCell::new(String::new()),
}
}
@@ -1337,29 +1331,8 @@ impl Node {
parent: JSRef<Node>,
child: Option<JSRef<Node>>,
suppress_observers: SuppressObserver) {
- // XXX assert owner_doc
- // Step 1-3: ranges.
- // Step 4.
- let mut nodes = match node.type_id() {
- DocumentFragmentNodeTypeId => node.children().collect(),
- _ => vec!(node.clone()),
- };
-
- // Step 5: DocumentFragment, mutation records.
- // Step 6: DocumentFragment.
- match node.type_id() {
- DocumentFragmentNodeTypeId => {
- for c in node.children() {
- Node::remove(c, node, Suppressed);
- }
- },
- _ => (),
- }
-
- // Step 7: mutation records.
- // Step 8.
- for node in nodes.iter_mut() {
- parent.add_child(*node, child);
+ fn do_insert(node: JSRef<Node>, parent: JSRef<Node>, child: Option<JSRef<Node>>) {
+ parent.add_child(node, child);
let is_in_doc = parent.is_in_doc();
for kid in node.traverse_preorder() {
let mut flags = kid.flags.get();
@@ -1372,14 +1345,47 @@ impl Node {
}
}
- // Step 9.
- match suppress_observers {
- Unsuppressed => {
- for node in nodes.iter() {
- node.node_inserted();
+ fn fire_observer_if_necessary(node: JSRef<Node>, suppress_observers: SuppressObserver) {
+ match suppress_observers {
+ Unsuppressed => node.node_inserted(),
+ Suppressed => ()
+ }
+ }
+
+ // XXX assert owner_doc
+ // Step 1-3: ranges.
+
+ match node.type_id() {
+ DocumentFragmentNodeTypeId => {
+ // Step 4.
+ // Step 5: DocumentFragment, mutation records.
+ // Step 6: DocumentFragment.
+ let mut kids = Vec::new();
+ for kid in node.children() {
+ kids.push(kid.clone());
+ Node::remove(kid, node, Suppressed);
}
+
+ // Step 7: mutation records.
+ // Step 8.
+ for kid in kids.iter() {
+ do_insert((*kid).clone(), parent, child);
+ }
+
+ for kid in kids.into_iter() {
+ fire_observer_if_necessary(kid, suppress_observers);
+ }
+ }
+ _ => {
+ // Step 4.
+ // Step 5: DocumentFragment, mutation records.
+ // Step 6: DocumentFragment.
+ // Step 7: mutation records.
+ // Step 8.
+ do_insert(node, parent, child);
+ // Step 9.
+ fire_observer_if_necessary(node, suppress_observers);
}
- Suppressed => ()
}
}
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index 71c8890ab6f..c9eeedc2a2a 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -19,7 +19,7 @@ use dom::location::Location;
use dom::navigator::Navigator;
use dom::performance::Performance;
use dom::screen::Screen;
-use layout_interface::ReflowGoal;
+use layout_interface::NoQuery;
use page::Page;
use script_task::{ExitWindowMsg, ScriptChan, TriggerLoadMsg, TriggerFragmentMsg};
use script_task::FromWindow;
@@ -312,7 +312,7 @@ impl Reflectable for Window {
pub trait WindowHelpers {
fn reflow(self);
- fn flush_layout(self, goal: ReflowGoal);
+ fn flush_layout(self);
fn wait_until_safe_to_modify_dom(self);
fn init_browser_context(self, doc: JSRef<Document>);
fn load_url(self, href: DOMString);
@@ -350,8 +350,8 @@ impl<'a> WindowHelpers for JSRef<'a, Window> {
self.page().damage();
}
- fn flush_layout(self, goal: ReflowGoal) {
- self.page().flush_layout(goal);
+ fn flush_layout(self) {
+ self.page().flush_layout(NoQuery);
}
fn wait_until_safe_to_modify_dom(self) {
diff --git a/components/script/layout_interface.rs b/components/script/layout_interface.rs
index 735893f497a..2cf64f961fb 100644
--- a/components/script/layout_interface.rs
+++ b/components/script/layout_interface.rs
@@ -60,9 +60,9 @@ pub enum Msg {
// 3) and really needs to be fast.
pub trait LayoutRPC {
/// Requests the dimensions of the content box, as in the `getBoundingClientRect()` call.
- fn content_box(&self, node: TrustedNodeAddress) -> ContentBoxResponse;
+ fn content_box(&self) -> ContentBoxResponse;
/// Requests the dimensions of all the content boxes, as in the `getClientRects()` call.
- fn content_boxes(&self, node: TrustedNodeAddress) -> ContentBoxesResponse;
+ fn content_boxes(&self) -> ContentBoxesResponse;
/// Requests the node containing the point of interest
fn hit_test(&self, node: TrustedNodeAddress, point: Point2D<f32>) -> Result<HitTestResponse, ()>;
fn mouse_over(&self, node: TrustedNodeAddress, point: Point2D<f32>) -> Result<MouseOverResponse, ()>;
@@ -82,6 +82,13 @@ pub enum ReflowGoal {
ReflowForScriptQuery,
}
+/// Any query to perform with this reflow.
+pub enum ReflowQueryType {
+ NoQuery,
+ ContentBoxQuery(TrustedNodeAddress),
+ ContentBoxesQuery(TrustedNodeAddress),
+}
+
/// Information needed for a reflow.
pub struct Reflow {
/// The document node.
@@ -99,7 +106,9 @@ pub struct Reflow {
/// The channel that we send a notification to.
pub script_join_chan: Sender<()>,
/// Unique identifier
- pub id: uint
+ pub id: uint,
+ /// The type of query if any to perform during this reflow.
+ pub query_type: ReflowQueryType,
}
/// Encapsulates a channel to the layout task.
diff --git a/components/script/lib.rs b/components/script/lib.rs
index 1624a8c2653..cbacf516a3f 100644
--- a/components/script/lib.rs
+++ b/components/script/lib.rs
@@ -5,7 +5,7 @@
#![comment = "The Servo Parallel Browser Project"]
#![license = "MPL"]
-#![feature(globs, macro_rules, struct_variant, phase, unsafe_destructor)]
+#![feature(default_type_params, globs, macro_rules, struct_variant, phase, unsafe_destructor)]
#![deny(unused_imports, unused_variable)]
#![allow(non_snake_case)]
diff --git a/components/script/page.rs b/components/script/page.rs
index 9fc7d74d2e9..01b396126ad 100644
--- a/components/script/page.rs
+++ b/components/script/page.rs
@@ -11,19 +11,22 @@ use dom::document::{Document, DocumentHelpers};
use dom::element::Element;
use dom::node::{Node, NodeHelpers};
use dom::window::Window;
-use layout_interface::{ReflowForDisplay};
-use layout_interface::{HitTestResponse, MouseOverResponse};
-use layout_interface::{GetRPCMsg, LayoutChan, LayoutRPC};
-use layout_interface::{Reflow, ReflowGoal, ReflowMsg};
+use layout_interface::{
+ ContentBoxQuery, ContentBoxResponse, ContentBoxesQuery, ContentBoxesResponse,
+ GetRPCMsg, HitTestResponse, LayoutChan, LayoutRPC, MouseOverResponse, NoQuery,
+ Reflow, ReflowForDisplay, ReflowForScriptQuery, ReflowGoal, ReflowMsg,
+ ReflowQueryType, TrustedNodeAddress
+};
use script_traits::{UntrustedNodeAddress, ScriptControlChan};
-use geom::point::Point2D;
+use geom::{Point2D, Rect};
use js::rust::Cx;
use servo_msg::compositor_msg::PerformingLayout;
use servo_msg::compositor_msg::ScriptListener;
use servo_msg::constellation_msg::{ConstellationChan, WindowSizeData};
use servo_msg::constellation_msg::{PipelineId, SubpageId};
use servo_net::resource_task::ResourceTask;
+use servo_util::geometry::Au;
use servo_util::str::DOMString;
use servo_util::smallvec::{SmallVec1, SmallVec};
use std::cell::Cell;
@@ -164,26 +167,48 @@ impl Page {
}
}
- pub fn flush_layout(&self, goal: ReflowGoal) {
- if self.damaged.get() {
+ pub fn flush_layout(&self, query: ReflowQueryType) {
+ // If we are damaged, we need to force a full reflow, so that queries interact with
+ // an accurate flow tree.
+ let (reflow_goal, force_reflow) = if self.damaged.get() {
+ (ReflowForDisplay, true)
+ } else {
+ match query {
+ ContentBoxQuery(_) | ContentBoxesQuery(_) => (ReflowForScriptQuery, true),
+ NoQuery => (ReflowForDisplay, false),
+ }
+ };
+
+ if force_reflow {
let frame = self.frame();
let window = frame.as_ref().unwrap().window.root();
- self.reflow(goal, window.control_chan().clone(), window.compositor());
+ self.reflow(reflow_goal, window.control_chan().clone(), window.compositor(), query);
} else {
self.avoided_reflows.set(self.avoided_reflows.get() + 1);
}
}
- pub fn layout(&self) -> &LayoutRPC {
- // FIXME This should probably be ReflowForQuery, not Display. All queries currently
- // currently rely on the display list, which means we can't destroy it by
- // doing a query reflow.
- self.flush_layout(ReflowForDisplay);
+ pub fn layout(&self) -> &LayoutRPC {
+ self.flush_layout(NoQuery);
self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough?
let layout_rpc: &LayoutRPC = &*self.layout_rpc;
layout_rpc
}
+ pub fn content_box_query(&self, content_box_request: TrustedNodeAddress) -> Rect<Au> {
+ self.flush_layout(ContentBoxQuery(content_box_request));
+ self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough?
+ let ContentBoxResponse(rect) = self.layout_rpc.content_box();
+ rect
+ }
+
+ pub fn content_boxes_query(&self, content_boxes_request: TrustedNodeAddress) -> Vec<Rect<Au>> {
+ self.flush_layout(ContentBoxesQuery(content_boxes_request));
+ self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough?
+ let ContentBoxesResponse(rects) = self.layout_rpc.content_boxes();
+ rects
+ }
+
// must handle root case separately
pub fn remove(&self, id: PipelineId) -> Option<Rc<Page>> {
let remove_idx = {
@@ -303,7 +328,8 @@ impl Page {
pub fn reflow(&self,
goal: ReflowGoal,
script_chan: ScriptControlChan,
- compositor: &ScriptListener) {
+ compositor: &ScriptListener,
+ query_type: ReflowQueryType) {
let root = match *self.frame() {
None => return,
@@ -349,6 +375,7 @@ impl Page {
script_chan: script_chan,
script_join_chan: join_chan,
id: last_reflow_id.get(),
+ query_type: query_type,
};
let LayoutChan(ref chan) = self.layout_chan;
diff --git a/components/script/script_task.rs b/components/script/script_task.rs
index deb6aaded80..7ef0d095545 100644
--- a/components/script/script_task.rs
+++ b/components/script/script_task.rs
@@ -29,7 +29,7 @@ use dom::window::{Window, WindowHelpers};
use dom::worker::{Worker, TrustedWorkerAddress};
use dom::xmlhttprequest::{TrustedXHRAddress, XMLHttpRequest, XHRProgress};
use parse::html::{InputString, InputUrl, parse_html};
-use layout_interface::{ScriptLayoutChan, LayoutChan, ReflowForDisplay};
+use layout_interface::{ScriptLayoutChan, LayoutChan, NoQuery, ReflowForDisplay};
use layout_interface;
use page::{Page, IterablePage, Frame};
use timers::TimerId;
@@ -815,7 +815,7 @@ impl ScriptTask {
let document_as_node = NodeCast::from_ref(document_js_ref);
document.content_changed(document_as_node);
}
- window.flush_layout(ReflowForDisplay);
+ window.flush_layout();
{
// No more reflow required
@@ -870,7 +870,7 @@ impl ScriptTask {
}
page.damage();
- page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor);
+ page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor, NoQuery);
}
/// This is the main entry point for receiving and dispatching DOM events.
@@ -969,7 +969,7 @@ impl ScriptTask {
let eventtarget: JSRef<EventTarget> = EventTargetCast::from_ref(node);
let _ = eventtarget.dispatch_event_with_target(None, *event);
- window.flush_layout(ReflowForDisplay);
+ window.flush_layout();
}
None => {}
}