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

use dom::document::AbstractDocument;
use dom::element::*;
use dom::htmlelement::HTMLElement;
use dom::htmlheadingelement::{Heading1, Heading2, Heading3, Heading4, Heading5, Heading6};
use dom::htmliframeelement::IFrameSize;
use dom::htmlformelement::HTMLFormElement;
use dom::node::{AbstractNode, ElementNodeTypeId, Node, ScriptView};
use dom::types::*;
use html::cssparse::{InlineProvenance, StylesheetProvenance, UrlProvenance, spawn_css_parser};
use js::jsapi::JSContext;
use style::Stylesheet;
use script_task::page_from_context;

use std::cast;
use std::cell::Cell;
use std::comm;
use std::comm::{Port, SharedChan};
use std::str;
use std::str::eq_slice;
use std::from_str::FromStr;
use hubbub::hubbub;
use servo_msg::constellation_msg::{ConstellationChan, SubpageId};
use servo_net::image_cache_task::ImageCacheTask;
use servo_net::resource_task::{Load, Payload, Done, ResourceTask, load_whole_resource};
use servo_util::tree::{TreeNodeRef, ElementLike};
use servo_util::url::make_url;
use extra::future::Future;
use extra::url::Url;
use geom::size::Size2D;

macro_rules! handle_element(
    ($cx: expr,
     $document: expr,
     $tag: expr,
     $string: expr,
     $type_id: expr,
     $ctor: ident,
     [ $(($field:ident : $field_init:expr)),* ]) => (
        handle_element_base!(htmlelement, HTMLElement,
                             $cx, $document, $tag, $string, $type_id, $ctor,
                             [$(($field:$field_init)),*]);
    )
)
macro_rules! handle_htmlelement(
    ($cx: expr,
     $document: expr,
     $tag: expr,
     $string: expr,
     $type_id: expr,
     $ctor: ident) => (
        handle_element_base!(element, Element,
                             $cx, $document, $tag, $string, $type_id, $ctor, []);
    )
)
macro_rules! handle_htmlmediaelement(
    ($cx: expr,
     $document: expr,
     $tag: expr,
     $string: expr,
     $type_id: expr,
     $ctor: ident) => (
        handle_element_base!(htmlmediaelement, HTMLMediaElement,
                             $cx, $document, $tag, $string, $type_id, $ctor, []);
    )
)
macro_rules! handle_htmltablecellelement(
    ($cx: expr,
     $document: expr,
     $tag: expr,
     $string: expr,
     $type_id: expr,
     $ctor: ident) => (
        handle_element_base!(htmltablecellelement, HTMLTableCellElement,
                             $cx, $document, $tag, $string, $type_id, $ctor, []);
    )
)
macro_rules! handle_element_base(
    ($parent: ident,
     $parent_init: ident,
     $cx: expr,
     $document: expr,
     $tag: expr,
     $string: expr,
     $type_id: expr,
     $ctor: ident,
     [ $(($field:ident : $field_init:expr)),* ]) => (
        if eq_slice($tag, $string) {
            let _element = @$ctor {
                $parent: $parent_init::new($type_id, ($tag).to_str(), $document),
                $(
                    $field: $field_init,
                )*
            };
            return unsafe { Node::as_abstract_node($cx, _element) };
        }
    )
)


pub struct JSFile {
    data: ~str,
    url: Url
}

type JSResult = ~[JSFile];

enum CSSMessage {
    CSSTaskNewFile(StylesheetProvenance),
    CSSTaskExit   
}

enum JSMessage {
    JSTaskNewFile(Url),
    JSTaskNewInlineScript(~str, Url),
    JSTaskExit
}

/// Messages generated by the HTML parser upon discovery of additional resources
pub enum HtmlDiscoveryMessage {
    HtmlDiscoveredStyle(Stylesheet),
    HtmlDiscoveredIFrame((Url, SubpageId, Future<Size2D<uint>>, bool)),
    HtmlDiscoveredScript(JSResult)
}

pub struct HtmlParserResult {
    root: AbstractNode<ScriptView>,
    discovery_port: Port<HtmlDiscoveryMessage>,
    url: Url,
}

trait NodeWrapping {
    unsafe fn to_hubbub_node(self) -> hubbub::NodeDataPtr;
    unsafe fn from_hubbub_node(n: hubbub::NodeDataPtr) -> Self;
}

impl NodeWrapping for AbstractNode<ScriptView> {
    unsafe fn to_hubbub_node(self) -> hubbub::NodeDataPtr {
        cast::transmute(self)
    }
    unsafe fn from_hubbub_node(n: hubbub::NodeDataPtr) -> AbstractNode<ScriptView> {
        cast::transmute(n)
    }
}

/**
Runs a task that coordinates parsing links to css stylesheets.

This function should be spawned in a separate task and spins waiting
for the html builder to find links to css stylesheets and sends off
tasks to parse each link.  When the html process finishes, it notifies
the listener, who then collects the css rules from each task it
spawned, collates them, and sends them to the given result channel.

# Arguments

* `to_parent` - A channel on which to send back the full set of rules.
* `from_parent` - A port on which to receive new links.

*/
fn css_link_listener(to_parent: SharedChan<HtmlDiscoveryMessage>,
                     from_parent: Port<CSSMessage>,
                     resource_task: ResourceTask) {
    let mut result_vec = ~[];

    loop {
        match from_parent.recv() {
            CSSTaskNewFile(provenance) => {
                result_vec.push(spawn_css_parser(provenance, resource_task.clone()));
            }
            CSSTaskExit => {
                break;
            }
        }
    }

    // Send the sheets back in order
    // FIXME: Shouldn't wait until after we've recieved CSSTaskExit to start sending these
    for port in result_vec.iter() {
        to_parent.send(HtmlDiscoveredStyle(port.recv()));
    }
}

fn js_script_listener(to_parent: SharedChan<HtmlDiscoveryMessage>,
                      from_parent: Port<JSMessage>,
                      resource_task: ResourceTask) {
    let mut result_vec = ~[];

    loop {
        match from_parent.recv() {
            JSTaskNewFile(url) => {
                match load_whole_resource(&resource_task, url.clone()) {
                    Err(_) => {
                        error!("error loading script %s", url.to_str());
                    }
                    Ok((metadata, bytes)) => {
                        result_vec.push(JSFile {
                            data: str::from_utf8(bytes),
                            url: metadata.final_url,
                        });
                    }
                }
            }
            JSTaskNewInlineScript(data, url) => {
                result_vec.push(JSFile { data: data, url: url });
            }
            JSTaskExit => {
                break;
            }
        }
    }

    to_parent.send(HtmlDiscoveredScript(result_vec));
}

// Silly macros to handle constructing      DOM nodes. This produces bad code and should be optimized
// via atomization (issue #85).

pub fn build_element_from_tag(cx: *JSContext, tag: &str, document: AbstractDocument) -> AbstractNode<ScriptView> {
    // TODO (Issue #85): use atoms
    handle_element!(cx, document, tag, "a",       HTMLAnchorElementTypeId, HTMLAnchorElement, []);
    handle_element!(cx, document, tag, "applet",  HTMLAppletElementTypeId, HTMLAppletElement, []);
    handle_element!(cx, document, tag, "area",    HTMLAreaElementTypeId, HTMLAreaElement, []);
    handle_element!(cx, document, tag, "base",    HTMLBaseElementTypeId, HTMLBaseElement, []);
    handle_element!(cx, document, tag, "br",      HTMLBRElementTypeId, HTMLBRElement, []);
    handle_element!(cx, document, tag, "body",    HTMLBodyElementTypeId, HTMLBodyElement, []);
    handle_element!(cx, document, tag, "button",  HTMLButtonElementTypeId, HTMLButtonElement, []);
    handle_element!(cx, document, tag, "canvas",  HTMLCanvasElementTypeId, HTMLCanvasElement, []);
    handle_element!(cx, document, tag, "data",    HTMLDataElementTypeId, HTMLDataElement, []);
    handle_element!(cx, document, tag, "datalist",HTMLDataListElementTypeId, HTMLDataListElement, []);
    handle_element!(cx, document, tag, "directory",HTMLDirectoryElementTypeId, HTMLDirectoryElement, []);<commit_msg>auto merge of #1160 : saneyuki/servo/dir, r=jdm<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/. */

use dom::document::AbstractDocument;
use dom::element::*;
use dom::htmlelement::HTMLElement;
use dom::htmlheadingelement::{Heading1, Heading2, Heading3, Heading4, Heading5, Heading6};
use dom::htmliframeelement::IFrameSize;
use dom::htmlformelement::HTMLFormElement;
use dom::node::{AbstractNode, ElementNodeTypeId, Node, ScriptView};
use dom::types::*;
use html::cssparse::{InlineProvenance, StylesheetProvenance, UrlProvenance, spawn_css_parser};
use js::jsapi::JSContext;
use style::Stylesheet;
use script_task::page_from_context;

use std::cast;
use std::cell::Cell;
use std::comm;
use std::comm::{Port, SharedChan};
use std::str;
use std::str::eq_slice;
use std::from_str::FromStr;
use hubbub::hubbub;
use servo_msg::constellation_msg::{ConstellationChan, SubpageId};
use servo_net::image_cache_task::ImageCacheTask;
use servo_net::resource_task::{Load, Payload, Done, ResourceTask, load_whole_resource};
use servo_util::tree::{TreeNodeRef, ElementLike};
use servo_util::url::make_url;
use extra::future::Future;
use extra::url::Url;
use geom::size::Size2D;

macro_rules! handle_element(
    ($cx: expr,
     $document: expr,
     $tag: expr,
     $string: expr,
     $type_id: expr,
     $ctor: ident,
     [ $(($field:ident : $field_init:expr)),* ]) => (
        handle_element_base!(htmlelement, HTMLElement,
                             $cx, $document, $tag, $string, $type_id, $ctor,
                             [$(($field:$field_init)),*]);
    )
)
macro_rules! handle_htmlelement(
    ($cx: expr,
     $document: expr,
     $tag: expr,
     $string: expr,
     $type_id: expr,
     $ctor: ident) => (
        handle_element_base!(element, Element,
                             $cx, $document, $tag, $string, $type_id, $ctor, []);
    )
)
macro_rules! handle_htmlmediaelement(
    ($cx: expr,
     $document: expr,
     $tag: expr,
     $string: expr,
     $type_id: expr,
     $ctor: ident) => (
        handle_element_base!(htmlmediaelement, HTMLMediaElement,
                             $cx, $document, $tag, $string, $type_id, $ctor, []);
    )
)
macro_rules! handle_htmltablecellelement(
    ($cx: expr,
     $document: expr,
     $tag: expr,
     $string: expr,
     $type_id: expr,
     $ctor: ident) => (
        handle_element_base!(htmltablecellelement, HTMLTableCellElement,
                             $cx, $document, $tag, $string, $type_id, $ctor, []);
    )
)
macro_rules! handle_element_base(
    ($parent: ident,
     $parent_init: ident,
     $cx: expr,
     $document: expr,
     $tag: expr,
     $string: expr,
     $type_id: expr,
     $ctor: ident,
     [ $(($field:ident : $field_init:expr)),* ]) => (
        if eq_slice($tag, $string) {
            let _element = @$ctor {
                $parent: $parent_init::new($type_id, ($tag).to_str(), $document),
                $(
                    $field: $field_init,
                )*
            };
            return unsafe { Node::as_abstract_node($cx, _element) };
        }
    )
)


pub struct JSFile {
    data: ~str,
    url: Url
}

type JSResult = ~[JSFile];

enum CSSMessage {
    CSSTaskNewFile(StylesheetProvenance),
    CSSTaskExit   
}

enum JSMessage {
    JSTaskNewFile(Url),
    JSTaskNewInlineScript(~str, Url),
    JSTaskExit
}

/// Messages generated by the HTML parser upon discovery of additional resources
pub enum HtmlDiscoveryMessage {
    HtmlDiscoveredStyle(Stylesheet),
    HtmlDiscoveredIFrame((Url, SubpageId, Future<Size2D<uint>>, bool)),
    HtmlDiscoveredScript(JSResult)
}

pub struct HtmlParserResult {
    root: AbstractNode<ScriptView>,
    discovery_port: Port<HtmlDiscoveryMessage>,
    url: Url,
}

trait NodeWrapping {
    unsafe fn to_hubbub_node(self) -> hubbub::NodeDataPtr;
    unsafe fn from_hubbub_node(n: hubbub::NodeDataPtr) -> Self;
}

impl NodeWrapping for AbstractNode<ScriptView> {
    unsafe fn to_hubbub_node(self) -> hubbub::NodeDataPtr {
        cast::transmute(self)
    }
    unsafe fn from_hubbub_node(n: hubbub::NodeDataPtr) -> AbstractNode<ScriptView> {
        cast::transmute(n)
    }
}

/**
Runs a task that coordinates parsing links to css stylesheets.

This function should be spawned in a separate task and spins waiting
for the html builder to find links to css stylesheets and sends off
tasks to parse each link.  When the html process finishes, it notifies
the listener, who then collects the css rules from each task it
spawned, collates them, and sends them to the given result channel.

# Arguments

* `to_parent` - A channel on which to send back the full set of rules.
* `from_parent` - A port on which to receive new links.

*/
fn css_link_listener(to_parent: SharedChan<HtmlDiscoveryMessage>,
                     from_parent: Port<CSSMessage>,
                     resource_task: ResourceTask) {
    let mut result_vec = ~[];

    loop {
        match from_parent.recv() {
            CSSTaskNewFile(provenance) => {
                result_vec.push(spawn_css_parser(provenance, resource_task.clone()));
            }
            CSSTaskExit => {
                break;
            }
        }
    }

    // Send the sheets back in order
    // FIXME: Shouldn't wait until after we've recieved CSSTaskExit to start sending these
    for port in result_vec.iter() {
        to_parent.send(HtmlDiscoveredStyle(port.recv()));
    }
}

fn js_script_listener(to_parent: SharedChan<HtmlDiscoveryMessage>,
                      from_parent: Port<JSMessage>,
                      resource_task: ResourceTask) {
    let mut result_vec = ~[];

    loop {
        match from_parent.recv() {
            JSTaskNewFile(url) => {
                match load_whole_resource(&resource_task, url.clone()) {
                    Err(_) => {
                        error!("error loading script %s", url.to_str());
                    }
                    Ok((metadata, bytes)) => {
                        result_vec.push(JSFile {
                            data: str::from_utf8(bytes),
                            url: metadata.final_url,
                        });
                    }
                }
            }
            JSTaskNewInlineScript(data, url) => {
                result_vec.push(JSFile { data: data, url: url });
            }
            JSTaskExit => {
                break;
            }
        }
    }

    to_parent.send(HtmlDiscoveredScript(result_vec));
}

// Silly macros to handle constructing      DOM nodes. This produces bad code and should be optimized
// via atomization (issue #85).

pub fn build_element_from_tag(cx: *JSContext, tag: &str, document: AbstractDocument) -> AbstractNode<ScriptView> {
    // TODO (Issue #85): use atoms
    handle_element!(cx, document, tag, "a",       HTMLAnchorElementTypeId, HTMLAnchorElement, []);
    handle_element!(cx, document, tag, "applet",  HTMLAppletElementTypeId, HTMLAppletElement, []);
    handle_element!(cx, document, tag, "area",    HTMLAreaElementTypeId, HTMLAreaElement, []);
    handle_element!(cx, document, tag, "base",    HTMLBaseElementTypeId, HTMLBaseElement, []);
    handle_element!(cx, document, tag, "br",      HTMLBRElementTypeId, HTMLBRElement, []);
    handle_element!(cx, document, tag, "body",    HTMLBodyElementTypeId, HTMLBodyElement, []);
    handle_element!(cx, document, tag, "button",  HTMLButtonElementTypeId, HTMLButtonElement, []);
    handle_element!(cx, document, tag, "canvas",  HTMLCanvasElementTypeId, HTMLCanvasElement, []);
    handle_element!(cx, document, tag, "data",    HTMLDataElementTypeId, HTMLDataElement, []);
    handle_element!(cx, document, tag, "datalist",HTMLDataListElementTypeId, HTMLDataListElement, []);
    handle_element!(cx, document, tag, "dir",     HTMLDirectoryElementTypeId, HTMLDirectoryElement, []);
{'fraction_non_alphanumeric': 0.12340398767218064, 'fraction_numerical': 0.002390087426882194, 'mean_word_length': 2.8667315175097277, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 59, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4257105', 'n_tokens_mistral': 4981, 'n_tokens_neox': 4870, 'n_words': 1170}