<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::attr::{Attr, AttrValue};
use dom::attr::AttrHelpers;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding;
use dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast};
use dom::bindings::codegen::InheritTypes::{HTMLTextAreaElementDerived, HTMLFieldSetElementDerived};
use dom::bindings::codegen::InheritTypes::{KeyboardEventCast, TextDerived};
use dom::bindings::js::{JSRef, LayoutJS, Temporary, OptionalRootable};
use dom::document::{Document, DocumentHelpers};
use dom::element::{Element, AttributeHandlers};
use dom::event::Event;
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::element::ElementTypeId;
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
use dom::htmlformelement::FormControl;
use dom::keyboardevent::KeyboardEvent;
use dom::node::{DisabledStateHelpers, Node, NodeHelpers, NodeDamage, NodeTypeId};
use dom::node::{document_from_node};
use textinput::{TextInput, Lines, KeyReaction};
use dom::virtualmethods::VirtualMethods;

use util::str::DOMString;
use string_cache::Atom;

use std::borrow::ToOwned;
use std::cell::Cell;

#[dom_struct]
pub struct HTMLTextAreaElement {
    htmlelement: HTMLElement,
    textinput: DOMRefCell<TextInput>,
    cols: Cell<u32>,
    rows: Cell<u32>,

    // https://html.spec.whatwg.org/multipage/forms.html#concept-textarea-dirty
    value_changed: Cell<bool>,
}

impl HTMLTextAreaElementDerived for EventTarget {
    fn is_htmltextareaelement(&self) -> bool {
        *self.type_id() == EventTargetTypeId::Node(NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement)))
    }
}

pub trait LayoutHTMLTextAreaElementHelpers {
    unsafe fn get_value_for_layout(self) -> String;
}

pub trait RawLayoutHTMLTextAreaElementHelpers {
    unsafe fn get_cols_for_layout(&self) -> u32;
    unsafe fn get_rows_for_layout(&self) -> u32;
}

impl LayoutHTMLTextAreaElementHelpers for LayoutJS<HTMLTextAreaElement> {
    #[allow(unrooted_must_root)]
    unsafe fn get_value_for_layout(self) -> String {
        (*self.unsafe_get()).textinput.borrow_for_layout().get_content()
    }
}

impl RawLayoutHTMLTextAreaElementHelpers for HTMLTextAreaElement {
    #[allow(unrooted_must_root)]
    unsafe fn get_cols_for_layout(&self) -> u32 {
        self.cols.get()
    }

    #[allow(unrooted_must_root)]
    unsafe fn get_rows_for_layout(&self) -> u32 {
        self.rows.get()
    }
}

static DEFAULT_COLS: u32 = 20;
static DEFAULT_ROWS: u32 = 2;

impl HTMLTextAreaElement {
    fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> HTMLTextAreaElement {
        HTMLTextAreaElement {
            htmlelement: HTMLElement::new_inherited(HTMLElementTypeId::HTMLTextAreaElement, localName, prefix, document),
            textinput: DOMRefCell::new(TextInput::new(Lines::Multiple, "".to_owned())),
            cols: Cell::new(DEFAULT_COLS),
            rows: Cell::new(DEFAULT_ROWS),
            value_changed: Cell::new(false),
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> Temporary<HTMLTextAreaElement> {
        let element = HTMLTextAreaElement::new_inherited(localName, prefix, document);
        Node::reflect_node(box element, document, HTMLTextAreaElementBinding::Wrap)
    }
}

impl<'a> HTMLTextAreaElementMethods for JSRef<'a, HTMLTextAreaElement> {
    // TODO A few of these attributes have default values and additional
    // constraints

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-cols
    make_uint_getter!(Cols);

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-cols
    make_uint_setter!(SetCols, "cols");

    // http://www.whatwg.org/html/#dom-fe-disabled
    make_bool_getter!(Disabled);

    // http://www.whatwg.org/html/#dom-fe-disabled
    make_bool_setter!(SetDisabled, "disabled");

    // https://html.spec.whatwg.org/multipage/forms.html#attr-fe-name
    make_getter!(Name);

    // https://html.spec.whatwg.org/multipage/forms.html#attr-fe-name
    make_setter!(SetName, "name");

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-placeholder
    make_getter!(Placeholder);

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-placeholder
    make_setter!(SetPlaceholder, "placeholder");

    // https://html.spec.whatwg.org/multipage/forms.html#attr-textarea-readonly
    make_bool_getter!(ReadOnly);

    // https://html.spec.whatwg.org/multipage/forms.html#attr-textarea-readonly
    make_bool_setter!(SetReadOnly, "readonly");

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-required
    make_bool_getter!(Required);

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-required
    make_bool_setter!(SetRequired, "required");

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-rows
    make_uint_getter!(Rows);

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-rows
    make_uint_setter!(SetRows, "rows");

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-wrap
    make_getter!(Wrap);

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-wrap
    make_setter!(SetWrap, "wrap");

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-type
    fn Type(self) -> DOMString {
        "textarea".to_owned()
    }

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-defaultvalue
    fn DefaultValue(self) -> DOMString {
        let node: JSRef<Node> = NodeCast::from_ref(self);
        node.GetTextContent().unwrap()
    }

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-defaultvalue
    fn SetDefaultValue(self, value: DOMString) {
        let node: JSRef<Node> = NodeCast::from_ref(self);
        node.SetTextContent(Some(value));

        // if the element's dirty value flag is false, then the element's
        // raw value must be set to the value of the element's textContent IDL attribute
        if !self.value_changed.get() {
            self.reset();
        }
    }

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-value
    fn Value(self) -> DOMString {
        self.textinput.borrow().get_content()
    }

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-value
    fn SetValue(self, value: DOMString) {
        // TODO move the cursor to the end of the field
        self.textinput.borrow_mut().set_content(value);
        self.value_changed.set(true);
        self.force_relayout();
    }
}

pub trait HTMLTextAreaElementHelpers {
    fn mutable(self) -> bool;
    fn reset(self);
}

impl<'a> HTMLTextAreaElementHelpers for JSRef<'a, HTMLTextAreaElement> {
    // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-mutable
    fn mutable(self) -> bool {
        // https://html.spec.whatwg.org/multipage/forms.html#the-textarea-element:concept-fe-mutable
        !(self.Disabled() || self.ReadOnly())
    }
    fn reset(self) {
        // https://html.spec.whatwg.org/multipage/forms.html#the-textarea-element:concept-form-reset-control
        self.SetValue(self.DefaultValue());
        self.value_changed.set(false);
    }
}

trait PrivateHTMLTextAreaElementHelpers {
    fn force_relayout(self);
}

impl<'a> PrivateHTMLTextAreaElementHelpers for JSRef<'a, HTMLTextAreaElement> {
    fn force_relayout(self) {
        let doc = document_from_node(self).root();
        let node: JSRef<Node> = NodeCast::from_ref(self);
        doc.r().content_changed(node, NodeDamage::OtherNodeDamage)
    }
}

impl<'a> VirtualMethods for JSRef<'a, HTMLTextAreaElement> {
    fn super_type<'b>(&'b self) -> Option<&'b VirtualMethods> {
        let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_borrowed_ref(self);
        Some(htmlelement as &VirtualMethods)
    }

    fn after_set_attr(&self, attr: JSRef<Attr>) {
        if let Some(ref s) = self.super_type() {
            s.after_set_attr(attr);
        }

        match attr.local_name() {
            &atom!("disabled") => {
                let node: JSRef<Node> = NodeCast::from_ref(*self);
                node.set_disabled_state(true);
                node.set_enabled_state(false);
            },
            &atom!("cols") => {
                match *attr.value() {
                    AttrValue::UInt(_, value) => self.cols.set(value),
                    _ => panic!("Expected an AttrValue::UInt"),
                }
            },
            &atom!("rows") => {
                match *attr.value() {
                    AttrValue::UInt(_, value) => self.rows.set(value),
                    _ => panic!("Expected an AttrValue::UInt"),
                }
            },
            _ => ()
        }
    }

    fn before_remove_attr(&self, attr: JSRef<Attr>) {
        if let Some(ref s) = self.super_type() {
            s.before_remove_attr(attr);
        }

        match attr.local_name() {
            &atom!("disabled") => {
                let node: JSRef<Node> = NodeCast::from_ref(*self);
                node.set_disabled_state(false);
                node.set_enabled_state(true);
                node.check_ancestors_disabled_state_for_form_control();
            },
            &atom!("cols") => {
                self.cols.set(DEFAULT_COLS);
            },
            &atom!("rows") => {
                self.rows.set(DEFAULT_ROWS);
            },
            _ => ()
        }
    }

    fn bind_to_tree(&self, tree_in_doc: bool) {
        if let Some(ref s) = self.super_type() {
            s.bind_to_tree(tree_in_doc);
        }

        let node: JSRef<Node> = NodeCast::from_ref(*self);
        node.check_ancestors_disabled_state_for_form_control();
    }

    fn parse_plain_attribute(&self, name: &Atom, value: DOMString) -> AttrValue {
        match name {
            &atom!("cols") => AttrValue::from_u32(value, DEFAULT_COLS),
            &atom!("rows") => AttrValue::from_u32(value, DEFAULT_ROWS),
            _ => self.super_type().unwrap().parse_plain_attribute(name, value),
        }
    }

    fn unbind_from_tree(&self, tree_in_doc: bool) {
        if let Some(ref s) = self.super_type() {
            s.unbind_from_tree(tree_in_doc);
        }

        let node: JSRef<Node> = NodeCast::from_ref(*self);
        if node.ancestors().any(|ancestor| ancestor.is_htmlfieldsetelement()) {
            node.check_ancestors_disabled_state_for_form_control();
        } else {
            node.check_disabled_attribute();
        }
    }

    fn child_inserted(&self, child: JSRef<Node>) {
        if let Some(s) = self.super_type() {
            s.child_inserted(child);
        }

        if child.is_text() && !self.value_changed.get() {
            self.reset();
        }
    }

    // copied and modified from htmlinputelement.rs
    fn handle_event(&self, event: JSRef<Event>) {
        if let Some(s) = self.super_type() {
            s.handle_event(event);
        }

        if "click" == event.Type().as_slice() && !event.DefaultPrevented() {
            //TODO: set the editing position for text inputs

            let doc = document_from_node(*self).root();
            doc.r().request_focus(ElementCast::from_ref(*self));
        } else if "keydown" == event.Type().as_slice() && !event.DefaultPrevented() {
            let keyevent: Option<JSRef<KeyboardEvent>> = KeyboardEventCast::to_ref(event);
            keyevent.map(|event| {
                match self.textinput.borrow_mut().handle_keydown(event) {
                    KeyReaction::TriggerDefaultAction => (),
                    KeyReaction::DispatchInput => {
                        self.force_relayout();
                        self.value_changed.set(true);
                    }
                    KeyReaction::Nothing => (),
                }
            });
        }
    }
}

impl<'a> FormControl<'a> for JSRef<'a, HTMLTextAreaElement> {
    fn to_element(self) -> JSRef<'a, Element> {
        ElementCast::from_ref(self)
    }
}
<commit_msg>Fixes #4508 dispatching input event at HTMLTextareaElement<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::attr::{Attr, AttrValue};
use dom::attr::AttrHelpers;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding;
use dom::bindings::codegen::Bindings::HTMLTextAreaElementBinding::HTMLTextAreaElementMethods;
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast, NodeCast};
use dom::bindings::codegen::InheritTypes::{HTMLTextAreaElementDerived, HTMLFieldSetElementDerived};
use dom::bindings::codegen::InheritTypes::{KeyboardEventCast, TextDerived};
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, JSRef, LayoutJS, Temporary, OptionalRootable};
use dom::bindings::refcounted::Trusted;
use dom::document::{Document, DocumentHelpers};
use dom::element::{Element, AttributeHandlers};
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::element::ElementTypeId;
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
use dom::htmlformelement::FormControl;
use dom::keyboardevent::KeyboardEvent;
use dom::node::{DisabledStateHelpers, Node, NodeHelpers, NodeDamage, NodeTypeId};
use dom::node::{document_from_node, window_from_node};
use textinput::{TextInput, Lines, KeyReaction};
use dom::virtualmethods::VirtualMethods;
use script_task::{ScriptMsg, Runnable};

use util::str::DOMString;
use string_cache::Atom;

use std::borrow::ToOwned;
use std::cell::Cell;

#[dom_struct]
pub struct HTMLTextAreaElement {
    htmlelement: HTMLElement,
    textinput: DOMRefCell<TextInput>,
    cols: Cell<u32>,
    rows: Cell<u32>,

    // https://html.spec.whatwg.org/multipage/forms.html#concept-textarea-dirty
    value_changed: Cell<bool>,
}

impl HTMLTextAreaElementDerived for EventTarget {
    fn is_htmltextareaelement(&self) -> bool {
        *self.type_id() == EventTargetTypeId::Node(NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement)))
    }
}

pub trait LayoutHTMLTextAreaElementHelpers {
    unsafe fn get_value_for_layout(self) -> String;
}

pub trait RawLayoutHTMLTextAreaElementHelpers {
    unsafe fn get_cols_for_layout(&self) -> u32;
    unsafe fn get_rows_for_layout(&self) -> u32;
}

impl LayoutHTMLTextAreaElementHelpers for LayoutJS<HTMLTextAreaElement> {
    #[allow(unrooted_must_root)]
    unsafe fn get_value_for_layout(self) -> String {
        (*self.unsafe_get()).textinput.borrow_for_layout().get_content()
    }
}

impl RawLayoutHTMLTextAreaElementHelpers for HTMLTextAreaElement {
    #[allow(unrooted_must_root)]
    unsafe fn get_cols_for_layout(&self) -> u32 {
        self.cols.get()
    }

    #[allow(unrooted_must_root)]
    unsafe fn get_rows_for_layout(&self) -> u32 {
        self.rows.get()
    }
}

static DEFAULT_COLS: u32 = 20;
static DEFAULT_ROWS: u32 = 2;

impl HTMLTextAreaElement {
    fn new_inherited(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> HTMLTextAreaElement {
        HTMLTextAreaElement {
            htmlelement: HTMLElement::new_inherited(HTMLElementTypeId::HTMLTextAreaElement, localName, prefix, document),
            textinput: DOMRefCell::new(TextInput::new(Lines::Multiple, "".to_owned())),
            cols: Cell::new(DEFAULT_COLS),
            rows: Cell::new(DEFAULT_ROWS),
            value_changed: Cell::new(false),
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(localName: DOMString, prefix: Option<DOMString>, document: JSRef<Document>) -> Temporary<HTMLTextAreaElement> {
        let element = HTMLTextAreaElement::new_inherited(localName, prefix, document);
        Node::reflect_node(box element, document, HTMLTextAreaElementBinding::Wrap)
    }
}

impl<'a> HTMLTextAreaElementMethods for JSRef<'a, HTMLTextAreaElement> {
    // TODO A few of these attributes have default values and additional
    // constraints

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-cols
    make_uint_getter!(Cols);

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-cols
    make_uint_setter!(SetCols, "cols");

    // http://www.whatwg.org/html/#dom-fe-disabled
    make_bool_getter!(Disabled);

    // http://www.whatwg.org/html/#dom-fe-disabled
    make_bool_setter!(SetDisabled, "disabled");

    // https://html.spec.whatwg.org/multipage/forms.html#attr-fe-name
    make_getter!(Name);

    // https://html.spec.whatwg.org/multipage/forms.html#attr-fe-name
    make_setter!(SetName, "name");

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-placeholder
    make_getter!(Placeholder);

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-placeholder
    make_setter!(SetPlaceholder, "placeholder");

    // https://html.spec.whatwg.org/multipage/forms.html#attr-textarea-readonly
    make_bool_getter!(ReadOnly);

    // https://html.spec.whatwg.org/multipage/forms.html#attr-textarea-readonly
    make_bool_setter!(SetReadOnly, "readonly");

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-required
    make_bool_getter!(Required);

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-required
    make_bool_setter!(SetRequired, "required");

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-rows
    make_uint_getter!(Rows);

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-rows
    make_uint_setter!(SetRows, "rows");

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-wrap
    make_getter!(Wrap);

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-wrap
    make_setter!(SetWrap, "wrap");

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-type
    fn Type(self) -> DOMString {
        "textarea".to_owned()
    }

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-defaultvalue
    fn DefaultValue(self) -> DOMString {
        let node: JSRef<Node> = NodeCast::from_ref(self);
        node.GetTextContent().unwrap()
    }

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-defaultvalue
    fn SetDefaultValue(self, value: DOMString) {
        let node: JSRef<Node> = NodeCast::from_ref(self);
        node.SetTextContent(Some(value));

        // if the element's dirty value flag is false, then the element's
        // raw value must be set to the value of the element's textContent IDL attribute
        if !self.value_changed.get() {
            self.reset();
        }
    }

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-value
    fn Value(self) -> DOMString {
        self.textinput.borrow().get_content()
    }

    // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea-value
    fn SetValue(self, value: DOMString) {
        // TODO move the cursor to the end of the field
        self.textinput.borrow_mut().set_content(value);
        self.value_changed.set(true);

        let window = window_from_node(self).root();
        let window = window.r();
        let chan = window.script_chan();
        let handler = Trusted::new(window.get_cx(), self, chan.clone());
        chan.send(ScriptMsg::RunnableMsg(box handler));

        self.force_relayout();
    }
}

pub trait HTMLTextAreaElementHelpers {
    fn mutable(self) -> bool;
    fn reset(self);
}

impl<'a> HTMLTextAreaElementHelpers for JSRef<'a, HTMLTextAreaElement> {
    // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-mutable
    fn mutable(self) -> bool {
        // https://html.spec.whatwg.org/multipage/forms.html#the-textarea-element:concept-fe-mutable
        !(self.Disabled() || self.ReadOnly())
    }
    fn reset(self) {
        // https://html.spec.whatwg.org/multipage/forms.html#the-textarea-element:concept-form-reset-control
        self.SetValue(self.DefaultValue());
        self.value_changed.set(false);
    }
}

trait PrivateHTMLTextAreaElementHelpers {
    fn force_relayout(self);
    fn dispatch_change_event(self);
}

impl<'a> PrivateHTMLTextAreaElementHelpers for JSRef<'a, HTMLTextAreaElement> {
    fn force_relayout(self) {
        let doc = document_from_node(self).root();
        let node: JSRef<Node> = NodeCast::from_ref(self);
        doc.r().content_changed(node, NodeDamage::OtherNodeDamage)
    }

    fn dispatch_change_event(self) {
        let window = window_from_node(self).root();
        let window = window.r();
        let event = Event::new(GlobalRef::Window(window),
                               "input".to_owned(),
                               EventBubbles::DoesNotBubble,
                               EventCancelable::NotCancelable).root();
        self.handle_event(event.r());
    }
}

impl<'a> VirtualMethods for JSRef<'a, HTMLTextAreaElement> {
    fn super_type<'b>(&'b self) -> Option<&'b VirtualMethods> {
        let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_borrowed_ref(self);
        Some(htmlelement as &VirtualMethods)
    }

    fn after_set_attr(&self, attr: JSRef<Attr>) {
        if let Some(ref s) = self.super_type() {
            s.after_set_attr(attr);
        }

        match attr.local_name() {
            &atom!("disabled") => {
                let node: JSRef<Node> = NodeCast::from_ref(*self);
                node.set_disabled_state(true);
                node.set_enabled_state(false);
            },
            &atom!("cols") => {
                match *attr.value() {
                    AttrValue::UInt(_, value) => self.cols.set(value),
                    _ => panic!("Expected an AttrValue::UInt"),
                }
            },
            &atom!("rows") => {
                match *attr.value() {
                    AttrValue::UInt(_, value) => self.rows.set(value),
                    _ => panic!("Expected an AttrValue::UInt"),
                }
            },
            _ => ()
        }
    }

    fn before_remove_attr(&self, attr: JSRef<Attr>) {
        if let Some(ref s) = self.super_type() {
            s.before_remove_attr(attr);
        }

        match attr.local_name() {
            &atom!("disabled") => {
                let node: JSRef<Node> = NodeCast::from_ref(*self);
                node.set_disabled_state(false);
                node.set_enabled_state(true);
                node.check_ancestors_disabled_state_for_form_control();
            },
            &atom!("cols") => {
                self.cols.set(DEFAULT_COLS);
            },
            &atom!("rows") => {
                self.rows.set(DEFAULT_ROWS);
            },
            _ => ()
        }
    }

    fn bind_to_tree(&self, tree_in_doc: bool) {
        if let Some(ref s) = self.super_type() {
            s.bind_to_tree(tree_in_doc);
        }

        let node: JSRef<Node> = NodeCast::from_ref(*self);
        node.check_ancestors_disabled_state_for_form_control();
    }

    fn parse_plain_attribute(&self, name: &Atom, value: DOMString) -> AttrValue {
        match name {
            &atom!("cols") => AttrValue::from_u32(value, DEFAULT_COLS),
            &atom!("rows") => AttrValue::from_u32(value, DEFAULT_ROWS),
            _ => self.super_type().unwrap().parse_plain_attribute(name, value),
        }
    }

    fn unbind_from_tree(&self, tree_in_doc: bool) {
        if let Some(ref s) = self.super_type() {
            s.unbind_from_tree(tree_in_doc);
        }

        let node: JSRef<Node> = NodeCast::from_ref(*self);
        if node.ancestors().any(|ancestor| ancestor.is_htmlfieldsetelement()) {
            node.check_ancestors_disabled_state_for_form_control();
        } else {
            node.check_disabled_attribute();
        }
    }

    fn child_inserted(&self, child: JSRef<Node>) {
        if let Some(s) = self.super_type() {
            s.child_inserted(child);
        }

        if child.is_text() && !self.value_changed.get() {
            self.reset();
        }
    }

    // copied and modified from htmlinputelement.rs
    fn handle_event(&self, event: JSRef<Event>) {
        if let Some(s) = self.super_type() {
            s.handle_event(event);
        }

        if "click" == event.Type().as_slice() && !event.DefaultPrevented() {
            //TODO: set the editing position for text inputs

            let doc = document_from_node(*self).root();
            doc.r().request_focus(ElementCast::from_ref(*self));
        } else if "keydown" == event.Type().as_slice() && !event.DefaultPrevented() {
            let keyevent: Option<JSRef<KeyboardEvent>> = KeyboardEventCast::to_ref(event);
            keyevent.map(|event| {
                match self.textinput.borrow_mut().handle_keydown(event) {
                    KeyReaction::TriggerDefaultAction => (),
                    KeyReaction::DispatchInput => {
                        self.force_relayout();
                        self.value_changed.set(true);
                    }
                    KeyReaction::Nothing => (),
                }
            });
        }
    }
}

impl<'a> FormControl<'a> for JSRef<'a, HTMLTextAreaElement> {
    fn to_element(self) -> JSRef<'a, Element> {
        ElementCast::from_ref(self)
    }
}

impl Runnable for Trusted<HTMLTextAreaElement> {
    fn handler(self: Box<Trusted<HTMLTextAreaElement>>) {
        let target = self.to_temporary().root();
        target.r().dispatch_change_event();
    }
}<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    chained_bft::{
        block_storage::BlockStore,
        common::{Payload, Round},
        event_processor::EventProcessor,
        liveness::{
            multi_proposer_election::MultiProposer,
            pacemaker::{ExponentialTimeInterval, Pacemaker},
            pacemaker_timeout_manager::HighestTimeoutCertificates,
            proposal_generator::ProposalGenerator,
            proposer_election::ProposerElection,
            rotating_proposer_election::RotatingProposer,
        },
        network::ConsensusNetworkImpl,
        persistent_storage::{PersistentLivenessStorage, PersistentStorage, RecoveryData},
        safety::safety_rules::SafetyRules,
    },
    counters,
    state_replication::{StateComputer, StateMachineReplication, TxnManager},
    util::time_service::{ClockTimeService, TimeService},
};
use channel;
use failure::prelude::*;
use futures::{
    compat::Future01CompatExt,
    executor::block_on,
    future::{FutureExt, TryFutureExt},
    select,
    stream::StreamExt,
};

use crate::chained_bft::{common::Author, epoch_manager::EpochManager};
use config::config::{ConsensusConfig, ConsensusProposerType};
use logger::prelude::*;
use std::{sync::Arc, time::Duration};
use tokio::runtime::{Runtime, TaskExecutor};
use types::crypto_proxies::ValidatorSigner;

/// Consensus configuration derived from ConsensusConfig
pub struct ChainedBftSMRConfig {
    /// Keep up to this number of committed blocks before cleaning them up from the block store.
    pub max_pruned_blocks_in_mem: usize,
    /// Initial timeout for pacemaker
    pub pacemaker_initial_timeout: Duration,
    /// Consensus proposer type
    pub proposer_type: ConsensusProposerType,
    /// Contiguous rounds for proposer
    pub contiguous_rounds: u32,
    /// Max block size (number of transactions) that consensus pulls from mempool
    pub max_block_size: u64,
}

impl ChainedBftSMRConfig {
    pub fn from_node_config(cfg: &ConsensusConfig) -> ChainedBftSMRConfig {
        let pacemaker_initial_timeout_ms = cfg.pacemaker_initial_timeout_ms().unwrap_or(1000);
        ChainedBftSMRConfig {
            max_pruned_blocks_in_mem: cfg.max_pruned_blocks_in_mem().unwrap_or(10000) as usize,
            pacemaker_initial_timeout: Duration::from_millis(pacemaker_initial_timeout_ms),
            proposer_type: cfg.get_proposer_type(),
            contiguous_rounds: cfg.contiguous_rounds(),
            max_block_size: cfg.max_block_size(),
        }
    }
}

/// ChainedBFTSMR is the one to generate the components (BlockStore, Proposer, etc.) and start the
/// driver. ChainedBftSMR implements the StateMachineReplication, it is going to be used by
/// ConsensusProvider for the e2e flow.
pub struct ChainedBftSMR<T> {
    author: Author,
    signer: Option<ValidatorSigner>,
    proposers: Vec<Author>,
    runtime: Option<Runtime>,
    block_store: Option<Arc<BlockStore<T>>>,
    network: ConsensusNetworkImpl,
    config: ChainedBftSMRConfig,
    storage: Arc<dyn PersistentStorage<T>>,
    initial_data: Option<RecoveryData<T>>,
    epoch_mgr: Arc<EpochManager>,
}

impl<T: Payload> ChainedBftSMR<T> {
    pub fn new(
        author: Author,
        signer: ValidatorSigner,
        proposers: Vec<Author>,
        network: ConsensusNetworkImpl,
        runtime: Runtime,
        config: ChainedBftSMRConfig,
        storage: Arc<dyn PersistentStorage<T>>,
        initial_data: RecoveryData<T>,
        epoch_mgr: Arc<EpochManager>,
    ) -> Self {
        Self {
            author,
            signer: Some(signer),
            proposers,
            runtime: Some(runtime),
            block_store: None,
            network,
            config,
            storage,
            initial_data: Some(initial_data),
            epoch_mgr,
        }
    }

    #[cfg(test)]
    pub fn block_store(&self) -> Option<Arc<BlockStore<T>>> {
        self.block_store.clone()
    }

    fn create_pacemaker(
        &self,
        persistent_liveness_storage: Box<dyn PersistentLivenessStorage>,
        time_service: Arc<dyn TimeService>,
        timeout_sender: channel::Sender<Round>,
        highest_timeout_certificate: HighestTimeoutCertificates,
    ) -> Pacemaker {
        // 1.5^6 ~= 11
        // Timeout goes from initial_timeout to initial_timeout*11 in 6 steps
        let time_interval = Box::new(ExponentialTimeInterval::new(
            self.config.pacemaker_initial_timeout,
            1.5,
            6,
        ));
        Pacemaker::new(
            persistent_liveness_storage,
            time_interval,
            time_service,
            timeout_sender,
            highest_timeout_certificate,
        )
    }

    /// Create a proposer election handler based on proposers
    fn create_proposer_election(&self) -> Box<dyn ProposerElection<T> + Send + Sync> {
        assert!(!self.proposers.is_empty());
        match self.config.proposer_type {
            ConsensusProposerType::MultipleOrderedProposers => {
                Box::new(MultiProposer::new(self.proposers.clone(), 2))
            }
            // We don't really have a fixed proposer!
            _ => Box::new(RotatingProposer::new(
                self.proposers.clone(),
                self.config.contiguous_rounds,
            )),
        }
    }

    fn start_event_processing(
        &mut self,
        executor: TaskExecutor,
        mut event_processor: EventProcessor<T>,
        mut pacemaker_timeout_sender_rx: channel::Receiver<Round>,
    ) {
        let mut network_receivers = self.network.start(&executor);
        let fut = async move {
            event_processor.start().await;
            loop {
                select! {
                    proposal_msg = network_receivers.proposals.select_next_some() => {
                        event_processor.process_proposal_msg(proposal_msg).await;
                    }
                    block_retrieval = network_receivers.block_retrieval.select_next_some() => {
                        event_processor.process_block_retrieval(block_retrieval).await;
                    }
                    vote_msg = network_receivers.votes.select_next_some() => {
                        event_processor.process_vote(vote_msg).await;
                    }
                    remote_timeout_msg = network_receivers.timeout_msgs.select_next_some() => {
                        event_processor.process_remote_timeout_msg(remote_timeout_msg).await;
                    }
                    local_timeout_round = pacemaker_timeout_sender_rx.select_next_some() => {
                        event_processor.process_local_timeout(local_timeout_round).await;
                    }
                    sync_info_msg = network_receivers.sync_info_msgs.select_next_some() => {
                        event_processor.process_sync_info_msg(sync_info_msg.0, sync_info_msg.1).await;
                    }
                    complete => {
                        break;
                    }
                }
            }
        };
        executor.spawn(fut.boxed().unit_error().compat());
    }
}

impl<T: Payload> StateMachineReplication for ChainedBftSMR<T> {
    type Payload = T;

    fn start(
        &mut self,
        txn_manager: Arc<dyn TxnManager<Payload = Self::Payload>>,
        state_computer: Arc<dyn StateComputer<Payload = Self::Payload>>,
    ) -> Result<()> {
        let executor = self
            .runtime
            .as_mut()
            .expect("Consensus start: No valid runtime found!")
            .executor();
        let time_service = Arc::new(ClockTimeService::new(executor.clone()));

        let initial_data = self
            .initial_data
            .take()
            .expect("already started, initial data is None");
        let consensus_state = initial_data.state();
        let highest_timeout_certificates = initial_data.highest_timeout_certificates().clone();
        if initial_data.need_sync() {
            loop {
                // make sure we sync to the root state in case we're not
                let status = block_on(state_computer.sync_to(initial_data.root_ledger_info()));
                match status {
                    Ok(true) => break,
                    Ok(false) => panic!(
                    "state synchronizer failure, this validator will be killed as it can not \
                 recover from this error.  After the validator is restarted, synchronization will \
                 be retried.",
                ),
                    Err(e) => panic!(
                    "state synchronizer failure: {:?}, this validator will be killed as it can not \
                 recover from this error.  After the validator is restarted, synchronization will \
                 be retried.",
                    e
                ),
                }
            }
        }

        // the signer is only stored in the SMR to be provided here
        let signer = self
            .signer
            .take()
            .expect("start called twice on the same Chained BFT SMR!");
        let block_store = Arc::new(block_on(BlockStore::new(
            Arc::clone(&self.storage),
            initial_data,
            signer,
            Arc::clone(&state_computer),
            true,
            self.config.max_pruned_blocks_in_mem,
        )));

        self.block_store = Some(Arc::clone(&block_store));

        // txn manager is required both by proposal generator (to pull the proposers)
        // and by event processor (to update their status).
        let proposal_generator = ProposalGenerator::new(
            block_store.clone(),
            Arc::clone(&txn_manager),
            time_service.clone(),
            self.config.max_block_size,
            true,
        );

        let safety_rules = SafetyRules::new(consensus_state);

        let (timeout_sender, timeout_receiver) =
            channel::new(1_024, &counters::PENDING_PACEMAKER_TIMEOUTS);
        let pacemaker = self.create_pacemaker(
            self.storage.persistent_liveness_storage(),
            time_service.clone(),
            timeout_sender,
            highest_timeout_certificates,
        );

        let proposer_election = self.create_proposer_election();
        let event_processor = EventProcessor::new(
            self.author,
            Arc::clone(&block_store),
            pacemaker,
            proposer_election,
            proposal_generator,
            safety_rules,
            state_computer,
            txn_manager,
            self.network.clone(),
            Arc::clone(&self.storage),
            time_service.clone(),
            true,
            Arc::clone(&self.epoch_mgr),
        );

        self.start_event_processing(executor, event_processor, timeout_receiver);

        debug!("Chained BFT SMR started.");
        Ok(())
    }

    /// Stop is synchronous: waits for all the worker threads to terminate.
    fn stop(&mut self) {
        if let Some(rt) = self.runtime.take() {
            block_on(rt.shutdown_now().compat()).unwrap();
            debug!("Chained BFT SMR stopped.")
        }
    }
}
<commit_msg>[consensus] Start network receivers before blocking on state sync during init<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    chained_bft::{
        block_storage::BlockStore,
        common::{Payload, Round},
        event_processor::EventProcessor,
        liveness::{
            multi_proposer_election::MultiProposer,
            pacemaker::{ExponentialTimeInterval, Pacemaker},
            pacemaker_timeout_manager::HighestTimeoutCertificates,
            proposal_generator::ProposalGenerator,
            proposer_election::ProposerElection,
            rotating_proposer_election::RotatingProposer,
        },
        network::{ConsensusNetworkImpl, NetworkReceivers},
        persistent_storage::{PersistentLivenessStorage, PersistentStorage, RecoveryData},
        safety::safety_rules::SafetyRules,
    },
    counters,
    state_replication::{StateComputer, StateMachineReplication, TxnManager},
    util::time_service::{ClockTimeService, TimeService},
};
use channel;
use failure::prelude::*;
use futures::{
    compat::Future01CompatExt,
    executor::block_on,
    future::{FutureExt, TryFutureExt},
    select,
    stream::StreamExt,
};

use crate::chained_bft::{common::Author, epoch_manager::EpochManager};
use config::config::{ConsensusConfig, ConsensusProposerType};
use logger::prelude::*;
use std::{sync::Arc, time::Duration};
use tokio::runtime::{Runtime, TaskExecutor};
use types::crypto_proxies::ValidatorSigner;

/// Consensus configuration derived from ConsensusConfig
pub struct ChainedBftSMRConfig {
    /// Keep up to this number of committed blocks before cleaning them up from the block store.
    pub max_pruned_blocks_in_mem: usize,
    /// Initial timeout for pacemaker
    pub pacemaker_initial_timeout: Duration,
    /// Consensus proposer type
    pub proposer_type: ConsensusProposerType,
    /// Contiguous rounds for proposer
    pub contiguous_rounds: u32,
    /// Max block size (number of transactions) that consensus pulls from mempool
    pub max_block_size: u64,
}

impl ChainedBftSMRConfig {
    pub fn from_node_config(cfg: &ConsensusConfig) -> ChainedBftSMRConfig {
        let pacemaker_initial_timeout_ms = cfg.pacemaker_initial_timeout_ms().unwrap_or(1000);
        ChainedBftSMRConfig {
            max_pruned_blocks_in_mem: cfg.max_pruned_blocks_in_mem().unwrap_or(10000) as usize,
            pacemaker_initial_timeout: Duration::from_millis(pacemaker_initial_timeout_ms),
            proposer_type: cfg.get_proposer_type(),
            contiguous_rounds: cfg.contiguous_rounds(),
            max_block_size: cfg.max_block_size(),
        }
    }
}

/// ChainedBFTSMR is the one to generate the components (BlockStore, Proposer, etc.) and start the
/// driver. ChainedBftSMR implements the StateMachineReplication, it is going to be used by
/// ConsensusProvider for the e2e flow.
pub struct ChainedBftSMR<T> {
    author: Author,
    signer: Option<ValidatorSigner>,
    proposers: Vec<Author>,
    runtime: Option<Runtime>,
    block_store: Option<Arc<BlockStore<T>>>,
    network: ConsensusNetworkImpl,
    config: ChainedBftSMRConfig,
    storage: Arc<dyn PersistentStorage<T>>,
    initial_data: Option<RecoveryData<T>>,
    epoch_mgr: Arc<EpochManager>,
}

impl<T: Payload> ChainedBftSMR<T> {
    pub fn new(
        author: Author,
        signer: ValidatorSigner,
        proposers: Vec<Author>,
        network: ConsensusNetworkImpl,
        runtime: Runtime,
        config: ChainedBftSMRConfig,
        storage: Arc<dyn PersistentStorage<T>>,
        initial_data: RecoveryData<T>,
        epoch_mgr: Arc<EpochManager>,
    ) -> Self {
        Self {
            author,
            signer: Some(signer),
            proposers,
            runtime: Some(runtime),
            block_store: None,
            network,
            config,
            storage,
            initial_data: Some(initial_data),
            epoch_mgr,
        }
    }

    #[cfg(test)]
    pub fn block_store(&self) -> Option<Arc<BlockStore<T>>> {
        self.block_store.clone()
    }

    fn create_pacemaker(
        &self,
        persistent_liveness_storage: Box<dyn PersistentLivenessStorage>,
        time_service: Arc<dyn TimeService>,
        timeout_sender: channel::Sender<Round>,
        highest_timeout_certificate: HighestTimeoutCertificates,
    ) -> Pacemaker {
        // 1.5^6 ~= 11
        // Timeout goes from initial_timeout to initial_timeout*11 in 6 steps
        let time_interval = Box::new(ExponentialTimeInterval::new(
            self.config.pacemaker_initial_timeout,
            1.5,
            6,
        ));
        Pacemaker::new(
            persistent_liveness_storage,
            time_interval,
            time_service,
            timeout_sender,
            highest_timeout_certificate,
        )
    }

    /// Create a proposer election handler based on proposers
    fn create_proposer_election(&self) -> Box<dyn ProposerElection<T> + Send + Sync> {
        assert!(!self.proposers.is_empty());
        match self.config.proposer_type {
            ConsensusProposerType::MultipleOrderedProposers => {
                Box::new(MultiProposer::new(self.proposers.clone(), 2))
            }
            // We don't really have a fixed proposer!
            _ => Box::new(RotatingProposer::new(
                self.proposers.clone(),
                self.config.contiguous_rounds,
            )),
        }
    }

    fn start_event_processing(
        &mut self,
        executor: TaskExecutor,
        mut event_processor: EventProcessor<T>,
        mut pacemaker_timeout_sender_rx: channel::Receiver<Round>,
        mut network_receivers: NetworkReceivers<T>,
    ) {
        let fut = async move {
            event_processor.start().await;
            loop {
                select! {
                    proposal_msg = network_receivers.proposals.select_next_some() => {
                        event_processor.process_proposal_msg(proposal_msg).await;
                    }
                    block_retrieval = network_receivers.block_retrieval.select_next_some() => {
                        event_processor.process_block_retrieval(block_retrieval).await;
                    }
                    vote_msg = network_receivers.votes.select_next_some() => {
                        event_processor.process_vote(vote_msg).await;
                    }
                    remote_timeout_msg = network_receivers.timeout_msgs.select_next_some() => {
                        event_processor.process_remote_timeout_msg(remote_timeout_msg).await;
                    }
                    local_timeout_round = pacemaker_timeout_sender_rx.select_next_some() => {
                        event_processor.process_local_timeout(local_timeout_round).await;
                    }
                    sync_info_msg = network_receivers.sync_info_msgs.select_next_some() => {
                        event_processor.process_sync_info_msg(sync_info_msg.0, sync_info_msg.1).await;
                    }
                    complete => {
                        break;
                    }
                }
            }
        };
        executor.spawn(fut.boxed().unit_error().compat());
    }
}

impl<T: Payload> StateMachineReplication for ChainedBftSMR<T> {
    type Payload = T;

    fn start(
        &mut self,
        txn_manager: Arc<dyn TxnManager<Payload = Self::Payload>>,
        state_computer: Arc<dyn StateComputer<Payload = Self::Payload>>,
    ) -> Result<()> {
        let executor = self
            .runtime
            .as_mut()
            .expect("Consensus start: No valid runtime found!")
            .executor();
        // Start network receivers before blocking on state synchronizer to unblock delivery of
        // network events.
        let network_receivers = self.network.start(&executor);
        let time_service = Arc::new(ClockTimeService::new(executor.clone()));
        let initial_data = self
            .initial_data
            .take()
            .expect("already started, initial data is None");
        let consensus_state = initial_data.state();
        let highest_timeout_certificates = initial_data.highest_timeout_certificates().clone();
        if initial_data.need_sync() {
            loop {
                // make sure we sync to the root state in case we're not
                let status = block_on(state_computer.sync_to(initial_data.root_ledger_info()));
                match status {
                    Ok(true) => break,
                    Ok(false) => panic!(
                    "state synchronizer failure, this validator will be killed as it can not \
                 recover from this error.  After the validator is restarted, synchronization will \
                 be retried.",
                ),
                    Err(e) => panic!(
                    "state synchronizer failure: {:?}, this validator will be killed as it can not \
                 recover from this error.  After the validator is restarted, synchronization will \
                 be retried.",
                    e
                ),
                }
            }
        }

        // the signer is only stored in the SMR to be provided here
        let signer = self
            .signer
            .take()
            .expect("start called twice on the same Chained BFT SMR!");
        let block_store = Arc::new(block_on(BlockStore::new(
            Arc::clone(&self.storage),
            initial_data,
            signer,
            Arc::clone(&state_computer),
            true,
            self.config.max_pruned_blocks_in_mem,
        )));

        self.block_store = Some(Arc::clone(&block_store));

        // txn manager is required both by proposal generator (to pull the proposers)
        // and by event processor (to update their status).
        let proposal_generator = ProposalGenerator::new(
            block_store.clone(),
            Arc::clone(&txn_manager),
            time_service.clone(),
            self.config.max_block_size,
            true,
        );

        let safety_rules = SafetyRules::new(consensus_state);

        let (timeout_sender, timeout_receiver) =
            channel::new(1_024, &counters::PENDING_PACEMAKER_TIMEOUTS);
        let pacemaker = self.create_pacemaker(
            self.storage.persistent_liveness_storage(),
            time_service.clone(),
            timeout_sender,
            highest_timeout_certificates,
        );

        let proposer_election = self.create_proposer_election();
        let event_processor = EventProcessor::new(
            self.author,
            Arc::clone(&block_store),
            pacemaker,
            proposer_election,
            proposal_generator,
            safety_rules,
            state_computer,
            txn_manager,
            self.network.clone(),
            Arc::clone(&self.storage),
            time_service.clone(),
            true,
            Arc::clone(&self.epoch_mgr),
        );

        self.start_event_processing(
            executor,
            event_processor,
            timeout_receiver,
            network_receivers,
        );

        debug!("Chained BFT SMR started.");
        Ok(())
    }

    /// Stop is synchronous: waits for all the worker threads to terminate.
    fn stop(&mut self) {
        if let Some(rt) = self.runtime.take() {
            block_on(rt.shutdown_now().compat()).unwrap();
            debug!("Chained BFT SMR stopped.")
        }
    }
}
<|endoftext|>


<commit_before>//! Parse the PE Control Flow Guard function table.
//! This table contains an entry for each function that may be invoked
//! dynamically. When present, it tends to cover a large percentage of the
//! functions in a module.
//!
//! This table referenced by the Load Config directory.
//! The reference consists of: (flags: u32, offset: VA, count: u32/u64).
//! The table is an array of entries that consist of:
//!   u32       RVA (both x32 and x64)
//!   variable  data
//! The flags in the table reference describe how big the entry data is (the
//! "stride").
//!
//! The Load Config Control Flow Guard metadata also references:
//!   - function pointer to indirect call check routine (supported)
//!   - function pointer to indirect call dispatch routine (supported)
//!   - IAT entry table (not supported here)
//!   - LongJump entry table (not supported here)
//!
//! references:
//!   - https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#load-configuration-directory
//!   - https://lucasg.github.io/2017/02/05/Control-Flow-Guard/

use anyhow::Result;
use byteorder::{ByteOrder, LittleEndian};
use log::debug;

use crate::{
    aspace::AddressSpace,
    loader::pe::PE,
    module::{Arch, Permissions},
    RVA, VA,
};

const IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK: u32 = 0xF000_0000;
const IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_SHIFT: u32 = 28;
const IMAGE_GUARD_CF_FUNCTION_TABLE_PRESENT: u32 = 0x400;

pub fn find_pe_cfguard_functions(pe: &PE) -> Result<Vec<VA>> {
    let mut ret = vec![];

    let load_config_directory_rva: RVA = {
        let opt_header = match pe.header.optional_header {
            Some(opt_header) => opt_header,
            _ => return Ok(vec![]),
        };

        let load_config_directory = match opt_header.data_directories.get_load_config_table() {
            Some(load_config_directory) => load_config_directory,
            _ => return Ok(vec![]),
        };

        load_config_directory.virtual_address as RVA
    };

    debug!(
        "load config directory: {:#x}",
        pe.module.address_space.base_address + load_config_directory_rva
    );

    // offsets defined here:
    // https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#load-configuration-directory

    // according to IDA, the first DWORD is `Size` not `Characteristics` (unused).
    let size = pe.module.address_space.relative.read_u32(load_config_directory_rva)?;

    // max offset into the config directory that we'll read.
    let max_config_directory_offset = match pe.module.arch {
        // CFG flags
        Arch::X32 => 0x58,
        Arch::X64 => 0x94,
    };

    // in `d3d11sdklayers.dll` for example, the config size is 0x70,
    // which is much too small to read the CFG table options.
    if max_config_directory_offset > size {
        debug!("no CF Guard table: load config directory too small");
        return Ok(vec![]);
    }

    let cfg_flags: u32 = match pe.module.arch {
        Arch::X32 => pe
            .module
            .address_space
            .relative
            .read_u32(load_config_directory_rva + 0x58)?,
        Arch::X64 => pe
            .module
            .address_space
            .relative
            .read_u32(load_config_directory_rva + 0x90)?,
    };
    debug!("CF guard flags: {:#x}", cfg_flags);

    if cfg_flags & IMAGE_GUARD_CF_FUNCTION_TABLE_PRESENT > 0 {
        let stride = ((cfg_flags & IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK) >> IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_SHIFT)
            as usize;
        if stride > 8 {
            // stride should really be 1, but we'll accept up to 8 for future compatibility.
            debug!("unexpected CF guard stride: {:#x}", stride);
            return Ok(vec![]);
        }

        let cfg_table_va: VA = match pe.module.arch {
            Arch::X32 => pe.module.read_va_at_rva(load_config_directory_rva + 0x50)?,
            Arch::X64 => pe.module.read_va_at_rva(load_config_directory_rva + 0x80)?,
        };
        if cfg_table_va == 0 {
            debug!("CF guard table empty");
            return Ok(vec![]);
        };
        debug!("CF Guard table: {:#x}", cfg_table_va);

        let cfg_table_count = match pe.module.arch {
            Arch::X32 => pe
                .module
                .address_space
                .relative
                .read_u32(load_config_directory_rva + 0x54)? as u64,
            Arch::X64 => pe
                .module
                .address_space
                .relative
                .read_u64(load_config_directory_rva + 0x88)? as u64,
        };
        debug!("CF Guard table count: {:#x}", cfg_table_count);

        // read the table buffer once up front, then iterate slices over it with
        // windows. this is at the expense of one allocation for the table.
        // it be faster than doing pe.module.with_va().read_i32() on each offset, on
        // large tables.
        //
        // 4 == sizeof(i32) RVA to function start, both x32 and x64
        let cfg_table_entry_size: usize = 4 + stride;
        let cfg_table = pe
            .module
            .address_space
            .read_bytes(cfg_table_va, cfg_table_count as usize * cfg_table_entry_size)?;
        for entry_buf in cfg_table.chunks_exact(cfg_table_entry_size) {
            let target = pe.module.address_space.base_address + LittleEndian::read_i32(entry_buf) as u64;

            if pe.module.probe_va(target, Permissions::X) {
                ret.push(target);
            } else {
                debug!("unexpected non-executable CFG target: {:#x}", target);
                break;
            }
        }

        // add function pointed to by GuardCFCheckFunctionPointer
        let guard_check_icall_fptr: VA = match pe.module.arch {
            Arch::X32 => pe.module.read_va_at_rva(load_config_directory_rva + 0x48)?,
            Arch::X64 => pe.module.read_va_at_rva(load_config_directory_rva + 0x70)?,
        };
        if guard_check_icall_fptr != 0 {
            debug!(
                "CF Guard check indirect call function pointer: {:#x}",
                guard_check_icall_fptr
            );

            if let Ok(guard_check_icall) = pe.module.read_va_at_va(guard_check_icall_fptr) {
                if pe.module.probe_va(guard_check_icall, Permissions::X) {
                    debug!("CF Guard check icall: {:#x}", guard_check_icall);
                    ret.push(guard_check_icall);
                }
            }
        }

        // add function pointed to by GuardCFDispatchFunctionPointer
        //
        // set to 0x0 when not used, as is often the case on 32-bit Windows DLLs.
        let guard_dispatch_icall_fptr: VA = match pe.module.arch {
            Arch::X32 => pe.module.read_va_at_rva(load_config_directory_rva + 0x4C)?,
            Arch::X64 => pe.module.read_va_at_rva(load_config_directory_rva + 0x78)?,
        };
        if guard_dispatch_icall_fptr != 0 {
            debug!(
                "CF Guard dispatch indirect call function pointer: {:#x}",
                guard_dispatch_icall_fptr
            );

            if let Ok(guard_dispatch_icall) = pe.module.read_va_at_va(guard_dispatch_icall_fptr) {
                if pe.module.probe_va(guard_dispatch_icall, Permissions::X) {
                    debug!("CF Guard dispatch icall: {:#x}", guard_dispatch_icall);
                    ret.push(guard_dispatch_icall);
                }
            }
        }
    }

    Ok(ret)
}

#[cfg(test)]
mod tests {
    use crate::rsrc::*;
    use anyhow::Result;

    #[test]
    fn k32() -> Result<()> {
        let buf = get_buf(Rsrc::K32);
        let pe = crate::loader::pe::PE::from_bytes(&buf)?;

        let fns = crate::analysis::pe::control_flow_guard::find_pe_cfguard_functions(&pe)?;
        assert_eq!(1502, fns.len());

        Ok(())
    }

    #[test]
    fn tiny() -> Result<()> {
        let buf = get_buf(Rsrc::TINY);
        let pe = crate::loader::pe::PE::from_bytes(&buf)?;

        let fns = crate::analysis::pe::control_flow_guard::find_pe_cfguard_functions(&pe)?;
        assert_eq!(0, fns.len());

        Ok(())
    }

    #[test]
    fn nop() -> Result<()> {
        let buf = get_buf(Rsrc::NOP);
        let pe = crate::loader::pe::PE::from_bytes(&buf)?;

        let fns = crate::analysis::pe::control_flow_guard::find_pe_cfguard_functions(&pe)?;
        assert_eq!(0, fns.len());

        Ok(())
    }

    #[test]
    fn mimi() -> Result<()> {
        let buf = get_buf(Rsrc::MIMI);
        let pe = crate::loader::pe::PE::from_bytes(&buf)?;

        let fns = crate::analysis::pe::control_flow_guard::find_pe_cfguard_functions(&pe)?;
        assert_eq!(0, fns.len());

        Ok(())
    }
}
<commit_msg>pe: cfg: reuse data directory accessor<commit_after>//! Parse the PE Control Flow Guard function table.
//! This table contains an entry for each function that may be invoked
//! dynamically. When present, it tends to cover a large percentage of the
//! functions in a module.
//!
//! This table referenced by the Load Config directory.
//! The reference consists of: (flags: u32, offset: VA, count: u32/u64).
//! The table is an array of entries that consist of:
//!   u32       RVA (both x32 and x64)
//!   variable  data
//! The flags in the table reference describe how big the entry data is (the
//! "stride").
//!
//! The Load Config Control Flow Guard metadata also references:
//!   - function pointer to indirect call check routine (supported)
//!   - function pointer to indirect call dispatch routine (supported)
//!   - IAT entry table (not supported here)
//!   - LongJump entry table (not supported here)
//!
//! references:
//!   - https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#load-configuration-directory
//!   - https://lucasg.github.io/2017/02/05/Control-Flow-Guard/

use anyhow::Result;
use byteorder::{ByteOrder, LittleEndian};
use log::debug;

use crate::{
    aspace::AddressSpace,
    loader::{pe, pe::PE},
    module::{Arch, Permissions},
    VA,
};

const IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK: u32 = 0xF000_0000;
const IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_SHIFT: u32 = 28;
const IMAGE_GUARD_CF_FUNCTION_TABLE_PRESENT: u32 = 0x400;

pub fn find_pe_cfguard_functions(pe: &PE) -> Result<Vec<VA>> {
    let mut ret = vec![];

    if let Ok(Some(load_config_directory)) = pe.get_data_directory(pe::IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG) {
        debug!("load config directory: {:#x}", load_config_directory.address);

        // offsets defined here:
        // https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#load-configuration-directory

        // according to IDA, the first DWORD is `Size` not `Characteristics` (unused).
        let size = pe.module.address_space.read_u32(load_config_directory.address)?;

        // max offset into the config directory that we'll read.
        let max_config_directory_offset = match pe.module.arch {
            // CFG flags
            Arch::X32 => 0x58,
            Arch::X64 => 0x94,
        };

        // in `d3d11sdklayers.dll` for example, the config size is 0x70,
        // which is much too small to read the CFG table options.
        if max_config_directory_offset > size {
            debug!("no CF Guard table: load config directory too small");
            return Ok(vec![]);
        }

        let cfg_flags: u32 = match pe.module.arch {
            Arch::X32 => pe.module.address_space.read_u32(load_config_directory.address + 0x58)?,
            Arch::X64 => pe.module.address_space.read_u32(load_config_directory.address + 0x90)?,
        };
        debug!("CF guard flags: {:#x}", cfg_flags);

        if cfg_flags & IMAGE_GUARD_CF_FUNCTION_TABLE_PRESENT > 0 {
            let stride = ((cfg_flags & IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK)
                >> IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_SHIFT) as usize;
            if stride > 8 {
                // stride should really be 1, but we'll accept up to 8 for future compatibility.
                debug!("unexpected CF guard stride: {:#x}", stride);
                return Ok(vec![]);
            }

            let cfg_table_va: VA = match pe.module.arch {
                Arch::X32 => pe.module.read_va_at_va(load_config_directory.address + 0x50)?,
                Arch::X64 => pe.module.read_va_at_va(load_config_directory.address + 0x80)?,
            };
            if cfg_table_va == 0 {
                debug!("CF guard table empty");
                return Ok(vec![]);
            };
            debug!("CF Guard table: {:#x}", cfg_table_va);

            let cfg_table_count = match pe.module.arch {
                Arch::X32 => pe.module.address_space.read_u32(load_config_directory.address + 0x54)? as u64,
                Arch::X64 => pe.module.address_space.read_u64(load_config_directory.address + 0x88)? as u64,
            };
            debug!("CF Guard table count: {:#x}", cfg_table_count);

            // read the table buffer once up front, then iterate slices over it with
            // windows. this is at the expense of one allocation for the table.
            // it be faster than doing pe.module.with_va().read_i32() on each offset, on
            // large tables.
            //
            // 4 == sizeof(i32) RVA to function start, both x32 and x64
            let cfg_table_entry_size: usize = 4 + stride;
            let cfg_table = pe
                .module
                .address_space
                .read_bytes(cfg_table_va, cfg_table_count as usize * cfg_table_entry_size)?;
            for entry_buf in cfg_table.chunks_exact(cfg_table_entry_size) {
                let target = pe.module.address_space.base_address + LittleEndian::read_i32(entry_buf) as u64;

                if pe.module.probe_va(target, Permissions::X) {
                    ret.push(target);
                } else {
                    debug!("unexpected non-executable CFG target: {:#x}", target);
                    break;
                }
            }

            // add function pointed to by GuardCFCheckFunctionPointer
            let guard_check_icall_fptr: VA = match pe.module.arch {
                Arch::X32 => pe.module.read_va_at_va(load_config_directory.address + 0x48)?,
                Arch::X64 => pe.module.read_va_at_va(load_config_directory.address + 0x70)?,
            };
            if guard_check_icall_fptr != 0 {
                debug!(
                    "CF Guard check indirect call function pointer: {:#x}",
                    guard_check_icall_fptr
                );

                if let Ok(guard_check_icall) = pe.module.read_va_at_va(guard_check_icall_fptr) {
                    if pe.module.probe_va(guard_check_icall, Permissions::X) {
                        debug!("CF Guard check icall: {:#x}", guard_check_icall);
                        ret.push(guard_check_icall);
                    }
                }
            }

            // add function pointed to by GuardCFDispatchFunctionPointer
            //
            // set to 0x0 when not used, as is often the case on 32-bit Windows DLLs.
            let guard_dispatch_icall_fptr: VA = match pe.module.arch {
                Arch::X32 => pe.module.read_va_at_va(load_config_directory.address + 0x4C)?,
                Arch::X64 => pe.module.read_va_at_va(load_config_directory.address + 0x78)?,
            };
            if guard_dispatch_icall_fptr != 0 {
                debug!(
                    "CF Guard dispatch indirect call function pointer: {:#x}",
                    guard_dispatch_icall_fptr
                );

                if let Ok(guard_dispatch_icall) = pe.module.read_va_at_va(guard_dispatch_icall_fptr) {
                    if pe.module.probe_va(guard_dispatch_icall, Permissions::X) {
                        debug!("CF Guard dispatch icall: {:#x}", guard_dispatch_icall);
                        ret.push(guard_dispatch_icall);
                    }
                }
            }
        }
    }

    Ok(ret)
}

#[cfg(test)]
mod tests {
    use crate::rsrc::*;
    use anyhow::Result;

    #[test]
    fn k32() -> Result<()> {
        let buf = get_buf(Rsrc::K32);
        let pe = crate::loader::pe::PE::from_bytes(&buf)?;

        let fns = crate::analysis::pe::control_flow_guard::find_pe_cfguard_functions(&pe)?;
        assert_eq!(1502, fns.len());

        Ok(())
    }

    #[test]
    fn tiny() -> Result<()> {
        let buf = get_buf(Rsrc::TINY);
        let pe = crate::loader::pe::PE::from_bytes(&buf)?;

        let fns = crate::analysis::pe::control_flow_guard::find_pe_cfguard_functions(&pe)?;
        assert_eq!(0, fns.len());

        Ok(())
    }

    #[test]
    fn nop() -> Result<()> {
        let buf = get_buf(Rsrc::NOP);
        let pe = crate::loader::pe::PE::from_bytes(&buf)?;

        let fns = crate::analysis::pe::control_flow_guard::find_pe_cfguard_functions(&pe)?;
        assert_eq!(0, fns.len());

        Ok(())
    }

    #[test]
    fn mimi() -> Result<()> {
        let buf = get_buf(Rsrc::MIMI);
        let pe = crate::loader::pe::PE::from_bytes(&buf)?;

        let fns = crate::analysis::pe::control_flow_guard::find_pe_cfguard_functions(&pe)?;
        assert_eq!(0, fns.len());

        Ok(())
    }
}
<|endoftext|>


<commit_before>//! TODO documentation

use wlroots_sys::{wlr_output_cursor, wlr_output_cursor_create, wlr_output_cursor_destroy,
                  wlr_output_cursor_move, wlr_output_cursor_set_image,
                  wlr_output_cursor_set_surface};

use {Output, OutputHandle, Surface};

#[derive(Debug, Eq, PartialEq)]
pub struct OutputCursor {
    cursor: *mut wlr_output_cursor,
    output_handle: OutputHandle
}

impl OutputCursor {
    /// Creates a new `OutputCursor` that's bound to the given `Output`.
    ///
    /// When the `Output` is destroyed, this can no longer be used.
    ///
    /// # Ergonomics
    /// TODO Put in module documentation
    ///
    /// To make this easier for you, I would suggest putting the `OutputCursor` in your
    /// `OutputHandler` implementor's state so that when the `Output` is removed you
    /// just don't have to think about it and it will clean itself up by itself.
    pub fn new<'output>(output: &'output mut Output) -> Option<OutputCursor> {
        unsafe {
            let output_handle = output.weak_reference();
            let cursor = wlr_output_cursor_create(output.as_ptr());
            if cursor.is_null() {
                None
            } else {
                Some(OutputCursor { cursor,
                                    output_handle })
            }
        }
    }

    /// Sets the hardware cursor's image.
    pub fn set_image(&mut self,
                     pixels: &[u8],
                     stride: i32,
                     width: u32,
                     height: u32,
                     hotspot_x: i32,
                     hotspot_y: i32)
                     -> bool {
        unsafe {
            // TODO Ensure the buffer is correct?
            wlr_output_cursor_set_image(self.cursor,
                                        pixels.as_ptr(),
                                        stride,
                                        width,
                                        height,
                                        hotspot_x,
                                        hotspot_y)
        }
    }

    /// Sets the hardware cursor's surface.
    pub fn set_surface(&mut self, surface: Surface, hotspot_x: i32, hotspot_y: i32) {
        unsafe {
            wlr_output_cursor_set_surface(self.cursor, surface.as_ptr(), hotspot_x, hotspot_y)
        }
    }

    /// Moves the hardware cursor to the desired location
    pub fn move_to(&mut self, x: f64, y: f64) -> bool {
        unsafe { wlr_output_cursor_move(self.cursor, x, y) }
    }
}

impl Drop for OutputCursor {
    fn drop(&mut self) {
        unsafe { wlr_output_cursor_destroy(self.cursor) }
    }
}
<commit_msg>Added output death catchers to OutputCursor<commit_after>//! TODO documentation

use wlroots_sys::{wlr_output_cursor, wlr_output_cursor_create, wlr_output_cursor_destroy,
                  wlr_output_cursor_move, wlr_output_cursor_set_image,
                  wlr_output_cursor_set_surface};

use {Output, OutputHandle, Surface, UpgradeHandleErr};

#[derive(Debug, Eq, PartialEq)]
pub struct OutputCursor {
    cursor: *mut wlr_output_cursor,
    output_handle: OutputHandle
}

impl OutputCursor {
    /// Creates a new `OutputCursor` that's bound to the given `Output`.
    ///
    /// When the `Output` is destroyed, this can no longer be used.
    ///
    /// # Ergonomics
    /// TODO Put in module documentation
    ///
    /// To make this easier for you, I would suggest putting the `OutputCursor` in your
    /// `OutputHandler` implementor's state so that when the `Output` is removed you
    /// just don't have to think about it and it will clean itself up by itself.
    pub fn new<'output>(output: &'output mut Output) -> Option<OutputCursor> {
        unsafe {
            let output_handle = output.weak_reference();
            let cursor = wlr_output_cursor_create(output.as_ptr());
            if cursor.is_null() {
                None
            } else {
                Some(OutputCursor { cursor,
                                    output_handle })
            }
        }
    }

    /// Sets the hardware cursor's image.
    pub fn set_image(&mut self,
                     pixels: &[u8],
                     stride: i32,
                     width: u32,
                     height: u32,
                     hotspot_x: i32,
                     hotspot_y: i32)
                     -> bool {
        unsafe {
            let cursor = self.cursor;
            let res = self.output_handle.run(|_| {
                                                 // TODO Ensure the buffer is correct?
                                                 wlr_output_cursor_set_image(cursor,
                                                                             pixels.as_ptr(),
                                                                             stride,
                                                                             width,
                                                                             height,
                                                                             hotspot_x,
                                                                             hotspot_y)
                                             });
            match res {
                Ok(res) => res,
                Err(UpgradeHandleErr::AlreadyDropped) => false,
                err @ Err(UpgradeHandleErr::AlreadyBorrowed) => panic!(err)
            }
        }
    }

    /// Sets the hardware cursor's surface.
    pub fn set_surface(&mut self, surface: Surface, hotspot_x: i32, hotspot_y: i32) {
        unsafe {
            let cursor = self.cursor;
            let res = self.output_handle.run(|_| {
                                                 wlr_output_cursor_set_surface(cursor,
                                                                               surface.as_ptr(),
                                                                               hotspot_x,
                                                                               hotspot_y)
                                             });
            match res {
                Ok(_) | Err(UpgradeHandleErr::AlreadyDropped) => {}
                err @ Err(UpgradeHandleErr::AlreadyBorrowed) => panic!(err)
            }
        }
    }

    /// Moves the hardware cursor to the desired location
    pub fn move_to(&mut self, x: f64, y: f64) -> bool {
        unsafe {
            let cursor = self.cursor;
            let res = self.output_handle.run(|_| wlr_output_cursor_move(cursor, x, y));
            match res {
                Ok(res) => res,
                Err(UpgradeHandleErr::AlreadyDropped) => false,
                err @ Err(UpgradeHandleErr::AlreadyBorrowed) => panic!(err)
            }
        }
    }
}

impl Drop for OutputCursor {
    fn drop(&mut self) {
        unsafe { wlr_output_cursor_destroy(self.cursor) }
    }
}
<|endoftext|>


<commit_before>use cairo::{self, MatrixTrait};
use markup5ever::local_name;
use std::cell::Cell;

use crate::bbox::BoundingBox;
use crate::coord_units::CoordUnits;
use crate::drawing_ctx::{CompositingAffines, DrawingCtx};
use crate::error::RenderingError;
use crate::length::{LengthHorizontal, LengthVertical};
use crate::node::{CascadedValues, NodeDraw, NodeResult, NodeTrait, RsvgNode};
use crate::parsers::{Parse, ParseValue};
use crate::property_bag::PropertyBag;
use crate::property_defs::Opacity;
use crate::rect::IRect;
use crate::surface_utils::{
    iterators::Pixels,
    shared_surface::SharedImageSurface,
    shared_surface::SurfaceType,
    ImageSurfaceDataExt,
};
use crate::unit_interval::UnitInterval;

coord_units!(MaskUnits, CoordUnits::ObjectBoundingBox);
coord_units!(MaskContentUnits, CoordUnits::UserSpaceOnUse);

pub struct NodeMask {
    x: Cell<LengthHorizontal>,
    y: Cell<LengthVertical>,
    width: Cell<LengthHorizontal>,
    height: Cell<LengthVertical>,

    units: Cell<MaskUnits>,
    content_units: Cell<MaskContentUnits>,
}

impl Default for NodeMask {
    fn default() -> NodeMask {
        NodeMask {
            // these values are per the spec
            x: Cell::new(LengthHorizontal::parse_str("-10%").unwrap()),
            y: Cell::new(LengthVertical::parse_str("-10%").unwrap()),

            width: Cell::new(LengthHorizontal::parse_str("120%").unwrap()),
            height: Cell::new(LengthVertical::parse_str("120%").unwrap()),

            units: Cell::new(MaskUnits::default()),
            content_units: Cell::new(MaskContentUnits::default()),
        }
    }
}

impl NodeMask {
    pub fn generate_cairo_mask(
        &self,
        mask_node: &RsvgNode,
        affines: &CompositingAffines,
        draw_ctx: &mut DrawingCtx,
        bbox: &BoundingBox,
    ) -> Result<(), RenderingError> {
        if bbox.rect.is_none() {
            // The node being masked is empty / doesn't have a
            // bounding box, so there's nothing to mask!
            return Ok(());
        }

        let bbox_rect = bbox.rect.as_ref().unwrap();

        let cascaded = CascadedValues::new_from_node(mask_node);
        let values = cascaded.get();

        let mask_content_surface = draw_ctx.create_surface_for_toplevel_viewport()?;

        let mask_units = CoordUnits::from(self.units.get());
        let content_units = CoordUnits::from(self.content_units.get());

        let (x, y, w, h) = {
            let params = if mask_units == CoordUnits::ObjectBoundingBox {
                draw_ctx.push_view_box(1.0, 1.0)
            } else {
                draw_ctx.get_view_params()
            };

            let x = self.x.get().normalize(&values, &params);
            let y = self.y.get().normalize(&values, &params);
            let w = self.width.get().normalize(&values, &params);
            let h = self.height.get().normalize(&values, &params);

            (x, y, w, h)
        };

        // Use a scope because mask_cr needs to release the
        // reference to the surface before we access the pixels
        {
            let mask_cr = cairo::Context::new(&mask_content_surface);
            mask_cr.set_matrix(affines.for_temporary_surface);
            mask_cr.transform(mask_node.borrow().get_transform());

            draw_ctx.push_cairo_context(mask_cr);

            if mask_units == CoordUnits::ObjectBoundingBox {
                draw_ctx.clip(
                    x * bbox_rect.width + bbox_rect.x,
                    y * bbox_rect.height + bbox_rect.y,
                    w * bbox_rect.width,
                    h * bbox_rect.height,
                );
            } else {
                draw_ctx.clip(x, y, w, h);
            }

            {
                let _params = if content_units == CoordUnits::ObjectBoundingBox {
                    let bbtransform = cairo::Matrix::new(
                        bbox_rect.width,
                        0.0,
                        0.0,
                        bbox_rect.height,
                        bbox_rect.x,
                        bbox_rect.y,
                    );

                    draw_ctx.get_cairo_context().transform(bbtransform);

                    draw_ctx.push_view_box(1.0, 1.0)
                } else {
                    draw_ctx.get_view_params()
                };

                let res = draw_ctx.with_discrete_layer(mask_node, values, false, &mut |dc| {
                    mask_node.draw_children(&cascaded, dc, false)
                });

                draw_ctx.pop_cairo_context();

                res
            }
        }?;

        let Opacity(opacity) = values.opacity;

        let mask_content_surface =
            SharedImageSurface::new(mask_content_surface, SurfaceType::SRgb)?;

        let mask_surface = compute_luminance_to_alpha(&mask_content_surface, opacity)?;

        let cr = draw_ctx.get_cairo_context();
        cr.set_matrix(affines.compositing);

        cr.mask_surface(&mask_surface, 0.0, 0.0);

        Ok(())
    }
}

// Returns a surface whose alpha channel for each pixel is equal to the
// luminance of that pixel's unpremultiplied RGB values.  The resulting
// surface's RGB values are not meanignful; only the alpha channel has
// useful luminance data.
//
// This is to get a mask suitable for use with cairo_mask_surface().
fn compute_luminance_to_alpha(
    surface: &SharedImageSurface,
    opacity: UnitInterval,
) -> Result<cairo::ImageSurface, cairo::Status> {
    let width = surface.width();
    let height = surface.height();

    let bounds = IRect {
        x0: 0,
        y0: 0,
        x1: width,
        y1: height,
    };

    let opacity = u8::from(opacity);
    let mut output = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;
    let output_stride = output.get_stride() as usize;

    {
        let mut output_data = output.get_data().unwrap();

        for (x, y, pixel) in Pixels::new(surface, bounds) {
            output_data.set_pixel(output_stride, pixel.to_mask(opacity), x, y);
        }
    }

    Ok(output)
}

impl NodeTrait for NodeMask {
    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        for (attr, value) in pbag.iter() {
            match attr {
                local_name!("x") => self.x.set(attr.parse(value)?),
                local_name!("y") => self.y.set(attr.parse(value)?),
                local_name!("width") => self
                    .width
                    .set(attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)?),
                local_name!("height") => self
                    .height
                    .set(attr.parse_and_validate(value, LengthVertical::check_nonnegative)?),
                local_name!("maskUnits") => self.units.set(attr.parse(value)?),
                local_name!("maskContentUnits") => self.content_units.set(attr.parse(value)?),
                _ => (),
            }
        }

        Ok(())
    }
}
<commit_msg>mask: remove interior mutability<commit_after>use cairo::{self, MatrixTrait};
use markup5ever::local_name;

use crate::bbox::BoundingBox;
use crate::coord_units::CoordUnits;
use crate::drawing_ctx::{CompositingAffines, DrawingCtx};
use crate::error::RenderingError;
use crate::length::{LengthHorizontal, LengthVertical};
use crate::node::{CascadedValues, NodeDraw, NodeResult, NodeTrait, RsvgNode};
use crate::parsers::{Parse, ParseValue};
use crate::property_bag::PropertyBag;
use crate::property_defs::Opacity;
use crate::rect::IRect;
use crate::surface_utils::{
    iterators::Pixels,
    shared_surface::SharedImageSurface,
    shared_surface::SurfaceType,
    ImageSurfaceDataExt,
};
use crate::unit_interval::UnitInterval;

coord_units!(MaskUnits, CoordUnits::ObjectBoundingBox);
coord_units!(MaskContentUnits, CoordUnits::UserSpaceOnUse);

pub struct NodeMask {
    x: LengthHorizontal,
    y: LengthVertical,
    width: LengthHorizontal,
    height: LengthVertical,

    units: MaskUnits,
    content_units: MaskContentUnits,
}

impl Default for NodeMask {
    fn default() -> NodeMask {
        NodeMask {
            // these values are per the spec
            x: LengthHorizontal::parse_str("-10%").unwrap(),
            y: LengthVertical::parse_str("-10%").unwrap(),
            width: LengthHorizontal::parse_str("120%").unwrap(),
            height: LengthVertical::parse_str("120%").unwrap(),

            units: MaskUnits::default(),
            content_units: MaskContentUnits::default(),
        }
    }
}

impl NodeMask {
    pub fn generate_cairo_mask(
        &self,
        mask_node: &RsvgNode,
        affines: &CompositingAffines,
        draw_ctx: &mut DrawingCtx,
        bbox: &BoundingBox,
    ) -> Result<(), RenderingError> {
        if bbox.rect.is_none() {
            // The node being masked is empty / doesn't have a
            // bounding box, so there's nothing to mask!
            return Ok(());
        }

        let bbox_rect = bbox.rect.as_ref().unwrap();

        let cascaded = CascadedValues::new_from_node(mask_node);
        let values = cascaded.get();

        let mask_content_surface = draw_ctx.create_surface_for_toplevel_viewport()?;

        let mask_units = CoordUnits::from(self.units);
        let content_units = CoordUnits::from(self.content_units);

        let (x, y, w, h) = {
            let params = if mask_units == CoordUnits::ObjectBoundingBox {
                draw_ctx.push_view_box(1.0, 1.0)
            } else {
                draw_ctx.get_view_params()
            };

            let x = self.x.normalize(&values, &params);
            let y = self.y.normalize(&values, &params);
            let w = self.width.normalize(&values, &params);
            let h = self.height.normalize(&values, &params);

            (x, y, w, h)
        };

        // Use a scope because mask_cr needs to release the
        // reference to the surface before we access the pixels
        {
            let mask_cr = cairo::Context::new(&mask_content_surface);
            mask_cr.set_matrix(affines.for_temporary_surface);
            mask_cr.transform(mask_node.borrow().get_transform());

            draw_ctx.push_cairo_context(mask_cr);

            if mask_units == CoordUnits::ObjectBoundingBox {
                draw_ctx.clip(
                    x * bbox_rect.width + bbox_rect.x,
                    y * bbox_rect.height + bbox_rect.y,
                    w * bbox_rect.width,
                    h * bbox_rect.height,
                );
            } else {
                draw_ctx.clip(x, y, w, h);
            }

            {
                let _params = if content_units == CoordUnits::ObjectBoundingBox {
                    let bbtransform = cairo::Matrix::new(
                        bbox_rect.width,
                        0.0,
                        0.0,
                        bbox_rect.height,
                        bbox_rect.x,
                        bbox_rect.y,
                    );

                    draw_ctx.get_cairo_context().transform(bbtransform);

                    draw_ctx.push_view_box(1.0, 1.0)
                } else {
                    draw_ctx.get_view_params()
                };

                let res = draw_ctx.with_discrete_layer(mask_node, values, false, &mut |dc| {
                    mask_node.draw_children(&cascaded, dc, false)
                });

                draw_ctx.pop_cairo_context();

                res
            }
        }?;

        let Opacity(opacity) = values.opacity;

        let mask_content_surface =
            SharedImageSurface::new(mask_content_surface, SurfaceType::SRgb)?;

        let mask_surface = compute_luminance_to_alpha(&mask_content_surface, opacity)?;

        let cr = draw_ctx.get_cairo_context();
        cr.set_matrix(affines.compositing);

        cr.mask_surface(&mask_surface, 0.0, 0.0);

        Ok(())
    }
}

// Returns a surface whose alpha channel for each pixel is equal to the
// luminance of that pixel's unpremultiplied RGB values.  The resulting
// surface's RGB values are not meanignful; only the alpha channel has
// useful luminance data.
//
// This is to get a mask suitable for use with cairo_mask_surface().
fn compute_luminance_to_alpha(
    surface: &SharedImageSurface,
    opacity: UnitInterval,
) -> Result<cairo::ImageSurface, cairo::Status> {
    let width = surface.width();
    let height = surface.height();

    let bounds = IRect {
        x0: 0,
        y0: 0,
        x1: width,
        y1: height,
    };

    let opacity = u8::from(opacity);
    let mut output = cairo::ImageSurface::create(cairo::Format::ARgb32, width, height)?;
    let output_stride = output.get_stride() as usize;

    {
        let mut output_data = output.get_data().unwrap();

        for (x, y, pixel) in Pixels::new(surface, bounds) {
            output_data.set_pixel(output_stride, pixel.to_mask(opacity), x, y);
        }
    }

    Ok(output)
}

impl NodeTrait for NodeMask {
    fn set_atts(&mut self, _: Option<&RsvgNode>, pbag: &PropertyBag<'_>) -> NodeResult {
        for (attr, value) in pbag.iter() {
            match attr {
                local_name!("x") => self.x = attr.parse(value)?,
                local_name!("y") => self.y = attr.parse(value)?,
                local_name!("width") => {
                    self.width =
                        attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)?
                }
                local_name!("height") => {
                    self.height =
                        attr.parse_and_validate(value, LengthVertical::check_nonnegative)?
                }
                local_name!("maskUnits") => self.units = attr.parse(value)?,
                local_name!("maskContentUnits") => self.content_units = attr.parse(value)?,
                _ => (),
            }
        }

        Ok(())
    }
}
<|endoftext|>