mirror of
https://github.com/servo/servo.git
synced 2025-06-12 10:24:43 +00:00
Issue #1820 - Improve the Blob implementation
This commit is contained in:
parent
bdb3a2538b
commit
f2885b8fc7
6 changed files with 125 additions and 22 deletions
|
@ -3,11 +3,15 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use dom::bindings::codegen::InheritTypes::FileDerived;
|
use dom::bindings::codegen::InheritTypes::FileDerived;
|
||||||
use dom::bindings::global::GlobalRef;
|
use dom::bindings::global::{GlobalRef, GlobalField};
|
||||||
use dom::bindings::js::Temporary;
|
use dom::bindings::js::{JSRef, Temporary};
|
||||||
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
|
use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object};
|
||||||
use dom::bindings::error::Fallible;
|
use dom::bindings::error::Fallible;
|
||||||
use dom::bindings::codegen::Bindings::BlobBinding;
|
use dom::bindings::codegen::Bindings::BlobBinding;
|
||||||
|
use dom::bindings::codegen::Bindings::BlobBinding::BlobMethods;
|
||||||
|
|
||||||
|
use servo_util::str::DOMString;
|
||||||
|
use std::cmp::{min, max};
|
||||||
|
|
||||||
#[jstraceable]
|
#[jstraceable]
|
||||||
pub enum BlobType {
|
pub enum BlobType {
|
||||||
|
@ -18,28 +22,107 @@ pub enum BlobType {
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
pub struct Blob {
|
pub struct Blob {
|
||||||
reflector_: Reflector,
|
reflector_: Reflector,
|
||||||
type_: BlobType
|
type_: BlobType,
|
||||||
|
bytes: Option<Vec<u8>>,
|
||||||
|
typeString: DOMString,
|
||||||
|
global: GlobalField
|
||||||
|
// isClosed_: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Blob {
|
impl Blob {
|
||||||
pub fn new_inherited() -> Blob {
|
pub fn new_inherited(global: &GlobalRef, bytes: Option<Vec<u8>>) -> Blob {
|
||||||
Blob {
|
Blob {
|
||||||
reflector_: Reflector::new(),
|
reflector_: Reflector::new(),
|
||||||
type_: BlobTypeId
|
type_: BlobTypeId,
|
||||||
|
bytes: bytes,
|
||||||
|
typeString: "".to_string(),
|
||||||
|
global: GlobalField::from_rooted(global)
|
||||||
|
//isClosed_: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(global: GlobalRef) -> Temporary<Blob> {
|
pub fn new(global: &GlobalRef, bytes: Option<Vec<u8>>) -> Temporary<Blob> {
|
||||||
reflect_dom_object(box Blob::new_inherited(),
|
reflect_dom_object(box Blob::new_inherited(global, bytes),
|
||||||
global,
|
*global,
|
||||||
BlobBinding::Wrap)
|
BlobBinding::Wrap)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn Constructor(global: &GlobalRef) -> Fallible<Temporary<Blob>> {
|
pub fn Constructor(global: &GlobalRef) -> Fallible<Temporary<Blob>> {
|
||||||
Ok(Blob::new(*global))
|
Ok(Blob::new(global, None))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn Constructor_(global: &GlobalRef, blobParts: DOMString) -> Fallible<Temporary<Blob>> {
|
||||||
|
//TODO: accept other blobParts types - ArrayBuffer or ArrayBufferView or Blob
|
||||||
|
//TODO: accept options parameter
|
||||||
|
let bytes: Option<Vec<u8>> = Some(blobParts.into_bytes());
|
||||||
|
Ok(Blob::new(global, bytes))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> BlobMethods for JSRef<'a, Blob> {
|
||||||
|
fn Size(self) -> u64{
|
||||||
|
match self.bytes {
|
||||||
|
None => 0,
|
||||||
|
Some(ref bytes) => bytes.len() as u64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Type(self) -> DOMString {
|
||||||
|
self.typeString.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn Slice(self, start: Option<i64>, end: Option<i64>,
|
||||||
|
_contentType: Option<DOMString>) -> Temporary<Blob> {
|
||||||
|
let size: i64 = self.Size().to_i64().unwrap();
|
||||||
|
let relativeStart: i64 = match start {
|
||||||
|
None => 0,
|
||||||
|
Some(start) => {
|
||||||
|
if start < 0 {
|
||||||
|
max(size.to_i64().unwrap() + start, 0)
|
||||||
|
} else {
|
||||||
|
min(start, size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let relativeEnd: i64 = match end {
|
||||||
|
None => size,
|
||||||
|
Some(end) => {
|
||||||
|
if end < 0 {
|
||||||
|
max(size + end, 0)
|
||||||
|
} else {
|
||||||
|
min(end, size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
let relativeContentType = match contentType {
|
||||||
|
None => "".to_string(),
|
||||||
|
Some(str) => str
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
//TODO: actually use relativeContentType in constructor
|
||||||
|
let span: i64 = max(relativeEnd - relativeStart, 0);
|
||||||
|
let global = self.global.root();
|
||||||
|
match self.bytes {
|
||||||
|
None => Blob::new(&global.root_ref(), None),
|
||||||
|
Some(ref vec) => {
|
||||||
|
let start = relativeStart.to_uint().unwrap();
|
||||||
|
let end = (relativeStart + span).to_uint().unwrap();
|
||||||
|
let mut bytes: Vec<u8> = Vec::new();
|
||||||
|
bytes.push_all(vec.slice(start, end));
|
||||||
|
Blob::new(&global.root_ref(), Some(bytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//fn IsClosed(self) -> bool {
|
||||||
|
// self.isClosed_.clone()
|
||||||
|
//}
|
||||||
|
|
||||||
|
//fn Close(self) {
|
||||||
|
// TODO
|
||||||
|
//}
|
||||||
|
}
|
||||||
impl Reflectable for Blob {
|
impl Reflectable for Blob {
|
||||||
fn reflector<'a>(&'a self) -> &'a Reflector {
|
fn reflector<'a>(&'a self) -> &'a Reflector {
|
||||||
&self.reflector_
|
&self.reflector_
|
||||||
|
|
|
@ -18,9 +18,9 @@ pub struct File {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl File {
|
impl File {
|
||||||
fn new_inherited(_file_bits: JSRef<Blob>, name: DOMString) -> File {
|
fn new_inherited(global: &GlobalRef, _file_bits: JSRef<Blob>, name: DOMString) -> File {
|
||||||
File {
|
File {
|
||||||
blob: Blob::new_inherited(),
|
blob: Blob::new_inherited(global, None),
|
||||||
name: name,
|
name: name,
|
||||||
type_: FileTypeId
|
type_: FileTypeId
|
||||||
}
|
}
|
||||||
|
@ -28,9 +28,9 @@ impl File {
|
||||||
// the relevant subfields of file_bits should be copied over
|
// the relevant subfields of file_bits should be copied over
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(global: GlobalRef, file_bits: JSRef<Blob>, name: DOMString) -> Temporary<File> {
|
pub fn new(global: &GlobalRef, file_bits: JSRef<Blob>, name: DOMString) -> Temporary<File> {
|
||||||
reflect_dom_object(box File::new_inherited(file_bits, name),
|
reflect_dom_object(box File::new_inherited(global, file_bits, name),
|
||||||
global,
|
*global,
|
||||||
FileBinding::Wrap)
|
FileBinding::Wrap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -121,6 +121,6 @@ impl PrivateFormDataHelpers for FormData {
|
||||||
let global = self.global.root();
|
let global = self.global.root();
|
||||||
let f: Option<JSRef<File>> = FileCast::to_ref(value);
|
let f: Option<JSRef<File>> = FileCast::to_ref(value);
|
||||||
let name = filename.unwrap_or(f.map(|inner| inner.name().clone()).unwrap_or("blob".to_string()));
|
let name = filename.unwrap_or(f.map(|inner| inner.name().clone()).unwrap_or("blob".to_string()));
|
||||||
File::new(global.root_ref(), value, name)
|
File::new(&global.root_ref(), value, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@ impl<'a> TestBindingMethods for JSRef<'a, TestBinding> {
|
||||||
fn SetEnumAttribute(self, _: TestEnum) {}
|
fn SetEnumAttribute(self, _: TestEnum) {}
|
||||||
fn InterfaceAttribute(self) -> Temporary<Blob> {
|
fn InterfaceAttribute(self) -> Temporary<Blob> {
|
||||||
let global = self.global.root();
|
let global = self.global.root();
|
||||||
Blob::new(global.root_ref())
|
Blob::new(&global.root_ref(), None)
|
||||||
}
|
}
|
||||||
fn SetInterfaceAttribute(self, _: JSRef<Blob>) {}
|
fn SetInterfaceAttribute(self, _: JSRef<Blob>) {}
|
||||||
fn UnionAttribute(self) -> HTMLElementOrLong { eLong(0) }
|
fn UnionAttribute(self) -> HTMLElementOrLong { eLong(0) }
|
||||||
|
@ -96,7 +96,7 @@ impl<'a> TestBindingMethods for JSRef<'a, TestBinding> {
|
||||||
fn GetEnumAttributeNullable(self) -> Option<TestEnum> { Some(_empty) }
|
fn GetEnumAttributeNullable(self) -> Option<TestEnum> { Some(_empty) }
|
||||||
fn GetInterfaceAttributeNullable(self) -> Option<Temporary<Blob>> {
|
fn GetInterfaceAttributeNullable(self) -> Option<Temporary<Blob>> {
|
||||||
let global = self.global.root();
|
let global = self.global.root();
|
||||||
Some(Blob::new(global.root_ref()))
|
Some(Blob::new(&global.root_ref(), None))
|
||||||
}
|
}
|
||||||
fn SetInterfaceAttributeNullable(self, _: Option<JSRef<Blob>>) {}
|
fn SetInterfaceAttributeNullable(self, _: Option<JSRef<Blob>>) {}
|
||||||
fn GetUnionAttributeNullable(self) -> Option<HTMLElementOrLong> { Some(eLong(0)) }
|
fn GetUnionAttributeNullable(self) -> Option<HTMLElementOrLong> { Some(eLong(0)) }
|
||||||
|
@ -120,7 +120,7 @@ impl<'a> TestBindingMethods for JSRef<'a, TestBinding> {
|
||||||
fn ReceiveEnum(self) -> TestEnum { _empty }
|
fn ReceiveEnum(self) -> TestEnum { _empty }
|
||||||
fn ReceiveInterface(self) -> Temporary<Blob> {
|
fn ReceiveInterface(self) -> Temporary<Blob> {
|
||||||
let global = self.global.root();
|
let global = self.global.root();
|
||||||
Blob::new(global.root_ref())
|
Blob::new(&global.root_ref(), None)
|
||||||
}
|
}
|
||||||
fn ReceiveAny(self, _: *mut JSContext) -> JSVal { NullValue() }
|
fn ReceiveAny(self, _: *mut JSContext) -> JSVal { NullValue() }
|
||||||
fn ReceiveUnion(self) -> HTMLElementOrLong { eLong(0) }
|
fn ReceiveUnion(self) -> HTMLElementOrLong { eLong(0) }
|
||||||
|
@ -142,7 +142,7 @@ impl<'a> TestBindingMethods for JSRef<'a, TestBinding> {
|
||||||
fn ReceiveNullableEnum(self) -> Option<TestEnum> { Some(_empty) }
|
fn ReceiveNullableEnum(self) -> Option<TestEnum> { Some(_empty) }
|
||||||
fn ReceiveNullableInterface(self) -> Option<Temporary<Blob>> {
|
fn ReceiveNullableInterface(self) -> Option<Temporary<Blob>> {
|
||||||
let global = self.global.root();
|
let global = self.global.root();
|
||||||
Some(Blob::new(global.root_ref()))
|
Some(Blob::new(&global.root_ref(), None))
|
||||||
}
|
}
|
||||||
fn ReceiveNullableUnion(self) -> Option<HTMLElementOrLong> { Some(eLong(0)) }
|
fn ReceiveNullableUnion(self) -> Option<HTMLElementOrLong> { Some(eLong(0)) }
|
||||||
fn ReceiveNullableUnion2(self) -> Option<EventOrString> { Some(eString("".to_string())) }
|
fn ReceiveNullableUnion2(self) -> Option<EventOrString> { Some(eString("".to_string())) }
|
||||||
|
|
|
@ -6,18 +6,23 @@
|
||||||
// http://dev.w3.org/2006/webapi/FileAPI/#dfn-Blob
|
// http://dev.w3.org/2006/webapi/FileAPI/#dfn-Blob
|
||||||
//[Exposed=Window,Worker][Constructor,
|
//[Exposed=Window,Worker][Constructor,
|
||||||
// Constructor(sequence<(ArrayBuffer or ArrayBufferView or Blob or DOMString)> blobParts, optional BlobPropertyBag options)]
|
// Constructor(sequence<(ArrayBuffer or ArrayBufferView or Blob or DOMString)> blobParts, optional BlobPropertyBag options)]
|
||||||
[Constructor]
|
[Constructor,
|
||||||
|
Constructor(DOMString blobParts)]
|
||||||
interface Blob {
|
interface Blob {
|
||||||
|
|
||||||
//readonly attribute unsigned long long size;
|
readonly attribute unsigned long long size;
|
||||||
//readonly attribute DOMString type;
|
readonly attribute DOMString type;
|
||||||
//readonly attribute boolean isClosed;
|
//readonly attribute boolean isClosed;
|
||||||
|
|
||||||
//slice Blob into byte-ranged chunks
|
//slice Blob into byte-ranged chunks
|
||||||
|
|
||||||
|
//TODO: implement slice with [Clamp]
|
||||||
//Blob slice([Clamp] optional long long start,
|
//Blob slice([Clamp] optional long long start,
|
||||||
// [Clamp] optional long long end,
|
// [Clamp] optional long long end,
|
||||||
// optional DOMString contentType);
|
// optional DOMString contentType);
|
||||||
|
Blob slice(optional long long start,
|
||||||
|
optional long long end,
|
||||||
|
optional DOMString contentType);
|
||||||
//void close();
|
//void close();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
15
tests/content/test_blob.html
Normal file
15
tests/content/test_blob.html
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<script src="harness.js"></script>
|
||||||
|
<script>
|
||||||
|
var testData = ['<a id="a"><b id="b">hey!</b></a>'];
|
||||||
|
var b = new Blob(testData); // the blob
|
||||||
|
is(b.size, 32);
|
||||||
|
is(b.type, "");
|
||||||
|
|
||||||
|
var bs = b.slice(0, 5);
|
||||||
|
is(bs.size, 5);
|
||||||
|
is(b.type, "");
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
</html>
|
Loading…
Add table
Add a link
Reference in a new issue