diff options
Diffstat (limited to 'src/components/script/dom/window.rs')
-rw-r--r-- | src/components/script/dom/window.rs | 513 |
1 files changed, 0 insertions, 513 deletions
diff --git a/src/components/script/dom/window.rs b/src/components/script/dom/window.rs deleted file mode 100644 index 23b6c71e029..00000000000 --- a/src/components/script/dom/window.rs +++ /dev/null @@ -1,513 +0,0 @@ -/* 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::EventHandlerBinding::{OnErrorEventHandlerNonNull, EventHandlerNonNull}; -use dom::bindings::codegen::Bindings::WindowBinding; -use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; -use dom::bindings::codegen::InheritTypes::EventTargetCast; -use dom::bindings::error::{Fallible, InvalidCharacter}; -use dom::bindings::global; -use dom::bindings::js::{JS, JSRef, Temporary, OptionalSettable}; -use dom::bindings::trace::{Traceable, Untraceable}; -use dom::bindings::utils::{Reflectable, Reflector}; -use dom::browsercontext::BrowserContext; -use dom::console::Console; -use dom::document::Document; -use dom::eventtarget::{EventTarget, WindowTypeId, EventTargetHelpers}; -use dom::location::Location; -use dom::navigator::Navigator; -use dom::performance::Performance; -use dom::screen::Screen; -use layout_interface::{ReflowForDisplay, DocumentDamageLevel}; -use page::Page; -use script_task::{ExitWindowMsg, FireTimerMsg, ScriptChan, TriggerLoadMsg, TriggerFragmentMsg}; -use script_traits::ScriptControlChan; - -use servo_msg::compositor_msg::ScriptListener; -use servo_net::image_cache_task::ImageCacheTask; -use servo_util::str::{DOMString,HTML_SPACE_CHARACTERS}; -use servo_util::task::{spawn_named}; - -use js::jsapi::JS_CallFunctionValue; -use js::jsapi::JSContext; -use js::jsapi::{JS_GC, JS_GetRuntime}; -use js::jsval::JSVal; -use js::jsval::NullValue; -use js::rust::with_compartment; -use url::{Url, UrlParser}; - -use serialize::base64::{FromBase64, ToBase64, STANDARD}; -use std::collections::hashmap::HashMap; -use std::cell::{Cell, RefCell}; -use std::cmp; -use std::comm::{channel, Sender}; -use std::comm::Select; -use std::hash::{Hash, sip}; -use std::io::timer::Timer; -use std::ptr; -use std::rc::Rc; -use time; - -#[deriving(PartialEq, Encodable, Eq)] -pub struct TimerId(i32); - -#[deriving(Encodable)] -pub struct TimerHandle { - handle: TimerId, - pub data: TimerData, - cancel_chan: Untraceable<Option<Sender<()>>>, -} - -impl Hash for TimerId { - fn hash(&self, state: &mut sip::SipState) { - let TimerId(id) = *self; - id.hash(state); - } -} - -impl TimerHandle { - fn cancel(&mut self) { - self.cancel_chan.as_ref().map(|chan| chan.send_opt(()).ok()); - } -} - -#[deriving(Encodable)] -pub struct Window { - eventtarget: EventTarget, - pub script_chan: ScriptChan, - control_chan: ScriptControlChan, - console: Cell<Option<JS<Console>>>, - location: Cell<Option<JS<Location>>>, - navigator: Cell<Option<JS<Navigator>>>, - pub image_cache_task: ImageCacheTask, - pub active_timers: Traceable<RefCell<HashMap<TimerId, TimerHandle>>>, - next_timer_handle: Traceable<Cell<i32>>, - compositor: Untraceable<Box<ScriptListener>>, - pub browser_context: Traceable<RefCell<Option<BrowserContext>>>, - pub page: Rc<Page>, - performance: Cell<Option<JS<Performance>>>, - pub navigationStart: u64, - pub navigationStartPrecise: f64, - screen: Cell<Option<JS<Screen>>>, -} - -impl Window { - pub fn get_cx(&self) -> *mut JSContext { - let js_info = self.page().js_info(); - (**js_info.get_ref().js_context).ptr - } - - pub fn page<'a>(&'a self) -> &'a Page { - &*self.page - } - pub fn get_url(&self) -> Url { - self.page().get_url() - } -} - -#[unsafe_destructor] -impl Drop for Window { - fn drop(&mut self) { - for (_, timer_handle) in self.active_timers.borrow_mut().mut_iter() { - timer_handle.cancel(); - } - } -} - -// Holder for the various JS values associated with setTimeout -// (ie. function value to invoke and all arguments to pass -// to the function when calling it) -#[deriving(Encodable)] -pub struct TimerData { - pub is_interval: bool, - pub funval: Traceable<JSVal>, -} - -impl<'a> WindowMethods for JSRef<'a, Window> { - fn Alert(&self, s: DOMString) { - // Right now, just print to the console - println!("ALERT: {:s}", s); - } - - fn Close(&self) { - let ScriptChan(ref chan) = self.script_chan; - chan.send(ExitWindowMsg(self.page.id.clone())); - } - - fn Document(&self) -> Temporary<Document> { - let frame = self.page().frame(); - Temporary::new(frame.get_ref().document.clone()) - } - - fn Location(&self) -> Temporary<Location> { - if self.location.get().is_none() { - let page = self.deref().page.clone(); - let location = Location::new(self, page); - self.location.assign(Some(location)); - } - Temporary::new(self.location.get().get_ref().clone()) - } - - fn Console(&self) -> Temporary<Console> { - if self.console.get().is_none() { - let console = Console::new(&global::Window(*self)); - self.console.assign(Some(console)); - } - Temporary::new(self.console.get().get_ref().clone()) - } - - fn Navigator(&self) -> Temporary<Navigator> { - if self.navigator.get().is_none() { - let navigator = Navigator::new(self); - self.navigator.assign(Some(navigator)); - } - Temporary::new(self.navigator.get().get_ref().clone()) - } - - fn SetTimeout(&self, _cx: *mut JSContext, callback: JSVal, timeout: i32) -> i32 { - self.set_timeout_or_interval(callback, timeout, false) - } - - fn ClearTimeout(&self, handle: i32) { - let mut timers = self.active_timers.deref().borrow_mut(); - let mut timer_handle = timers.pop(&TimerId(handle)); - match timer_handle { - Some(ref mut handle) => handle.cancel(), - None => { } - } - timers.remove(&TimerId(handle)); - } - - fn SetInterval(&self, _cx: *mut JSContext, callback: JSVal, timeout: i32) -> i32 { - self.set_timeout_or_interval(callback, timeout, true) - } - - fn ClearInterval(&self, handle: i32) { - self.ClearTimeout(handle); - } - - fn Window(&self) -> Temporary<Window> { - Temporary::from_rooted(self) - } - - fn Self(&self) -> Temporary<Window> { - self.Window() - } - - // http://www.whatwg.org/html/#dom-frames - fn Frames(&self) -> Temporary<Window> { - self.Window() - } - - fn Parent(&self) -> Temporary<Window> { - //TODO - Once we support iframes correctly this needs to return the parent frame - self.Window() - } - - fn Performance(&self) -> Temporary<Performance> { - if self.performance.get().is_none() { - let performance = Performance::new(self); - self.performance.assign(Some(performance)); - } - Temporary::new(self.performance.get().get_ref().clone()) - } - - fn GetOnclick(&self) -> Option<EventHandlerNonNull> { - let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self); - eventtarget.get_event_handler_common("click") - } - - fn SetOnclick(&self, listener: Option<EventHandlerNonNull>) { - let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self); - eventtarget.set_event_handler_common("click", listener) - } - - fn GetOnload(&self) -> Option<EventHandlerNonNull> { - let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self); - eventtarget.get_event_handler_common("load") - } - - fn SetOnload(&self, listener: Option<EventHandlerNonNull>) { - let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self); - eventtarget.set_event_handler_common("load", listener) - } - - fn GetOnunload(&self) -> Option<EventHandlerNonNull> { - let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self); - eventtarget.get_event_handler_common("unload") - } - - fn SetOnunload(&self, listener: Option<EventHandlerNonNull>) { - let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self); - eventtarget.set_event_handler_common("unload", listener) - } - - fn GetOnerror(&self) -> Option<OnErrorEventHandlerNonNull> { - let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self); - eventtarget.get_event_handler_common("error") - } - - fn SetOnerror(&self, listener: Option<OnErrorEventHandlerNonNull>) { - let eventtarget: &JSRef<EventTarget> = EventTargetCast::from_ref(self); - eventtarget.set_event_handler_common("error", listener) - } - - fn Screen(&self) -> Temporary<Screen> { - if self.screen.get().is_none() { - let screen = Screen::new(self); - self.screen.assign(Some(screen)); - } - Temporary::new(self.screen.get().get_ref().clone()) - } - - fn Debug(&self, message: DOMString) { - debug!("{:s}", message); - } - - fn Gc(&self) { - unsafe { - JS_GC(JS_GetRuntime(self.get_cx())); - } - } - - // http://www.whatwg.org/html/#atob - fn Btoa(&self, btoa: DOMString) -> Fallible<DOMString> { - let input = btoa.as_slice(); - // "The btoa() method must throw an InvalidCharacterError exception if - // the method's first argument contains any character whose code point - // is greater than U+00FF." - if input.chars().any(|c: char| c > '\u00FF') { - Err(InvalidCharacter) - } else { - // "Otherwise, the user agent must convert that argument to a - // sequence of octets whose nth octet is the eight-bit - // representation of the code point of the nth character of - // the argument," - let octets = input.chars().map(|c: char| c as u8).collect::<Vec<u8>>(); - - // "and then must apply the base64 algorithm to that sequence of - // octets, and return the result. [RFC4648]" - Ok(octets.as_slice().to_base64(STANDARD)) - } - } - - // http://www.whatwg.org/html/#atob - fn Atob(&self, atob: DOMString) -> Fallible<DOMString> { - // "Let input be the string being parsed." - let mut input = atob.as_slice(); - - // "Remove all space characters from input." - // serialize::base64::from_base64 ignores \r and \n, - // but it treats the other space characters as - // invalid input. - fn is_html_space(c: char) -> bool { - HTML_SPACE_CHARACTERS.iter().any(|&m| m == c) - } - let without_spaces = input.chars() - .filter(|&c| ! is_html_space(c)) - .collect::<String>(); - input = without_spaces.as_slice(); - - // "If the length of input divides by 4 leaving no remainder, then: - // if input ends with one or two U+003D EQUALS SIGN (=) characters, - // remove them from input." - if input.len() % 4 == 0 { - if input.ends_with("==") { - input = input.slice_to(input.len() - 2) - } else if input.ends_with("=") { - input = input.slice_to(input.len() - 1) - } - } - - // "If the length of input divides by 4 leaving a remainder of 1, - // throw an InvalidCharacterError exception and abort these steps." - if input.len() % 4 == 1 { - return Err(InvalidCharacter) - } - - // "If input contains a character that is not in the following list of - // characters and character ranges, throw an InvalidCharacterError - // exception and abort these steps: - // - // U+002B PLUS SIGN (+) - // U+002F SOLIDUS (/) - // Alphanumeric ASCII characters" - if input.chars() - .find(|&c| !(c == '+' || c == '/' || c.is_alphanumeric())) - .is_some() { - return Err(InvalidCharacter) - } - - match input.from_base64() { - Ok(data) => Ok(data.iter().map(|&b| b as char).collect::<String>()), - Err(..) => Err(InvalidCharacter) - } - } -} - -impl Reflectable for Window { - fn reflector<'a>(&'a self) -> &'a Reflector { - self.eventtarget.reflector() - } -} - -pub trait WindowHelpers { - fn damage_and_reflow(&self, damage: DocumentDamageLevel); - fn wait_until_safe_to_modify_dom(&self); - fn init_browser_context(&self, doc: &JSRef<Document>); - fn load_url(&self, href: DOMString); - fn handle_fire_timer(&self, timer_id: TimerId, cx: *mut JSContext); -} - -trait PrivateWindowHelpers { - fn set_timeout_or_interval(&self, callback: JSVal, timeout: i32, is_interval: bool) -> i32; -} - -impl<'a> WindowHelpers for JSRef<'a, Window> { - fn damage_and_reflow(&self, damage: DocumentDamageLevel) { - // FIXME This should probably be ReflowForQuery, not Display. All queries currently - // currently rely on the display list, which means we can't destroy it by - // doing a query reflow. - self.page().damage(damage); - self.page().reflow(ReflowForDisplay, self.control_chan.clone(), *self.compositor); - } - - fn wait_until_safe_to_modify_dom(&self) { - // FIXME: This disables concurrent layout while we are modifying the DOM, since - // our current architecture is entirely unsafe in the presence of races. - self.page().join_layout(); - } - - fn init_browser_context(&self, doc: &JSRef<Document>) { - *self.browser_context.deref().borrow_mut() = Some(BrowserContext::new(doc)); - } - - /// Commence a new URL load which will either replace this window or scroll to a fragment. - fn load_url(&self, href: DOMString) { - let base_url = self.page().get_url(); - debug!("current page url is {:?}", base_url); - let url = UrlParser::new().base_url(&base_url).parse(href.as_slice()); - // FIXME: handle URL parse errors more gracefully. - let url = url.unwrap(); - let ScriptChan(ref script_chan) = self.script_chan; - if href.as_slice().starts_with("#") { - script_chan.send(TriggerFragmentMsg(self.page.id, url)); - } else { - script_chan.send(TriggerLoadMsg(self.page.id, url)); - } - } - - fn handle_fire_timer(&self, timer_id: TimerId, cx: *mut JSContext) { - let this_value = self.reflector().get_jsobject(); - - let data = match self.active_timers.deref().borrow().find(&timer_id) { - None => return, - Some(timer_handle) => timer_handle.data, - }; - - // TODO: Support extra arguments. This requires passing a `*JSVal` array as `argv`. - with_compartment(cx, this_value, || { - let mut rval = NullValue(); - unsafe { - JS_CallFunctionValue(cx, this_value, *data.funval, - 0, ptr::mut_null(), &mut rval); - } - }); - - if !data.is_interval { - self.active_timers.deref().borrow_mut().remove(&timer_id); - } - } -} - -impl<'a> PrivateWindowHelpers for JSRef<'a, Window> { - fn set_timeout_or_interval(&self, callback: JSVal, timeout: i32, is_interval: bool) -> i32 { - let timeout = cmp::max(0, timeout) as u64; - let handle = self.next_timer_handle.deref().get(); - self.next_timer_handle.deref().set(handle + 1); - - // Post a delayed message to the per-window timer task; it will dispatch it - // to the relevant script handler that will deal with it. - let tm = Timer::new().unwrap(); - let (cancel_chan, cancel_port) = channel(); - let chan = self.script_chan.clone(); - let page_id = self.page.id.clone(); - let spawn_name = if is_interval { - "Window:SetInterval" - } else { - "Window:SetTimeout" - }; - spawn_named(spawn_name, proc() { - let mut tm = tm; - let timeout_port = if is_interval { - tm.periodic(timeout) - } else { - tm.oneshot(timeout) - }; - let cancel_port = cancel_port; - - let select = Select::new(); - let mut timeout_handle = select.handle(&timeout_port); - unsafe { timeout_handle.add() }; - let mut cancel_handle = select.handle(&cancel_port); - unsafe { cancel_handle.add() }; - - loop { - let id = select.wait(); - if id == timeout_handle.id() { - timeout_port.recv(); - let ScriptChan(ref chan) = chan; - chan.send(FireTimerMsg(page_id, TimerId(handle))); - if !is_interval { - break; - } - } else if id == cancel_handle.id() { - break; - } - } - }); - let timer_id = TimerId(handle); - let timer = TimerHandle { - handle: timer_id, - cancel_chan: Untraceable::new(Some(cancel_chan)), - data: TimerData { - is_interval: is_interval, - funval: Traceable::new(callback), - } - }; - self.active_timers.deref().borrow_mut().insert(timer_id, timer); - handle - } -} - -impl Window { - pub fn new(cx: *mut JSContext, - page: Rc<Page>, - script_chan: ScriptChan, - control_chan: ScriptControlChan, - compositor: Box<ScriptListener>, - image_cache_task: ImageCacheTask) - -> Temporary<Window> { - let win = box Window { - eventtarget: EventTarget::new_inherited(WindowTypeId), - script_chan: script_chan, - control_chan: control_chan, - console: Cell::new(None), - compositor: Untraceable::new(compositor), - page: page, - location: Cell::new(None), - navigator: Cell::new(None), - image_cache_task: image_cache_task, - active_timers: Traceable::new(RefCell::new(HashMap::new())), - next_timer_handle: Traceable::new(Cell::new(0)), - browser_context: Traceable::new(RefCell::new(None)), - performance: Cell::new(None), - navigationStart: time::get_time().sec as u64, - navigationStartPrecise: time::precise_time_s(), - screen: Cell::new(None), - }; - - WindowBinding::Wrap(cx, win) - } -} |