mirror of
https://github.com/servo/servo.git
synced 2025-08-07 22:45:34 +01:00
style: Rewrite the restyle hints code to allow different kinds of element snapshots, and use it for Gecko.
This is a rewrite for how style interfaces with its consumers in order to allow different representations for an element snapshot. This also changes the requirements of an element snapshot, requiring them to only implement MatchAttr, instead of MatchAttrGeneric. This is important for stylo since implementing MatchAttrGeneric is way more difficult for us given the atom limitations. This also allows for more performant implementations in the Gecko side of things.
This commit is contained in:
parent
ca9bc23b39
commit
611e611215
15 changed files with 565 additions and 246 deletions
|
@ -45,6 +45,7 @@ COMPILATION_TARGETS = {
|
|||
"includes": [
|
||||
"{}/dist/include/nsThemeConstants.h",
|
||||
"{}/dist/include/mozilla/dom/AnimationEffectReadOnlyBinding.h",
|
||||
"{}/dist/include/mozilla/ServoElementSnapshot.h",
|
||||
],
|
||||
"files": [
|
||||
"{}/dist/include/nsStyleStruct.h",
|
||||
|
@ -74,6 +75,8 @@ COMPILATION_TARGETS = {
|
|||
"nsDataHashtable.h", "nsCSSScanner.h", "nsTArray",
|
||||
"pair", "SheetParsingMode.h", "StaticPtr.h", "nsProxyRelease.h",
|
||||
"mozilla/dom/AnimationEffectReadOnlyBinding.h",
|
||||
"nsChangeHint.h", "ServoElementSnapshot.h",
|
||||
"EventStates.h", "nsAttrValue.h", "nsAttrName.h",
|
||||
"/Types.h", # <- Disallow UnionTypes.h
|
||||
"/utility", # <- Disallow xutility
|
||||
"nsINode.h", # <- For `NodeFlags`.
|
||||
|
@ -122,7 +125,7 @@ COMPILATION_TARGETS = {
|
|||
"nsStyleCoord", "nsStyleGradientStop", "nsStyleImageLayers",
|
||||
"nsStyleImageLayers::Layer", "nsStyleImageLayers::LayerType",
|
||||
"nsStyleUnit", "nsStyleUnion", "nsStyleCoord::CalcValue",
|
||||
"nsStyleCoord::Calc",
|
||||
"nsStyleCoord::Calc", "nsRestyleHint", "ServoElementSnapshot",
|
||||
|
||||
"SheetParsingMode", "nsMainThreadPtrHandle",
|
||||
"nsMainThreadPtrHolder", "nscolor", "nsFont", "FontFamilyList",
|
||||
|
|
|
@ -13,7 +13,10 @@ use gecko_bindings::bindings::{RawServoStyleSet, RawServoStyleSheet, ServoComput
|
|||
use gecko_bindings::bindings::{ServoDeclarationBlock, ServoNodeData, ThreadSafePrincipalHolder};
|
||||
use gecko_bindings::bindings::{ThreadSafeURIHolder, nsHTMLCSSStyleSheet};
|
||||
use gecko_bindings::ptr::{GeckoArcPrincipal, GeckoArcURI};
|
||||
use gecko_bindings::structs::ServoElementSnapshot;
|
||||
use gecko_bindings::structs::nsRestyleHint;
|
||||
use gecko_bindings::structs::{SheetParsingMode, nsIAtom};
|
||||
use snapshot::GeckoElementSnapshot;
|
||||
use std::mem::transmute;
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
|
@ -437,3 +440,22 @@ pub extern "C" fn Servo_CSSSupports(property: *const u8, property_length: u32,
|
|||
Err(()) => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Servo_ComputeRestyleHint(element: *mut RawGeckoElement,
|
||||
snapshot: *mut ServoElementSnapshot,
|
||||
raw_data: *mut RawServoStyleSet) -> nsRestyleHint {
|
||||
let per_doc_data = unsafe { &mut *(raw_data as *mut PerDocumentStyleData) };
|
||||
let snapshot = unsafe { GeckoElementSnapshot::from_raw(snapshot) };
|
||||
let element = unsafe { GeckoElement::from_raw(element) };
|
||||
|
||||
// NB: This involves an FFI call, we can get rid of it easily if needed.
|
||||
let current_state = element.get_state();
|
||||
|
||||
let hint = per_doc_data.stylist
|
||||
.compute_restyle_hint(&element, &snapshot,
|
||||
current_state);
|
||||
|
||||
// NB: Binary representations match.
|
||||
unsafe { transmute(hint.bits() as u32) }
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ extern crate util;
|
|||
|
||||
mod context;
|
||||
mod data;
|
||||
mod snapshot;
|
||||
mod snapshot_helpers;
|
||||
#[allow(non_snake_case)]
|
||||
pub mod glue;
|
||||
mod traversal;
|
||||
|
|
149
ports/geckolib/snapshot.rs
Normal file
149
ports/geckolib/snapshot.rs
Normal file
|
@ -0,0 +1,149 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
use gecko_bindings::bindings;
|
||||
use gecko_bindings::structs::ServoElementSnapshot;
|
||||
use gecko_bindings::structs::ServoElementSnapshotFlags as Flags;
|
||||
use selectors::parser::AttrSelector;
|
||||
use snapshot_helpers;
|
||||
use string_cache::Atom;
|
||||
use style::element_state::ElementState;
|
||||
use style::restyle_hints::ElementSnapshot;
|
||||
use wrapper::AttrSelectorHelpers;
|
||||
|
||||
// NB: This is sound, in some sense, because during computation of restyle hints
|
||||
// the snapshot is kept alive by the modified elements table.
|
||||
#[derive(Debug)]
|
||||
pub struct GeckoElementSnapshot(*mut ServoElementSnapshot);
|
||||
|
||||
impl GeckoElementSnapshot {
|
||||
#[inline]
|
||||
pub unsafe fn from_raw(raw: *mut ServoElementSnapshot) -> Self {
|
||||
GeckoElementSnapshot(raw)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_html_element_in_html_document(&self) -> bool {
|
||||
unsafe { (*self.0).mIsHTMLElementInHTMLDocument }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn has_any(&self, flags: Flags) -> bool {
|
||||
unsafe { ((*self.0).mContains as u8 & flags as u8) != 0 }
|
||||
}
|
||||
}
|
||||
|
||||
impl ::selectors::MatchAttr for GeckoElementSnapshot {
|
||||
type AttrString = Atom;
|
||||
|
||||
fn match_attr_has(&self, attr: &AttrSelector) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_SnapshotHasAttr(self.0,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()))
|
||||
}
|
||||
}
|
||||
|
||||
fn match_attr_equals(&self, attr: &AttrSelector, value: &Self::AttrString) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_SnapshotAttrEquals(self.0,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr(),
|
||||
/* ignoreCase = */ false)
|
||||
}
|
||||
}
|
||||
|
||||
fn match_attr_equals_ignore_ascii_case(&self, attr: &AttrSelector, value: &Self::AttrString) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_SnapshotAttrEquals(self.0,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr(),
|
||||
/* ignoreCase = */ true)
|
||||
}
|
||||
}
|
||||
fn match_attr_includes(&self, attr: &AttrSelector, value: &Self::AttrString) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_SnapshotAttrIncludes(self.0,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr())
|
||||
}
|
||||
}
|
||||
fn match_attr_dash(&self, attr: &AttrSelector, value: &Self::AttrString) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_SnapshotAttrDashEquals(self.0,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr())
|
||||
}
|
||||
}
|
||||
fn match_attr_prefix(&self, attr: &AttrSelector, value: &Self::AttrString) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_SnapshotAttrHasPrefix(self.0,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr())
|
||||
}
|
||||
}
|
||||
fn match_attr_substring(&self, attr: &AttrSelector, value: &Self::AttrString) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_SnapshotAttrHasSubstring(self.0,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr())
|
||||
}
|
||||
}
|
||||
fn match_attr_suffix(&self, attr: &AttrSelector, value: &Self::AttrString) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_SnapshotAttrHasSuffix(self.0,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ElementSnapshot for GeckoElementSnapshot {
|
||||
fn state(&self) -> Option<ElementState> {
|
||||
if self.has_any(Flags::State) {
|
||||
Some(ElementState::from_bits_truncate(unsafe { (*self.0).mState as u16 }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn has_attrs(&self) -> bool {
|
||||
self.has_any(Flags::Attributes)
|
||||
}
|
||||
|
||||
fn id_attr(&self) -> Option<Atom> {
|
||||
let ptr = unsafe {
|
||||
bindings::Gecko_SnapshotAtomAttrValue(self.0,
|
||||
atom!("id").as_ptr())
|
||||
};
|
||||
|
||||
if ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(Atom::from(ptr))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: share logic with Element::{has_class, each_class}?
|
||||
fn has_class(&self, name: &Atom) -> bool {
|
||||
snapshot_helpers::has_class(self.0,
|
||||
name,
|
||||
bindings::Gecko_SnapshotClassOrClassList)
|
||||
}
|
||||
|
||||
fn each_class<F>(&self, callback: F)
|
||||
where F: FnMut(&Atom)
|
||||
{
|
||||
snapshot_helpers::each_class(self.0,
|
||||
callback,
|
||||
bindings::Gecko_SnapshotClassOrClassList)
|
||||
}
|
||||
}
|
53
ports/geckolib/snapshot_helpers.rs
Normal file
53
ports/geckolib/snapshot_helpers.rs
Normal file
|
@ -0,0 +1,53 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! Element an snapshot common logic.
|
||||
|
||||
use gecko_bindings::structs::nsIAtom;
|
||||
use std::{ptr, slice};
|
||||
use string_cache::Atom;
|
||||
|
||||
pub type ClassOrClassList<T> = unsafe extern fn (T, *mut *mut nsIAtom, *mut *mut *mut nsIAtom) -> u32;
|
||||
|
||||
pub fn has_class<T>(item: T,
|
||||
name: &Atom,
|
||||
getter: ClassOrClassList<T>) -> bool
|
||||
{
|
||||
unsafe {
|
||||
let mut class: *mut nsIAtom = ptr::null_mut();
|
||||
let mut list: *mut *mut nsIAtom = ptr::null_mut();
|
||||
let length = getter(item, &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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn each_class<F, T>(item: T,
|
||||
mut callback: F,
|
||||
getter: ClassOrClassList<T>)
|
||||
where F: FnMut(&Atom)
|
||||
{
|
||||
unsafe {
|
||||
let mut class: *mut nsIAtom = ptr::null_mut();
|
||||
let mut list: *mut *mut nsIAtom = ptr::null_mut();
|
||||
let length = getter(item, &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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,7 +7,6 @@
|
|||
use gecko_bindings::bindings;
|
||||
use gecko_bindings::bindings::Gecko_ChildrenCount;
|
||||
use gecko_bindings::bindings::Gecko_ClassOrClassList;
|
||||
use gecko_bindings::bindings::Gecko_GetElementId;
|
||||
use gecko_bindings::bindings::Gecko_GetNodeData;
|
||||
use gecko_bindings::bindings::ServoNodeData;
|
||||
use gecko_bindings::bindings::{Gecko_ElementState, Gecko_GetDocumentElement};
|
||||
|
@ -20,7 +19,6 @@ use gecko_bindings::bindings::{Gecko_GetPrevSibling, Gecko_GetPrevSiblingElement
|
|||
use gecko_bindings::bindings::{Gecko_GetServoDeclarationBlock, Gecko_IsHTMLElementInHTMLDocument};
|
||||
use gecko_bindings::bindings::{Gecko_IsLink, Gecko_IsRootElement, Gecko_IsTextNode};
|
||||
use gecko_bindings::bindings::{Gecko_IsUnvisitedLink, Gecko_IsVisitedLink};
|
||||
#[allow(unused_imports)] // Used in commented-out code.
|
||||
use gecko_bindings::bindings::{Gecko_LocalName, Gecko_Namespace, Gecko_NodeIsElement, Gecko_SetNodeData};
|
||||
use gecko_bindings::bindings::{RawGeckoDocument, RawGeckoElement, RawGeckoNode};
|
||||
use gecko_bindings::structs::nsIAtom;
|
||||
|
@ -30,29 +28,25 @@ use libc::uintptr_t;
|
|||
use selectors::Element;
|
||||
use selectors::matching::DeclarationBlock;
|
||||
use selectors::parser::{AttrSelector, NamespaceConstraint};
|
||||
use snapshot::GeckoElementSnapshot;
|
||||
use snapshot_helpers;
|
||||
use std::marker::PhantomData;
|
||||
use std::ops::BitOr;
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
use std::sync::Arc;
|
||||
use string_cache::{Atom, BorrowedAtom, BorrowedNamespace, Namespace};
|
||||
use style::data::PrivateStyleData;
|
||||
use style::dom::{OpaqueNode, PresentationalHintsSynthetizer};
|
||||
use style::dom::{TDocument, TElement, TNode, TRestyleDamage, UnsafeNode};
|
||||
use style::element_state::ElementState;
|
||||
#[allow(unused_imports)] // Used in commented-out code.
|
||||
use style::error_reporting::StdoutErrorReporter;
|
||||
use style::gecko_selector_impl::{GeckoSelectorImpl, NonTSPseudoClass};
|
||||
#[allow(unused_imports)] // Used in commented-out code.
|
||||
use style::parser::ParserContextExtraData;
|
||||
#[allow(unused_imports)] // Used in commented-out code.
|
||||
use style::properties::{ComputedValues, parse_style_attribute};
|
||||
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock};
|
||||
use style::refcell::{Ref, RefCell, RefMut};
|
||||
use style::restyle_hints::ElementSnapshot;
|
||||
use style::selector_impl::ElementExt;
|
||||
use style::sink::Push;
|
||||
#[allow(unused_imports)] // Used in commented-out code.
|
||||
use url::Url;
|
||||
|
||||
pub type NonOpaqueStyleData = *mut RefCell<PrivateStyleData>;
|
||||
|
@ -324,7 +318,7 @@ impl<'ld> TDocument for GeckoDocument<'ld> {
|
|||
}
|
||||
}
|
||||
|
||||
fn drain_modified_elements(&self) -> Vec<(GeckoElement<'ld>, ElementSnapshot)> {
|
||||
fn drain_modified_elements(&self) -> Vec<(GeckoElement<'ld>, GeckoElementSnapshot)> {
|
||||
unimplemented!()
|
||||
/*
|
||||
let elements = unsafe { self.document.drain_modified_elements() };
|
||||
|
@ -497,48 +491,30 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
|||
}
|
||||
|
||||
fn get_id(&self) -> Option<Atom> {
|
||||
unsafe {
|
||||
let ptr = Gecko_GetElementId(self.element);
|
||||
if ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(Atom::from(ptr))
|
||||
}
|
||||
let ptr = unsafe {
|
||||
bindings::Gecko_AtomAttrValue(self.element,
|
||||
atom!("id").as_ptr())
|
||||
};
|
||||
|
||||
if ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(Atom::from(ptr))
|
||||
}
|
||||
}
|
||||
|
||||
fn has_class(&self, name: &Atom) -> bool {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
snapshot_helpers::has_class(self.element,
|
||||
name,
|
||||
Gecko_ClassOrClassList)
|
||||
}
|
||||
|
||||
fn each_class<F>(&self, mut callback: F) where F: FnMut(&Atom) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fn each_class<F>(&self, callback: F)
|
||||
where F: FnMut(&Atom)
|
||||
{
|
||||
snapshot_helpers::each_class(self.element,
|
||||
callback,
|
||||
Gecko_ClassOrClassList)
|
||||
}
|
||||
|
||||
fn is_html_element_in_html_document(&self) -> bool {
|
||||
|
@ -548,9 +524,9 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
|||
}
|
||||
}
|
||||
|
||||
trait AttrSelectorHelpers {
|
||||
pub trait AttrSelectorHelpers {
|
||||
fn ns_or_null(&self) -> *mut nsIAtom;
|
||||
fn select_name<'le>(&self, el: &GeckoElement<'le>) -> *mut nsIAtom;
|
||||
fn select_name(&self, is_html_element_in_html_document: bool) -> *mut nsIAtom;
|
||||
}
|
||||
|
||||
impl AttrSelectorHelpers for AttrSelector {
|
||||
|
@ -561,13 +537,12 @@ impl AttrSelectorHelpers for AttrSelector {
|
|||
}
|
||||
}
|
||||
|
||||
fn select_name<'le>(&self, el: &GeckoElement<'le>) -> *mut nsIAtom {
|
||||
if el.is_html_element_in_html_document() {
|
||||
fn select_name(&self, is_html_element_in_html_document: bool) -> *mut nsIAtom {
|
||||
if is_html_element_in_html_document {
|
||||
self.lower_name.as_ptr()
|
||||
} else {
|
||||
self.name.as_ptr()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -577,14 +552,14 @@ impl<'le> ::selectors::MatchAttr for GeckoElement<'le> {
|
|||
unsafe {
|
||||
bindings::Gecko_HasAttr(self.element,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self))
|
||||
attr.select_name(self.is_html_element_in_html_document()))
|
||||
}
|
||||
}
|
||||
fn match_attr_equals(&self, attr: &AttrSelector, value: &Self::AttrString) -> bool {
|
||||
unsafe {
|
||||
bindings::Gecko_AttrEquals(self.element,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr(),
|
||||
/* ignoreCase = */ false)
|
||||
}
|
||||
|
@ -593,7 +568,7 @@ impl<'le> ::selectors::MatchAttr for GeckoElement<'le> {
|
|||
unsafe {
|
||||
bindings::Gecko_AttrEquals(self.element,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr(),
|
||||
/* ignoreCase = */ false)
|
||||
}
|
||||
|
@ -602,7 +577,7 @@ impl<'le> ::selectors::MatchAttr for GeckoElement<'le> {
|
|||
unsafe {
|
||||
bindings::Gecko_AttrIncludes(self.element,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr())
|
||||
}
|
||||
}
|
||||
|
@ -610,7 +585,7 @@ impl<'le> ::selectors::MatchAttr for GeckoElement<'le> {
|
|||
unsafe {
|
||||
bindings::Gecko_AttrDashEquals(self.element,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr())
|
||||
}
|
||||
}
|
||||
|
@ -618,7 +593,7 @@ impl<'le> ::selectors::MatchAttr for GeckoElement<'le> {
|
|||
unsafe {
|
||||
bindings::Gecko_AttrHasPrefix(self.element,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr())
|
||||
}
|
||||
}
|
||||
|
@ -626,7 +601,7 @@ impl<'le> ::selectors::MatchAttr for GeckoElement<'le> {
|
|||
unsafe {
|
||||
bindings::Gecko_AttrHasSubstring(self.element,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr())
|
||||
}
|
||||
}
|
||||
|
@ -634,13 +609,16 @@ impl<'le> ::selectors::MatchAttr for GeckoElement<'le> {
|
|||
unsafe {
|
||||
bindings::Gecko_AttrHasSuffix(self.element,
|
||||
attr.ns_or_null(),
|
||||
attr.select_name(self),
|
||||
attr.select_name(self.is_html_element_in_html_document()),
|
||||
value.as_ptr())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'le> ElementExt for GeckoElement<'le> {
|
||||
type Snapshot = GeckoElementSnapshot;
|
||||
|
||||
#[inline]
|
||||
fn is_link(&self) -> bool {
|
||||
self.match_non_ts_pseudo_class(NonTSPseudoClass::AnyLink)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue