mirror of
https://github.com/servo/servo.git
synced 2025-07-22 23:03:42 +01:00
remove page and move functionality to browing context
Allow for adding history items Fixed nested iframe test failure Cleanup and small refactors fixup
This commit is contained in:
parent
392135bd0c
commit
cbc5ca65a8
9 changed files with 461 additions and 479 deletions
|
@ -15,6 +15,7 @@ use dom::bindings::conversions::{FromJSValConvertible, jsstring_to_str};
|
|||
use dom::bindings::global::GlobalRef;
|
||||
use dom::bindings::inheritance::Castable;
|
||||
use dom::bindings::js::Root;
|
||||
use dom::browsingcontext::{BrowsingContext, IterableContext};
|
||||
use dom::element::Element;
|
||||
use dom::node::Node;
|
||||
use dom::window::Window;
|
||||
|
@ -22,10 +23,8 @@ use ipc_channel::ipc::IpcSender;
|
|||
use js::jsapi::{ObjectClassName, RootedObject, RootedValue};
|
||||
use js::jsval::UndefinedValue;
|
||||
use msg::constellation_msg::PipelineId;
|
||||
use page::{IterablePage, Page};
|
||||
use script_thread::get_page;
|
||||
use script_thread::get_browsing_context;
|
||||
use std::ffi::CStr;
|
||||
use std::rc::Rc;
|
||||
use std::str;
|
||||
use style::properties::longhands::{margin_top, margin_right, margin_bottom, margin_left};
|
||||
use util::str::DOMString;
|
||||
|
@ -66,28 +65,31 @@ pub fn handle_evaluate_js(global: &GlobalRef, eval: String, reply: IpcSender<Eva
|
|||
reply.send(result).unwrap();
|
||||
}
|
||||
|
||||
pub fn handle_get_root_node(page: &Rc<Page>, pipeline: PipelineId, reply: IpcSender<NodeInfo>) {
|
||||
let page = get_page(&*page, pipeline);
|
||||
let document = page.document();
|
||||
pub fn handle_get_root_node(context: &Root<BrowsingContext>, pipeline: PipelineId, reply: IpcSender<NodeInfo>) {
|
||||
let context = get_browsing_context(context, pipeline);
|
||||
let document = context.active_document();
|
||||
|
||||
let node = document.upcast::<Node>();
|
||||
reply.send(node.summarize()).unwrap();
|
||||
}
|
||||
|
||||
pub fn handle_get_document_element(page: &Rc<Page>,
|
||||
pub fn handle_get_document_element(context: &Root<BrowsingContext>,
|
||||
pipeline: PipelineId,
|
||||
reply: IpcSender<NodeInfo>) {
|
||||
let page = get_page(&*page, pipeline);
|
||||
let document = page.document();
|
||||
let context = get_browsing_context(context, pipeline);
|
||||
let document = context.active_document();
|
||||
let document_element = document.GetDocumentElement().unwrap();
|
||||
|
||||
let node = document_element.upcast::<Node>();
|
||||
reply.send(node.summarize()).unwrap();
|
||||
}
|
||||
|
||||
fn find_node_by_unique_id(page: &Rc<Page>, pipeline: PipelineId, node_id: String) -> Root<Node> {
|
||||
let page = get_page(&*page, pipeline);
|
||||
let document = page.document();
|
||||
fn find_node_by_unique_id(context: &Root<BrowsingContext>,
|
||||
pipeline: PipelineId,
|
||||
node_id: String)
|
||||
-> Root<Node> {
|
||||
let context = get_browsing_context(context, pipeline);
|
||||
let document = context.active_document();
|
||||
let node = document.upcast::<Node>();
|
||||
|
||||
for candidate in node.traverse_preorder() {
|
||||
|
@ -99,29 +101,29 @@ fn find_node_by_unique_id(page: &Rc<Page>, pipeline: PipelineId, node_id: String
|
|||
panic!("couldn't find node with unique id {}", node_id)
|
||||
}
|
||||
|
||||
pub fn handle_get_children(page: &Rc<Page>,
|
||||
pub fn handle_get_children(context: &Root<BrowsingContext>,
|
||||
pipeline: PipelineId,
|
||||
node_id: String,
|
||||
reply: IpcSender<Vec<NodeInfo>>) {
|
||||
let parent = find_node_by_unique_id(&*page, pipeline, node_id);
|
||||
let parent = find_node_by_unique_id(context, pipeline, node_id);
|
||||
let children = parent.children()
|
||||
.map(|child| child.summarize())
|
||||
.collect();
|
||||
reply.send(children).unwrap();
|
||||
}
|
||||
|
||||
pub fn handle_get_layout(page: &Rc<Page>,
|
||||
pub fn handle_get_layout(context: &Root<BrowsingContext>,
|
||||
pipeline: PipelineId,
|
||||
node_id: String,
|
||||
reply: IpcSender<ComputedNodeLayout>) {
|
||||
let node = find_node_by_unique_id(&*page, pipeline, node_id);
|
||||
let node = find_node_by_unique_id(context, pipeline, node_id);
|
||||
|
||||
let elem = node.downcast::<Element>().expect("should be getting layout of element");
|
||||
let rect = elem.GetBoundingClientRect();
|
||||
let width = rect.Width() as f32;
|
||||
let height = rect.Height() as f32;
|
||||
|
||||
let window = page.window();
|
||||
let window = context.active_window();
|
||||
let elem = node.downcast::<Element>().expect("should be getting layout of element");
|
||||
let computed_style = window.r().GetComputedStyle(elem, None);
|
||||
|
||||
|
@ -200,11 +202,11 @@ pub fn handle_get_cached_messages(_pipeline_id: PipelineId,
|
|||
reply.send(messages).unwrap();
|
||||
}
|
||||
|
||||
pub fn handle_modify_attribute(page: &Rc<Page>,
|
||||
pub fn handle_modify_attribute(context: &Root<BrowsingContext>,
|
||||
pipeline: PipelineId,
|
||||
node_id: String,
|
||||
modifications: Vec<Modification>) {
|
||||
let node = find_node_by_unique_id(&*page, pipeline, node_id);
|
||||
let node = find_node_by_unique_id(context, pipeline, node_id);
|
||||
let elem = node.downcast::<Element>().expect("should be getting layout of element");
|
||||
|
||||
for modification in modifications {
|
||||
|
@ -222,22 +224,25 @@ pub fn handle_wants_live_notifications(global: &GlobalRef, send_notifications: b
|
|||
global.set_devtools_wants_updates(send_notifications);
|
||||
}
|
||||
|
||||
pub fn handle_set_timeline_markers(page: &Rc<Page>,
|
||||
pub fn handle_set_timeline_markers(context: &Root<BrowsingContext>,
|
||||
marker_types: Vec<TimelineMarkerType>,
|
||||
reply: IpcSender<TimelineMarker>) {
|
||||
let window = page.window();
|
||||
let window = context.active_window();
|
||||
window.set_devtools_timeline_markers(marker_types, reply);
|
||||
}
|
||||
|
||||
pub fn handle_drop_timeline_markers(page: &Rc<Page>, marker_types: Vec<TimelineMarkerType>) {
|
||||
let window = page.window();
|
||||
pub fn handle_drop_timeline_markers(context: &Root<BrowsingContext>,
|
||||
marker_types: Vec<TimelineMarkerType>) {
|
||||
let window = context.active_window();
|
||||
window.drop_devtools_timeline_markers(marker_types);
|
||||
}
|
||||
|
||||
pub fn handle_request_animation_frame(page: &Rc<Page>, id: PipelineId, actor_name: String) {
|
||||
let page = page.find(id).expect("There is no such page");
|
||||
let doc = page.document();
|
||||
let devtools_sender = page.window().devtools_chan().unwrap();
|
||||
pub fn handle_request_animation_frame(context: &Root<BrowsingContext>,
|
||||
id: PipelineId,
|
||||
actor_name: String) {
|
||||
let context = context.find(id).expect("There is no such context");
|
||||
let doc = context.active_document();
|
||||
let devtools_sender = context.active_window().devtools_chan().unwrap();
|
||||
doc.request_animation_frame(box move |time| {
|
||||
let msg = ScriptToDevtoolsControlMsg::FramerateTick(actor_name, time);
|
||||
devtools_sender.send(msg).unwrap();
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use dom::bindings::cell::DOMRefCell;
|
||||
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
|
||||
use dom::bindings::conversions::{ToJSValConvertible, root_from_handleobject};
|
||||
use dom::bindings::js::{JS, Root, RootedReference};
|
||||
use dom::bindings::proxyhandler::{fill_property_descriptor, get_property_descriptor};
|
||||
|
@ -22,27 +23,48 @@ use js::jsapi::{JS_ForwardGetPropertyTo, JS_ForwardSetPropertyTo, JS_GetClass, J
|
|||
use js::jsapi::{JS_GetOwnPropertyDescriptorById, JS_HasPropertyById, MutableHandle};
|
||||
use js::jsapi::{MutableHandleValue, ObjectOpResult, RootedObject, RootedValue};
|
||||
use js::jsval::{UndefinedValue, PrivateValue};
|
||||
use msg::constellation_msg::{PipelineId, SubpageId};
|
||||
use std::cell::Cell;
|
||||
use url::Url;
|
||||
use util::str::DOMString;
|
||||
|
||||
#[dom_struct]
|
||||
pub struct BrowsingContext {
|
||||
reflector: Reflector,
|
||||
|
||||
/// Pipeline id associated with this context.
|
||||
id: PipelineId,
|
||||
|
||||
/// Indicates if reflow is required when reloading.
|
||||
needs_reflow: Cell<bool>,
|
||||
|
||||
/// Stores this context's session history
|
||||
history: DOMRefCell<Vec<SessionHistoryEntry>>,
|
||||
active_index: usize,
|
||||
|
||||
/// The index of the active session history entry
|
||||
active_index: Cell<usize>,
|
||||
|
||||
/// Stores the child browsing contexts (ex. iframe browsing context)
|
||||
children: DOMRefCell<Vec<JS<BrowsingContext>>>,
|
||||
|
||||
frame_element: Option<JS<Element>>,
|
||||
}
|
||||
|
||||
impl BrowsingContext {
|
||||
pub fn new_inherited(frame_element: Option<&Element>) -> BrowsingContext {
|
||||
pub fn new_inherited(frame_element: Option<&Element>, id: PipelineId) -> BrowsingContext {
|
||||
BrowsingContext {
|
||||
reflector: Reflector::new(),
|
||||
id: id,
|
||||
needs_reflow: Cell::new(true),
|
||||
history: DOMRefCell::new(vec![]),
|
||||
active_index: 0,
|
||||
active_index: Cell::new(0),
|
||||
children: DOMRefCell::new(vec![]),
|
||||
frame_element: frame_element.map(JS::from_ref),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
pub fn new(window: &Window, frame_element: Option<&Element>) -> Root<BrowsingContext> {
|
||||
pub fn new(window: &Window, frame_element: Option<&Element>, id: PipelineId) -> Root<BrowsingContext> {
|
||||
unsafe {
|
||||
let WindowProxyHandler(handler) = window.windowproxy_handler();
|
||||
assert!(!handler.is_null());
|
||||
|
@ -57,7 +79,7 @@ impl BrowsingContext {
|
|||
NewWindowProxy(cx, parent, handler));
|
||||
assert!(!window_proxy.ptr.is_null());
|
||||
|
||||
let object = box BrowsingContext::new_inherited(frame_element);
|
||||
let object = box BrowsingContext::new_inherited(frame_element, id);
|
||||
|
||||
let raw = Box::into_raw(object);
|
||||
SetProxyExtra(window_proxy.ptr, 0, &PrivateValue(raw as *const _));
|
||||
|
@ -70,12 +92,20 @@ impl BrowsingContext {
|
|||
|
||||
pub fn init(&self, document: &Document) {
|
||||
assert!(self.history.borrow().is_empty());
|
||||
assert_eq!(self.active_index, 0);
|
||||
self.history.borrow_mut().push(SessionHistoryEntry::new(document));
|
||||
assert_eq!(self.active_index.get(), 0);
|
||||
self.history.borrow_mut().push(SessionHistoryEntry::new(document, document.url().clone(), document.Title()));
|
||||
}
|
||||
|
||||
pub fn push_history(&self, document: &Document) {
|
||||
let mut history = self.history.borrow_mut();
|
||||
// Clear all session history entries after the active index
|
||||
history.drain((self.active_index.get() + 1)..);
|
||||
history.push(SessionHistoryEntry::new(document, document.url().clone(), document.Title()));
|
||||
self.active_index.set(self.active_index.get() + 1);
|
||||
}
|
||||
|
||||
pub fn active_document(&self) -> Root<Document> {
|
||||
Root::from_ref(&*self.history.borrow()[self.active_index].document)
|
||||
Root::from_ref(&self.history.borrow()[self.active_index.get()].document)
|
||||
}
|
||||
|
||||
pub fn active_window(&self) -> Root<Window> {
|
||||
|
@ -91,6 +121,92 @@ impl BrowsingContext {
|
|||
assert!(!window_proxy.get().is_null());
|
||||
window_proxy.get()
|
||||
}
|
||||
|
||||
pub fn remove(&self, id: PipelineId) -> Option<Root<BrowsingContext>> {
|
||||
let remove_idx = self.children
|
||||
.borrow()
|
||||
.iter()
|
||||
.position(|context| context.id == id);
|
||||
match remove_idx {
|
||||
Some(idx) => Some(Root::from_ref(&*self.children.borrow_mut().remove(idx))),
|
||||
None => {
|
||||
self.children
|
||||
.borrow_mut()
|
||||
.iter_mut()
|
||||
.filter_map(|context| context.remove(id))
|
||||
.next()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_reflow_status(&self, status: bool) -> bool {
|
||||
let old = self.needs_reflow.get();
|
||||
self.needs_reflow.set(status);
|
||||
old
|
||||
}
|
||||
|
||||
pub fn pipeline(&self) -> PipelineId {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn push_child_context(&self, context: &BrowsingContext) {
|
||||
self.children.borrow_mut().push(JS::from_ref(&context));
|
||||
}
|
||||
|
||||
pub fn find_child_by_subpage(&self, subpage_id: SubpageId) -> Option<Root<Window>> {
|
||||
self.children.borrow().iter().find(|context| {
|
||||
let window = context.active_window();
|
||||
window.subpage() == Some(subpage_id)
|
||||
}).map(|context| context.active_window())
|
||||
}
|
||||
|
||||
pub fn clear_session_history(&self) {
|
||||
self.active_index.set(0);
|
||||
self.history.borrow_mut().clear();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ContextIterator {
|
||||
stack: Vec<Root<BrowsingContext>>,
|
||||
}
|
||||
|
||||
pub trait IterableContext {
|
||||
fn iter(&self) -> ContextIterator;
|
||||
fn find(&self, id: PipelineId) -> Option<Root<BrowsingContext>>;
|
||||
}
|
||||
|
||||
impl IterableContext for BrowsingContext {
|
||||
fn iter(&self) -> ContextIterator {
|
||||
ContextIterator {
|
||||
stack: vec!(Root::from_ref(self)),
|
||||
}
|
||||
}
|
||||
|
||||
fn find(&self, id: PipelineId) -> Option<Root<BrowsingContext>> {
|
||||
if self.id == id {
|
||||
return Some(Root::from_ref(self));
|
||||
}
|
||||
|
||||
self.children.borrow()
|
||||
.iter()
|
||||
.filter_map(|c| c.find(id))
|
||||
.next()
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for ContextIterator {
|
||||
type Item = Root<BrowsingContext>;
|
||||
|
||||
fn next(&mut self) -> Option<Root<BrowsingContext>> {
|
||||
let popped = self.stack.pop();
|
||||
if let Some(ref context) = popped {
|
||||
self.stack.extend(context.children.borrow()
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|ref c| Root::from_ref(&**c)));
|
||||
}
|
||||
popped
|
||||
}
|
||||
}
|
||||
|
||||
// This isn't a DOM struct, just a convenience struct
|
||||
|
@ -100,14 +216,16 @@ impl BrowsingContext {
|
|||
#[derive(JSTraceable, HeapSizeOf)]
|
||||
pub struct SessionHistoryEntry {
|
||||
document: JS<Document>,
|
||||
children: Vec<JS<BrowsingContext>>,
|
||||
url: Url,
|
||||
title: DOMString,
|
||||
}
|
||||
|
||||
impl SessionHistoryEntry {
|
||||
fn new(document: &Document) -> SessionHistoryEntry {
|
||||
fn new(document: &Document, url: Url, title: DOMString) -> SessionHistoryEntry {
|
||||
SessionHistoryEntry {
|
||||
document: JS::from_ref(document),
|
||||
children: vec![],
|
||||
url: url,
|
||||
title: title,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,6 @@ use layout_interface::ReflowQueryType;
|
|||
use msg::constellation_msg::{ConstellationChan, LoadData};
|
||||
use msg::constellation_msg::{NavigationDirection, PipelineId, SubpageId};
|
||||
use net_traits::response::HttpsState;
|
||||
use page::IterablePage;
|
||||
use script_traits::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed};
|
||||
use script_traits::{IFrameLoadInfo, MozBrowserEvent, ScriptMsg as ConstellationMsg};
|
||||
use std::ascii::AsciiExt;
|
||||
|
@ -418,11 +417,8 @@ impl HTMLIFrameElementMethods for HTMLIFrameElement {
|
|||
self.subpage_id.get().and_then(|subpage_id| {
|
||||
let window = window_from_node(self);
|
||||
let window = window.r();
|
||||
let children = window.page().children.borrow();
|
||||
children.iter().find(|page| {
|
||||
let window = page.window();
|
||||
window.subpage() == Some(subpage_id)
|
||||
}).map(|page| page.window())
|
||||
let browsing_context = window.browsing_context();
|
||||
browsing_context.find_child_by_subpage(subpage_id)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -10,12 +10,12 @@ use dom::bindings::inheritance::Castable;
|
|||
use dom::bindings::js::{Root, RootedReference};
|
||||
use dom::bindings::refcounted::Trusted;
|
||||
use dom::bindings::reflector::{Reflectable, Reflector, reflect_dom_object};
|
||||
use dom::browsingcontext::IterableContext;
|
||||
use dom::event::{Event, EventBubbles, EventCancelable};
|
||||
use dom::storageevent::StorageEvent;
|
||||
use dom::urlhelper::UrlHelper;
|
||||
use ipc_channel::ipc;
|
||||
use net_traits::storage_thread::{StorageThread, StorageThreadMsg, StorageType};
|
||||
use page::IterablePage;
|
||||
use script_runtime::ScriptChan;
|
||||
use script_thread::{MainThreadRunnable, ScriptThread};
|
||||
use task_source::dom_manipulation::DOMManipulationTask;
|
||||
|
@ -199,9 +199,9 @@ impl MainThreadRunnable for StorageEventRunnable {
|
|||
Some(storage)
|
||||
);
|
||||
|
||||
let root_page = script_thread.root_page();
|
||||
for it_page in root_page.iter() {
|
||||
let it_window_root = it_page.window();
|
||||
let root_context = script_thread.root_browsing_context();
|
||||
for it_context in root_context.iter() {
|
||||
let it_window_root = it_context.active_window();
|
||||
let it_window = it_window_root.r();
|
||||
assert!(UrlHelper::SameOrigin(&ev_url, &it_window.get_url()));
|
||||
// TODO: Such a Document object is not necessarily fully active, but events fired on such
|
||||
|
|
|
@ -51,7 +51,6 @@ use net_traits::bluetooth_thread::BluetoothMethodMsg;
|
|||
use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheThread};
|
||||
use net_traits::storage_thread::{StorageThread, StorageType};
|
||||
use num_traits::ToPrimitive;
|
||||
use page::Page;
|
||||
use profile_traits::mem;
|
||||
use reporter::CSSErrorReporter;
|
||||
use rustc_serialize::base64::{FromBase64, STANDARD, ToBase64};
|
||||
|
@ -151,7 +150,6 @@ pub struct Window {
|
|||
#[ignore_heap_size_of = "TODO(#6911) newtypes containing unmeasurable types are hard"]
|
||||
compositor: IpcSender<ScriptToCompositorMsg>,
|
||||
browsing_context: MutNullableHeap<JS<BrowsingContext>>,
|
||||
page: Rc<Page>,
|
||||
performance: MutNullableHeap<JS<Performance>>,
|
||||
navigation_start: u64,
|
||||
navigation_start_precise: f64,
|
||||
|
@ -337,10 +335,6 @@ impl Window {
|
|||
self.browsing_context.get().unwrap()
|
||||
}
|
||||
|
||||
pub fn page(&self) -> &Page {
|
||||
&*self.page
|
||||
}
|
||||
|
||||
pub fn bluetooth_thread(&self) -> IpcSender<BluetoothMethodMsg> {
|
||||
self.bluetooth_thread.clone()
|
||||
}
|
||||
|
@ -1420,7 +1414,6 @@ impl Window {
|
|||
|
||||
impl Window {
|
||||
pub fn new(runtime: Rc<Runtime>,
|
||||
page: Rc<Page>,
|
||||
script_chan: MainThreadScriptChan,
|
||||
dom_task_source: DOMManipulationTaskSource,
|
||||
user_task_source: UserInteractionTaskSource,
|
||||
|
@ -1467,7 +1460,6 @@ impl Window {
|
|||
console: Default::default(),
|
||||
crypto: Default::default(),
|
||||
compositor: compositor,
|
||||
page: page,
|
||||
navigator: Default::default(),
|
||||
image_cache_thread: image_cache_thread,
|
||||
mem_profiler_chan: mem_profiler_chan,
|
||||
|
|
|
@ -95,7 +95,6 @@ pub mod layout_interface;
|
|||
mod mem;
|
||||
mod network_listener;
|
||||
pub mod origin;
|
||||
pub mod page;
|
||||
pub mod parse;
|
||||
pub mod reporter;
|
||||
pub mod script_runtime;
|
||||
|
|
|
@ -1,134 +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::cell::DOMRefCell;
|
||||
use dom::bindings::js::{JS, Root};
|
||||
use dom::document::Document;
|
||||
use dom::window::Window;
|
||||
use msg::constellation_msg::PipelineId;
|
||||
use std::cell::Cell;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// Encapsulates a handle to a frame in a frame tree.
|
||||
#[derive(JSTraceable, HeapSizeOf)]
|
||||
#[allow(unrooted_must_root)] // FIXME(#6687) this is wrong
|
||||
pub struct Page {
|
||||
/// Pipeline id associated with this page.
|
||||
id: PipelineId,
|
||||
|
||||
/// The outermost frame containing the document and window.
|
||||
frame: DOMRefCell<Option<Frame>>,
|
||||
|
||||
/// Indicates if reflow is required when reloading.
|
||||
needs_reflow: Cell<bool>,
|
||||
|
||||
// Child Pages.
|
||||
pub children: DOMRefCell<Vec<Rc<Page>>>,
|
||||
}
|
||||
|
||||
pub struct PageIterator {
|
||||
stack: Vec<Rc<Page>>,
|
||||
}
|
||||
|
||||
pub trait IterablePage {
|
||||
fn iter(&self) -> PageIterator;
|
||||
fn find(&self, id: PipelineId) -> Option<Rc<Page>>;
|
||||
}
|
||||
|
||||
impl IterablePage for Rc<Page> {
|
||||
fn iter(&self) -> PageIterator {
|
||||
PageIterator {
|
||||
stack: vec!(self.clone()),
|
||||
}
|
||||
}
|
||||
fn find(&self, id: PipelineId) -> Option<Rc<Page>> {
|
||||
if self.id == id {
|
||||
return Some(self.clone());
|
||||
}
|
||||
|
||||
self.children.borrow()
|
||||
.iter()
|
||||
.filter_map(|p| p.find(id))
|
||||
.next()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Page {
|
||||
pub fn new(id: PipelineId) -> Page {
|
||||
Page {
|
||||
id: id,
|
||||
frame: DOMRefCell::new(None),
|
||||
needs_reflow: Cell::new(true),
|
||||
children: DOMRefCell::new(vec!()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pipeline(&self) -> PipelineId {
|
||||
self.id
|
||||
}
|
||||
|
||||
pub fn window(&self) -> Root<Window> {
|
||||
Root::from_ref(&*self.frame.borrow().as_ref().unwrap().window)
|
||||
}
|
||||
|
||||
pub fn document(&self) -> Root<Document> {
|
||||
Root::from_ref(&*self.frame.borrow().as_ref().unwrap().document)
|
||||
}
|
||||
|
||||
// must handle root case separately
|
||||
pub fn remove(&self, id: PipelineId) -> Option<Rc<Page>> {
|
||||
let remove_idx = {
|
||||
self.children
|
||||
.borrow()
|
||||
.iter()
|
||||
.position(|page_tree| page_tree.id == id)
|
||||
};
|
||||
match remove_idx {
|
||||
Some(idx) => Some(self.children.borrow_mut().remove(idx)),
|
||||
None => {
|
||||
self.children
|
||||
.borrow_mut()
|
||||
.iter_mut()
|
||||
.filter_map(|page_tree| page_tree.remove(id))
|
||||
.next()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for PageIterator {
|
||||
type Item = Rc<Page>;
|
||||
|
||||
fn next(&mut self) -> Option<Rc<Page>> {
|
||||
let popped = self.stack.pop();
|
||||
if let Some(ref page) = popped {
|
||||
self.stack.extend(page.children.borrow().iter().cloned());
|
||||
}
|
||||
popped
|
||||
}
|
||||
}
|
||||
|
||||
impl Page {
|
||||
pub fn set_reflow_status(&self, status: bool) -> bool {
|
||||
let old = self.needs_reflow.get();
|
||||
self.needs_reflow.set(status);
|
||||
old
|
||||
}
|
||||
|
||||
#[allow(unrooted_must_root)]
|
||||
pub fn set_frame(&self, frame: Option<Frame>) {
|
||||
*self.frame.borrow_mut() = frame;
|
||||
}
|
||||
}
|
||||
|
||||
/// Information for one frame in the browsing context.
|
||||
#[derive(JSTraceable, HeapSizeOf)]
|
||||
#[must_root]
|
||||
pub struct Frame {
|
||||
/// The document for this frame.
|
||||
pub document: JS<Document>,
|
||||
/// The window object for this frame.
|
||||
pub window: JS<Window>,
|
||||
}
|
|
@ -32,7 +32,7 @@ use dom::bindings::js::{RootCollectionPtr, RootedReference};
|
|||
use dom::bindings::refcounted::{LiveDOMReferences, Trusted};
|
||||
use dom::bindings::trace::JSTraceable;
|
||||
use dom::bindings::utils::WRAP_CALLBACKS;
|
||||
use dom::browsingcontext::BrowsingContext;
|
||||
use dom::browsingcontext::{BrowsingContext, IterableContext};
|
||||
use dom::document::{Document, DocumentProgressHandler, DocumentSource, FocusType, IsHTMLDocument};
|
||||
use dom::element::Element;
|
||||
use dom::event::{Event, EventBubbles, EventCancelable};
|
||||
|
@ -69,7 +69,6 @@ use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheResult, ImageCach
|
|||
use net_traits::storage_thread::StorageThread;
|
||||
use net_traits::{AsyncResponseTarget, ControlMsg, LoadConsumer, LoadContext, Metadata, ResourceThread};
|
||||
use network_listener::NetworkListener;
|
||||
use page::{Frame, IterablePage, Page};
|
||||
use parse::ParserRoot;
|
||||
use parse::html::{ParseContext, parse_html};
|
||||
use parse::xml::{self, parse_xml};
|
||||
|
@ -304,7 +303,7 @@ impl OpaqueSender<CommonScriptMsg> for Sender<MainThreadScriptMsg> {
|
|||
#[allow(unrooted_must_root)]
|
||||
pub struct ScriptThread {
|
||||
/// A handle to the information pertaining to page layout
|
||||
page: DOMRefCell<Option<Rc<Page>>>,
|
||||
browsing_context: MutNullableHeap<JS<BrowsingContext>>,
|
||||
/// A list of data pertaining to loads that have not yet received a network response
|
||||
incomplete_loads: DOMRefCell<Vec<InProgressLoad>>,
|
||||
/// A handle to the image cache thread.
|
||||
|
@ -409,12 +408,10 @@ impl<'a> Drop for ScriptMemoryFailsafe<'a> {
|
|||
fn drop(&mut self) {
|
||||
match self.owner {
|
||||
Some(owner) => {
|
||||
unsafe {
|
||||
let page = owner.page.borrow_for_script_deallocation();
|
||||
for page in page.iter() {
|
||||
let window = page.window();
|
||||
window.clear_js_runtime_for_script_deallocation();
|
||||
}
|
||||
let context = owner.browsing_context.get();
|
||||
for context in context.iter() {
|
||||
let window = context.active_window();
|
||||
window.clear_js_runtime_for_script_deallocation();
|
||||
}
|
||||
}
|
||||
None => (),
|
||||
|
@ -550,7 +547,7 @@ impl ScriptThread {
|
|||
let control_port = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(state.control_port);
|
||||
|
||||
ScriptThread {
|
||||
page: DOMRefCell::new(None),
|
||||
browsing_context: MutNullableHeap::new(None),
|
||||
incomplete_loads: DOMRefCell::new(vec!()),
|
||||
|
||||
image_cache_thread: state.image_cache_thread,
|
||||
|
@ -593,19 +590,19 @@ impl ScriptThread {
|
|||
}
|
||||
}
|
||||
|
||||
// Return the root page in the frame tree. Panics if it doesn't exist.
|
||||
pub fn root_page(&self) -> Rc<Page> {
|
||||
self.page.borrow().as_ref().unwrap().clone()
|
||||
// Return the root browsing context in the frame tree. Panics if it doesn't exist.
|
||||
pub fn root_browsing_context(&self) -> Root<BrowsingContext> {
|
||||
self.browsing_context.get().unwrap()
|
||||
}
|
||||
|
||||
fn root_page_exists(&self) -> bool {
|
||||
self.page.borrow().is_some()
|
||||
fn root_browsing_context_exists(&self) -> bool {
|
||||
self.browsing_context.get().is_some()
|
||||
}
|
||||
|
||||
/// Find a child page of the root page by pipeline id. Returns `None` if the root page does
|
||||
/// not exist or the subpage cannot be found.
|
||||
fn find_subpage(&self, pipeline_id: PipelineId) -> Option<Rc<Page>> {
|
||||
self.page.borrow().as_ref().and_then(|page| page.find(pipeline_id))
|
||||
/// Find a child browsing context of the root context by pipeline id. Returns `None` if the
|
||||
/// root context does not exist or the child context cannot be found.
|
||||
fn find_child_context(&self, pipeline_id: PipelineId) -> Option<Root<BrowsingContext>> {
|
||||
self.browsing_context.get().and_then(|context| context.find(pipeline_id))
|
||||
}
|
||||
|
||||
pub fn get_cx(&self) -> *mut JSContext {
|
||||
|
@ -629,11 +626,11 @@ impl ScriptThread {
|
|||
let mut resizes = vec!();
|
||||
|
||||
{
|
||||
let page = self.page.borrow();
|
||||
if let Some(page) = page.as_ref() {
|
||||
for page in page.iter() {
|
||||
let context = self.browsing_context.get();
|
||||
if let Some(context) = context {
|
||||
for context in context.iter() {
|
||||
// Only process a resize if layout is idle.
|
||||
let window = page.window();
|
||||
let window = context.active_window();
|
||||
let resize_event = window.steal_resize_event();
|
||||
match resize_event {
|
||||
Some(size) => resizes.push((window.pipeline(), size)),
|
||||
|
@ -782,10 +779,10 @@ impl ScriptThread {
|
|||
// Issue batched reflows on any pages that require it (e.g. if images loaded)
|
||||
// TODO(gw): In the future we could probably batch other types of reflows
|
||||
// into this loop too, but for now it's only images.
|
||||
let page = self.page.borrow();
|
||||
if let Some(page) = page.as_ref() {
|
||||
for page in page.iter() {
|
||||
let window = page.window();
|
||||
let context = self.browsing_context.get();
|
||||
if let Some(context) = context {
|
||||
for context in context.iter() {
|
||||
let window = context.active_window();
|
||||
let pending_reflows = window.get_pending_reflow_count();
|
||||
if pending_reflows > 0 {
|
||||
window.reflow(ReflowGoal::ForDisplay,
|
||||
|
@ -946,45 +943,45 @@ impl ScriptThread {
|
|||
TimerSource::FromWorker => panic!("Worker timeouts must not be sent to script thread"),
|
||||
};
|
||||
|
||||
let page = self.root_page();
|
||||
let page = page.find(pipeline_id).expect("ScriptThread: received fire timer msg for a
|
||||
let context = self.root_browsing_context();
|
||||
let context = context.find(pipeline_id).expect("ScriptThread: received fire timer msg for a
|
||||
pipeline ID not associated with this script thread. This is a bug.");
|
||||
let window = page.window();
|
||||
let window = context.active_window();
|
||||
|
||||
window.handle_fire_timer(id);
|
||||
}
|
||||
|
||||
fn handle_msg_from_devtools(&self, msg: DevtoolScriptControlMsg) {
|
||||
let page = self.root_page();
|
||||
let context = self.root_browsing_context();
|
||||
match msg {
|
||||
DevtoolScriptControlMsg::EvaluateJS(id, s, reply) => {
|
||||
let window = get_page(&page, id).window();
|
||||
let window = get_browsing_context(&context, id).active_window();
|
||||
let global_ref = GlobalRef::Window(window.r());
|
||||
devtools::handle_evaluate_js(&global_ref, s, reply)
|
||||
},
|
||||
DevtoolScriptControlMsg::GetRootNode(id, reply) =>
|
||||
devtools::handle_get_root_node(&page, id, reply),
|
||||
devtools::handle_get_root_node(&context, id, reply),
|
||||
DevtoolScriptControlMsg::GetDocumentElement(id, reply) =>
|
||||
devtools::handle_get_document_element(&page, id, reply),
|
||||
devtools::handle_get_document_element(&context, id, reply),
|
||||
DevtoolScriptControlMsg::GetChildren(id, node_id, reply) =>
|
||||
devtools::handle_get_children(&page, id, node_id, reply),
|
||||
devtools::handle_get_children(&context, id, node_id, reply),
|
||||
DevtoolScriptControlMsg::GetLayout(id, node_id, reply) =>
|
||||
devtools::handle_get_layout(&page, id, node_id, reply),
|
||||
devtools::handle_get_layout(&context, id, node_id, reply),
|
||||
DevtoolScriptControlMsg::GetCachedMessages(pipeline_id, message_types, reply) =>
|
||||
devtools::handle_get_cached_messages(pipeline_id, message_types, reply),
|
||||
DevtoolScriptControlMsg::ModifyAttribute(id, node_id, modifications) =>
|
||||
devtools::handle_modify_attribute(&page, id, node_id, modifications),
|
||||
devtools::handle_modify_attribute(&context, id, node_id, modifications),
|
||||
DevtoolScriptControlMsg::WantsLiveNotifications(id, to_send) => {
|
||||
let window = get_page(&page, id).window();
|
||||
let window = get_browsing_context(&context, id).active_window();
|
||||
let global_ref = GlobalRef::Window(window.r());
|
||||
devtools::handle_wants_live_notifications(&global_ref, to_send)
|
||||
},
|
||||
DevtoolScriptControlMsg::SetTimelineMarkers(_pipeline_id, marker_types, reply) =>
|
||||
devtools::handle_set_timeline_markers(&page, marker_types, reply),
|
||||
devtools::handle_set_timeline_markers(&context, marker_types, reply),
|
||||
DevtoolScriptControlMsg::DropTimelineMarkers(_pipeline_id, marker_types) =>
|
||||
devtools::handle_drop_timeline_markers(&page, marker_types),
|
||||
devtools::handle_drop_timeline_markers(&context, marker_types),
|
||||
DevtoolScriptControlMsg::RequestAnimationFrame(pipeline_id, name) =>
|
||||
devtools::handle_request_animation_frame(&page, pipeline_id, name),
|
||||
devtools::handle_request_animation_frame(&context, pipeline_id, name),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -993,48 +990,48 @@ impl ScriptThread {
|
|||
}
|
||||
|
||||
fn handle_webdriver_msg(&self, pipeline_id: PipelineId, msg: WebDriverScriptCommand) {
|
||||
let page = self.root_page();
|
||||
let context = self.root_browsing_context();
|
||||
match msg {
|
||||
WebDriverScriptCommand::ExecuteScript(script, reply) =>
|
||||
webdriver_handlers::handle_execute_script(&page, pipeline_id, script, reply),
|
||||
webdriver_handlers::handle_execute_script(&context, pipeline_id, script, reply),
|
||||
WebDriverScriptCommand::FindElementCSS(selector, reply) =>
|
||||
webdriver_handlers::handle_find_element_css(&page, pipeline_id, selector, reply),
|
||||
webdriver_handlers::handle_find_element_css(&context, pipeline_id, selector, reply),
|
||||
WebDriverScriptCommand::FindElementsCSS(selector, reply) =>
|
||||
webdriver_handlers::handle_find_elements_css(&page, pipeline_id, selector, reply),
|
||||
webdriver_handlers::handle_find_elements_css(&context, pipeline_id, selector, reply),
|
||||
WebDriverScriptCommand::FocusElement(element_id, reply) =>
|
||||
webdriver_handlers::handle_focus_element(&page, pipeline_id, element_id, reply),
|
||||
webdriver_handlers::handle_focus_element(&context, pipeline_id, element_id, reply),
|
||||
WebDriverScriptCommand::GetActiveElement(reply) =>
|
||||
webdriver_handlers::handle_get_active_element(&page, pipeline_id, reply),
|
||||
webdriver_handlers::handle_get_active_element(&context, pipeline_id, reply),
|
||||
WebDriverScriptCommand::GetElementTagName(node_id, reply) =>
|
||||
webdriver_handlers::handle_get_name(&page, pipeline_id, node_id, reply),
|
||||
webdriver_handlers::handle_get_name(&context, pipeline_id, node_id, reply),
|
||||
WebDriverScriptCommand::GetElementAttribute(node_id, name, reply) =>
|
||||
webdriver_handlers::handle_get_attribute(&page, pipeline_id, node_id, name, reply),
|
||||
webdriver_handlers::handle_get_attribute(&context, pipeline_id, node_id, name, reply),
|
||||
WebDriverScriptCommand::GetElementCSS(node_id, name, reply) =>
|
||||
webdriver_handlers::handle_get_css(&page, pipeline_id, node_id, name, reply),
|
||||
webdriver_handlers::handle_get_css(&context, pipeline_id, node_id, name, reply),
|
||||
WebDriverScriptCommand::GetElementRect(node_id, reply) =>
|
||||
webdriver_handlers::handle_get_rect(&page, pipeline_id, node_id, reply),
|
||||
webdriver_handlers::handle_get_rect(&context, pipeline_id, node_id, reply),
|
||||
WebDriverScriptCommand::GetElementText(node_id, reply) =>
|
||||
webdriver_handlers::handle_get_text(&page, pipeline_id, node_id, reply),
|
||||
webdriver_handlers::handle_get_text(&context, pipeline_id, node_id, reply),
|
||||
WebDriverScriptCommand::GetFrameId(frame_id, reply) =>
|
||||
webdriver_handlers::handle_get_frame_id(&page, pipeline_id, frame_id, reply),
|
||||
webdriver_handlers::handle_get_frame_id(&context, pipeline_id, frame_id, reply),
|
||||
WebDriverScriptCommand::GetUrl(reply) =>
|
||||
webdriver_handlers::handle_get_url(&page, pipeline_id, reply),
|
||||
webdriver_handlers::handle_get_url(&context, pipeline_id, reply),
|
||||
WebDriverScriptCommand::GetWindowSize(reply) =>
|
||||
webdriver_handlers::handle_get_window_size(&page, pipeline_id, reply),
|
||||
webdriver_handlers::handle_get_window_size(&context, pipeline_id, reply),
|
||||
WebDriverScriptCommand::IsEnabled(element_id, reply) =>
|
||||
webdriver_handlers::handle_is_enabled(&page, pipeline_id, element_id, reply),
|
||||
webdriver_handlers::handle_is_enabled(&context, pipeline_id, element_id, reply),
|
||||
WebDriverScriptCommand::IsSelected(element_id, reply) =>
|
||||
webdriver_handlers::handle_is_selected(&page, pipeline_id, element_id, reply),
|
||||
webdriver_handlers::handle_is_selected(&context, pipeline_id, element_id, reply),
|
||||
WebDriverScriptCommand::GetTitle(reply) =>
|
||||
webdriver_handlers::handle_get_title(&page, pipeline_id, reply),
|
||||
webdriver_handlers::handle_get_title(&context, pipeline_id, reply),
|
||||
WebDriverScriptCommand::ExecuteAsyncScript(script, reply) =>
|
||||
webdriver_handlers::handle_execute_async_script(&page, pipeline_id, script, reply),
|
||||
webdriver_handlers::handle_execute_async_script(&context, pipeline_id, script, reply),
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_resize(&self, id: PipelineId, size: WindowSizeData, size_type: WindowSizeType) {
|
||||
if let Some(ref page) = self.find_subpage(id) {
|
||||
let window = page.window();
|
||||
if let Some(ref context) = self.find_child_context(id) {
|
||||
let window = context.active_window();
|
||||
window.set_resize_event(size, size_type);
|
||||
return;
|
||||
}
|
||||
|
@ -1047,13 +1044,13 @@ impl ScriptThread {
|
|||
}
|
||||
|
||||
fn handle_viewport(&self, id: PipelineId, rect: Rect<f32>) {
|
||||
let page = self.page.borrow();
|
||||
if let Some(page) = page.as_ref() {
|
||||
if let Some(ref inner_page) = page.find(id) {
|
||||
let window = inner_page.window();
|
||||
let context = self.browsing_context.get();
|
||||
if let Some(context) = context {
|
||||
if let Some(inner_context) = context.find(id) {
|
||||
let window = inner_context.active_window();
|
||||
if window.set_page_clip_rect_with_new_viewport(rect) {
|
||||
let page = get_page(page, id);
|
||||
self.rebuild_and_force_reflow(&*page, ReflowReason::Viewport);
|
||||
let context = get_browsing_context(&context, id);
|
||||
self.rebuild_and_force_reflow(&context, ReflowReason::Viewport);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -1099,11 +1096,11 @@ impl ScriptThread {
|
|||
content_process_shutdown_chan: content_process_shutdown_chan,
|
||||
};
|
||||
|
||||
let page = self.root_page();
|
||||
let parent_page = page.find(containing_pipeline_id).expect("ScriptThread: received a layout
|
||||
let context = self.root_browsing_context();
|
||||
let parent_context = context.find(containing_pipeline_id).expect("ScriptThread: received a layout
|
||||
whose parent has a PipelineId which does not correspond to a pipeline in the script
|
||||
thread's page tree. This is a bug.");
|
||||
let parent_window = parent_page.window();
|
||||
thread's browsing context tree. This is a bug.");
|
||||
let parent_window = parent_context.active_window();
|
||||
|
||||
// Tell layout to actually spawn the thread.
|
||||
parent_window.layout_chan()
|
||||
|
@ -1119,8 +1116,8 @@ impl ScriptThread {
|
|||
}
|
||||
|
||||
fn handle_loads_complete(&self, pipeline: PipelineId) {
|
||||
let page = get_page(&self.root_page(), pipeline);
|
||||
let doc = page.document();
|
||||
let context = get_browsing_context(&self.root_browsing_context(), pipeline);
|
||||
let doc = context.active_document();
|
||||
let doc = doc.r();
|
||||
if doc.loader().is_blocked() {
|
||||
return;
|
||||
|
@ -1141,14 +1138,14 @@ impl ScriptThread {
|
|||
let mut dom_tree_size = 0;
|
||||
let mut reports = vec![];
|
||||
|
||||
if let Some(root_page) = self.page.borrow().as_ref() {
|
||||
for it_page in root_page.iter() {
|
||||
let current_url = it_page.document().url().to_string();
|
||||
if let Some(root_context) = self.browsing_context.get() {
|
||||
for it_context in root_context.iter() {
|
||||
let current_url = it_context.active_document().url().to_string();
|
||||
|
||||
for child in it_page.document().upcast::<Node>().traverse_preorder() {
|
||||
for child in it_context.active_document().upcast::<Node>().traverse_preorder() {
|
||||
dom_tree_size += heap_size_of_self_and_children(&*child);
|
||||
}
|
||||
let window = it_page.window();
|
||||
let window = it_context.active_window();
|
||||
dom_tree_size += heap_size_of_self_and_children(&*window);
|
||||
|
||||
reports.push(Report {
|
||||
|
@ -1166,9 +1163,9 @@ impl ScriptThread {
|
|||
|
||||
/// Handles freeze message
|
||||
fn handle_freeze_msg(&self, id: PipelineId) {
|
||||
if let Some(root_page) = self.page.borrow().as_ref() {
|
||||
if let Some(ref inner_page) = root_page.find(id) {
|
||||
let window = inner_page.window();
|
||||
if let Some(root_context) = self.browsing_context.get() {
|
||||
if let Some(ref inner_context) = root_context.find(id) {
|
||||
let window = inner_context.active_window();
|
||||
window.freeze();
|
||||
return;
|
||||
}
|
||||
|
@ -1183,12 +1180,12 @@ impl ScriptThread {
|
|||
|
||||
/// Handles thaw message
|
||||
fn handle_thaw_msg(&self, id: PipelineId) {
|
||||
if let Some(ref inner_page) = self.root_page().find(id) {
|
||||
let needed_reflow = inner_page.set_reflow_status(false);
|
||||
if let Some(inner_context) = self.root_browsing_context().find(id) {
|
||||
let needed_reflow = inner_context.set_reflow_status(false);
|
||||
if needed_reflow {
|
||||
self.rebuild_and_force_reflow(&*inner_page, ReflowReason::CachedPageNeededReflow);
|
||||
self.rebuild_and_force_reflow(&inner_context, ReflowReason::CachedPageNeededReflow);
|
||||
}
|
||||
let window = inner_page.window();
|
||||
let window = inner_context.active_window();
|
||||
window.thaw();
|
||||
return;
|
||||
}
|
||||
|
@ -1203,10 +1200,10 @@ impl ScriptThread {
|
|||
fn handle_focus_iframe_msg(&self,
|
||||
parent_pipeline_id: PipelineId,
|
||||
subpage_id: SubpageId) {
|
||||
let borrowed_page = self.root_page();
|
||||
let page = borrowed_page.find(parent_pipeline_id).unwrap();
|
||||
let borrowed_context = self.root_browsing_context();
|
||||
let context = borrowed_context.find(parent_pipeline_id).unwrap();
|
||||
|
||||
let doc = page.document();
|
||||
let doc = context.active_document();
|
||||
let frame_element = doc.find_iframe(subpage_id);
|
||||
|
||||
if let Some(ref frame_element) = frame_element {
|
||||
|
@ -1219,13 +1216,13 @@ impl ScriptThread {
|
|||
fn handle_framed_content_changed(&self,
|
||||
parent_pipeline_id: PipelineId,
|
||||
subpage_id: SubpageId) {
|
||||
let borrowed_page = self.root_page();
|
||||
let page = borrowed_page.find(parent_pipeline_id).unwrap();
|
||||
let doc = page.document();
|
||||
let root_context = self.root_browsing_context();
|
||||
let context = root_context.find(parent_pipeline_id).unwrap();
|
||||
let doc = context.active_document();
|
||||
let frame_element = doc.find_iframe(subpage_id);
|
||||
if let Some(ref frame_element) = frame_element {
|
||||
frame_element.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
|
||||
let window = page.window();
|
||||
let window = context.active_window();
|
||||
window.reflow(ReflowGoal::ForDisplay,
|
||||
ReflowQueryType::NoQuery,
|
||||
ReflowReason::FramedContentChanged);
|
||||
|
@ -1238,10 +1235,10 @@ impl ScriptThread {
|
|||
parent_pipeline_id: PipelineId,
|
||||
subpage_id: SubpageId,
|
||||
event: MozBrowserEvent) {
|
||||
let borrowed_page = self.root_page();
|
||||
let borrowed_context = self.root_browsing_context();
|
||||
|
||||
let frame_element = borrowed_page.find(parent_pipeline_id).and_then(|page| {
|
||||
let doc = page.document();
|
||||
let frame_element = borrowed_context.find(parent_pipeline_id).and_then(|context| {
|
||||
let doc = context.active_document();
|
||||
doc.find_iframe(subpage_id)
|
||||
});
|
||||
|
||||
|
@ -1255,10 +1252,10 @@ impl ScriptThread {
|
|||
old_subpage_id: SubpageId,
|
||||
new_subpage_id: SubpageId,
|
||||
new_pipeline_id: PipelineId) {
|
||||
let borrowed_page = self.root_page();
|
||||
let borrowed_context = self.root_browsing_context();
|
||||
|
||||
let frame_element = borrowed_page.find(containing_pipeline_id).and_then(|page| {
|
||||
let doc = page.document();
|
||||
let frame_element = borrowed_context.find(containing_pipeline_id).and_then(|context| {
|
||||
let doc = context.active_document();
|
||||
doc.find_iframe(old_subpage_id)
|
||||
});
|
||||
|
||||
|
@ -1267,12 +1264,12 @@ impl ScriptThread {
|
|||
|
||||
/// Window was resized, but this script was not active, so don't reflow yet
|
||||
fn handle_resize_inactive_msg(&self, id: PipelineId, new_size: WindowSizeData) {
|
||||
let page = self.root_page();
|
||||
let page = page.find(id).expect("Received resize message for PipelineId not associated
|
||||
with a page in the page tree. This is a bug.");
|
||||
let window = page.window();
|
||||
let context = self.root_browsing_context();
|
||||
let context = context.find(id).expect("Received resize message for PipelineId not associated
|
||||
with a browsing context in the browsing context tree. This is a bug.");
|
||||
let window = context.active_window();
|
||||
window.set_window_size(new_size);
|
||||
page.set_reflow_status(true);
|
||||
context.set_reflow_status(true);
|
||||
}
|
||||
|
||||
/// We have gotten a window.close from script, which we pass on to the compositor.
|
||||
|
@ -1312,8 +1309,8 @@ impl ScriptThread {
|
|||
|
||||
/// Handles a request for the window title.
|
||||
fn handle_get_title_msg(&self, pipeline_id: PipelineId) {
|
||||
let page = get_page(&self.root_page(), pipeline_id);
|
||||
let document = page.document();
|
||||
let context = get_browsing_context(&self.root_browsing_context(), pipeline_id);
|
||||
let document = context.active_document();
|
||||
document.send_title_to_compositor();
|
||||
}
|
||||
|
||||
|
@ -1341,47 +1338,46 @@ impl ScriptThread {
|
|||
}
|
||||
|
||||
let has_pending_loads = self.incomplete_loads.borrow().len() > 0;
|
||||
let has_root_page = self.page.borrow().is_some();
|
||||
let has_root_context = self.root_browsing_context_exists();
|
||||
|
||||
// Exit if no pending loads and no root page
|
||||
return !has_pending_loads && !has_root_page;
|
||||
// Exit if no pending loads and no root context
|
||||
return !has_pending_loads && !has_root_context;
|
||||
}
|
||||
|
||||
// If root is being exited, shut down all pages
|
||||
let page = self.root_page();
|
||||
let window = page.window();
|
||||
// If root is being exited, shut down all contexts
|
||||
let context = self.root_browsing_context();
|
||||
let window = context.active_window();
|
||||
if window.pipeline() == id {
|
||||
debug!("shutting down layout for root page {:?}", id);
|
||||
shut_down_layout(&page);
|
||||
debug!("shutting down layout for root context {:?}", id);
|
||||
shut_down_layout(&context);
|
||||
return true
|
||||
}
|
||||
|
||||
// otherwise find just the matching page and exit all sub-pages
|
||||
if let Some(ref mut child_page) = page.remove(id) {
|
||||
debug!("shutting down layout for child context {:?}", id);
|
||||
shut_down_layout(&*child_page);
|
||||
// otherwise find just the matching context and exit all sub-contexts
|
||||
if let Some(ref mut child_context) = context.remove(id) {
|
||||
shut_down_layout(&child_context);
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
/// Handles when layout thread finishes all animation in one tick
|
||||
fn handle_tick_all_animations(&self, id: PipelineId) {
|
||||
let page = get_page(&self.root_page(), id);
|
||||
let document = page.document();
|
||||
let context = get_browsing_context(&self.root_browsing_context(), id);
|
||||
let document = context.active_document();
|
||||
document.run_the_animation_frame_callbacks();
|
||||
}
|
||||
|
||||
/// Handles a Web font being loaded. Does nothing if the page no longer exists.
|
||||
fn handle_web_font_loaded(&self, pipeline_id: PipelineId) {
|
||||
if let Some(ref page) = self.find_subpage(pipeline_id) {
|
||||
self.rebuild_and_force_reflow(page, ReflowReason::WebFontLoaded);
|
||||
if let Some(context) = self.find_child_context(pipeline_id) {
|
||||
self.rebuild_and_force_reflow(&context, ReflowReason::WebFontLoaded);
|
||||
}
|
||||
}
|
||||
|
||||
/// Notify the containing document of a child frame that has completed loading.
|
||||
fn handle_frame_load_event(&self, containing_pipeline: PipelineId, id: PipelineId) {
|
||||
let page = get_page(&self.root_page(), containing_pipeline);
|
||||
let document = page.document();
|
||||
let context = get_browsing_context(&self.root_browsing_context(), containing_pipeline);
|
||||
let document = context.active_document();
|
||||
if let Some(iframe) = document.find_iframe_by_pipeline(id) {
|
||||
iframe.iframe_load_event_steps(id);
|
||||
}
|
||||
|
@ -1400,84 +1396,28 @@ impl ScriptThread {
|
|||
let ConstellationChan(ref chan) = self.constellation_chan;
|
||||
chan.send(ConstellationMsg::SetFinalUrl(incomplete.pipeline_id, final_url.clone())).unwrap();
|
||||
}
|
||||
debug!("ScriptThread: loading {} on page {:?}", incomplete.url, incomplete.pipeline_id);
|
||||
debug!("ScriptThread: loading {} on context {:?}", incomplete.url, incomplete.pipeline_id);
|
||||
|
||||
let frame_element = incomplete.parent_info.and_then(|(parent_id, subpage_id)| {
|
||||
// The root page may not exist yet, if the parent of this frame
|
||||
// The root context may not exist yet, if the parent of this frame
|
||||
// exists in a different script thread.
|
||||
let borrowed_page = self.page.borrow();
|
||||
let root_context = self.browsing_context.get();
|
||||
|
||||
// In the case a parent id exists but the matching page
|
||||
// cannot be found, this means the page exists in a different
|
||||
// In the case a parent id exists but the matching context
|
||||
// cannot be found, this means the context exists in a different
|
||||
// script thread (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.as_ref().and_then(|borrowed_page| {
|
||||
borrowed_page.find(parent_id).and_then(|page| {
|
||||
let doc = page.document();
|
||||
root_context.and_then(|root_context| {
|
||||
root_context.find(parent_id).and_then(|context| {
|
||||
let doc = context.active_document();
|
||||
doc.find_iframe(subpage_id)
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
// Create a new frame tree entry.
|
||||
let page = Rc::new(Page::new(incomplete.pipeline_id));
|
||||
if !self.root_page_exists() {
|
||||
// We have a new root frame tree.
|
||||
*self.page.borrow_mut() = Some(page.clone());
|
||||
} else if let Some((parent, _)) = incomplete.parent_info {
|
||||
// We have a new child frame.
|
||||
let parent_page = self.root_page();
|
||||
// TODO(gw): This find will fail when we are sharing script threads
|
||||
// between cross origin iframes in the same TLD.
|
||||
let parent_page = parent_page.find(parent)
|
||||
.expect("received load for subpage with missing parent");
|
||||
parent_page.children.borrow_mut().push(page.clone());
|
||||
}
|
||||
|
||||
enum PageToRemove {
|
||||
Root,
|
||||
Child(PipelineId),
|
||||
}
|
||||
struct AutoPageRemover<'a> {
|
||||
page: PageToRemove,
|
||||
script_thread: &'a ScriptThread,
|
||||
neutered: bool,
|
||||
}
|
||||
impl<'a> AutoPageRemover<'a> {
|
||||
fn new(script_thread: &'a ScriptThread, page: PageToRemove) -> AutoPageRemover<'a> {
|
||||
AutoPageRemover {
|
||||
page: page,
|
||||
script_thread: script_thread,
|
||||
neutered: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn neuter(&mut self) {
|
||||
self.neutered = true;
|
||||
}
|
||||
}
|
||||
impl<'a> Drop for AutoPageRemover<'a> {
|
||||
fn drop(&mut self) {
|
||||
if !self.neutered {
|
||||
match self.page {
|
||||
PageToRemove::Root => *self.script_thread.page.borrow_mut() = None,
|
||||
PageToRemove::Child(id) => {
|
||||
self.script_thread.root_page().remove(id).unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let page_to_remove = if !self.root_page_exists() {
|
||||
PageToRemove::Root
|
||||
} else {
|
||||
PageToRemove::Child(incomplete.pipeline_id)
|
||||
};
|
||||
let mut page_remover = AutoPageRemover::new(self, page_to_remove);
|
||||
let MainThreadScriptChan(ref sender) = self.chan;
|
||||
let DOMManipulationTaskSource(ref dom_sender) = self.dom_manipulation_task_source;
|
||||
let UserInteractionTaskSource(ref user_sender) = self.user_interaction_task_source;
|
||||
|
@ -1491,7 +1431,6 @@ impl ScriptThread {
|
|||
|
||||
// Create the window and document objects.
|
||||
let window = Window::new(self.js_runtime.clone(),
|
||||
page.clone(),
|
||||
MainThreadScriptChan(sender.clone()),
|
||||
DOMManipulationTaskSource(dom_sender.clone()),
|
||||
UserInteractionTaskSource(user_sender.clone()),
|
||||
|
@ -1514,10 +1453,74 @@ impl ScriptThread {
|
|||
incomplete.pipeline_id,
|
||||
incomplete.parent_info,
|
||||
incomplete.window_size);
|
||||
|
||||
let frame_element = frame_element.r().map(Castable::upcast);
|
||||
let browsing_context = BrowsingContext::new(&window, frame_element);
|
||||
|
||||
enum ContextToRemove {
|
||||
Root,
|
||||
Child(PipelineId),
|
||||
None,
|
||||
}
|
||||
struct AutoContextRemover<'a> {
|
||||
context: ContextToRemove,
|
||||
script_thread: &'a ScriptThread,
|
||||
neutered: bool,
|
||||
}
|
||||
impl<'a> AutoContextRemover<'a> {
|
||||
fn new(script_thread: &'a ScriptThread, context: ContextToRemove) -> AutoContextRemover<'a> {
|
||||
AutoContextRemover {
|
||||
context: context,
|
||||
script_thread: script_thread,
|
||||
neutered: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn neuter(&mut self) {
|
||||
self.neutered = true;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for AutoContextRemover<'a> {
|
||||
fn drop(&mut self) {
|
||||
if !self.neutered {
|
||||
match self.context {
|
||||
ContextToRemove::Root => {
|
||||
self.script_thread.browsing_context.set(None)
|
||||
},
|
||||
ContextToRemove::Child(id) => {
|
||||
self.script_thread.root_browsing_context().remove(id).unwrap();
|
||||
},
|
||||
ContextToRemove::None => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut using_new_context = true;
|
||||
|
||||
let (browsing_context, context_to_remove) = if !self.root_browsing_context_exists() {
|
||||
// Create a new context tree entry. This will become the root context.
|
||||
let new_context = BrowsingContext::new(&window, frame_element, incomplete.pipeline_id);
|
||||
// We have a new root frame tree.
|
||||
self.browsing_context.set(Some(&new_context));
|
||||
(new_context, ContextToRemove::Root)
|
||||
} else if let Some((parent, _)) = incomplete.parent_info {
|
||||
// Create a new context tree entry. This will be a child context.
|
||||
let new_context = BrowsingContext::new(&window, frame_element, incomplete.pipeline_id);
|
||||
|
||||
let root_context = self.root_browsing_context();
|
||||
// TODO(gw): This find will fail when we are sharing script threads
|
||||
// between cross origin iframes in the same TLD.
|
||||
let parent_context = root_context.find(parent)
|
||||
.expect("received load for child context with missing parent");
|
||||
parent_context.push_child_context(&*new_context);
|
||||
(new_context, ContextToRemove::Child(incomplete.pipeline_id))
|
||||
} else {
|
||||
using_new_context = false;
|
||||
(self.root_browsing_context(), ContextToRemove::None)
|
||||
};
|
||||
|
||||
window.init_browsing_context(&browsing_context);
|
||||
let mut context_remover = AutoContextRemover::new(self, context_to_remove);
|
||||
|
||||
let last_modified = metadata.headers.as_ref().and_then(|headers| {
|
||||
headers.get().map(|&LastModified(HttpDate(ref tm))| dom_last_modified(tm))
|
||||
|
@ -1534,8 +1537,8 @@ impl ScriptThread {
|
|||
});
|
||||
|
||||
let loader = DocumentLoader::new_with_thread(self.resource_thread.clone(),
|
||||
Some(page.pipeline()),
|
||||
Some(incomplete.url.clone()));
|
||||
Some(browsing_context.pipeline()),
|
||||
Some(incomplete.url.clone()));
|
||||
|
||||
let is_html_document = match metadata.content_type {
|
||||
Some(ContentType(Mime(TopLevel::Application, SubLevel::Xml, _))) |
|
||||
|
@ -1552,20 +1555,18 @@ impl ScriptThread {
|
|||
last_modified,
|
||||
DocumentSource::FromParser,
|
||||
loader);
|
||||
browsing_context.init(&document);
|
||||
if using_new_context {
|
||||
browsing_context.init(&document);
|
||||
} else {
|
||||
browsing_context.push_history(&document);
|
||||
}
|
||||
document.set_ready_state(DocumentReadyState::Loading);
|
||||
|
||||
// Create the root frame
|
||||
page.set_frame(Some(Frame {
|
||||
document: JS::from_rooted(&document),
|
||||
window: JS::from_rooted(&window),
|
||||
}));
|
||||
|
||||
let ConstellationChan(ref chan) = self.constellation_chan;
|
||||
chan.send(ConstellationMsg::ActivateDocument(incomplete.pipeline_id)).unwrap();
|
||||
|
||||
// Notify devtools that a new script global exists.
|
||||
self.notify_devtools(document.Title(), final_url.clone(), (page.pipeline(), None));
|
||||
self.notify_devtools(document.Title(), final_url.clone(), (browsing_context.pipeline(), None));
|
||||
|
||||
let is_javascript = incomplete.url.scheme() == "javascript";
|
||||
let parse_input = if is_javascript {
|
||||
|
@ -1625,7 +1626,7 @@ impl ScriptThread {
|
|||
window.freeze();
|
||||
}
|
||||
|
||||
page_remover.neuter();
|
||||
context_remover.neuter();
|
||||
|
||||
document.get_current_parser().unwrap()
|
||||
}
|
||||
|
@ -1664,8 +1665,8 @@ impl ScriptThread {
|
|||
}
|
||||
|
||||
/// Reflows non-incrementally, rebuilding the entire layout tree in the process.
|
||||
fn rebuild_and_force_reflow(&self, page: &Page, reason: ReflowReason) {
|
||||
let document = page.document();
|
||||
fn rebuild_and_force_reflow(&self, context: &Root<BrowsingContext>, reason: ReflowReason) {
|
||||
let document = context.active_document();
|
||||
document.dirty_all_nodes();
|
||||
let window = window_from_node(document.r());
|
||||
window.reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, reason);
|
||||
|
@ -1676,8 +1677,8 @@ impl ScriptThread {
|
|||
/// TODO: Actually perform DOM event dispatch.
|
||||
fn handle_event(&self, pipeline_id: PipelineId, event: CompositorEvent) {
|
||||
|
||||
// DOM events can only be handled if there's a root page.
|
||||
if !self.root_page_exists() {
|
||||
// DOM events can only be handled if there's a root browsing context.
|
||||
if !self.root_browsing_context_exists() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1691,8 +1692,8 @@ impl ScriptThread {
|
|||
}
|
||||
|
||||
MouseMoveEvent(point) => {
|
||||
let page = get_page(&self.root_page(), pipeline_id);
|
||||
let document = page.document();
|
||||
let context = get_browsing_context(&self.root_browsing_context(), pipeline_id);
|
||||
let document = context.active_document();
|
||||
|
||||
// Get the previous target temporarily
|
||||
let prev_mouse_over_target = self.topmost_mouse_over_target.get();
|
||||
|
@ -1765,14 +1766,14 @@ impl ScriptThread {
|
|||
}
|
||||
|
||||
TouchpadPressureEvent(point, pressure, phase) => {
|
||||
let page = get_page(&self.root_page(), pipeline_id);
|
||||
let document = page.document();
|
||||
let context = get_browsing_context(&self.root_browsing_context(), pipeline_id);
|
||||
let document = context.active_document();
|
||||
document.r().handle_touchpad_pressure_event(self.js_runtime.rt(), point, pressure, phase);
|
||||
}
|
||||
|
||||
KeyEvent(key, state, modifiers) => {
|
||||
let page = get_page(&self.root_page(), pipeline_id);
|
||||
let document = page.document();
|
||||
let context = get_browsing_context(&self.root_browsing_context(), pipeline_id);
|
||||
let document = context.active_document();
|
||||
document.dispatch_key_event(
|
||||
key, state, modifiers, &mut self.compositor.borrow_mut());
|
||||
}
|
||||
|
@ -1784,8 +1785,8 @@ impl ScriptThread {
|
|||
mouse_event_type: MouseEventType,
|
||||
button: MouseButton,
|
||||
point: Point2D<f32>) {
|
||||
let page = get_page(&self.root_page(), pipeline_id);
|
||||
let document = page.document();
|
||||
let context = get_browsing_context(&self.root_browsing_context(), pipeline_id);
|
||||
let document = context.active_document();
|
||||
document.handle_mouse_event(self.js_runtime.rt(), button, point, mouse_event_type);
|
||||
}
|
||||
|
||||
|
@ -1795,8 +1796,8 @@ impl ScriptThread {
|
|||
identifier: TouchId,
|
||||
point: Point2D<f32>)
|
||||
-> bool {
|
||||
let page = get_page(&self.root_page(), pipeline_id);
|
||||
let document = page.document();
|
||||
let context = get_browsing_context(&self.root_browsing_context(), pipeline_id);
|
||||
let document = context.active_document();
|
||||
document.handle_touch_event(self.js_runtime.rt(), event_type, identifier, point)
|
||||
}
|
||||
|
||||
|
@ -1808,8 +1809,8 @@ impl ScriptThread {
|
|||
{
|
||||
let nurl = &load_data.url;
|
||||
if let Some(fragment) = nurl.fragment() {
|
||||
let page = get_page(&self.root_page(), pipeline_id);
|
||||
let document = page.document();
|
||||
let context = get_browsing_context(&self.root_browsing_context(), pipeline_id);
|
||||
let document = context.active_document();
|
||||
let document = document.r();
|
||||
let url = document.url();
|
||||
if &url[..Position::AfterQuery] == &nurl[..Position::AfterQuery] &&
|
||||
|
@ -1827,9 +1828,9 @@ impl ScriptThread {
|
|||
|
||||
match subpage_id {
|
||||
Some(subpage_id) => {
|
||||
let borrowed_page = self.root_page();
|
||||
let iframe = borrowed_page.find(pipeline_id).and_then(|page| {
|
||||
let doc = page.document();
|
||||
let root_context = self.root_browsing_context();
|
||||
let iframe = root_context.find(pipeline_id).and_then(|context| {
|
||||
let doc = context.active_document();
|
||||
doc.find_iframe(subpage_id)
|
||||
});
|
||||
if let Some(iframe) = iframe.r() {
|
||||
|
@ -1844,14 +1845,14 @@ impl ScriptThread {
|
|||
}
|
||||
|
||||
fn handle_resize_event(&self, pipeline_id: PipelineId, new_size: WindowSizeData, size_type: WindowSizeType) {
|
||||
let page = get_page(&self.root_page(), pipeline_id);
|
||||
let window = page.window();
|
||||
let context = get_browsing_context(&self.root_browsing_context(), pipeline_id);
|
||||
let window = context.active_window();
|
||||
window.set_window_size(new_size);
|
||||
window.force_reflow(ReflowGoal::ForDisplay,
|
||||
ReflowQueryType::NoQuery,
|
||||
ReflowReason::WindowResize);
|
||||
|
||||
let document = page.document();
|
||||
let document = context.active_document();
|
||||
let fragment_node = window.steal_fragment_name()
|
||||
.and_then(|name| document.find_fragment_node(&*name));
|
||||
match fragment_node {
|
||||
|
@ -1911,13 +1912,13 @@ impl ScriptThread {
|
|||
}
|
||||
|
||||
fn handle_parsing_complete(&self, id: PipelineId) {
|
||||
let parent_page = self.root_page();
|
||||
let page = match parent_page.find(id) {
|
||||
Some(page) => page,
|
||||
let parent_context = self.root_browsing_context();
|
||||
let context = match parent_context.find(id) {
|
||||
Some(context) => context,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let document = page.document();
|
||||
let document = context.active_document();
|
||||
let final_url = document.url();
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#the-end step 1
|
||||
|
@ -1933,7 +1934,7 @@ impl ScriptThread {
|
|||
window.reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, ReflowReason::FirstLoad);
|
||||
|
||||
// No more reflow required
|
||||
page.set_reflow_status(false);
|
||||
context.set_reflow_status(false);
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#the-end steps 3-4.
|
||||
document.process_deferred_scripts();
|
||||
|
@ -1943,13 +1944,13 @@ impl ScriptThread {
|
|||
|
||||
fn handle_css_error_reporting(&self, pipeline_id: PipelineId, filename: String,
|
||||
line: usize, column: usize, msg: String) {
|
||||
let parent_page = self.root_page();
|
||||
let page = match parent_page.find(pipeline_id) {
|
||||
Some(page) => page,
|
||||
let parent_context = self.root_browsing_context();
|
||||
let context = match parent_context.find(pipeline_id) {
|
||||
Some(context) => context,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let document = page.document();
|
||||
let document = context.active_document();
|
||||
let css_error = CSSError {
|
||||
filename: filename,
|
||||
line: line,
|
||||
|
@ -1958,7 +1959,7 @@ impl ScriptThread {
|
|||
};
|
||||
|
||||
document.report_css_error(css_error.clone());
|
||||
let window = page.window();
|
||||
let window = context.active_window();
|
||||
|
||||
if window.live_devtools_updates() {
|
||||
if let Some(ref chan) = self.devtools_chan {
|
||||
|
@ -1978,15 +1979,15 @@ impl Drop for ScriptThread {
|
|||
}
|
||||
}
|
||||
|
||||
/// Shuts down layout for the given page tree.
|
||||
fn shut_down_layout(page_tree: &Rc<Page>) {
|
||||
/// Shuts down layout for the given browsing context tree.
|
||||
fn shut_down_layout(context_tree: &Root<BrowsingContext>) {
|
||||
let mut channels = vec!();
|
||||
|
||||
for page in page_tree.iter() {
|
||||
for context in context_tree.iter() {
|
||||
// Tell the layout thread to begin shutting down, and wait until it
|
||||
// processed this message.
|
||||
let (response_chan, response_port) = channel();
|
||||
let window = page.window();
|
||||
let window = context.active_window();
|
||||
let LayoutChan(chan) = window.layout_chan().clone();
|
||||
if chan.send(layout_interface::Msg::PrepareToExit(response_chan)).is_ok() {
|
||||
channels.push(chan);
|
||||
|
@ -1995,11 +1996,12 @@ fn shut_down_layout(page_tree: &Rc<Page>) {
|
|||
}
|
||||
|
||||
// Drop our references to the JSContext and DOM objects.
|
||||
for page in page_tree.iter() {
|
||||
let window = page.window();
|
||||
for context in context_tree.iter() {
|
||||
let window = context.active_window();
|
||||
window.clear_js_runtime();
|
||||
|
||||
// Sever the connection between the global and the DOM tree
|
||||
page.set_frame(None);
|
||||
context.clear_session_history();
|
||||
}
|
||||
|
||||
// Destroy the layout thread. If there were node leaks, layout will now crash safely.
|
||||
|
@ -2008,10 +2010,12 @@ fn shut_down_layout(page_tree: &Rc<Page>) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_page(page: &Rc<Page>, pipeline_id: PipelineId) -> Rc<Page> {
|
||||
page.find(pipeline_id).expect("ScriptThread: received an event \
|
||||
message for a layout channel that is not associated with this script thread.\
|
||||
This is a bug.")
|
||||
pub fn get_browsing_context(context: &Root<BrowsingContext>,
|
||||
pipeline_id: PipelineId)
|
||||
-> Root<BrowsingContext> {
|
||||
context.find(pipeline_id).expect("ScriptThread: received an event \
|
||||
message for a layout channel that is not associated with this script thread.\
|
||||
This is a bug.")
|
||||
}
|
||||
|
||||
fn dom_last_modified(tm: &Tm) -> String {
|
||||
|
|
|
@ -15,6 +15,7 @@ use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
|
|||
use dom::bindings::conversions::{FromJSValConvertible, StringificationBehavior};
|
||||
use dom::bindings::inheritance::Castable;
|
||||
use dom::bindings::js::Root;
|
||||
use dom::browsingcontext::BrowsingContext;
|
||||
use dom::element::Element;
|
||||
use dom::htmlelement::HTMLElement;
|
||||
use dom::htmliframeelement::HTMLIFrameElement;
|
||||
|
@ -31,15 +32,16 @@ use js::jsapi::{HandleValue, RootedValue};
|
|||
use js::jsval::UndefinedValue;
|
||||
use msg::constellation_msg::{PipelineId, WindowSizeData};
|
||||
use msg::webdriver_msg::{WebDriverFrameId, WebDriverJSError, WebDriverJSResult, WebDriverJSValue};
|
||||
use page::Page;
|
||||
use script_thread::get_page;
|
||||
use std::rc::Rc;
|
||||
use script_thread::get_browsing_context;
|
||||
use url::Url;
|
||||
use util::str::DOMString;
|
||||
|
||||
fn find_node_by_unique_id(page: &Rc<Page>, pipeline: PipelineId, node_id: String) -> Option<Root<Node>> {
|
||||
let page = get_page(&*page, pipeline);
|
||||
let document = page.document();
|
||||
fn find_node_by_unique_id(context: &Root<BrowsingContext>,
|
||||
pipeline: PipelineId,
|
||||
node_id: String)
|
||||
-> Option<Root<Node>> {
|
||||
let context = get_browsing_context(&context, pipeline);
|
||||
let document = context.active_document();
|
||||
document.upcast::<Node>().traverse_preorder().find(|candidate| candidate.unique_id() == node_id)
|
||||
}
|
||||
|
||||
|
@ -63,12 +65,12 @@ pub unsafe fn jsval_to_webdriver(cx: *mut JSContext, val: HandleValue) -> WebDri
|
|||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
pub fn handle_execute_script(page: &Rc<Page>,
|
||||
pub fn handle_execute_script(context: &Root<BrowsingContext>,
|
||||
pipeline: PipelineId,
|
||||
eval: String,
|
||||
reply: IpcSender<WebDriverJSResult>) {
|
||||
let page = get_page(&*page, pipeline);
|
||||
let window = page.window();
|
||||
let context = get_browsing_context(&context, pipeline);
|
||||
let window = context.active_window();
|
||||
let result = unsafe {
|
||||
let cx = window.get_cx();
|
||||
let mut rval = RootedValue::new(cx, UndefinedValue());
|
||||
|
@ -78,19 +80,19 @@ pub fn handle_execute_script(page: &Rc<Page>,
|
|||
reply.send(result).unwrap();
|
||||
}
|
||||
|
||||
pub fn handle_execute_async_script(page: &Rc<Page>,
|
||||
pub fn handle_execute_async_script(context: &Root<BrowsingContext>,
|
||||
pipeline: PipelineId,
|
||||
eval: String,
|
||||
reply: IpcSender<WebDriverJSResult>) {
|
||||
let page = get_page(&*page, pipeline);
|
||||
let window = page.window();
|
||||
let context = get_browsing_context(&context, pipeline);
|
||||
let window = context.active_window();
|
||||
let cx = window.get_cx();
|
||||
window.set_webdriver_script_chan(Some(reply));
|
||||
let mut rval = RootedValue::new(cx, UndefinedValue());
|
||||
window.evaluate_js_on_global_with_result(&eval, rval.handle_mut());
|
||||
}
|
||||
|
||||
pub fn handle_get_frame_id(page: &Rc<Page>,
|
||||
pub fn handle_get_frame_id(context: &Root<BrowsingContext>,
|
||||
pipeline: PipelineId,
|
||||
webdriver_frame_id: WebDriverFrameId,
|
||||
reply: IpcSender<Result<Option<PipelineId>, ()>>) {
|
||||
|
@ -100,7 +102,7 @@ pub fn handle_get_frame_id(page: &Rc<Page>,
|
|||
Ok(None)
|
||||
},
|
||||
WebDriverFrameId::Element(x) => {
|
||||
match find_node_by_unique_id(page, pipeline, x) {
|
||||
match find_node_by_unique_id(context, pipeline, x) {
|
||||
Some(ref node) => {
|
||||
match node.downcast::<HTMLIFrameElement>() {
|
||||
Some(ref elem) => Ok(elem.GetContentWindow()),
|
||||
|
@ -111,7 +113,7 @@ pub fn handle_get_frame_id(page: &Rc<Page>,
|
|||
}
|
||||
},
|
||||
WebDriverFrameId::Parent => {
|
||||
let window = page.window();
|
||||
let window = context.active_window();
|
||||
Ok(window.parent())
|
||||
}
|
||||
};
|
||||
|
@ -120,9 +122,9 @@ pub fn handle_get_frame_id(page: &Rc<Page>,
|
|||
reply.send(frame_id).unwrap()
|
||||
}
|
||||
|
||||
pub fn handle_find_element_css(page: &Rc<Page>, _pipeline: PipelineId, selector: String,
|
||||
pub fn handle_find_element_css(context: &Root<BrowsingContext>, _pipeline: PipelineId, selector: String,
|
||||
reply: IpcSender<Result<Option<String>, ()>>) {
|
||||
reply.send(match page.document().QuerySelector(DOMString::from(selector)) {
|
||||
reply.send(match context.active_document().QuerySelector(DOMString::from(selector)) {
|
||||
Ok(node) => {
|
||||
Ok(node.map(|x| x.upcast::<Node>().unique_id()))
|
||||
}
|
||||
|
@ -130,11 +132,11 @@ pub fn handle_find_element_css(page: &Rc<Page>, _pipeline: PipelineId, selector:
|
|||
}).unwrap();
|
||||
}
|
||||
|
||||
pub fn handle_find_elements_css(page: &Rc<Page>,
|
||||
pub fn handle_find_elements_css(context: &Root<BrowsingContext>,
|
||||
_pipeline: PipelineId,
|
||||
selector: String,
|
||||
reply: IpcSender<Result<Vec<String>, ()>>) {
|
||||
reply.send(match page.document().QuerySelectorAll(DOMString::from(selector)) {
|
||||
reply.send(match context.active_document().QuerySelectorAll(DOMString::from(selector)) {
|
||||
Ok(ref nodes) => {
|
||||
let mut result = Vec::with_capacity(nodes.Length() as usize);
|
||||
for i in 0..nodes.Length() {
|
||||
|
@ -150,11 +152,11 @@ pub fn handle_find_elements_css(page: &Rc<Page>,
|
|||
}).unwrap();
|
||||
}
|
||||
|
||||
pub fn handle_focus_element(page: &Rc<Page>,
|
||||
pub fn handle_focus_element(context: &Root<BrowsingContext>,
|
||||
pipeline: PipelineId,
|
||||
element_id: String,
|
||||
reply: IpcSender<Result<(), ()>>) {
|
||||
reply.send(match find_node_by_unique_id(page, pipeline, element_id) {
|
||||
reply.send(match find_node_by_unique_id(context, pipeline, element_id) {
|
||||
Some(ref node) => {
|
||||
match node.downcast::<HTMLElement>() {
|
||||
Some(ref elem) => {
|
||||
|
@ -169,22 +171,22 @@ pub fn handle_focus_element(page: &Rc<Page>,
|
|||
}).unwrap();
|
||||
}
|
||||
|
||||
pub fn handle_get_active_element(page: &Rc<Page>,
|
||||
pub fn handle_get_active_element(context: &Root<BrowsingContext>,
|
||||
_pipeline: PipelineId,
|
||||
reply: IpcSender<Option<String>>) {
|
||||
reply.send(page.document().GetActiveElement().map(
|
||||
reply.send(context.active_document().GetActiveElement().map(
|
||||
|elem| elem.upcast::<Node>().unique_id())).unwrap();
|
||||
}
|
||||
|
||||
pub fn handle_get_title(page: &Rc<Page>, _pipeline: PipelineId, reply: IpcSender<String>) {
|
||||
reply.send(String::from(page.document().Title())).unwrap();
|
||||
pub fn handle_get_title(context: &Root<BrowsingContext>, _pipeline: PipelineId, reply: IpcSender<String>) {
|
||||
reply.send(String::from(context.active_document().Title())).unwrap();
|
||||
}
|
||||
|
||||
pub fn handle_get_rect(page: &Rc<Page>,
|
||||
pub fn handle_get_rect(context: &Root<BrowsingContext>,
|
||||
pipeline: PipelineId,
|
||||
element_id: String,
|
||||
reply: IpcSender<Result<Rect<f64>, ()>>) {
|
||||
reply.send(match find_node_by_unique_id(&*page, pipeline, element_id) {
|
||||
reply.send(match find_node_by_unique_id(context, pipeline, element_id) {
|
||||
Some(elem) => {
|
||||
// https://w3c.github.io/webdriver/webdriver-spec.html#dfn-calculate-the-absolute-position
|
||||
match elem.downcast::<HTMLElement>() {
|
||||
|
@ -218,11 +220,11 @@ pub fn handle_get_rect(page: &Rc<Page>,
|
|||
}).unwrap();
|
||||
}
|
||||
|
||||
pub fn handle_get_text(page: &Rc<Page>,
|
||||
pub fn handle_get_text(context: &Root<BrowsingContext>,
|
||||
pipeline: PipelineId,
|
||||
node_id: String,
|
||||
reply: IpcSender<Result<String, ()>>) {
|
||||
reply.send(match find_node_by_unique_id(&*page, pipeline, node_id) {
|
||||
reply.send(match find_node_by_unique_id(context, pipeline, node_id) {
|
||||
Some(ref node) => {
|
||||
Ok(node.GetTextContent().map_or("".to_owned(), String::from))
|
||||
},
|
||||
|
@ -230,11 +232,11 @@ pub fn handle_get_text(page: &Rc<Page>,
|
|||
}).unwrap();
|
||||
}
|
||||
|
||||
pub fn handle_get_name(page: &Rc<Page>,
|
||||
pub fn handle_get_name(context: &Root<BrowsingContext>,
|
||||
pipeline: PipelineId,
|
||||
node_id: String,
|
||||
reply: IpcSender<Result<String, ()>>) {
|
||||
reply.send(match find_node_by_unique_id(&*page, pipeline, node_id) {
|
||||
reply.send(match find_node_by_unique_id(context, pipeline, node_id) {
|
||||
Some(node) => {
|
||||
Ok(String::from(node.downcast::<Element>().unwrap().TagName()))
|
||||
},
|
||||
|
@ -242,12 +244,12 @@ pub fn handle_get_name(page: &Rc<Page>,
|
|||
}).unwrap();
|
||||
}
|
||||
|
||||
pub fn handle_get_attribute(page: &Rc<Page>,
|
||||
pub fn handle_get_attribute(context: &Root<BrowsingContext>,
|
||||
pipeline: PipelineId,
|
||||
node_id: String,
|
||||
name: String,
|
||||
reply: IpcSender<Result<Option<String>, ()>>) {
|
||||
reply.send(match find_node_by_unique_id(&*page, pipeline, node_id) {
|
||||
reply.send(match find_node_by_unique_id(context, pipeline, node_id) {
|
||||
Some(node) => {
|
||||
Ok(node.downcast::<Element>().unwrap().GetAttribute(DOMString::from(name))
|
||||
.map(String::from))
|
||||
|
@ -256,14 +258,14 @@ pub fn handle_get_attribute(page: &Rc<Page>,
|
|||
}).unwrap();
|
||||
}
|
||||
|
||||
pub fn handle_get_css(page: &Rc<Page>,
|
||||
pub fn handle_get_css(context: &Root<BrowsingContext>,
|
||||
pipeline: PipelineId,
|
||||
node_id: String,
|
||||
name: String,
|
||||
reply: IpcSender<Result<String, ()>>) {
|
||||
reply.send(match find_node_by_unique_id(&*page, pipeline, node_id) {
|
||||
reply.send(match find_node_by_unique_id(context, pipeline, node_id) {
|
||||
Some(node) => {
|
||||
let window = page.window();
|
||||
let window = context.active_window();
|
||||
let elem = node.downcast::<Element>().unwrap();
|
||||
Ok(String::from(
|
||||
window.GetComputedStyle(&elem, None).GetPropertyValue(DOMString::from(name))))
|
||||
|
@ -272,27 +274,27 @@ pub fn handle_get_css(page: &Rc<Page>,
|
|||
}).unwrap();
|
||||
}
|
||||
|
||||
pub fn handle_get_url(page: &Rc<Page>,
|
||||
pub fn handle_get_url(context: &Root<BrowsingContext>,
|
||||
_pipeline: PipelineId,
|
||||
reply: IpcSender<Url>) {
|
||||
let document = page.document();
|
||||
let document = context.active_document();
|
||||
let url = document.url();
|
||||
reply.send((*url).clone()).unwrap();
|
||||
}
|
||||
|
||||
pub fn handle_get_window_size(page: &Rc<Page>,
|
||||
pub fn handle_get_window_size(context: &Root<BrowsingContext>,
|
||||
_pipeline: PipelineId,
|
||||
reply: IpcSender<Option<WindowSizeData>>) {
|
||||
let window = page.window();
|
||||
let window = context.active_window();
|
||||
let size = window.window_size();
|
||||
reply.send(size).unwrap();
|
||||
}
|
||||
|
||||
pub fn handle_is_enabled(page: &Rc<Page>,
|
||||
pub fn handle_is_enabled(context: &Root<BrowsingContext>,
|
||||
pipeline: PipelineId,
|
||||
element_id: String,
|
||||
reply: IpcSender<Result<bool, ()>>) {
|
||||
reply.send(match find_node_by_unique_id(page, pipeline, element_id) {
|
||||
reply.send(match find_node_by_unique_id(&context, pipeline, element_id) {
|
||||
Some(ref node) => {
|
||||
match node.downcast::<Element>() {
|
||||
Some(elem) => Ok(elem.enabled_state()),
|
||||
|
@ -303,11 +305,11 @@ pub fn handle_is_enabled(page: &Rc<Page>,
|
|||
}).unwrap();
|
||||
}
|
||||
|
||||
pub fn handle_is_selected(page: &Rc<Page>,
|
||||
pub fn handle_is_selected(context: &Root<BrowsingContext>,
|
||||
pipeline: PipelineId,
|
||||
element_id: String,
|
||||
reply: IpcSender<Result<bool, ()>>) {
|
||||
reply.send(match find_node_by_unique_id(page, pipeline, element_id) {
|
||||
reply.send(match find_node_by_unique_id(context, pipeline, element_id) {
|
||||
Some(ref node) => {
|
||||
if let Some(input_element) = node.downcast::<HTMLInputElement>() {
|
||||
Ok(input_element.Checked())
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue