diff options
author | bors-servo <release+servo@mozilla.com> | 2013-09-16 21:09:49 -0700 |
---|---|---|
committer | bors-servo <release+servo@mozilla.com> | 2013-09-16 21:09:49 -0700 |
commit | a67fda0f462a9956396ea19d057bb7637047c7b2 (patch) | |
tree | 700dc678e0e8ff5cc4bdb7cd22f471f280df4a59 /src | |
parent | 77a09b2003f000537c4f87f50fb45b6e2fd6eb75 (diff) | |
parent | 89ed9580dea72d23dd6aa285204b575b5ab709be (diff) | |
download | servo-a67fda0f462a9956396ea19d057bb7637047c7b2.tar.gz servo-a67fda0f462a9956396ea19d057bb7637047c7b2.zip |
auto merge of #947 : jdm/servo/textcontent-setter2, r=metajack
Diffstat (limited to 'src')
-rw-r--r-- | src/components/main/layout/layout_task.rs | 2 | ||||
-rw-r--r-- | src/components/script/dom/bindings/codegen/Bindings.conf | 2 | ||||
-rw-r--r-- | src/components/script/dom/bindings/codegen/CodegenRust.py | 3 | ||||
-rw-r--r-- | src/components/script/dom/document.rs | 18 | ||||
-rw-r--r-- | src/components/script/dom/node.rs | 72 | ||||
-rw-r--r-- | src/components/script/dom/window.rs | 6 | ||||
-rw-r--r-- | src/components/script/layout_interface.rs | 2 | ||||
-rw-r--r-- | src/components/script/script_task.rs | 26 | ||||
-rw-r--r-- | src/test/html/content/test_textcontent.html | 17 |
9 files changed, 124 insertions, 24 deletions
diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs index 3f497fcb458..c46c2080819 100644 --- a/src/components/main/layout/layout_task.rs +++ b/src/components/main/layout/layout_task.rs @@ -360,7 +360,7 @@ impl LayoutTask { // FIXME(pcwalton): This should probably be *one* channel, but we can't fix this without // either select or a filtered recv() that only looks for messages of a given type. data.script_join_chan.send(()); - data.script_chan.send(ReflowCompleteMsg(self.id)); + data.script_chan.send(ReflowCompleteMsg(self.id, data.id)); } /// Handles a query from the script task. This is the main routine that DOM functions like diff --git a/src/components/script/dom/bindings/codegen/Bindings.conf b/src/components/script/dom/bindings/codegen/Bindings.conf index 4942b59b132..9d55b8e6038 100644 --- a/src/components/script/dom/bindings/codegen/Bindings.conf +++ b/src/components/script/dom/bindings/codegen/Bindings.conf @@ -293,7 +293,7 @@ DOMInterfaces = { 'nativeType': 'AbstractNode<ScriptView>', 'concreteType': 'Node<ScriptView>', 'pointerType': '', - 'needsAbstract': ['appendChild', 'removeChild'] + 'needsAbstract': ['appendChild', 'removeChild', 'textContent'] }, 'NodeList': [ diff --git a/src/components/script/dom/bindings/codegen/CodegenRust.py b/src/components/script/dom/bindings/codegen/CodegenRust.py index 1e433b25608..8c3e650b01a 100644 --- a/src/components/script/dom/bindings/codegen/CodegenRust.py +++ b/src/components/script/dom/bindings/codegen/CodegenRust.py @@ -4020,7 +4020,8 @@ def finalizeHook(descriptor, hookName, context): assert descriptor.nativeIsISupports release = """let val = JS_GetReservedSlot(obj, 0); let _: @mut %s = cast::transmute(RUST_JSVAL_TO_PRIVATE(val)); -""" % descriptor.concreteType +debug!("%s finalize: %%p", this); +""" % (descriptor.concreteType, descriptor.concreteType) #return clearWrapper + release return release diff --git a/src/components/script/dom/document.rs b/src/components/script/dom/document.rs index d84f2427f8d..137ba6cf1e9 100644 --- a/src/components/script/dom/document.rs +++ b/src/components/script/dom/document.rs @@ -331,10 +331,7 @@ impl Document { for title_child in child.children() { child.remove_child(title_child); } - let new_text = unsafe { - Node::as_abstract_node(cx, @Text::new(title.to_str())) - }; - child.add_child(new_text); + child.add_child(self.CreateTextNode(title)); break; } if !has_title { @@ -344,10 +341,7 @@ impl Document { let new_title = unsafe { Node::as_abstract_node(cx, new_title) }; - let new_text = unsafe { - Node::as_abstract_node(cx, @Text::new(title.to_str())) - }; - new_title.add_child(new_text); + new_title.add_child(self.CreateTextNode(title)); node.add_child(new_title); } break; @@ -443,7 +437,7 @@ impl Document { self.createHTMLCollection(|elem| elem.get_attr("name").is_some() && eq_slice(elem.get_attr("name").unwrap(), name.to_str())) } - + pub fn createHTMLCollection(&self, callback: &fn(elem: &Element) -> bool) -> @mut HTMLCollection { let mut elements = ~[]; let _ = for child in self.root.traverse_preorder() { @@ -464,6 +458,12 @@ impl Document { window.content_changed() } } + + pub fn wait_until_safe_to_modify_dom(&self) { + for window in self.window.iter() { + window.wait_until_safe_to_modify_dom(); + } + } } impl Traceable for Document { diff --git a/src/components/script/dom/node.rs b/src/components/script/dom/node.rs index bb3e0948f5f..289d571ea86 100644 --- a/src/components/script/dom/node.rs +++ b/src/components/script/dom/node.rs @@ -562,11 +562,11 @@ impl Node<ScriptView> { pub fn SetNodeValue(&mut self, _val: &DOMString, _rv: &mut ErrorResult) { } - pub fn GetTextContent(&self) -> DOMString { + pub fn GetTextContent(&self, abstract_self: AbstractNode<ScriptView>) -> DOMString { match self.type_id { ElementNodeTypeId(*) => { let mut content = ~""; - for node in self.abstract.unwrap().traverse_preorder() { + for node in abstract_self.traverse_preorder() { if node.is_text() { do node.with_imm_text() |text| { let s = text.parent.Data(); @@ -577,7 +577,7 @@ impl Node<ScriptView> { str(content) } CommentNodeTypeId | TextNodeTypeId => { - do self.abstract.unwrap().with_imm_characterdata() |characterdata| { + do abstract_self.with_imm_characterdata() |characterdata| { characterdata.Data() } } @@ -587,13 +587,73 @@ impl Node<ScriptView> { } } - pub fn SetTextContent(&mut self, _val: &DOMString, _rv: &mut ErrorResult) { + // http://dom.spec.whatwg.org/#concept-node-replace-all + pub fn replace_all(&mut self, + abstract_self: AbstractNode<ScriptView>, + node: Option<AbstractNode<ScriptView>>) { + //FIXME: We should batch document notifications that occur here + let mut rv = Ok(()); + for child in abstract_self.children() { + self.RemoveChild(abstract_self, child, &mut rv); + } + match node { + None => {}, + Some(node) => { + self.AppendChild(abstract_self, node, &mut rv); + } + } + } + + pub fn SetTextContent(&mut self, + abstract_self: AbstractNode<ScriptView>, + value: &DOMString, + _rv: &mut ErrorResult) { + let is_empty = match value { + &str(~"") | &null_string => true, + _ => false + }; + match self.type_id { + ElementNodeTypeId(*) => { + let node = if is_empty { + None + } else { + let text_node = do self.owner_doc.unwrap().with_base |document| { + document.CreateTextNode(value) + }; + Some(text_node) + }; + self.replace_all(abstract_self, node); + } + CommentNodeTypeId | TextNodeTypeId => { + self.wait_until_safe_to_modify_dom(); + + do abstract_self.with_mut_characterdata() |characterdata| { + characterdata.data = value.to_str(); + + // Notify the document that the content of this node is different + for doc in self.owner_doc.iter() { + do doc.with_base |doc| { + doc.content_changed(); + } + } + } + } + DoctypeNodeTypeId => {} + } } pub fn InsertBefore(&mut self, _node: AbstractNode<ScriptView>, _child: Option<AbstractNode<ScriptView>>, _rv: &mut ErrorResult) -> AbstractNode<ScriptView> { fail!("stub") } + fn wait_until_safe_to_modify_dom(&self) { + for doc in self.owner_doc.iter() { + do doc.with_base |doc| { + doc.wait_until_safe_to_modify_dom(); + } + } + } + pub fn AppendChild(&mut self, abstract_self: AbstractNode<ScriptView>, node: AbstractNode<ScriptView>, @@ -626,6 +686,8 @@ impl Node<ScriptView> { // TODO: Should we handle WRONG_DOCUMENT_ERR here? if rv.is_ok() { + self.wait_until_safe_to_modify_dom(); + // If the node already exists it is removed from current parent node. node.parent_node().map(|parent| parent.remove_child(node)); abstract_self.add_child(node); @@ -659,6 +721,8 @@ impl Node<ScriptView> { *rv = Err(NotFound); } if rv.is_ok() { + self.wait_until_safe_to_modify_dom(); + abstract_self.remove_child(node); self.remove_from_doc(); } diff --git a/src/components/script/dom/window.rs b/src/components/script/dom/window.rs index 5c27aa83d00..ec2db1bf4ac 100644 --- a/src/components/script/dom/window.rs +++ b/src/components/script/dom/window.rs @@ -173,6 +173,12 @@ impl Window { self.page.reflow_all(ReflowForDisplay, self.script_chan.clone(), self.compositor); } + pub fn wait_until_safe_to_modify_dom(&self) { + // FIXME: This disables concurrent layout while we are modifying the DOM, since + // our current architecture is entirely unsafe in the presence of races. + self.page.join_layout(); + } + #[fixed_stack_segment] pub fn new(cx: *JSContext, page: @mut Page, diff --git a/src/components/script/layout_interface.rs b/src/components/script/layout_interface.rs index 89336494959..47f9c1ac362 100644 --- a/src/components/script/layout_interface.rs +++ b/src/components/script/layout_interface.rs @@ -105,6 +105,8 @@ pub struct Reflow { window_size: Size2D<uint>, /// The channel that we send a notification to. script_join_chan: Chan<()>, + /// Unique identifier + id: uint } /// Encapsulates a channel to the layout task. diff --git a/src/components/script/script_task.rs b/src/components/script/script_task.rs index 149fa893c28..8335ccb5936 100644 --- a/src/components/script/script_task.rs +++ b/src/components/script/script_task.rs @@ -68,7 +68,7 @@ pub enum ScriptMsg { /// Fires a JavaScript timeout. FireTimerMsg(PipelineId, ~TimerData), /// Notifies script that reflow is finished. - ReflowCompleteMsg(PipelineId), + ReflowCompleteMsg(PipelineId, uint), /// Notifies script that window has been resized but to not take immediate action. ResizeInactiveMsg(PipelineId, Size2D<uint>), /// Exits the constellation. @@ -106,6 +106,9 @@ pub struct Page { /// Pipeline id associated with this page. id: PipelineId, + /// Unique id for last reflow request; used for confirming completion reply. + last_reflow_id: uint, + /// The outermost frame containing the document, window, and page URL. frame: Option<Frame>, @@ -158,6 +161,7 @@ impl PageTree { url: None, next_subpage_id: SubpageId(0), resize_event: None, + last_reflow_id: 0 }, inner: ~[], } @@ -216,7 +220,7 @@ impl Page { /// Sends a ping to layout and waits for the response. The response will arrive when the /// layout task has finished any pending request messages. - fn join_layout(&mut self) { + pub fn join_layout(&mut self) { if self.layout_join_port.is_some() { let join_port = replace(&mut self.layout_join_port, None); match join_port { @@ -263,6 +267,8 @@ impl Page { let (join_port, join_chan) = comm::stream(); self.layout_join_port = Some(join_port); + self.last_reflow_id += 1; + match self.frame { None => fail!(~"Tried to relayout with no root frame!"), Some(ref frame) => { @@ -275,6 +281,7 @@ impl Page { script_chan: script_chan, script_join_chan: join_chan, damage: replace(&mut self.damage, None).unwrap(), + id: self.last_reflow_id, }; self.layout_chan.send(ReflowMsg(reflow)) @@ -498,7 +505,7 @@ impl ScriptTask { SendEventMsg(id, event) => self.handle_event(id, event), FireTimerMsg(id, timer_data) => self.handle_fire_timer_msg(id, timer_data), NavigateMsg(direction) => self.handle_navigate_msg(direction), - ReflowCompleteMsg(id) => self.handle_reflow_complete_msg(id), + ReflowCompleteMsg(id, reflow_id) => self.handle_reflow_complete_msg(id, reflow_id), ResizeInactiveMsg(id, new_size) => self.handle_resize_inactive_msg(id, new_size), ExitMsg => { self.handle_exit_msg(); @@ -576,11 +583,14 @@ impl ScriptTask { } /// Handles a notification that reflow completed. - fn handle_reflow_complete_msg(&mut self, pipeline_id: PipelineId) { - debug!("Script: Reflow complete for %?", pipeline_id); - self.page_tree.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.").page.layout_join_port = None; + fn handle_reflow_complete_msg(&mut self, pipeline_id: PipelineId, reflow_id: uint) { + debug!("Script: Reflow %? complete for %?", reflow_id, pipeline_id); + let page_tree = self.page_tree.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."); + if page_tree.page.last_reflow_id == reflow_id { + page_tree.page.layout_join_port = None; + } self.constellation_chan.send(RendererReadyMsg(pipeline_id)); self.compositor.set_ready_state(FinishedLoading); } diff --git a/src/test/html/content/test_textcontent.html b/src/test/html/content/test_textcontent.html new file mode 100644 index 00000000000..b56c05d48a9 --- /dev/null +++ b/src/test/html/content/test_textcontent.html @@ -0,0 +1,17 @@ +<html> +<head> +<script src="harness.js"></script> +<script> + var div = document.getElementsByTagName('div')[0]; + is(div.textContent, "this is\n text content"); + var newContent = "new text con\ntent"; + div.textContent = newContent; + is(div.textContent, newContent); + finish(); +</script> +</head> +<body> +<div>this is + text content</div> +</body> +</html> |