<commit_before>/* 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/. */

//! The script task is the task that owns the DOM in memory, runs JavaScript, and spawns parsing
//! and layout tasks. It's in charge of processing events for all same-origin pages in a frame
//! tree, and manages the entire lifetime of pages in the frame tree from initial request to
//! teardown.
//!
//! Page loads follow a two-step process. When a request for a new page load is received, the
//! network request is initiated and the relevant data pertaining to the new page is stashed.
//! While the non-blocking request is ongoing, the script task is free to process further events,
//! noting when they pertain to ongoing loads (such as resizes/viewport adjustments). When the
//! initial response is received for an ongoing load, the second phase starts - the frame tree
//! entry is created, along with the Window and Document objects, and the appropriate parser
//! takes over the response body. Once parsing is complete, the document lifecycle for loading
//! a page runs its course and the script task returns to processing events in the main event
//! loop.

use devtools;
use devtools_traits::ScriptToDevtoolsControlMsg;
use devtools_traits::{DevtoolScriptControlMsg, DevtoolsPageInfo};
use document_loader::{DocumentLoader, LoadType, NotifierData};
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyState};
use dom::bindings::codegen::InheritTypes::{ElementCast, EventCast, EventTargetCast, NodeCast};
use dom::bindings::conversions::FromJSValConvertible;
use dom::bindings::conversions::StringificationBehavior;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, RootCollection, trace_roots};
use dom::bindings::js::{Root, RootCollectionPtr, RootedReference};
use dom::bindings::refcounted::{LiveDOMReferences, Trusted, TrustedReference, trace_refcounted_objects};
use dom::bindings::trace::{JSTraceable, RootedVec, trace_traceables};
use dom::bindings::utils::{DOM_CALLBACKS, WRAP_CALLBACKS};
use dom::document::{Document, DocumentProgressHandler, IsHTMLDocument};
use dom::document::{DocumentProgressTask, DocumentSource, MouseEventType};
use dom::element::Element;
use dom::event::{EventBubbles, EventCancelable};
use dom::node::{Node, NodeDamage, window_from_node};
use dom::servohtmlparser::{ParserContext, ServoHTMLParser};
use dom::uievent::UIEvent;
use dom::window::{ReflowReason, ScriptHelpers, Window};
use dom::worker::TrustedWorkerAddress;
use euclid::Rect;
use euclid::point::Point2D;
use hyper::header::{ContentType, HttpDate};
use hyper::header::{Headers, LastModified};
use hyper::method::Method;
use hyper::mime::{Mime, SubLevel, TopLevel};
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
use js::glue::CollectServoSizes;
use js::jsapi::{DOMProxyShadowsResult, HandleId, HandleObject, RootedValue, SetDOMProxyInformation};
use js::jsapi::{DisableIncrementalGC, JS_AddExtraGCRootsTracer, JS_SetWrapObjectCallbacks};
use js::jsapi::{GCDescription, GCProgress, JSGCInvocationKind, SetGCSliceCallback};
use js::jsapi::{JSAutoRequest, JSGCStatus, JS_GetRuntime, JS_SetGCCallback, SetDOMCallbacks};
use js::jsapi::{JSContext, JSRuntime, JSTracer};
use js::jsval::UndefinedValue;
use js::rust::Runtime;
use layout_interface::{ReflowQueryType};
use layout_interface::{self, LayoutChan, NewLayoutTaskInfo, ReflowGoal, ScriptLayoutChan};
use libc;
use mem::heap_size_of_eventtarget;
use msg::compositor_msg::{LayerId, ScriptToCompositorMsg};
use msg::constellation_msg::Msg as ConstellationMsg;
use msg::constellation_msg::{ConstellationChan, FocusType, LoadData};
use msg::constellation_msg::{MozBrowserEvent, PipelineExitType, PipelineId};
use msg::constellation_msg::{SubpageId, WindowSizeData, WorkerId};
use msg::webdriver_msg::WebDriverScriptCommand;
use net_traits::LoadData as NetLoadData;
use net_traits::image_cache_task::{ImageCacheChan, ImageCacheResult, ImageCacheTask};
use net_traits::storage_task::StorageTask;
use net_traits::{AsyncResponseTarget, ControlMsg, LoadConsumer, Metadata, ResourceTask};
use network_listener::NetworkListener;
use page::{Frame, IterablePage, Page};
use parse::html::{ParseContext, parse_html};
use profile_traits::mem::{self, OpaqueSender, Report, ReportKind, ReportsChan};
use profile_traits::time::{self, ProfilerCategory, profile};
use script_traits::CompositorEvent::{ClickEvent, ResizeEvent};
use script_traits::CompositorEvent::{KeyEvent, MouseMoveEvent};
use script_traits::CompositorEvent::{MouseDownEvent, MouseUpEvent};
use script_traits::{CompositorEvent, ConstellationControlMsg};
use script_traits::{InitialScriptState, MouseButton, NewLayoutInfo};
use script_traits::{OpaqueScriptLayoutChannel, ScriptState, ScriptTaskFactory};
use std::any::Any;
use std::borrow::ToOwned;
use std::cell::{Cell, RefCell};
use std::collections::HashSet;
use std::io::{Write, stdout};
use std::mem as std_mem;
use std::option::Option;
use std::ptr;
use std::rc::Rc;
use std::result::Result;
use std::sync::mpsc::{Receiver, Select, Sender, channel};
use std::sync::{Arc, Mutex};
use string_cache::Atom;
use time::{Tm, now};
use timers::TimerId;
use url::{Url, UrlParser};
use util::opts;
use util::str::DOMString;
use util::task::spawn_named_with_send_on_failure;
use util::task_state;
use webdriver_handlers;

thread_local!(pub static STACK_ROOTS: Cell<Option<RootCollectionPtr>> = Cell::new(None));
thread_local!(static SCRIPT_TASK_ROOT: RefCell<Option<*const ScriptTask>> = RefCell::new(None));

unsafe extern fn trace_rust_roots(tr: *mut JSTracer, _data: *mut libc::c_void) {
    SCRIPT_TASK_ROOT.with(|root| {
        if let Some(script_task) = *root.borrow() {
            (*script_task).trace(tr);
        }
    });

    trace_traceables(tr);
    trace_roots(tr);
}

/// A document load that is in the process of fetching the requested resource. Contains
/// data that will need to be present when the document and frame tree entry are created,
/// but is only easily available at initiation of the load and on a push basis (so some
/// data will be updated according to future resize events, viewport changes, etc.)
#[derive(JSTraceable)]
struct InProgressLoad {
    /// The pipeline which requested this load.
    pipeline_id: PipelineId,
    /// The parent pipeline and child subpage associated with this load, if any.
    parent_info: Option<(PipelineId, SubpageId)>,
    /// The current window size associated with this pipeline.
    window_size: Option<WindowSizeData>,
    /// Channel to the layout task associated with this pipeline.
    layout_chan: LayoutChan,
    /// The current viewport clipping rectangle applying to this pipeline, if any.
    clip_rect: Option<Rect<f32>>,
    /// The requested URL of the load.
    url: Url,
}

impl InProgressLoad {
    /// Create a new InProgressLoad object.
    fn new(id: PipelineId,
           parent_info: Option<(PipelineId, SubpageId)>,
           layout_chan: LayoutChan,
           window_size: Option<WindowSizeData>,
           url: Url) -> InProgressLoad {
        InProgressLoad {
            pipeline_id: id,
            parent_info: parent_info,
            layout_chan: layout_chan,
            window_size: window_size,
            clip_rect: None,
            url: url,
        }
    }
}

#[derive(Copy, Clone)]
pub enum TimerSource {
    FromWindow(PipelineId),
    FromWorker
}

pub trait Runnable {
    fn handler(self: Box<Self>);
}

pub trait MainThreadRunnable {
    fn handler(self: Box<Self>, script_task: &ScriptTask);
}

enum MixedMessage {
    FromConstellation(ConstellationControlMsg),
    FromScript(MainThreadScriptMsg),
    FromDevtools(DevtoolScriptControlMsg),
    FromImageCache(ImageCacheResult),
}

/// Common messages used to control the event loops in both the script and the worker
pub enum CommonScriptMsg {
    /// Requests that the script task measure its memory usage. The results are sent back via the
    /// supplied channel.
    CollectReports(ReportsChan),
    /// Fires a JavaScript timeout
    /// TimerSource must be FromWindow when dispatched to ScriptTask and
    /// must be FromWorker when dispatched to a DedicatedGlobalWorkerScope
    FireTimer(TimerSource, TimerId),
    /// A DOM object's last pinned reference was removed (dispatched to all tasks).
    RefcountCleanup(TrustedReference),
    /// Generic message that encapsulates event handling.
    RunnableMsg(ScriptTaskEventCategory, Box<Runnable + Send>),
}

#[derive(Clone, Copy, Debug, Eq, Hash, JSTraceable, PartialEq)]
pub enum ScriptTaskEventCategory {
    AttachLayout,
    ConstellationMsg,
    DevtoolsMsg,
    DocumentEvent,
    DomEvent,
    FileRead,
    ImageCacheMsg,
    InputEvent,
    NetworkEvent,
    Resize,
    ScriptEvent,
    UpdateReplacedElement,
    SetViewport,
    WebSocketEvent,
    WorkerEvent,
    XhrEvent,
}

/// Messages used to control the script event loop
pub enum MainThreadScriptMsg {
    /// Common variants associated with the script messages
    Common(CommonScriptMsg),
    /// Notify a document that all pending loads are complete.
    DocumentLoadsComplete(PipelineId),
    /// Notifies the script that a window associated with a particular pipeline
    /// should be closed (only dispatched to ScriptTask).
    ExitWindow(PipelineId),
    /// Generic message for running tasks in the ScriptTask
    MainThreadRunnableMsg(Box<MainThreadRunnable + Send>),
    /// Begins a content-initiated load on the specified pipeline (only
    /// dispatched to ScriptTask).
    Navigate(PipelineId, LoadData),
}

/// A cloneable interface for communicating with an event loop.
pub trait ScriptChan {
    /// Send a message to the associated event loop.
    fn send(&self, msg: CommonScriptMsg) -> Result<(), ()>;
    /// Clone this handle.
    fn clone(&self) -> Box<ScriptChan + Send>;
}

impl OpaqueSender<CommonScriptMsg> for Box<ScriptChan + Send> {
    fn send(&self, msg: CommonScriptMsg) {
        ScriptChan::send(&**self, msg).unwrap();
    }
}

/// An interface for receiving ScriptMsg values in an event loop. Used for synchronous DOM
/// APIs that need to abstract over multiple kinds of event loops (worker/main thread) with
/// different Receiver interfaces.
pub trait ScriptPort {
    fn recv(&self) -> CommonScriptMsg;
}

impl ScriptPort for Receiver<CommonScriptMsg> {
    fn recv(&self) -> CommonScriptMsg {
        self.recv().unwrap()
    }
}

impl ScriptPort for Receiver<MainThreadScriptMsg> {
    fn recv(&self) -> CommonScriptMsg {
        match self.recv().unwrap() {
            MainThreadScriptMsg::Common(script_msg) => script_msg,
            _ => panic!("unexpected main thread event message!")
        }
    }
}

impl ScriptPort for Receiver<(TrustedWorkerAddress, CommonScriptMsg)> {
    fn recv(&self) -> CommonScriptMsg {
        self.recv().unwrap().1
    }
}

impl ScriptPort for Receiver<(TrustedWorkerAddress, MainThreadScriptMsg)> {
    fn recv(&self) -> CommonScriptMsg {
        match self.recv().unwrap().1 {
            MainThreadScriptMsg::Common(script_msg) => script_msg,
            _ => panic!("unexpected main thread event message!")
        }
    }
}

/// Encapsulates internal communication of shared messages within the script task.
#[derive(JSTraceable)]
pub struct SendableMainThreadScriptChan(pub Sender<CommonScriptMsg>);

impl ScriptChan for SendableMainThreadScriptChan {
    fn send(&self, msg: CommonScriptMsg) -> Result<(), ()> {
        let SendableMainThreadScriptChan(ref chan) = *self;
        chan.send(msg).map_err(|_| ())
    }

    fn clone(&self) -> Box<ScriptChan + Send> {
        let SendableMainThreadScriptChan(ref chan) = *self;
        box SendableMainThreadScriptChan((*chan).clone())
    }
}

impl SendableMainThreadScriptChan {
    /// Creates a new script chan.
    pub fn new() -> (Receiver<CommonScriptMsg>, Box<SendableMainThreadScriptChan>) {
        let (chan, port) = channel();
        (port, box SendableMainThreadScriptChan(chan))
    }
}

/// Encapsulates internal communication of main thread messages within the script task.
#[derive(JSTraceable)]
pub struct MainThreadScriptChan(pub Sender<MainThreadScriptMsg>);

impl ScriptChan for MainThreadScriptChan {
    fn send(&self, msg: CommonScriptMsg) -> Result<(), ()> {
        let MainThreadScriptChan(ref chan) = *self;
        chan.send(MainThreadScriptMsg::Common(msg)).map_err(|_| ())
    }

    fn clone(&self) -> Box<ScriptChan + Send> {
        let MainThreadScriptChan(ref chan) = *self;
        box MainThreadScriptChan((*chan).clone())
    }
}

impl MainThreadScriptChan {
    /// Creates a new script chan.
    pub fn new() -> (Receiver<MainThreadScriptMsg>, Box<MainThreadScriptChan>) {
        let (chan, port) = channel();
        (port, box MainThreadScriptChan(chan))
    }
}

pub struct StackRootTLS;

impl StackRootTLS {
    pub fn new(roots: &RootCollection) -> StackRootTLS {
        STACK_ROOTS.with(|ref r| {
            r.set(Some(RootCollectionPtr(roots as *const _)))
        });
        StackRootTLS
    }
}

impl Drop for StackRootTLS {
    fn drop(&mut self) {
        STACK_ROOTS.with(|ref r| r.set(None));
    }
}


/// Information for an entire page. Pages are top-level browsing contexts and can contain multiple
/// frames.
#[derive(JSTraceable)]
// ScriptTask instances are rooted on creation, so this is okay
#[allow(unrooted_must_root)]
pub struct ScriptTask {
    /// A handle to the information pertaining to page layout
    page: DOMRefCell<Option<Rc<Page>>>,
    /// 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 task.
    image_cache_task: ImageCacheTask,
    /// A handle to the resource task. This is an `Arc` to avoid running out of file descriptors if
    /// there are many iframes.
    resource_task: Arc<ResourceTask>,
    /// A handle to the storage task.
    storage_task: StorageTask,

    /// The port on which the script task receives messages (load URL, exit, etc.)
    port: Receiver<MainThreadScriptMsg>,
    /// A channel to hand out to script task-based entities that need to be able to enqueue
    /// events in the event queue.
    chan: MainThreadScriptChan,

    /// A channel to hand out to tasks that need to respond to a message from the script task.
    control_chan: Sender<ConstellationControlMsg>,

    /// The port on which the constellation and layout tasks can communicate with the
    /// script task.
    control_port: Receiver<ConstellationControlMsg>,

    /// For communicating load url messages to the constellation
    constellation_chan: ConstellationChan,

    /// A handle to the compositor for communicating ready state messages.
    compositor: DOMRefCell<IpcSender<ScriptToCompositorMsg>>,

    /// The port on which we receive messages from the image cache
    image_cache_port: Receiver<ImageCacheResult>,

    /// The channel on which the image cache can send messages to ourself.
    image_cache_channel: ImageCacheChan,

    /// For providing contact with the time profiler.
    time_profiler_chan: time::ProfilerChan,

    /// For providing contact with the memory profiler.
    mem_profiler_chan: mem::ProfilerChan,

    /// For providing instructions to an optional devtools server.
    devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
    /// For receiving commands from an optional devtools server. Will be ignored if
    /// no such server exists.
    devtools_port: Receiver<DevtoolScriptControlMsg>,
    devtools_sender: IpcSender<DevtoolScriptControlMsg>,

    /// The JavaScript runtime.
    js_runtime: Rc<Runtime>,

    mouse_over_targets: DOMRefCell<Vec<JS<Node>>>,

    /// List of pipelines that have been owned and closed by this script task.
    closed_pipelines: RefCell<HashSet<PipelineId>>,
}

/// In the event of task failure, all data on the stack runs its destructor. However, there
/// are no reachable, owning pointers to the DOM memory, so it never gets freed by default
/// when the script task fails. The ScriptMemoryFailsafe uses the destructor bomb pattern
/// to forcibly tear down the JS compartments for pages associated with the failing ScriptTask.
struct ScriptMemoryFailsafe<'a> {
    owner: Option<&'a ScriptTask>,
}

impl<'a> ScriptMemoryFailsafe<'a> {
    fn neuter(&mut self) {
        self.owner = None;
    }

    fn new(owner: &'a ScriptTask) -> ScriptMemoryFailsafe<'a> {
        ScriptMemoryFailsafe {
            owner: Some(owner),
        }
    }
}

impl<'a> Drop for ScriptMemoryFailsafe<'a> {
    #[allow(unrooted_must_root)]
    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.r().clear_js_runtime_for_script_deallocation();
                    }
                }
            }
            None => (),
        }
    }
}

impl ScriptTaskFactory for ScriptTask {
    fn create_layout_channel(_phantom: Option<&mut ScriptTask>) -> OpaqueScriptLayoutChannel {
        let (chan, port) = channel();
        ScriptLayoutChan::new(chan, port)
    }

    fn clone_layout_channel(_phantom: Option<&mut ScriptTask>, pair: &OpaqueScriptLayoutChannel) -> Box<Any + Send> {
        box pair.sender() as Box<Any + Send>
    }

    fn create(_phantom: Option<&mut ScriptTask>,
              state: InitialScriptState,
              layout_chan: &OpaqueScriptLayoutChannel,
              load_data: LoadData) {
        let ConstellationChan(const_chan) = state.constellation_chan.clone();
        let (script_chan, script_port) = channel();
        let layout_chan = LayoutChan(layout_chan.sender());
        let failure_info = state.failure_info;
        spawn_named_with_send_on_failure(format!("ScriptTask {:?}", state.id), task_state::SCRIPT, move || {
            let roots = RootCollection::new();
            let _stack_roots_tls = StackRootTLS::new(&roots);
            let chan = MainThreadScriptChan(script_chan);
            let channel_for_reporter = chan.clone();
            let id = state.id;
            let parent_info = state.parent_info;
            let mem_profiler_chan = state.mem_profiler_chan.clone();
            let window_size = state.window_size;
            let script_task = ScriptTask::new(state,
                                              script_port,
                                              chan);

            SCRIPT_TASK_ROOT.with(|root| {
                *root.borrow_mut() = Some(&script_task as *const _);
            });

            let mut failsafe = ScriptMemoryFailsafe::new(&script_task);

            let new_load = InProgressLoad::new(id, parent_info, layout_chan, window_size,
                                               load_data.url.clone());
            script_task.start_page_load(new_load, load_data);

            let reporter_name = format!("script-reporter-{}", id.0);
            mem_profiler_chan.run_with_memory_reporting(|| {
                script_task.start();
            }, reporter_name, channel_for_reporter, CommonScriptMsg::CollectReports);

            // This must always be the very last operation performed before the task completes
            failsafe.neuter();
        }, ConstellationMsg::Failure(failure_info), const_chan);
    }
}

thread_local!(static GC_CYCLE_START: Cell<Option<Tm>> = Cell::new(None));
thread_local!(static GC_SLICE_START: Cell<Option<Tm>> = Cell::new(None));

unsafe extern "C" fn gc_slice_callback(_rt: *mut JSRuntime, progress: GCProgress, desc: *const GCDescription) {
    match progress {
        GCProgress::GC_CYCLE_BEGIN => {
            GC_CYCLE_START.with(|start| {
                start.set(Some(now()));
                println!("GC cycle began");
            })
        },
        GCProgress::GC_SLICE_BEGIN => {
            GC_SLICE_START.with(|start| {
                start.set(Some(now()));
                println!("GC slice began");
            })
        },
        GCProgress::GC_SLICE_END => {
            GC_SLICE_START.with(|start| {
                let dur = now() - start.get().unwrap();
                start.set(None);
                println!("GC slice ended: duration={}", dur);
            })
        },
        GCProgress::GC_CYCLE_END => {
            GC_CYCLE_START.with(|start| {
                let dur = now() - start.get().unwrap();
                start.set(None);
                println!("GC cycle ended: duration={}", dur);
            })
        },
    };
    if !desc.is_null() {
        let desc: &GCDescription = &*desc;
        let invocationKind = match desc.invocationKind_ {
            JSGCInvocationKind::GC_NORMAL => "GC_NORMAL",
            JSGCInvocationKind::GC_SHRINK => "GC_SHRINK",
        };
        println!("  isCompartment={}, invocationKind={}", desc.isCompartment_, invocationKind);
    }
    let _ = stdout().flush();
}

unsafe extern "C" fn debug_gc_callback(_rt: *mut JSRuntime, status: JSGCStatus, _data: *mut libc::c_void) {
    match status {
        JSGCStatus::JSGC_BEGIN => task_state::enter(task_state::IN_GC),
        JSGCStatus::JSGC_END   => task_state::exit(task_state::IN_GC),
    }
}

unsafe extern "C" fn shadow_check_callback(_cx: *mut JSContext,
    _object: HandleObject, _id: HandleId) -> DOMProxyShadowsResult {
    // XXX implement me
    DOMProxyShadowsResult::ShadowCheckFailed
}

impl ScriptTask {
    pub fn page_fetch_complete(id: PipelineId, subpage: Option<SubpageId>, metadata: Metadata)
                               -> Option<Root<ServoHTMLParser>> {
        SCRIPT_TASK_ROOT.with(|root| {
            let script_task = unsafe { &*root.borrow().unwrap() };
            script_task.handle_page_fetch_complete(id, subpage, metadata)
        })
    }

    pub fn parsing_complete(id: PipelineId) {
        SCRIPT_TASK_ROOT.with(|root| {
            let script_task = unsafe { &*root.borrow().unwrap() };
            script_task.handle_parsing_complete(id);
        });
    }

    pub fn process_event(msg: CommonScriptMsg) {
        SCRIPT_TASK_ROOT.with(|root| {
            if let Some(script_task) = *root.borrow() {
                let script_task = unsafe { &*script_task };
                script_task.handle_msg_from_script(MainThreadScriptMsg::Common(msg));
            }
        });
    }

    /// Creates a new script task.
    pub fn new(state: InitialScriptState,
               port: Receiver<MainThreadScriptMsg>,
               chan: MainThreadScriptChan)
               -> ScriptTask {
        let runtime = ScriptTask::new_rt_and_cx();

        unsafe {
            JS_SetWrapObjectCallbacks(runtime.rt(),
                                      &WRAP_CALLBACKS);
        }

        // Ask the router to proxy IPC messages from the devtools to us.
        let (ipc_devtools_sender, ipc_devtools_receiver) = ipc::channel().unwrap();
        let devtools_port = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_devtools_receiver);

        // Ask the router to proxy IPC messages from the image cache task to us.
        let (ipc_image_cache_channel, ipc_image_cache_port) = ipc::channel().unwrap();
        let image_cache_port =
            ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_image_cache_port);

        ScriptTask {
            page: DOMRefCell::new(None),
            incomplete_loads: DOMRefCell::new(vec!()),

            image_cache_task: state.image_cache_task,
            image_cache_channel: ImageCacheChan(ipc_image_cache_channel),
            image_cache_port: image_cache_port,

            resource_task: Arc::new(state.resource_task),
            storage_task: state.storage_task,

            port: port,
            chan: chan,
            control_chan: state.control_chan,
            control_port: state.control_port,
            constellation_chan: state.constellation_chan,
            compositor: DOMRefCell::new(state.compositor),
            time_profiler_chan: state.time_profiler_chan,
            mem_profiler_chan: state.mem_profiler_chan,

            devtools_chan: state.devtools_chan,
            devtools_port: devtools_port,
            devtools_sender: ipc_devtools_sender,

            js_runtime: Rc::new(runtime),
            mouse_over_targets: DOMRefCell::new(vec!()),
            closed_pipelines: RefCell::new(HashSet::new()),
        }
    }

    pub fn new_rt_and_cx() -> Runtime {
        LiveDOMReferences::initialize();
        let runtime = Runtime::new();

        unsafe {
            JS_AddExtraGCRootsTracer(runtime.rt(), Some(trace_rust_roots), ptr::null_mut());
            JS_AddExtraGCRootsTracer(runtime.rt(), Some(trace_refcounted_objects), ptr::null_mut());
        }

        // Needed for debug assertions about whether GC is running.
        if cfg!(debug_assertions) {
            unsafe {
                JS_SetGCCallback(runtime.rt(), Some(debug_gc_callback), ptr::null_mut());
            }
        }
        if opts::get().gc_profile {
            unsafe {
                SetGCSliceCallback(runtime.rt(), Some(gc_slice_callback));
            }
        }

        unsafe {
            SetDOMProxyInformation(ptr::null(), 0, Some(shadow_check_callback));
            SetDOMCallbacks(runtime.rt(), &DOM_CALLBACKS);
            // Pre barriers aren't working correctly at the moment
            DisableIncrementalGC(runtime.rt());
        }

        runtime
    }

    // 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()
    }

    pub fn get_cx(&self) -> *mut JSContext {
        self.js_runtime.cx()
    }

    /// Starts the script task. After calling this method, the script task will loop receiving
    /// messages on its port.
    pub fn start(&self) {
        while self.handle_msgs() {
            // Go on...
        }
    }

    /// Handle incoming control messages.
    fn handle_msgs(&self) -> bool {
        // Handle pending resize events.<commit_msg>Add preserveWrapperCallback (fixes #7218)<commit_after>/* 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/. */

//! The script task is the task that owns the DOM in memory, runs JavaScript, and spawns parsing
//! and layout tasks. It's in charge of processing events for all same-origin pages in a frame
//! tree, and manages the entire lifetime of pages in the frame tree from initial request to
//! teardown.
//!
//! Page loads follow a two-step process. When a request for a new page load is received, the
//! network request is initiated and the relevant data pertaining to the new page is stashed.
//! While the non-blocking request is ongoing, the script task is free to process further events,
//! noting when they pertain to ongoing loads (such as resizes/viewport adjustments). When the
//! initial response is received for an ongoing load, the second phase starts - the frame tree
//! entry is created, along with the Window and Document objects, and the appropriate parser
//! takes over the response body. Once parsing is complete, the document lifecycle for loading
//! a page runs its course and the script task returns to processing events in the main event
//! loop.

use devtools;
use devtools_traits::ScriptToDevtoolsControlMsg;
use devtools_traits::{DevtoolScriptControlMsg, DevtoolsPageInfo};
use document_loader::{DocumentLoader, LoadType, NotifierData};
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyState};
use dom::bindings::codegen::InheritTypes::{ElementCast, EventCast, EventTargetCast, NodeCast};
use dom::bindings::conversions::FromJSValConvertible;
use dom::bindings::conversions::StringificationBehavior;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, RootCollection, trace_roots};
use dom::bindings::js::{Root, RootCollectionPtr, RootedReference};
use dom::bindings::refcounted::{LiveDOMReferences, Trusted, TrustedReference, trace_refcounted_objects};
use dom::bindings::trace::{JSTraceable, RootedVec, trace_traceables};
use dom::bindings::utils::{DOM_CALLBACKS, WRAP_CALLBACKS};
use dom::document::{Document, DocumentProgressHandler, IsHTMLDocument};
use dom::document::{DocumentProgressTask, DocumentSource, MouseEventType};
use dom::element::Element;
use dom::event::{EventBubbles, EventCancelable};
use dom::node::{Node, NodeDamage, window_from_node};
use dom::servohtmlparser::{ParserContext, ServoHTMLParser};
use dom::uievent::UIEvent;
use dom::window::{ReflowReason, ScriptHelpers, Window};
use dom::worker::TrustedWorkerAddress;
use euclid::Rect;
use euclid::point::Point2D;
use hyper::header::{ContentType, HttpDate};
use hyper::header::{Headers, LastModified};
use hyper::method::Method;
use hyper::mime::{Mime, SubLevel, TopLevel};
use ipc_channel::ipc::{self, IpcSender};
use ipc_channel::router::ROUTER;
use js::glue::CollectServoSizes;
use js::jsapi::{DOMProxyShadowsResult, HandleId, HandleObject, RootedValue, SetDOMProxyInformation};
use js::jsapi::{DisableIncrementalGC, JS_AddExtraGCRootsTracer, JS_SetWrapObjectCallbacks};
use js::jsapi::{GCDescription, GCProgress, JSGCInvocationKind, SetGCSliceCallback};
use js::jsapi::{JSAutoRequest, JSGCStatus, JS_GetRuntime, JS_SetGCCallback, SetDOMCallbacks};
use js::jsapi::{JSContext, JSRuntime, JSTracer};
use js::jsapi::{JSObject, SetPreserveWrapperCallback};
use js::jsval::UndefinedValue;
use js::rust::Runtime;
use layout_interface::{ReflowQueryType};
use layout_interface::{self, LayoutChan, NewLayoutTaskInfo, ReflowGoal, ScriptLayoutChan};
use libc;
use mem::heap_size_of_eventtarget;
use msg::compositor_msg::{LayerId, ScriptToCompositorMsg};
use msg::constellation_msg::Msg as ConstellationMsg;
use msg::constellation_msg::{ConstellationChan, FocusType, LoadData};
use msg::constellation_msg::{MozBrowserEvent, PipelineExitType, PipelineId};
use msg::constellation_msg::{SubpageId, WindowSizeData, WorkerId};
use msg::webdriver_msg::WebDriverScriptCommand;
use net_traits::LoadData as NetLoadData;
use net_traits::image_cache_task::{ImageCacheChan, ImageCacheResult, ImageCacheTask};
use net_traits::storage_task::StorageTask;
use net_traits::{AsyncResponseTarget, ControlMsg, LoadConsumer, Metadata, ResourceTask};
use network_listener::NetworkListener;
use page::{Frame, IterablePage, Page};
use parse::html::{ParseContext, parse_html};
use profile_traits::mem::{self, OpaqueSender, Report, ReportKind, ReportsChan};
use profile_traits::time::{self, ProfilerCategory, profile};
use script_traits::CompositorEvent::{ClickEvent, ResizeEvent};
use script_traits::CompositorEvent::{KeyEvent, MouseMoveEvent};
use script_traits::CompositorEvent::{MouseDownEvent, MouseUpEvent};
use script_traits::{CompositorEvent, ConstellationControlMsg};
use script_traits::{InitialScriptState, MouseButton, NewLayoutInfo};
use script_traits::{OpaqueScriptLayoutChannel, ScriptState, ScriptTaskFactory};
use std::any::Any;
use std::borrow::ToOwned;
use std::cell::{Cell, RefCell};
use std::collections::HashSet;
use std::io::{Write, stdout};
use std::mem as std_mem;
use std::option::Option;
use std::ptr;
use std::rc::Rc;
use std::result::Result;
use std::sync::mpsc::{Receiver, Select, Sender, channel};
use std::sync::{Arc, Mutex};
use string_cache::Atom;
use time::{Tm, now};
use timers::TimerId;
use url::{Url, UrlParser};
use util::opts;
use util::str::DOMString;
use util::task::spawn_named_with_send_on_failure;
use util::task_state;
use webdriver_handlers;

thread_local!(pub static STACK_ROOTS: Cell<Option<RootCollectionPtr>> = Cell::new(None));
thread_local!(static SCRIPT_TASK_ROOT: RefCell<Option<*const ScriptTask>> = RefCell::new(None));

unsafe extern fn trace_rust_roots(tr: *mut JSTracer, _data: *mut libc::c_void) {
    SCRIPT_TASK_ROOT.with(|root| {
        if let Some(script_task) = *root.borrow() {
            (*script_task).trace(tr);
        }
    });

    trace_traceables(tr);
    trace_roots(tr);
}

/// A document load that is in the process of fetching the requested resource. Contains
/// data that will need to be present when the document and frame tree entry are created,
/// but is only easily available at initiation of the load and on a push basis (so some
/// data will be updated according to future resize events, viewport changes, etc.)
#[derive(JSTraceable)]
struct InProgressLoad {
    /// The pipeline which requested this load.
    pipeline_id: PipelineId,
    /// The parent pipeline and child subpage associated with this load, if any.
    parent_info: Option<(PipelineId, SubpageId)>,
    /// The current window size associated with this pipeline.
    window_size: Option<WindowSizeData>,
    /// Channel to the layout task associated with this pipeline.
    layout_chan: LayoutChan,
    /// The current viewport clipping rectangle applying to this pipeline, if any.
    clip_rect: Option<Rect<f32>>,
    /// The requested URL of the load.
    url: Url,
}

impl InProgressLoad {
    /// Create a new InProgressLoad object.
    fn new(id: PipelineId,
           parent_info: Option<(PipelineId, SubpageId)>,
           layout_chan: LayoutChan,
           window_size: Option<WindowSizeData>,
           url: Url) -> InProgressLoad {
        InProgressLoad {
            pipeline_id: id,
            parent_info: parent_info,
            layout_chan: layout_chan,
            window_size: window_size,
            clip_rect: None,
            url: url,
        }
    }
}

#[derive(Copy, Clone)]
pub enum TimerSource {
    FromWindow(PipelineId),
    FromWorker
}

pub trait Runnable {
    fn handler(self: Box<Self>);
}

pub trait MainThreadRunnable {
    fn handler(self: Box<Self>, script_task: &ScriptTask);
}

enum MixedMessage {
    FromConstellation(ConstellationControlMsg),
    FromScript(MainThreadScriptMsg),
    FromDevtools(DevtoolScriptControlMsg),
    FromImageCache(ImageCacheResult),
}

/// Common messages used to control the event loops in both the script and the worker
pub enum CommonScriptMsg {
    /// Requests that the script task measure its memory usage. The results are sent back via the
    /// supplied channel.
    CollectReports(ReportsChan),
    /// Fires a JavaScript timeout
    /// TimerSource must be FromWindow when dispatched to ScriptTask and
    /// must be FromWorker when dispatched to a DedicatedGlobalWorkerScope
    FireTimer(TimerSource, TimerId),
    /// A DOM object's last pinned reference was removed (dispatched to all tasks).
    RefcountCleanup(TrustedReference),
    /// Generic message that encapsulates event handling.
    RunnableMsg(ScriptTaskEventCategory, Box<Runnable + Send>),
}

#[derive(Clone, Copy, Debug, Eq, Hash, JSTraceable, PartialEq)]
pub enum ScriptTaskEventCategory {
    AttachLayout,
    ConstellationMsg,
    DevtoolsMsg,
    DocumentEvent,
    DomEvent,
    FileRead,
    ImageCacheMsg,
    InputEvent,
    NetworkEvent,
    Resize,
    ScriptEvent,
    UpdateReplacedElement,
    SetViewport,
    WebSocketEvent,
    WorkerEvent,
    XhrEvent,
}

/// Messages used to control the script event loop
pub enum MainThreadScriptMsg {
    /// Common variants associated with the script messages
    Common(CommonScriptMsg),
    /// Notify a document that all pending loads are complete.
    DocumentLoadsComplete(PipelineId),
    /// Notifies the script that a window associated with a particular pipeline
    /// should be closed (only dispatched to ScriptTask).
    ExitWindow(PipelineId),
    /// Generic message for running tasks in the ScriptTask
    MainThreadRunnableMsg(Box<MainThreadRunnable + Send>),
    /// Begins a content-initiated load on the specified pipeline (only
    /// dispatched to ScriptTask).
    Navigate(PipelineId, LoadData),
}

/// A cloneable interface for communicating with an event loop.
pub trait ScriptChan {
    /// Send a message to the associated event loop.
    fn send(&self, msg: CommonScriptMsg) -> Result<(), ()>;
    /// Clone this handle.
    fn clone(&self) -> Box<ScriptChan + Send>;
}

impl OpaqueSender<CommonScriptMsg> for Box<ScriptChan + Send> {
    fn send(&self, msg: CommonScriptMsg) {
        ScriptChan::send(&**self, msg).unwrap();
    }
}

/// An interface for receiving ScriptMsg values in an event loop. Used for synchronous DOM
/// APIs that need to abstract over multiple kinds of event loops (worker/main thread) with
/// different Receiver interfaces.
pub trait ScriptPort {
    fn recv(&self) -> CommonScriptMsg;
}

impl ScriptPort for Receiver<CommonScriptMsg> {
    fn recv(&self) -> CommonScriptMsg {
        self.recv().unwrap()
    }
}

impl ScriptPort for Receiver<MainThreadScriptMsg> {
    fn recv(&self) -> CommonScriptMsg {
        match self.recv().unwrap() {
            MainThreadScriptMsg::Common(script_msg) => script_msg,
            _ => panic!("unexpected main thread event message!")
        }
    }
}

impl ScriptPort for Receiver<(TrustedWorkerAddress, CommonScriptMsg)> {
    fn recv(&self) -> CommonScriptMsg {
        self.recv().unwrap().1
    }
}

impl ScriptPort for Receiver<(TrustedWorkerAddress, MainThreadScriptMsg)> {
    fn recv(&self) -> CommonScriptMsg {
        match self.recv().unwrap().1 {
            MainThreadScriptMsg::Common(script_msg) => script_msg,
            _ => panic!("unexpected main thread event message!")
        }
    }
}

/// Encapsulates internal communication of shared messages within the script task.
#[derive(JSTraceable)]
pub struct SendableMainThreadScriptChan(pub Sender<CommonScriptMsg>);

impl ScriptChan for SendableMainThreadScriptChan {
    fn send(&self, msg: CommonScriptMsg) -> Result<(), ()> {
        let SendableMainThreadScriptChan(ref chan) = *self;
        chan.send(msg).map_err(|_| ())
    }

    fn clone(&self) -> Box<ScriptChan + Send> {
        let SendableMainThreadScriptChan(ref chan) = *self;
        box SendableMainThreadScriptChan((*chan).clone())
    }
}

impl SendableMainThreadScriptChan {
    /// Creates a new script chan.
    pub fn new() -> (Receiver<CommonScriptMsg>, Box<SendableMainThreadScriptChan>) {
        let (chan, port) = channel();
        (port, box SendableMainThreadScriptChan(chan))
    }
}

/// Encapsulates internal communication of main thread messages within the script task.
#[derive(JSTraceable)]
pub struct MainThreadScriptChan(pub Sender<MainThreadScriptMsg>);

impl ScriptChan for MainThreadScriptChan {
    fn send(&self, msg: CommonScriptMsg) -> Result<(), ()> {
        let MainThreadScriptChan(ref chan) = *self;
        chan.send(MainThreadScriptMsg::Common(msg)).map_err(|_| ())
    }

    fn clone(&self) -> Box<ScriptChan + Send> {
        let MainThreadScriptChan(ref chan) = *self;
        box MainThreadScriptChan((*chan).clone())
    }
}

impl MainThreadScriptChan {
    /// Creates a new script chan.
    pub fn new() -> (Receiver<MainThreadScriptMsg>, Box<MainThreadScriptChan>) {
        let (chan, port) = channel();
        (port, box MainThreadScriptChan(chan))
    }
}

pub struct StackRootTLS;

impl StackRootTLS {
    pub fn new(roots: &RootCollection) -> StackRootTLS {
        STACK_ROOTS.with(|ref r| {
            r.set(Some(RootCollectionPtr(roots as *const _)))
        });
        StackRootTLS
    }
}

impl Drop for StackRootTLS {
    fn drop(&mut self) {
        STACK_ROOTS.with(|ref r| r.set(None));
    }
}


/// Information for an entire page. Pages are top-level browsing contexts and can contain multiple
/// frames.
#[derive(JSTraceable)]
// ScriptTask instances are rooted on creation, so this is okay
#[allow(unrooted_must_root)]
pub struct ScriptTask {
    /// A handle to the information pertaining to page layout
    page: DOMRefCell<Option<Rc<Page>>>,
    /// 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 task.
    image_cache_task: ImageCacheTask,
    /// A handle to the resource task. This is an `Arc` to avoid running out of file descriptors if
    /// there are many iframes.
    resource_task: Arc<ResourceTask>,
    /// A handle to the storage task.
    storage_task: StorageTask,

    /// The port on which the script task receives messages (load URL, exit, etc.)
    port: Receiver<MainThreadScriptMsg>,
    /// A channel to hand out to script task-based entities that need to be able to enqueue
    /// events in the event queue.
    chan: MainThreadScriptChan,

    /// A channel to hand out to tasks that need to respond to a message from the script task.
    control_chan: Sender<ConstellationControlMsg>,

    /// The port on which the constellation and layout tasks can communicate with the
    /// script task.
    control_port: Receiver<ConstellationControlMsg>,

    /// For communicating load url messages to the constellation
    constellation_chan: ConstellationChan,

    /// A handle to the compositor for communicating ready state messages.
    compositor: DOMRefCell<IpcSender<ScriptToCompositorMsg>>,

    /// The port on which we receive messages from the image cache
    image_cache_port: Receiver<ImageCacheResult>,

    /// The channel on which the image cache can send messages to ourself.
    image_cache_channel: ImageCacheChan,

    /// For providing contact with the time profiler.
    time_profiler_chan: time::ProfilerChan,

    /// For providing contact with the memory profiler.
    mem_profiler_chan: mem::ProfilerChan,

    /// For providing instructions to an optional devtools server.
    devtools_chan: Option<IpcSender<ScriptToDevtoolsControlMsg>>,
    /// For receiving commands from an optional devtools server. Will be ignored if
    /// no such server exists.
    devtools_port: Receiver<DevtoolScriptControlMsg>,
    devtools_sender: IpcSender<DevtoolScriptControlMsg>,

    /// The JavaScript runtime.
    js_runtime: Rc<Runtime>,

    mouse_over_targets: DOMRefCell<Vec<JS<Node>>>,

    /// List of pipelines that have been owned and closed by this script task.
    closed_pipelines: RefCell<HashSet<PipelineId>>,
}

/// In the event of task failure, all data on the stack runs its destructor. However, there
/// are no reachable, owning pointers to the DOM memory, so it never gets freed by default
/// when the script task fails. The ScriptMemoryFailsafe uses the destructor bomb pattern
/// to forcibly tear down the JS compartments for pages associated with the failing ScriptTask.
struct ScriptMemoryFailsafe<'a> {
    owner: Option<&'a ScriptTask>,
}

impl<'a> ScriptMemoryFailsafe<'a> {
    fn neuter(&mut self) {
        self.owner = None;
    }

    fn new(owner: &'a ScriptTask) -> ScriptMemoryFailsafe<'a> {
        ScriptMemoryFailsafe {
            owner: Some(owner),
        }
    }
}

impl<'a> Drop for ScriptMemoryFailsafe<'a> {
    #[allow(unrooted_must_root)]
    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.r().clear_js_runtime_for_script_deallocation();
                    }
                }
            }
            None => (),
        }
    }
}

impl ScriptTaskFactory for ScriptTask {
    fn create_layout_channel(_phantom: Option<&mut ScriptTask>) -> OpaqueScriptLayoutChannel {
        let (chan, port) = channel();
        ScriptLayoutChan::new(chan, port)
    }

    fn clone_layout_channel(_phantom: Option<&mut ScriptTask>, pair: &OpaqueScriptLayoutChannel) -> Box<Any + Send> {
        box pair.sender() as Box<Any + Send>
    }

    fn create(_phantom: Option<&mut ScriptTask>,
              state: InitialScriptState,
              layout_chan: &OpaqueScriptLayoutChannel,
              load_data: LoadData) {
        let ConstellationChan(const_chan) = state.constellation_chan.clone();
        let (script_chan, script_port) = channel();
        let layout_chan = LayoutChan(layout_chan.sender());
        let failure_info = state.failure_info;
        spawn_named_with_send_on_failure(format!("ScriptTask {:?}", state.id), task_state::SCRIPT, move || {
            let roots = RootCollection::new();
            let _stack_roots_tls = StackRootTLS::new(&roots);
            let chan = MainThreadScriptChan(script_chan);
            let channel_for_reporter = chan.clone();
            let id = state.id;
            let parent_info = state.parent_info;
            let mem_profiler_chan = state.mem_profiler_chan.clone();
            let window_size = state.window_size;
            let script_task = ScriptTask::new(state,
                                              script_port,
                                              chan);

            SCRIPT_TASK_ROOT.with(|root| {
                *root.borrow_mut() = Some(&script_task as *const _);
            });

            let mut failsafe = ScriptMemoryFailsafe::new(&script_task);

            let new_load = InProgressLoad::new(id, parent_info, layout_chan, window_size,
                                               load_data.url.clone());
            script_task.start_page_load(new_load, load_data);

            let reporter_name = format!("script-reporter-{}", id.0);
            mem_profiler_chan.run_with_memory_reporting(|| {
                script_task.start();
            }, reporter_name, channel_for_reporter, CommonScriptMsg::CollectReports);

            // This must always be the very last operation performed before the task completes
            failsafe.neuter();
        }, ConstellationMsg::Failure(failure_info), const_chan);
    }
}

thread_local!(static GC_CYCLE_START: Cell<Option<Tm>> = Cell::new(None));
thread_local!(static GC_SLICE_START: Cell<Option<Tm>> = Cell::new(None));

unsafe extern "C" fn gc_slice_callback(_rt: *mut JSRuntime, progress: GCProgress, desc: *const GCDescription) {
    match progress {
        GCProgress::GC_CYCLE_BEGIN => {
            GC_CYCLE_START.with(|start| {
                start.set(Some(now()));
                println!("GC cycle began");
            })
        },
        GCProgress::GC_SLICE_BEGIN => {
            GC_SLICE_START.with(|start| {
                start.set(Some(now()));
                println!("GC slice began");
            })
        },
        GCProgress::GC_SLICE_END => {
            GC_SLICE_START.with(|start| {
                let dur = now() - start.get().unwrap();
                start.set(None);
                println!("GC slice ended: duration={}", dur);
            })
        },
        GCProgress::GC_CYCLE_END => {
            GC_CYCLE_START.with(|start| {
                let dur = now() - start.get().unwrap();
                start.set(None);
                println!("GC cycle ended: duration={}", dur);
            })
        },
    };
    if !desc.is_null() {
        let desc: &GCDescription = &*desc;
        let invocationKind = match desc.invocationKind_ {
            JSGCInvocationKind::GC_NORMAL => "GC_NORMAL",
            JSGCInvocationKind::GC_SHRINK => "GC_SHRINK",
        };
        println!("  isCompartment={}, invocationKind={}", desc.isCompartment_, invocationKind);
    }
    let _ = stdout().flush();
}

unsafe extern "C" fn debug_gc_callback(_rt: *mut JSRuntime, status: JSGCStatus, _data: *mut libc::c_void) {
    match status {
        JSGCStatus::JSGC_BEGIN => task_state::enter(task_state::IN_GC),
        JSGCStatus::JSGC_END   => task_state::exit(task_state::IN_GC),
    }
}

unsafe extern "C" fn shadow_check_callback(_cx: *mut JSContext,
    _object: HandleObject, _id: HandleId) -> DOMProxyShadowsResult {
    // XXX implement me
    DOMProxyShadowsResult::ShadowCheckFailed
}

impl ScriptTask {
    pub fn page_fetch_complete(id: PipelineId, subpage: Option<SubpageId>, metadata: Metadata)
                               -> Option<Root<ServoHTMLParser>> {
        SCRIPT_TASK_ROOT.with(|root| {
            let script_task = unsafe { &*root.borrow().unwrap() };
            script_task.handle_page_fetch_complete(id, subpage, metadata)
        })
    }

    pub fn parsing_complete(id: PipelineId) {
        SCRIPT_TASK_ROOT.with(|root| {
            let script_task = unsafe { &*root.borrow().unwrap() };
            script_task.handle_parsing_complete(id);
        });
    }

    pub fn process_event(msg: CommonScriptMsg) {
        SCRIPT_TASK_ROOT.with(|root| {
            if let Some(script_task) = *root.borrow() {
                let script_task = unsafe { &*script_task };
                script_task.handle_msg_from_script(MainThreadScriptMsg::Common(msg));
            }
        });
    }

    /// Creates a new script task.
    pub fn new(state: InitialScriptState,
               port: Receiver<MainThreadScriptMsg>,
               chan: MainThreadScriptChan)
               -> ScriptTask {
        let runtime = ScriptTask::new_rt_and_cx();

        unsafe {
            JS_SetWrapObjectCallbacks(runtime.rt(),
                                      &WRAP_CALLBACKS);
        }

        // Ask the router to proxy IPC messages from the devtools to us.
        let (ipc_devtools_sender, ipc_devtools_receiver) = ipc::channel().unwrap();
        let devtools_port = ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_devtools_receiver);

        // Ask the router to proxy IPC messages from the image cache task to us.
        let (ipc_image_cache_channel, ipc_image_cache_port) = ipc::channel().unwrap();
        let image_cache_port =
            ROUTER.route_ipc_receiver_to_new_mpsc_receiver(ipc_image_cache_port);

        ScriptTask {
            page: DOMRefCell::new(None),
            incomplete_loads: DOMRefCell::new(vec!()),

            image_cache_task: state.image_cache_task,
            image_cache_channel: ImageCacheChan(ipc_image_cache_channel),
            image_cache_port: image_cache_port,

            resource_task: Arc::new(state.resource_task),
            storage_task: state.storage_task,

            port: port,
            chan: chan,
            control_chan: state.control_chan,
            control_port: state.control_port,
            constellation_chan: state.constellation_chan,
            compositor: DOMRefCell::new(state.compositor),
            time_profiler_chan: state.time_profiler_chan,
            mem_profiler_chan: state.mem_profiler_chan,

            devtools_chan: state.devtools_chan,
            devtools_port: devtools_port,
            devtools_sender: ipc_devtools_sender,

            js_runtime: Rc::new(runtime),
            mouse_over_targets: DOMRefCell::new(vec!()),
            closed_pipelines: RefCell::new(HashSet::new()),
        }
    }

    pub fn new_rt_and_cx() -> Runtime {
        LiveDOMReferences::initialize();
        let runtime = Runtime::new();

        unsafe {
            JS_AddExtraGCRootsTracer(runtime.rt(), Some(trace_rust_roots), ptr::null_mut());
            JS_AddExtraGCRootsTracer(runtime.rt(), Some(trace_refcounted_objects), ptr::null_mut());
        }

        // Needed for debug assertions about whether GC is running.
        if cfg!(debug_assertions) {
            unsafe {
                JS_SetGCCallback(runtime.rt(), Some(debug_gc_callback), ptr::null_mut());
            }
        }
        if opts::get().gc_profile {
            unsafe {
                SetGCSliceCallback(runtime.rt(), Some(gc_slice_callback));
            }
        }

        unsafe {
            unsafe extern "C" fn empty_wrapper_callback(_: *mut JSContext, _: *mut JSObject) -> u8 { 1 }
            SetDOMProxyInformation(ptr::null(), 0, Some(shadow_check_callback));
            SetDOMCallbacks(runtime.rt(), &DOM_CALLBACKS);
            SetPreserveWrapperCallback(runtime.rt(), Some(empty_wrapper_callback));
            // Pre barriers aren't working correctly at the moment
            DisableIncrementalGC(runtime.rt());
        }

        runtime
    }

    // 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()
    }

    pub fn get_cx(&self) -> *mut JSContext {
        self.js_runtime.cx()
    }

    /// Starts the script task. After calling this method, the script task will loop receiving
    /// messages on its port.
    pub fn start(&self) {
        while self.handle_msgs() {
            // Go on...
        }
    }

    /// Handle incoming control messages.
    fn handle_msgs(&self) -> bool {
        // Handle pending resize events.
{'fraction_non_alphanumeric': 0.10726060100799575, 'fraction_numerical': 0.0005305240820038653, 'mean_word_length': 3.2115384615384617, 'pattern_counts': {'":': 0, '<': 153, '<?xml version=': 0, '>': 232, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 16, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4531672', 'n_tokens_mistral': 15412, 'n_tokens_neox': 15040, 'n_words': 4082}