diff --git a/src/components/script/dom/bindings/codegen/Bindings.conf b/src/components/script/dom/bindings/codegen/Bindings.conf index ca0f71d9bf7..6fc67b5aab5 100644 --- a/src/components/script/dom/bindings/codegen/Bindings.conf +++ b/src/components/script/dom/bindings/codegen/Bindings.conf @@ -15,6 +15,9 @@ DOMInterfaces = { +'DedicatedWorkerGlobalScope': { + 'createGlobal': True, +}, 'EventListener': { 'nativeType': 'EventListenerBinding::EventListener', }, diff --git a/src/components/script/dom/bindings/global.rs b/src/components/script/dom/bindings/global.rs index 2e34e78e7c3..6d3f9c5cc44 100644 --- a/src/components/script/dom/bindings/global.rs +++ b/src/components/script/dom/bindings/global.rs @@ -6,6 +6,7 @@ use dom::bindings::js::{JS, JSRef, Root}; use dom::bindings::utils::{Reflectable, Reflector}; +use dom::workerglobalscope::WorkerGlobalScope; use dom::window::Window; use page::Page; use script_task::ScriptChan; @@ -16,27 +17,32 @@ use url::Url; pub enum GlobalRef<'a> { Window(JSRef<'a, Window>), + Worker(JSRef<'a, WorkerGlobalScope>), } pub enum GlobalRoot<'a, 'b> { WindowRoot(Root<'a, 'b, Window>), + WorkerRoot(Root<'a, 'b, WorkerGlobalScope>), } #[deriving(Encodable)] pub enum GlobalField { WindowField(JS), + WorkerField(JS), } impl<'a> GlobalRef<'a> { pub fn get_cx(&self) -> *mut JSContext { match *self { Window(ref window) => window.get_cx(), + Worker(ref worker) => worker.get_cx(), } } pub fn as_window<'b>(&'b self) -> &'b JSRef<'b, Window> { match *self { Window(ref window) => window, + Worker(_) => fail!("expected a Window scope"), } } @@ -57,6 +63,7 @@ impl<'a> Reflectable for GlobalRef<'a> { fn reflector<'b>(&'b self) -> &'b Reflector { match *self { Window(ref window) => window.reflector(), + Worker(ref worker) => worker.reflector(), } } } @@ -65,6 +72,7 @@ impl<'a, 'b> GlobalRoot<'a, 'b> { pub fn root_ref<'c>(&'c self) -> GlobalRef<'c> { match *self { WindowRoot(ref window) => Window(window.root_ref()), + WorkerRoot(ref worker) => Worker(worker.root_ref()), } } } @@ -73,12 +81,14 @@ impl GlobalField { pub fn from_rooted(global: &GlobalRef) -> GlobalField { match *global { Window(ref window) => WindowField(JS::from_rooted(window)), + Worker(ref worker) => WorkerField(JS::from_rooted(worker)), } } pub fn root(&self) -> GlobalRoot { match *self { WindowField(ref window) => WindowRoot(window.root()), + WorkerField(ref worker) => WorkerRoot(worker.root()), } } } diff --git a/src/components/script/dom/bindings/utils.rs b/src/components/script/dom/bindings/utils.rs index 628debb24ea..36f68a93f3e 100644 --- a/src/components/script/dom/bindings/utils.rs +++ b/src/components/script/dom/bindings/utils.rs @@ -5,7 +5,7 @@ use dom::bindings::codegen::PrototypeList; use dom::bindings::codegen::PrototypeList::MAX_PROTO_CHAIN_LENGTH; use dom::bindings::conversions::{FromJSValConvertible, IDLInterface}; -use dom::bindings::global::{GlobalRef, GlobalField, WindowField}; +use dom::bindings::global::{GlobalRef, GlobalField, WindowField, WorkerField}; use dom::bindings::js::{JS, Temporary, Root}; use dom::bindings::trace::Untraceable; use dom::browsercontext; @@ -591,7 +591,12 @@ pub fn global_object_for_js_object(obj: *mut JSObject) -> GlobalField { Err(_) => (), } - fail!("found DOM global that doesn't unwrap to Window") + match FromJSValConvertible::from_jsval(ptr::mut_null(), ObjectOrNullValue(global), ()) { + Ok(worker) => return WorkerField(worker), + Err(_) => (), + } + + fail!("found DOM global that doesn't unwrap to Window or WorkerGlobalScope") } } diff --git a/src/components/script/dom/dedicatedworkerglobalscope.rs b/src/components/script/dom/dedicatedworkerglobalscope.rs new file mode 100644 index 00000000000..13eb6c958da --- /dev/null +++ b/src/components/script/dom/dedicatedworkerglobalscope.rs @@ -0,0 +1,62 @@ +/* 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::DedicatedWorkerGlobalScopeBinding; +use dom::bindings::codegen::InheritTypes::DedicatedWorkerGlobalScopeDerived; +use dom::bindings::js::Temporary; +use dom::bindings::utils::{Reflectable, Reflector}; +use dom::eventtarget::EventTarget; +use dom::eventtarget::WorkerGlobalScopeTypeId; +use dom::workerglobalscope::DedicatedGlobalScope; +use dom::workerglobalscope::WorkerGlobalScope; +use script_task::ScriptTask; + +use js::rust::Cx; + +use std::rc::Rc; + +#[deriving(Encodable)] +pub struct DedicatedWorkerGlobalScope { + workerglobalscope: WorkerGlobalScope, +} + +impl DedicatedWorkerGlobalScope { + pub fn new_inherited(cx: Rc) -> DedicatedWorkerGlobalScope { + DedicatedWorkerGlobalScope { + workerglobalscope: WorkerGlobalScope::new_inherited(DedicatedGlobalScope, cx), + } + } + + pub fn new(cx: Rc) -> Temporary { + let scope = box DedicatedWorkerGlobalScope::new_inherited(cx.clone()); + DedicatedWorkerGlobalScopeBinding::Wrap(cx.ptr, scope) + } + + pub fn init() -> Temporary { + let (_js_runtime, js_context) = ScriptTask::new_rt_and_cx(); + DedicatedWorkerGlobalScope::new(js_context.clone()) + } + + pub fn get_rust_cx<'a>(&'a self) -> &'a Rc { + self.workerglobalscope.get_rust_cx() + } +} + +pub trait DedicatedWorkerGlobalScopeMethods { +} + +impl Reflectable for DedicatedWorkerGlobalScope { + fn reflector<'a>(&'a self) -> &'a Reflector { + self.workerglobalscope.reflector() + } +} + +impl DedicatedWorkerGlobalScopeDerived for EventTarget { + fn is_dedicatedworkerglobalscope(&self) -> bool { + match self.type_id { + WorkerGlobalScopeTypeId(DedicatedGlobalScope) => true, + _ => false + } + } +} diff --git a/src/components/script/dom/eventtarget.rs b/src/components/script/dom/eventtarget.rs index 25a50b40ab3..1683f280100 100644 --- a/src/components/script/dom/eventtarget.rs +++ b/src/components/script/dom/eventtarget.rs @@ -12,6 +12,7 @@ use dom::bindings::utils::{Reflectable, Reflector}; use dom::event::Event; use dom::eventdispatcher::dispatch_event; use dom::node::NodeTypeId; +use dom::workerglobalscope::WorkerGlobalScopeId; use dom::xmlhttprequest::XMLHttpRequestId; use dom::virtualmethods::VirtualMethods; use js::jsapi::{JS_CompileUCFunction, JS_GetFunctionObject, JS_CloneFunctionObject}; @@ -34,6 +35,8 @@ pub enum ListenerPhase { pub enum EventTargetTypeId { NodeTargetTypeId(NodeTypeId), WindowTypeId, + WorkerTypeId, + WorkerGlobalScopeTypeId(WorkerGlobalScopeId), XMLHttpRequestTargetTypeId(XMLHttpRequestId) } diff --git a/src/components/script/dom/webidls/DedicatedWorkerGlobalScope.webidl b/src/components/script/dom/webidls/DedicatedWorkerGlobalScope.webidl new file mode 100644 index 00000000000..c0c69219e1e --- /dev/null +++ b/src/components/script/dom/webidls/DedicatedWorkerGlobalScope.webidl @@ -0,0 +1,10 @@ +/* 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/. */ + +// http://www.whatwg.org/html/#dedicatedworkerglobalscope +[Global/*=Worker,DedicatedWorker*/] +/*sealed*/ interface DedicatedWorkerGlobalScope : WorkerGlobalScope { + //void postMessage(any message, optional sequence transfer); + // attribute EventHandler onmessage; +}; diff --git a/src/components/script/dom/webidls/Worker.webidl b/src/components/script/dom/webidls/Worker.webidl new file mode 100644 index 00000000000..273ab8e5bfd --- /dev/null +++ b/src/components/script/dom/webidls/Worker.webidl @@ -0,0 +1,20 @@ +/* -*- 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/. */ + +// http://www.whatwg.org/html/#abstractworker +[NoInterfaceObject/*, Exposed=Window,Worker*/] +interface AbstractWorker { + // attribute EventHandler onerror; +}; + +// http://www.whatwg.org/html/#worker +[Constructor(DOMString scriptURL)/*, Exposed=Window,Worker*/] +interface Worker : EventTarget { + //void terminate(); + + //void postMessage(any message/*, optional sequence transfer*/); + // attribute EventHandler onmessage; +}; +Worker implements AbstractWorker; diff --git a/src/components/script/dom/webidls/WorkerGlobalScope.webidl b/src/components/script/dom/webidls/WorkerGlobalScope.webidl new file mode 100644 index 00000000000..a8e3b92d193 --- /dev/null +++ b/src/components/script/dom/webidls/WorkerGlobalScope.webidl @@ -0,0 +1,33 @@ +/* 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/. */ + +// http://www.whatwg.org/html/#workerglobalscope +//[Exposed=Worker] +interface WorkerGlobalScope : EventTarget { + readonly attribute WorkerGlobalScope self; + //readonly attribute WorkerLocation location; + + //void close(); + // attribute OnErrorEventHandler onerror; + // attribute EventHandler onlanguagechange; + // attribute EventHandler onoffline; + // attribute EventHandler ononline; + + // also has obsolete members +}; + +// http://www.whatwg.org/html/#WorkerGlobalScope-partial +//[Exposed=Worker] +partial interface WorkerGlobalScope { + //void importScripts(DOMString... urls); + //readonly attribute WorkerNavigator navigator; +}; +//WorkerGlobalScope implements WindowTimers; +//WorkerGlobalScope implements WindowBase64; + +// Proprietary +partial interface WorkerGlobalScope { + [Replaceable] + readonly attribute Console console; +}; diff --git a/src/components/script/dom/worker.rs b/src/components/script/dom/worker.rs new file mode 100644 index 00000000000..d28d4958ab8 --- /dev/null +++ b/src/components/script/dom/worker.rs @@ -0,0 +1,84 @@ +/* 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::WorkerBinding; +use dom::bindings::error::{Fallible, Syntax}; +use dom::bindings::global::GlobalRef; +use dom::bindings::js::{Temporary, RootCollection}; +use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object}; +use dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope; +use dom::eventtarget::{EventTarget, WorkerTypeId}; +use script_task::StackRootTLS; + +use servo_net::resource_task::load_whole_resource; +use servo_util::str::DOMString; +use servo_util::url::try_parse_url; + +use native; +use rustrt::task::TaskOpts; + +#[deriving(Encodable)] +pub struct Worker { + eventtarget: EventTarget, +} + +impl Worker { + pub fn new_inherited() -> Worker { + Worker { + eventtarget: EventTarget::new_inherited(WorkerTypeId), + } + } + + pub fn new(global: &GlobalRef) -> Temporary { + reflect_dom_object(box Worker::new_inherited(), + global, + WorkerBinding::Wrap) + } + + // http://www.whatwg.org/html/#dom-worker + pub fn Constructor(global: &GlobalRef, scriptURL: DOMString) -> Fallible> { + // Step 2-4. + let worker_url = match try_parse_url(scriptURL.as_slice(), Some(global.get_url())) { + Ok(url) => url, + Err(_) => return Err(Syntax), + }; + + let resource_task = global.page().resource_task.deref().clone(); + + let mut task_opts = TaskOpts::new(); + task_opts.name = Some(format!("Web Worker at {}", worker_url).into_maybe_owned()); + native::task::spawn_opts(task_opts, proc() { + let roots = RootCollection::new(); + let _stack_roots_tls = StackRootTLS::new(&roots); + + let (filename, source) = match load_whole_resource(&resource_task, worker_url.clone()) { + Err(_) => { + println!("error loading script {}", worker_url); + return; + } + Ok((metadata, bytes)) => { + (metadata.final_url, String::from_utf8(bytes).unwrap()) + } + }; + + let global = DedicatedWorkerGlobalScope::init().root(); + match global.get_rust_cx().evaluate_script( + global.reflector().get_jsobject(), source, filename.to_str(), 1) { + Ok(_) => (), + Err(_) => println!("evaluate_script failed") + } + }); + + Ok(Worker::new(global)) + } +} + +pub trait WorkerMethods { +} + +impl Reflectable for Worker { + fn reflector<'a>(&'a self) -> &'a Reflector { + self.eventtarget.reflector() + } +} diff --git a/src/components/script/dom/workerglobalscope.rs b/src/components/script/dom/workerglobalscope.rs new file mode 100644 index 00000000000..e6f9235723d --- /dev/null +++ b/src/components/script/dom/workerglobalscope.rs @@ -0,0 +1,71 @@ +/* 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::trace::Untraceable; +use dom::bindings::global; +use dom::bindings::js::{JS, JSRef, Temporary, OptionalSettable}; +use dom::bindings::utils::{Reflectable, Reflector}; +use dom::console::Console; +use dom::eventtarget::{EventTarget, WorkerGlobalScopeTypeId}; + +use js::jsapi::JSContext; +use js::rust::Cx; + +use std::cell::Cell; +use std::rc::Rc; + +#[deriving(PartialEq,Encodable)] +pub enum WorkerGlobalScopeId { + DedicatedGlobalScope, +} + +#[deriving(Encodable)] +pub struct WorkerGlobalScope { + pub eventtarget: EventTarget, + js_context: Untraceable>, + console: Cell>>, +} + +impl WorkerGlobalScope { + pub fn new_inherited(type_id: WorkerGlobalScopeId, + cx: Rc) -> WorkerGlobalScope { + WorkerGlobalScope { + eventtarget: EventTarget::new_inherited(WorkerGlobalScopeTypeId(type_id)), + js_context: Untraceable::new(cx), + console: Cell::new(None), + } + } + + pub fn get_rust_cx<'a>(&'a self) -> &'a Rc { + &*self.js_context + } + pub fn get_cx(&self) -> *mut JSContext { + self.js_context.ptr + } +} + +pub trait WorkerGlobalScopeMethods { + fn Self(&self) -> Temporary; + fn Console(&self) -> Temporary; +} + +impl<'a> WorkerGlobalScopeMethods for JSRef<'a, WorkerGlobalScope> { + fn Self(&self) -> Temporary { + Temporary::from_rooted(self) + } + + fn Console(&self) -> Temporary { + if self.console.get().is_none() { + let console = Console::new(&global::Worker(*self)); + self.console.assign(Some(console)); + } + Temporary::new(self.console.get().get_ref().clone()) + } +} + +impl Reflectable for WorkerGlobalScope { + fn reflector<'a>(&'a self) -> &'a Reflector { + self.eventtarget.reflector() + } +} diff --git a/src/components/script/script.rs b/src/components/script/script.rs index b95db6c76ce..54f6f0db30f 100644 --- a/src/components/script/script.rs +++ b/src/components/script/script.rs @@ -30,6 +30,7 @@ extern crate js; extern crate libc; extern crate native; extern crate net; +extern crate rustrt; extern crate serialize; extern crate time; #[phase(plugin)] @@ -75,6 +76,7 @@ pub mod dom { pub mod comment; pub mod console; pub mod customevent; + pub mod dedicatedworkerglobalscope; pub mod document; pub mod documentfragment; pub mod documenttype; @@ -174,6 +176,8 @@ pub mod dom { pub mod validitystate; pub mod virtualmethods; pub mod window; + pub mod worker; + pub mod workerglobalscope; pub mod xmlhttprequest; pub mod xmlhttprequesteventtarget; pub mod xmlhttprequestupload; diff --git a/src/components/script/script_task.rs b/src/components/script/script_task.rs index 5551e9d6869..e173a37f762 100644 --- a/src/components/script/script_task.rs +++ b/src/components/script/script_task.rs @@ -115,10 +115,10 @@ impl ScriptChan { } } -struct StackRootTLS; +pub struct StackRootTLS; impl StackRootTLS { - fn new(roots: &RootCollection) -> StackRootTLS { + pub fn new(roots: &RootCollection) -> StackRootTLS { StackRoots.replace(Some(roots as *RootCollection)); StackRootTLS } @@ -210,6 +210,21 @@ impl ScriptTask { window_size: WindowSizeData) -> Rc { let (js_runtime, js_context) = ScriptTask::new_rt_and_cx(); + unsafe { + // JS_SetWrapObjectCallbacks clobbers the existing wrap callback, + // and JSCompartment::wrap crashes if that happens. The only way + // to retrieve the default callback is as the result of + // JS_SetWrapObjectCallbacks, which is why we call it twice. + let callback = JS_SetWrapObjectCallbacks((*js_runtime).ptr, + None, + Some(wrap_for_same_compartment), + None); + JS_SetWrapObjectCallbacks((*js_runtime).ptr, + callback, + Some(wrap_for_same_compartment), + Some(pre_wrap)); + } + let page = Page::new(id, None, layout_chan, window_size, resource_task.clone(), constellation_chan.clone(), @@ -231,26 +246,12 @@ impl ScriptTask { }) } - fn new_rt_and_cx() -> (js::rust::rt, Rc) { + pub fn new_rt_and_cx() -> (js::rust::rt, Rc) { let js_runtime = js::rust::rt(); assert!({ let ptr: *mut JSRuntime = (*js_runtime).ptr; ptr.is_not_null() }); - unsafe { - // JS_SetWrapObjectCallbacks clobbers the existing wrap callback, - // and JSCompartment::wrap crashes if that happens. The only way - // to retrieve the default callback is as the result of - // JS_SetWrapObjectCallbacks, which is why we call it twice. - let callback = JS_SetWrapObjectCallbacks((*js_runtime).ptr, - None, - Some(wrap_for_same_compartment), - None); - JS_SetWrapObjectCallbacks((*js_runtime).ptr, - callback, - Some(wrap_for_same_compartment), - Some(pre_wrap)); - } let js_context = js_runtime.cx(); assert!({ diff --git a/src/test/content/test_interfaces.html b/src/test/content/test_interfaces.html index a784a87f345..c26c9322278 100644 --- a/src/test/content/test_interfaces.html +++ b/src/test/content/test_interfaces.html @@ -59,6 +59,7 @@ var interfaceNamesInGlobalScope = [ "Comment", "Console", "CustomEvent", + "DedicatedWorkerGlobalScope", // #2823 "Document", "DocumentFragment", "DocumentType", @@ -156,6 +157,8 @@ var interfaceNamesInGlobalScope = [ "URLSearchParams", "ValidityState", "Window", + "Worker", + "WorkerGlobalScope", // #2823 "XMLHttpRequest", "XMLHttpRequestUpload", ];