<commit_before>/*
 * Cymbalum, Molecular Simulation in Rust
 * Copyright (C) 2015 Guillaume Fraux
 *
 * 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 std::collections::HashMap;
use std::cmp::max;

use ::potentials::{PairPotential, AnglePotential, DihedralPotential};

/// The Interaction type hold all data about the potentials in the system,
/// indexed by particle type.
pub struct Interactions {
    /// Pair potentials
    pairs: HashMap<(u16, u16), Vec<Box<PairPotential>>>,
    /// Bond potentials
    bonds: HashMap<(u16, u16), Vec<Box<PairPotential>>>,
    /// Angle potentials
    angles: HashMap<(u16, u16, u16), Vec<Box<AnglePotential>>>,
    /// Dihedral angles potentials
    dihedrals: HashMap<(u16, u16, u16, u16), Vec<Box<DihedralPotential>>>,
}

impl Interactions {
    pub fn new() -> Interactions {
        Interactions{
            pairs: HashMap::new(),
            bonds: HashMap::new(),
            angles: HashMap::new(),
            dihedrals: HashMap::new(),
        }
    }

    /// Add a pair interaction to the pair `(i, j)`
    pub fn add_pair<T>(&mut self, i: u16, j:u16, potential: T) where T: PairPotential + 'static {
        let (i, j) = sort_pair(i, j);
        if !self.pairs.contains_key(&(i, j)) {
            self.pairs.insert((i, j), Vec::new());
        }
        let pairs = self.pairs.get_mut(&(i, j)).unwrap();
        pairs.push(Box::new(potential));
    }

    /// Get all pair interactions corresponding to the pair `(i, j)`
    pub fn pairs<'a>(&'a self, i: u16, j:u16) -> Option<&'a Vec<Box<PairPotential>>> {
        let (i, j) = sort_pair(i, j);
        self.pairs.get(&(i, j))
    }

    /// Add a bonded interaction to the pair `(i, j)`
    pub fn add_bond<T>(&mut self, i: u16, j:u16, potential: T) where T: PairPotential + 'static {
        let (i, j) = sort_pair(i, j);
        if !self.bonds.contains_key(&(i, j)) {
            self.bonds.insert((i, j), Vec::new());
        }
        let bonds = self.bonds.get_mut(&(i, j)).unwrap();
        bonds.push(Box::new(potential));
    }

    /// Get all bonded interactions corresponding to the pair `(i, j)`
    pub fn bonds<'a>(&'a self, i: u16, j:u16) -> Option<&'a Vec<Box<PairPotential>>> {
        let (i, j) = sort_pair(i, j);
        self.bonds.get(&(i, j))
    }

    /// Add an angle interaction to the angle `(i, j, k)`
    pub fn add_angle<T>(&mut self, i: u16, j:u16, k:u16, potential: T) where T: AnglePotential + 'static {
        let (i, j, k) = sort_angle(i, j, k);
        if !self.angles.contains_key(&(i, j, k)) {
            self.angles.insert((i, j, k), Vec::new());
        }
        let angles = self.angles.get_mut(&(i, j, k)).unwrap();
        angles.push(Box::new(potential));
    }

    /// Get all angle interactions corresponding to the angle `(i, j, k)`
    pub fn angles<'a>(&'a self, i: u16, j:u16, k:u16) -> Option<&'a Vec<Box<AnglePotential>>> {
        let (i, j, k) = sort_angle(i, j, k);
        self.angles.get(&(i, j, k))
    }

    /// Add a dihedral interaction to the dihedral `(i, j, k, m)`
    pub fn add_dihedral<T>(&mut self, i: u16, j:u16, k:u16, m:u16, potential: T) where T: DihedralPotential + 'static {
        let (i, j, k, m) = sort_dihedral(i, j, k, m);
        if !self.dihedrals.contains_key(&(i, j, k, m)) {
            self.dihedrals.insert((i, j, k, m), Vec::new());
        }
        let dihedrals = self.dihedrals.get_mut(&(i, j, k, m)).unwrap();
        dihedrals.push(Box::new(potential));
    }

    /// Get all dihedral interactions corresponding to the dihedral `(i, j, k, m)`
    pub fn dihedrals<'a>(&'a self, i: u16, j:u16, k:u16, m:u16) -> Option<&'a Vec<Box<DihedralPotential>>> {
        let (i, j, k, m) = sort_dihedral(i, j, k, m);
        self.dihedrals.get(&(i, j, k, m))
    }
}

/// Sort pair indexes to get a cannonical representation
#[inline] fn sort_pair(i: u16, j:u16) -> (u16, u16) {
    if i < j {
        (i, j)
    } else {
        (j, i)
    }
}

/// Sort angle indexes to get a cannonical representation
#[inline] fn sort_angle(i: u16, j:u16, k:u16) -> (u16, u16, u16) {
    if i < k {
        (i, j, k)
    } else {
        (k, j, i)
    }
}

/// Sort dihedral indexes to get a cannonical representation
#[inline] fn sort_dihedral(i: u16, j:u16, k:u16, m:u16) -> (u16, u16, u16, u16) {
    if max(i, j) < max(k, m) {
        (i, j, k, m)
    } else {
        (m, k, j, i)
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use ::potentials::Harmonic;

    #[test]
    fn pairs() {
        let mut interactions = Interactions::new();

        interactions.add_pair(0, 3, Harmonic{x0: 0.0, k: 0.0});
        assert_eq!(interactions.pairs(0, 3).unwrap().len(), 1);
        assert_eq!(interactions.pairs(3, 0).unwrap().len(), 1);

        interactions.add_pair(0, 0, Harmonic{x0: 0.0, k: 0.0});
        assert_eq!(interactions.pairs(0, 0).unwrap().len(), 1);
    }

    #[test]
    fn bonds() {
        let mut interactions = Interactions::new();

        interactions.add_bond(0, 3, Harmonic{x0: 0.0, k: 0.0});
        assert_eq!(interactions.bonds(0, 3).unwrap().len(), 1);
        assert_eq!(interactions.bonds(3, 0).unwrap().len(), 1);

        interactions.add_bond(0, 0, Harmonic{x0: 0.0, k: 0.0});
        assert_eq!(interactions.bonds(0, 0).unwrap().len(), 1);
    }

    #[test]
    fn angles() {
        let mut interactions = Interactions::new();

        interactions.add_angle(0, 3, 7, Harmonic{x0: 0.0, k: 0.0});
        assert_eq!(interactions.angles(0, 3, 7).unwrap().len(), 1);
        assert_eq!(interactions.angles(7, 3, 0).unwrap().len(), 1);

        interactions.add_angle(0, 0, 7, Harmonic{x0: 0.0, k: 0.0});
        assert_eq!(interactions.angles(0, 0, 7).unwrap().len(), 1);
        assert_eq!(interactions.angles(7, 0, 0).unwrap().len(), 1);

        interactions.add_angle(42, 42, 42, Harmonic{x0: 0.0, k: 0.0});
        assert_eq!(interactions.angles(42, 42, 42).unwrap().len(), 1);
    }

    #[test]
    fn dihedrals() {
        let mut interactions = Interactions::new();

        interactions.add_dihedral(0, 3, 7, 2, Harmonic{x0: 0.0, k: 0.0});
        assert_eq!(interactions.dihedrals(0, 3, 7, 2).unwrap().len(), 1);
        assert_eq!(interactions.dihedrals(2, 7, 3, 0).unwrap().len(), 1);

        interactions.add_dihedral(0, 0, 7, 2, Harmonic{x0: 0.0, k: 0.0});
        assert_eq!(interactions.dihedrals(0, 0, 7, 2).unwrap().len(), 1);
        assert_eq!(interactions.dihedrals(2, 7, 0, 0).unwrap().len(), 1);

        interactions.add_dihedral(0, 0, 9, 0, Harmonic{x0: 0.0, k: 0.0});
        assert_eq!(interactions.dihedrals(0, 0, 9, 0).unwrap().len(), 1);
        assert_eq!(interactions.dihedrals(0, 9, 0, 0).unwrap().len(), 1);

        interactions.add_dihedral(42, 42, 42, 42, Harmonic{x0: 0.0, k: 0.0});
        assert_eq!(interactions.dihedrals(42, 42, 42, 42).unwrap().len(), 1);
    }
}
<commit_msg>Use BTreeMap instead of HashMap for Interaction<commit_after>/*
 * Cymbalum, Molecular Simulation in Rust
 * Copyright (C) 2015 Guillaume Fraux
 *
 * 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 std::collections::BTreeMap;
use std::cmp::max;

use ::potentials::{PairPotential, AnglePotential, DihedralPotential};

/// The Interaction type hold all data about the potentials in the system,
/// indexed by particle type.
pub struct Interactions {
    /// Pair potentials
    pairs: BTreeMap<(u16, u16), Vec<Box<PairPotential>>>,
    /// Bond potentials
    bonds: BTreeMap<(u16, u16), Vec<Box<PairPotential>>>,
    /// Angle potentials
    angles: BTreeMap<(u16, u16, u16), Vec<Box<AnglePotential>>>,
    /// Dihedral angles potentials
    dihedrals: BTreeMap<(u16, u16, u16, u16), Vec<Box<DihedralPotential>>>,
}

impl Interactions {
    pub fn new() -> Interactions {
        Interactions{
            pairs: BTreeMap::new(),
            bonds: BTreeMap::new(),
            angles: BTreeMap::new(),
            dihedrals: BTreeMap::new(),
        }
    }

    /// Add a pair interaction to the pair `(i, j)`
    pub fn add_pair<T>(&mut self, i: u16, j:u16, potential: T) where T: PairPotential + 'static {
        let (i, j) = sort_pair(i, j);
        if !self.pairs.contains_key(&(i, j)) {
            self.pairs.insert((i, j), Vec::new());
        }
        let pairs = self.pairs.get_mut(&(i, j)).unwrap();
        pairs.push(Box::new(potential));
    }

    /// Get all pair interactions corresponding to the pair `(i, j)`
    pub fn pairs<'a>(&'a self, i: u16, j:u16) -> Option<&'a Vec<Box<PairPotential>>> {
        let (i, j) = sort_pair(i, j);
        self.pairs.get(&(i, j))
    }

    /// Add a bonded interaction to the pair `(i, j)`
    pub fn add_bond<T>(&mut self, i: u16, j:u16, potential: T) where T: PairPotential + 'static {
        let (i, j) = sort_pair(i, j);
        if !self.bonds.contains_key(&(i, j)) {
            self.bonds.insert((i, j), Vec::new());
        }
        let bonds = self.bonds.get_mut(&(i, j)).unwrap();
        bonds.push(Box::new(potential));
    }

    /// Get all bonded interactions corresponding to the pair `(i, j)`
    pub fn bonds<'a>(&'a self, i: u16, j:u16) -> Option<&'a Vec<Box<PairPotential>>> {
        let (i, j) = sort_pair(i, j);
        self.bonds.get(&(i, j))
    }

    /// Add an angle interaction to the angle `(i, j, k)`
    pub fn add_angle<T>(&mut self, i: u16, j:u16, k:u16, potential: T) where T: AnglePotential + 'static {
        let (i, j, k) = sort_angle(i, j, k);
        if !self.angles.contains_key(&(i, j, k)) {
            self.angles.insert((i, j, k), Vec::new());
        }
        let angles = self.angles.get_mut(&(i, j, k)).unwrap();
        angles.push(Box::new(potential));
    }

    /// Get all angle interactions corresponding to the angle `(i, j, k)`
    pub fn angles<'a>(&'a self, i: u16, j:u16, k:u16) -> Option<&'a Vec<Box<AnglePotential>>> {
        let (i, j, k) = sort_angle(i, j, k);
        self.angles.get(&(i, j, k))
    }

    /// Add a dihedral interaction to the dihedral `(i, j, k, m)`
    pub fn add_dihedral<T>(&mut self, i: u16, j:u16, k:u16, m:u16, potential: T) where T: DihedralPotential + 'static {
        let (i, j, k, m) = sort_dihedral(i, j, k, m);
        if !self.dihedrals.contains_key(&(i, j, k, m)) {
            self.dihedrals.insert((i, j, k, m), Vec::new());
        }
        let dihedrals = self.dihedrals.get_mut(&(i, j, k, m)).unwrap();
        dihedrals.push(Box::new(potential));
    }

    /// Get all dihedral interactions corresponding to the dihedral `(i, j, k, m)`
    pub fn dihedrals<'a>(&'a self, i: u16, j:u16, k:u16, m:u16) -> Option<&'a Vec<Box<DihedralPotential>>> {
        let (i, j, k, m) = sort_dihedral(i, j, k, m);
        self.dihedrals.get(&(i, j, k, m))
    }
}

/// Sort pair indexes to get a cannonical representation
#[inline] fn sort_pair(i: u16, j:u16) -> (u16, u16) {
    if i < j {
        (i, j)
    } else {
        (j, i)
    }
}

/// Sort angle indexes to get a cannonical representation
#[inline] fn sort_angle(i: u16, j:u16, k:u16) -> (u16, u16, u16) {
    if i < k {
        (i, j, k)
    } else {
        (k, j, i)
    }
}

/// Sort dihedral indexes to get a cannonical representation
#[inline] fn sort_dihedral(i: u16, j:u16, k:u16, m:u16) -> (u16, u16, u16, u16) {
    if max(i, j) < max(k, m) {
        (i, j, k, m)
    } else {
        (m, k, j, i)
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use ::potentials::Harmonic;

    #[test]
    fn pairs() {
        let mut interactions = Interactions::new();

        interactions.add_pair(0, 3, Harmonic{x0: 0.0, k: 0.0});
        assert_eq!(interactions.pairs(0, 3).unwrap().len(), 1);
        assert_eq!(interactions.pairs(3, 0).unwrap().len(), 1);

        interactions.add_pair(0, 0, Harmonic{x0: 0.0, k: 0.0});
        assert_eq!(interactions.pairs(0, 0).unwrap().len(), 1);
    }

    #[test]
    fn bonds() {
        let mut interactions = Interactions::new();

        interactions.add_bond(0, 3, Harmonic{x0: 0.0, k: 0.0});
        assert_eq!(interactions.bonds(0, 3).unwrap().len(), 1);
        assert_eq!(interactions.bonds(3, 0).unwrap().len(), 1);

        interactions.add_bond(0, 0, Harmonic{x0: 0.0, k: 0.0});
        assert_eq!(interactions.bonds(0, 0).unwrap().len(), 1);
    }

    #[test]
    fn angles() {
        let mut interactions = Interactions::new();

        interactions.add_angle(0, 3, 7, Harmonic{x0: 0.0, k: 0.0});
        assert_eq!(interactions.angles(0, 3, 7).unwrap().len(), 1);
        assert_eq!(interactions.angles(7, 3, 0).unwrap().len(), 1);

        interactions.add_angle(0, 0, 7, Harmonic{x0: 0.0, k: 0.0});
        assert_eq!(interactions.angles(0, 0, 7).unwrap().len(), 1);
        assert_eq!(interactions.angles(7, 0, 0).unwrap().len(), 1);

        interactions.add_angle(42, 42, 42, Harmonic{x0: 0.0, k: 0.0});
        assert_eq!(interactions.angles(42, 42, 42).unwrap().len(), 1);
    }

    #[test]
    fn dihedrals() {
        let mut interactions = Interactions::new();

        interactions.add_dihedral(0, 3, 7, 2, Harmonic{x0: 0.0, k: 0.0});
        assert_eq!(interactions.dihedrals(0, 3, 7, 2).unwrap().len(), 1);
        assert_eq!(interactions.dihedrals(2, 7, 3, 0).unwrap().len(), 1);

        interactions.add_dihedral(0, 0, 7, 2, Harmonic{x0: 0.0, k: 0.0});
        assert_eq!(interactions.dihedrals(0, 0, 7, 2).unwrap().len(), 1);
        assert_eq!(interactions.dihedrals(2, 7, 0, 0).unwrap().len(), 1);

        interactions.add_dihedral(0, 0, 9, 0, Harmonic{x0: 0.0, k: 0.0});
        assert_eq!(interactions.dihedrals(0, 0, 9, 0).unwrap().len(), 1);
        assert_eq!(interactions.dihedrals(0, 9, 0, 0).unwrap().len(), 1);

        interactions.add_dihedral(42, 42, 42, 42, Harmonic{x0: 0.0, k: 0.0});
        assert_eq!(interactions.dihedrals(42, 42, 42, 42).unwrap().len(), 1);
    }
}
<|endoftext|>


<commit_before>use rustc::lint::*;
use rustc::ty;
use rustc::hir::*;
use utils::{match_def_path, paths, span_note_and_lint};

/// **What it does:** Checks for calls to `std::mem::drop` with a value
/// that derives the Copy trait
///
/// **Why is this bad?** Calling `std::mem::drop` [does nothing for types that
/// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html), since the
/// value will be copied and moved into the function on invocation.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// let x:i32 = 42;   // i32 implements Copy
/// std::mem::drop(x) // A copy of x is passed to the function, leaving the original unaffected
/// ```
declare_lint! {
    pub DROP_COPY,
    Warn,
    "calls to `std::mem::drop` with a value that implements Copy"
}

/// **What it does:** Checks for calls to `std::mem::forget` with a value that
/// derives the Copy trait
///
/// **Why is this bad?** Calling `std::mem::forget` [does nothing for types that
/// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html) since the
/// value will be copied and moved into the function on invocation.
///
/// An alternative, but also valid, explanation is that Copy types do not implement
/// the Drop trait, which means they have no destructors. Without a destructor, there
/// is nothing for `std::mem::forget` to ignore.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// let x:i32 = 42;     // i32 implements Copy
/// std::mem::forget(x) // A copy of x is passed to the function, leaving the original unaffected
/// ```
declare_lint! {
    pub FORGET_COPY,
    Warn,
    "calls to `std::mem::forget` with a value that implements Copy"
}

#[allow(missing_copy_implementations)]
pub struct Pass;

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(DROP_COPY, FORGET_COPY)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
        if_let_chain!{[
            let ExprCall(ref path, ref args) = expr.node,
            let ExprPath(ref qpath) = path.node,
            args.len() == 1,
        ], {
            let def_id = cx.tables.qpath_def(qpath, path.id).def_id();
            let lint;
            let msg;

            if match_def_path(cx.tcx, def_id, &paths::DROP) {
                lint = DROP_COPY;
                msg = "call to `std::mem::drop` with a value that implements Copy. \
                       Dropping a copy leaves the original intact.";
            } else if match_def_path(cx.tcx, def_id, &paths::MEM_FORGET) {
                lint = FORGET_COPY;
                msg = "call to `std::mem::forget` with a value that implements Copy. \
                       Forgetting a copy leaves the original intact.";
            } else {
                return;
            }

            let arg = &args[0];
            let arg_ty = cx.tables.expr_ty(arg);

            let parameter_environment = ty::ParameterEnvironment::for_item(cx.tcx, arg.id);
            if !arg_ty.moves_by_default(cx.tcx.global_tcx(), &parameter_environment, arg.span) {
                span_note_and_lint(cx,
                                   lint,
                                   expr.span,
                                   msg,
                                   arg.span,
                                   &format!("argument has type {}", arg_ty.sty));
            }
        }}
    }
}
<commit_msg>Stored linter messages in constants. Use is_copy() function instead of calling moves_by_default()<commit_after>use rustc::lint::*;
use rustc::hir::*;
use utils::{match_def_path, paths, span_note_and_lint, is_copy};

const DROP_COPY_SUMMARY:&'static str = "calls to `std::mem::drop` with a value that implements Copy";
const FORGET_COPY_SUMMARY:&'static str = "calls to `std::mem::forget` with a value that implements Copy";

/// **What it does:** Checks for calls to `std::mem::drop` with a value
/// that derives the Copy trait
///
/// **Why is this bad?** Calling `std::mem::drop` [does nothing for types that
/// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html), since the
/// value will be copied and moved into the function on invocation.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// let x:i32 = 42;   // i32 implements Copy
/// std::mem::drop(x) // A copy of x is passed to the function, leaving the original unaffected
/// ```
declare_lint! {
    pub DROP_COPY,
    Warn,
    DROP_COPY_SUMMARY
}

/// **What it does:** Checks for calls to `std::mem::forget` with a value that
/// derives the Copy trait
///
/// **Why is this bad?** Calling `std::mem::forget` [does nothing for types that
/// implement Copy](https://doc.rust-lang.org/std/mem/fn.drop.html) since the
/// value will be copied and moved into the function on invocation.
///
/// An alternative, but also valid, explanation is that Copy types do not implement
/// the Drop trait, which means they have no destructors. Without a destructor, there
/// is nothing for `std::mem::forget` to ignore.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust
/// let x:i32 = 42;     // i32 implements Copy
/// std::mem::forget(x) // A copy of x is passed to the function, leaving the original unaffected
/// ```
declare_lint! {
    pub FORGET_COPY,
    Warn,
    FORGET_COPY_SUMMARY
}

#[allow(missing_copy_implementations)]
pub struct Pass;

impl LintPass for Pass {
    fn get_lints(&self) -> LintArray {
        lint_array!(DROP_COPY, FORGET_COPY)
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
    fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
        if_let_chain!{[
            let ExprCall(ref path, ref args) = expr.node,
            let ExprPath(ref qpath) = path.node,
            args.len() == 1,
        ], {
            let def_id = cx.tables.qpath_def(qpath, path.id).def_id();
            let lint;
            let msg;

            if match_def_path(cx.tcx, def_id, &paths::DROP) {
                lint = DROP_COPY;
                msg = DROP_COPY_SUMMARY.to_string() + ". Dropping a copy leaves the original intact.";
            } else if match_def_path(cx.tcx, def_id, &paths::MEM_FORGET) {
                lint = FORGET_COPY;
                msg = FORGET_COPY_SUMMARY.to_string() + ". Forgetting a copy leaves the original intact.";
            } else {
                return;
            }

            let arg = &args[0];
            let arg_ty = cx.tables.expr_ty(arg);
            if is_copy(cx, arg_ty, cx.tcx.hir.get_parent(arg.id)) {
                span_note_and_lint(cx,
                                   lint,
                                   expr.span,
                                   &msg,
                                   arg.span,
                                   &format!("argument has type {}", arg_ty.sty));
            }
        }}
    }
}
<|endoftext|>


<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 https://mozilla.org/MPL/2.0/. */

use crate::context::LayoutContext;
use crate::dom_traversal::{Contents, NodeExt};
use crate::formatting_contexts::IndependentFormattingContext;
use crate::fragments::{AnonymousFragment, BoxFragment, CollapsedBlockMargins, Fragment};
use crate::geom::flow_relative::{Rect, Sides, Vec2};
use crate::sizing::ContentSizesRequest;
use crate::style_ext::{ComputedValuesExt, DisplayInside};
use crate::{ContainingBlock, DefiniteContainingBlock};
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use servo_arc::Arc;
use style::properties::ComputedValues;
use style::values::computed::{Length, LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
use style::Zero;

#[derive(Debug)]
pub(crate) struct AbsolutelyPositionedBox {
    pub contents: IndependentFormattingContext,
}

#[derive(Debug)]
pub(crate) struct AbsolutelyPositionedFragment<'box_> {
    absolutely_positioned_box: &'box_ AbsolutelyPositionedBox,

    /// The rank of the child from which this absolutely positioned fragment
    /// came from, when doing the layout of a block container. Used to compute
    /// static positions when going up the tree.
    pub(crate) tree_rank: usize,

    pub(crate) inline_start: AbsoluteBoxOffsets<LengthPercentage>,
    inline_size: LengthPercentageOrAuto,

    pub(crate) block_start: AbsoluteBoxOffsets<LengthPercentage>,
    block_size: LengthPercentageOrAuto,
}

#[derive(Clone, Copy, Debug)]
pub(crate) enum AbsoluteBoxOffsets<NonStatic> {
    StaticStart { start: Length },
    Start { start: NonStatic },
    End { end: NonStatic },
    Both { start: NonStatic, end: NonStatic },
}

impl AbsolutelyPositionedBox {
    pub fn construct<'dom>(
        context: &LayoutContext,
        style: Arc<ComputedValues>,
        display_inside: DisplayInside,
        contents: Contents<impl NodeExt<'dom>>,
    ) -> Self {
        // "Shrink-to-fit" in https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width
        let content_sizes = ContentSizesRequest::inline_if(
            // If inline-size is non-auto, that value is used without shrink-to-fit
            style.inline_size_is_auto() &&
            // If it is, then the only case where shrink-to-fit is *not* used is
            // if both offsets are non-auto, leaving inline-size as the only variable
            // in the constraint equation.
            !style.inline_box_offsets_are_both_non_auto(),
        );
        Self {
            contents: IndependentFormattingContext::construct(
                context,
                style,
                display_inside,
                contents,
                content_sizes,
            ),
        }
    }

    pub(crate) fn layout<'a>(
        &'a self,
        initial_start_corner: Vec2<Length>,
        tree_rank: usize,
    ) -> AbsolutelyPositionedFragment {
        let style = &self.contents.style;
        let box_offsets = style.box_offsets();
        let box_size = style.box_size();

        let inline_size = box_size.inline;
        let block_size = box_size.block;

        fn absolute_box_offsets(
            initial_static_start: Length,
            start: LengthPercentageOrAuto,
            end: LengthPercentageOrAuto,
        ) -> AbsoluteBoxOffsets<LengthPercentage> {
            match (start.non_auto(), end.non_auto()) {
                (None, None) => AbsoluteBoxOffsets::StaticStart {
                    start: initial_static_start,
                },
                (Some(start), Some(end)) => AbsoluteBoxOffsets::Both { start, end },
                (None, Some(end)) => AbsoluteBoxOffsets::End { end },
                (Some(start), None) => AbsoluteBoxOffsets::Start { start },
            }
        }

        let inline_start = absolute_box_offsets(
            initial_start_corner.inline,
            box_offsets.inline_start,
            box_offsets.inline_end,
        );
        let block_start = absolute_box_offsets(
            initial_start_corner.block,
            box_offsets.block_start,
            box_offsets.block_end,
        );

        AbsolutelyPositionedFragment {
            absolutely_positioned_box: self,
            tree_rank,
            inline_start,
            inline_size,
            block_start,
            block_size,
        }
    }
}

impl<'a> AbsolutelyPositionedFragment<'a> {
    pub(crate) fn in_positioned_containing_block(
        layout_context: &LayoutContext,
        absolute: &[Self],
        fragments: &mut Vec<Fragment>,
        content_rect_size: &Vec2<Length>,
        padding: &Sides<Length>,
        style: &ComputedValues,
    ) {
        if absolute.is_empty() {
            return;
        }
        let padding_rect = Rect {
            size: content_rect_size.clone(),
            // Ignore the content rect’s position in its own containing block:
            start_corner: Vec2::zero(),
        }
        .inflate(&padding);
        let containing_block = DefiniteContainingBlock {
            size: padding_rect.size.clone(),
            style,
        };
        fragments.push(Fragment::Anonymous(AnonymousFragment {
            children: absolute
                .par_iter()
                .map(|a| a.layout(layout_context, &containing_block))
                .collect(),
            rect: padding_rect,
            mode: style.writing_mode,
        }))
    }

    pub(crate) fn layout(
        &self,
        layout_context: &LayoutContext,
        containing_block: &DefiniteContainingBlock,
    ) -> Fragment {
        let style = &self.absolutely_positioned_box.contents.style;
        let cbis = containing_block.size.inline;
        let cbbs = containing_block.size.block;

        let padding = style.padding().percentages_relative_to(cbis);
        let border = style.border_width();
        let computed_margin = style.margin().percentages_relative_to(cbis);
        let pb = &padding + &border;

        enum Anchor {
            Start(Length),
            End(Length),
        }

        fn solve_axis(
            containing_size: Length,
            padding_border_sum: Length,
            computed_margin_start: LengthOrAuto,
            computed_margin_end: LengthOrAuto,
            solve_margins: impl FnOnce(Length) -> (Length, Length),
            box_offsets: AbsoluteBoxOffsets<LengthPercentage>,
            size: LengthPercentageOrAuto,
        ) -> (Anchor, LengthOrAuto, Length, Length) {
            let size = size.percentage_relative_to(containing_size);
            match box_offsets {
                AbsoluteBoxOffsets::StaticStart { start } => (
                    Anchor::Start(start),
                    size,
                    computed_margin_start.auto_is(Length::zero),
                    computed_margin_end.auto_is(Length::zero),
                ),
                AbsoluteBoxOffsets::Start { start } => (
                    Anchor::Start(start.percentage_relative_to(containing_size)),
                    size,
                    computed_margin_start.auto_is(Length::zero),
                    computed_margin_end.auto_is(Length::zero),
                ),
                AbsoluteBoxOffsets::End { end } => (
                    Anchor::End(end.percentage_relative_to(containing_size)),
                    size,
                    computed_margin_start.auto_is(Length::zero),
                    computed_margin_end.auto_is(Length::zero),
                ),
                AbsoluteBoxOffsets::Both { start, end } => {
                    let start = start.percentage_relative_to(containing_size);
                    let end = end.percentage_relative_to(containing_size);

                    let mut margin_start = computed_margin_start.auto_is(Length::zero);
                    let mut margin_end = computed_margin_end.auto_is(Length::zero);

                    let size = if let LengthOrAuto::LengthPercentage(size) = size {
                        let margins = containing_size - start - end - padding_border_sum - size;
                        match (computed_margin_start, computed_margin_end) {
                            (LengthOrAuto::Auto, LengthOrAuto::Auto) => {
                                let (s, e) = solve_margins(margins);
                                margin_start = s;
                                margin_end = e;
                            },
                            (LengthOrAuto::Auto, LengthOrAuto::LengthPercentage(end)) => {
                                margin_start = margins - end;
                            },
                            (LengthOrAuto::LengthPercentage(start), LengthOrAuto::Auto) => {
                                margin_end = margins - start;
                            },
                            (
                                LengthOrAuto::LengthPercentage(_),
                                LengthOrAuto::LengthPercentage(_),
                            ) => {},
                        }
                        size
                    } else {
                        // FIXME(nox): What happens if that is negative?
                        containing_size -
                            start -
                            end -
                            padding_border_sum -
                            margin_start -
                            margin_end
                    };
                    (
                        Anchor::Start(start),
                        LengthOrAuto::LengthPercentage(size),
                        margin_start,
                        margin_end,
                    )
                },
            }
        }

        let (inline_anchor, inline_size, margin_inline_start, margin_inline_end) = solve_axis(
            cbis,
            pb.inline_sum(),
            computed_margin.inline_start,
            computed_margin.inline_end,
            |margins| {
                if margins.px() >= 0. {
                    (margins / 2., margins / 2.)
                } else {
                    (Length::zero(), margins)
                }
            },
            self.inline_start,
            self.inline_size,
        );

        let (block_anchor, block_size, margin_block_start, margin_block_end) = solve_axis(
            cbis,
            pb.block_sum(),
            computed_margin.block_start,
            computed_margin.block_end,
            |margins| (margins / 2., margins / 2.),
            self.block_start,
            self.block_size,
        );

        let margin = Sides {
            inline_start: margin_inline_start,
            inline_end: margin_inline_end,
            block_start: margin_block_start,
            block_end: margin_block_end,
        };

        let inline_size = inline_size.auto_is(|| {
            let available_size = match inline_anchor {
                Anchor::Start(start) => cbis - start - pb.inline_sum() - margin.inline_sum(),
                Anchor::End(end) => cbis - end - pb.inline_sum() - margin.inline_sum(),
            };

            if self
                .absolutely_positioned_box
                .contents
                .as_replaced()
                .is_ok()
            {
                // FIXME: implement https://drafts.csswg.org/css2/visudet.html#abs-replaced-width
                available_size
            } else {
                self.absolutely_positioned_box
                    .contents
                    .content_sizes
                    .shrink_to_fit(available_size)
            }
        });

        let mut absolutely_positioned_fragments = Vec::new();
        let mut independent_layout = match self.absolutely_positioned_box.contents.as_replaced() {
            Ok(replaced) => {
                // FIXME: implement https://drafts.csswg.org/css2/visudet.html#abs-replaced-width
                // and https://drafts.csswg.org/css2/visudet.html#abs-replaced-height
                let block_size = block_size.auto_is(Length::zero);
                let fragments = replaced.make_fragments(
                    &self.absolutely_positioned_box.contents.style,
                    Vec2 {
                        inline: inline_size,
                        block: block_size,
                    },
                );
                crate::formatting_contexts::IndependentLayout {
                    fragments,
                    content_block_size: block_size,
                }
            },
            Err(non_replaced) => {
                let containing_block_for_children = ContainingBlock {
                    inline_size,
                    block_size,
                    style,
                };
                // https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
                assert_eq!(
                    containing_block.style.writing_mode,
                    containing_block_for_children.style.writing_mode,
                    "Mixed writing modes are not supported yet"
                );
                let dummy_tree_rank = 0;
                non_replaced.layout(
                    layout_context,
                    &containing_block_for_children,
                    dummy_tree_rank,
                    &mut absolutely_positioned_fragments,
                )
            },
        };

        let inline_start = match inline_anchor {
            Anchor::Start(start) => start + pb.inline_start + margin.inline_start,
            Anchor::End(end) => cbbs - end - pb.inline_end - margin.inline_end - inline_size,
        };

        let block_size = block_size.auto_is(|| independent_layout.content_block_size);
        let block_start = match block_anchor {
            Anchor::Start(start) => start + pb.block_start + margin.block_start,
            Anchor::End(end) => cbbs - end - pb.block_end - margin.block_end - block_size,
        };

        let content_rect = Rect {
            start_corner: Vec2 {
                inline: inline_start,
                block: block_start,
            },
            size: Vec2 {
                inline: inline_size,
                block: block_size,
            },
        };

        AbsolutelyPositionedFragment::in_positioned_containing_block(
            layout_context,
            &absolutely_positioned_fragments,
            &mut independent_layout.fragments,
            &content_rect.size,
            &padding,
            style,
        );

        Fragment::Box(BoxFragment {
            style: style.clone(),
            children: independent_layout.fragments,
            content_rect,
            padding,
            border,
            margin,
            block_margins_collapsed_with_children: CollapsedBlockMargins::zero(),
        })
    }
}

pub(crate) fn adjust_static_positions(
    absolutely_positioned_fragments: &mut [AbsolutelyPositionedFragment],
    child_fragments: &mut [Fragment],
    tree_rank_in_parent: usize,
) {
    for abspos_fragment in absolutely_positioned_fragments {
        let child_fragment_rect = match &child_fragments[abspos_fragment.tree_rank] {
            Fragment::Box(b) => &b.content_rect,
            Fragment::Anonymous(a) => &a.rect,
            _ => unreachable!(),
        };

        abspos_fragment.tree_rank = tree_rank_in_parent;

        if let AbsoluteBoxOffsets::StaticStart { start } = &mut abspos_fragment.inline_start {
            *start += child_fragment_rect.start_corner.inline;
        }

        if let AbsoluteBoxOffsets::StaticStart { start } = &mut abspos_fragment.block_start {
            *start += child_fragment_rect.start_corner.block;
        }
    }
}
<commit_msg>Remove inline/block_size from AbsolutelyPositionedFragment<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 https://mozilla.org/MPL/2.0/. */

use crate::context::LayoutContext;
use crate::dom_traversal::{Contents, NodeExt};
use crate::formatting_contexts::IndependentFormattingContext;
use crate::fragments::{AnonymousFragment, BoxFragment, CollapsedBlockMargins, Fragment};
use crate::geom::flow_relative::{Rect, Sides, Vec2};
use crate::sizing::ContentSizesRequest;
use crate::style_ext::{ComputedValuesExt, DisplayInside};
use crate::{ContainingBlock, DefiniteContainingBlock};
use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
use servo_arc::Arc;
use style::properties::ComputedValues;
use style::values::computed::{Length, LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
use style::Zero;

#[derive(Debug)]
pub(crate) struct AbsolutelyPositionedBox {
    pub contents: IndependentFormattingContext,
}

#[derive(Debug)]
pub(crate) struct AbsolutelyPositionedFragment<'box_> {
    absolutely_positioned_box: &'box_ AbsolutelyPositionedBox,

    /// The rank of the child from which this absolutely positioned fragment
    /// came from, when doing the layout of a block container. Used to compute
    /// static positions when going up the tree.
    pub(crate) tree_rank: usize,

    pub(crate) inline_start: AbsoluteBoxOffsets<LengthPercentage>,
    pub(crate) block_start: AbsoluteBoxOffsets<LengthPercentage>,
}

#[derive(Clone, Copy, Debug)]
pub(crate) enum AbsoluteBoxOffsets<NonStatic> {
    StaticStart { start: Length },
    Start { start: NonStatic },
    End { end: NonStatic },
    Both { start: NonStatic, end: NonStatic },
}

impl AbsolutelyPositionedBox {
    pub fn construct<'dom>(
        context: &LayoutContext,
        style: Arc<ComputedValues>,
        display_inside: DisplayInside,
        contents: Contents<impl NodeExt<'dom>>,
    ) -> Self {
        // "Shrink-to-fit" in https://drafts.csswg.org/css2/visudet.html#abs-non-replaced-width
        let content_sizes = ContentSizesRequest::inline_if(
            // If inline-size is non-auto, that value is used without shrink-to-fit
            style.inline_size_is_auto() &&
            // If it is, then the only case where shrink-to-fit is *not* used is
            // if both offsets are non-auto, leaving inline-size as the only variable
            // in the constraint equation.
            !style.inline_box_offsets_are_both_non_auto(),
        );
        Self {
            contents: IndependentFormattingContext::construct(
                context,
                style,
                display_inside,
                contents,
                content_sizes,
            ),
        }
    }

    pub(crate) fn layout<'a>(
        &'a self,
        initial_start_corner: Vec2<Length>,
        tree_rank: usize,
    ) -> AbsolutelyPositionedFragment {
        let style = &self.contents.style;
        let box_offsets = style.box_offsets();

        fn absolute_box_offsets(
            initial_static_start: Length,
            start: LengthPercentageOrAuto,
            end: LengthPercentageOrAuto,
        ) -> AbsoluteBoxOffsets<LengthPercentage> {
            match (start.non_auto(), end.non_auto()) {
                (None, None) => AbsoluteBoxOffsets::StaticStart {
                    start: initial_static_start,
                },
                (Some(start), Some(end)) => AbsoluteBoxOffsets::Both { start, end },
                (None, Some(end)) => AbsoluteBoxOffsets::End { end },
                (Some(start), None) => AbsoluteBoxOffsets::Start { start },
            }
        }

        let inline_start = absolute_box_offsets(
            initial_start_corner.inline,
            box_offsets.inline_start,
            box_offsets.inline_end,
        );
        let block_start = absolute_box_offsets(
            initial_start_corner.block,
            box_offsets.block_start,
            box_offsets.block_end,
        );

        AbsolutelyPositionedFragment {
            absolutely_positioned_box: self,
            tree_rank,
            inline_start,
            block_start,
        }
    }
}

impl<'a> AbsolutelyPositionedFragment<'a> {
    pub(crate) fn in_positioned_containing_block(
        layout_context: &LayoutContext,
        absolute: &[Self],
        fragments: &mut Vec<Fragment>,
        content_rect_size: &Vec2<Length>,
        padding: &Sides<Length>,
        style: &ComputedValues,
    ) {
        if absolute.is_empty() {
            return;
        }
        let padding_rect = Rect {
            size: content_rect_size.clone(),
            // Ignore the content rect’s position in its own containing block:
            start_corner: Vec2::zero(),
        }
        .inflate(&padding);
        let containing_block = DefiniteContainingBlock {
            size: padding_rect.size.clone(),
            style,
        };
        fragments.push(Fragment::Anonymous(AnonymousFragment {
            children: absolute
                .par_iter()
                .map(|a| a.layout(layout_context, &containing_block))
                .collect(),
            rect: padding_rect,
            mode: style.writing_mode,
        }))
    }

    pub(crate) fn layout(
        &self,
        layout_context: &LayoutContext,
        containing_block: &DefiniteContainingBlock,
    ) -> Fragment {
        let style = &self.absolutely_positioned_box.contents.style;
        let cbis = containing_block.size.inline;
        let cbbs = containing_block.size.block;

        let box_size = style.box_size();
        let padding = style.padding().percentages_relative_to(cbis);
        let border = style.border_width();
        let computed_margin = style.margin().percentages_relative_to(cbis);
        let pb = &padding + &border;

        enum Anchor {
            Start(Length),
            End(Length),
        }

        fn solve_axis(
            containing_size: Length,
            padding_border_sum: Length,
            computed_margin_start: LengthOrAuto,
            computed_margin_end: LengthOrAuto,
            solve_margins: impl FnOnce(Length) -> (Length, Length),
            box_offsets: AbsoluteBoxOffsets<LengthPercentage>,
            size: LengthPercentageOrAuto,
        ) -> (Anchor, LengthOrAuto, Length, Length) {
            let size = size.percentage_relative_to(containing_size);
            match box_offsets {
                AbsoluteBoxOffsets::StaticStart { start } => (
                    Anchor::Start(start),
                    size,
                    computed_margin_start.auto_is(Length::zero),
                    computed_margin_end.auto_is(Length::zero),
                ),
                AbsoluteBoxOffsets::Start { start } => (
                    Anchor::Start(start.percentage_relative_to(containing_size)),
                    size,
                    computed_margin_start.auto_is(Length::zero),
                    computed_margin_end.auto_is(Length::zero),
                ),
                AbsoluteBoxOffsets::End { end } => (
                    Anchor::End(end.percentage_relative_to(containing_size)),
                    size,
                    computed_margin_start.auto_is(Length::zero),
                    computed_margin_end.auto_is(Length::zero),
                ),
                AbsoluteBoxOffsets::Both { start, end } => {
                    let start = start.percentage_relative_to(containing_size);
                    let end = end.percentage_relative_to(containing_size);

                    let mut margin_start = computed_margin_start.auto_is(Length::zero);
                    let mut margin_end = computed_margin_end.auto_is(Length::zero);

                    let size = if let LengthOrAuto::LengthPercentage(size) = size {
                        let margins = containing_size - start - end - padding_border_sum - size;
                        match (computed_margin_start, computed_margin_end) {
                            (LengthOrAuto::Auto, LengthOrAuto::Auto) => {
                                let (s, e) = solve_margins(margins);
                                margin_start = s;
                                margin_end = e;
                            },
                            (LengthOrAuto::Auto, LengthOrAuto::LengthPercentage(end)) => {
                                margin_start = margins - end;
                            },
                            (LengthOrAuto::LengthPercentage(start), LengthOrAuto::Auto) => {
                                margin_end = margins - start;
                            },
                            (
                                LengthOrAuto::LengthPercentage(_),
                                LengthOrAuto::LengthPercentage(_),
                            ) => {},
                        }
                        size
                    } else {
                        // FIXME(nox): What happens if that is negative?
                        containing_size -
                            start -
                            end -
                            padding_border_sum -
                            margin_start -
                            margin_end
                    };
                    (
                        Anchor::Start(start),
                        LengthOrAuto::LengthPercentage(size),
                        margin_start,
                        margin_end,
                    )
                },
            }
        }

        let (inline_anchor, inline_size, margin_inline_start, margin_inline_end) = solve_axis(
            cbis,
            pb.inline_sum(),
            computed_margin.inline_start,
            computed_margin.inline_end,
            |margins| {
                if margins.px() >= 0. {
                    (margins / 2., margins / 2.)
                } else {
                    (Length::zero(), margins)
                }
            },
            self.inline_start,
            box_size.inline,
        );

        let (block_anchor, block_size, margin_block_start, margin_block_end) = solve_axis(
            cbis,
            pb.block_sum(),
            computed_margin.block_start,
            computed_margin.block_end,
            |margins| (margins / 2., margins / 2.),
            self.block_start,
            box_size.block,
        );

        let margin = Sides {
            inline_start: margin_inline_start,
            inline_end: margin_inline_end,
            block_start: margin_block_start,
            block_end: margin_block_end,
        };

        let inline_size = inline_size.auto_is(|| {
            let available_size = match inline_anchor {
                Anchor::Start(start) => cbis - start - pb.inline_sum() - margin.inline_sum(),
                Anchor::End(end) => cbis - end - pb.inline_sum() - margin.inline_sum(),
            };

            if self
                .absolutely_positioned_box
                .contents
                .as_replaced()
                .is_ok()
            {
                // FIXME: implement https://drafts.csswg.org/css2/visudet.html#abs-replaced-width
                available_size
            } else {
                self.absolutely_positioned_box
                    .contents
                    .content_sizes
                    .shrink_to_fit(available_size)
            }
        });

        let mut absolutely_positioned_fragments = Vec::new();
        let mut independent_layout = match self.absolutely_positioned_box.contents.as_replaced() {
            Ok(replaced) => {
                // FIXME: implement https://drafts.csswg.org/css2/visudet.html#abs-replaced-width
                // and https://drafts.csswg.org/css2/visudet.html#abs-replaced-height
                let block_size = block_size.auto_is(Length::zero);
                let fragments = replaced.make_fragments(
                    &self.absolutely_positioned_box.contents.style,
                    Vec2 {
                        inline: inline_size,
                        block: block_size,
                    },
                );
                crate::formatting_contexts::IndependentLayout {
                    fragments,
                    content_block_size: block_size,
                }
            },
            Err(non_replaced) => {
                let containing_block_for_children = ContainingBlock {
                    inline_size,
                    block_size,
                    style,
                };
                // https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
                assert_eq!(
                    containing_block.style.writing_mode,
                    containing_block_for_children.style.writing_mode,
                    "Mixed writing modes are not supported yet"
                );
                let dummy_tree_rank = 0;
                non_replaced.layout(
                    layout_context,
                    &containing_block_for_children,
                    dummy_tree_rank,
                    &mut absolutely_positioned_fragments,
                )
            },
        };

        let inline_start = match inline_anchor {
            Anchor::Start(start) => start + pb.inline_start + margin.inline_start,
            Anchor::End(end) => cbbs - end - pb.inline_end - margin.inline_end - inline_size,
        };

        let block_size = block_size.auto_is(|| independent_layout.content_block_size);
        let block_start = match block_anchor {
            Anchor::Start(start) => start + pb.block_start + margin.block_start,
            Anchor::End(end) => cbbs - end - pb.block_end - margin.block_end - block_size,
        };

        let content_rect = Rect {
            start_corner: Vec2 {
                inline: inline_start,
                block: block_start,
            },
            size: Vec2 {
                inline: inline_size,
                block: block_size,
            },
        };

        AbsolutelyPositionedFragment::in_positioned_containing_block(
            layout_context,
            &absolutely_positioned_fragments,
            &mut independent_layout.fragments,
            &content_rect.size,
            &padding,
            style,
        );

        Fragment::Box(BoxFragment {
            style: style.clone(),
            children: independent_layout.fragments,
            content_rect,
            padding,
            border,
            margin,
            block_margins_collapsed_with_children: CollapsedBlockMargins::zero(),
        })
    }
}

pub(crate) fn adjust_static_positions(
    absolutely_positioned_fragments: &mut [AbsolutelyPositionedFragment],
    child_fragments: &mut [Fragment],
    tree_rank_in_parent: usize,
) {
    for abspos_fragment in absolutely_positioned_fragments {
        let child_fragment_rect = match &child_fragments[abspos_fragment.tree_rank] {
            Fragment::Box(b) => &b.content_rect,
            Fragment::Anonymous(a) => &a.rect,
            _ => unreachable!(),
        };

        abspos_fragment.tree_rank = tree_rank_in_parent;

        if let AbsoluteBoxOffsets::StaticStart { start } = &mut abspos_fragment.inline_start {
            *start += child_fragment_rect.start_corner.inline;
        }

        if let AbsoluteBoxOffsets::StaticStart { start } = &mut abspos_fragment.block_start {
            *start += child_fragment_rect.start_corner.block;
        }
    }
}
<|endoftext|>


<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::bindings::callback::{CallbackContainer, ExceptionHandling, CallbackFunction};
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::ErrorEventBinding::ErrorEventMethods;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::Bindings::EventHandlerBinding::OnErrorEventHandlerNonNull;
use dom::bindings::codegen::Bindings::EventListenerBinding::EventListener;
use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods;
use dom::bindings::codegen::UnionTypes::EventOrString;
use dom::bindings::error::{Error, Fallible, report_pending_exception};
use dom::bindings::inheritance::{Castable, EventTargetTypeId};
use dom::bindings::js::Root;
use dom::bindings::reflector::{Reflectable, Reflector};
use dom::errorevent::ErrorEvent;
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::eventdispatcher::dispatch_event;
use dom::virtualmethods::VirtualMethods;
use dom::window::Window;
use fnv::FnvHasher;
use heapsize::HeapSizeOf;
use js::jsapi::{CompileFunction, JS_GetFunctionObject, RootedValue};
use js::jsapi::{HandleObject, JSContext, RootedFunction};
use js::jsapi::{JSAutoCompartment, JSAutoRequest};
use js::rust::{AutoObjectVectorWrapper, CompileOptionsWrapper};
use libc::{c_char, size_t};
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::default::Default;
use std::ffi::CString;
use std::hash::BuildHasherDefault;
use std::rc::Rc;
use std::{intrinsics, ptr};
use string_cache::Atom;
use url::Url;
use util::str::DOMString;

#[derive(PartialEq, Clone, JSTraceable)]
pub enum CommonEventHandler {
    EventHandler(Rc<EventHandlerNonNull>),
    ErrorEventHandler(Rc<OnErrorEventHandlerNonNull>),
}

impl CommonEventHandler {
    fn parent(&self) -> &CallbackFunction {
        match *self {
            CommonEventHandler::EventHandler(ref handler) => &handler.parent,
            CommonEventHandler::ErrorEventHandler(ref handler) => &handler.parent,
        }
    }
}

#[derive(JSTraceable, Copy, Clone, PartialEq, HeapSizeOf)]
pub enum ListenerPhase {
    Capturing,
    Bubbling,
}

impl PartialEq for EventTargetTypeId {
    #[inline]
    fn eq(&self, other: &EventTargetTypeId) -> bool {
        match (*self, *other) {
            (EventTargetTypeId::Node(this_type), EventTargetTypeId::Node(other_type)) => {
                this_type == other_type
            }
            _ => self.eq_slow(other)
        }
    }
}

impl EventTargetTypeId {
    #[allow(unsafe_code)]
    fn eq_slow(&self, other: &EventTargetTypeId) -> bool {
        match (*self, *other) {
            (EventTargetTypeId::Node(this_type), EventTargetTypeId::Node(other_type)) => {
                this_type == other_type
            }
            (EventTargetTypeId::WorkerGlobalScope(this_type),
             EventTargetTypeId::WorkerGlobalScope(other_type)) => {
                this_type == other_type
            }
            (EventTargetTypeId::XMLHttpRequestEventTarget(this_type),
             EventTargetTypeId::XMLHttpRequestEventTarget(other_type)) => {
                this_type == other_type
            }
            (_, _) => {
                unsafe {
                    intrinsics::discriminant_value(self) == intrinsics::discriminant_value(other)
                }
            }
        }
    }
}

#[derive(JSTraceable, Clone, PartialEq)]
pub enum EventListenerType {
    Additive(Rc<EventListener>),
    Inline(CommonEventHandler),
}

impl HeapSizeOf for EventListenerType {
    fn heap_size_of_children(&self) -> usize {
        // FIXME: Rc<T> isn't HeapSizeOf and we can't ignore it due to #6870 and #6871
        0
    }
}

impl EventListenerType {
    // https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm
    pub fn call_or_handle_event<T: Reflectable>(&self,
                                                object: &T,
                                                event: &Event,
                                                exception_handle: ExceptionHandling) {
        // Step 3
        match *self {
            EventListenerType::Additive(ref listener) => {
                let _ = listener.HandleEvent_(object, event, exception_handle);
            },
            EventListenerType::Inline(ref handler) => {
                match *handler {
                    CommonEventHandler::ErrorEventHandler(ref handler) => {
                        if let Some(event) = event.downcast::<ErrorEvent>() {
                            let global = object.global();
                            let cx = global.r().get_cx();
                            let error = RootedValue::new(cx, event.Error(cx));
                            let _ = handler.Call_(object,
                                                  EventOrString::String(event.Message()),
                                                  Some(event.Filename()),
                                                  Some(event.Lineno()),
                                                  Some(event.Colno()),
                                                  Some(error.handle()),
                                                  exception_handle);
                            return;
                        }

                        let _ = handler.Call_(object, EventOrString::Event(Root::from_ref(event)),
                                              None, None, None, None, exception_handle);
                    }

                    CommonEventHandler::EventHandler(ref handler) => {
                        let _ = handler.Call_(object, event, exception_handle);
                    }
                }
            },
        }

        // TODO(#8490): step 4 (cancel event based on return value)
    }
}

#[derive(JSTraceable, Clone, PartialEq, HeapSizeOf)]
#[privatize]
pub struct EventListenerEntry {
    phase: ListenerPhase,
    listener: EventListenerType
}

#[dom_struct]
pub struct EventTarget {
    reflector_: Reflector,
    handlers: DOMRefCell<HashMap<Atom, Vec<EventListenerEntry>, BuildHasherDefault<FnvHasher>>>,
}

impl EventTarget {
    pub fn new_inherited() -> EventTarget {
        EventTarget {
            reflector_: Reflector::new(),
            handlers: DOMRefCell::new(Default::default()),
        }
    }

    pub fn get_listeners(&self, type_: &Atom) -> Option<Vec<EventListenerType>> {
        self.handlers.borrow().get(type_).map(|listeners| {
            listeners.iter().map(|entry| entry.listener.clone()).collect()
        })
    }

    pub fn get_listeners_for(&self, type_: &Atom, desired_phase: ListenerPhase)
        -> Option<Vec<EventListenerType>> {
        self.handlers.borrow().get(type_).map(|listeners| {
            let filtered = listeners.iter().filter(|entry| entry.phase == desired_phase);
            filtered.map(|entry| entry.listener.clone()).collect()
        })
    }

    pub fn dispatch_event_with_target(&self,
                                  target: &EventTarget,
                                  event: &Event) -> bool {
        dispatch_event(self, Some(target), event)
    }

    pub fn dispatch_event(&self, event: &Event) -> bool {
        dispatch_event(self, None, event)
    }

    pub fn set_inline_event_listener(&self,
                                     ty: Atom,
                                     listener: Option<CommonEventHandler>) {
        let mut handlers = self.handlers.borrow_mut();
        let entries = match handlers.entry(ty) {
            Occupied(entry) => entry.into_mut(),
            Vacant(entry) => entry.insert(vec!()),
        };

        let idx = entries.iter().position(|ref entry| {
            match entry.listener {
                EventListenerType::Inline(_) => true,
                _ => false,
            }
        });

        match idx {
            Some(idx) => {
                match listener {
                    Some(listener) => entries[idx].listener = EventListenerType::Inline(listener),
                    None => {
                        entries.remove(idx);
                    }
                }
            }
            None => {
                if listener.is_some() {
                    entries.push(EventListenerEntry {
                        phase: ListenerPhase::Bubbling,
                        listener: EventListenerType::Inline(listener.unwrap()),
                    });
                }
            }
        }
    }

    pub fn get_inline_event_listener(&self, ty: &Atom) -> Option<CommonEventHandler> {
        let handlers = self.handlers.borrow();
        let entries = handlers.get(ty);
        entries.and_then(|entries| entries.iter().filter_map(|entry| {
            match entry.listener {
                EventListenerType::Inline(ref handler) => Some(handler.clone()),
                _ => None,
            }
        }).next())
    }

    #[allow(unsafe_code)]
    // https://html.spec.whatwg.org/multipage/#getting-the-current-value-of-the-event-handler
    pub fn set_event_handler_uncompiled(&self,
                                    cx: *mut JSContext,
                                    url: Url,
                                    scope: HandleObject,
                                    ty: &str,
                                    source: DOMString) {
        let url = CString::new(url.serialize()).unwrap();
        let name = CString::new(ty).unwrap();
        let lineno = 0; //XXXjdm need to get a real number here

        static mut ARG_NAMES: [*const c_char; 1] = [b"event\0" as *const u8 as *const c_char];
        static mut ERROR_ARG_NAMES: [*const c_char; 5] = [b"event\0" as *const u8 as *const c_char,
                                                          b"source\0" as *const u8 as *const c_char,
                                                          b"lineno\0" as *const u8 as *const c_char,
                                                          b"colno\0" as *const u8 as *const c_char,
                                                          b"error\0" as *const u8 as *const c_char];
        // step 10
        let is_error = ty == "error" && self.is::<Window>();
        let args = unsafe {
            if is_error {
                &ERROR_ARG_NAMES[..]
            } else {
                &ARG_NAMES[..]
            }
        };

        let source: Vec<u16> = source.utf16_units().collect();
        let options = CompileOptionsWrapper::new(cx, url.as_ptr(), lineno);
        let scopechain = AutoObjectVectorWrapper::new(cx);

        let _ar = JSAutoRequest::new(cx);
        let _ac = JSAutoCompartment::new(cx, scope.get());
        let mut handler = RootedFunction::new(cx, ptr::null_mut());
        let rv = unsafe {
            CompileFunction(cx,
                            scopechain.ptr,
                            options.ptr,
                            name.as_ptr(),
                            args.len() as u32,
                            args.as_ptr(),
                            source.as_ptr(),
                            source.len() as size_t,
                            handler.handle_mut())
        };
        if !rv || handler.ptr.is_null() {
            report_pending_exception(cx, self.reflector().get_jsobject().get());
            return;
        }

        let funobj = unsafe { JS_GetFunctionObject(handler.ptr) };
        assert!(!funobj.is_null());
        if is_error {
            self.set_error_event_handler(ty, Some(OnErrorEventHandlerNonNull::new(funobj)));
        } else {
            self.set_event_handler_common(ty, Some(EventHandlerNonNull::new(funobj)));
        }
    }

    pub fn set_event_handler_common<T: CallbackContainer>(
        &self, ty: &str, listener: Option<Rc<T>>)
    {
        let event_listener = listener.map(|listener|
                                          CommonEventHandler::EventHandler(
                                              EventHandlerNonNull::new(listener.callback())));
        self.set_inline_event_listener(Atom::from(ty), event_listener);
    }

    pub fn set_error_event_handler<T: CallbackContainer>(
        &self, ty: &str, listener: Option<Rc<T>>)
    {
        let event_listener = listener.map(|listener|
                                          CommonEventHandler::ErrorEventHandler(
                                              OnErrorEventHandlerNonNull::new(listener.callback())));
        self.set_inline_event_listener(Atom::from(ty), event_listener);
    }

    pub fn get_event_handler_common<T: CallbackContainer>(&self, ty: &str) -> Option<Rc<T>> {
        let listener = self.get_inline_event_listener(&Atom::from(ty));
        listener.map(|listener| CallbackContainer::new(listener.parent().callback()))
    }

    pub fn has_handlers(&self) -> bool {
        !self.handlers.borrow().is_empty()
    }

    // https://html.spec.whatwg.org/multipage/#fire-a-simple-event
    pub fn fire_simple_event(&self, name: &str) -> Root<Event> {
        self.fire_event(name, EventBubbles::DoesNotBubble,
                        EventCancelable::NotCancelable)
    }

    // https://dom.spec.whatwg.org/#concept-event-fire
    pub fn fire_event(&self, name: &str,
                      bubbles: EventBubbles,
                      cancelable: EventCancelable)
                      -> Root<Event> {
        let global = self.global();
        let event = Event::new(global.r(), Atom::from(name), bubbles, cancelable);

        event.fire(self);

        event
    }
}

impl EventTargetMethods for EventTarget {
    // https://dom.spec.whatwg.org/#dom-eventtarget-addeventlistener
    fn AddEventListener(&self,
                        ty: DOMString,
                        listener: Option<Rc<EventListener>>,
                        capture: bool) {
        if let Some(listener) = listener {
            let mut handlers = self.handlers.borrow_mut();
            let entry = match handlers.entry(Atom::from(&*ty)) {
                Occupied(entry) => entry.into_mut(),
                Vacant(entry) => entry.insert(vec!()),
            };

            let phase = if capture { ListenerPhase::Capturing } else { ListenerPhase::Bubbling };
            let new_entry = EventListenerEntry {
                phase: phase,
                listener: EventListenerType::Additive(listener)
            };
            if !entry.contains(&new_entry) {
                entry.push(new_entry);
            }
        }
    }

    // https://dom.spec.whatwg.org/#dom-eventtarget-removeeventlistener
    fn RemoveEventListener(&self,
                           ty: DOMString,
                           listener: Option<Rc<EventListener>>,
                           capture: bool) {
        if let Some(ref listener) = listener {
            let mut handlers = self.handlers.borrow_mut();
            let entry = handlers.get_mut(&Atom::from(&*ty));
            for entry in entry {
                let phase = if capture { ListenerPhase::Capturing } else { ListenerPhase::Bubbling };
                let old_entry = EventListenerEntry {
                    phase: phase,
                    listener: EventListenerType::Additive(listener.clone())
                };
                if let Some(position) = entry.iter().position(|e| *e == old_entry) {
                    entry.remove(position);
                }
            }
        }
    }

    // https://dom.spec.whatwg.org/#dom-eventtarget-dispatchevent
    fn DispatchEvent(&self, event: &Event) -> Fallible<bool> {
        if event.dispatching() || !event.initialized() {
            return Err(Error::InvalidState);
        }
        event.set_trusted(false);
        Ok(self.dispatch_event(event))
    }
}

impl VirtualMethods for EventTarget {
    fn super_type(&self) -> Option<&VirtualMethods> {
        None
    }
}
<commit_msg>Use if-let in set_inline_event_listener.<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::bindings::callback::{CallbackContainer, ExceptionHandling, CallbackFunction};
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::ErrorEventBinding::ErrorEventMethods;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::Bindings::EventHandlerBinding::OnErrorEventHandlerNonNull;
use dom::bindings::codegen::Bindings::EventListenerBinding::EventListener;
use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods;
use dom::bindings::codegen::UnionTypes::EventOrString;
use dom::bindings::error::{Error, Fallible, report_pending_exception};
use dom::bindings::inheritance::{Castable, EventTargetTypeId};
use dom::bindings::js::Root;
use dom::bindings::reflector::{Reflectable, Reflector};
use dom::errorevent::ErrorEvent;
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::eventdispatcher::dispatch_event;
use dom::virtualmethods::VirtualMethods;
use dom::window::Window;
use fnv::FnvHasher;
use heapsize::HeapSizeOf;
use js::jsapi::{CompileFunction, JS_GetFunctionObject, RootedValue};
use js::jsapi::{HandleObject, JSContext, RootedFunction};
use js::jsapi::{JSAutoCompartment, JSAutoRequest};
use js::rust::{AutoObjectVectorWrapper, CompileOptionsWrapper};
use libc::{c_char, size_t};
use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::default::Default;
use std::ffi::CString;
use std::hash::BuildHasherDefault;
use std::rc::Rc;
use std::{intrinsics, ptr};
use string_cache::Atom;
use url::Url;
use util::str::DOMString;

#[derive(PartialEq, Clone, JSTraceable)]
pub enum CommonEventHandler {
    EventHandler(Rc<EventHandlerNonNull>),
    ErrorEventHandler(Rc<OnErrorEventHandlerNonNull>),
}

impl CommonEventHandler {
    fn parent(&self) -> &CallbackFunction {
        match *self {
            CommonEventHandler::EventHandler(ref handler) => &handler.parent,
            CommonEventHandler::ErrorEventHandler(ref handler) => &handler.parent,
        }
    }
}

#[derive(JSTraceable, Copy, Clone, PartialEq, HeapSizeOf)]
pub enum ListenerPhase {
    Capturing,
    Bubbling,
}

impl PartialEq for EventTargetTypeId {
    #[inline]
    fn eq(&self, other: &EventTargetTypeId) -> bool {
        match (*self, *other) {
            (EventTargetTypeId::Node(this_type), EventTargetTypeId::Node(other_type)) => {
                this_type == other_type
            }
            _ => self.eq_slow(other)
        }
    }
}

impl EventTargetTypeId {
    #[allow(unsafe_code)]
    fn eq_slow(&self, other: &EventTargetTypeId) -> bool {
        match (*self, *other) {
            (EventTargetTypeId::Node(this_type), EventTargetTypeId::Node(other_type)) => {
                this_type == other_type
            }
            (EventTargetTypeId::WorkerGlobalScope(this_type),
             EventTargetTypeId::WorkerGlobalScope(other_type)) => {
                this_type == other_type
            }
            (EventTargetTypeId::XMLHttpRequestEventTarget(this_type),
             EventTargetTypeId::XMLHttpRequestEventTarget(other_type)) => {
                this_type == other_type
            }
            (_, _) => {
                unsafe {
                    intrinsics::discriminant_value(self) == intrinsics::discriminant_value(other)
                }
            }
        }
    }
}

#[derive(JSTraceable, Clone, PartialEq)]
pub enum EventListenerType {
    Additive(Rc<EventListener>),
    Inline(CommonEventHandler),
}

impl HeapSizeOf for EventListenerType {
    fn heap_size_of_children(&self) -> usize {
        // FIXME: Rc<T> isn't HeapSizeOf and we can't ignore it due to #6870 and #6871
        0
    }
}

impl EventListenerType {
    // https://html.spec.whatwg.org/multipage/#the-event-handler-processing-algorithm
    pub fn call_or_handle_event<T: Reflectable>(&self,
                                                object: &T,
                                                event: &Event,
                                                exception_handle: ExceptionHandling) {
        // Step 3
        match *self {
            EventListenerType::Additive(ref listener) => {
                let _ = listener.HandleEvent_(object, event, exception_handle);
            },
            EventListenerType::Inline(ref handler) => {
                match *handler {
                    CommonEventHandler::ErrorEventHandler(ref handler) => {
                        if let Some(event) = event.downcast::<ErrorEvent>() {
                            let global = object.global();
                            let cx = global.r().get_cx();
                            let error = RootedValue::new(cx, event.Error(cx));
                            let _ = handler.Call_(object,
                                                  EventOrString::String(event.Message()),
                                                  Some(event.Filename()),
                                                  Some(event.Lineno()),
                                                  Some(event.Colno()),
                                                  Some(error.handle()),
                                                  exception_handle);
                            return;
                        }

                        let _ = handler.Call_(object, EventOrString::Event(Root::from_ref(event)),
                                              None, None, None, None, exception_handle);
                    }

                    CommonEventHandler::EventHandler(ref handler) => {
                        let _ = handler.Call_(object, event, exception_handle);
                    }
                }
            },
        }

        // TODO(#8490): step 4 (cancel event based on return value)
    }
}

#[derive(JSTraceable, Clone, PartialEq, HeapSizeOf)]
#[privatize]
pub struct EventListenerEntry {
    phase: ListenerPhase,
    listener: EventListenerType
}

#[dom_struct]
pub struct EventTarget {
    reflector_: Reflector,
    handlers: DOMRefCell<HashMap<Atom, Vec<EventListenerEntry>, BuildHasherDefault<FnvHasher>>>,
}

impl EventTarget {
    pub fn new_inherited() -> EventTarget {
        EventTarget {
            reflector_: Reflector::new(),
            handlers: DOMRefCell::new(Default::default()),
        }
    }

    pub fn get_listeners(&self, type_: &Atom) -> Option<Vec<EventListenerType>> {
        self.handlers.borrow().get(type_).map(|listeners| {
            listeners.iter().map(|entry| entry.listener.clone()).collect()
        })
    }

    pub fn get_listeners_for(&self, type_: &Atom, desired_phase: ListenerPhase)
        -> Option<Vec<EventListenerType>> {
        self.handlers.borrow().get(type_).map(|listeners| {
            let filtered = listeners.iter().filter(|entry| entry.phase == desired_phase);
            filtered.map(|entry| entry.listener.clone()).collect()
        })
    }

    pub fn dispatch_event_with_target(&self,
                                  target: &EventTarget,
                                  event: &Event) -> bool {
        dispatch_event(self, Some(target), event)
    }

    pub fn dispatch_event(&self, event: &Event) -> bool {
        dispatch_event(self, None, event)
    }

    pub fn set_inline_event_listener(&self,
                                     ty: Atom,
                                     listener: Option<CommonEventHandler>) {
        let mut handlers = self.handlers.borrow_mut();
        let entries = match handlers.entry(ty) {
            Occupied(entry) => entry.into_mut(),
            Vacant(entry) => entry.insert(vec!()),
        };

        let idx = entries.iter().position(|ref entry| {
            match entry.listener {
                EventListenerType::Inline(_) => true,
                _ => false,
            }
        });

        match idx {
            Some(idx) => {
                match listener {
                    Some(listener) => entries[idx].listener = EventListenerType::Inline(listener),
                    None => {
                        entries.remove(idx);
                    }
                }
            }
            None => {
                if let Some(listener) = listener {
                    entries.push(EventListenerEntry {
                        phase: ListenerPhase::Bubbling,
                        listener: EventListenerType::Inline(listener),
                    });
                }
            }
        }
    }

    pub fn get_inline_event_listener(&self, ty: &Atom) -> Option<CommonEventHandler> {
        let handlers = self.handlers.borrow();
        let entries = handlers.get(ty);
        entries.and_then(|entries| entries.iter().filter_map(|entry| {
            match entry.listener {
                EventListenerType::Inline(ref handler) => Some(handler.clone()),
                _ => None,
            }
        }).next())
    }

    #[allow(unsafe_code)]
    // https://html.spec.whatwg.org/multipage/#getting-the-current-value-of-the-event-handler
    pub fn set_event_handler_uncompiled(&self,
                                    cx: *mut JSContext,
                                    url: Url,
                                    scope: HandleObject,
                                    ty: &str,
                                    source: DOMString) {
        let url = CString::new(url.serialize()).unwrap();
        let name = CString::new(ty).unwrap();
        let lineno = 0; //XXXjdm need to get a real number here

        static mut ARG_NAMES: [*const c_char; 1] = [b"event\0" as *const u8 as *const c_char];
        static mut ERROR_ARG_NAMES: [*const c_char; 5] = [b"event\0" as *const u8 as *const c_char,
                                                          b"source\0" as *const u8 as *const c_char,
                                                          b"lineno\0" as *const u8 as *const c_char,
                                                          b"colno\0" as *const u8 as *const c_char,
                                                          b"error\0" as *const u8 as *const c_char];
        // step 10
        let is_error = ty == "error" && self.is::<Window>();
        let args = unsafe {
            if is_error {
                &ERROR_ARG_NAMES[..]
            } else {
                &ARG_NAMES[..]
            }
        };

        let source: Vec<u16> = source.utf16_units().collect();
        let options = CompileOptionsWrapper::new(cx, url.as_ptr(), lineno);
        let scopechain = AutoObjectVectorWrapper::new(cx);

        let _ar = JSAutoRequest::new(cx);
        let _ac = JSAutoCompartment::new(cx, scope.get());
        let mut handler = RootedFunction::new(cx, ptr::null_mut());
        let rv = unsafe {
            CompileFunction(cx,
                            scopechain.ptr,
                            options.ptr,
                            name.as_ptr(),
                            args.len() as u32,
                            args.as_ptr(),
                            source.as_ptr(),
                            source.len() as size_t,
                            handler.handle_mut())
        };
        if !rv || handler.ptr.is_null() {
            report_pending_exception(cx, self.reflector().get_jsobject().get());
            return;
        }

        let funobj = unsafe { JS_GetFunctionObject(handler.ptr) };
        assert!(!funobj.is_null());
        if is_error {
            self.set_error_event_handler(ty, Some(OnErrorEventHandlerNonNull::new(funobj)));
        } else {
            self.set_event_handler_common(ty, Some(EventHandlerNonNull::new(funobj)));
        }
    }

    pub fn set_event_handler_common<T: CallbackContainer>(
        &self, ty: &str, listener: Option<Rc<T>>)
    {
        let event_listener = listener.map(|listener|
                                          CommonEventHandler::EventHandler(
                                              EventHandlerNonNull::new(listener.callback())));
        self.set_inline_event_listener(Atom::from(ty), event_listener);
    }

    pub fn set_error_event_handler<T: CallbackContainer>(
        &self, ty: &str, listener: Option<Rc<T>>)
    {
        let event_listener = listener.map(|listener|
                                          CommonEventHandler::ErrorEventHandler(
                                              OnErrorEventHandlerNonNull::new(listener.callback())));
        self.set_inline_event_listener(Atom::from(ty), event_listener);
    }

    pub fn get_event_handler_common<T: CallbackContainer>(&self, ty: &str) -> Option<Rc<T>> {
        let listener = self.get_inline_event_listener(&Atom::from(ty));
        listener.map(|listener| CallbackContainer::new(listener.parent().callback()))
    }

    pub fn has_handlers(&self) -> bool {
        !self.handlers.borrow().is_empty()
    }

    // https://html.spec.whatwg.org/multipage/#fire-a-simple-event
    pub fn fire_simple_event(&self, name: &str) -> Root<Event> {
        self.fire_event(name, EventBubbles::DoesNotBubble,
                        EventCancelable::NotCancelable)
    }

    // https://dom.spec.whatwg.org/#concept-event-fire
    pub fn fire_event(&self, name: &str,
                      bubbles: EventBubbles,
                      cancelable: EventCancelable)
                      -> Root<Event> {
        let global = self.global();
        let event = Event::new(global.r(), Atom::from(name), bubbles, cancelable);

        event.fire(self);

        event
    }
}

impl EventTargetMethods for EventTarget {
    // https://dom.spec.whatwg.org/#dom-eventtarget-addeventlistener
    fn AddEventListener(&self,
                        ty: DOMString,
                        listener: Option<Rc<EventListener>>,
                        capture: bool) {
        if let Some(listener) = listener {
            let mut handlers = self.handlers.borrow_mut();
            let entry = match handlers.entry(Atom::from(&*ty)) {
                Occupied(entry) => entry.into_mut(),
                Vacant(entry) => entry.insert(vec!()),
            };

            let phase = if capture { ListenerPhase::Capturing } else { ListenerPhase::Bubbling };
            let new_entry = EventListenerEntry {
                phase: phase,
                listener: EventListenerType::Additive(listener)
            };
            if !entry.contains(&new_entry) {
                entry.push(new_entry);
            }
        }
    }

    // https://dom.spec.whatwg.org/#dom-eventtarget-removeeventlistener
    fn RemoveEventListener(&self,
                           ty: DOMString,
                           listener: Option<Rc<EventListener>>,
                           capture: bool) {
        if let Some(ref listener) = listener {
            let mut handlers = self.handlers.borrow_mut();
            let entry = handlers.get_mut(&Atom::from(&*ty));
            for entry in entry {
                let phase = if capture { ListenerPhase::Capturing } else { ListenerPhase::Bubbling };
                let old_entry = EventListenerEntry {
                    phase: phase,
                    listener: EventListenerType::Additive(listener.clone())
                };
                if let Some(position) = entry.iter().position(|e| *e == old_entry) {
                    entry.remove(position);
                }
            }
        }
    }

    // https://dom.spec.whatwg.org/#dom-eventtarget-dispatchevent
    fn DispatchEvent(&self, event: &Event) -> Fallible<bool> {
        if event.dispatching() || !event.initialized() {
            return Err(Error::InvalidState);
        }
        event.set_trusted(false);
        Ok(self.dispatch_event(event))
    }
}

impl VirtualMethods for EventTarget {
    fn super_type(&self) -> Option<&VirtualMethods> {
        None
    }
}
<|endoftext|>