snapshot.rs (6327B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 5 //! A gecko snapshot, that stores the element attributes and state before they 6 //! change in order to properly calculate restyle hints. 7 8 use crate::dom::TElement; 9 use crate::gecko::snapshot_helpers; 10 use crate::gecko::wrapper::GeckoElement; 11 use crate::gecko_bindings::bindings; 12 use crate::gecko_bindings::structs::ServoElementSnapshot; 13 use crate::gecko_bindings::structs::ServoElementSnapshotFlags as Flags; 14 use crate::gecko_bindings::structs::ServoElementSnapshotTable; 15 use crate::invalidation::element::element_wrapper::ElementSnapshot; 16 use crate::selector_parser::AttrValue; 17 use crate::string_cache::{Atom, Namespace}; 18 use crate::values::{AtomIdent, AtomString}; 19 use crate::LocalName; 20 use crate::WeakAtom; 21 use dom::ElementState; 22 use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint}; 23 24 /// A snapshot of a Gecko element. 25 pub type GeckoElementSnapshot = ServoElementSnapshot; 26 27 /// A map from elements to snapshots for Gecko's style back-end. 28 pub type SnapshotMap = ServoElementSnapshotTable; 29 30 impl SnapshotMap { 31 /// Gets the snapshot for this element, if any. 32 /// 33 /// FIXME(emilio): The transmute() business we do here is kind of nasty, but 34 /// it's a consequence of the map being a OpaqueNode -> Snapshot table in 35 /// Servo and an Element -> Snapshot table in Gecko. 36 /// 37 /// We should be able to make this a more type-safe with type annotations by 38 /// making SnapshotMap a trait and moving the implementations outside, but 39 /// that's a pain because it implies parameterizing SharedStyleContext. 40 pub fn get<E: TElement>(&self, element: &E) -> Option<&GeckoElementSnapshot> { 41 debug_assert!(element.has_snapshot()); 42 43 unsafe { 44 let element = ::std::mem::transmute::<&E, &GeckoElement>(element); 45 bindings::Gecko_GetElementSnapshot(self, element.0).as_ref() 46 } 47 } 48 } 49 50 impl GeckoElementSnapshot { 51 #[inline] 52 fn has_any(&self, flags: Flags) -> bool { 53 (self.mContains as u8 & flags as u8) != 0 54 } 55 56 /// Returns true if the snapshot has stored state for pseudo-classes 57 /// that depend on things other than `ElementState`. 58 #[inline] 59 pub fn has_other_pseudo_class_state(&self) -> bool { 60 self.has_any(Flags::OtherPseudoClassState) 61 } 62 63 /// Returns true if the snapshot recorded an id change. 64 #[inline] 65 pub fn id_changed(&self) -> bool { 66 self.mIdAttributeChanged() 67 } 68 69 /// Returns true if the snapshot recorded a class attribute change. 70 #[inline] 71 pub fn class_changed(&self) -> bool { 72 self.mClassAttributeChanged() 73 } 74 75 /// Executes the callback once for each attribute that changed. 76 #[inline] 77 pub fn each_attr_changed<F>(&self, mut callback: F) 78 where 79 F: FnMut(&AtomIdent), 80 { 81 for attr in self.mChangedAttrNames.iter() { 82 unsafe { AtomIdent::with(attr.mRawPtr, &mut callback) } 83 } 84 } 85 86 /// selectors::Element::attr_matches 87 pub fn attr_matches( 88 &self, 89 ns: &NamespaceConstraint<&Namespace>, 90 local_name: &LocalName, 91 operation: &AttrSelectorOperation<&AttrValue>, 92 ) -> bool { 93 snapshot_helpers::attr_matches(&self.mAttrs, ns, local_name, operation) 94 } 95 } 96 97 impl ElementSnapshot for GeckoElementSnapshot { 98 fn debug_list_attributes(&self) -> String { 99 use nsstring::nsCString; 100 let mut string = nsCString::new(); 101 unsafe { 102 bindings::Gecko_Snapshot_DebugListAttributes(self, &mut string); 103 } 104 String::from_utf8_lossy(&*string).into_owned() 105 } 106 107 fn state(&self) -> Option<ElementState> { 108 if self.has_any(Flags::State) { 109 Some(ElementState::from_bits_retain(self.mState)) 110 } else { 111 None 112 } 113 } 114 115 #[inline] 116 fn has_attrs(&self) -> bool { 117 self.has_any(Flags::Attributes) 118 } 119 120 #[inline] 121 fn id_attr(&self) -> Option<&WeakAtom> { 122 if !self.has_any(Flags::Id) { 123 return None; 124 } 125 126 snapshot_helpers::get_id(&*self.mAttrs) 127 } 128 129 #[inline] 130 fn is_part(&self, name: &AtomIdent) -> bool { 131 let attr = match snapshot_helpers::find_attr(&*self.mAttrs, &atom!("part")) { 132 Some(attr) => attr, 133 None => return false, 134 }; 135 136 snapshot_helpers::has_class_or_part(name, CaseSensitivity::CaseSensitive, attr) 137 } 138 139 #[inline] 140 fn imported_part(&self, name: &AtomIdent) -> Option<AtomIdent> { 141 snapshot_helpers::imported_part(&*self.mAttrs, name) 142 } 143 144 #[inline] 145 fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool { 146 if !self.has_any(Flags::MaybeClass) { 147 return false; 148 } 149 150 snapshot_helpers::has_class_or_part(name, case_sensitivity, &self.mClass) 151 } 152 153 #[inline] 154 fn each_class<F>(&self, callback: F) 155 where 156 F: FnMut(&AtomIdent), 157 { 158 if !self.has_any(Flags::MaybeClass) { 159 return; 160 } 161 162 snapshot_helpers::each_class_or_part(&self.mClass, callback) 163 } 164 165 #[inline] 166 fn lang_attr(&self) -> Option<AtomString> { 167 let ptr = unsafe { bindings::Gecko_SnapshotLangValue(self) }; 168 if ptr.is_null() { 169 None 170 } else { 171 Some(AtomString(unsafe { Atom::from_addrefed(ptr) })) 172 } 173 } 174 175 /// Returns true if the snapshot has stored state for custom states 176 #[inline] 177 fn has_custom_states(&self) -> bool { 178 self.has_any(Flags::CustomState) 179 } 180 181 /// Returns true if the snapshot has a given CustomState 182 #[inline] 183 fn has_custom_state(&self, state: &AtomIdent) -> bool { 184 unsafe { 185 self.mCustomStates 186 .iter() 187 .any(|setstate| AtomIdent::with(setstate.mRawPtr, |setstate| state == setstate)) 188 } 189 } 190 191 #[inline] 192 fn each_custom_state<F>(&self, mut callback: F) 193 where 194 F: FnMut(&AtomIdent), 195 { 196 unsafe { 197 for atom in self.mCustomStates.iter() { 198 AtomIdent::with(atom.mRawPtr, &mut callback) 199 } 200 } 201 } 202 }