Skip to content

Commit

Permalink
Auto merge of #19511 - DonatJR:implement-read-methods-on-filereadersy…
Browse files Browse the repository at this point in the history
…nc, r=jdm

Implement read methods on FileReaderSync

<!-- Please describe your changes on the following line: -->
Implemented the read methods on the FileReaderSync struct according to https://w3c.github.io/FileAPI/#dfn-FileReaderSync

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [x] These changes fix #15114

<!-- Either: -->
- [x] There are tests for these changes

<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/19511)
<!-- Reviewable:end -->
  • Loading branch information
bors-servo committed Jun 4, 2018
2 parents 0b6df7e + 0fdafb0 commit 652a177
Show file tree
Hide file tree
Showing 9 changed files with 278 additions and 105 deletions.
3 changes: 3 additions & 0 deletions components/script/dom/bindings/error.rs
Expand Up @@ -69,6 +69,8 @@ pub enum Error {
TypeMismatch,
/// InvalidModificationError DOMException
InvalidModification,
/// NotReadableError DOMException
NotReadable,

/// TypeError JavaScript Error
Type(String),
Expand Down Expand Up @@ -110,6 +112,7 @@ pub unsafe fn throw_dom_exception(cx: *mut JSContext, global: &GlobalScope, resu
Error::QuotaExceeded => DOMErrorName::QuotaExceededError,
Error::TypeMismatch => DOMErrorName::TypeMismatchError,
Error::InvalidModification => DOMErrorName::InvalidModificationError,
Error::NotReadable => DOMErrorName::NotReadableError,
Error::Type(message) => {
assert!(!JS_IsExceptionPending(cx));
throw_type_error(cx, &message);
Expand Down
2 changes: 2 additions & 0 deletions components/script/dom/domexception.rs
Expand Up @@ -35,6 +35,7 @@ pub enum DOMErrorName {
TimeoutError = DOMExceptionConstants::TIMEOUT_ERR,
InvalidNodeTypeError = DOMExceptionConstants::INVALID_NODE_TYPE_ERR,
DataCloneError = DOMExceptionConstants::DATA_CLONE_ERR,
NotReadableError = DOMExceptionConstants::NOT_READABLE_ERR,
}

#[dom_struct]
Expand Down Expand Up @@ -94,6 +95,7 @@ impl DOMExceptionMethods for DOMException {
DOMErrorName::InvalidNodeTypeError =>
"The supplied node is incorrect or has an incorrect ancestor for this operation.",
DOMErrorName::DataCloneError => "The object can not be cloned.",
DOMErrorName::NotReadableError => "The I/O read operation failed."
};

DOMString::from(message)
Expand Down
208 changes: 139 additions & 69 deletions components/script/dom/filereader.rs
Expand Up @@ -51,12 +51,15 @@ pub type TrustedFileReader = Trusted<FileReader>;
pub struct ReadMetaData {
pub blobtype: String,
pub label: Option<String>,
pub function: FileReaderFunction
pub function: FileReaderFunction,
}

impl ReadMetaData {
pub fn new(blobtype: String,
label: Option<String>, function: FileReaderFunction) -> ReadMetaData {
pub fn new(
blobtype: String,
label: Option<String>,
function: FileReaderFunction,
) -> ReadMetaData {
ReadMetaData {
blobtype: blobtype,
label: label,
Expand All @@ -82,6 +85,54 @@ pub enum FileReaderResult {
String(DOMString),
}

pub struct FileReaderSharedFunctionality;

impl FileReaderSharedFunctionality {
pub fn dataurl_format(blob_contents: &[u8], blob_type: String) -> DOMString {
let base64 = base64::encode(&blob_contents);

let dataurl = if blob_type.is_empty() {
format!("data:base64,{}", base64)
} else {
format!("data:{};base64,{}", blob_type, base64)
};

DOMString::from(dataurl)
}

pub fn text_decode(
blob_contents: &[u8],
blob_type: &str,
blob_label: &Option<String>,
) -> DOMString {
//https://w3c.github.io/FileAPI/#encoding-determination
// Steps 1 & 2 & 3
let mut encoding = blob_label
.as_ref()
.map(|string| string.as_bytes())
.and_then(Encoding::for_label);

// Step 4 & 5
encoding = encoding.or_else(|| {
let resultmime = blob_type.parse::<Mime>().ok();
resultmime.and_then(|Mime(_, _, ref parameters)| {
parameters
.iter()
.find(|&&(ref k, _)| &Attr::Charset == k)
.and_then(|&(_, ref v)| Encoding::for_label(v.as_str().as_bytes()))
})
});

// Step 6
let enc = encoding.unwrap_or(UTF_8);

let convert = blob_contents;
// Step 7
let (output, _, _) = enc.decode(convert);
DOMString::from(output)
}
}

#[dom_struct]
pub struct FileReader {
eventtarget: EventTarget,
Expand All @@ -103,16 +154,23 @@ impl FileReader {
}

pub fn new(global: &GlobalScope) -> DomRoot<FileReader> {
reflect_dom_object(Box::new(FileReader::new_inherited()),
global, FileReaderBinding::Wrap)
reflect_dom_object(
Box::new(FileReader::new_inherited()),
global,
FileReaderBinding::Wrap,
)
}

pub fn Constructor(global: &GlobalScope) -> Fallible<DomRoot<FileReader>> {
Ok(FileReader::new(global))
}

//https://w3c.github.io/FileAPI/#dfn-error-steps
pub fn process_read_error(filereader: TrustedFileReader, gen_id: GenerationId, error: DOMErrorName) {
pub fn process_read_error(
filereader: TrustedFileReader,
gen_id: GenerationId,
error: DOMErrorName,
) {
let fr = filereader.root();

macro_rules! return_on_abort(
Expand Down Expand Up @@ -174,8 +232,12 @@ impl FileReader {

// https://w3c.github.io/FileAPI/#dfn-readAsText
#[allow(unsafe_code)]
pub fn process_read_eof(filereader: TrustedFileReader, gen_id: GenerationId,
data: ReadMetaData, blob_contents: Arc<Vec<u8>>) {
pub fn process_read_eof(
filereader: TrustedFileReader,
gen_id: GenerationId,
data: ReadMetaData,
blob_contents: Arc<Vec<u8>>,
) {
let fr = filereader.root();

macro_rules! return_on_abort(
Expand All @@ -192,13 +254,21 @@ impl FileReader {
// Step 8.2

match data.function {
FileReaderFunction::ReadAsDataUrl =>
FileReader::perform_readasdataurl(&fr.result, data, &blob_contents),
FileReaderFunction::ReadAsText =>
FileReader::perform_readastext(&fr.result, data, &blob_contents),
FileReaderFunction::ReadAsDataUrl => {
FileReader::perform_readasdataurl(&fr.result, data, &blob_contents)
},
FileReaderFunction::ReadAsText => {
FileReader::perform_readastext(&fr.result, data, &blob_contents)
},
FileReaderFunction::ReadAsArrayBuffer => {
let _ac = JSAutoCompartment::new(fr.global().get_cx(), *fr.reflector().get_jsobject());
FileReader::perform_readasarraybuffer(&fr.result, fr.global().get_cx(), data, &blob_contents)
let _ac =
JSAutoCompartment::new(fr.global().get_cx(), *fr.reflector().get_jsobject());
FileReader::perform_readasarraybuffer(
&fr.result,
fr.global().get_cx(),
data,
&blob_contents,
)
},
};

Expand All @@ -215,55 +285,43 @@ impl FileReader {
}

// https://w3c.github.io/FileAPI/#dfn-readAsText
fn perform_readastext(result: &DomRefCell<Option<FileReaderResult>>, data: ReadMetaData, blob_bytes: &[u8]) {
fn perform_readastext(
result: &DomRefCell<Option<FileReaderResult>>,
data: ReadMetaData,
blob_bytes: &[u8],
) {
let blob_label = &data.label;
let blob_type = &data.blobtype;

//https://w3c.github.io/FileAPI/#encoding-determination
// Steps 1 & 2 & 3
let mut encoding = blob_label.as_ref()
.map(|string| string.as_bytes())
.and_then(Encoding::for_label);

// Step 4 & 5
encoding = encoding.or_else(|| {
let resultmime = blob_type.parse::<Mime>().ok();
resultmime.and_then(|Mime(_, _, ref parameters)| {
parameters.iter()
.find(|&&(ref k, _)| &Attr::Charset == k)
.and_then(|&(_, ref v)| Encoding::for_label(v.as_str().as_bytes()))
})
});

// Step 6
let enc = encoding.unwrap_or(UTF_8);

let convert = blob_bytes;
// Step 7
let (output, _, _) = enc.decode(convert);
*result.borrow_mut() = Some(FileReaderResult::String(DOMString::from(output)));
let output = FileReaderSharedFunctionality::text_decode(blob_bytes, blob_type, blob_label);
*result.borrow_mut() = Some(FileReaderResult::String(output));
}

//https://w3c.github.io/FileAPI/#dfn-readAsDataURL
fn perform_readasdataurl(result: &DomRefCell<Option<FileReaderResult>>, data: ReadMetaData, bytes: &[u8]) {
let base64 = base64::encode(bytes);

let output = if data.blobtype.is_empty() {
format!("data:base64,{}", base64)
} else {
format!("data:{};base64,{}", data.blobtype, base64)
};

*result.borrow_mut() = Some(FileReaderResult::String(DOMString::from(output)));
fn perform_readasdataurl(
result: &DomRefCell<Option<FileReaderResult>>,
data: ReadMetaData,
bytes: &[u8],
) {
let output = FileReaderSharedFunctionality::dataurl_format(bytes, data.blobtype);

*result.borrow_mut() = Some(FileReaderResult::String(output));
}

// https://w3c.github.io/FileAPI/#dfn-readAsArrayBuffer
#[allow(unsafe_code)]
fn perform_readasarraybuffer(result: &DomRefCell<Option<FileReaderResult>>,
cx: *mut JSContext, _: ReadMetaData, bytes: &[u8]) {
fn perform_readasarraybuffer(
result: &DomRefCell<Option<FileReaderResult>>,
cx: *mut JSContext,
_: ReadMetaData,
bytes: &[u8],
) {
unsafe {
rooted!(in(cx) let mut array_buffer = ptr::null_mut::<JSObject>());
assert!(ArrayBuffer::create(cx, CreateWith::Slice(bytes), array_buffer.handle_mut()).is_ok());
assert!(
ArrayBuffer::create(cx, CreateWith::Slice(bytes), array_buffer.handle_mut())
.is_ok()
);

*result.borrow_mut() = Some(FileReaderResult::ArrayBuffer(Heap::default()));

Expand Down Expand Up @@ -335,13 +393,12 @@ impl FileReaderMethods for FileReader {
// https://w3c.github.io/FileAPI/#dfn-result
unsafe fn GetResult(&self, _: *mut JSContext) -> Option<StringOrObject> {
self.result.borrow().as_ref().map(|r| match *r {
FileReaderResult::String(ref string) =>
StringOrObject::String(string.clone()),
FileReaderResult::String(ref string) => StringOrObject::String(string.clone()),
FileReaderResult::ArrayBuffer(ref arr_buffer) => {
let result = RootedTraceableBox::new(Heap::default());
result.set((*arr_buffer.ptr.get()).to_object());
StringOrObject::Object(result)
}
},
})
}

Expand All @@ -351,12 +408,17 @@ impl FileReaderMethods for FileReader {
}
}


impl FileReader {
fn dispatch_progress_event(&self, type_: Atom, loaded: u64, total: Option<u64>) {
let progressevent = ProgressEvent::new(&self.global(),
type_, EventBubbles::DoesNotBubble, EventCancelable::NotCancelable,
total.is_some(), loaded, total.unwrap_or(0));
let progressevent = ProgressEvent::new(
&self.global(),
type_,
EventBubbles::DoesNotBubble,
EventCancelable::NotCancelable,
total.is_some(),
loaded,
total.unwrap_or(0),
);
progressevent.upcast::<Event>().fire(self.upcast());
}

Expand All @@ -365,7 +427,12 @@ impl FileReader {
self.generation_id.set(GenerationId(prev_id + 1));
}

fn read(&self, function: FileReaderFunction, blob: &Blob, label: Option<DOMString>) -> ErrorResult {
fn read(
&self,
function: FileReaderFunction,
blob: &Blob,
label: Option<DOMString>,
) -> ErrorResult {
// Step 1
if self.ready_state.get() == FileReaderReadyState::Loading {
return Err(Error::InvalidState);
Expand All @@ -388,16 +455,19 @@ impl FileReader {
let canceller = global.task_canceller();
let task_source = global.file_reading_task_source();

thread::Builder::new().name("file reader async operation".to_owned()).spawn(move || {
perform_annotated_read_operation(
gen_id,
load_data,
blob_contents,
fr,
task_source,
canceller,
)
}).expect("Thread spawning failed");
thread::Builder::new()
.name("file reader async operation".to_owned())
.spawn(move || {
perform_annotated_read_operation(
gen_id,
load_data,
blob_contents,
fr,
task_source,
canceller,
)
})
.expect("Thread spawning failed");

Ok(())
}
Expand Down

0 comments on commit 652a177

Please sign in to comment.