diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index 9d343baeea1c..d5ab8acd9faa 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -78,3 +78,4 @@ string_cache = "0.1" string_cache_plugin = "0.1" euclid = "0.1" tendril = "0.1.1" +rand = "0.3" diff --git a/components/script/dom/bindings/error.rs b/components/script/dom/bindings/error.rs index 9c0567067db7..373c6c7cbe07 100644 --- a/components/script/dom/bindings/error.rs +++ b/components/script/dom/bindings/error.rs @@ -62,6 +62,10 @@ pub enum Error { DataClone, /// NoModificationAllowedError DOMException NoModificationAllowed, + /// QuotaExceededError DOMException + QuotaExceeded, + /// TypeMismatchError DOMException + TypeMismatch, /// TypeError JavaScript Error Type(DOMString), @@ -101,6 +105,8 @@ pub fn throw_dom_exception(cx: *mut JSContext, global: GlobalRef, Error::InvalidNodeType => DOMErrorName::InvalidNodeTypeError, Error::DataClone => DOMErrorName::DataCloneError, Error::NoModificationAllowed => DOMErrorName::NoModificationAllowedError, + Error::QuotaExceeded => DOMErrorName::QuotaExceededError, + Error::TypeMismatch => DOMErrorName::TypeMismatchError, Error::Type(message) => { assert!(unsafe { JS_IsExceptionPending(cx) } == 0); throw_type_error(cx, &message); diff --git a/components/script/dom/crypto.rs b/components/script/dom/crypto.rs new file mode 100644 index 000000000000..aedf1aa0c262 --- /dev/null +++ b/components/script/dom/crypto.rs @@ -0,0 +1,82 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +use dom::bindings::codegen::Bindings::CryptoBinding; +use dom::bindings::codegen::Bindings::CryptoBinding::CryptoMethods; +use dom::bindings::error::{Error, Fallible}; +use dom::bindings::global::GlobalRef; +use dom::bindings::js::Root; +use dom::bindings::utils::{Reflector, reflect_dom_object}; +use dom::bindings::cell::DOMRefCell; + +use js::jsapi::{JSContext, JSObject}; +use js::jsapi::{JS_GetObjectAsArrayBufferView, JS_GetArrayBufferViewType, Type}; + +use std::ptr; +use std::slice; + +use rand::{Rng, OsRng}; + +no_jsmanaged_fields!(OsRng); + +// https://developer.mozilla.org/en-US/docs/Web/API/Crypto +#[dom_struct] +pub struct Crypto { + reflector_: Reflector, + rng: DOMRefCell, +} + +impl Crypto { + fn new_inherited() -> Crypto { + Crypto { + reflector_: Reflector::new(), + rng: DOMRefCell::new(OsRng::new().unwrap()), + } + } + + pub fn new(global: GlobalRef) -> Root { + reflect_dom_object(box Crypto::new_inherited(), global, CryptoBinding::Wrap) + } +} + +impl<'a> CryptoMethods for &'a Crypto { + // https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#Crypto-method-getRandomValues + #[allow(unsafe_code)] + fn GetRandomValues(self, _cx: *mut JSContext, input: *mut JSObject) + -> Fallible<*mut JSObject> { + let mut length = 0; + let mut data = ptr::null_mut(); + if unsafe { JS_GetObjectAsArrayBufferView(input, &mut length, &mut data).is_null() } { + return Err(Error::Type("Argument to Crypto.getRandomValues is not an ArrayBufferView".to_owned())); + } + if !is_integer_buffer(input) { + return Err(Error::TypeMismatch); + } + if length > 65536 { + return Err(Error::QuotaExceeded); + } + + let mut buffer = unsafe { + slice::from_raw_parts_mut(data, length as usize) + }; + + self.rng.borrow_mut().fill_bytes(&mut buffer); + + Ok(input) + } +} + +#[allow(unsafe_code)] +fn is_integer_buffer(input: *mut JSObject) -> bool { + match unsafe { JS_GetArrayBufferViewType(input) } { + Type::Uint8 | + Type::Uint8Clamped | + Type::Int8 | + Type::Uint16 | + Type::Int16 | + Type::Uint32 | + Type::Int32 => true, + _ => false + } +} diff --git a/components/script/dom/domexception.rs b/components/script/dom/domexception.rs index 9bf3a2dd88c6..e724b0bcafbd 100644 --- a/components/script/dom/domexception.rs +++ b/components/script/dom/domexception.rs @@ -32,6 +32,7 @@ pub enum DOMErrorName { NetworkError = DOMExceptionConstants::NETWORK_ERR, AbortError = DOMExceptionConstants::ABORT_ERR, URLMismatchError = DOMExceptionConstants::URL_MISMATCH_ERR, + TypeMismatchError = DOMExceptionConstants::TYPE_MISMATCH_ERR, QuotaExceededError = DOMExceptionConstants::QUOTA_EXCEEDED_ERR, TimeoutError = DOMExceptionConstants::TIMEOUT_ERR, InvalidNodeTypeError = DOMExceptionConstants::INVALID_NODE_TYPE_ERR, @@ -93,6 +94,7 @@ impl<'a> DOMExceptionMethods for &'a DOMException { DOMErrorName::NetworkError => "A network error occurred.", DOMErrorName::AbortError => "The operation was aborted.", DOMErrorName::URLMismatchError => "The given URL does not match another URL.", + DOMErrorName::TypeMismatchError => "The given type does not match any expected type.", DOMErrorName::QuotaExceededError => "The quota has been exceeded.", DOMErrorName::TimeoutError => "The operation timed out.", DOMErrorName::InvalidNodeTypeError => diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 466aced53d7c..88695f8cc043 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -197,6 +197,7 @@ pub mod closeevent; pub mod comment; pub mod console; mod create; +pub mod crypto; pub mod customevent; pub mod dedicatedworkerglobalscope; pub mod document; diff --git a/components/script/dom/webidls/Crypto.webidl b/components/script/dom/webidls/Crypto.webidl new file mode 100644 index 000000000000..032fce382bba --- /dev/null +++ b/components/script/dom/webidls/Crypto.webidl @@ -0,0 +1,25 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The origin of this IDL file is + * https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#crypto-interface + * + */ + +[NoInterfaceObject] +interface GlobalCrypto { + readonly attribute Crypto crypto; +}; + +Window implements GlobalCrypto; +WorkerGlobalScope implements GlobalCrypto; + +//[Exposed=(Window,Worker)] +interface Crypto { + //readonly attribute SubtleCrypto subtle; + //ArrayBufferView getRandomValues(ArrayBufferView array); + [Throws] + ArrayBufferView getRandomValues(object array); +}; diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 43fd74298322..fa9c988a30cb 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -19,6 +19,7 @@ use dom::bindings::num::Finite; use dom::bindings::utils::{GlobalStaticData, Reflectable, WindowProxyHandler}; use dom::browsercontext::BrowserContext; use dom::console::Console; +use dom::crypto::Crypto; use dom::document::{Document, DocumentHelpers}; use dom::element::Element; use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId}; @@ -100,6 +101,7 @@ pub struct Window { script_chan: Box, control_chan: ScriptControlChan, console: MutNullableHeap>, + crypto: MutNullableHeap>, navigator: MutNullableHeap>, image_cache_task: ImageCacheTask, image_cache_chan: ImageCacheChan, @@ -360,6 +362,10 @@ impl<'a> WindowMethods for &'a Window { self.console.or_init(|| Console::new(GlobalRef::Window(self))) } + fn Crypto(self) -> Root { + self.crypto.or_init(|| Crypto::new(GlobalRef::Window(self))) + } + // https://html.spec.whatwg.org/#dom-frameelement fn GetFrameElement(self) -> Option> { // FIXME(https://github.com/rust-lang/rust/issues/23338) @@ -981,6 +987,7 @@ impl Window { image_cache_chan: image_cache_chan, control_chan: control_chan, console: Default::default(), + crypto: Default::default(), compositor: DOMRefCell::new(compositor), page: page, navigator: Default::default(), diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index bf654a368195..3039a7cbde40 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.rs @@ -11,6 +11,7 @@ use dom::bindings::global::GlobalRef; use dom::bindings::js::{JS, Root, MutNullableHeap}; use dom::bindings::utils::Reflectable; use dom::console::Console; +use dom::crypto::Crypto; use dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScopeHelpers; use dom::eventtarget::{EventTarget, EventTargetTypeId}; use dom::workerlocation::WorkerLocation; @@ -49,6 +50,7 @@ pub struct WorkerGlobalScope { location: MutNullableHeap>, navigator: MutNullableHeap>, console: MutNullableHeap>, + crypto: MutNullableHeap>, timers: TimerManager, devtools_chan: Option, } @@ -68,6 +70,7 @@ impl WorkerGlobalScope { location: Default::default(), navigator: Default::default(), console: Default::default(), + crypto: Default::default(), timers: TimerManager::new(), devtools_chan: devtools_chan, } @@ -157,6 +160,10 @@ impl<'a> WorkerGlobalScopeMethods for &'a WorkerGlobalScope { self.console.or_init(|| Console::new(GlobalRef::Worker(self))) } + fn Crypto(self) -> Root { + self.crypto.or_init(|| Crypto::new(GlobalRef::Worker(self))) + } + fn Btoa(self, btoa: DOMString) -> Fallible { base64_btoa(btoa) } diff --git a/components/script/lib.rs b/components/script/lib.rs index 642efd546504..c1cdc1c02f60 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -53,6 +53,7 @@ extern crate rustc_serialize; extern crate time; extern crate canvas; extern crate canvas_traits; +extern crate rand; extern crate profile_traits; extern crate script_traits; extern crate selectors; diff --git a/components/servo/Cargo.lock b/components/servo/Cargo.lock index db5ee6b194ed..497cf2fe73fa 100644 --- a/components/servo/Cargo.lock +++ b/components/servo/Cargo.lock @@ -1075,6 +1075,7 @@ dependencies = [ "plugins 0.0.1", "png 0.1.0 (git+https://github.com/servo/rust-png)", "profile_traits 0.0.1", + "rand 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "script_traits 0.0.1", "selectors 0.1.0 (git+https://github.com/servo/rust-selectors)", diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index 910339bc1747..07bc8729aed4 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -1055,6 +1055,7 @@ dependencies = [ "plugins 0.0.1", "png 0.1.0 (git+https://github.com/servo/rust-png)", "profile_traits 0.0.1", + "rand 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "script_traits 0.0.1", "selectors 0.1.0 (git+https://github.com/servo/rust-selectors)", diff --git a/ports/gonk/Cargo.lock b/ports/gonk/Cargo.lock index 58ddb54782c9..c2145716cd38 100644 --- a/ports/gonk/Cargo.lock +++ b/ports/gonk/Cargo.lock @@ -963,6 +963,7 @@ dependencies = [ "plugins 0.0.1", "png 0.1.0 (git+https://github.com/servo/rust-png)", "profile_traits 0.0.1", + "rand 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "script_traits 0.0.1", "selectors 0.1.0 (git+https://github.com/servo/rust-selectors)", diff --git a/tests/wpt/include.ini b/tests/wpt/include.ini index dc8066e6bbb8..cbd4f34dbf14 100644 --- a/tests/wpt/include.ini +++ b/tests/wpt/include.ini @@ -107,3 +107,5 @@ skip: true skip: false [hr-time] skip: false +[WebCryptoAPI] + skip: false diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.html b/tests/wpt/mozilla/tests/mozilla/interfaces.html index effcfdae2087..9d33f8ae54e2 100644 --- a/tests/wpt/mozilla/tests/mozilla/interfaces.html +++ b/tests/wpt/mozilla/tests/mozilla/interfaces.html @@ -15,6 +15,7 @@ "ArrayBuffer", "Atomics", "Boolean", + "Crypto", "DataView", "Date", "Error",