aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom')
-rw-r--r--components/script/dom/htmlformelement.rs138
-rw-r--r--components/script/dom/htmliframeelement.rs13
-rw-r--r--components/script/dom/htmlinputelement.rs5
-rw-r--r--components/script/dom/htmlselectelement.rs4
4 files changed, 137 insertions, 23 deletions
diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs
index 999ccacc8a8..0e8b6174ab5 100644
--- a/components/script/dom/htmlformelement.rs
+++ b/components/script/dom/htmlformelement.rs
@@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::attr::AttrValue;
+use dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use dom::bindings::codegen::Bindings::HTMLButtonElementBinding::HTMLButtonElementMethods;
@@ -15,10 +16,12 @@ use dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, Nod
use dom::bindings::js::{JS, MutNullableHeap, Root};
use dom::bindings::refcounted::Trusted;
use dom::bindings::reflector::Reflectable;
+use dom::blob::Blob;
use dom::document::Document;
use dom::element::Element;
use dom::event::{EventBubbles, EventCancelable};
use dom::eventtarget::EventTarget;
+use dom::file::File;
use dom::htmlbuttonelement::HTMLButtonElement;
use dom::htmlcollection::CollectionFilter;
use dom::htmldatalistelement::HTMLDataListElement;
@@ -33,19 +36,23 @@ use dom::htmltextareaelement::HTMLTextAreaElement;
use dom::node::{Node, document_from_node, window_from_node};
use dom::virtualmethods::VirtualMethods;
use dom::window::Window;
-use hyper::header::ContentType;
+use encoding::EncodingRef;
+use encoding::all::UTF_8;
+use encoding::label::encoding_from_whatwg_label;
+use hyper::header::{Charset, ContentDisposition, ContentType, DispositionParam, DispositionType};
use hyper::method::Method;
-use hyper::mime;
use msg::constellation_msg::{LoadData, PipelineId};
+use rand::random;
use script_runtime::ScriptChan;
use script_thread::{MainThreadScriptMsg, Runnable};
use std::borrow::ToOwned;
use std::cell::Cell;
+use std::str::from_utf8;
use std::sync::mpsc::Sender;
use string_cache::Atom;
use task_source::dom_manipulation::DOMManipulationTask;
use url::form_urlencoded;
-use util::str::DOMString;
+use util::str::{DOMString, split_html_space_chars};
#[derive(JSTraceable, PartialEq, Clone, Copy, HeapSizeOf)]
pub struct GenerationId(u32);
@@ -237,6 +244,86 @@ pub enum ResetFrom {
impl HTMLFormElement {
+ fn generate_boundary(&self) -> String {
+ let i1 = random::<u32>();
+ let i2 = random::<u32>();
+
+ format!("---------------------------{0}{1}", i1, i2)
+ }
+
+ // https://html.spec.whatwg.org/multipage/#picking-an-encoding-for-the-form
+ fn pick_encoding(&self) -> EncodingRef {
+ // Step 2
+ if self.upcast::<Element>().has_attribute(&atom!("accept-charset")) {
+ // Substep 1
+ let input = self.upcast::<Element>().get_string_attribute(&atom!("accept-charset"));
+
+ // Substep 2, 3, 4
+ let mut candidate_encodings = split_html_space_chars(&*input).filter_map(encoding_from_whatwg_label);
+
+ // Substep 5, 6
+ return candidate_encodings.next().unwrap_or(UTF_8);
+ }
+
+ // Step 1, 3
+ document_from_node(self).encoding()
+ }
+
+ // https://html.spec.whatwg.org/multipage/#multipart/form-data-encoding-algorithm
+ fn encode_form_data(&self, form_data: &mut Vec<FormDatum>,
+ encoding: Option<EncodingRef>,
+ boundary: String) -> String {
+ // Step 1
+ let mut result = "".to_owned();
+
+ // Step 2
+ // (maybe take encoding as input)
+ let encoding = encoding.unwrap_or(self.pick_encoding());
+
+ // Step 3
+ let charset = &*encoding.whatwg_name().unwrap();
+
+ // Step 4
+ for entry in form_data.iter_mut() {
+ // Substep 1
+ if entry.name == "_charset_" && entry.ty == "hidden" {
+ entry.value = FormDatumValue::String(DOMString::from(charset.clone()));
+ }
+ // TODO: Substep 2
+
+ // Step 5
+ // https://tools.ietf.org/html/rfc7578#section-4
+ result.push_str(&*format!("\r\n--{}\r\n", boundary));
+ let mut content_disposition = ContentDisposition {
+ disposition: DispositionType::Ext("form-data".to_owned()),
+ parameters: vec![DispositionParam::Ext("name".to_owned(), String::from(entry.name.clone()))]
+ };
+
+ match entry.value {
+ FormDatumValue::String(ref s) =>
+ result.push_str(&*format!("Content-Disposition: {}\r\n\r\n{}",
+ content_disposition,
+ s)),
+ FormDatumValue::File(ref f) => {
+ content_disposition.parameters.push(
+ DispositionParam::Filename(Charset::Ext(String::from(charset.clone())),
+ None,
+ f.name().clone().into()));
+ let content_type = ContentType(f.upcast::<Blob>().Type().parse().unwrap());
+ result.push_str(&*format!("Content-Disposition: {}\r\n{}\r\n\r\n",
+ content_disposition,
+ content_type));
+
+ result.push_str(from_utf8(&f.upcast::<Blob>().get_data().get_bytes()).unwrap());
+ }
+ }
+ }
+
+ result.push_str(&*format!("\r\n--{}--", boundary));
+
+ return result;
+ }
+
/// [Form submission](https://html.spec.whatwg.org/multipage/#concept-form-submit)
pub fn submit(&self, submit_method_flag: SubmittedFrom, submitter: FormSubmitter) {
// Step 1
@@ -264,7 +351,7 @@ impl HTMLFormElement {
}
}
// Step 6
- let form_data = self.get_form_dataset(Some(submitter));
+ let mut form_data = self.get_form_dataset(Some(submitter));
// Step 7
let mut action = submitter.action();
// Step 8
@@ -287,13 +374,21 @@ impl HTMLFormElement {
let parsed_data = match enctype {
FormEncType::UrlEncoded => {
- let mime: mime::Mime = "application/x-www-form-urlencoded".parse().unwrap();
- load_data.headers.set(ContentType(mime));
+ load_data.headers.set(ContentType::form_url_encoded());
+
form_urlencoded::Serializer::new(String::new())
- .extend_pairs(form_data.into_iter().map(|field| (field.name, field.value)))
+ .extend_pairs(form_data.into_iter().map(|field| (field.name.clone(), field.value_str())))
.finish()
}
- _ => "".to_owned() // TODO: Add serializers for the other encoding types
+ FormEncType::FormDataEncoded => {
+ let boundary = self.generate_boundary();
+ let mime = mime!(Multipart / FormData; Boundary =(&boundary));
+ load_data.headers.set(ContentType(mime));
+
+ self.encode_form_data(&mut form_data, None, boundary)
+ }
+ // TODO: Support plain text encoding
+ FormEncType::TextPlainEncoded => "".to_owned()
};
// Step 18
@@ -311,6 +406,7 @@ impl HTMLFormElement {
("http", FormMethod::FormPost) | ("https", FormMethod::FormPost) => {
load_data.method = Method::Post;
load_data.data = Some(parsed_data.into_bytes());
+ self.plan_to_navigate(load_data, &win);
}
// https://html.spec.whatwg.org/multipage/#submit-get-action
("file", _) | ("about", _) | ("data", FormMethod::FormGet) |
@@ -435,7 +531,7 @@ impl HTMLFormElement {
data_set.push(FormDatum {
ty: textarea.Type(),
name: name,
- value: textarea.Value()
+ value: FormDatumValue::String(textarea.Value())
});
}
}
@@ -489,7 +585,10 @@ impl HTMLFormElement {
"file" | "textarea" => (),
_ => {
datum.name = clean_crlf(&datum.name);
- datum.value = clean_crlf(&datum.value);
+ datum.value = FormDatumValue::String(clean_crlf( match datum.value {
+ FormDatumValue::String(ref s) => s,
+ FormDatumValue::File(_) => unreachable!()
+ }));
}
}
};
@@ -544,12 +643,25 @@ impl HTMLFormElement {
}
-// TODO: add file support
-#[derive(HeapSizeOf)]
+pub enum FormDatumValue {
+ File(Root<File>),
+ String(DOMString)
+}
+
+// #[derive(HeapSizeOf)]
pub struct FormDatum {
pub ty: DOMString,
pub name: DOMString,
- pub value: DOMString
+ pub value: FormDatumValue
+}
+
+impl FormDatum {
+ pub fn value_str(&self) -> String {
+ match self.value {
+ FormDatumValue::String(ref s) => String::from(s.clone()),
+ FormDatumValue::File(ref f) => String::from(f.name().clone())
+ }
+ }
}
#[derive(Copy, Clone, HeapSizeOf)]
diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs
index 89e7d6f89e8..bf3ff02487c 100644
--- a/components/script/dom/htmliframeelement.rs
+++ b/components/script/dom/htmliframeelement.rs
@@ -33,7 +33,7 @@ use ipc_channel::ipc;
use js::jsapi::{JSAutoCompartment, JSAutoRequest, RootedValue, JSContext, MutableHandleValue};
use js::jsval::{UndefinedValue, NullValue};
use layout_interface::ReflowQueryType;
-use msg::constellation_msg::{ConstellationChan};
+use msg::constellation_msg::{ConstellationChan, LoadData};
use msg::constellation_msg::{NavigationDirection, PipelineId, SubpageId};
use net_traits::response::HttpsState;
use page::IterablePage;
@@ -98,7 +98,7 @@ impl HTMLIFrameElement {
(subpage_id, old_subpage_id)
}
- pub fn navigate_or_reload_child_browsing_context(&self, url: Option<Url>) {
+ pub fn navigate_or_reload_child_browsing_context(&self, load_data: Option<LoadData>) {
let sandboxed = if self.is_sandboxed() {
IFrameSandboxed
} else {
@@ -115,8 +115,8 @@ impl HTMLIFrameElement {
//TODO(#9592): Deal with the case where an iframe is being reloaded so url is None.
// The iframe should always have access to the nested context's active
// document URL through the browsing context.
- if let Some(ref url) = url {
- *load_blocker = Some(LoadBlocker::new(&*document, LoadType::Subframe(url.clone())));
+ if let Some(ref load_data) = load_data {
+ *load_blocker = Some(LoadBlocker::new(&*document, LoadType::Subframe(load_data.url.clone())));
}
let window = window_from_node(self);
@@ -127,7 +127,7 @@ impl HTMLIFrameElement {
let ConstellationChan(ref chan) = *window.constellation_chan();
let load_info = IFrameLoadInfo {
- url: url,
+ load_data: load_data,
containing_pipeline_id: window.pipeline(),
new_subpage_id: new_subpage_id,
old_subpage_id: old_subpage_id,
@@ -149,7 +149,8 @@ impl HTMLIFrameElement {
None => Url::parse("about:blank").unwrap(),
};
- self.navigate_or_reload_child_browsing_context(Some(url));
+ // TODO - loaddata here should have referrer info (not None, None)
+ self.navigate_or_reload_child_browsing_context(Some(LoadData::new(url, None, None)));
}
#[allow(unsafe_code)]
diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs
index d42709b487d..0e675b35b27 100644
--- a/components/script/dom/htmlinputelement.rs
+++ b/components/script/dom/htmlinputelement.rs
@@ -22,7 +22,7 @@ use dom::event::{Event, EventBubbles, EventCancelable};
use dom::eventtarget::EventTarget;
use dom::htmlelement::HTMLElement;
use dom::htmlfieldsetelement::HTMLFieldSetElement;
-use dom::htmlformelement::{FormControl, FormDatum, FormSubmitter, HTMLFormElement};
+use dom::htmlformelement::{FormDatumValue, FormControl, FormDatum, FormSubmitter, HTMLFormElement};
use dom::htmlformelement::{ResetFrom, SubmittedFrom};
use dom::keyboardevent::KeyboardEvent;
use dom::node::{Node, NodeDamage, UnbindContext};
@@ -625,6 +625,7 @@ impl HTMLInputElement {
atom!("radio") | atom!("checkbox") => if !self.Checked() || name.is_empty() {
return None;
},
+
atom!("image") | atom!("file") => return None, // Unimplemented
// Step 3.1: it's not the "Image Button" and doesn't have a name attribute.
_ => if name.is_empty() {
@@ -637,7 +638,7 @@ impl HTMLInputElement {
Some(FormDatum {
ty: DOMString::from(&*ty), // FIXME(ajeffrey): Convert directly from Atoms to DOMStrings
name: name,
- value: self.Value()
+ value: FormDatumValue::String(self.Value())
})
}
diff --git a/components/script/dom/htmlselectelement.rs b/components/script/dom/htmlselectelement.rs
index 45161f5ee2d..cfce718bcbe 100644
--- a/components/script/dom/htmlselectelement.rs
+++ b/components/script/dom/htmlselectelement.rs
@@ -14,7 +14,7 @@ use dom::document::Document;
use dom::element::{AttributeMutation, Element};
use dom::htmlelement::HTMLElement;
use dom::htmlfieldsetelement::HTMLFieldSetElement;
-use dom::htmlformelement::{FormControl, FormDatum, HTMLFormElement};
+use dom::htmlformelement::{FormDatumValue, FormControl, FormDatum, HTMLFormElement};
use dom::htmloptionelement::HTMLOptionElement;
use dom::node::{Node, UnbindContext, window_from_node};
use dom::nodelist::NodeList;
@@ -94,7 +94,7 @@ impl HTMLSelectElement {
data_set.push(FormDatum {
ty: self.Type(),
name: self.Name(),
- value: opt.Value()
+ value: FormDatumValue::String(opt.Value())
});
}
}