Auto merge of #11431 - bholley:atom_traversal, r=mbrubeck

Geckolib: Add performant implementations of get_id, has_class, and each_class

<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="35" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/11431)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-05-26 00:55:48 -05:00
commit 60d78b7272
4 changed files with 62 additions and 12 deletions

View file

@ -146,6 +146,10 @@ extern "C" {
pub fn Gecko_IsRootElement(element: *mut RawGeckoElement) -> bool;
pub fn Gecko_LocalName(element: *mut RawGeckoElement) -> *mut nsIAtom;
pub fn Gecko_Namespace(element: *mut RawGeckoElement) -> *mut nsIAtom;
pub fn Gecko_GetElementId(element: *mut RawGeckoElement) -> *mut nsIAtom;
pub fn Gecko_ClassOrClassList(element: *mut RawGeckoElement,
class_: *mut *mut nsIAtom,
classList: *mut *mut *mut nsIAtom) -> u32;
pub fn Gecko_GetNodeData(node: *mut RawGeckoNode) -> *mut ServoNodeData;
pub fn Gecko_SetNodeData(node: *mut RawGeckoNode,
data: *mut ServoNodeData);

View file

@ -75,10 +75,16 @@ eval ./rust-bindgen/target/debug/bindgen \
-x c++ -std=gnu++0x \
"-I$DIST_INCLUDE" \
"-I$DIST_INCLUDE/nspr/" \
"-I$SRCDIR/nsprpub/pr/include/" \
"-I$1/nsprpub/pr/include/" \
$PLATFORM_DEPENDENT_DEFINES \
-DMOZILLA_INTERNAL_API \
-DMOZ_STYLO_BINDINGS=1 \
-DJS_DEBUG=1 \
-DDEBUG=1 -DTRACING=1 -DOS_POSIX=1 \
-DIMPL_LIBXUL \
-o ../bindings.rs \
-no-type-renaming \
-include "$1/mozilla-config.h" \
"$DIST_INCLUDE/mozilla/ServoBindings.h" \
-match "ServoBindings.h" \
-match "nsStyleStructList.h" \

View file

@ -134,6 +134,10 @@ impl Atom {
self.0
}
pub unsafe fn with<F>(ptr: *mut nsIAtom, callback: &mut F) where F: FnMut(&Atom) {
callback(transmute(&ptr))
}
// Static atoms have a dummy AddRef/Release, so we don't bother calling
// AddRef() here. This would cause memory corruption with non-static atoms
// both because (a) we wouldn't hold the atom alive, and (b) we can't avoid
@ -226,3 +230,12 @@ impl From<String> for Atom {
Atom::from(&*string)
}
}
impl From<*mut nsIAtom> for Atom {
fn from(ptr: *mut nsIAtom) -> Atom {
unsafe {
Gecko_AddRefAtom(ptr);
Atom(ptr)
}
}
}

View file

@ -5,7 +5,9 @@
#![allow(unsafe_code)]
use gecko_bindings::bindings::{Gecko_ChildrenCount};
use gecko_bindings::bindings::{Gecko_ClassOrClassList};
use gecko_bindings::bindings::{Gecko_ElementState, Gecko_GetAttrAsUTF8, Gecko_GetDocumentElement};
use gecko_bindings::bindings::{Gecko_GetElementId};
use gecko_bindings::bindings::{Gecko_GetFirstChild, Gecko_GetFirstChildElement};
use gecko_bindings::bindings::{Gecko_GetLastChild, Gecko_GetLastChildElement};
use gecko_bindings::bindings::{Gecko_GetNextSibling, Gecko_GetNextSiblingElement};
@ -18,6 +20,7 @@ use gecko_bindings::bindings::{Gecko_IsUnvisitedLink, Gecko_IsVisitedLink};
use gecko_bindings::bindings::{Gecko_LocalName, Gecko_Namespace, Gecko_NodeIsElement, Gecko_SetNodeData};
use gecko_bindings::bindings::{RawGeckoDocument, RawGeckoElement, RawGeckoNode};
use gecko_bindings::bindings::{ServoNodeData};
use gecko_bindings::bindings::{nsIAtom};
use libc::uintptr_t;
use properties::GeckoComputedValues;
use selector_impl::{GeckoSelectorImpl, NonTSPseudoClass, PrivateStyleData};
@ -28,6 +31,7 @@ use smallvec::VecLike;
use std::cell::{Ref, RefCell, RefMut};
use std::marker::PhantomData;
use std::ops::BitOr;
use std::ptr;
use std::slice;
use std::str::from_utf8_unchecked;
use std::sync::Arc;
@ -447,23 +451,46 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
}
fn get_id(&self) -> Option<Atom> {
// FIXME(bholley): Servo caches the id atom directly on the element to
// make this blazing fast. Assuming that was a measured optimization, doing
// the dumb thing like we do below will almost certainly be a bottleneck.
self.get_attr(&ns!(), &atom!("id")).map(|s| Atom::from(s))
unsafe {
let ptr = Gecko_GetElementId(self.element);
if ptr.is_null() {
None
} else {
Some(Atom::from(ptr))
}
}
}
fn has_class(&self, name: &Atom) -> bool {
// FIXME(bholley): Do this smarter.
self.get_attr(&ns!(), &atom!("class"))
.map_or(false, |classes| classes.split(" ").any(|n| &Atom::from(n) == name))
unsafe {
let mut class: *mut nsIAtom = ptr::null_mut();
let mut list: *mut *mut nsIAtom = ptr::null_mut();
let length = Gecko_ClassOrClassList(self.element, &mut class, &mut list);
match length {
0 => false,
1 => name.as_ptr() == class,
n => {
let classes = slice::from_raw_parts(list, n as usize);
classes.iter().any(|ptr| name.as_ptr() == *ptr)
}
}
}
}
fn each_class<F>(&self, mut callback: F) where F: FnMut(&Atom) {
// FIXME(bholley): Synergize with the DOM to stop splitting strings here.
if let Some(classes) = self.get_attr(&ns!(), &atom!("class")) {
for c in classes.split(" ") {
callback(&Atom::from(c));
unsafe {
let mut class: *mut nsIAtom = ptr::null_mut();
let mut list: *mut *mut nsIAtom = ptr::null_mut();
let length = Gecko_ClassOrClassList(self.element, &mut class, &mut list);
match length {
0 => {}
1 => Atom::with(class, &mut callback),
n => {
let classes = slice::from_raw_parts(list, n as usize);
for c in classes {
Atom::with(*c, &mut callback)
}
}
}
}
}