Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement the form owner concept #15938

Merged
merged 1 commit into from Mar 15, 2017
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Implement the form owner concept

  • Loading branch information
mukilan authored and nox committed Mar 15, 2017
commit 38a61712e41ddeebb52cace6a9787735947964a4

Some generated files are not rendered by default. Learn more.

@@ -45,7 +45,7 @@ fnv = "1.0"
gfx_traits = {path = "../gfx_traits"}
heapsize = "0.3.6"
heapsize_derive = "0.1"
html5ever = {version = "0.13", features = ["heap_size", "unstable"]}
html5ever = {version = "0.14", features = ["heap_size", "unstable"]}
html5ever-atoms = {version = "0.2", features = ["heap_size"]}
hyper = "0.9.9"
hyper_serde = "0.5"
@@ -56,7 +56,7 @@ use dom::htmlbodyelement::HTMLBodyElement;
use dom::htmlcollection::{CollectionFilter, HTMLCollection};
use dom::htmlelement::HTMLElement;
use dom::htmlembedelement::HTMLEmbedElement;
use dom::htmlformelement::HTMLFormElement;
use dom::htmlformelement::{FormControl, FormControlElementHelpers, HTMLFormElement};
use dom::htmlheadelement::HTMLHeadElement;
use dom::htmlhtmlelement::HTMLHtmlElement;
use dom::htmliframeelement::HTMLIFrameElement;
@@ -68,6 +68,7 @@ use dom::location::Location;
use dom::messageevent::MessageEvent;
use dom::mouseevent::MouseEvent;
use dom::node::{self, CloneChildrenFlag, Node, NodeDamage, window_from_node, IS_IN_DOC, LayoutNodeHelpers};
use dom::node::VecPreOrderInsertionHelper;
use dom::nodeiterator::NodeIterator;
use dom::nodelist::NodeList;
use dom::pagetransitionevent::PageTransitionEvent;
@@ -120,7 +121,7 @@ use servo_url::{ImmutableOrigin, MutableOrigin, ServoUrl};
use std::ascii::AsciiExt;
use std::borrow::ToOwned;
use std::cell::{Cell, Ref, RefMut};
use std::collections::{HashMap, VecDeque};
use std::collections::{HashMap, HashSet, VecDeque};
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::default::Default;
use std::iter::once;
@@ -313,6 +314,12 @@ pub struct Document {
dom_count: Cell<u32>,
/// Entry node for fullscreen.
fullscreen_element: MutNullableJS<Element>,
/// Map from ID to set of form control elements that have that ID as
/// their 'form' content attribute. Used to reset form controls
/// whenever any element with the same ID as the form attribute
/// is inserted or removed from the document.
/// See https://html.spec.whatwg.org/multipage/#form-owner
form_id_listener_map: DOMRefCell<HashMap<Atom, HashSet<JS<Element>>>>,
}

#[derive(JSTraceable, HeapSizeOf)]
@@ -576,20 +583,26 @@ impl Document {
self,
to_unregister,
id);
let mut id_map = self.id_map.borrow_mut();
let is_empty = match id_map.get_mut(&id) {
None => false,
Some(elements) => {
let position = elements.iter()
.position(|element| &**element == to_unregister)
.expect("This element should be in registered.");
elements.remove(position);
elements.is_empty()
// Limit the scope of the borrow because id_map might be borrowed again by
// GetElementById through the following sequence of calls
// reset_form_owner_for_listeners -> reset_form_owner -> GetElementById
{
let mut id_map = self.id_map.borrow_mut();
let is_empty = match id_map.get_mut(&id) {
None => false,
Some(elements) => {
let position = elements.iter()
.position(|element| &**element == to_unregister)
.expect("This element should be in registered.");
elements.remove(position);
elements.is_empty()
}
};
if is_empty {
id_map.remove(&id);
}
};
if is_empty {
id_map.remove(&id);
}
self.reset_form_owner_for_listeners(&id);
}

/// Associate an element present in this document with the provided id.
@@ -601,34 +614,34 @@ impl Document {
assert!(element.upcast::<Node>().is_in_doc());
assert!(!id.is_empty());

let mut id_map = self.id_map.borrow_mut();

let root = self.GetDocumentElement()
.expect("The element is in the document, so there must be a document \
element.");

match id_map.entry(id) {
Vacant(entry) => {
entry.insert(vec![JS::from_ref(element)]);
}
Occupied(entry) => {
let elements = entry.into_mut();
// Limit the scope of the borrow because id_map might be borrowed again by
// GetElementById through the following sequence of calls
// reset_form_owner_for_listeners -> reset_form_owner -> GetElementById
{
let mut id_map = self.id_map.borrow_mut();
let mut elements = id_map.entry(id.clone()).or_insert(Vec::new());
elements.insert_pre_order(element, root.r().upcast::<Node>());
}
self.reset_form_owner_for_listeners(&id);
}

let new_node = element.upcast::<Node>();
let mut head: usize = 0;
let root = root.upcast::<Node>();
for node in root.traverse_preorder() {
if let Some(elem) = node.downcast() {
if &*(*elements)[head] == elem {
head += 1;
}
if new_node == &*node || head == elements.len() {
break;
}
}
}
pub fn register_form_id_listener<T: ?Sized + FormControl>(&self, id: DOMString, listener: &T) {
let mut map = self.form_id_listener_map.borrow_mut();
let listener = listener.to_element();
let mut set = map.entry(Atom::from(id)).or_insert(HashSet::new());
set.insert(JS::from_ref(listener));
}

elements.insert(head, JS::from_ref(element));
pub fn unregister_form_id_listener<T: ?Sized + FormControl>(&self, id: DOMString, listener: &T) {
let mut map = self.form_id_listener_map.borrow_mut();
if let Occupied(mut entry) = map.entry(Atom::from(id)) {
entry.get_mut().remove(&JS::from_ref(listener.to_element()));
if entry.get().is_empty() {
entry.remove();
}
}
}
@@ -2101,6 +2114,7 @@ impl Document {
spurious_animation_frames: Cell::new(0),
dom_count: Cell::new(1),
fullscreen_element: MutNullableJS::new(None),
form_id_listener_map: Default::default(),
}
}

@@ -2414,6 +2428,17 @@ impl Document {
}
}
}

fn reset_form_owner_for_listeners(&self, id: &Atom) {
let map = self.form_id_listener_map.borrow();
if let Some(listeners) = map.get(id) {
for listener in listeners {
listener.r().as_maybe_form_control()
.expect("Element must be a form control")
.reset_form_owner();
}
}
}
}


@@ -19,6 +19,7 @@ use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, ScrollToOptions};
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::codegen::UnionTypes::NodeOrString;
use dom::bindings::conversions::DerivedFrom;
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
use dom::bindings::js::{JS, LayoutJS, MutNullableJS};
@@ -44,6 +45,7 @@ use dom::htmlcollection::HTMLCollection;
use dom::htmlelement::HTMLElement;
use dom::htmlfieldsetelement::HTMLFieldSetElement;
use dom::htmlfontelement::{HTMLFontElement, HTMLFontElementLayoutHelpers};
use dom::htmlformelement::FormControlElementHelpers;
use dom::htmlhrelement::{HTMLHRElement, HTMLHRLayoutHelpers};
use dom::htmliframeelement::{HTMLIFrameElement, HTMLIFrameElementLayoutMethods};
use dom::htmlimageelement::{HTMLImageElement, LayoutHTMLImageElementHelpers};
@@ -1360,6 +1362,14 @@ impl Element {
let document = document_from_node(self);
document.get_allow_fullscreen()
}

// https://html.spec.whatwg.org/multipage/#home-subtree
pub fn is_in_same_home_subtree<T>(&self, other: &T) -> bool
where T: DerivedFrom<Element> + DomObject
{
let other = other.upcast::<Element>();
self.root_element() == other.root_element()
}
}

impl ElementMethods for Element {
@@ -2240,6 +2250,10 @@ impl VirtualMethods for Element {
s.bind_to_tree(tree_in_doc);
}

if let Some(f) = self.as_maybe_form_control() {
f.bind_form_control_to_tree();
}

if !tree_in_doc {
return;
}
@@ -2255,6 +2269,10 @@ impl VirtualMethods for Element {
fn unbind_from_tree(&self, context: &UnbindContext) {
self.super_type().unwrap().unbind_from_tree(context);

if let Some(f) = self.as_maybe_form_control() {
f.unbind_form_control_from_tree();
}

if !context.tree_in_doc {
return;
}
@@ -7,7 +7,7 @@ use dom::attr::Attr;
use dom::bindings::codegen::Bindings::HTMLButtonElementBinding;
use dom::bindings::codegen::Bindings::HTMLButtonElementBinding::HTMLButtonElementMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::Root;
use dom::bindings::js::{MutNullableJS, Root};
use dom::bindings::str::DOMString;
use dom::document::Document;
use dom::element::{AttributeMutation, Element};
@@ -26,6 +26,7 @@ use dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
use html5ever_atoms::LocalName;
use std::cell::Cell;
use std::default::Default;
use style::element_state::*;

#[derive(JSTraceable, PartialEq, Copy, Clone)]
@@ -40,7 +41,8 @@ enum ButtonType {
#[dom_struct]
pub struct HTMLButtonElement {
htmlelement: HTMLElement,
button_type: Cell<ButtonType>
button_type: Cell<ButtonType>,
form_owner: MutNullableJS<HTMLFormElement>,
}

impl HTMLButtonElement {
@@ -51,7 +53,8 @@ impl HTMLButtonElement {
htmlelement:
HTMLElement::new_inherited_with_state(IN_ENABLED_STATE,
local_name, prefix, document),
button_type: Cell::new(ButtonType::Submit)
button_type: Cell::new(ButtonType::Submit),
form_owner: Default::default(),
}
}

@@ -211,6 +214,9 @@ impl VirtualMethods for HTMLButtonElement {
self.button_type.set(ButtonType::Submit);
}
}
},
&local_name!("form") => {
self.form_attribute_mutated(mutation);
}
_ => {},
}
@@ -237,7 +243,19 @@ impl VirtualMethods for HTMLButtonElement {
}
}

impl FormControl for HTMLButtonElement {}
impl FormControl for HTMLButtonElement {
fn form_owner(&self) -> Option<Root<HTMLFormElement>> {
self.form_owner.get()
}

fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
self.form_owner.set(form);
}

fn to_element<'a>(&'a self) -> &'a Element {
self.upcast::<Element>()
}
}

impl Validatable for HTMLButtonElement {
fn is_instance_validatable(&self) -> bool {
@@ -6,7 +6,7 @@ use dom::attr::Attr;
use dom::bindings::codegen::Bindings::HTMLFieldSetElementBinding;
use dom::bindings::codegen::Bindings::HTMLFieldSetElementBinding::HTMLFieldSetElementMethods;
use dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
use dom::bindings::js::Root;
use dom::bindings::js::{MutNullableJS, Root};
use dom::bindings::str::DOMString;
use dom::document::Document;
use dom::element::{AttributeMutation, Element};
@@ -19,11 +19,13 @@ use dom::validitystate::ValidityState;
use dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
use html5ever_atoms::LocalName;
use std::default::Default;
use style::element_state::*;

#[dom_struct]
pub struct HTMLFieldSetElement {
htmlelement: HTMLElement
htmlelement: HTMLElement,
form_owner: MutNullableJS<HTMLFormElement>,
}

impl HTMLFieldSetElement {
@@ -33,7 +35,8 @@ impl HTMLFieldSetElement {
HTMLFieldSetElement {
htmlelement:
HTMLElement::new_inherited_with_state(IN_ENABLED_STATE,
local_name, prefix, document)
local_name, prefix, document),
form_owner: Default::default(),
}
}

@@ -148,9 +151,24 @@ impl VirtualMethods for HTMLFieldSetElement {
}
}
},
&local_name!("form") => {
self.form_attribute_mutated(mutation);
},
_ => {},
}
}
}

impl FormControl for HTMLFieldSetElement {}
impl FormControl for HTMLFieldSetElement {
fn form_owner(&self) -> Option<Root<HTMLFormElement>> {
self.form_owner.get()
}

fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
self.form_owner.set(form);
}

fn to_element<'a>(&'a self) -> &'a Element {
self.upcast::<Element>()
}
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.