gecko-dev/servo/src/components/script/dom/window.rs
Patrick Walton cf773ab0f2 servo: Merge #1424 - Harden layout (from pcwalton:harden-layout); r=pcwalton
This changeset gets rid of the `FooView` phantom type in favor of a more brute force approach that just whitelists methods that layout is allowed to call. The set is surprisingly small now that layout isn't going to the DOM for much.

If this approach turns out not to scale, we can do something fancier, but I'd rather just have it be safe and secure first and then refactor later for programmer happiness.

r? @kmcallister

Source-Repo: https://github.com/servo/servo
Source-Revision: 824c7ac613ebb80bb432ff6425c5e25c642b6afb
2013-12-17 18:16:05 -08:00

262 lines
7.7 KiB
Rust

/* 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::WindowBinding;
use dom::bindings::utils::{Reflectable, Reflector, Traceable};
use dom::bindings::utils::{trace_option, trace_reflector};
use dom::bindings::utils::DOMString;
use dom::document::AbstractDocument;
use dom::eventtarget::{EventTarget, WindowTypeId};
use dom::node::AbstractNode;
use dom::location::Location;
use dom::navigator::Navigator;
use layout_interface::{ReflowForDisplay, DocumentDamageLevel};
use script_task::{ExitWindowMsg, FireTimerMsg, Page, ScriptChan};
use servo_msg::compositor_msg::ScriptListener;
use servo_net::image_cache_task::ImageCacheTask;
use js::glue::*;
use js::jsapi::{JSObject, JSContext, JS_DefineProperty};
use js::jsapi::{JSPropertyOp, JSStrictPropertyOp, JSTracer};
use js::{JSVAL_NULL, JSPROP_ENUMERATE};
use std::cell::Cell;
use std::comm;
use std::comm::SharedChan;
use std::hashmap::HashSet;
use std::ptr;
use std::int;
use std::rt::io::timer::Timer;
use std::task::spawn_with;
use js::jsapi::JSVal;
pub enum TimerControlMsg {
TimerMessage_Fire(~TimerData),
TimerMessage_Close,
TimerMessage_TriggerExit //XXXjdm this is just a quick hack to talk to the script task
}
pub struct Window {
eventtarget: EventTarget,
page: @mut Page,
script_chan: ScriptChan,
compositor: @ScriptListener,
timer_chan: SharedChan<TimerControlMsg>,
location: Option<@mut Location>,
navigator: Option<@mut Navigator>,
image_cache_task: ImageCacheTask,
active_timers: ~HashSet<i32>,
next_timer_handle: i32,
}
impl Window {
pub fn get_cx(&self) -> *JSObject {
self.page.js_info.get_ref().js_compartment.cx.ptr
}
}
#[unsafe_destructor]
impl Drop for Window {
fn drop(&mut self) {
self.timer_chan.send(TimerMessage_Close);
}
}
// 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)
pub struct TimerData {
handle: i32,
funval: JSVal,
args: ~[JSVal],
}
impl Window {
pub fn Alert(&self, s: DOMString) {
// Right now, just print to the console
println(format!("ALERT: {:s}", s));
}
pub fn Close(&self) {
self.timer_chan.send(TimerMessage_TriggerExit);
}
pub fn Document(&self) -> AbstractDocument {
self.page.frame.unwrap().document
}
pub fn Name(&self) -> DOMString {
~""
}
pub fn SetName(&self, _name: DOMString) {
}
pub fn Status(&self) -> DOMString {
~""
}
pub fn SetStatus(&self, _status: DOMString) {
}
pub fn Closed(&self) -> bool {
false
}
pub fn Stop(&self) {
}
pub fn Focus(&self) {
}
pub fn Blur(&self) {
}
pub fn GetFrameElement(&self) -> Option<AbstractNode> {
None
}
pub fn Location(&mut self) -> @mut Location {
if self.location.is_none() {
self.location = Some(Location::new(self, self.page));
}
self.location.unwrap()
}
pub fn Navigator(&mut self) -> @mut Navigator {
if self.navigator.is_none() {
self.navigator = Some(Navigator::new(self));
}
self.navigator.unwrap()
}
pub fn Confirm(&self, _message: DOMString) -> bool {
false
}
pub fn Prompt(&self, _message: DOMString, _default: DOMString) -> Option<DOMString> {
None
}
pub fn Print(&self) {
}
pub fn ShowModalDialog(&self, _cx: *JSContext, _url: DOMString, _argument: JSVal) -> JSVal {
JSVAL_NULL
}
}
impl Reflectable for Window {
fn reflector<'a>(&'a self) -> &'a Reflector {
self.eventtarget.reflector()
}
fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector {
self.eventtarget.mut_reflector()
}
}
impl Window {
pub fn SetTimeout(&mut self, _cx: *JSContext, callback: JSVal, timeout: i32) -> i32 {
let timeout = int::max(0, timeout) as u64;
let handle = self.next_timer_handle;
self.next_timer_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 = Cell::new(Timer::new().unwrap());
let chan = self.timer_chan.clone();
do spawn {
let mut tm = tm.take();
tm.sleep(timeout);
chan.send(TimerMessage_Fire(~TimerData {
handle: handle,
funval: callback,
args: ~[]
}));
}
self.active_timers.insert(handle);
handle
}
pub fn ClearTimeout(&mut self, handle: i32) {
self.active_timers.remove(&handle);
}
pub 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.script_chan.clone(), self.compositor);
}
pub 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();
}
#[fixed_stack_segment]
pub fn new(cx: *JSContext,
page: @mut Page,
script_chan: ScriptChan,
compositor: @ScriptListener,
image_cache_task: ImageCacheTask)
-> @mut Window {
let win = @mut Window {
eventtarget: EventTarget::new_inherited(WindowTypeId),
page: page,
script_chan: script_chan.clone(),
compositor: compositor,
timer_chan: {
let (timer_port, timer_chan) = comm::stream::<TimerControlMsg>();
let id = page.id.clone();
do spawn_with(script_chan) |script_chan| {
loop {
match timer_port.recv() {
TimerMessage_Close => break,
TimerMessage_Fire(td) => script_chan.send(FireTimerMsg(id, td)),
TimerMessage_TriggerExit => script_chan.send(ExitWindowMsg(id)),
}
}
}
SharedChan::new(timer_chan)
},
location: None,
navigator: None,
image_cache_task: image_cache_task,
active_timers: ~HashSet::new(),
next_timer_handle: 0
};
let global = WindowBinding::Wrap(cx, ptr::null(), win);
unsafe {
let fn_names = ["window","self"];
for str in fn_names.iter() {
do (*str).to_c_str().with_ref |name| {
JS_DefineProperty(cx, global, name,
RUST_OBJECT_TO_JSVAL(global),
Some(GetJSClassHookStubPointer(PROPERTY_STUB) as JSPropertyOp),
Some(GetJSClassHookStubPointer(STRICT_PROPERTY_STUB) as JSStrictPropertyOp),
JSPROP_ENUMERATE);
}
}
}
win
}
}
impl Traceable for Window {
fn trace(&self, tracer: *mut JSTracer) {
debug!("tracing window");
self.page.frame.map(|frame| trace_reflector(tracer, "document", frame.document.reflector()));
trace_option(tracer, "location", self.location);
trace_option(tracer, "navigator", self.navigator);
}
}