mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
Use a single JSContext per JSRuntime.
The long-term plan for SpiderMonkey is to eliminate JSContexts by merging (most of) it into JSRuntime, so to future-proof our code, we should avoid creating multiple JSContexts for the same JSRuntime. However, this implies we'll have to use the same JSContext for objects in different compartments, so we need to enter compartments. This is done by using the with_compartment function.
This commit is contained in:
parent
bba4bef106
commit
0016b1839e
5 changed files with 119 additions and 88 deletions
|
@ -1755,22 +1755,26 @@ def CreateBindingJSObject(descriptor, parent=None):
|
||||||
create = " let mut raw: JS<%s> = JS::from_raw(&mut *aObject);\n" % descriptor.concreteType
|
create = " let mut raw: JS<%s> = JS::from_raw(&mut *aObject);\n" % descriptor.concreteType
|
||||||
if descriptor.proxy:
|
if descriptor.proxy:
|
||||||
assert not descriptor.createGlobal
|
assert not descriptor.createGlobal
|
||||||
handler = """
|
create += """
|
||||||
let js_info = aScope.deref().page().js_info();
|
let js_info = aScope.deref().page().js_info();
|
||||||
let handler = js_info.get_ref().dom_static.proxy_handlers.deref().get(&(PrototypeList::id::%s as uint));
|
let handler = js_info.get_ref().dom_static.proxy_handlers.deref().get(&(PrototypeList::id::%s as uint));
|
||||||
""" % descriptor.name
|
let private = PrivateValue(squirrel_away_unique(aObject) as *libc::c_void);
|
||||||
create += handler + """ let obj = NewProxyObject(aCx, *handler,
|
let obj = with_compartment(aCx, proto, || {
|
||||||
&PrivateValue(squirrel_away_unique(aObject) as *libc::c_void),
|
NewProxyObject(aCx, *handler,
|
||||||
proto, %s,
|
&private,
|
||||||
ptr::null(), ptr::null());
|
proto, %s,
|
||||||
|
ptr::null(), ptr::null())
|
||||||
|
});
|
||||||
assert!(obj.is_not_null());
|
assert!(obj.is_not_null());
|
||||||
|
|
||||||
""" % (parent)
|
""" % (descriptor.name, parent)
|
||||||
else:
|
else:
|
||||||
if descriptor.createGlobal:
|
if descriptor.createGlobal:
|
||||||
create += " let obj = CreateDOMGlobal(aCx, &Class.base as *js::Class as *JSClass);\n"
|
create += " let obj = CreateDOMGlobal(aCx, &Class.base as *js::Class as *JSClass);\n"
|
||||||
else:
|
else:
|
||||||
create += " let obj = JS_NewObject(aCx, &Class.base as *js::Class as *JSClass, proto, %s);\n" % parent
|
create += (" let obj = with_compartment(aCx, proto, || {\n"
|
||||||
|
" JS_NewObject(aCx, &Class.base as *js::Class as *JSClass, proto, %s)\n"
|
||||||
|
" });\n" % parent)
|
||||||
create += """ assert!(obj.is_not_null());
|
create += """ assert!(obj.is_not_null());
|
||||||
|
|
||||||
JS_SetReservedSlot(obj, DOM_OBJECT_SLOT as u32,
|
JS_SetReservedSlot(obj, DOM_OBJECT_SLOT as u32,
|
||||||
|
@ -1797,8 +1801,7 @@ class CGWrapMethod(CGAbstractMethod):
|
||||||
assert!(scope.is_not_null());
|
assert!(scope.is_not_null());
|
||||||
assert!(((*JS_GetClass(scope)).flags & JSCLASS_IS_GLOBAL) != 0);
|
assert!(((*JS_GetClass(scope)).flags & JSCLASS_IS_GLOBAL) != 0);
|
||||||
|
|
||||||
//JSAutoCompartment ac(aCx, scope);
|
let proto = with_compartment(aCx, scope, || GetProtoObject(aCx, scope, scope));
|
||||||
let proto = GetProtoObject(aCx, scope, scope);
|
|
||||||
assert!(proto.is_not_null());
|
assert!(proto.is_not_null());
|
||||||
|
|
||||||
%s
|
%s
|
||||||
|
@ -1809,8 +1812,10 @@ class CGWrapMethod(CGAbstractMethod):
|
||||||
else:
|
else:
|
||||||
return """
|
return """
|
||||||
%s
|
%s
|
||||||
let proto = GetProtoObject(aCx, obj, obj);
|
with_compartment(aCx, obj, || {
|
||||||
JS_SetPrototype(aCx, obj, proto);
|
let proto = GetProtoObject(aCx, obj, obj);
|
||||||
|
JS_SetPrototype(aCx, obj, proto);
|
||||||
|
});
|
||||||
raw.mut_reflector().set_jsobject(obj);
|
raw.mut_reflector().set_jsobject(obj);
|
||||||
return raw;""" % CreateBindingJSObject(self.descriptor)
|
return raw;""" % CreateBindingJSObject(self.descriptor)
|
||||||
|
|
||||||
|
@ -4241,6 +4246,7 @@ class CGBindingRoot(CGThing):
|
||||||
'js::glue::{GetProxyPrivate, NewProxyObject, ProxyTraps}',
|
'js::glue::{GetProxyPrivate, NewProxyObject, ProxyTraps}',
|
||||||
'js::glue::{RUST_FUNCTION_VALUE_TO_JITINFO}',
|
'js::glue::{RUST_FUNCTION_VALUE_TO_JITINFO}',
|
||||||
'js::glue::{RUST_JS_NumberValue, RUST_JSID_IS_STRING}',
|
'js::glue::{RUST_JS_NumberValue, RUST_JSID_IS_STRING}',
|
||||||
|
'js::rust::with_compartment',
|
||||||
'dom::types::*',
|
'dom::types::*',
|
||||||
'dom::bindings',
|
'dom::bindings',
|
||||||
'dom::bindings::js::{JS, JSRef, Root, RootedReference, Temporary}',
|
'dom::bindings::js::{JS, JSRef, Root, RootedReference, Temporary}',
|
||||||
|
|
|
@ -41,6 +41,7 @@ use js::jsfriendapi::bindgen::JS_NewObjectWithUniqueType;
|
||||||
use js::jsval::JSVal;
|
use js::jsval::JSVal;
|
||||||
use js::jsval::{PrivateValue, ObjectValue, NullValue, ObjectOrNullValue};
|
use js::jsval::{PrivateValue, ObjectValue, NullValue, ObjectOrNullValue};
|
||||||
use js::jsval::{Int32Value, UInt32Value, DoubleValue, BooleanValue, UndefinedValue};
|
use js::jsval::{Int32Value, UInt32Value, DoubleValue, BooleanValue, UndefinedValue};
|
||||||
|
use js::rust::with_compartment;
|
||||||
use js::{JSPROP_ENUMERATE, JSCLASS_IS_GLOBAL, JSCLASS_IS_DOMJSCLASS};
|
use js::{JSPROP_ENUMERATE, JSCLASS_IS_GLOBAL, JSCLASS_IS_DOMJSCLASS};
|
||||||
use js::JSPROP_PERMANENT;
|
use js::JSPROP_PERMANENT;
|
||||||
use js::{JSFUN_CONSTRUCTOR, JSPROP_READONLY};
|
use js::{JSFUN_CONSTRUCTOR, JSPROP_READONLY};
|
||||||
|
@ -583,7 +584,9 @@ pub fn CreateDOMGlobal(cx: *JSContext, class: *JSClass) -> *JSObject {
|
||||||
if obj.is_null() {
|
if obj.is_null() {
|
||||||
return ptr::null();
|
return ptr::null();
|
||||||
}
|
}
|
||||||
JS_InitStandardClasses(cx, obj);
|
with_compartment(cx, obj, || {
|
||||||
|
JS_InitStandardClasses(cx, obj);
|
||||||
|
});
|
||||||
initialize_global(obj);
|
initialize_global(obj);
|
||||||
obj
|
obj
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ use dom::window::Window;
|
||||||
|
|
||||||
use js::jsapi::JSObject;
|
use js::jsapi::JSObject;
|
||||||
use js::glue::{WrapperNew, CreateWrapperProxyHandler, ProxyTraps};
|
use js::glue::{WrapperNew, CreateWrapperProxyHandler, ProxyTraps};
|
||||||
|
use js::rust::with_compartment;
|
||||||
|
|
||||||
use libc::c_void;
|
use libc::c_void;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
|
@ -56,9 +57,9 @@ impl BrowserContext {
|
||||||
|
|
||||||
let parent = win.deref().reflector().get_jsobject();
|
let parent = win.deref().reflector().get_jsobject();
|
||||||
let cx = js_info.get_ref().js_context.deref().deref().ptr;
|
let cx = js_info.get_ref().js_context.deref().deref().ptr;
|
||||||
let wrapper = unsafe {
|
let wrapper = with_compartment(cx, parent, || unsafe {
|
||||||
WrapperNew(cx, parent, *handler.deref())
|
WrapperNew(cx, parent, *handler.deref())
|
||||||
};
|
});
|
||||||
assert!(wrapper.is_not_null());
|
assert!(wrapper.is_not_null());
|
||||||
wrapper
|
wrapper
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,10 +35,12 @@ use layout_interface;
|
||||||
use geom::point::Point2D;
|
use geom::point::Point2D;
|
||||||
use geom::size::Size2D;
|
use geom::size::Size2D;
|
||||||
use js::global::DEBUG_FNS;
|
use js::global::DEBUG_FNS;
|
||||||
use js::jsapi::{JSObject, JS_CallFunctionValue, JS_DefineFunctions};
|
use js::jsapi::{JS_CallFunctionValue, JS_DefineFunctions};
|
||||||
use js::jsapi::{JS_SetWrapObjectCallbacks, JS_SetGCZeal, JS_DEFAULT_ZEAL_FREQ};
|
use js::jsapi::{JS_SetWrapObjectCallbacks, JS_SetGCZeal, JS_DEFAULT_ZEAL_FREQ};
|
||||||
|
use js::jsapi::{JSContext, JSRuntime};
|
||||||
use js::jsval::NullValue;
|
use js::jsval::NullValue;
|
||||||
use js::rust::{Cx, RtUtils};
|
use js::rust::{Cx, RtUtils};
|
||||||
|
use js::rust::with_compartment;
|
||||||
use js;
|
use js;
|
||||||
use servo_msg::compositor_msg::{FinishedLoading, LayerId, Loading, PerformingLayout};
|
use servo_msg::compositor_msg::{FinishedLoading, LayerId, Loading, PerformingLayout};
|
||||||
use servo_msg::compositor_msg::{ScriptListener};
|
use servo_msg::compositor_msg::{ScriptListener};
|
||||||
|
@ -426,21 +428,8 @@ impl Page {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn initialize_js_info(&self, js_context: Rc<Cx>, global: *JSObject) {
|
pub fn initialize_js_info(&self, js_context: Rc<Cx>) {
|
||||||
assert!(global.is_not_null());
|
*self.mut_js_info() = Some(JSPageInfo {
|
||||||
|
|
||||||
// Note that the order that these variables are initialized is _not_ arbitrary. Switching
|
|
||||||
// them around can -- and likely will -- lead to things breaking.
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
JS_SetGCZeal(js_context.deref().ptr, 0, JS_DEFAULT_ZEAL_FREQ);
|
|
||||||
}
|
|
||||||
|
|
||||||
js_context.set_default_options_and_version();
|
|
||||||
js_context.set_logging_error_reporter();
|
|
||||||
|
|
||||||
let mut js_info = self.mut_js_info();
|
|
||||||
*js_info = Some(JSPageInfo {
|
|
||||||
dom_static: GlobalStaticData(),
|
dom_static: GlobalStaticData(),
|
||||||
js_context: Untraceable::new(js_context),
|
js_context: Untraceable::new(js_context),
|
||||||
});
|
});
|
||||||
|
@ -548,6 +537,8 @@ pub struct ScriptTask {
|
||||||
|
|
||||||
/// The JavaScript runtime.
|
/// The JavaScript runtime.
|
||||||
pub js_runtime: js::rust::rt,
|
pub js_runtime: js::rust::rt,
|
||||||
|
/// The JSContext.
|
||||||
|
pub js_context: RefCell<Option<Rc<Cx>>>,
|
||||||
|
|
||||||
pub mouse_over_targets: RefCell<Option<Vec<JS<Node>>>>
|
pub mouse_over_targets: RefCell<Option<Vec<JS<Node>>>>
|
||||||
}
|
}
|
||||||
|
@ -581,6 +572,7 @@ impl<'a> Drop for ScriptMemoryFailsafe<'a> {
|
||||||
for page in page_tree.iter() {
|
for page in page_tree.iter() {
|
||||||
*page.mut_js_info() = None;
|
*page.mut_js_info() = None;
|
||||||
}
|
}
|
||||||
|
*owner.js_context.borrow_mut() = None;
|
||||||
}
|
}
|
||||||
None => (),
|
None => (),
|
||||||
}
|
}
|
||||||
|
@ -599,15 +591,7 @@ impl ScriptTask {
|
||||||
img_cache_task: ImageCacheTask,
|
img_cache_task: ImageCacheTask,
|
||||||
window_size: Size2D<uint>)
|
window_size: Size2D<uint>)
|
||||||
-> Rc<ScriptTask> {
|
-> Rc<ScriptTask> {
|
||||||
let js_runtime = js::rust::rt();
|
let (js_runtime, js_context) = ScriptTask::new_rt_and_cx();
|
||||||
|
|
||||||
unsafe {
|
|
||||||
JS_SetWrapObjectCallbacks(js_runtime.deref().ptr,
|
|
||||||
ptr::null(),
|
|
||||||
wrap_for_same_compartment,
|
|
||||||
ptr::null());
|
|
||||||
}
|
|
||||||
|
|
||||||
Rc::new(ScriptTask {
|
Rc::new(ScriptTask {
|
||||||
page_tree: RefCell::new(PageTree::new(id, layout_chan, window_size)),
|
page_tree: RefCell::new(PageTree::new(id, layout_chan, window_size)),
|
||||||
|
|
||||||
|
@ -620,10 +604,42 @@ impl ScriptTask {
|
||||||
compositor: compositor,
|
compositor: compositor,
|
||||||
|
|
||||||
js_runtime: js_runtime,
|
js_runtime: js_runtime,
|
||||||
|
js_context: RefCell::new(Some(js_context)),
|
||||||
mouse_over_targets: RefCell::new(None)
|
mouse_over_targets: RefCell::new(None)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn new_rt_and_cx() -> (js::rust::rt, Rc<Cx>) {
|
||||||
|
let js_runtime = js::rust::rt();
|
||||||
|
assert!({
|
||||||
|
let ptr: *JSRuntime = (*js_runtime).ptr;
|
||||||
|
ptr.is_not_null()
|
||||||
|
});
|
||||||
|
unsafe {
|
||||||
|
JS_SetWrapObjectCallbacks((*js_runtime).ptr,
|
||||||
|
ptr::null(),
|
||||||
|
wrap_for_same_compartment,
|
||||||
|
ptr::null());
|
||||||
|
}
|
||||||
|
|
||||||
|
let js_context = js_runtime.cx();
|
||||||
|
assert!({
|
||||||
|
let ptr: *JSContext = (*js_context).ptr;
|
||||||
|
ptr.is_not_null()
|
||||||
|
});
|
||||||
|
js_context.set_default_options_and_version();
|
||||||
|
js_context.set_logging_error_reporter();
|
||||||
|
unsafe {
|
||||||
|
JS_SetGCZeal((*js_context).ptr, 0, JS_DEFAULT_ZEAL_FREQ);
|
||||||
|
}
|
||||||
|
|
||||||
|
(js_runtime, js_context)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_cx(&self) -> *JSContext {
|
||||||
|
(**self.js_context.borrow().get_ref()).ptr
|
||||||
|
}
|
||||||
|
|
||||||
/// Starts the script task. After calling this method, the script task will loop receiving
|
/// Starts the script task. After calling this method, the script task will loop receiving
|
||||||
/// messages on its port.
|
/// messages on its port.
|
||||||
pub fn start(&self) {
|
pub fn start(&self) {
|
||||||
|
@ -772,10 +788,9 @@ impl ScriptTask {
|
||||||
Some(timer_handle) => {
|
Some(timer_handle) => {
|
||||||
// TODO: Support extra arguments. This requires passing a `*JSVal` array as `argv`.
|
// TODO: Support extra arguments. This requires passing a `*JSVal` array as `argv`.
|
||||||
let rval = NullValue();
|
let rval = NullValue();
|
||||||
let js_info = page.js_info();
|
|
||||||
let cx = js_info.get_ref().js_context.deref().deref().ptr;
|
|
||||||
unsafe {
|
unsafe {
|
||||||
JS_CallFunctionValue(cx, this_value, *timer_handle.data.funval,
|
JS_CallFunctionValue(self.get_cx(), this_value,
|
||||||
|
*timer_handle.data.funval,
|
||||||
0, ptr::null(), &rval);
|
0, ptr::null(), &rval);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -844,20 +859,16 @@ impl ScriptTask {
|
||||||
// If root is being exited, shut down all pages
|
// If root is being exited, shut down all pages
|
||||||
let mut page_tree = self.page_tree.borrow_mut();
|
let mut page_tree = self.page_tree.borrow_mut();
|
||||||
if page_tree.page().id == id {
|
if page_tree.page().id == id {
|
||||||
for page in page_tree.iter() {
|
debug!("shutting down layout for root page {:?}", id);
|
||||||
debug!("shutting down layout for root page {:?}", page.id);
|
*self.js_context.borrow_mut() = None;
|
||||||
shut_down_layout(&*page)
|
shut_down_layout(&mut *page_tree);
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise find just the matching page and exit all sub-pages
|
// otherwise find just the matching page and exit all sub-pages
|
||||||
match page_tree.remove(id) {
|
match page_tree.remove(id) {
|
||||||
Some(ref mut page_tree) => {
|
Some(ref mut page_tree) => {
|
||||||
for page in page_tree.iter() {
|
shut_down_layout(&mut *page_tree);
|
||||||
debug!("shutting down layout for page {:?}", page.id);
|
|
||||||
shut_down_layout(&*page)
|
|
||||||
}
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
// TODO(tkuehn): pipeline closing is currently duplicated across
|
// TODO(tkuehn): pipeline closing is currently duplicated across
|
||||||
|
@ -892,21 +903,22 @@ impl ScriptTask {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let cx = self.js_runtime.cx();
|
let cx = self.js_context.borrow();
|
||||||
|
let cx = cx.get_ref();
|
||||||
// Create the window and document objects.
|
// Create the window and document objects.
|
||||||
let mut window = Window::new(cx.deref().ptr,
|
let mut window = Window::new(cx.deref().ptr,
|
||||||
page_tree.page.clone(),
|
page_tree.page.clone(),
|
||||||
self.chan.clone(),
|
self.chan.clone(),
|
||||||
self.compositor.dup(),
|
self.compositor.dup(),
|
||||||
self.image_cache_task.clone()).root();
|
self.image_cache_task.clone()).root();
|
||||||
page.initialize_js_info(cx.clone(), window.reflector().get_jsobject());
|
page.initialize_js_info(cx.clone());
|
||||||
let mut document = Document::new(&*window, Some(url.clone()), HTMLDocument, None).root();
|
let mut document = Document::new(&*window, Some(url.clone()), HTMLDocument, None).root();
|
||||||
window.deref_mut().init_browser_context(&*document);
|
window.deref_mut().init_browser_context(&*document);
|
||||||
|
|
||||||
{
|
with_compartment((**cx).ptr, window.reflector().get_jsobject(), || {
|
||||||
let mut js_info = page.mut_js_info();
|
let mut js_info = page.mut_js_info();
|
||||||
RegisterBindings::Register(&window.unrooted(), js_info.get_mut_ref());
|
RegisterBindings::Register(&window.unrooted(), js_info.get_mut_ref());
|
||||||
}
|
});
|
||||||
|
|
||||||
self.compositor.set_ready_state(Loading);
|
self.compositor.set_ready_state(Loading);
|
||||||
// Parse HTML.
|
// Parse HTML.
|
||||||
|
@ -980,23 +992,25 @@ impl ScriptTask {
|
||||||
let js_scripts = js_scripts.take_unwrap();
|
let js_scripts = js_scripts.take_unwrap();
|
||||||
debug!("js_scripts: {:?}", js_scripts);
|
debug!("js_scripts: {:?}", js_scripts);
|
||||||
|
|
||||||
// Define debug functions.
|
with_compartment((**cx).ptr, window.reflector().get_jsobject(), || {
|
||||||
unsafe {
|
// Define debug functions.
|
||||||
assert!(JS_DefineFunctions((*cx).ptr,
|
unsafe {
|
||||||
window.reflector().get_jsobject(),
|
assert!(JS_DefineFunctions((**cx).ptr,
|
||||||
DEBUG_FNS.as_ptr()) != 0);
|
window.reflector().get_jsobject(),
|
||||||
}
|
DEBUG_FNS.as_ptr()) != 0);
|
||||||
|
|
||||||
// Evaluate every script in the document.
|
|
||||||
for file in js_scripts.iter() {
|
|
||||||
let global_obj = window.reflector().get_jsobject();
|
|
||||||
//FIXME: this should have some kind of error handling, or explicitly
|
|
||||||
// drop an exception on the floor.
|
|
||||||
match cx.evaluate_script(global_obj, file.data.clone(), file.url.to_str(), 1) {
|
|
||||||
Ok(_) => (),
|
|
||||||
Err(_) => println!("evaluate_script failed")
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// Evaluate every script in the document.
|
||||||
|
for file in js_scripts.iter() {
|
||||||
|
let global_obj = window.reflector().get_jsobject();
|
||||||
|
//FIXME: this should have some kind of error handling, or explicitly
|
||||||
|
// drop an exception on the floor.
|
||||||
|
match cx.evaluate_script(global_obj, file.data.clone(), file.url.to_str(), 1) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(_) => println!("evaluate_script failed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// We have no concept of a document loader right now, so just dispatch the
|
// We have no concept of a document loader right now, so just dispatch the
|
||||||
// "load" event as soon as we've finished executing all scripts parsed during
|
// "load" event as soon as we've finished executing all scripts parsed during
|
||||||
|
@ -1221,25 +1235,32 @@ impl ScriptTask {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shuts down layout for the given page.
|
/// Shuts down layout for the given page tree.
|
||||||
fn shut_down_layout(page: &Page) {
|
fn shut_down_layout(page_tree: &mut PageTree) {
|
||||||
page.join_layout();
|
for page in page_tree.iter() {
|
||||||
|
page.join_layout();
|
||||||
|
|
||||||
// Tell the layout task to begin shutting down.
|
// Tell the layout task to begin shutting down, and wait until it
|
||||||
let (response_chan, response_port) = channel();
|
// processed this message.
|
||||||
let LayoutChan(ref chan) = *page.layout_chan;
|
let (response_chan, response_port) = channel();
|
||||||
chan.send(layout_interface::PrepareToExitMsg(response_chan));
|
let LayoutChan(ref chan) = *page.layout_chan;
|
||||||
response_port.recv();
|
chan.send(layout_interface::PrepareToExitMsg(response_chan));
|
||||||
|
response_port.recv();
|
||||||
|
}
|
||||||
|
|
||||||
// Destroy all nodes. Setting frame and js_info to None will trigger our
|
// Remove our references to the DOM objects in this page tree.
|
||||||
// compartment to shutdown, run GC, etc.
|
for page in page_tree.iter() {
|
||||||
|
*page.mut_frame() = None;
|
||||||
|
}
|
||||||
|
|
||||||
let mut js_info = page.mut_js_info();
|
// Drop our references to the JSContext, potentially triggering a GC.
|
||||||
|
for page in page_tree.iter() {
|
||||||
let mut frame = page.mut_frame();
|
*page.mut_js_info() = None;
|
||||||
*frame = None;
|
}
|
||||||
*js_info = None;
|
|
||||||
|
|
||||||
// Destroy the layout task. If there were node leaks, layout will now crash safely.
|
// Destroy the layout task. If there were node leaks, layout will now crash safely.
|
||||||
chan.send(layout_interface::ExitNowMsg);
|
for page in page_tree.iter() {
|
||||||
|
let LayoutChan(ref chan) = *page.layout_chan;
|
||||||
|
chan.send(layout_interface::ExitNowMsg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 92d2979ff9656e6dcc018486bc3a7d1bcc63fd99
|
Subproject commit 50365a556b51da2455de051bfe4697872f49d8df
|
Loading…
Add table
Add a link
Reference in a new issue