Add performant implementations of get_id, has_class, and each_class.

This commit is contained in:
Bobby Holley 2016-05-25 11:53:53 -07:00
parent 0e6bc9f60b
commit b90b4a5e0f
2 changed files with 51 additions and 11 deletions

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)
}
}
}
}
}