-
-
Save jdm/9900569 to your computer and use it in GitHub Desktop.
struct Node { | |
shared_ptr<Node> parent; | |
shared_ptr<Node> first_child; | |
TextNode* as_text_node() { assert(/*sensible check*/); return (TextNode*)this; } | |
virtual Element* as_element() { return NULL; } | |
}; | |
struct TextNode: public Node { | |
}; | |
struct Element: public Node { | |
vector<string, string> attrs; | |
void set_attribute(const string& key, const string& value); | |
virtual void before_set_attr(const string& key, const string& value); | |
virtual void after_set_attr(const string& key, const string& value); | |
virtual Element* as_element() { return this; } | |
}; | |
void Element::set_attribute(const string& key, const string& value) | |
{ | |
before_set_attr(key, value); | |
//...update attrs... | |
after_set_attr(key, value); | |
} | |
struct HTMLImageElement: public Element { | |
virtual void before_set_attr(const string& key, const string& value); | |
}; | |
void HTMLImageElement::before_set_attr(const string& key, const string& value) | |
{ | |
if (key == "src") { | |
//..remove cached image with url |value|... | |
} | |
Element::before_set_attr(key, value); | |
} | |
struct HTMLVideoElement: public Element { | |
bool cross_origin; | |
void after_set_attr(const string& key, const string& value); | |
}; | |
void HTMLVideoElement::after_set_attr(const string& key, const string& value) | |
{ | |
if (key == "crossOrigin") { | |
cross_origin = value == "true"; | |
} | |
Element::after_set_attr(key, value); | |
} | |
void process_any_element(Element* element) { | |
//... | |
} | |
shared_ptr<HTMLVideoElement> videoElement = ...; | |
process_any_element(videoElement); | |
shared_ptr<Node> node = videoElement->first_child; | |
shared_ptr<Element> element = node->as_element(); | |
if (!element) { | |
shared_ptr<TextNode> text = node->as_text_node(); | |
} |
The &
vs JS
pointer thing should mostly just work (TM) with any system, it is a goal of Rust to support smart pointer use like that and its a goal of our work on DST and variance. I guess the only thing to be aware of is if we only do virtual dispatch on pointer-to-T, then this should work for smart pointers to.
While trying to translate this into rust+rust-lang/rfcs/#9, I noticed an error in this part:
shared_ptr<Node> node = videoElement->first_child;
shared_ptr<Element> element = node->as_element();
if (!element) {
shared_ptr<TextNode> text = node->as_text_node();
}
If node
is an Element, then the local variable element
is created as a new shared_ptr
that thinks it has sole ownership of it--it is not sharing with node
. So element
and node
will both try to free the same object when they go out of scope, and if one outlive the other you could access an already-destroyed object through the longer-lived one. Same problem for text
.
This would still compile fine in C++, of course, which illustrates why we all want Rust. The proposals for modeling the DOM in rust may look different, but none of them would let something like that compile.
I think this is what you want:
shared_ptr<Node> node = videoElement->first_child;
Element* element = node->as_element();
if (!element) {
TextNode* text = node->as_text_node();
}
where element
and text
would be borrowed pointers in translated rust versions. Do I have this right?
One added concern with representing the DOM in Servo: the vast majority of our DOM methods take
&JS<T>
smart pointer arguments instead of&T
, whereT
can be a type in theNode
hierarchy. It would be swell if we could pass&JS<Element>
to a method taking&JS<Node>
.