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 http://mozilla.org/MPL/2.0/. */
4
5 //! Element nodes.
6
7 use devtools_traits::AttrInfo;
8 use dom::activation::Activatable;
9 use dom::attr::{Attr, AttrHelpersForLayout};
10 use dom::bindings::cell::DomRefCell;
11 use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
12 use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
13 use dom::bindings::codegen::Bindings::ElementBinding;
14 use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
15 use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
16 use dom::bindings::codegen::Bindings::FunctionBinding::Function;
17 use dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods;
18 use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
19 use dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, ScrollToOptions};
20 use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
21 use dom::bindings::codegen::UnionTypes::NodeOrString;
22 use dom::bindings::conversions::DerivedFrom;
23 use dom::bindings::error::{Error, ErrorResult, Fallible};
24 use dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
25 use dom::bindings::refcounted::{Trusted, TrustedPromise};
26 use dom::bindings::reflector::DomObject;
27 use dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom, RootedReference};
28 use dom::bindings::str::DOMString;
29 use dom::bindings::xmlname::{namespace_from_domstring, validate_and_extract, xml_name_type};
30 use dom::bindings::xmlname::XMLName::InvalidXMLName;
31 use dom::characterdata::CharacterData;
32 use dom::create::create_element;
33 use dom::customelementregistry::{CallbackReaction, CustomElementDefinition, CustomElementReaction};
34 use dom::document::{Document, LayoutDocumentHelpers};
35 use dom::documentfragment::DocumentFragment;
36 use dom::domrect::DOMRect;
37 use dom::domtokenlist::DOMTokenList;
38 use dom::event::Event;
39 use dom::eventtarget::EventTarget;
40 use dom::htmlanchorelement::HTMLAnchorElement;
41 use dom::htmlbodyelement::{HTMLBodyElement, HTMLBodyElementLayoutHelpers};
42 use dom::htmlbuttonelement::HTMLButtonElement;
43 use dom::htmlcanvaselement::{HTMLCanvasElement, LayoutHTMLCanvasElementHelpers};
44 use dom::htmlcollection::HTMLCollection;
45 use dom::htmlelement::HTMLElement;
46 use dom::htmlfieldsetelement::HTMLFieldSetElement;
47 use dom::htmlfontelement::{HTMLFontElement, HTMLFontElementLayoutHelpers};
48 use dom::htmlformelement::FormControlElementHelpers;
49 use dom::htmlhrelement::{HTMLHRElement, HTMLHRLayoutHelpers};
50 use dom::htmliframeelement::{HTMLIFrameElement, HTMLIFrameElementLayoutMethods};
51 use dom::htmlimageelement::{HTMLImageElement, LayoutHTMLImageElementHelpers};
52 use dom::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers};
53 use dom::htmllabelelement::HTMLLabelElement;
54 use dom::htmllegendelement::HTMLLegendElement;
55 use dom::htmllinkelement::HTMLLinkElement;
56 use dom::htmlobjectelement::HTMLObjectElement;
57 use dom::htmloptgroupelement::HTMLOptGroupElement;
58 use dom::htmlselectelement::HTMLSelectElement;
59 use dom::htmlstyleelement::HTMLStyleElement;
60 use dom::htmltablecellelement::{HTMLTableCellElement, HTMLTableCellElementLayoutHelpers};
61 use dom::htmltableelement::{HTMLTableElement, HTMLTableElementLayoutHelpers};
62 use dom::htmltablerowelement::{HTMLTableRowElement, HTMLTableRowElementLayoutHelpers};
63 use dom::htmltablesectionelement::{HTMLTableSectionElement, HTMLTableSectionElementLayoutHelpers};
64 use dom::htmltemplateelement::HTMLTemplateElement;
65 use dom::htmltextareaelement::{HTMLTextAreaElement, LayoutHTMLTextAreaElementHelpers};
66 use dom::mutationobserver::{Mutation, MutationObserver};
67 use dom::namednodemap::NamedNodeMap;
68 use dom::node::{ChildrenMutation, LayoutNodeHelpers, Node};
69 use dom::node::{NodeDamage, NodeFlags, UnbindContext};
70 use dom::node::{document_from_node, window_from_node};
71 use dom::nodelist::NodeList;
72 use dom::promise::Promise;
73 use dom::servoparser::ServoParser;
74 use dom::text::Text;
75 use dom::validation::Validatable;
76 use dom::virtualmethods::{VirtualMethods, vtable_for};
77 use dom::window::ReflowReason;
78 use dom_struct::dom_struct;
79 use html5ever::{Prefix, LocalName, Namespace, QualName};
80 use html5ever::serialize;
81 use html5ever::serialize::SerializeOpts;
82 use html5ever::serialize::TraversalScope;
83 use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode};
84 use js::jsapi::Heap;
85 use js::jsval::JSVal;
86 use net_traits::request::CorsSettings;
87 use ref_filter_map::ref_filter_map;
88 use script_layout_interface::message::ReflowGoal;
89 use script_thread::ScriptThread;
90 use selectors::Element as SelectorsElement;
91 use selectors::attr::{AttrSelectorOperation, NamespaceConstraint, CaseSensitivity};
92 use selectors::matching::{ElementSelectorFlags, MatchingContext};
93 use selectors::sink::Push;
94 use servo_arc::Arc;
95 use servo_atoms::Atom;
96 use std::borrow::Cow;
97 use std::cell::{Cell, Ref};
98 use std::default::Default;
99 use std::fmt;
100 use std::mem;
101 use std::rc::Rc;
102 use std::str::FromStr;
103 use style::CaseSensitivityExt;
104 use style::applicable_declarations::ApplicableDeclarationBlock;
105 use style::attr::{AttrValue, LengthOrPercentageOrAuto};
106 use style::context::QuirksMode;
107 use style::dom_apis;
108 use style::element_state::ElementState;
109 use style::invalidation::element::restyle_hints::RestyleHint;
110 use style::properties::{ComputedValues, Importance, PropertyDeclaration};
111 use style::properties::{PropertyDeclarationBlock, parse_style_attribute};
112 use style::properties::longhands::{self, background_image, border_spacing, font_family, font_size};
113 use style::properties::longhands::{overflow_x, overflow_y};
114 use style::rule_tree::CascadeLevel;
115 use style::selector_parser::{NonTSPseudoClass, PseudoElement, RestyleDamage, SelectorImpl, SelectorParser};
116 use style::selector_parser::extended_filtering;
117 use style::shared_lock::{SharedRwLock, Locked};
118 use style::thread_state;
119 use style::values::{CSSFloat, Either};
120 use style::values::{specified, computed};
121 use stylesheet_loader::StylesheetOwner;
122 use task::TaskOnce;
123 use xml5ever::serialize as xmlSerialize;
124 use xml5ever::serialize::SerializeOpts as XmlSerializeOpts;
125 use xml5ever::serialize::TraversalScope as XmlTraversalScope;
126 use xml5ever::serialize::TraversalScope::ChildrenOnly as XmlChildrenOnly;
127 use xml5ever::serialize::TraversalScope::IncludeNode as XmlIncludeNode;
128
129 // TODO: Update focus state when the top-level browsing context gains or loses system focus,
130 // and when the element enters or leaves a browsing context container.
131 // https://html.spec.whatwg.org/multipage/#selector-focus
132
133 #[dom_struct]
134 pub struct Element {
135 node: Node,
136 local_name: LocalName,
137 tag_name: TagName,
138 namespace: Namespace,
139 prefix: DomRefCell<Option<Prefix>>,
140 attrs: DomRefCell<Vec<Dom<Attr>>>,
141 id_attribute: DomRefCell<Option<Atom>>,
142 is: DomRefCell<Option<LocalName>>,
143 #[ignore_malloc_size_of = "Arc"]
144 style_attribute: DomRefCell<Option<Arc<Locked<PropertyDeclarationBlock>>>>,
145 attr_list: MutNullableDom<NamedNodeMap>,
146 class_list: MutNullableDom<DOMTokenList>,
147 state: Cell<ElementState>,
148 /// These flags are set by the style system to indicate the that certain
149 /// operations may require restyling this element or its descendants. The
150 /// flags are not atomic, so the style system takes care of only set them
151 /// when it has exclusive access to the element.
152 #[ignore_malloc_size_of = "bitflags defined in rust-selectors"]
153 selector_flags: Cell<ElementSelectorFlags>,
154 /// <https://html.spec.whatwg.org/multipage/#custom-element-reaction-queue>
155 custom_element_reaction_queue: DomRefCell<Vec<CustomElementReaction>>,
156 /// <https://dom.spec.whatwg.org/#concept-element-custom-element-definition>
157 #[ignore_malloc_size_of = "Rc"]
158 custom_element_definition: DomRefCell<Option<Rc<CustomElementDefinition>>>,
159 /// <https://dom.spec.whatwg.org/#concept-element-custom-element-state>
160 custom_element_state: Cell<CustomElementState>,
161 }
162
163 impl fmt::Debug for Element {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result164 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
165 write!(f, "<{}", self.local_name)?;
166 if let Some(ref id) = *self.id_attribute.borrow() {
167 write!(f, " id={}", id)?;
168 }
169 write!(f, ">")
170 }
171 }
172
173 impl fmt::Debug for DomRoot<Element> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result174 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
175 (**self).fmt(f)
176 }
177 }
178
179 #[derive(MallocSizeOf, PartialEq)]
180 pub enum ElementCreator {
181 ParserCreated(u64),
182 ScriptCreated,
183 }
184
185 pub enum CustomElementCreationMode {
186 Synchronous,
187 Asynchronous,
188 }
189
190 /// <https://dom.spec.whatwg.org/#concept-element-custom-element-state>
191 #[derive(Clone, Copy, Eq, JSTraceable, MallocSizeOf, PartialEq)]
192 pub enum CustomElementState {
193 Undefined,
194 Failed,
195 Uncustomized,
196 Custom,
197 }
198
199 impl ElementCreator {
is_parser_created(&self) -> bool200 pub fn is_parser_created(&self) -> bool {
201 match *self {
202 ElementCreator::ParserCreated(_) => true,
203 ElementCreator::ScriptCreated => false,
204 }
205 }
return_line_number(&self) -> u64206 pub fn return_line_number(&self) -> u64 {
207 match *self {
208 ElementCreator::ParserCreated(l) => l,
209 ElementCreator::ScriptCreated => 1,
210 }
211 }
212 }
213
214 pub enum AdjacentPosition {
215 BeforeBegin,
216 AfterEnd,
217 AfterBegin,
218 BeforeEnd,
219 }
220
221 impl FromStr for AdjacentPosition {
222 type Err = Error;
223
from_str(position: &str) -> Result<Self, Self::Err>224 fn from_str(position: &str) -> Result<Self, Self::Err> {
225 match_ignore_ascii_case! { &*position,
226 "beforebegin" => Ok(AdjacentPosition::BeforeBegin),
227 "afterbegin" => Ok(AdjacentPosition::AfterBegin),
228 "beforeend" => Ok(AdjacentPosition::BeforeEnd),
229 "afterend" => Ok(AdjacentPosition::AfterEnd),
230 _ => Err(Error::Syntax)
231 }
232 }
233 }
234
235 //
236 // Element methods
237 //
238 impl Element {
create(name: QualName, is: Option<LocalName>, document: &Document, creator: ElementCreator, mode: CustomElementCreationMode) -> DomRoot<Element>239 pub fn create(name: QualName,
240 is: Option<LocalName>,
241 document: &Document,
242 creator: ElementCreator,
243 mode: CustomElementCreationMode)
244 -> DomRoot<Element> {
245 create_element(name, is, document, creator, mode)
246 }
247
new_inherited(local_name: LocalName, namespace: Namespace, prefix: Option<Prefix>, document: &Document) -> Element248 pub fn new_inherited(local_name: LocalName,
249 namespace: Namespace, prefix: Option<Prefix>,
250 document: &Document) -> Element {
251 Element::new_inherited_with_state(ElementState::empty(), local_name,
252 namespace, prefix, document)
253 }
254
new_inherited_with_state(state: ElementState, local_name: LocalName, namespace: Namespace, prefix: Option<Prefix>, document: &Document) -> Element255 pub fn new_inherited_with_state(state: ElementState, local_name: LocalName,
256 namespace: Namespace, prefix: Option<Prefix>,
257 document: &Document)
258 -> Element {
259 Element {
260 node: Node::new_inherited(document),
261 local_name: local_name,
262 tag_name: TagName::new(),
263 namespace: namespace,
264 prefix: DomRefCell::new(prefix),
265 attrs: DomRefCell::new(vec![]),
266 id_attribute: DomRefCell::new(None),
267 is: DomRefCell::new(None),
268 style_attribute: DomRefCell::new(None),
269 attr_list: Default::default(),
270 class_list: Default::default(),
271 state: Cell::new(state),
272 selector_flags: Cell::new(ElementSelectorFlags::empty()),
273 custom_element_reaction_queue: Default::default(),
274 custom_element_definition: Default::default(),
275 custom_element_state: Cell::new(CustomElementState::Uncustomized),
276 }
277 }
278
new(local_name: LocalName, namespace: Namespace, prefix: Option<Prefix>, document: &Document) -> DomRoot<Element>279 pub fn new(local_name: LocalName,
280 namespace: Namespace,
281 prefix: Option<Prefix>,
282 document: &Document) -> DomRoot<Element> {
283 Node::reflect_node(
284 Box::new(Element::new_inherited(local_name, namespace, prefix, document)),
285 document,
286 ElementBinding::Wrap)
287 }
288
restyle(&self, damage: NodeDamage)289 pub fn restyle(&self, damage: NodeDamage) {
290 let doc = self.node.owner_doc();
291 let mut restyle = doc.ensure_pending_restyle(self);
292
293 // FIXME(bholley): I think we should probably only do this for
294 // NodeStyleDamaged, but I'm preserving existing behavior.
295 restyle.hint.insert(RestyleHint::RESTYLE_SELF);
296
297 if damage == NodeDamage::OtherNodeDamage {
298 restyle.damage = RestyleDamage::rebuild_and_reflow();
299 }
300 }
301
set_is(&self, is: LocalName)302 pub fn set_is(&self, is: LocalName) {
303 *self.is.borrow_mut() = Some(is);
304 }
305
get_is(&self) -> Option<LocalName>306 pub fn get_is(&self) -> Option<LocalName> {
307 self.is.borrow().clone()
308 }
309
set_custom_element_state(&self, state: CustomElementState)310 pub fn set_custom_element_state(&self, state: CustomElementState) {
311 self.custom_element_state.set(state);
312 }
313
get_custom_element_state(&self) -> CustomElementState314 pub fn get_custom_element_state(&self) -> CustomElementState {
315 self.custom_element_state.get()
316 }
317
set_custom_element_definition(&self, definition: Rc<CustomElementDefinition>)318 pub fn set_custom_element_definition(&self, definition: Rc<CustomElementDefinition>) {
319 *self.custom_element_definition.borrow_mut() = Some(definition);
320 }
321
get_custom_element_definition(&self) -> Option<Rc<CustomElementDefinition>>322 pub fn get_custom_element_definition(&self) -> Option<Rc<CustomElementDefinition>> {
323 (*self.custom_element_definition.borrow()).clone()
324 }
325
push_callback_reaction(&self, function: Rc<Function>, args: Box<[Heap<JSVal>]>)326 pub fn push_callback_reaction(&self, function: Rc<Function>, args: Box<[Heap<JSVal>]>) {
327 self.custom_element_reaction_queue.borrow_mut().push(CustomElementReaction::Callback(function, args));
328 }
329
push_upgrade_reaction(&self, definition: Rc<CustomElementDefinition>)330 pub fn push_upgrade_reaction(&self, definition: Rc<CustomElementDefinition>) {
331 self.custom_element_reaction_queue.borrow_mut().push(CustomElementReaction::Upgrade(definition));
332 }
333
clear_reaction_queue(&self)334 pub fn clear_reaction_queue(&self) {
335 self.custom_element_reaction_queue.borrow_mut().clear();
336 }
337
invoke_reactions(&self)338 pub fn invoke_reactions(&self) {
339 // TODO: This is not spec compliant, as this will allow some reactions to be processed
340 // after clear_reaction_queue has been called.
341 rooted_vec!(let mut reactions);
342 while !self.custom_element_reaction_queue.borrow().is_empty() {
343 mem::swap(&mut *reactions, &mut *self.custom_element_reaction_queue.borrow_mut());
344 for reaction in reactions.iter() {
345 reaction.invoke(self);
346 }
347 reactions.clear();
348 }
349 }
350
351 /// style will be `None` for elements in a `display: none` subtree. otherwise, the element has a
352 /// layout box iff it doesn't have `display: none`.
style(&self) -> Option<Arc<ComputedValues>>353 pub fn style(&self) -> Option<Arc<ComputedValues>> {
354 window_from_node(self).style_query(
355 self.upcast::<Node>().to_trusted_node_address()
356 )
357 }
358
359 // https://drafts.csswg.org/cssom-view/#css-layout-box
has_css_layout_box(&self) -> bool360 pub fn has_css_layout_box(&self) -> bool {
361 self.style()
362 .map_or(false, |s| !s.get_box().clone_display().is_none())
363 }
364
365 // https://drafts.csswg.org/cssom-view/#potentially-scrollable
potentially_scrollable(&self) -> bool366 fn potentially_scrollable(&self) -> bool {
367 self.has_css_layout_box() && !self.has_any_visible_overflow()
368 }
369
370 // https://drafts.csswg.org/cssom-view/#scrolling-box
has_scrolling_box(&self) -> bool371 fn has_scrolling_box(&self) -> bool {
372 // TODO: scrolling mechanism, such as scrollbar (We don't have scrollbar yet)
373 // self.has_scrolling_mechanism()
374 self.has_any_hidden_overflow()
375 }
376
has_overflow(&self) -> bool377 fn has_overflow(&self) -> bool {
378 self.ScrollHeight() > self.ClientHeight() ||
379 self.ScrollWidth() > self.ClientWidth()
380 }
381
382 // TODO: Once #19183 is closed (overflow-x/y types moved out of mako), then we could implement
383 // a more generic `fn has_some_overflow(&self, overflow: Overflow)` rather than have
384 // these two `has_any_{visible,hidden}_overflow` methods which are very structurally
385 // similar.
386
387 /// Computed value of overflow-x or overflow-y is "visible"
has_any_visible_overflow(&self) -> bool388 fn has_any_visible_overflow(&self) -> bool {
389 self.style().map_or(false, |s| {
390 let box_ = s.get_box();
391
392 box_.clone_overflow_x() == overflow_x::computed_value::T::Visible ||
393 box_.clone_overflow_y() == overflow_y::computed_value::T::Visible
394 })
395 }
396
397 /// Computed value of overflow-x or overflow-y is "hidden"
has_any_hidden_overflow(&self) -> bool398 fn has_any_hidden_overflow(&self) -> bool {
399 self.style().map_or(false, |s| {
400 let box_ = s.get_box();
401
402 box_.clone_overflow_x() == overflow_x::computed_value::T::Hidden ||
403 box_.clone_overflow_y() == overflow_y::computed_value::T::Hidden
404 })
405 }
406 }
407
408 #[allow(unsafe_code)]
409 pub trait RawLayoutElementHelpers {
get_attr_for_layout<'a>(&'a self, namespace: &Namespace, name: &LocalName) -> Option<&'a AttrValue>410 unsafe fn get_attr_for_layout<'a>(&'a self, namespace: &Namespace, name: &LocalName)
411 -> Option<&'a AttrValue>;
get_attr_val_for_layout<'a>(&'a self, namespace: &Namespace, name: &LocalName) -> Option<&'a str>412 unsafe fn get_attr_val_for_layout<'a>(&'a self, namespace: &Namespace, name: &LocalName)
413 -> Option<&'a str>;
get_attr_vals_for_layout<'a>(&'a self, name: &LocalName) -> Vec<&'a AttrValue>414 unsafe fn get_attr_vals_for_layout<'a>(&'a self, name: &LocalName) -> Vec<&'a AttrValue>;
415 }
416
417 #[inline]
418 #[allow(unsafe_code)]
get_attr_for_layout<'a>(elem: &'a Element, namespace: &Namespace, name: &LocalName) -> Option<LayoutDom<Attr>>419 pub unsafe fn get_attr_for_layout<'a>(elem: &'a Element, namespace: &Namespace, name: &LocalName)
420 -> Option<LayoutDom<Attr>> {
421 // cast to point to T in RefCell<T> directly
422 let attrs = elem.attrs.borrow_for_layout();
423 attrs.iter().find(|attr| {
424 let attr = attr.to_layout();
425 *name == attr.local_name_atom_forever() &&
426 (*attr.unsafe_get()).namespace() == namespace
427 }).map(|attr| attr.to_layout())
428 }
429
430 #[allow(unsafe_code)]
431 impl RawLayoutElementHelpers for Element {
432 #[inline]
get_attr_for_layout<'a>(&'a self, namespace: &Namespace, name: &LocalName) -> Option<&'a AttrValue>433 unsafe fn get_attr_for_layout<'a>(&'a self, namespace: &Namespace, name: &LocalName)
434 -> Option<&'a AttrValue> {
435 get_attr_for_layout(self, namespace, name).map(|attr| {
436 attr.value_forever()
437 })
438 }
439
440 #[inline]
get_attr_val_for_layout<'a>(&'a self, namespace: &Namespace, name: &LocalName) -> Option<&'a str>441 unsafe fn get_attr_val_for_layout<'a>(&'a self, namespace: &Namespace, name: &LocalName)
442 -> Option<&'a str> {
443 get_attr_for_layout(self, namespace, name).map(|attr| {
444 attr.value_ref_forever()
445 })
446 }
447
448 #[inline]
get_attr_vals_for_layout<'a>(&'a self, name: &LocalName) -> Vec<&'a AttrValue>449 unsafe fn get_attr_vals_for_layout<'a>(&'a self, name: &LocalName) -> Vec<&'a AttrValue> {
450 let attrs = self.attrs.borrow_for_layout();
451 attrs.iter().filter_map(|attr| {
452 let attr = attr.to_layout();
453 if *name == attr.local_name_atom_forever() {
454 Some(attr.value_forever())
455 } else {
456 None
457 }
458 }).collect()
459 }
460 }
461
462 pub trait LayoutElementHelpers {
463 #[allow(unsafe_code)]
has_class_for_layout(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool464 unsafe fn has_class_for_layout(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool;
465 #[allow(unsafe_code)]
get_classes_for_layout(&self) -> Option<&'static [Atom]>466 unsafe fn get_classes_for_layout(&self) -> Option<&'static [Atom]>;
467
468 #[allow(unsafe_code)]
synthesize_presentational_hints_for_legacy_attributes<V>(&self, &mut V) where V: Push<ApplicableDeclarationBlock>469 unsafe fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, &mut V)
470 where V: Push<ApplicableDeclarationBlock>;
471 #[allow(unsafe_code)]
get_colspan(self) -> u32472 unsafe fn get_colspan(self) -> u32;
473 #[allow(unsafe_code)]
get_rowspan(self) -> u32474 unsafe fn get_rowspan(self) -> u32;
475 #[allow(unsafe_code)]
is_html_element(&self) -> bool476 unsafe fn is_html_element(&self) -> bool;
id_attribute(&self) -> *const Option<Atom>477 fn id_attribute(&self) -> *const Option<Atom>;
style_attribute(&self) -> *const Option<Arc<Locked<PropertyDeclarationBlock>>>478 fn style_attribute(&self) -> *const Option<Arc<Locked<PropertyDeclarationBlock>>>;
local_name(&self) -> &LocalName479 fn local_name(&self) -> &LocalName;
namespace(&self) -> &Namespace480 fn namespace(&self) -> &Namespace;
get_lang_for_layout(&self) -> String481 fn get_lang_for_layout(&self) -> String;
get_checked_state_for_layout(&self) -> bool482 fn get_checked_state_for_layout(&self) -> bool;
get_indeterminate_state_for_layout(&self) -> bool483 fn get_indeterminate_state_for_layout(&self) -> bool;
get_state_for_layout(&self) -> ElementState484 fn get_state_for_layout(&self) -> ElementState;
insert_selector_flags(&self, flags: ElementSelectorFlags)485 fn insert_selector_flags(&self, flags: ElementSelectorFlags);
has_selector_flags(&self, flags: ElementSelectorFlags) -> bool486 fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool;
487 }
488
489 impl LayoutElementHelpers for LayoutDom<Element> {
490 #[allow(unsafe_code)]
491 #[inline]
has_class_for_layout(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool492 unsafe fn has_class_for_layout(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
493 get_attr_for_layout(&*self.unsafe_get(), &ns!(), &local_name!("class")).map_or(false, |attr| {
494 attr.value_tokens_forever().unwrap().iter().any(|atom| case_sensitivity.eq_atom(atom, name))
495 })
496 }
497
498 #[allow(unsafe_code)]
499 #[inline]
get_classes_for_layout(&self) -> Option<&'static [Atom]>500 unsafe fn get_classes_for_layout(&self) -> Option<&'static [Atom]> {
501 get_attr_for_layout(&*self.unsafe_get(), &ns!(), &local_name!("class"))
502 .map(|attr| attr.value_tokens_forever().unwrap())
503 }
504
505 #[allow(unsafe_code)]
synthesize_presentational_hints_for_legacy_attributes<V>(&self, hints: &mut V) where V: Push<ApplicableDeclarationBlock>506 unsafe fn synthesize_presentational_hints_for_legacy_attributes<V>(&self, hints: &mut V)
507 where V: Push<ApplicableDeclarationBlock>
508 {
509 // FIXME(emilio): Just a single PDB should be enough.
510 #[inline]
511 fn from_declaration(shared_lock: &SharedRwLock, declaration: PropertyDeclaration)
512 -> ApplicableDeclarationBlock {
513 ApplicableDeclarationBlock::from_declarations(
514 Arc::new(shared_lock.wrap(PropertyDeclarationBlock::with_one(
515 declaration, Importance::Normal
516 ))),
517 CascadeLevel::PresHints)
518 }
519
520 let document = self.upcast::<Node>().owner_doc_for_layout();
521 let shared_lock = document.style_shared_lock();
522
523 let bgcolor = if let Some(this) = self.downcast::<HTMLBodyElement>() {
524 this.get_background_color()
525 } else if let Some(this) = self.downcast::<HTMLTableElement>() {
526 this.get_background_color()
527 } else if let Some(this) = self.downcast::<HTMLTableCellElement>() {
528 this.get_background_color()
529 } else if let Some(this) = self.downcast::<HTMLTableRowElement>() {
530 this.get_background_color()
531 } else if let Some(this) = self.downcast::<HTMLTableSectionElement>() {
532 this.get_background_color()
533 } else {
534 None
535 };
536
537 if let Some(color) = bgcolor {
538 hints.push(from_declaration(
539 shared_lock,
540 PropertyDeclaration::BackgroundColor(color.into())
541 ));
542 }
543
544 let background = if let Some(this) = self.downcast::<HTMLBodyElement>() {
545 this.get_background()
546 } else {
547 None
548 };
549
550 if let Some(url) = background {
551 hints.push(from_declaration(
552 shared_lock,
553 PropertyDeclaration::BackgroundImage(
554 background_image::SpecifiedValue(vec![
555 Either::Second(specified::Image::for_cascade(url.into()))
556 ]))));
557 }
558
559 let color = if let Some(this) = self.downcast::<HTMLFontElement>() {
560 this.get_color()
561 } else if let Some(this) = self.downcast::<HTMLBodyElement>() {
562 // https://html.spec.whatwg.org/multipage/#the-page:the-body-element-20
563 this.get_color()
564 } else if let Some(this) = self.downcast::<HTMLHRElement>() {
565 // https://html.spec.whatwg.org/multipage/#the-hr-element-2:presentational-hints-5
566 this.get_color()
567 } else {
568 None
569 };
570
571 if let Some(color) = color {
572 hints.push(from_declaration(
573 shared_lock,
574 PropertyDeclaration::Color(
575 longhands::color::SpecifiedValue(color.into())
576 )
577 ));
578 }
579
580 let font_family = if let Some(this) = self.downcast::<HTMLFontElement>() {
581 this.get_face()
582 } else {
583 None
584 };
585
586 if let Some(font_family) = font_family {
587 hints.push(from_declaration(
588 shared_lock,
589 PropertyDeclaration::FontFamily(
590 font_family::SpecifiedValue::Values(
591 computed::font::FontFamilyList::new(Box::new([
592 computed::font::SingleFontFamily::from_atom(
593 font_family)]))))));
594 }
595
596 let font_size = self.downcast::<HTMLFontElement>().and_then(|this| this.get_size());
597
598 if let Some(font_size) = font_size {
599 hints.push(from_declaration(
600 shared_lock,
601 PropertyDeclaration::FontSize(
602 font_size::SpecifiedValue::from_html_size(font_size as u8)
603 )
604 ))
605 }
606
607 let cellspacing = if let Some(this) = self.downcast::<HTMLTableElement>() {
608 this.get_cellspacing()
609 } else {
610 None
611 };
612
613 if let Some(cellspacing) = cellspacing {
614 let width_value = specified::Length::from_px(cellspacing as f32);
615 hints.push(from_declaration(
616 shared_lock,
617 PropertyDeclaration::BorderSpacing(
618 Box::new(border_spacing::SpecifiedValue::new(
619 width_value.clone().into(),
620 width_value.into()
621 ))
622 )
623 ));
624 }
625
626
627 let size = if let Some(this) = self.downcast::<HTMLInputElement>() {
628 // FIXME(pcwalton): More use of atoms, please!
629 match (*self.unsafe_get()).get_attr_val_for_layout(&ns!(), &local_name!("type")) {
630 // Not text entry widget
631 Some("hidden") | Some("date") | Some("month") | Some("week") |
632 Some("time") | Some("datetime-local") | Some("number") | Some("range") |
633 Some("color") | Some("checkbox") | Some("radio") | Some("file") |
634 Some("submit") | Some("image") | Some("reset") | Some("button") => {
635 None
636 },
637 // Others
638 _ => {
639 match this.size_for_layout() {
640 0 => None,
641 s => Some(s as i32),
642 }
643 },
644 }
645 } else {
646 None
647 };
648
649 if let Some(size) = size {
650 let value = specified::NoCalcLength::ServoCharacterWidth(specified::CharacterWidth(size));
651 hints.push(from_declaration(
652 shared_lock,
653 PropertyDeclaration::Width(
654 specified::LengthOrPercentageOrAuto::Length(value))));
655 }
656
657 let width = if let Some(this) = self.downcast::<HTMLIFrameElement>() {
658 this.get_width()
659 } else if let Some(this) = self.downcast::<HTMLImageElement>() {
660 this.get_width()
661 } else if let Some(this) = self.downcast::<HTMLTableElement>() {
662 this.get_width()
663 } else if let Some(this) = self.downcast::<HTMLTableCellElement>() {
664 this.get_width()
665 } else if let Some(this) = self.downcast::<HTMLHRElement>() {
666 // https://html.spec.whatwg.org/multipage/#the-hr-element-2:attr-hr-width
667 this.get_width()
668 } else if let Some(this) = self.downcast::<HTMLCanvasElement>() {
669 this.get_width()
670 } else {
671 LengthOrPercentageOrAuto::Auto
672 };
673
674 // FIXME(emilio): Use from_computed value here and below.
675 match width {
676 LengthOrPercentageOrAuto::Auto => {}
677 LengthOrPercentageOrAuto::Percentage(percentage) => {
678 let width_value =
679 specified::LengthOrPercentageOrAuto::Percentage(computed::Percentage(percentage));
680 hints.push(from_declaration(
681 shared_lock,
682 PropertyDeclaration::Width(width_value)));
683 }
684 LengthOrPercentageOrAuto::Length(length) => {
685 let width_value = specified::LengthOrPercentageOrAuto::Length(
686 specified::NoCalcLength::Absolute(specified::AbsoluteLength::Px(length.to_f32_px())));
687 hints.push(from_declaration(
688 shared_lock,
689 PropertyDeclaration::Width(width_value)));
690 }
691 }
692
693
694 let height = if let Some(this) = self.downcast::<HTMLIFrameElement>() {
695 this.get_height()
696 } else if let Some(this) = self.downcast::<HTMLImageElement>() {
697 this.get_height()
698 } else if let Some(this) = self.downcast::<HTMLCanvasElement>() {
699 this.get_height()
700 } else {
701 LengthOrPercentageOrAuto::Auto
702 };
703
704 match height {
705 LengthOrPercentageOrAuto::Auto => {}
706 LengthOrPercentageOrAuto::Percentage(percentage) => {
707 let height_value =
708 specified::LengthOrPercentageOrAuto::Percentage(computed::Percentage(percentage));
709 hints.push(from_declaration(
710 shared_lock,
711 PropertyDeclaration::Height(height_value)));
712 }
713 LengthOrPercentageOrAuto::Length(length) => {
714 let height_value = specified::LengthOrPercentageOrAuto::Length(
715 specified::NoCalcLength::Absolute(specified::AbsoluteLength::Px(length.to_f32_px())));
716 hints.push(from_declaration(
717 shared_lock,
718 PropertyDeclaration::Height(height_value)));
719 }
720 }
721
722
723 let cols = if let Some(this) = self.downcast::<HTMLTextAreaElement>() {
724 match this.get_cols() {
725 0 => None,
726 c => Some(c as i32),
727 }
728 } else {
729 None
730 };
731
732 if let Some(cols) = cols {
733 // TODO(mttr) ServoCharacterWidth uses the size math for <input type="text">, but
734 // the math for <textarea> is a little different since we need to take
735 // scrollbar size into consideration (but we don't have a scrollbar yet!)
736 //
737 // https://html.spec.whatwg.org/multipage/#textarea-effective-width
738 let value = specified::NoCalcLength::ServoCharacterWidth(specified::CharacterWidth(cols));
739 hints.push(from_declaration(
740 shared_lock,
741 PropertyDeclaration::Width(specified::LengthOrPercentageOrAuto::Length(value))));
742 }
743
744 let rows = if let Some(this) = self.downcast::<HTMLTextAreaElement>() {
745 match this.get_rows() {
746 0 => None,
747 r => Some(r as i32),
748 }
749 } else {
750 None
751 };
752
753 if let Some(rows) = rows {
754 // TODO(mttr) This should take scrollbar size into consideration.
755 //
756 // https://html.spec.whatwg.org/multipage/#textarea-effective-height
757 let value = specified::NoCalcLength::FontRelative(specified::FontRelativeLength::Em(rows as CSSFloat));
758 hints.push(from_declaration(
759 shared_lock,
760 PropertyDeclaration::Height(specified::LengthOrPercentageOrAuto::Length(value))));
761 }
762
763
764 let border = if let Some(this) = self.downcast::<HTMLTableElement>() {
765 this.get_border()
766 } else {
767 None
768 };
769
770 if let Some(border) = border {
771 let width_value = specified::BorderSideWidth::Length(specified::Length::from_px(border as f32));
772 hints.push(from_declaration(
773 shared_lock,
774 PropertyDeclaration::BorderTopWidth(width_value.clone())));
775 hints.push(from_declaration(
776 shared_lock,
777 PropertyDeclaration::BorderLeftWidth(width_value.clone())));
778 hints.push(from_declaration(
779 shared_lock,
780 PropertyDeclaration::BorderBottomWidth(width_value.clone())));
781 hints.push(from_declaration(
782 shared_lock,
783 PropertyDeclaration::BorderRightWidth(width_value)));
784 }
785 }
786
787 #[allow(unsafe_code)]
get_colspan(self) -> u32788 unsafe fn get_colspan(self) -> u32 {
789 if let Some(this) = self.downcast::<HTMLTableCellElement>() {
790 this.get_colspan().unwrap_or(1)
791 } else {
792 // Don't panic since `display` can cause this to be called on arbitrary
793 // elements.
794 1
795 }
796 }
797
798 #[allow(unsafe_code)]
get_rowspan(self) -> u32799 unsafe fn get_rowspan(self) -> u32 {
800 if let Some(this) = self.downcast::<HTMLTableCellElement>() {
801 this.get_rowspan().unwrap_or(1)
802 } else {
803 // Don't panic since `display` can cause this to be called on arbitrary
804 // elements.
805 1
806 }
807 }
808
809 #[inline]
810 #[allow(unsafe_code)]
is_html_element(&self) -> bool811 unsafe fn is_html_element(&self) -> bool {
812 (*self.unsafe_get()).namespace == ns!(html)
813 }
814
815 #[allow(unsafe_code)]
id_attribute(&self) -> *const Option<Atom>816 fn id_attribute(&self) -> *const Option<Atom> {
817 unsafe {
818 (*self.unsafe_get()).id_attribute.borrow_for_layout()
819 }
820 }
821
822 #[allow(unsafe_code)]
style_attribute(&self) -> *const Option<Arc<Locked<PropertyDeclarationBlock>>>823 fn style_attribute(&self) -> *const Option<Arc<Locked<PropertyDeclarationBlock>>> {
824 unsafe {
825 (*self.unsafe_get()).style_attribute.borrow_for_layout()
826 }
827 }
828
829 #[allow(unsafe_code)]
local_name(&self) -> &LocalName830 fn local_name(&self) -> &LocalName {
831 unsafe {
832 &(*self.unsafe_get()).local_name
833 }
834 }
835
836 #[allow(unsafe_code)]
namespace(&self) -> &Namespace837 fn namespace(&self) -> &Namespace {
838 unsafe {
839 &(*self.unsafe_get()).namespace
840 }
841 }
842
843 #[allow(unsafe_code)]
get_lang_for_layout(&self) -> String844 fn get_lang_for_layout(&self) -> String {
845 unsafe {
846 let mut current_node = Some(self.upcast::<Node>());
847 while let Some(node) = current_node {
848 current_node = node.parent_node_ref();
849 match node.downcast::<Element>().map(|el| el.unsafe_get()) {
850 Some(elem) => {
851 if let Some(attr) = (*elem).get_attr_val_for_layout(&ns!(xml), &local_name!("lang")) {
852 return attr.to_owned();
853 }
854 if let Some(attr) = (*elem).get_attr_val_for_layout(&ns!(), &local_name!("lang")) {
855 return attr.to_owned();
856 }
857 }
858 None => continue
859 }
860 }
861 // TODO: Check meta tags for a pragma-set default language
862 // TODO: Check HTTP Content-Language header
863 String::new()
864 }
865 }
866
867 #[inline]
868 #[allow(unsafe_code)]
get_checked_state_for_layout(&self) -> bool869 fn get_checked_state_for_layout(&self) -> bool {
870 // TODO option and menuitem can also have a checked state.
871 match self.downcast::<HTMLInputElement>() {
872 Some(input) => unsafe {
873 input.checked_state_for_layout()
874 },
875 None => false,
876 }
877 }
878
879 #[inline]
880 #[allow(unsafe_code)]
get_indeterminate_state_for_layout(&self) -> bool881 fn get_indeterminate_state_for_layout(&self) -> bool {
882 // TODO progress elements can also be matched with :indeterminate
883 match self.downcast::<HTMLInputElement>() {
884 Some(input) => unsafe {
885 input.indeterminate_state_for_layout()
886 },
887 None => false,
888 }
889 }
890
891 #[inline]
892 #[allow(unsafe_code)]
get_state_for_layout(&self) -> ElementState893 fn get_state_for_layout(&self) -> ElementState {
894 unsafe {
895 (*self.unsafe_get()).state.get()
896 }
897 }
898
899 #[inline]
900 #[allow(unsafe_code)]
insert_selector_flags(&self, flags: ElementSelectorFlags)901 fn insert_selector_flags(&self, flags: ElementSelectorFlags) {
902 debug_assert!(thread_state::get().is_layout());
903 unsafe {
904 let f = &(*self.unsafe_get()).selector_flags;
905 f.set(f.get() | flags);
906 }
907 }
908
909 #[inline]
910 #[allow(unsafe_code)]
has_selector_flags(&self, flags: ElementSelectorFlags) -> bool911 fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool {
912 unsafe {
913 (*self.unsafe_get()).selector_flags.get().contains(flags)
914 }
915 }
916 }
917
918 impl Element {
is_html_element(&self) -> bool919 pub fn is_html_element(&self) -> bool {
920 self.namespace == ns!(html)
921 }
922
html_element_in_html_document(&self) -> bool923 pub fn html_element_in_html_document(&self) -> bool {
924 self.is_html_element() && self.upcast::<Node>().is_in_html_doc()
925 }
926
local_name(&self) -> &LocalName927 pub fn local_name(&self) -> &LocalName {
928 &self.local_name
929 }
930
parsed_name(&self, mut name: DOMString) -> LocalName931 pub fn parsed_name(&self, mut name: DOMString) -> LocalName {
932 if self.html_element_in_html_document() {
933 name.make_ascii_lowercase();
934 }
935 LocalName::from(name)
936 }
937
namespace(&self) -> &Namespace938 pub fn namespace(&self) -> &Namespace {
939 &self.namespace
940 }
941
prefix(&self) -> Ref<Option<Prefix>>942 pub fn prefix(&self) -> Ref<Option<Prefix>> {
943 self.prefix.borrow()
944 }
945
set_prefix(&self, prefix: Option<Prefix>)946 pub fn set_prefix(&self, prefix: Option<Prefix>) {
947 *self.prefix.borrow_mut() = prefix;
948 }
949
attrs(&self) -> Ref<[Dom<Attr>]>950 pub fn attrs(&self) -> Ref<[Dom<Attr>]> {
951 Ref::map(self.attrs.borrow(), |attrs| &**attrs)
952 }
953
954 // Element branch of https://dom.spec.whatwg.org/#locate-a-namespace
locate_namespace(&self, prefix: Option<DOMString>) -> Namespace955 pub fn locate_namespace(&self, prefix: Option<DOMString>) -> Namespace {
956 let prefix = prefix.map(String::from).map(LocalName::from);
957
958 let inclusive_ancestor_elements =
959 self.upcast::<Node>()
960 .inclusive_ancestors()
961 .filter_map(DomRoot::downcast::<Self>);
962
963 // Steps 3-4.
964 for element in inclusive_ancestor_elements {
965 // Step 1.
966 if element.namespace() != &ns!() &&
967 element.prefix().as_ref().map(|p| &**p) == prefix.as_ref().map(|p| &**p)
968 {
969 return element.namespace().clone();
970 }
971
972 // Step 2.
973 let attr = ref_filter_map(self.attrs(), |attrs| {
974 attrs.iter().find(|attr| {
975 if attr.namespace() != &ns!(xmlns) {
976 return false;
977 }
978 match (attr.prefix(), prefix.as_ref()) {
979 (Some(&namespace_prefix!("xmlns")), Some(prefix)) => {
980 attr.local_name() == prefix
981 },
982 (None, None) => attr.local_name() == &local_name!("xmlns"),
983 _ => false,
984 }
985 })
986 });
987
988 if let Some(attr) = attr {
989 return (**attr.value()).into();
990 }
991 }
992
993 ns!()
994 }
995
style_attribute(&self) -> &DomRefCell<Option<Arc<Locked<PropertyDeclarationBlock>>>>996 pub fn style_attribute(&self) -> &DomRefCell<Option<Arc<Locked<PropertyDeclarationBlock>>>> {
997 &self.style_attribute
998 }
999
summarize(&self) -> Vec<AttrInfo>1000 pub fn summarize(&self) -> Vec<AttrInfo> {
1001 self.attrs.borrow().iter()
1002 .map(|attr| attr.summarize())
1003 .collect()
1004 }
1005
is_void(&self) -> bool1006 pub fn is_void(&self) -> bool {
1007 if self.namespace != ns!(html) {
1008 return false
1009 }
1010 match self.local_name {
1011 /* List of void elements from
1012 https://html.spec.whatwg.org/multipage/#html-fragment-serialisation-algorithm */
1013
1014 local_name!("area") | local_name!("base") | local_name!("basefont") |
1015 local_name!("bgsound") | local_name!("br") |
1016 local_name!("col") | local_name!("embed") | local_name!("frame") |
1017 local_name!("hr") | local_name!("img") |
1018 local_name!("input") | local_name!("keygen") | local_name!("link") |
1019 local_name!("menuitem") | local_name!("meta") |
1020 local_name!("param") | local_name!("source") | local_name!("track") |
1021 local_name!("wbr") => true,
1022 _ => false
1023 }
1024 }
1025
serialize(&self, traversal_scope: TraversalScope) -> Fallible<DOMString>1026 pub fn serialize(&self, traversal_scope: TraversalScope) -> Fallible<DOMString> {
1027 let mut writer = vec![];
1028 match serialize(&mut writer,
1029 &self.upcast::<Node>(),
1030 SerializeOpts {
1031 traversal_scope: traversal_scope,
1032 ..Default::default()
1033 }) {
1034 // FIXME(ajeffrey): Directly convert UTF8 to DOMString
1035 Ok(()) => Ok(DOMString::from(String::from_utf8(writer).unwrap())),
1036 Err(_) => panic!("Cannot serialize element"),
1037 }
1038 }
1039
xmlSerialize(&self, traversal_scope: XmlTraversalScope) -> Fallible<DOMString>1040 pub fn xmlSerialize(&self, traversal_scope: XmlTraversalScope) -> Fallible<DOMString> {
1041 let mut writer = vec![];
1042 match xmlSerialize::serialize(&mut writer,
1043 &self.upcast::<Node>(),
1044 XmlSerializeOpts {
1045 traversal_scope: traversal_scope,
1046 ..Default::default()
1047 }) {
1048 Ok(()) => Ok(DOMString::from(String::from_utf8(writer).unwrap())),
1049 Err(_) => panic!("Cannot serialize element"),
1050 }
1051 }
1052
root_element(&self) -> DomRoot<Element>1053 pub fn root_element(&self) -> DomRoot<Element> {
1054 if self.node.is_in_doc() {
1055 self.upcast::<Node>()
1056 .owner_doc()
1057 .GetDocumentElement()
1058 .unwrap()
1059 } else {
1060 self.upcast::<Node>()
1061 .inclusive_ancestors()
1062 .filter_map(DomRoot::downcast)
1063 .last()
1064 .expect("We know inclusive_ancestors will return `self` which is an element")
1065 }
1066 }
1067
1068 // https://dom.spec.whatwg.org/#locate-a-namespace-prefix
lookup_prefix(&self, namespace: Namespace) -> Option<DOMString>1069 pub fn lookup_prefix(&self, namespace: Namespace) -> Option<DOMString> {
1070 for node in self.upcast::<Node>().inclusive_ancestors() {
1071 let element = node.downcast::<Element>()?;
1072 // Step 1.
1073 if *element.namespace() == namespace {
1074 if let Some(prefix) = element.GetPrefix() {
1075 return Some(prefix);
1076 }
1077 }
1078
1079 // Step 2.
1080 for attr in element.attrs.borrow().iter() {
1081 if attr.prefix() == Some(&namespace_prefix!("xmlns")) &&
1082 **attr.value() == *namespace {
1083 return Some(attr.LocalName());
1084 }
1085 }
1086 }
1087 None
1088 }
1089
is_focusable_area(&self) -> bool1090 pub fn is_focusable_area(&self) -> bool {
1091 if self.is_actually_disabled() {
1092 return false;
1093 }
1094 // TODO: Check whether the element is being rendered (i.e. not hidden).
1095 let node = self.upcast::<Node>();
1096 if node.get_flag(NodeFlags::SEQUENTIALLY_FOCUSABLE) {
1097 return true;
1098 }
1099 // https://html.spec.whatwg.org/multipage/#specially-focusable
1100 match node.type_id() {
1101 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAnchorElement)) |
1102 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement)) |
1103 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSelectElement)) |
1104 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement)) => {
1105 true
1106 }
1107 _ => false,
1108 }
1109 }
1110
is_actually_disabled(&self) -> bool1111 pub fn is_actually_disabled(&self) -> bool {
1112 let node = self.upcast::<Node>();
1113 match node.type_id() {
1114 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLButtonElement)) |
1115 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement)) |
1116 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSelectElement)) |
1117 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement)) |
1118 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLOptionElement)) => {
1119 self.disabled_state()
1120 }
1121 // TODO:
1122 // an optgroup element that has a disabled attribute
1123 // a menuitem element that has a disabled attribute
1124 // a fieldset element that is a disabled fieldset
1125 _ => false,
1126 }
1127 }
1128
push_new_attribute(&self, local_name: LocalName, value: AttrValue, name: LocalName, namespace: Namespace, prefix: Option<Prefix>)1129 pub fn push_new_attribute(&self,
1130 local_name: LocalName,
1131 value: AttrValue,
1132 name: LocalName,
1133 namespace: Namespace,
1134 prefix: Option<Prefix>) {
1135 let window = window_from_node(self);
1136 let attr = Attr::new(&window,
1137 local_name,
1138 value,
1139 name,
1140 namespace,
1141 prefix,
1142 Some(self));
1143 self.push_attribute(&attr);
1144 }
1145
push_attribute(&self, attr: &Attr)1146 pub fn push_attribute(&self, attr: &Attr) {
1147 let name = attr.local_name().clone();
1148 let namespace = attr.namespace().clone();
1149 let value = DOMString::from(&**attr.value());
1150 let mutation = Mutation::Attribute {
1151 name: name.clone(),
1152 namespace: namespace.clone(),
1153 old_value: value.clone(),
1154 };
1155
1156 MutationObserver::queue_a_mutation_record(&self.node, mutation);
1157
1158 if self.get_custom_element_definition().is_some() {
1159 let reaction = CallbackReaction::AttributeChanged(name, None, Some(value), namespace);
1160 ScriptThread::enqueue_callback_reaction(self, reaction, None);
1161 }
1162
1163 assert!(attr.GetOwnerElement().r() == Some(self));
1164 self.will_mutate_attr(attr);
1165 self.attrs.borrow_mut().push(Dom::from_ref(attr));
1166 if attr.namespace() == &ns!() {
1167 vtable_for(self.upcast()).attribute_mutated(attr, AttributeMutation::Set(None));
1168 }
1169 }
1170
get_attribute(&self, namespace: &Namespace, local_name: &LocalName) -> Option<DomRoot<Attr>>1171 pub fn get_attribute(&self, namespace: &Namespace, local_name: &LocalName) -> Option<DomRoot<Attr>> {
1172 self.attrs
1173 .borrow()
1174 .iter()
1175 .find(|attr| attr.local_name() == local_name && attr.namespace() == namespace)
1176 .map(|js| DomRoot::from_ref(&**js))
1177 }
1178
1179 // https://dom.spec.whatwg.org/#concept-element-attributes-get-by-name
get_attribute_by_name(&self, name: DOMString) -> Option<DomRoot<Attr>>1180 pub fn get_attribute_by_name(&self, name: DOMString) -> Option<DomRoot<Attr>> {
1181 let name = &self.parsed_name(name);
1182 self.attrs.borrow().iter().find(|a| a.name() == name).map(|js| DomRoot::from_ref(&**js))
1183 }
1184
set_attribute_from_parser(&self, qname: QualName, value: DOMString, prefix: Option<Prefix>)1185 pub fn set_attribute_from_parser(&self,
1186 qname: QualName,
1187 value: DOMString,
1188 prefix: Option<Prefix>) {
1189 // Don't set if the attribute already exists, so we can handle add_attrs_if_missing
1190 if self.attrs
1191 .borrow()
1192 .iter()
1193 .any(|a| *a.local_name() == qname.local && *a.namespace() == qname.ns) {
1194 return;
1195 }
1196
1197 let name = match prefix {
1198 None => qname.local.clone(),
1199 Some(ref prefix) => {
1200 let name = format!("{}:{}", &**prefix, &*qname.local);
1201 LocalName::from(name)
1202 },
1203 };
1204 let value = self.parse_attribute(&qname.ns, &qname.local, value);
1205 self.push_new_attribute(qname.local, value, name, qname.ns, prefix);
1206 }
1207
set_attribute(&self, name: &LocalName, value: AttrValue)1208 pub fn set_attribute(&self, name: &LocalName, value: AttrValue) {
1209 assert!(name == &name.to_ascii_lowercase());
1210 assert!(!name.contains(":"));
1211
1212 self.set_first_matching_attribute(name.clone(),
1213 value,
1214 name.clone(),
1215 ns!(),
1216 None,
1217 |attr| attr.local_name() == name);
1218 }
1219
1220 // https://html.spec.whatwg.org/multipage/#attr-data-*
set_custom_attribute(&self, name: DOMString, value: DOMString) -> ErrorResult1221 pub fn set_custom_attribute(&self, name: DOMString, value: DOMString) -> ErrorResult {
1222 // Step 1.
1223 if let InvalidXMLName = xml_name_type(&name) {
1224 return Err(Error::InvalidCharacter);
1225 }
1226
1227 // Steps 2-5.
1228 let name = LocalName::from(name);
1229 let value = self.parse_attribute(&ns!(), &name, value);
1230 self.set_first_matching_attribute(name.clone(),
1231 value,
1232 name.clone(),
1233 ns!(),
1234 None,
1235 |attr| {
1236 *attr.name() == name && *attr.namespace() == ns!()
1237 });
1238 Ok(())
1239 }
1240
set_first_matching_attribute<F>(&self, local_name: LocalName, value: AttrValue, name: LocalName, namespace: Namespace, prefix: Option<Prefix>, find: F) where F: Fn(&Attr) -> bool1241 fn set_first_matching_attribute<F>(&self,
1242 local_name: LocalName,
1243 value: AttrValue,
1244 name: LocalName,
1245 namespace: Namespace,
1246 prefix: Option<Prefix>,
1247 find: F)
1248 where F: Fn(&Attr) -> bool
1249 {
1250 let attr = self.attrs
1251 .borrow()
1252 .iter()
1253 .find(|attr| find(&attr))
1254 .map(|js| DomRoot::from_ref(&**js));
1255 if let Some(attr) = attr {
1256 attr.set_value(value, self);
1257 } else {
1258 self.push_new_attribute(local_name, value, name, namespace, prefix);
1259 };
1260 }
1261
parse_attribute(&self, namespace: &Namespace, local_name: &LocalName, value: DOMString) -> AttrValue1262 pub fn parse_attribute(&self,
1263 namespace: &Namespace,
1264 local_name: &LocalName,
1265 value: DOMString)
1266 -> AttrValue {
1267 if *namespace == ns!() {
1268 vtable_for(self.upcast()).parse_plain_attribute(local_name, value)
1269 } else {
1270 AttrValue::String(value.into())
1271 }
1272 }
1273
remove_attribute(&self, namespace: &Namespace, local_name: &LocalName) -> Option<DomRoot<Attr>>1274 pub fn remove_attribute(&self, namespace: &Namespace, local_name: &LocalName) -> Option<DomRoot<Attr>> {
1275 self.remove_first_matching_attribute(|attr| {
1276 attr.namespace() == namespace && attr.local_name() == local_name
1277 })
1278 }
1279
remove_attribute_by_name(&self, name: &LocalName) -> Option<DomRoot<Attr>>1280 pub fn remove_attribute_by_name(&self, name: &LocalName) -> Option<DomRoot<Attr>> {
1281 self.remove_first_matching_attribute(|attr| attr.name() == name)
1282 }
1283
remove_first_matching_attribute<F>(&self, find: F) -> Option<DomRoot<Attr>> where F: Fn(&Attr) -> bool1284 fn remove_first_matching_attribute<F>(&self, find: F) -> Option<DomRoot<Attr>>
1285 where F: Fn(&Attr) -> bool {
1286 let idx = self.attrs.borrow().iter().position(|attr| find(&attr));
1287 idx.map(|idx| {
1288 let attr = DomRoot::from_ref(&*(*self.attrs.borrow())[idx]);
1289 self.will_mutate_attr(&attr);
1290
1291 let name = attr.local_name().clone();
1292 let namespace = attr.namespace().clone();
1293 let old_value = DOMString::from(&**attr.value());
1294 let mutation = Mutation::Attribute {
1295 name: name.clone(),
1296 namespace: namespace.clone(),
1297 old_value: old_value.clone(),
1298 };
1299
1300 MutationObserver::queue_a_mutation_record(&self.node, mutation);
1301
1302 let reaction = CallbackReaction::AttributeChanged(name, Some(old_value), None, namespace);
1303 ScriptThread::enqueue_callback_reaction(self, reaction, None);
1304
1305 self.attrs.borrow_mut().remove(idx);
1306 attr.set_owner(None);
1307 if attr.namespace() == &ns!() {
1308 vtable_for(self.upcast()).attribute_mutated(&attr, AttributeMutation::Removed);
1309 }
1310 attr
1311 })
1312 }
1313
has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool1314 pub fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
1315 self.get_attribute(&ns!(), &local_name!("class")).map_or(false, |attr| {
1316 attr.value().as_tokens().iter().any(|atom| case_sensitivity.eq_atom(name, atom))
1317 })
1318 }
1319
set_atomic_attribute(&self, local_name: &LocalName, value: DOMString)1320 pub fn set_atomic_attribute(&self, local_name: &LocalName, value: DOMString) {
1321 assert!(*local_name == local_name.to_ascii_lowercase());
1322 let value = AttrValue::from_atomic(value.into());
1323 self.set_attribute(local_name, value);
1324 }
1325
has_attribute(&self, local_name: &LocalName) -> bool1326 pub fn has_attribute(&self, local_name: &LocalName) -> bool {
1327 assert!(local_name.bytes().all(|b| b.to_ascii_lowercase() == b));
1328 self.attrs
1329 .borrow()
1330 .iter()
1331 .any(|attr| attr.local_name() == local_name && attr.namespace() == &ns!())
1332 }
1333
set_bool_attribute(&self, local_name: &LocalName, value: bool)1334 pub fn set_bool_attribute(&self, local_name: &LocalName, value: bool) {
1335 if self.has_attribute(local_name) == value {
1336 return;
1337 }
1338 if value {
1339 self.set_string_attribute(local_name, DOMString::new());
1340 } else {
1341 self.remove_attribute(&ns!(), local_name);
1342 }
1343 }
1344
get_url_attribute(&self, local_name: &LocalName) -> DOMString1345 pub fn get_url_attribute(&self, local_name: &LocalName) -> DOMString {
1346 assert!(*local_name == local_name.to_ascii_lowercase());
1347 let attr = match self.get_attribute(&ns!(), local_name) {
1348 Some(attr) => attr,
1349 None => return DOMString::new(),
1350 };
1351 let value = &**attr.value();
1352 // XXXManishearth this doesn't handle `javascript:` urls properly
1353 let base = document_from_node(self).base_url();
1354 let value = base.join(value)
1355 .map(|parsed| parsed.into_string())
1356 .unwrap_or_else(|_| value.to_owned());
1357 DOMString::from(value)
1358 }
1359
get_string_attribute(&self, local_name: &LocalName) -> DOMString1360 pub fn get_string_attribute(&self, local_name: &LocalName) -> DOMString {
1361 match self.get_attribute(&ns!(), local_name) {
1362 Some(x) => x.Value(),
1363 None => DOMString::new(),
1364 }
1365 }
1366
set_string_attribute(&self, local_name: &LocalName, value: DOMString)1367 pub fn set_string_attribute(&self, local_name: &LocalName, value: DOMString) {
1368 assert!(*local_name == local_name.to_ascii_lowercase());
1369 self.set_attribute(local_name, AttrValue::String(value.into()));
1370 }
1371
get_tokenlist_attribute(&self, local_name: &LocalName) -> Vec<Atom>1372 pub fn get_tokenlist_attribute(&self, local_name: &LocalName) -> Vec<Atom> {
1373 self.get_attribute(&ns!(), local_name).map(|attr| {
1374 attr.value()
1375 .as_tokens()
1376 .to_vec()
1377 }).unwrap_or(vec!())
1378 }
1379
set_tokenlist_attribute(&self, local_name: &LocalName, value: DOMString)1380 pub fn set_tokenlist_attribute(&self, local_name: &LocalName, value: DOMString) {
1381 assert!(*local_name == local_name.to_ascii_lowercase());
1382 self.set_attribute(local_name,
1383 AttrValue::from_serialized_tokenlist(value.into()));
1384 }
1385
set_atomic_tokenlist_attribute(&self, local_name: &LocalName, tokens: Vec<Atom>)1386 pub fn set_atomic_tokenlist_attribute(&self, local_name: &LocalName, tokens: Vec<Atom>) {
1387 assert!(*local_name == local_name.to_ascii_lowercase());
1388 self.set_attribute(local_name, AttrValue::from_atomic_tokens(tokens));
1389 }
1390
get_int_attribute(&self, local_name: &LocalName, default: i32) -> i321391 pub fn get_int_attribute(&self, local_name: &LocalName, default: i32) -> i32 {
1392 // TODO: Is this assert necessary?
1393 assert!(local_name.chars().all(|ch| {
1394 !ch.is_ascii() || ch.to_ascii_lowercase() == ch
1395 }));
1396 let attribute = self.get_attribute(&ns!(), local_name);
1397
1398 match attribute {
1399 Some(ref attribute) => {
1400 match *attribute.value() {
1401 AttrValue::Int(_, value) => value,
1402 _ => panic!("Expected an AttrValue::Int: \
1403 implement parse_plain_attribute"),
1404 }
1405 }
1406 None => default,
1407 }
1408 }
1409
set_int_attribute(&self, local_name: &LocalName, value: i32)1410 pub fn set_int_attribute(&self, local_name: &LocalName, value: i32) {
1411 assert!(*local_name == local_name.to_ascii_lowercase());
1412 self.set_attribute(local_name, AttrValue::Int(value.to_string(), value));
1413 }
1414
get_uint_attribute(&self, local_name: &LocalName, default: u32) -> u321415 pub fn get_uint_attribute(&self, local_name: &LocalName, default: u32) -> u32 {
1416 assert!(local_name.chars().all(|ch| !ch.is_ascii() || ch.to_ascii_lowercase() == ch));
1417 let attribute = self.get_attribute(&ns!(), local_name);
1418 match attribute {
1419 Some(ref attribute) => {
1420 match *attribute.value() {
1421 AttrValue::UInt(_, value) => value,
1422 _ => panic!("Expected an AttrValue::UInt: implement parse_plain_attribute"),
1423 }
1424 }
1425 None => default,
1426 }
1427 }
set_uint_attribute(&self, local_name: &LocalName, value: u32)1428 pub fn set_uint_attribute(&self, local_name: &LocalName, value: u32) {
1429 assert!(*local_name == local_name.to_ascii_lowercase());
1430 self.set_attribute(local_name, AttrValue::UInt(value.to_string(), value));
1431 }
1432
will_mutate_attr(&self, attr: &Attr)1433 pub fn will_mutate_attr(&self, attr: &Attr) {
1434 let node = self.upcast::<Node>();
1435 node.owner_doc().element_attr_will_change(self, attr);
1436 }
1437
1438 // https://dom.spec.whatwg.org/#insert-adjacent
insert_adjacent(&self, where_: AdjacentPosition, node: &Node) -> Fallible<Option<DomRoot<Node>>>1439 pub fn insert_adjacent(&self, where_: AdjacentPosition, node: &Node)
1440 -> Fallible<Option<DomRoot<Node>>> {
1441 let self_node = self.upcast::<Node>();
1442 match where_ {
1443 AdjacentPosition::BeforeBegin => {
1444 if let Some(parent) = self_node.GetParentNode() {
1445 Node::pre_insert(node, &parent, Some(self_node)).map(Some)
1446 } else {
1447 Ok(None)
1448 }
1449 }
1450 AdjacentPosition::AfterBegin => {
1451 Node::pre_insert(node, &self_node, self_node.GetFirstChild().r()).map(Some)
1452 }
1453 AdjacentPosition::BeforeEnd => {
1454 Node::pre_insert(node, &self_node, None).map(Some)
1455 }
1456 AdjacentPosition::AfterEnd => {
1457 if let Some(parent) = self_node.GetParentNode() {
1458 Node::pre_insert(node, &parent, self_node.GetNextSibling().r()).map(Some)
1459 } else {
1460 Ok(None)
1461 }
1462 }
1463 }
1464 }
1465
1466 // https://drafts.csswg.org/cssom-view/#dom-element-scroll
scroll(&self, x_: f64, y_: f64, behavior: ScrollBehavior)1467 pub fn scroll(&self, x_: f64, y_: f64, behavior: ScrollBehavior) {
1468 // Step 1.2 or 2.3
1469 let x = if x_.is_finite() { x_ } else { 0.0f64 };
1470 let y = if y_.is_finite() { y_ } else { 0.0f64 };
1471
1472 let node = self.upcast::<Node>();
1473
1474 // Step 3
1475 let doc = node.owner_doc();
1476
1477 // Step 4
1478 if !doc.is_fully_active() {
1479 return;
1480 }
1481
1482 // Step 5
1483 let win = match doc.GetDefaultView() {
1484 None => return,
1485 Some(win) => win,
1486 };
1487
1488 // Step 7
1489 if *self.root_element() == *self {
1490 if doc.quirks_mode() != QuirksMode::Quirks {
1491 win.scroll(x, y, behavior);
1492 }
1493
1494 return;
1495 }
1496
1497 // Step 9
1498 if doc.GetBody().r() == self.downcast::<HTMLElement>() &&
1499 doc.quirks_mode() == QuirksMode::Quirks &&
1500 !self.potentially_scrollable() {
1501 win.scroll(x, y, behavior);
1502 return;
1503 }
1504
1505 // Step 10
1506 if !self.has_css_layout_box() ||
1507 !self.has_scrolling_box() ||
1508 !self.has_overflow()
1509 {
1510 return;
1511 }
1512
1513 // Step 11
1514 win.scroll_node(node, x, y, behavior);
1515 }
1516
1517 // https://w3c.github.io/DOM-Parsing/#parsing
parse_fragment(&self, markup: DOMString) -> Fallible<DomRoot<DocumentFragment>>1518 pub fn parse_fragment(&self, markup: DOMString) -> Fallible<DomRoot<DocumentFragment>> {
1519 // Steps 1-2.
1520 let context_document = document_from_node(self);
1521 // TODO(#11995): XML case.
1522 let new_children = ServoParser::parse_html_fragment(self, markup);
1523 // Step 3.
1524 let fragment = DocumentFragment::new(&context_document);
1525 // Step 4.
1526 for child in new_children {
1527 fragment.upcast::<Node>().AppendChild(&child).unwrap();
1528 }
1529 // Step 5.
1530 Ok(fragment)
1531 }
1532
fragment_parsing_context(owner_doc: &Document, element: Option<&Self>) -> DomRoot<Self>1533 pub fn fragment_parsing_context(owner_doc: &Document, element: Option<&Self>) -> DomRoot<Self> {
1534 match element {
1535 Some(elem) if elem.local_name() != &local_name!("html") || !elem.html_element_in_html_document() => {
1536 DomRoot::from_ref(elem)
1537 },
1538 _ => {
1539 DomRoot::upcast(HTMLBodyElement::new(local_name!("body"), None, owner_doc))
1540 }
1541 }
1542 }
1543
1544 // https://fullscreen.spec.whatwg.org/#fullscreen-element-ready-check
fullscreen_element_ready_check(&self) -> bool1545 pub fn fullscreen_element_ready_check(&self) -> bool {
1546 if !self.is_connected() {
1547 return false
1548 }
1549 let document = document_from_node(self);
1550 document.get_allow_fullscreen()
1551 }
1552
1553 // https://html.spec.whatwg.org/multipage/#home-subtree
is_in_same_home_subtree<T>(&self, other: &T) -> bool where T: DerivedFrom<Element> + DomObject1554 pub fn is_in_same_home_subtree<T>(&self, other: &T) -> bool
1555 where T: DerivedFrom<Element> + DomObject
1556 {
1557 let other = other.upcast::<Element>();
1558 self.root_element() == other.root_element()
1559 }
1560 }
1561
1562 impl ElementMethods for Element {
1563 // https://dom.spec.whatwg.org/#dom-element-namespaceuri
GetNamespaceURI(&self) -> Option<DOMString>1564 fn GetNamespaceURI(&self) -> Option<DOMString> {
1565 Node::namespace_to_string(self.namespace.clone())
1566 }
1567
1568 // https://dom.spec.whatwg.org/#dom-element-localname
LocalName(&self) -> DOMString1569 fn LocalName(&self) -> DOMString {
1570 // FIXME(ajeffrey): Convert directly from LocalName to DOMString
1571 DOMString::from(&*self.local_name)
1572 }
1573
1574 // https://dom.spec.whatwg.org/#dom-element-prefix
GetPrefix(&self) -> Option<DOMString>1575 fn GetPrefix(&self) -> Option<DOMString> {
1576 self.prefix.borrow().as_ref().map(|p| DOMString::from(&**p))
1577 }
1578
1579 // https://dom.spec.whatwg.org/#dom-element-tagname
TagName(&self) -> DOMString1580 fn TagName(&self) -> DOMString {
1581 let name = self.tag_name.or_init(|| {
1582 let qualified_name = match *self.prefix.borrow() {
1583 Some(ref prefix) => {
1584 Cow::Owned(format!("{}:{}", &**prefix, &*self.local_name))
1585 },
1586 None => Cow::Borrowed(&*self.local_name)
1587 };
1588 if self.html_element_in_html_document() {
1589 LocalName::from(qualified_name.to_ascii_uppercase())
1590 } else {
1591 LocalName::from(qualified_name)
1592 }
1593 });
1594 DOMString::from(&*name)
1595 }
1596
1597 // https://dom.spec.whatwg.org/#dom-element-id
Id(&self) -> DOMString1598 fn Id(&self) -> DOMString {
1599 self.get_string_attribute(&local_name!("id"))
1600 }
1601
1602 // https://dom.spec.whatwg.org/#dom-element-id
SetId(&self, id: DOMString)1603 fn SetId(&self, id: DOMString) {
1604 self.set_atomic_attribute(&local_name!("id"), id);
1605 }
1606
1607 // https://dom.spec.whatwg.org/#dom-element-classname
ClassName(&self) -> DOMString1608 fn ClassName(&self) -> DOMString {
1609 self.get_string_attribute(&local_name!("class"))
1610 }
1611
1612 // https://dom.spec.whatwg.org/#dom-element-classname
SetClassName(&self, class: DOMString)1613 fn SetClassName(&self, class: DOMString) {
1614 self.set_tokenlist_attribute(&local_name!("class"), class);
1615 }
1616
1617 // https://dom.spec.whatwg.org/#dom-element-classlist
ClassList(&self) -> DomRoot<DOMTokenList>1618 fn ClassList(&self) -> DomRoot<DOMTokenList> {
1619 self.class_list.or_init(|| DOMTokenList::new(self, &local_name!("class")))
1620 }
1621
1622 // https://dom.spec.whatwg.org/#dom-element-attributes
Attributes(&self) -> DomRoot<NamedNodeMap>1623 fn Attributes(&self) -> DomRoot<NamedNodeMap> {
1624 self.attr_list.or_init(|| NamedNodeMap::new(&window_from_node(self), self))
1625 }
1626
1627 // https://dom.spec.whatwg.org/#dom-element-hasattributes
HasAttributes(&self) -> bool1628 fn HasAttributes(&self) -> bool {
1629 !self.attrs.borrow().is_empty()
1630 }
1631
1632 // https://dom.spec.whatwg.org/#dom-element-getattributenames
GetAttributeNames(&self) -> Vec<DOMString>1633 fn GetAttributeNames(&self) -> Vec<DOMString> {
1634 self.attrs.borrow().iter().map(|attr| attr.Name()).collect()
1635 }
1636
1637 // https://dom.spec.whatwg.org/#dom-element-getattribute
GetAttribute(&self, name: DOMString) -> Option<DOMString>1638 fn GetAttribute(&self, name: DOMString) -> Option<DOMString> {
1639 self.GetAttributeNode(name)
1640 .map(|s| s.Value())
1641 }
1642
1643 // https://dom.spec.whatwg.org/#dom-element-getattributens
GetAttributeNS(&self, namespace: Option<DOMString>, local_name: DOMString) -> Option<DOMString>1644 fn GetAttributeNS(&self,
1645 namespace: Option<DOMString>,
1646 local_name: DOMString)
1647 -> Option<DOMString> {
1648 self.GetAttributeNodeNS(namespace, local_name)
1649 .map(|attr| attr.Value())
1650 }
1651
1652 // https://dom.spec.whatwg.org/#dom-element-getattributenode
GetAttributeNode(&self, name: DOMString) -> Option<DomRoot<Attr>>1653 fn GetAttributeNode(&self, name: DOMString) -> Option<DomRoot<Attr>> {
1654 self.get_attribute_by_name(name)
1655 }
1656
1657 // https://dom.spec.whatwg.org/#dom-element-getattributenodens
GetAttributeNodeNS(&self, namespace: Option<DOMString>, local_name: DOMString) -> Option<DomRoot<Attr>>1658 fn GetAttributeNodeNS(&self,
1659 namespace: Option<DOMString>,
1660 local_name: DOMString)
1661 -> Option<DomRoot<Attr>> {
1662 let namespace = &namespace_from_domstring(namespace);
1663 self.get_attribute(namespace, &LocalName::from(local_name))
1664 }
1665
1666 // https://dom.spec.whatwg.org/#dom-element-setattribute
SetAttribute(&self, name: DOMString, value: DOMString) -> ErrorResult1667 fn SetAttribute(&self, name: DOMString, value: DOMString) -> ErrorResult {
1668 // Step 1.
1669 if xml_name_type(&name) == InvalidXMLName {
1670 return Err(Error::InvalidCharacter);
1671 }
1672
1673 // Step 2.
1674 let name = self.parsed_name(name);
1675
1676 // Step 3-5.
1677 let value = self.parse_attribute(&ns!(), &name, value);
1678 self.set_first_matching_attribute(
1679 name.clone(), value, name.clone(), ns!(), None,
1680 |attr| *attr.name() == name);
1681 Ok(())
1682 }
1683
1684 // https://dom.spec.whatwg.org/#dom-element-setattributens
SetAttributeNS(&self, namespace: Option<DOMString>, qualified_name: DOMString, value: DOMString) -> ErrorResult1685 fn SetAttributeNS(&self,
1686 namespace: Option<DOMString>,
1687 qualified_name: DOMString,
1688 value: DOMString) -> ErrorResult {
1689 let (namespace, prefix, local_name) =
1690 validate_and_extract(namespace, &qualified_name)?;
1691 let qualified_name = LocalName::from(qualified_name);
1692 let value = self.parse_attribute(&namespace, &local_name, value);
1693 self.set_first_matching_attribute(
1694 local_name.clone(), value, qualified_name, namespace.clone(), prefix,
1695 |attr| *attr.local_name() == local_name && *attr.namespace() == namespace);
1696 Ok(())
1697 }
1698
1699 // https://dom.spec.whatwg.org/#dom-element-setattributenode
SetAttributeNode(&self, attr: &Attr) -> Fallible<Option<DomRoot<Attr>>>1700 fn SetAttributeNode(&self, attr: &Attr) -> Fallible<Option<DomRoot<Attr>>> {
1701 // Step 1.
1702 if let Some(owner) = attr.GetOwnerElement() {
1703 if &*owner != self {
1704 return Err(Error::InUseAttribute);
1705 }
1706 }
1707
1708 let vtable = vtable_for(self.upcast());
1709
1710 // This ensures that the attribute is of the expected kind for this
1711 // specific element. This is inefficient and should probably be done
1712 // differently.
1713 attr.swap_value(
1714 &mut vtable.parse_plain_attribute(attr.local_name(), attr.Value()),
1715 );
1716
1717 // Step 2.
1718 let position = self.attrs.borrow().iter().position(|old_attr| {
1719 attr.namespace() == old_attr.namespace() && attr.local_name() == old_attr.local_name()
1720 });
1721
1722 if let Some(position) = position {
1723 let old_attr = DomRoot::from_ref(&*self.attrs.borrow()[position]);
1724
1725 // Step 3.
1726 if &*old_attr == attr {
1727 return Ok(Some(DomRoot::from_ref(attr)));
1728 }
1729
1730 // Step 4.
1731 if self.get_custom_element_definition().is_some() {
1732 let old_name = old_attr.local_name().clone();
1733 let old_value = DOMString::from(&**old_attr.value());
1734 let new_value = DOMString::from(&**attr.value());
1735 let namespace = old_attr.namespace().clone();
1736 let reaction = CallbackReaction::AttributeChanged(old_name, Some(old_value),
1737 Some(new_value), namespace);
1738 ScriptThread::enqueue_callback_reaction(self, reaction, None);
1739 }
1740 self.will_mutate_attr(attr);
1741 attr.set_owner(Some(self));
1742 self.attrs.borrow_mut()[position] = Dom::from_ref(attr);
1743 old_attr.set_owner(None);
1744 if attr.namespace() == &ns!() {
1745 vtable.attribute_mutated(
1746 &attr, AttributeMutation::Set(Some(&old_attr.value())));
1747 }
1748
1749 // Step 6.
1750 Ok(Some(old_attr))
1751 } else {
1752 // Step 5.
1753 attr.set_owner(Some(self));
1754 self.push_attribute(attr);
1755
1756 // Step 6.
1757 Ok(None)
1758 }
1759 }
1760
1761 // https://dom.spec.whatwg.org/#dom-element-setattributenodens
SetAttributeNodeNS(&self, attr: &Attr) -> Fallible<Option<DomRoot<Attr>>>1762 fn SetAttributeNodeNS(&self, attr: &Attr) -> Fallible<Option<DomRoot<Attr>>> {
1763 self.SetAttributeNode(attr)
1764 }
1765
1766 // https://dom.spec.whatwg.org/#dom-element-removeattribute
RemoveAttribute(&self, name: DOMString)1767 fn RemoveAttribute(&self, name: DOMString) {
1768 let name = self.parsed_name(name);
1769 self.remove_attribute_by_name(&name);
1770 }
1771
1772 // https://dom.spec.whatwg.org/#dom-element-removeattributens
RemoveAttributeNS(&self, namespace: Option<DOMString>, local_name: DOMString)1773 fn RemoveAttributeNS(&self, namespace: Option<DOMString>, local_name: DOMString) {
1774 let namespace = namespace_from_domstring(namespace);
1775 let local_name = LocalName::from(local_name);
1776 self.remove_attribute(&namespace, &local_name);
1777 }
1778
1779 // https://dom.spec.whatwg.org/#dom-element-removeattributenode
RemoveAttributeNode(&self, attr: &Attr) -> Fallible<DomRoot<Attr>>1780 fn RemoveAttributeNode(&self, attr: &Attr) -> Fallible<DomRoot<Attr>> {
1781 self.remove_first_matching_attribute(|a| a == attr)
1782 .ok_or(Error::NotFound)
1783 }
1784
1785 // https://dom.spec.whatwg.org/#dom-element-hasattribute
HasAttribute(&self, name: DOMString) -> bool1786 fn HasAttribute(&self, name: DOMString) -> bool {
1787 self.GetAttribute(name).is_some()
1788 }
1789
1790 // https://dom.spec.whatwg.org/#dom-element-hasattributens
HasAttributeNS(&self, namespace: Option<DOMString>, local_name: DOMString) -> bool1791 fn HasAttributeNS(&self, namespace: Option<DOMString>, local_name: DOMString) -> bool {
1792 self.GetAttributeNS(namespace, local_name).is_some()
1793 }
1794
1795 // https://dom.spec.whatwg.org/#dom-element-getelementsbytagname
GetElementsByTagName(&self, localname: DOMString) -> DomRoot<HTMLCollection>1796 fn GetElementsByTagName(&self, localname: DOMString) -> DomRoot<HTMLCollection> {
1797 let window = window_from_node(self);
1798 HTMLCollection::by_qualified_name(&window, self.upcast(), LocalName::from(&*localname))
1799 }
1800
1801 // https://dom.spec.whatwg.org/#dom-element-getelementsbytagnamens
GetElementsByTagNameNS(&self, maybe_ns: Option<DOMString>, localname: DOMString) -> DomRoot<HTMLCollection>1802 fn GetElementsByTagNameNS(&self,
1803 maybe_ns: Option<DOMString>,
1804 localname: DOMString)
1805 -> DomRoot<HTMLCollection> {
1806 let window = window_from_node(self);
1807 HTMLCollection::by_tag_name_ns(&window, self.upcast(), localname, maybe_ns)
1808 }
1809
1810 // https://dom.spec.whatwg.org/#dom-element-getelementsbyclassname
GetElementsByClassName(&self, classes: DOMString) -> DomRoot<HTMLCollection>1811 fn GetElementsByClassName(&self, classes: DOMString) -> DomRoot<HTMLCollection> {
1812 let window = window_from_node(self);
1813 HTMLCollection::by_class_name(&window, self.upcast(), classes)
1814 }
1815
1816 // https://drafts.csswg.org/cssom-view/#dom-element-getclientrects
GetClientRects(&self) -> Vec<DomRoot<DOMRect>>1817 fn GetClientRects(&self) -> Vec<DomRoot<DOMRect>> {
1818 let win = window_from_node(self);
1819 let raw_rects = self.upcast::<Node>().content_boxes();
1820 raw_rects.iter().map(|rect| {
1821 DOMRect::new(win.upcast(),
1822 rect.origin.x.to_f64_px(),
1823 rect.origin.y.to_f64_px(),
1824 rect.size.width.to_f64_px(),
1825 rect.size.height.to_f64_px())
1826 }).collect()
1827 }
1828
1829 // https://drafts.csswg.org/cssom-view/#dom-element-getboundingclientrect
GetBoundingClientRect(&self) -> DomRoot<DOMRect>1830 fn GetBoundingClientRect(&self) -> DomRoot<DOMRect> {
1831 let win = window_from_node(self);
1832 let rect = self.upcast::<Node>().bounding_content_box_or_zero();
1833 DOMRect::new(win.upcast(),
1834 rect.origin.x.to_f64_px(),
1835 rect.origin.y.to_f64_px(),
1836 rect.size.width.to_f64_px(),
1837 rect.size.height.to_f64_px())
1838 }
1839
1840 // https://drafts.csswg.org/cssom-view/#dom-element-scroll
Scroll(&self, options: &ScrollToOptions)1841 fn Scroll(&self, options: &ScrollToOptions) {
1842 // Step 1
1843 let left = options.left.unwrap_or(self.ScrollLeft());
1844 let top = options.top.unwrap_or(self.ScrollTop());
1845 self.scroll(left, top, options.parent.behavior);
1846 }
1847
1848 // https://drafts.csswg.org/cssom-view/#dom-element-scroll
Scroll_(&self, x: f64, y: f64)1849 fn Scroll_(&self, x: f64, y: f64) {
1850 self.scroll(x, y, ScrollBehavior::Auto);
1851 }
1852
1853 // https://drafts.csswg.org/cssom-view/#dom-element-scrollto
ScrollTo(&self, options: &ScrollToOptions)1854 fn ScrollTo(&self, options: &ScrollToOptions) {
1855 self.Scroll(options);
1856 }
1857
1858 // https://drafts.csswg.org/cssom-view/#dom-element-scrollto
ScrollTo_(&self, x: f64, y: f64)1859 fn ScrollTo_(&self, x: f64, y: f64) {
1860 self.Scroll_(x, y);
1861 }
1862
1863 // https://drafts.csswg.org/cssom-view/#dom-element-scrollby
ScrollBy(&self, options: &ScrollToOptions)1864 fn ScrollBy(&self, options: &ScrollToOptions) {
1865 // Step 2
1866 let delta_left = options.left.unwrap_or(0.0f64);
1867 let delta_top = options.top.unwrap_or(0.0f64);
1868 let left = self.ScrollLeft();
1869 let top = self.ScrollTop();
1870 self.scroll(left + delta_left, top + delta_top,
1871 options.parent.behavior);
1872 }
1873
1874 // https://drafts.csswg.org/cssom-view/#dom-element-scrollby
ScrollBy_(&self, x: f64, y: f64)1875 fn ScrollBy_(&self, x: f64, y: f64) {
1876 let left = self.ScrollLeft();
1877 let top = self.ScrollTop();
1878 self.scroll(left + x, top + y, ScrollBehavior::Auto);
1879 }
1880
1881 // https://drafts.csswg.org/cssom-view/#dom-element-scrolltop
ScrollTop(&self) -> f641882 fn ScrollTop(&self) -> f64 {
1883 let node = self.upcast::<Node>();
1884
1885 // Step 1
1886 let doc = node.owner_doc();
1887
1888 // Step 2
1889 if !doc.is_fully_active() {
1890 return 0.0;
1891 }
1892
1893 // Step 3
1894 let win = match doc.GetDefaultView() {
1895 None => return 0.0,
1896 Some(win) => win,
1897 };
1898
1899 // Step 5
1900 if *self.root_element() == *self {
1901 if doc.quirks_mode() == QuirksMode::Quirks {
1902 return 0.0;
1903 }
1904
1905 // Step 6
1906 return win.ScrollY() as f64;
1907 }
1908
1909 // Step 7
1910 if doc.GetBody().r() == self.downcast::<HTMLElement>() &&
1911 doc.quirks_mode() == QuirksMode::Quirks &&
1912 !self.potentially_scrollable() {
1913 return win.ScrollY() as f64;
1914 }
1915
1916
1917 // Step 8
1918 if !self.has_css_layout_box() {
1919 return 0.0;
1920 }
1921
1922 // Step 9
1923 let point = node.scroll_offset();
1924 return point.y.abs() as f64;
1925 }
1926
1927 // https://drafts.csswg.org/cssom-view/#dom-element-scrolltop
SetScrollTop(&self, y_: f64)1928 fn SetScrollTop(&self, y_: f64) {
1929 let behavior = ScrollBehavior::Auto;
1930
1931 // Step 1, 2
1932 let y = if y_.is_finite() { y_ } else { 0.0f64 };
1933
1934 let node = self.upcast::<Node>();
1935
1936 // Step 3
1937 let doc = node.owner_doc();
1938
1939 // Step 4
1940 if !doc.is_fully_active() {
1941 return;
1942 }
1943
1944 // Step 5
1945 let win = match doc.GetDefaultView() {
1946 None => return,
1947 Some(win) => win,
1948 };
1949
1950 // Step 7
1951 if *self.root_element() == *self {
1952 if doc.quirks_mode() != QuirksMode::Quirks {
1953 win.scroll(win.ScrollX() as f64, y, behavior);
1954 }
1955
1956 return;
1957 }
1958
1959 // Step 9
1960 if doc.GetBody().r() == self.downcast::<HTMLElement>() &&
1961 doc.quirks_mode() == QuirksMode::Quirks &&
1962 !self.potentially_scrollable() {
1963 win.scroll(win.ScrollX() as f64, y, behavior);
1964 return;
1965 }
1966
1967 // Step 10
1968 if !self.has_css_layout_box() ||
1969 !self.has_scrolling_box() ||
1970 !self.has_overflow()
1971 {
1972 return;
1973 }
1974
1975 // Step 11
1976 win.scroll_node(node, self.ScrollLeft(), y, behavior);
1977 }
1978
1979 // https://drafts.csswg.org/cssom-view/#dom-element-scrolltop
ScrollLeft(&self) -> f641980 fn ScrollLeft(&self) -> f64 {
1981 let node = self.upcast::<Node>();
1982
1983 // Step 1
1984 let doc = node.owner_doc();
1985
1986 // Step 2
1987 if !doc.is_fully_active() {
1988 return 0.0;
1989 }
1990
1991 // Step 3
1992 let win = match doc.GetDefaultView() {
1993 None => return 0.0,
1994 Some(win) => win,
1995 };
1996
1997 // Step 5
1998 if *self.root_element() == *self {
1999 if doc.quirks_mode() != QuirksMode::Quirks {
2000 // Step 6
2001 return win.ScrollX() as f64;
2002 }
2003
2004 return 0.0;
2005 }
2006
2007 // Step 7
2008 if doc.GetBody().r() == self.downcast::<HTMLElement>() &&
2009 doc.quirks_mode() == QuirksMode::Quirks &&
2010 !self.potentially_scrollable() {
2011 return win.ScrollX() as f64;
2012 }
2013
2014
2015 // Step 8
2016 if !self.has_css_layout_box() {
2017 return 0.0;
2018 }
2019
2020 // Step 9
2021 let point = node.scroll_offset();
2022 return point.x.abs() as f64;
2023 }
2024
2025 // https://drafts.csswg.org/cssom-view/#dom-element-scrollleft
SetScrollLeft(&self, x_: f64)2026 fn SetScrollLeft(&self, x_: f64) {
2027 let behavior = ScrollBehavior::Auto;
2028
2029 // Step 1, 2
2030 let x = if x_.is_finite() { x_ } else { 0.0f64 };
2031
2032 let node = self.upcast::<Node>();
2033
2034 // Step 3
2035 let doc = node.owner_doc();
2036
2037 // Step 4
2038 if !doc.is_fully_active() {
2039 return;
2040 }
2041
2042 // Step 5
2043 let win = match doc.GetDefaultView() {
2044 None => return,
2045 Some(win) => win,
2046 };
2047
2048 // Step 7
2049 if *self.root_element() == *self {
2050 if doc.quirks_mode() == QuirksMode::Quirks {
2051 return;
2052 }
2053
2054 win.scroll(x, win.ScrollY() as f64, behavior);
2055 return;
2056 }
2057
2058 // Step 9
2059 if doc.GetBody().r() == self.downcast::<HTMLElement>() &&
2060 doc.quirks_mode() == QuirksMode::Quirks &&
2061 !self.potentially_scrollable() {
2062 win.scroll(x, win.ScrollY() as f64, behavior);
2063 return;
2064 }
2065
2066 // Step 10
2067 if !self.has_css_layout_box() ||
2068 !self.has_scrolling_box() ||
2069 !self.has_overflow()
2070 {
2071 return;
2072 }
2073
2074 // Step 11
2075 win.scroll_node(node, x, self.ScrollTop(), behavior);
2076 }
2077
2078 // https://drafts.csswg.org/cssom-view/#dom-element-scrollwidth
ScrollWidth(&self) -> i322079 fn ScrollWidth(&self) -> i32 {
2080 self.upcast::<Node>().scroll_area().size.width
2081 }
2082
2083 // https://drafts.csswg.org/cssom-view/#dom-element-scrollheight
ScrollHeight(&self) -> i322084 fn ScrollHeight(&self) -> i32 {
2085 self.upcast::<Node>().scroll_area().size.height
2086 }
2087
2088 // https://drafts.csswg.org/cssom-view/#dom-element-clienttop
ClientTop(&self) -> i322089 fn ClientTop(&self) -> i32 {
2090 self.upcast::<Node>().client_rect().origin.y
2091 }
2092
2093 // https://drafts.csswg.org/cssom-view/#dom-element-clientleft
ClientLeft(&self) -> i322094 fn ClientLeft(&self) -> i32 {
2095 self.upcast::<Node>().client_rect().origin.x
2096 }
2097
2098 // https://drafts.csswg.org/cssom-view/#dom-element-clientwidth
ClientWidth(&self) -> i322099 fn ClientWidth(&self) -> i32 {
2100 self.upcast::<Node>().client_rect().size.width
2101 }
2102
2103 // https://drafts.csswg.org/cssom-view/#dom-element-clientheight
ClientHeight(&self) -> i322104 fn ClientHeight(&self) -> i32 {
2105 self.upcast::<Node>().client_rect().size.height
2106 }
2107
2108 /// <https://w3c.github.io/DOM-Parsing/#widl-Element-innerHTML>
GetInnerHTML(&self) -> Fallible<DOMString>2109 fn GetInnerHTML(&self) -> Fallible<DOMString> {
2110 let qname = QualName::new(self.prefix().clone(),
2111 self.namespace().clone(),
2112 self.local_name().clone());
2113 if document_from_node(self).is_html_document() {
2114 return self.serialize(ChildrenOnly(Some(qname)));
2115 } else {
2116 return self.xmlSerialize(XmlChildrenOnly(Some(qname)));
2117 }
2118 }
2119
2120 /// <https://w3c.github.io/DOM-Parsing/#widl-Element-innerHTML>
SetInnerHTML(&self, value: DOMString) -> ErrorResult2121 fn SetInnerHTML(&self, value: DOMString) -> ErrorResult {
2122 // Step 1.
2123 let frag = self.parse_fragment(value)?;
2124 // Step 2.
2125 // https://github.com/w3c/DOM-Parsing/issues/1
2126 let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() {
2127 DomRoot::upcast(template.Content())
2128 } else {
2129 DomRoot::from_ref(self.upcast())
2130 };
2131 Node::replace_all(Some(frag.upcast()), &target);
2132 Ok(())
2133 }
2134
2135 // https://dvcs.w3.org/hg/innerhtml/raw-file/tip/index.html#widl-Element-outerHTML
GetOuterHTML(&self) -> Fallible<DOMString>2136 fn GetOuterHTML(&self) -> Fallible<DOMString> {
2137 if document_from_node(self).is_html_document() {
2138 return self.serialize(IncludeNode);
2139 } else {
2140 return self.xmlSerialize(XmlIncludeNode);
2141 }
2142 }
2143
2144 // https://w3c.github.io/DOM-Parsing/#dom-element-outerhtml
SetOuterHTML(&self, value: DOMString) -> ErrorResult2145 fn SetOuterHTML(&self, value: DOMString) -> ErrorResult {
2146 let context_document = document_from_node(self);
2147 let context_node = self.upcast::<Node>();
2148 // Step 1.
2149 let context_parent = match context_node.GetParentNode() {
2150 None => {
2151 // Step 2.
2152 return Ok(());
2153 },
2154 Some(parent) => parent,
2155 };
2156
2157 let parent = match context_parent.type_id() {
2158 // Step 3.
2159 NodeTypeId::Document(_) => return Err(Error::NoModificationAllowed),
2160
2161 // Step 4.
2162 NodeTypeId::DocumentFragment => {
2163 let body_elem = Element::create(QualName::new(None, ns!(html), local_name!("body")),
2164 None,
2165 &context_document,
2166 ElementCreator::ScriptCreated,
2167 CustomElementCreationMode::Synchronous);
2168 DomRoot::upcast(body_elem)
2169 },
2170 _ => context_node.GetParentElement().unwrap()
2171 };
2172
2173 // Step 5.
2174 let frag = parent.parse_fragment(value)?;
2175 // Step 6.
2176 context_parent.ReplaceChild(frag.upcast(), context_node)?;
2177 Ok(())
2178 }
2179
2180 // https://dom.spec.whatwg.org/#dom-nondocumenttypechildnode-previouselementsibling
GetPreviousElementSibling(&self) -> Option<DomRoot<Element>>2181 fn GetPreviousElementSibling(&self) -> Option<DomRoot<Element>> {
2182 self.upcast::<Node>().preceding_siblings().filter_map(DomRoot::downcast).next()
2183 }
2184
2185 // https://dom.spec.whatwg.org/#dom-nondocumenttypechildnode-nextelementsibling
GetNextElementSibling(&self) -> Option<DomRoot<Element>>2186 fn GetNextElementSibling(&self) -> Option<DomRoot<Element>> {
2187 self.upcast::<Node>().following_siblings().filter_map(DomRoot::downcast).next()
2188 }
2189
2190 // https://dom.spec.whatwg.org/#dom-parentnode-children
Children(&self) -> DomRoot<HTMLCollection>2191 fn Children(&self) -> DomRoot<HTMLCollection> {
2192 let window = window_from_node(self);
2193 HTMLCollection::children(&window, self.upcast())
2194 }
2195
2196 // https://dom.spec.whatwg.org/#dom-parentnode-firstelementchild
GetFirstElementChild(&self) -> Option<DomRoot<Element>>2197 fn GetFirstElementChild(&self) -> Option<DomRoot<Element>> {
2198 self.upcast::<Node>().child_elements().next()
2199 }
2200
2201 // https://dom.spec.whatwg.org/#dom-parentnode-lastelementchild
GetLastElementChild(&self) -> Option<DomRoot<Element>>2202 fn GetLastElementChild(&self) -> Option<DomRoot<Element>> {
2203 self.upcast::<Node>().rev_children().filter_map(DomRoot::downcast::<Element>).next()
2204 }
2205
2206 // https://dom.spec.whatwg.org/#dom-parentnode-childelementcount
ChildElementCount(&self) -> u322207 fn ChildElementCount(&self) -> u32 {
2208 self.upcast::<Node>().child_elements().count() as u32
2209 }
2210
2211 // https://dom.spec.whatwg.org/#dom-parentnode-prepend
Prepend(&self, nodes: Vec<NodeOrString>) -> ErrorResult2212 fn Prepend(&self, nodes: Vec<NodeOrString>) -> ErrorResult {
2213 self.upcast::<Node>().prepend(nodes)
2214 }
2215
2216 // https://dom.spec.whatwg.org/#dom-parentnode-append
Append(&self, nodes: Vec<NodeOrString>) -> ErrorResult2217 fn Append(&self, nodes: Vec<NodeOrString>) -> ErrorResult {
2218 self.upcast::<Node>().append(nodes)
2219 }
2220
2221 // https://dom.spec.whatwg.org/#dom-parentnode-queryselector
QuerySelector(&self, selectors: DOMString) -> Fallible<Option<DomRoot<Element>>>2222 fn QuerySelector(&self, selectors: DOMString) -> Fallible<Option<DomRoot<Element>>> {
2223 let root = self.upcast::<Node>();
2224 root.query_selector(selectors)
2225 }
2226
2227 // https://dom.spec.whatwg.org/#dom-parentnode-queryselectorall
QuerySelectorAll(&self, selectors: DOMString) -> Fallible<DomRoot<NodeList>>2228 fn QuerySelectorAll(&self, selectors: DOMString) -> Fallible<DomRoot<NodeList>> {
2229 let root = self.upcast::<Node>();
2230 root.query_selector_all(selectors)
2231 }
2232
2233 // https://dom.spec.whatwg.org/#dom-childnode-before
Before(&self, nodes: Vec<NodeOrString>) -> ErrorResult2234 fn Before(&self, nodes: Vec<NodeOrString>) -> ErrorResult {
2235 self.upcast::<Node>().before(nodes)
2236 }
2237
2238 // https://dom.spec.whatwg.org/#dom-childnode-after
After(&self, nodes: Vec<NodeOrString>) -> ErrorResult2239 fn After(&self, nodes: Vec<NodeOrString>) -> ErrorResult {
2240 self.upcast::<Node>().after(nodes)
2241 }
2242
2243 // https://dom.spec.whatwg.org/#dom-childnode-replacewith
ReplaceWith(&self, nodes: Vec<NodeOrString>) -> ErrorResult2244 fn ReplaceWith(&self, nodes: Vec<NodeOrString>) -> ErrorResult {
2245 self.upcast::<Node>().replace_with(nodes)
2246 }
2247
2248 // https://dom.spec.whatwg.org/#dom-childnode-remove
Remove(&self)2249 fn Remove(&self) {
2250 self.upcast::<Node>().remove_self();
2251 }
2252
2253 // https://dom.spec.whatwg.org/#dom-element-matches
Matches(&self, selectors: DOMString) -> Fallible<bool>2254 fn Matches(&self, selectors: DOMString) -> Fallible<bool> {
2255 let selectors =
2256 match SelectorParser::parse_author_origin_no_namespace(&selectors) {
2257 Err(_) => return Err(Error::Syntax),
2258 Ok(selectors) => selectors,
2259 };
2260
2261 let quirks_mode = document_from_node(self).quirks_mode();
2262 let element = DomRoot::from_ref(self);
2263
2264 Ok(dom_apis::element_matches(&element, &selectors, quirks_mode))
2265 }
2266
2267 // https://dom.spec.whatwg.org/#dom-element-webkitmatchesselector
WebkitMatchesSelector(&self, selectors: DOMString) -> Fallible<bool>2268 fn WebkitMatchesSelector(&self, selectors: DOMString) -> Fallible<bool> {
2269 self.Matches(selectors)
2270 }
2271
2272 // https://dom.spec.whatwg.org/#dom-element-closest
Closest(&self, selectors: DOMString) -> Fallible<Option<DomRoot<Element>>>2273 fn Closest(&self, selectors: DOMString) -> Fallible<Option<DomRoot<Element>>> {
2274 let selectors =
2275 match SelectorParser::parse_author_origin_no_namespace(&selectors) {
2276 Err(_) => return Err(Error::Syntax),
2277 Ok(selectors) => selectors,
2278 };
2279
2280 let quirks_mode = document_from_node(self).quirks_mode();
2281 Ok(dom_apis::element_closest(
2282 DomRoot::from_ref(self),
2283 &selectors,
2284 quirks_mode,
2285 ))
2286 }
2287
2288 // https://dom.spec.whatwg.org/#dom-element-insertadjacentelement
InsertAdjacentElement(&self, where_: DOMString, element: &Element) -> Fallible<Option<DomRoot<Element>>>2289 fn InsertAdjacentElement(&self, where_: DOMString, element: &Element)
2290 -> Fallible<Option<DomRoot<Element>>> {
2291 let where_ = where_.parse::<AdjacentPosition>()?;
2292 let inserted_node = self.insert_adjacent(where_, element.upcast())?;
2293 Ok(inserted_node.map(|node| DomRoot::downcast(node).unwrap()))
2294 }
2295
2296 // https://dom.spec.whatwg.org/#dom-element-insertadjacenttext
InsertAdjacentText(&self, where_: DOMString, data: DOMString) -> ErrorResult2297 fn InsertAdjacentText(&self, where_: DOMString, data: DOMString)
2298 -> ErrorResult {
2299 // Step 1.
2300 let text = Text::new(data, &document_from_node(self));
2301
2302 // Step 2.
2303 let where_ = where_.parse::<AdjacentPosition>()?;
2304 self.insert_adjacent(where_, text.upcast()).map(|_| ())
2305 }
2306
2307 // https://w3c.github.io/DOM-Parsing/#dom-element-insertadjacenthtml
InsertAdjacentHTML(&self, position: DOMString, text: DOMString) -> ErrorResult2308 fn InsertAdjacentHTML(&self, position: DOMString, text: DOMString)
2309 -> ErrorResult {
2310 // Step 1.
2311 let position = position.parse::<AdjacentPosition>()?;
2312
2313 let context = match position {
2314 AdjacentPosition::BeforeBegin | AdjacentPosition::AfterEnd => {
2315 match self.upcast::<Node>().GetParentNode() {
2316 Some(ref node) if node.is::<Document>() => {
2317 return Err(Error::NoModificationAllowed)
2318 }
2319 None => return Err(Error::NoModificationAllowed),
2320 Some(node) => node,
2321 }
2322 }
2323 AdjacentPosition::AfterBegin | AdjacentPosition::BeforeEnd => {
2324 DomRoot::from_ref(self.upcast::<Node>())
2325 }
2326 };
2327
2328 // Step 2.
2329 let context = Element::fragment_parsing_context(
2330 &context.owner_doc(), context.downcast::<Element>());
2331
2332 // Step 3.
2333 let fragment = context.parse_fragment(text)?;
2334
2335 // Step 4.
2336 self.insert_adjacent(position, fragment.upcast()).map(|_| ())
2337 }
2338
2339 // check-tidy: no specs after this line
EnterFormalActivationState(&self) -> ErrorResult2340 fn EnterFormalActivationState(&self) -> ErrorResult {
2341 match self.as_maybe_activatable() {
2342 Some(a) => {
2343 a.enter_formal_activation_state();
2344 return Ok(());
2345 },
2346 None => return Err(Error::NotSupported)
2347 }
2348 }
2349
ExitFormalActivationState(&self) -> ErrorResult2350 fn ExitFormalActivationState(&self) -> ErrorResult {
2351 match self.as_maybe_activatable() {
2352 Some(a) => {
2353 a.exit_formal_activation_state();
2354 return Ok(());
2355 },
2356 None => return Err(Error::NotSupported)
2357 }
2358 }
2359
2360 // https://fullscreen.spec.whatwg.org/#dom-element-requestfullscreen
2361 #[allow(unrooted_must_root)]
RequestFullscreen(&self) -> Rc<Promise>2362 fn RequestFullscreen(&self) -> Rc<Promise> {
2363 let doc = document_from_node(self);
2364 doc.enter_fullscreen(self)
2365 }
2366 }
2367
2368 impl VirtualMethods for Element {
super_type(&self) -> Option<&VirtualMethods>2369 fn super_type(&self) -> Option<&VirtualMethods> {
2370 Some(self.upcast::<Node>() as &VirtualMethods)
2371 }
2372
attribute_affects_presentational_hints(&self, attr: &Attr) -> bool2373 fn attribute_affects_presentational_hints(&self, attr: &Attr) -> bool {
2374 // FIXME: This should be more fine-grained, not all elements care about these.
2375 if attr.local_name() == &local_name!("width") ||
2376 attr.local_name() == &local_name!("height") {
2377 return true;
2378 }
2379
2380 self.super_type().unwrap().attribute_affects_presentational_hints(attr)
2381 }
2382
attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation)2383 fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
2384 self.super_type().unwrap().attribute_mutated(attr, mutation);
2385 let node = self.upcast::<Node>();
2386 let doc = node.owner_doc();
2387 match attr.local_name() {
2388 &local_name!("style") => {
2389 // Modifying the `style` attribute might change style.
2390 *self.style_attribute.borrow_mut() = match mutation {
2391 AttributeMutation::Set(..) => {
2392 // This is the fast path we use from
2393 // CSSStyleDeclaration.
2394 //
2395 // Juggle a bit to keep the borrow checker happy
2396 // while avoiding the extra clone.
2397 let is_declaration = match *attr.value() {
2398 AttrValue::Declaration(..) => true,
2399 _ => false,
2400 };
2401
2402 let block = if is_declaration {
2403 let mut value = AttrValue::String(String::new());
2404 attr.swap_value(&mut value);
2405 let (serialization, block) = match value {
2406 AttrValue::Declaration(s, b) => (s, b),
2407 _ => unreachable!(),
2408 };
2409 let mut value = AttrValue::String(serialization);
2410 attr.swap_value(&mut value);
2411 block
2412 } else {
2413 let win = window_from_node(self);
2414 Arc::new(doc.style_shared_lock().wrap(parse_style_attribute(
2415 &attr.value(),
2416 &doc.base_url(),
2417 win.css_error_reporter(),
2418 doc.quirks_mode())))
2419 };
2420
2421 Some(block)
2422 }
2423 AttributeMutation::Removed => {
2424 None
2425 }
2426 };
2427 },
2428 &local_name!("id") => {
2429 *self.id_attribute.borrow_mut() =
2430 mutation.new_value(attr).and_then(|value| {
2431 let value = value.as_atom();
2432 if value != &atom!("") {
2433 Some(value.clone())
2434 } else {
2435 None
2436 }
2437 });
2438 if node.is_in_doc() {
2439 let value = attr.value().as_atom().clone();
2440 match mutation {
2441 AttributeMutation::Set(old_value) => {
2442 if let Some(old_value) = old_value {
2443 let old_value = old_value.as_atom().clone();
2444 doc.unregister_named_element(self, old_value);
2445 }
2446 if value != atom!("") {
2447 doc.register_named_element(self, value);
2448 }
2449 },
2450 AttributeMutation::Removed => {
2451 if value != atom!("") {
2452 doc.unregister_named_element(self, value);
2453 }
2454 }
2455 }
2456 }
2457 },
2458 _ => {
2459 // FIXME(emilio): This is pretty dubious, and should be done in
2460 // the relevant super-classes.
2461 if attr.namespace() == &ns!() &&
2462 attr.local_name() == &local_name!("src") {
2463 node.dirty(NodeDamage::OtherNodeDamage);
2464 }
2465 },
2466 };
2467
2468 // Make sure we rev the version even if we didn't dirty the node. If we
2469 // don't do this, various attribute-dependent htmlcollections (like those
2470 // generated by getElementsByClassName) might become stale.
2471 node.rev_version();
2472 }
2473
parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue2474 fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
2475 match name {
2476 &local_name!("id") => AttrValue::from_atomic(value.into()),
2477 &local_name!("class") => AttrValue::from_serialized_tokenlist(value.into()),
2478 _ => self.super_type().unwrap().parse_plain_attribute(name, value),
2479 }
2480 }
2481
bind_to_tree(&self, tree_in_doc: bool)2482 fn bind_to_tree(&self, tree_in_doc: bool) {
2483 if let Some(ref s) = self.super_type() {
2484 s.bind_to_tree(tree_in_doc);
2485 }
2486
2487 if let Some(f) = self.as_maybe_form_control() {
2488 f.bind_form_control_to_tree();
2489 }
2490
2491 if !tree_in_doc {
2492 return;
2493 }
2494
2495 let doc = document_from_node(self);
2496 if let Some(ref value) = *self.id_attribute.borrow() {
2497 doc.register_named_element(self, value.clone());
2498 }
2499 // This is used for layout optimization.
2500 doc.increment_dom_count();
2501 }
2502
unbind_from_tree(&self, context: &UnbindContext)2503 fn unbind_from_tree(&self, context: &UnbindContext) {
2504 self.super_type().unwrap().unbind_from_tree(context);
2505
2506 if let Some(f) = self.as_maybe_form_control() {
2507 f.unbind_form_control_from_tree();
2508 }
2509
2510 if !context.tree_in_doc {
2511 return;
2512 }
2513
2514 let doc = document_from_node(self);
2515 let fullscreen = doc.GetFullscreenElement();
2516 if fullscreen.r() == Some(self) {
2517 doc.exit_fullscreen();
2518 }
2519 if let Some(ref value) = *self.id_attribute.borrow() {
2520 doc.unregister_named_element(self, value.clone());
2521 }
2522 // This is used for layout optimization.
2523 doc.decrement_dom_count();
2524 }
2525
children_changed(&self, mutation: &ChildrenMutation)2526 fn children_changed(&self, mutation: &ChildrenMutation) {
2527 if let Some(ref s) = self.super_type() {
2528 s.children_changed(mutation);
2529 }
2530
2531 let flags = self.selector_flags.get();
2532 if flags.intersects(ElementSelectorFlags::HAS_SLOW_SELECTOR) {
2533 // All children of this node need to be restyled when any child changes.
2534 self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
2535 } else {
2536 if flags.intersects(ElementSelectorFlags::HAS_SLOW_SELECTOR_LATER_SIBLINGS) {
2537 if let Some(next_child) = mutation.next_child() {
2538 for child in next_child.inclusively_following_siblings() {
2539 if child.is::<Element>() {
2540 child.dirty(NodeDamage::OtherNodeDamage);
2541 }
2542 }
2543 }
2544 }
2545 if flags.intersects(ElementSelectorFlags::HAS_EDGE_CHILD_SELECTOR) {
2546 if let Some(child) = mutation.modified_edge_element() {
2547 child.dirty(NodeDamage::OtherNodeDamage);
2548 }
2549 }
2550 }
2551 }
2552
adopting_steps(&self, old_doc: &Document)2553 fn adopting_steps(&self, old_doc: &Document) {
2554 self.super_type().unwrap().adopting_steps(old_doc);
2555
2556 if document_from_node(self).is_html_document() != old_doc.is_html_document() {
2557 self.tag_name.clear();
2558 }
2559 }
2560 }
2561
2562 impl<'a> SelectorsElement for DomRoot<Element> {
2563 type Impl = SelectorImpl;
2564
opaque(&self) -> ::selectors::OpaqueElement2565 fn opaque(&self) -> ::selectors::OpaqueElement {
2566 ::selectors::OpaqueElement::new(self.reflector().get_jsobject().get())
2567 }
2568
parent_element(&self) -> Option<DomRoot<Element>>2569 fn parent_element(&self) -> Option<DomRoot<Element>> {
2570 self.upcast::<Node>().GetParentElement()
2571 }
2572
match_pseudo_element( &self, _pseudo: &PseudoElement, _context: &mut MatchingContext<Self::Impl>, ) -> bool2573 fn match_pseudo_element(
2574 &self,
2575 _pseudo: &PseudoElement,
2576 _context: &mut MatchingContext<Self::Impl>,
2577 ) -> bool {
2578 false
2579 }
2580
2581
first_child_element(&self) -> Option<DomRoot<Element>>2582 fn first_child_element(&self) -> Option<DomRoot<Element>> {
2583 self.node.child_elements().next()
2584 }
2585
last_child_element(&self) -> Option<DomRoot<Element>>2586 fn last_child_element(&self) -> Option<DomRoot<Element>> {
2587 self.node.rev_children().filter_map(DomRoot::downcast).next()
2588 }
2589
prev_sibling_element(&self) -> Option<DomRoot<Element>>2590 fn prev_sibling_element(&self) -> Option<DomRoot<Element>> {
2591 self.node.preceding_siblings().filter_map(DomRoot::downcast).next()
2592 }
2593
next_sibling_element(&self) -> Option<DomRoot<Element>>2594 fn next_sibling_element(&self) -> Option<DomRoot<Element>> {
2595 self.node.following_siblings().filter_map(DomRoot::downcast).next()
2596 }
2597
attr_matches(&self, ns: &NamespaceConstraint<&Namespace>, local_name: &LocalName, operation: &AttrSelectorOperation<&String>) -> bool2598 fn attr_matches(&self,
2599 ns: &NamespaceConstraint<&Namespace>,
2600 local_name: &LocalName,
2601 operation: &AttrSelectorOperation<&String>)
2602 -> bool {
2603 match *ns {
2604 NamespaceConstraint::Specific(ref ns) => {
2605 self.get_attribute(ns, local_name)
2606 .map_or(false, |attr| attr.value().eval_selector(operation))
2607 }
2608 NamespaceConstraint::Any => {
2609 self.attrs.borrow().iter().any(|attr| {
2610 attr.local_name() == local_name &&
2611 attr.value().eval_selector(operation)
2612 })
2613 }
2614 }
2615 }
2616
is_root(&self) -> bool2617 fn is_root(&self) -> bool {
2618 match self.node.GetParentNode() {
2619 None => false,
2620 Some(node) => node.is::<Document>(),
2621 }
2622 }
2623
is_empty(&self) -> bool2624 fn is_empty(&self) -> bool {
2625 self.node.children().all(|node| !node.is::<Element>() && match node.downcast::<Text>() {
2626 None => true,
2627 Some(text) => text.upcast::<CharacterData>().data().is_empty()
2628 })
2629 }
2630
local_name(&self) -> &LocalName2631 fn local_name(&self) -> &LocalName {
2632 Element::local_name(self)
2633 }
2634
namespace(&self) -> &Namespace2635 fn namespace(&self) -> &Namespace {
2636 Element::namespace(self)
2637 }
2638
match_non_ts_pseudo_class<F>( &self, pseudo_class: &NonTSPseudoClass, _: &mut MatchingContext<Self::Impl>, _: &mut F, ) -> bool where F: FnMut(&Self, ElementSelectorFlags),2639 fn match_non_ts_pseudo_class<F>(
2640 &self,
2641 pseudo_class: &NonTSPseudoClass,
2642 _: &mut MatchingContext<Self::Impl>,
2643 _: &mut F,
2644 ) -> bool
2645 where
2646 F: FnMut(&Self, ElementSelectorFlags),
2647 {
2648 match *pseudo_class {
2649 // https://github.com/servo/servo/issues/8718
2650 NonTSPseudoClass::Link |
2651 NonTSPseudoClass::AnyLink => self.is_link(),
2652 NonTSPseudoClass::Visited => false,
2653
2654 NonTSPseudoClass::ServoNonZeroBorder => {
2655 match self.downcast::<HTMLTableElement>() {
2656 None => false,
2657 Some(this) => {
2658 match this.get_border() {
2659 None | Some(0) => false,
2660 Some(_) => true,
2661 }
2662 }
2663 }
2664 },
2665
2666 NonTSPseudoClass::ServoCaseSensitiveTypeAttr(ref expected_value) => {
2667 self.get_attribute(&ns!(), &local_name!("type"))
2668 .map_or(false, |attr| attr.value().eq(expected_value))
2669 }
2670
2671 // FIXME(heycam): This is wrong, since extended_filtering accepts
2672 // a string containing commas (separating each language tag in
2673 // a list) but the pseudo-class instead should be parsing and
2674 // storing separate <ident> or <string>s for each language tag.
2675 NonTSPseudoClass::Lang(ref lang) => extended_filtering(&*self.get_lang(), &*lang),
2676
2677 NonTSPseudoClass::ReadOnly =>
2678 !Element::state(self).contains(pseudo_class.state_flag()),
2679
2680 NonTSPseudoClass::Active |
2681 NonTSPseudoClass::Focus |
2682 NonTSPseudoClass::Fullscreen |
2683 NonTSPseudoClass::Hover |
2684 NonTSPseudoClass::Enabled |
2685 NonTSPseudoClass::Disabled |
2686 NonTSPseudoClass::Checked |
2687 NonTSPseudoClass::Indeterminate |
2688 NonTSPseudoClass::ReadWrite |
2689 NonTSPseudoClass::PlaceholderShown |
2690 NonTSPseudoClass::Target =>
2691 Element::state(self).contains(pseudo_class.state_flag()),
2692 }
2693 }
2694
is_link(&self) -> bool2695 fn is_link(&self) -> bool {
2696 // FIXME: This is HTML only.
2697 let node = self.upcast::<Node>();
2698 match node.type_id() {
2699 // https://html.spec.whatwg.org/multipage/#selector-link
2700 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAnchorElement)) |
2701 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAreaElement)) |
2702 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLinkElement)) => {
2703 self.has_attribute(&local_name!("href"))
2704 },
2705 _ => false,
2706 }
2707 }
2708
has_id(&self, id: &Atom, case_sensitivity: CaseSensitivity) -> bool2709 fn has_id(&self, id: &Atom, case_sensitivity: CaseSensitivity) -> bool {
2710 self.id_attribute.borrow().as_ref().map_or(false, |atom| case_sensitivity.eq_atom(id, atom))
2711 }
2712
has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool2713 fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
2714 Element::has_class(&**self, name, case_sensitivity)
2715 }
2716
is_html_element_in_html_document(&self) -> bool2717 fn is_html_element_in_html_document(&self) -> bool {
2718 self.html_element_in_html_document()
2719 }
2720
is_html_slot_element(&self) -> bool2721 fn is_html_slot_element(&self) -> bool {
2722 self.is_html_element() && self.local_name() == &local_name!("slot")
2723 }
2724 }
2725
2726
2727 impl Element {
as_maybe_activatable(&self) -> Option<&Activatable>2728 pub fn as_maybe_activatable(&self) -> Option<&Activatable> {
2729 let element = match self.upcast::<Node>().type_id() {
2730 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement)) => {
2731 let element = self.downcast::<HTMLInputElement>().unwrap();
2732 Some(element as &Activatable)
2733 },
2734 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLButtonElement)) => {
2735 let element = self.downcast::<HTMLButtonElement>().unwrap();
2736 Some(element as &Activatable)
2737 },
2738 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAnchorElement)) => {
2739 let element = self.downcast::<HTMLAnchorElement>().unwrap();
2740 Some(element as &Activatable)
2741 },
2742 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLabelElement)) => {
2743 let element = self.downcast::<HTMLLabelElement>().unwrap();
2744 Some(element as &Activatable)
2745 },
2746 _ => {
2747 None
2748 }
2749 };
2750 element.and_then(|elem| {
2751 if elem.is_instance_activatable() {
2752 Some(elem)
2753 } else {
2754 None
2755 }
2756 })
2757 }
2758
as_stylesheet_owner(&self) -> Option<&StylesheetOwner>2759 pub fn as_stylesheet_owner(&self) -> Option<&StylesheetOwner> {
2760 if let Some(s) = self.downcast::<HTMLStyleElement>() {
2761 return Some(s as &StylesheetOwner)
2762 }
2763
2764 if let Some(l) = self.downcast::<HTMLLinkElement>() {
2765 return Some(l as &StylesheetOwner)
2766 }
2767
2768 None
2769 }
2770
2771 // https://html.spec.whatwg.org/multipage/#category-submit
as_maybe_validatable(&self) -> Option<&Validatable>2772 pub fn as_maybe_validatable(&self) -> Option<&Validatable> {
2773 let element = match self.upcast::<Node>().type_id() {
2774 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement)) => {
2775 let element = self.downcast::<HTMLInputElement>().unwrap();
2776 Some(element as &Validatable)
2777 },
2778 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLButtonElement)) => {
2779 let element = self.downcast::<HTMLButtonElement>().unwrap();
2780 Some(element as &Validatable)
2781 },
2782 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLObjectElement)) => {
2783 let element = self.downcast::<HTMLObjectElement>().unwrap();
2784 Some(element as &Validatable)
2785 },
2786 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSelectElement)) => {
2787 let element = self.downcast::<HTMLSelectElement>().unwrap();
2788 Some(element as &Validatable)
2789 },
2790 NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement)) => {
2791 let element = self.downcast::<HTMLTextAreaElement>().unwrap();
2792 Some(element as &Validatable)
2793 },
2794 _ => {
2795 None
2796 }
2797 };
2798 element
2799 }
2800
click_in_progress(&self) -> bool2801 pub fn click_in_progress(&self) -> bool {
2802 self.upcast::<Node>().get_flag(NodeFlags::CLICK_IN_PROGRESS)
2803 }
2804
set_click_in_progress(&self, click: bool)2805 pub fn set_click_in_progress(&self, click: bool) {
2806 self.upcast::<Node>().set_flag(NodeFlags::CLICK_IN_PROGRESS, click)
2807 }
2808
2809 // https://html.spec.whatwg.org/multipage/#nearest-activatable-element
nearest_activable_element(&self) -> Option<DomRoot<Element>>2810 pub fn nearest_activable_element(&self) -> Option<DomRoot<Element>> {
2811 match self.as_maybe_activatable() {
2812 Some(el) => Some(DomRoot::from_ref(el.as_element())),
2813 None => {
2814 let node = self.upcast::<Node>();
2815 for node in node.ancestors() {
2816 if let Some(node) = node.downcast::<Element>() {
2817 if node.as_maybe_activatable().is_some() {
2818 return Some(DomRoot::from_ref(node));
2819 }
2820 }
2821 }
2822 None
2823 }
2824 }
2825 }
2826
2827 /// Please call this method *only* for real click events
2828 ///
2829 /// <https://html.spec.whatwg.org/multipage/#run-authentic-click-activation-steps>
2830 ///
2831 /// Use an element's synthetic click activation (or handle_event) for any script-triggered clicks.
2832 /// If the spec says otherwise, check with Manishearth first
authentic_click_activation(&self, event: &Event)2833 pub fn authentic_click_activation(&self, event: &Event) {
2834 // Not explicitly part of the spec, however this helps enforce the invariants
2835 // required to save state between pre-activation and post-activation
2836 // since we cannot nest authentic clicks (unlike synthetic click activation, where
2837 // the script can generate more click events from the handler)
2838 assert!(!self.click_in_progress());
2839
2840 let target = self.upcast();
2841 // Step 2 (requires canvas support)
2842 // Step 3
2843 self.set_click_in_progress(true);
2844 // Step 4
2845 let e = self.nearest_activable_element();
2846 match e {
2847 Some(ref el) => match el.as_maybe_activatable() {
2848 Some(elem) => {
2849 // Step 5-6
2850 elem.pre_click_activation();
2851 event.fire(target);
2852 if !event.DefaultPrevented() {
2853 // post click activation
2854 elem.activation_behavior(event, target);
2855 } else {
2856 elem.canceled_activation();
2857 }
2858 }
2859 // Step 6
2860 None => {
2861 event.fire(target);
2862 }
2863 },
2864 // Step 6
2865 None => {
2866 event.fire(target);
2867 }
2868 }
2869 // Step 7
2870 self.set_click_in_progress(false);
2871 }
2872
2873 // https://html.spec.whatwg.org/multipage/#language
get_lang(&self) -> String2874 pub fn get_lang(&self) -> String {
2875 self.upcast::<Node>().inclusive_ancestors().filter_map(|node| {
2876 node.downcast::<Element>().and_then(|el| {
2877 el.get_attribute(&ns!(xml), &local_name!("lang")).or_else(|| {
2878 el.get_attribute(&ns!(), &local_name!("lang"))
2879 }).map(|attr| String::from(attr.Value()))
2880 })
2881 // TODO: Check meta tags for a pragma-set default language
2882 // TODO: Check HTTP Content-Language header
2883 }).next().unwrap_or(String::new())
2884 }
2885
state(&self) -> ElementState2886 pub fn state(&self) -> ElementState {
2887 self.state.get()
2888 }
2889
set_state(&self, which: ElementState, value: bool)2890 pub fn set_state(&self, which: ElementState, value: bool) {
2891 let mut state = self.state.get();
2892 if state.contains(which) == value {
2893 return;
2894 }
2895 let node = self.upcast::<Node>();
2896 node.owner_doc().element_state_will_change(self);
2897 if value {
2898 state.insert(which);
2899 } else {
2900 state.remove(which);
2901 }
2902 self.state.set(state);
2903 }
2904
active_state(&self) -> bool2905 pub fn active_state(&self) -> bool {
2906 self.state.get().contains(ElementState::IN_ACTIVE_STATE)
2907 }
2908
2909 /// <https://html.spec.whatwg.org/multipage/#concept-selector-active>
set_active_state(&self, value: bool)2910 pub fn set_active_state(&self, value: bool) {
2911 self.set_state(ElementState::IN_ACTIVE_STATE, value);
2912
2913 if let Some(parent) = self.upcast::<Node>().GetParentElement() {
2914 parent.set_active_state(value);
2915 }
2916 }
2917
focus_state(&self) -> bool2918 pub fn focus_state(&self) -> bool {
2919 self.state.get().contains(ElementState::IN_FOCUS_STATE)
2920 }
2921
set_focus_state(&self, value: bool)2922 pub fn set_focus_state(&self, value: bool) {
2923 self.set_state(ElementState::IN_FOCUS_STATE, value);
2924 self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
2925 }
2926
hover_state(&self) -> bool2927 pub fn hover_state(&self) -> bool {
2928 self.state.get().contains(ElementState::IN_HOVER_STATE)
2929 }
2930
set_hover_state(&self, value: bool)2931 pub fn set_hover_state(&self, value: bool) {
2932 self.set_state(ElementState::IN_HOVER_STATE, value)
2933 }
2934
enabled_state(&self) -> bool2935 pub fn enabled_state(&self) -> bool {
2936 self.state.get().contains(ElementState::IN_ENABLED_STATE)
2937 }
2938
set_enabled_state(&self, value: bool)2939 pub fn set_enabled_state(&self, value: bool) {
2940 self.set_state(ElementState::IN_ENABLED_STATE, value)
2941 }
2942
disabled_state(&self) -> bool2943 pub fn disabled_state(&self) -> bool {
2944 self.state.get().contains(ElementState::IN_DISABLED_STATE)
2945 }
2946
set_disabled_state(&self, value: bool)2947 pub fn set_disabled_state(&self, value: bool) {
2948 self.set_state(ElementState::IN_DISABLED_STATE, value)
2949 }
2950
read_write_state(&self) -> bool2951 pub fn read_write_state(&self) -> bool {
2952 self.state.get().contains(ElementState::IN_READ_WRITE_STATE)
2953 }
2954
set_read_write_state(&self, value: bool)2955 pub fn set_read_write_state(&self, value: bool) {
2956 self.set_state(ElementState::IN_READ_WRITE_STATE, value)
2957 }
2958
placeholder_shown_state(&self) -> bool2959 pub fn placeholder_shown_state(&self) -> bool {
2960 self.state.get().contains(ElementState::IN_PLACEHOLDER_SHOWN_STATE)
2961 }
2962
set_placeholder_shown_state(&self, value: bool)2963 pub fn set_placeholder_shown_state(&self, value: bool) {
2964 if self.placeholder_shown_state() != value {
2965 self.set_state(ElementState::IN_PLACEHOLDER_SHOWN_STATE, value);
2966 self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
2967 }
2968 }
2969
target_state(&self) -> bool2970 pub fn target_state(&self) -> bool {
2971 self.state.get().contains(ElementState::IN_TARGET_STATE)
2972 }
2973
set_target_state(&self, value: bool)2974 pub fn set_target_state(&self, value: bool) {
2975 self.set_state(ElementState::IN_TARGET_STATE, value)
2976 }
2977
fullscreen_state(&self) -> bool2978 pub fn fullscreen_state(&self) -> bool {
2979 self.state.get().contains(ElementState::IN_FULLSCREEN_STATE)
2980 }
2981
set_fullscreen_state(&self, value: bool)2982 pub fn set_fullscreen_state(&self, value: bool) {
2983 self.set_state(ElementState::IN_FULLSCREEN_STATE, value)
2984 }
2985
2986 /// <https://dom.spec.whatwg.org/#connected>
is_connected(&self) -> bool2987 pub fn is_connected(&self) -> bool {
2988 let node = self.upcast::<Node>();
2989 let root = node.GetRootNode();
2990 root.is::<Document>()
2991 }
2992 }
2993
2994 impl Element {
check_ancestors_disabled_state_for_form_control(&self)2995 pub fn check_ancestors_disabled_state_for_form_control(&self) {
2996 let node = self.upcast::<Node>();
2997 if self.disabled_state() {
2998 return;
2999 }
3000 for ancestor in node.ancestors() {
3001 if !ancestor.is::<HTMLFieldSetElement>() {
3002 continue;
3003 }
3004 if !ancestor.downcast::<Element>().unwrap().disabled_state() {
3005 continue;
3006 }
3007 if ancestor.is_parent_of(node) {
3008 self.set_disabled_state(true);
3009 self.set_enabled_state(false);
3010 return;
3011 }
3012 if let Some(ref legend) = ancestor.children().find(|n| n.is::<HTMLLegendElement>()) {
3013 // XXXabinader: should we save previous ancestor to avoid this iteration?
3014 if node.ancestors().any(|ancestor| ancestor == *legend) {
3015 continue;
3016 }
3017 }
3018 self.set_disabled_state(true);
3019 self.set_enabled_state(false);
3020 return;
3021 }
3022 }
3023
check_parent_disabled_state_for_option(&self)3024 pub fn check_parent_disabled_state_for_option(&self) {
3025 if self.disabled_state() {
3026 return;
3027 }
3028 let node = self.upcast::<Node>();
3029 if let Some(ref parent) = node.GetParentNode() {
3030 if parent.is::<HTMLOptGroupElement>() &&
3031 parent.downcast::<Element>().unwrap().disabled_state() {
3032 self.set_disabled_state(true);
3033 self.set_enabled_state(false);
3034 }
3035 }
3036 }
3037
check_disabled_attribute(&self)3038 pub fn check_disabled_attribute(&self) {
3039 let has_disabled_attrib = self.has_attribute(&local_name!("disabled"));
3040 self.set_disabled_state(has_disabled_attrib);
3041 self.set_enabled_state(!has_disabled_attrib);
3042 }
3043 }
3044
3045 #[derive(Clone, Copy)]
3046 pub enum AttributeMutation<'a> {
3047 /// The attribute is set, keep track of old value.
3048 /// <https://dom.spec.whatwg.org/#attribute-is-set>
3049 Set(Option<&'a AttrValue>),
3050
3051 /// The attribute is removed.
3052 /// <https://dom.spec.whatwg.org/#attribute-is-removed>
3053 Removed,
3054 }
3055
3056 impl<'a> AttributeMutation<'a> {
is_removal(&self) -> bool3057 pub fn is_removal(&self) -> bool {
3058 match *self {
3059 AttributeMutation::Removed => true,
3060 AttributeMutation::Set(..) => false,
3061 }
3062 }
3063
new_value<'b>(&self, attr: &'b Attr) -> Option<Ref<'b, AttrValue>>3064 pub fn new_value<'b>(&self, attr: &'b Attr) -> Option<Ref<'b, AttrValue>> {
3065 match *self {
3066 AttributeMutation::Set(_) => Some(attr.value()),
3067 AttributeMutation::Removed => None,
3068 }
3069 }
3070 }
3071
3072 /// A holder for an element's "tag name", which will be lazily
3073 /// resolved and cached. Should be reset when the document
3074 /// owner changes.
3075 #[derive(JSTraceable, MallocSizeOf)]
3076 struct TagName {
3077 ptr: DomRefCell<Option<LocalName>>,
3078 }
3079
3080 impl TagName {
new() -> TagName3081 fn new() -> TagName {
3082 TagName { ptr: DomRefCell::new(None) }
3083 }
3084
3085 /// Retrieve a copy of the current inner value. If it is `None`, it is
3086 /// initialized with the result of `cb` first.
or_init<F>(&self, cb: F) -> LocalName where F: FnOnce() -> LocalName3087 fn or_init<F>(&self, cb: F) -> LocalName
3088 where F: FnOnce() -> LocalName
3089 {
3090 match &mut *self.ptr.borrow_mut() {
3091 &mut Some(ref name) => name.clone(),
3092 ptr => {
3093 let name = cb();
3094 *ptr = Some(name.clone());
3095 name
3096 }
3097 }
3098 }
3099
3100 /// Clear the cached tag name, so that it will be re-calculated the
3101 /// next time that `or_init()` is called.
clear(&self)3102 fn clear(&self) {
3103 *self.ptr.borrow_mut() = None;
3104 }
3105 }
3106
3107 pub struct ElementPerformFullscreenEnter {
3108 element: Trusted<Element>,
3109 promise: TrustedPromise,
3110 error: bool,
3111 }
3112
3113 impl ElementPerformFullscreenEnter {
new(element: Trusted<Element>, promise: TrustedPromise, error: bool) -> Box<ElementPerformFullscreenEnter>3114 pub fn new(element: Trusted<Element>, promise: TrustedPromise, error: bool) -> Box<ElementPerformFullscreenEnter> {
3115 Box::new(ElementPerformFullscreenEnter {
3116 element: element,
3117 promise: promise,
3118 error: error,
3119 })
3120 }
3121 }
3122
3123 impl TaskOnce for ElementPerformFullscreenEnter {
3124 #[allow(unrooted_must_root)]
run_once(self)3125 fn run_once(self) {
3126 let element = self.element.root();
3127 let promise = self.promise.root();
3128 let document = document_from_node(element.r());
3129
3130 // Step 7.1
3131 if self.error || !element.fullscreen_element_ready_check() {
3132 document.upcast::<EventTarget>().fire_event(atom!("fullscreenerror"));
3133 promise.reject_error(Error::Type(String::from("fullscreen is not connected")));
3134 return
3135 }
3136
3137 // TODO Step 7.2-4
3138 // Step 7.5
3139 element.set_fullscreen_state(true);
3140 document.set_fullscreen_element(Some(&element));
3141 document.window().reflow(ReflowGoal::Full, ReflowReason::ElementStateChanged);
3142
3143 // Step 7.6
3144 document.upcast::<EventTarget>().fire_event(atom!("fullscreenchange"));
3145
3146 // Step 7.7
3147 promise.resolve_native(&());
3148 }
3149 }
3150
3151 pub struct ElementPerformFullscreenExit {
3152 element: Trusted<Element>,
3153 promise: TrustedPromise,
3154 }
3155
3156 impl ElementPerformFullscreenExit {
new(element: Trusted<Element>, promise: TrustedPromise) -> Box<ElementPerformFullscreenExit>3157 pub fn new(element: Trusted<Element>, promise: TrustedPromise) -> Box<ElementPerformFullscreenExit> {
3158 Box::new(ElementPerformFullscreenExit {
3159 element: element,
3160 promise: promise,
3161 })
3162 }
3163 }
3164
3165 impl TaskOnce for ElementPerformFullscreenExit {
3166 #[allow(unrooted_must_root)]
run_once(self)3167 fn run_once(self) {
3168 let element = self.element.root();
3169 let document = document_from_node(element.r());
3170 // TODO Step 9.1-5
3171 // Step 9.6
3172 element.set_fullscreen_state(false);
3173
3174 document.window().reflow(ReflowGoal::Full, ReflowReason::ElementStateChanged);
3175
3176 document.set_fullscreen_element(None);
3177
3178 // Step 9.8
3179 document.upcast::<EventTarget>().fire_event(atom!("fullscreenchange"));
3180
3181 // Step 9.10
3182 self.promise.root().resolve_native(&());
3183 }
3184 }
3185
reflect_cross_origin_attribute(element: &Element) -> Option<DOMString>3186 pub fn reflect_cross_origin_attribute(element: &Element) -> Option<DOMString> {
3187 let attr = element.get_attribute(&ns!(), &local_name!("crossorigin"));
3188
3189 if let Some(mut val) = attr.map(|v| v.Value()) {
3190 val.make_ascii_lowercase();
3191 if val == "anonymous" || val == "use-credentials" {
3192 return Some(val);
3193 }
3194 return Some(DOMString::from("anonymous"));
3195 }
3196 None
3197 }
3198
set_cross_origin_attribute(element: &Element, value: Option<DOMString>)3199 pub fn set_cross_origin_attribute(element: &Element, value: Option<DOMString>) {
3200 match value {
3201 Some(val) => element.set_string_attribute(&local_name!("crossorigin"), val),
3202 None => {
3203 element.remove_attribute(&ns!(), &local_name!("crossorigin"));
3204 }
3205 }
3206 }
3207
cors_setting_for_element(element: &Element) -> Option<CorsSettings>3208 pub fn cors_setting_for_element(element: &Element) -> Option<CorsSettings> {
3209 reflect_cross_origin_attribute(element).map_or(None, |attr| {
3210 match &*attr {
3211 "anonymous" => Some(CorsSettings::Anonymous),
3212 "use-credentials" => Some(CorsSettings::UseCredentials),
3213 _ => unreachable!()
3214 }
3215 })
3216 }
3217