servo/components/script/dom/htmliframeelement.rs
Martin Robinson fbb1e0c6b8 Send incremental frame tree updates to the compositor
This allows the compositor to add frames after the call to SetIds,
where the initial frame tree is created. There are still some issues
preventing proper late frame creation, but this prevents crashes when
it happens.

Fixes #3738.
2014-11-11 17:36:19 -08:00

251 lines
8 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::attr::Attr;
use dom::attr::AttrHelpers;
use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding;
use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
use dom::bindings::codegen::InheritTypes::{NodeCast, ElementCast};
use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLIFrameElementDerived};
use dom::bindings::js::{JSRef, Temporary, OptionalRootable};
use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::Document;
use dom::element::{HTMLIFrameElementTypeId, Element};
use dom::element::AttributeHandlers;
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, NodeHelpers, ElementNodeTypeId, window_from_node};
use dom::virtualmethods::VirtualMethods;
use dom::window::Window;
use page::IterablePage;
use servo_msg::constellation_msg::{PipelineId, SubpageId};
use servo_msg::constellation_msg::{IFrameSandboxed, IFrameUnsandboxed};
use servo_msg::constellation_msg::{ConstellationChan, ScriptLoadedURLInIFrameMsg};
use servo_util::str::DOMString;
use std::ascii::StrAsciiExt;
use std::cell::Cell;
use url::{Url, UrlParser};
enum SandboxAllowance {
AllowNothing = 0x00,
AllowSameOrigin = 0x01,
AllowTopNavigation = 0x02,
AllowForms = 0x04,
AllowScripts = 0x08,
AllowPointerLock = 0x10,
AllowPopups = 0x20
}
#[dom_struct]
pub struct HTMLIFrameElement {
htmlelement: HTMLElement,
size: Cell<Option<IFrameSize>>,
sandbox: Cell<Option<u8>>,
}
impl HTMLIFrameElementDerived for EventTarget {
fn is_htmliframeelement(&self) -> bool {
*self.type_id() == NodeTargetTypeId(ElementNodeTypeId(HTMLIFrameElementTypeId))
}
}
#[jstraceable]
#[privatize]
pub struct IFrameSize {
pipeline_id: PipelineId,
subpage_id: SubpageId,
}
impl IFrameSize {
#[inline]
pub fn pipeline_id<'a>(&'a self) -> &'a PipelineId {
&self.pipeline_id
}
#[inline]
pub fn subpage_id<'a>(&'a self) -> &'a SubpageId {
&self.subpage_id
}
}
pub trait HTMLIFrameElementHelpers {
fn is_sandboxed(self) -> bool;
fn get_url(self) -> Option<Url>;
/// http://www.whatwg.org/html/#process-the-iframe-attributes
fn process_the_iframe_attributes(self);
}
impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> {
fn is_sandboxed(self) -> bool {
self.sandbox.get().is_some()
}
fn get_url(self) -> Option<Url> {
let element: JSRef<Element> = ElementCast::from_ref(self);
element.get_attribute(ns!(""), &atom!("src")).root().and_then(|src| {
let url = src.value();
if url.as_slice().is_empty() {
None
} else {
let window = window_from_node(self).root();
UrlParser::new().base_url(&window.page().get_url())
.parse(url.as_slice()).ok()
}
})
}
fn process_the_iframe_attributes(self) {
let url = match self.get_url() {
Some(url) => url.clone(),
None => Url::parse("about:blank").unwrap(),
};
let sandboxed = if self.is_sandboxed() {
IFrameSandboxed
} else {
IFrameUnsandboxed
};
// Subpage Id
let window = window_from_node(self).root();
let page = window.page();
let subpage_id = page.get_next_subpage_id();
self.size.set(Some(IFrameSize {
pipeline_id: page.id,
subpage_id: subpage_id,
}));
let ConstellationChan(ref chan) = page.constellation_chan;
chan.send(ScriptLoadedURLInIFrameMsg(url, page.id, subpage_id, sandboxed));
}
}
impl HTMLIFrameElement {
fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> HTMLIFrameElement {
HTMLIFrameElement {
htmlelement: HTMLElement::new_inherited(HTMLIFrameElementTypeId, localName, prefix, document),
size: Cell::new(None),
sandbox: Cell::new(None),
}
}
#[allow(unrooted_must_root)]
pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> Temporary<HTMLIFrameElement> {
let element = HTMLIFrameElement::new_inherited(localName, prefix, document);
Node::reflect_node(box element, document, HTMLIFrameElementBinding::Wrap)
}
#[inline]
pub fn size(&self) -> Option<IFrameSize> {
self.size.get()
}
}
impl<'a> HTMLIFrameElementMethods for JSRef<'a, HTMLIFrameElement> {
fn Src(self) -> DOMString {
let element: JSRef<Element> = ElementCast::from_ref(self);
element.get_string_attribute(&atom!("src"))
}
fn SetSrc(self, src: DOMString) {
let element: JSRef<Element> = ElementCast::from_ref(self);
element.set_url_attribute(&atom!("src"), src)
}
fn Sandbox(self) -> DOMString {
let element: JSRef<Element> = ElementCast::from_ref(self);
element.get_string_attribute(&atom!("sandbox"))
}
fn SetSandbox(self, sandbox: DOMString) {
let element: JSRef<Element> = ElementCast::from_ref(self);
element.set_string_attribute(&atom!("sandbox"), sandbox);
}
fn GetContentWindow(self) -> Option<Temporary<Window>> {
self.size.get().and_then(|size| {
let window = window_from_node(self).root();
let children = window.page().children.borrow();
let child = children.iter().find(|child| {
child.subpage_id.unwrap() == size.subpage_id
});
child.and_then(|page| {
page.frame.borrow().as_ref().map(|frame| {
Temporary::new(frame.window.clone())
})
})
})
}
}
impl<'a> VirtualMethods for JSRef<'a, HTMLIFrameElement> {
fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_borrowed_ref(self);
Some(htmlelement as &VirtualMethods)
}
fn after_set_attr(&self, attr: JSRef<Attr>) {
match self.super_type() {
Some(ref s) => s.after_set_attr(attr),
_ => ()
}
match attr.local_name() {
&atom!("sandbox") => {
let mut modes = AllowNothing as u8;
for word in attr.value().as_slice().split(' ') {
modes |= match word.to_ascii_lower().as_slice() {
"allow-same-origin" => AllowSameOrigin,
"allow-forms" => AllowForms,
"allow-pointer-lock" => AllowPointerLock,
"allow-popups" => AllowPopups,
"allow-scripts" => AllowScripts,
"allow-top-navigation" => AllowTopNavigation,
_ => AllowNothing
} as u8;
}
self.sandbox.set(Some(modes));
},
&atom!("src") => {
let node: JSRef<Node> = NodeCast::from_ref(*self);
if node.is_in_doc() {
self.process_the_iframe_attributes()
}
},
_ => ()
}
}
fn before_remove_attr(&self, attr: JSRef<Attr>) {
match self.super_type() {
Some(ref s) => s.before_remove_attr(attr),
_ => ()
}
match attr.local_name() {
&atom!("sandbox") => self.sandbox.set(None),
_ => ()
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
match self.super_type() {
Some(ref s) => s.bind_to_tree(tree_in_doc),
_ => (),
}
if tree_in_doc {
self.process_the_iframe_attributes();
}
}
}
impl Reflectable for HTMLIFrameElement {
fn reflector<'a>(&'a self) -> &'a Reflector {
self.htmlelement.reflector()
}
}