@@ -146,6 +146,7 @@ use servo_media::{ClientContextId, ServoMedia};
146146use servo_url:: { ImmutableOrigin , MutableOrigin , ServoUrl } ;
147147use std:: borrow:: Cow ;
148148use std:: cell:: Cell ;
149+ use std:: cmp:: Ordering ;
149150use std:: collections:: hash_map:: Entry :: { Occupied , Vacant } ;
150151use std:: collections:: { HashMap , HashSet , VecDeque } ;
151152use std:: default:: Default ;
@@ -243,6 +244,7 @@ pub struct Document {
243244 quirks_mode : Cell < QuirksMode > ,
244245 /// Caches for the getElement methods
245246 id_map : DomRefCell < HashMap < Atom , Vec < Dom < Element > > > > ,
247+ name_map : DomRefCell < HashMap < Atom , Vec < Dom < Element > > > > ,
246248 tag_map : DomRefCell < HashMap < LocalName , Dom < HTMLCollection > > > ,
247249 tagns_map : DomRefCell < HashMap < QualName , Dom < HTMLCollection > > > ,
248250 classes_map : DomRefCell < HashMap < Vec < Atom > , Dom < HTMLCollection > > > ,
@@ -450,6 +452,12 @@ impl CollectionFilter for AnchorsFilter {
450452 }
451453}
452454
455+ enum ElementLookupResult {
456+ None ,
457+ One ( DomRoot < Element > ) ,
458+ Many ,
459+ }
460+
453461#[ allow( non_snake_case) ]
454462impl Document {
455463 #[ inline]
@@ -709,14 +717,14 @@ impl Document {
709717 }
710718
711719 /// Remove any existing association between the provided id and any elements in this document.
712- pub fn unregister_named_element ( & self , to_unregister : & Element , id : Atom ) {
720+ pub fn unregister_element_id ( & self , to_unregister : & Element , id : Atom ) {
713721 self . document_or_shadow_root
714722 . unregister_named_element ( & self . id_map , to_unregister, & id) ;
715723 self . reset_form_owner_for_listeners ( & id) ;
716724 }
717725
718726 /// Associate an element present in this document with the provided id.
719- pub fn register_named_element ( & self , element : & Element , id : Atom ) {
727+ pub fn register_element_id ( & self , element : & Element , id : Atom ) {
720728 let root = self . GetDocumentElement ( ) . expect (
721729 "The element is in the document, so there must be a document \
722730 element.",
@@ -730,6 +738,26 @@ impl Document {
730738 self . reset_form_owner_for_listeners ( & id) ;
731739 }
732740
741+ /// Remove any existing association between the provided name and any elements in this document.
742+ pub fn unregister_element_name ( & self , to_unregister : & Element , name : Atom ) {
743+ self . document_or_shadow_root
744+ . unregister_named_element ( & self . name_map , to_unregister, & name) ;
745+ }
746+
747+ /// Associate an element present in this document with the provided name.
748+ pub fn register_element_name ( & self , element : & Element , name : Atom ) {
749+ let root = self . GetDocumentElement ( ) . expect (
750+ "The element is in the document, so there must be a document \
751+ element.",
752+ ) ;
753+ self . document_or_shadow_root . register_named_element (
754+ & self . name_map ,
755+ element,
756+ & name,
757+ DomRoot :: from_ref ( root. upcast :: < Node > ( ) ) ,
758+ ) ;
759+ }
760+
733761 pub fn register_form_id_listener < T : ?Sized + FormControl > ( & self , id : DOMString , listener : & T ) {
734762 let mut map = self . form_id_listener_map . borrow_mut ( ) ;
735763 let listener = listener. to_element ( ) ;
@@ -823,18 +851,13 @@ impl Document {
823851 }
824852
825853 fn get_anchor_by_name ( & self , name : & str ) -> Option < DomRoot < Element > > {
826- // TODO faster name lookups (see #25548)
827- let check_anchor = |node : & HTMLAnchorElement | {
828- let elem = node. upcast :: < Element > ( ) ;
829- elem. get_attribute ( & ns ! ( ) , & local_name ! ( "name" ) )
830- . map_or ( false , |attr| & * * attr. value ( ) == name)
831- } ;
832- let doc_node = self . upcast :: < Node > ( ) ;
833- doc_node
834- . traverse_preorder ( ShadowIncluding :: No )
835- . filter_map ( DomRoot :: downcast)
836- . find ( |node| check_anchor ( & node) )
837- . map ( DomRoot :: upcast)
854+ let name = Atom :: from ( name) ;
855+ self . name_map . borrow ( ) . get ( & name) . and_then ( |elements| {
856+ elements
857+ . iter ( )
858+ . find ( |e| e. is :: < HTMLAnchorElement > ( ) )
859+ . map ( |e| DomRoot :: from_ref ( & * * e) )
860+ } )
838861 }
839862
840863 // https://html.spec.whatwg.org/multipage/#current-document-readiness
@@ -2524,6 +2547,75 @@ impl Document {
25242547 . unwrap ( ) ;
25252548 receiver. recv ( ) . unwrap ( ) ;
25262549 }
2550+
2551+ // https://html.spec.whatwg.org/multipage/#dom-tree-accessors:supported-property-names
2552+ // (This takes the filter as a method so the window named getter can use it too)
2553+ pub fn supported_property_names_impl (
2554+ & self ,
2555+ nameditem_filter : fn ( & Node , & Atom ) -> bool ,
2556+ ) -> Vec < DOMString > {
2557+ // The tricky part here is making sure we return the names in
2558+ // tree order, without just resorting to a full tree walkthrough.
2559+
2560+ let mut first_elements_with_name: HashMap < & Atom , & Dom < Element > > = HashMap :: new ( ) ;
2561+
2562+ // Get the first-in-tree-order element for each name in the name_map
2563+ let name_map = self . name_map . borrow ( ) ;
2564+ name_map. iter ( ) . for_each ( |( name, value) | {
2565+ if let Some ( first) = value
2566+ . iter ( )
2567+ . find ( |n| nameditem_filter ( ( * * * n) . upcast :: < Node > ( ) , & name) )
2568+ {
2569+ first_elements_with_name. insert ( name, first) ;
2570+ }
2571+ } ) ;
2572+
2573+ // Get the first-in-tree-order element for each name in the id_map;
2574+ // if we already had one from the name_map, figure out which of
2575+ // the two is first.
2576+ let id_map = self . id_map . borrow ( ) ;
2577+ id_map. iter ( ) . for_each ( |( name, value) | {
2578+ if let Some ( first) = value
2579+ . iter ( )
2580+ . find ( |n| nameditem_filter ( ( * * * n) . upcast :: < Node > ( ) , & name) )
2581+ {
2582+ match first_elements_with_name. get ( & name) {
2583+ None => {
2584+ first_elements_with_name. insert ( name, first) ;
2585+ } ,
2586+ Some ( el) => {
2587+ if * el != first && first. upcast :: < Node > ( ) . is_before ( el. upcast :: < Node > ( ) ) {
2588+ first_elements_with_name. insert ( name, first) ;
2589+ }
2590+ } ,
2591+ }
2592+ }
2593+ } ) ;
2594+
2595+ // first_elements_with_name now has our supported property names
2596+ // as keys, and the elements to order on as values.
2597+ let mut sortable_vec: Vec < ( & Atom , & Dom < Element > ) > = first_elements_with_name
2598+ . iter ( )
2599+ . map ( |( k, v) | ( * k, * v) )
2600+ . collect ( ) ;
2601+ sortable_vec. sort_unstable_by ( |a, b| {
2602+ if a. 1 == b. 1 {
2603+ // This can happen if an img has an id different from its name,
2604+ // spec does not say which string to put first.
2605+ a. 0 . cmp ( & b. 0 )
2606+ } else if a. 1 . upcast :: < Node > ( ) . is_before ( b. 1 . upcast :: < Node > ( ) ) {
2607+ Ordering :: Less
2608+ } else {
2609+ Ordering :: Greater
2610+ }
2611+ } ) ;
2612+
2613+ // And now that they're sorted, we can return the keys
2614+ sortable_vec
2615+ . iter ( )
2616+ . map ( |( k, _v) | DOMString :: from ( & * * * k) )
2617+ . collect ( )
2618+ }
25272619}
25282620
25292621fn is_character_value_key ( key : & Key ) -> bool {
@@ -2735,6 +2827,7 @@ impl Document {
27352827 // https://dom.spec.whatwg.org/#concept-document-quirks
27362828 quirks_mode : Cell :: new ( QuirksMode :: NoQuirks ) ,
27372829 id_map : DomRefCell :: new ( HashMap :: new ( ) ) ,
2830+ name_map : DomRefCell :: new ( HashMap :: new ( ) ) ,
27382831 // https://dom.spec.whatwg.org/#concept-document-encoding
27392832 encoding : Cell :: new ( encoding) ,
27402833 is_html_document : is_html_document == IsHTMLDocument :: HTMLDocument ,
@@ -3397,6 +3490,81 @@ impl Document {
33973490 StylesheetSetRef :: Document ( & mut * self . stylesheets . borrow_mut ( ) ) ,
33983491 )
33993492 }
3493+
3494+ // https://html.spec.whatwg.org/multipage/#dom-tree-accessors:determine-the-value-of-a-named-property
3495+ // Support method for steps 1-3:
3496+ // Count if there are 0, 1, or >1 elements that match the name.
3497+ // (This takes the filter as a method so the window named getter can use it too)
3498+ fn look_up_named_elements (
3499+ & self ,
3500+ name : & Atom ,
3501+ nameditem_filter : fn ( & Node , & Atom ) -> bool ,
3502+ ) -> ElementLookupResult {
3503+ // We might match because of either id==name or name==name, so there
3504+ // are two sets of nodes to look through, but we don't need a
3505+ // full tree traversal.
3506+ let id_map = self . id_map . borrow ( ) ;
3507+ let name_map = self . name_map . borrow ( ) ;
3508+ let id_vec = id_map. get ( & name) ;
3509+ let name_vec = name_map. get ( & name) ;
3510+
3511+ // If nothing can possibly have the name, exit fast
3512+ if id_vec. is_none ( ) && name_vec. is_none ( ) {
3513+ return ElementLookupResult :: None ;
3514+ }
3515+
3516+ let one_from_id_map = if let Some ( id_vec) = id_vec {
3517+ let mut elements = id_vec
3518+ . iter ( )
3519+ . filter ( |n| nameditem_filter ( ( * * * n) . upcast :: < Node > ( ) , & name) )
3520+ . peekable ( ) ;
3521+ if let Some ( first) = elements. next ( ) {
3522+ if elements. peek ( ) . is_none ( ) {
3523+ Some ( first)
3524+ } else {
3525+ return ElementLookupResult :: Many ;
3526+ }
3527+ } else {
3528+ None
3529+ }
3530+ } else {
3531+ None
3532+ } ;
3533+
3534+ let one_from_name_map = if let Some ( name_vec) = name_vec {
3535+ let mut elements = name_vec
3536+ . iter ( )
3537+ . filter ( |n| nameditem_filter ( ( * * * n) . upcast :: < Node > ( ) , & name) )
3538+ . peekable ( ) ;
3539+ if let Some ( first) = elements. next ( ) {
3540+ if elements. peek ( ) . is_none ( ) {
3541+ Some ( first)
3542+ } else {
3543+ return ElementLookupResult :: Many ;
3544+ }
3545+ } else {
3546+ None
3547+ }
3548+ } else {
3549+ None
3550+ } ;
3551+
3552+ // We now have two elements, or one element, or the same
3553+ // element twice, or no elements.
3554+ match ( one_from_id_map, one_from_name_map) {
3555+ ( Some ( one) , None ) | ( None , Some ( one) ) => {
3556+ ElementLookupResult :: One ( DomRoot :: from_ref ( & one) )
3557+ } ,
3558+ ( Some ( one) , Some ( other) ) => {
3559+ if one == other {
3560+ ElementLookupResult :: One ( DomRoot :: from_ref ( & one) )
3561+ } else {
3562+ ElementLookupResult :: Many
3563+ }
3564+ } ,
3565+ ( None , None ) => ElementLookupResult :: None ,
3566+ }
3567+ }
34003568}
34013569
34023570impl Element {
@@ -4297,69 +4465,39 @@ impl DocumentMethods for Document {
42974465 }
42984466 impl CollectionFilter for NamedElementFilter {
42994467 fn filter ( & self , elem : & Element , _root : & Node ) -> bool {
4300- filter_by_name ( & self . name , elem. upcast ( ) )
4301- }
4302- }
4303- // https://html.spec.whatwg.org/multipage/#dom-document-nameditem-filter
4304- fn filter_by_name ( name : & Atom , node : & Node ) -> bool {
4305- // TODO faster name lookups (see #25548)
4306- let html_elem_type = match node. type_id ( ) {
4307- NodeTypeId :: Element ( ElementTypeId :: HTMLElement ( type_) ) => type_,
4308- _ => return false ,
4309- } ;
4310- let elem = match node. downcast :: < Element > ( ) {
4311- Some ( elem) => elem,
4312- None => return false ,
4313- } ;
4314- match html_elem_type {
4315- HTMLElementTypeId :: HTMLFormElement => {
4316- match elem. get_attribute ( & ns ! ( ) , & local_name ! ( "name" ) ) {
4317- Some ( ref attr) => attr. value ( ) . as_atom ( ) == name,
4318- None => false ,
4319- }
4320- } ,
4321- HTMLElementTypeId :: HTMLImageElement => {
4322- match elem. get_attribute ( & ns ! ( ) , & local_name ! ( "name" ) ) {
4323- Some ( ref attr) => {
4324- if attr. value ( ) . as_atom ( ) == name {
4325- true
4326- } else {
4327- match elem. get_attribute ( & ns ! ( ) , & local_name ! ( "id" ) ) {
4328- Some ( ref attr) => attr. value ( ) . as_atom ( ) == name,
4329- None => false ,
4330- }
4331- }
4332- } ,
4333- None => false ,
4334- }
4335- } ,
4336- // TODO: Handle <embed>, <iframe> and <object>.
4337- _ => false ,
4468+ elem. upcast :: < Node > ( ) . is_document_named_item ( & self . name )
43384469 }
43394470 }
4471+
43404472 let name = Atom :: from ( name) ;
4341- let root = self . upcast :: < Node > ( ) ;
4342- unsafe {
4343- // Step 1.
4344- let mut elements = root
4345- . traverse_preorder ( ShadowIncluding :: No )
4346- . filter ( |node| filter_by_name ( & name, & node) )
4347- . peekable ( ) ;
4348- if let Some ( first) = elements. next ( ) {
4349- if elements. peek ( ) . is_none ( ) {
4350- // TODO: Step 2.
4351- // Step 3.
4473+
4474+ match self . look_up_named_elements ( & name, Node :: is_document_named_item) {
4475+ ElementLookupResult :: None => {
4476+ return None ;
4477+ } ,
4478+ ElementLookupResult :: One ( element) => {
4479+ if let Some ( nested_proxy) = element
4480+ . downcast :: < HTMLIFrameElement > ( )
4481+ . and_then ( |iframe| iframe. GetContentWindow ( ) )
4482+ {
4483+ unsafe {
4484+ return Some ( NonNull :: new_unchecked (
4485+ nested_proxy. reflector ( ) . get_jsobject ( ) . get ( ) ,
4486+ ) ) ;
4487+ }
4488+ }
4489+ unsafe {
43524490 return Some ( NonNull :: new_unchecked (
4353- first . reflector ( ) . get_jsobject ( ) . get ( ) ,
4491+ element . reflector ( ) . get_jsobject ( ) . get ( ) ,
43544492 ) ) ;
43554493 }
4356- } else {
4357- return None ;
4358- }
4359- }
4494+ } ,
4495+ ElementLookupResult :: Many => { } ,
4496+ } ;
4497+
43604498 // Step 4.
43614499 let filter = NamedElementFilter { name : name } ;
4362- let collection = HTMLCollection :: create ( self . window ( ) , root , Box :: new ( filter) ) ;
4500+ let collection = HTMLCollection :: create ( self . window ( ) , self . upcast ( ) , Box :: new ( filter) ) ;
43634501 unsafe {
43644502 Some ( NonNull :: new_unchecked (
43654503 collection. reflector ( ) . get_jsobject ( ) . get ( ) ,
@@ -4369,8 +4507,7 @@ impl DocumentMethods for Document {
43694507
43704508 // https://html.spec.whatwg.org/multipage/#dom-tree-accessors:supported-property-names
43714509 fn SupportedPropertyNames ( & self ) -> Vec < DOMString > {
4372- // FIXME: unimplemented (https://github.com/servo/servo/issues/7273)
4373- vec ! [ ]
4510+ self . supported_property_names_impl ( Node :: is_document_named_item)
43744511 }
43754512
43764513 // https://html.spec.whatwg.org/multipage/#dom-document-clear
0 commit comments