diff --git a/components/script/dom/servohtmlparser.rs b/components/script/dom/servohtmlparser.rs
index c4d594186c82..9fe69afb0f05 100644
--- a/components/script/dom/servohtmlparser.rs
+++ b/components/script/dom/servohtmlparser.rs
@@ -15,7 +15,8 @@ use dom::document::Document;
use parse::html::JSMessage;
use std::default::Default;
-use std::cell::RefCell;
+use std::cell::{Cell, UnsafeCell};
+use std::kinds::marker;
use url::Url;
use js::jsapi::JSTracer;
use html5ever::tokenizer;
@@ -32,13 +33,73 @@ pub struct Sink {
pub type Tokenizer = tokenizer::Tokenizer>;
+/// Like `RefCell`, but lets us break the rules when we need to,
+/// for garbage collector integration. See rust-lang/rust#18131.
+pub struct TokenizerCell {
+ value: UnsafeCell,
+ borrowed: Cell,
+ nocopy: marker::NoCopy,
+ nosync: marker::NoSync,
+}
+
+pub struct TokenizerRefMut<'a> {
+ _parent: &'a TokenizerCell,
+}
+
+impl TokenizerCell {
+ fn new(value: Tokenizer) -> TokenizerCell {
+ TokenizerCell {
+ value: UnsafeCell::new(value),
+ borrowed: Cell::new(false),
+ nocopy: marker::NoCopy,
+ nosync: marker::NoSync,
+ }
+ }
+
+ pub fn borrow_mut<'a>(&'a self) -> TokenizerRefMut<'a> {
+ if self.borrowed.get() {
+ fail!("TokenizerCell already borrowed!");
+ }
+ self.borrowed.set(true);
+ TokenizerRefMut {
+ _parent: self,
+ }
+ }
+}
+
+#[unsafe_destructor] // because we have lifetime parameters
+impl<'a> Drop for TokenizerRefMut<'a> {
+ fn drop(&mut self) {
+ debug_assert!(self._parent.borrowed.get());
+ self._parent.borrowed.set(false);
+ }
+}
+
+impl<'b> Deref for TokenizerRefMut<'b> {
+ #[inline]
+ fn deref<'a>(&'a self) -> &'a Tokenizer {
+ unsafe {
+ &*self._parent.value.get()
+ }
+ }
+}
+
+impl<'b> DerefMut for TokenizerRefMut<'b> {
+ #[inline]
+ fn deref_mut<'a>(&'a mut self) -> &'a mut Tokenizer {
+ unsafe {
+ &mut *self._parent.value.get()
+ }
+ }
+}
+
// NB: JSTraceable is *not* auto-derived.
// You must edit the impl below if you add fields!
#[must_root]
#[privatize]
pub struct ServoHTMLParser {
reflector_: Reflector,
- tokenizer: RefCell,
+ tokenizer: TokenizerCell,
}
impl ServoHTMLParser {
@@ -61,14 +122,14 @@ impl ServoHTMLParser {
let parser = ServoHTMLParser {
reflector_: Reflector::new(),
- tokenizer: RefCell::new(tok),
+ tokenizer: TokenizerCell::new(tok),
};
reflect_dom_object(box parser, &global::Window(*window), ServoHTMLParserBinding::Wrap)
}
#[inline]
- pub fn tokenizer<'a>(&'a self) -> &'a RefCell {
+ pub fn tokenizer<'a>(&'a self) -> &'a TokenizerCell {
&self.tokenizer
}
}
@@ -91,15 +152,23 @@ impl tree_builder::Tracer for Tracer {
impl JSTraceable for ServoHTMLParser {
fn trace(&self, trc: *mut JSTracer) {
+ self.reflector_.trace(trc);
+
let tracer = Tracer {
trc: trc,
};
let tracer = &tracer as &tree_builder::Tracer;
- self.reflector_.trace(trc);
- let tokenizer = self.tokenizer.borrow();
- let tree_builder = tokenizer.sink();
- tree_builder.trace_handles(tracer);
- tree_builder.sink().trace(trc);
+ unsafe {
+ // There might be an active TokenizerRefMut, especially if parsing
+ // triggered a garbage collection. It's safe to ignore the borrow
+ // flag, because that mutable reference won't be used along the
+ // code paths reachable from tracing.
+
+ let tokenizer: &Tokenizer = &*self.tokenizer.value.get();
+ let tree_builder = tokenizer.sink();
+ tree_builder.trace_handles(tracer);
+ tree_builder.sink().trace(trc);
+ }
}
}