Implement window.frameElement, change window.parent to make use of it.

This commit is contained in:
Glenn Watson 2015-02-05 10:01:04 +10:00
parent 5436922169
commit 1e0e98b63c
10 changed files with 88 additions and 74 deletions

View file

@ -11,6 +11,7 @@ use dom::bindings::proxyhandler::{get_property_descriptor, fill_property_descrip
use dom::bindings::utils::{Reflectable, WindowProxyHandler};
use dom::bindings::utils::get_array_index_from_id;
use dom::document::{Document, DocumentHelpers};
use dom::element::Element;
use dom::window::Window;
use dom::window::WindowHelpers;
@ -33,16 +34,16 @@ pub struct BrowserContext {
history: Vec<SessionHistoryEntry>,
active_index: uint,
window_proxy: *mut JSObject,
parent: Option<JS<Window>>,
frame_element: Option<JS<Element>>,
}
impl BrowserContext {
pub fn new(document: JSRef<Document>, parent: Option<JSRef<Window>>) -> BrowserContext {
pub fn new(document: JSRef<Document>, frame_element: Option<JSRef<Element>>) -> BrowserContext {
let mut context = BrowserContext {
history: vec!(SessionHistoryEntry::new(document)),
active_index: 0,
window_proxy: ptr::null_mut(),
parent: parent.map(|p| JS::from_rooted(p)),
frame_element: frame_element.map(JS::from_rooted),
};
context.create_window_proxy();
context
@ -57,17 +58,15 @@ impl BrowserContext {
doc.r().window()
}
pub fn frame_element(&self) -> Option<Temporary<Element>> {
self.frame_element.map(Temporary::new)
}
pub fn window_proxy(&self) -> *mut JSObject {
assert!(!self.window_proxy.is_null());
self.window_proxy
}
pub fn parent(&self) -> Option<Temporary<Window>> {
self.parent.map(|p| {
p.root().r().browser_context().as_ref().unwrap().active_window()
})
}
#[allow(unsafe_blocks)]
fn create_window_proxy(&mut self) {
let win = self.active_window().root();

View file

@ -36,7 +36,7 @@
// attribute any opener;
//readonly attribute WindowProxy parent;
readonly attribute Window parent;
//readonly attribute Element? frameElement;
readonly attribute Element? frameElement;
//WindowProxy open(optional DOMString url = "about:blank", optional DOMString target = "_blank", optional DOMString features = "", optional boolean replace = false);
//getter WindowProxy (unsigned long index);
//getter object (DOMString name);

View file

@ -18,9 +18,11 @@ use dom::bindings::utils::Reflectable;
use dom::browsercontext::BrowserContext;
use dom::console::Console;
use dom::document::Document;
use dom::element::Element;
use dom::eventtarget::{EventTarget, EventTargetHelpers, EventTargetTypeId};
use dom::location::Location;
use dom::navigator::Navigator;
use dom::node::window_from_node;
use dom::performance::Performance;
use dom::screen::Screen;
use dom::storage::Storage;
@ -68,7 +70,7 @@ pub struct Window {
navigation_start_precise: f64,
screen: MutNullableJS<Screen>,
session_storage: MutNullableJS<Storage>,
timers: TimerManager
timers: TimerManager,
}
impl Window {
@ -215,6 +217,10 @@ impl<'a> WindowMethods for JSRef<'a, Window> {
self.console.or_init(|| Console::new(GlobalRef::Window(self)))
}
fn GetFrameElement(self) -> Option<Temporary<Element>> {
self.browser_context().as_ref().unwrap().frame_element()
}
fn Navigator(self) -> Temporary<Navigator> {
self.navigator.or_init(|| Navigator::new(self))
}
@ -278,7 +284,14 @@ impl<'a> WindowMethods for JSRef<'a, Window> {
// https://html.spec.whatwg.org/multipage/browsers.html#dom-parent
fn Parent(self) -> Temporary<Window> {
self.browser_context().as_ref().unwrap().parent().unwrap_or(self.Window())
let browser_context = self.browser_context();
let browser_context = browser_context.as_ref().unwrap();
browser_context.frame_element().map_or(self.Window(), |fe| {
let frame_element = fe.root();
let window = window_from_node(frame_element.r()).root();
window.r().browser_context().as_ref().unwrap().active_window()
})
}
fn Performance(self) -> Temporary<Performance> {
@ -318,7 +331,7 @@ impl<'a> WindowMethods for JSRef<'a, Window> {
pub trait WindowHelpers {
fn flush_layout(self, goal: ReflowGoal, query: ReflowQueryType);
fn init_browser_context(self, doc: JSRef<Document>, parent: Option<JSRef<Window>>);
fn init_browser_context(self, doc: JSRef<Document>, frame_element: Option<JSRef<Element>>);
fn load_url(self, href: DOMString);
fn handle_fire_timer(self, timer_id: TimerId);
fn IndexedGetter(self, _index: u32, _found: &mut bool) -> Option<Temporary<Window>>;
@ -361,8 +374,8 @@ impl<'a> WindowHelpers for JSRef<'a, Window> {
self.page().flush_layout(goal, query);
}
fn init_browser_context(self, doc: JSRef<Document>, parent: Option<JSRef<Window>>) {
*self.browser_context.borrow_mut() = Some(BrowserContext::new(doc, parent));
fn init_browser_context(self, doc: JSRef<Document>, frame_element: Option<JSRef<Element>>) {
*self.browser_context.borrow_mut() = Some(BrowserContext::new(doc, frame_element));
}
/// Commence a new URL load which will either replace this window or scroll to a fragment.
@ -413,7 +426,7 @@ impl Window {
navigation_start_precise: time::precise_time_ns() as f64,
screen: Default::default(),
session_storage: Default::default(),
timers: TimerManager::new()
timers: TimerManager::new(),
};
WindowBinding::Wrap(cx, win)

View file

@ -12,7 +12,7 @@ use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, Documen
use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods;
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::codegen::InheritTypes::{ElementCast, EventTargetCast, NodeCast, EventCast};
use dom::bindings::codegen::InheritTypes::{ElementCast, EventTargetCast, HTMLIFrameElementCast, NodeCast, EventCast};
use dom::bindings::conversions::FromJSValConvertible;
use dom::bindings::conversions::StringificationBehavior;
use dom::bindings::global::GlobalRef;
@ -28,6 +28,7 @@ use dom::event::{Event, EventHelpers, EventBubbles, EventCancelable};
use dom::uievent::UIEvent;
use dom::eventtarget::{EventTarget, EventTargetHelpers};
use dom::htmlelement::HTMLElementTypeId;
use dom::htmliframeelement::HTMLIFrameElement;
use dom::keyboardevent::KeyboardEvent;
use dom::mouseevent::MouseEvent;
use dom::node::{self, Node, NodeHelpers, NodeDamage, NodeTypeId};
@ -53,7 +54,7 @@ use script_traits::ScriptTaskFactory;
use servo_msg::compositor_msg::ReadyState::{FinishedLoading, Loading, PerformingLayout};
use servo_msg::compositor_msg::{LayerId, ScriptListener};
use servo_msg::constellation_msg::{ConstellationChan};
use servo_msg::constellation_msg::{LoadData, NavigationDirection, PipelineId};
use servo_msg::constellation_msg::{LoadData, NavigationDirection, PipelineId, SubpageId};
use servo_msg::constellation_msg::{Failure, Msg, WindowSizeData, Key, KeyState};
use servo_msg::constellation_msg::{KeyModifiers, SUPER, SHIFT, CONTROL, ALT};
use servo_msg::constellation_msg::{PipelineExitType};
@ -586,8 +587,8 @@ impl ScriptTask {
match msg {
ConstellationControlMsg::AttachLayout(_) =>
panic!("should have handled AttachLayout already"),
ConstellationControlMsg::Load(id, parent_id, load_data) =>
self.load(id, parent_id, load_data),
ConstellationControlMsg::Load(id, parent, load_data) =>
self.load(id, parent, load_data),
ConstellationControlMsg::SendEvent(id, event) =>
self.handle_event(id, event),
ConstellationControlMsg::ReflowComplete(id, reflow_id) =>
@ -767,24 +768,35 @@ impl ScriptTask {
/// The entry point to document loading. Defines bindings, sets up the window and document
/// objects, parses HTML and CSS, and kicks off initial layout.
fn load(&self, pipeline_id: PipelineId, parent_id: Option<PipelineId>, load_data: LoadData) {
fn load(&self, pipeline_id: PipelineId,
parent: Option<(PipelineId, SubpageId)>, load_data: LoadData) {
let url = load_data.url.clone();
debug!("ScriptTask: loading {:?} on page {:?}", url, pipeline_id);
let borrowed_page = self.page.borrow_mut();
let parent_window = parent_id.and_then(|pid| {
// In the case a parent id exists but the matching page
// cannot be found, this means the page exists in a different
// script task (due to origin) so it shouldn't be returned.
// TODO: window.parent will continue to return self in that
// case, which is wrong. We should be returning an object that
// denies access to most properties (per
// https://github.com/servo/servo/issues/3939#issuecomment-62287025).
borrowed_page.find(pid).map(|page| {
page.frame.borrow().as_ref().unwrap().window.root()
})
});
let frame_element = parent.and_then(|(parent_id, subpage_id)| {
// In the case a parent id exists but the matching page
// cannot be found, this means the page exists in a different
// script task (due to origin) so it shouldn't be returned.
// TODO: window.parent will continue to return self in that
// case, which is wrong. We should be returning an object that
// denies access to most properties (per
// https://github.com/servo/servo/issues/3939#issuecomment-62287025).
borrowed_page.find(parent_id).and_then(|page| {
let match_iframe = |&:&node: &JSRef<HTMLIFrameElement>| {
node.subpage_id().map_or(false, |id| id == subpage_id)
};
let doc = page.frame().as_ref().unwrap().document.root();
let doc: JSRef<Node> = NodeCast::from_ref(doc.r());
doc.traverse_preorder()
.filter_map(|node| HTMLIFrameElementCast::to_ref(node))
.find(match_iframe)
.map(|node| Temporary::from_rooted(ElementCast::from_ref(node)))
})
}).root();
let page = borrowed_page.find(pipeline_id).expect("ScriptTask: received a load
message for a layout channel that is not associated with this script task. This
@ -865,7 +877,7 @@ impl ScriptTask {
if let Some(tm) = last_modified {
document.r().set_last_modified(dom_last_modified(&tm));
}
window.r().init_browser_context(document.r(), parent_window.r());
window.r().init_browser_context(document.r(), frame_element.r());
{