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 use dom::bindings::codegen::Bindings::FormDataBinding::FormDataMethods;
6 use dom::bindings::error::{Error, Fallible};
7 use dom::bindings::reflector::DomObject;
8 use dom::bindings::root::DomRoot;
9 use dom::bindings::str::USVString;
10 use dom::blob::{Blob, BlobImpl};
11 use dom::formdata::FormData;
12 use dom::globalscope::GlobalScope;
13 use dom::promise::Promise;
14 use js::jsapi::JSContext;
15 use js::jsapi::JS_ClearPendingException;
16 use js::jsapi::JS_ParseJSON;
17 use js::jsapi::Value as JSValue;
18 use js::jsval::UndefinedValue;
19 use mime::{Mime, TopLevel, SubLevel};
20 use std::cell::Ref;
21 use std::rc::Rc;
22 use std::str;
23 use url::form_urlencoded;
24
25 #[derive(Clone, Copy, JSTraceable, MallocSizeOf)]
26 pub enum BodyType {
27 Blob,
28 FormData,
29 Json,
30 Text
31 }
32
33 pub enum FetchedData {
34 Text(String),
35 Json(JSValue),
36 BlobData(DomRoot<Blob>),
37 FormData(DomRoot<FormData>),
38 }
39
40 // https://fetch.spec.whatwg.org/#concept-body-consume-body
41 #[allow(unrooted_must_root)]
consume_body<T: BodyOperations + DomObject>(object: &T, body_type: BodyType) -> Rc<Promise>42 pub fn consume_body<T: BodyOperations + DomObject>(object: &T, body_type: BodyType) -> Rc<Promise> {
43 let promise = Promise::new(&object.global());
44
45 // Step 1
46 if object.get_body_used() || object.is_locked() {
47 promise.reject_error(Error::Type(
48 "The response's stream is disturbed or locked".to_string(),
49 ));
50 return promise;
51 }
52
53 object.set_body_promise(&promise, body_type);
54
55 // Steps 2-4
56 // TODO: Body does not yet have a stream.
57
58 consume_body_with_promise(object, body_type, &promise);
59
60 promise
61 }
62
63 // https://fetch.spec.whatwg.org/#concept-body-consume-body
64 #[allow(unrooted_must_root)]
consume_body_with_promise<T: BodyOperations + DomObject>(object: &T, body_type: BodyType, promise: &Promise)65 pub fn consume_body_with_promise<T: BodyOperations + DomObject>(object: &T,
66 body_type: BodyType,
67 promise: &Promise) {
68 // Step 5
69 let body = match object.take_body() {
70 Some(body) => body,
71 None => return,
72 };
73
74 let pkg_data_results = run_package_data_algorithm(object,
75 body,
76 body_type,
77 object.get_mime_type());
78
79 match pkg_data_results {
80 Ok(results) => {
81 match results {
82 FetchedData::Text(s) => promise.resolve_native(&USVString(s)),
83 FetchedData::Json(j) => promise.resolve_native(&j),
84 FetchedData::BlobData(b) => promise.resolve_native(&b),
85 FetchedData::FormData(f) => promise.resolve_native(&f),
86 };
87 },
88 Err(err) => promise.reject_error(err),
89 }
90 }
91
92 // https://fetch.spec.whatwg.org/#concept-body-package-data
93 #[allow(unsafe_code)]
run_package_data_algorithm<T: BodyOperations + DomObject>(object: &T, bytes: Vec<u8>, body_type: BodyType, mime_type: Ref<Vec<u8>>) -> Fallible<FetchedData>94 fn run_package_data_algorithm<T: BodyOperations + DomObject>(object: &T,
95 bytes: Vec<u8>,
96 body_type: BodyType,
97 mime_type: Ref<Vec<u8>>)
98 -> Fallible<FetchedData> {
99 let global = object.global();
100 let cx = global.get_cx();
101 let mime = &*mime_type;
102 match body_type {
103 BodyType::Text => run_text_data_algorithm(bytes),
104 BodyType::Json => run_json_data_algorithm(cx, bytes),
105 BodyType::Blob => run_blob_data_algorithm(&global, bytes, mime),
106 BodyType::FormData => run_form_data_algorithm(&global, bytes, mime),
107 }
108 }
109
run_text_data_algorithm(bytes: Vec<u8>) -> Fallible<FetchedData>110 fn run_text_data_algorithm(bytes: Vec<u8>) -> Fallible<FetchedData> {
111 Ok(FetchedData::Text(String::from_utf8_lossy(&bytes).into_owned()))
112 }
113
114 #[allow(unsafe_code)]
run_json_data_algorithm(cx: *mut JSContext, bytes: Vec<u8>) -> Fallible<FetchedData>115 fn run_json_data_algorithm(cx: *mut JSContext,
116 bytes: Vec<u8>) -> Fallible<FetchedData> {
117 let json_text = String::from_utf8_lossy(&bytes);
118 let json_text: Vec<u16> = json_text.encode_utf16().collect();
119 rooted!(in(cx) let mut rval = UndefinedValue());
120 unsafe {
121 if !JS_ParseJSON(cx,
122 json_text.as_ptr(),
123 json_text.len() as u32,
124 rval.handle_mut()) {
125 JS_ClearPendingException(cx);
126 // TODO: See issue #13464. Exception should be thrown instead of cleared.
127 return Err(Error::Type("Failed to parse JSON".to_string()));
128 }
129 Ok(FetchedData::Json(rval.get()))
130 }
131 }
132
run_blob_data_algorithm(root: &GlobalScope, bytes: Vec<u8>, mime: &[u8]) -> Fallible<FetchedData>133 fn run_blob_data_algorithm(root: &GlobalScope,
134 bytes: Vec<u8>,
135 mime: &[u8]) -> Fallible<FetchedData> {
136 let mime_string = if let Ok(s) = String::from_utf8(mime.to_vec()) {
137 s
138 } else {
139 "".to_string()
140 };
141 let blob = Blob::new(root, BlobImpl::new_from_bytes(bytes), mime_string);
142 Ok(FetchedData::BlobData(blob))
143 }
144
run_form_data_algorithm(root: &GlobalScope, bytes: Vec<u8>, mime: &[u8]) -> Fallible<FetchedData>145 fn run_form_data_algorithm(root: &GlobalScope, bytes: Vec<u8>, mime: &[u8]) -> Fallible<FetchedData> {
146 let mime_str = if let Ok(s) = str::from_utf8(mime) {
147 s
148 } else {
149 ""
150 };
151 let mime: Mime = mime_str.parse().map_err(
152 |_| Error::Type("Inappropriate MIME-type for Body".to_string()))?;
153 match mime {
154 // TODO
155 // ... Parser for Mime(TopLevel::Multipart, SubLevel::FormData, _)
156 // ... is not fully determined yet.
157 Mime(TopLevel::Application, SubLevel::WwwFormUrlEncoded, _) => {
158 let entries = form_urlencoded::parse(&bytes);
159 let formdata = FormData::new(None, root);
160 for (k, e) in entries {
161 formdata.Append(USVString(k.into_owned()), USVString(e.into_owned()));
162 }
163 return Ok(FetchedData::FormData(formdata));
164 },
165 _ => return Err(Error::Type("Inappropriate MIME-type for Body".to_string())),
166 }
167 }
168
169 pub trait BodyOperations {
get_body_used(&self) -> bool170 fn get_body_used(&self) -> bool;
set_body_promise(&self, p: &Rc<Promise>, body_type: BodyType)171 fn set_body_promise(&self, p: &Rc<Promise>, body_type: BodyType);
172 /// Returns `Some(_)` if the body is complete, `None` if there is more to
173 /// come.
take_body(&self) -> Option<Vec<u8>>174 fn take_body(&self) -> Option<Vec<u8>>;
is_locked(&self) -> bool175 fn is_locked(&self) -> bool;
get_mime_type(&self) -> Ref<Vec<u8>>176 fn get_mime_type(&self) -> Ref<Vec<u8>>;
177 }
178