<commit_before>//! Generic message handling.
//!
//! This module provides the ability to read SBD messages from and write SBD messages to streams.

use std::fs::File;
use std::io::Read;
use std::path::Path;

use byteorder::{ReadBytesExt, BigEndian};

use {Error, Result};
use information_element::InformationElement;

/// An SBD message.
#[derive(Debug, Default)]
pub struct Message {
    protocol_revision_number: u8,
    overall_message_length: u16,
    information_elements: Vec<InformationElement>,
}

impl Message {

    /// Reads in a message from a file path.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::message::Message;
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Message> {
        let file = try!(File::open(path));
        Message::read_from(file)
    }

    /// Reads in a message from an object that implements `Read`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::File;
    /// use sbd::message::Message;
    /// let mut file = File::open("data/0-mo.sbd").unwrap();
    /// let message = Message::read_from(file).unwrap();
    pub fn read_from<R: Read>(mut readable: R) -> Result<Message> {
        let mut message: Message = Default::default();
        message.protocol_revision_number = try!(readable.read_u8());
        message.overall_message_length = try!(readable.read_u16::<BigEndian>());
        if message.protocol_revision_number != 1 {
            return Err(Error::InvalidProtocolRevisionNumber(message.protocol_revision_number));
        }
        Ok(message)
    }

    /// Returns the overall message length of the SBD message.
    ///
    /// This value *includes* the initial three bytes, whereas the `overall_message_length`
    /// value in the SBD header does *not* include those bytes.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::message::Message;
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// assert_eq!(59, message.len());
    /// ```
    pub fn len(&self) -> usize {
        self.overall_message_length as usize + 3
    }

    /// Returns true if this message is mobile originated.
    ///
    /// This is deteremined by the set of information elements in this message.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::message::Message;
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// assert!(message.is_mobile_originated());
    /// ```
    pub fn is_mobile_originated(&self) -> bool {
        true
    }

    /// Returns true if this message is mobile terminated.
    ///
    /// This is deteremined by the set of information elements in this message.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::message::Message;
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// assert!(!message.is_mobile_terminated());
    /// ```
    pub fn is_mobile_terminated(&self) -> bool {
        false
    }
}

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

    use std::fs::File;

    #[test]
    fn from_path() {
        Message::from_path("data/0-mo.sbd").unwrap();
    }

    #[test]
    fn from_read() {
        let file = File::open("data/0-mo.sbd").unwrap();
        Message::read_from(file).unwrap();
    }

    #[test]
    fn from_path_that_doesnt_exist() {
        assert!(Message::from_path("notafile.sbd").is_err());
    }

    #[test]
    fn from_path_that_is_not_an_sbd_message() {
        assert!(Message::from_path("data/1-invalid.sbd").is_err());
    }

    #[test]
    fn len() {
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        assert_eq!(59, message.len());
    }

    #[test]
    fn is_mobile_originated() {
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        assert!(message.is_mobile_originated());
        assert!(!message.is_mobile_terminated());
        // TODO try to get a mobile terminated message to test the other way
    }
}
<commit_msg>Read overall message length *after* checking protocol<commit_after>//! Generic message handling.
//!
//! This module provides the ability to read SBD messages from and write SBD messages to streams.

use std::fs::File;
use std::io::Read;
use std::path::Path;

use byteorder::{ReadBytesExt, BigEndian};

use {Error, Result};
use information_element::InformationElement;

/// An SBD message.
#[derive(Debug, Default)]
pub struct Message {
    protocol_revision_number: u8,
    overall_message_length: u16,
    information_elements: Vec<InformationElement>,
}

impl Message {

    /// Reads in a message from a file path.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::message::Message;
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Message> {
        let file = try!(File::open(path));
        Message::read_from(file)
    }

    /// Reads in a message from an object that implements `Read`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::File;
    /// use sbd::message::Message;
    /// let mut file = File::open("data/0-mo.sbd").unwrap();
    /// let message = Message::read_from(file).unwrap();
    pub fn read_from<R: Read>(mut readable: R) -> Result<Message> {
        let mut message: Message = Default::default();
        message.protocol_revision_number = try!(readable.read_u8());
        if message.protocol_revision_number != 1 {
            return Err(Error::InvalidProtocolRevisionNumber(message.protocol_revision_number));
        }
        message.overall_message_length = try!(readable.read_u16::<BigEndian>());
        Ok(message)
    }

    /// Returns the overall message length of the SBD message.
    ///
    /// This value *includes* the initial three bytes, whereas the `overall_message_length`
    /// value in the SBD header does *not* include those bytes.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::message::Message;
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// assert_eq!(59, message.len());
    /// ```
    pub fn len(&self) -> usize {
        self.overall_message_length as usize + 3
    }

    /// Returns true if this message is mobile originated.
    ///
    /// This is deteremined by the set of information elements in this message.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::message::Message;
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// assert!(message.is_mobile_originated());
    /// ```
    pub fn is_mobile_originated(&self) -> bool {
        true
    }

    /// Returns true if this message is mobile terminated.
    ///
    /// This is deteremined by the set of information elements in this message.
    ///
    /// # Examples
    ///
    /// ```
    /// use sbd::message::Message;
    /// let message = Message::from_path("data/0-mo.sbd").unwrap();
    /// assert!(!message.is_mobile_terminated());
    /// ```
    pub fn is_mobile_terminated(&self) -> bool {
        false
    }
}

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

    use std::fs::File;

    #[test]
    fn from_path() {
        Message::from_path("data/0-mo.sbd").unwrap();
    }

    #[test]
    fn from_read() {
        let file = File::open("data/0-mo.sbd").unwrap();
        Message::read_from(file).unwrap();
    }

    #[test]
    fn from_path_that_doesnt_exist() {
        assert!(Message::from_path("notafile.sbd").is_err());
    }

    #[test]
    fn from_path_that_is_not_an_sbd_message() {
        assert!(Message::from_path("data/1-invalid.sbd").is_err());
    }

    #[test]
    fn len() {
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        assert_eq!(59, message.len());
    }

    #[test]
    fn is_mobile_originated() {
        let message = Message::from_path("data/0-mo.sbd").unwrap();
        assert!(message.is_mobile_originated());
        assert!(!message.is_mobile_terminated());
        // TODO try to get a mobile terminated message to test the other way
    }
}
<|endoftext|>


<commit_before>use core::*;
use bitboard::*;
use moves::*;
use board::*;
use tables::*;
use game::*;

pub struct MoveGen {
    diag_pin_map: [Bitboard; 64],
    nondiag_pin_map: [Bitboard; 64]
}

impl MoveGen {
    pub fn new() -> MoveGen {
        return MoveGen { diag_pin_map: [Bitboard::new(0); 64], nondiag_pin_map: [Bitboard::new(0); 64] };
    }

    pub fn fill_move_buffer(&mut self, game: &Game, move_buffer: &mut Vec<Move>) {
        move_buffer.clear();

        use Color::*;
        use PieceType::*;

        let friendly_color   = game.to_move;
        let opponent_color  = !friendly_color;

        let empty_squares    = game.board.unoccupied();
        let occupied_squares = game.board.occupied();
        let friendly_pieces  = game.board.occupied_by(friendly_color);
        let opponent_pieces  = game.board.occupied_by(!friendly_color);

        let king_square     = game.board.get_king_square(friendly_color);
        let king_attackers = game.board.attackers(king_square, opponent_color);
        let check_multiplicity = king_attackers.population();
        let in_check = check_multiplicity > 0;
        let king_moves = KING_TABLE[king_square.idx()];
        let king_danger_squares = game.board.attacked(!friendly_color, true);

        if check_multiplicity > 1 {
            for to in king_moves & empty_squares & !king_danger_squares {
                move_buffer.push(Move::new(king_square, to, QUIET_FLAG));
            }
            return;
        }

        let mut capture_mask = opponent_pieces;
        let mut quiet_mask = empty_squares;

        if check_multiplicity == 1 {
            capture_mask = king_attackers;

            let checker_square = king_attackers.bitscan_forward();
            if game.board.piece_at(checker_square).unwrap().ptype.is_slider() {
                quiet_mask = ray_between_squares(king_square, checker_square);
            } else {
                quiet_mask = Bitboard::new(0);
            }
        }

        let mut pinned_diagonally = Bitboard::new(0);
        let mut pinned_nondiagonally = Bitboard::new(0);

        {
            let opRQ = game.board.get_pieces(opponent_color, Rook) | game.board.get_pieces(opponent_color, Queen);
            let mut pinner = xray_rook_attacks(occupied_squares, friendly_pieces, king_square) & opRQ;
            for pinner_square in pinner {
                let connecting_bits = ray_between_squares(king_square, pinner_square);
                let pinned_bit = connecting_bits & friendly_pieces;
                self.nondiag_pin_map[pinned_bit.bitscan_forward().idx()] = connecting_bits;
                assert!(pinned_bit.population() == 1);
                pinned_nondiagonally |= pinned_bit;
            }

            let opBQ = game.board.get_pieces(opponent_color, Bishop) | game.board.get_pieces(opponent_color, Queen);
            pinner = xray_bishop_attacks(occupied_squares, friendly_pieces, king_square) & opBQ;
            for pinner_square in pinner {
                let connecting_bits = ray_between_squares(king_square, pinner_square);
                let pinned_bit = connecting_bits & friendly_pieces;
                self.diag_pin_map[pinned_bit.bitscan_forward().idx()] = connecting_bits;
                assert!(pinned_bit.population() == 1);
                pinned_diagonally |= pinned_bit;
            }
        }

        let pinned = pinned_diagonally | pinned_nondiagonally;

        let friendly_pawns = game.board.get_pieces(friendly_color, Pawn);
        let delta_pawn_single_push: i32 = if game.to_move == White { -8 } else { 8 };
        let delta_pawn_double_push: i32 = if game.to_move == White { -16 } else { 16 };
        let double_pawn_push_rank = if game.to_move == White { RANK4 } else { RANK5 };
        let promotion_rank = if game.to_move == White { 8 } else { 1 };

        /*********/
        /* PAWNS */
        /*********/
        {
            let advanceable_pawns = friendly_pawns & !pinned_diagonally;

            let advanced_pawns =
                if friendly_color == White {
                    advanceable_pawns.shifted_up() & empty_squares
                } else {
                    advanceable_pawns.shifted_down() & empty_squares
                };

            let double_advanced_pawns =
                if friendly_color == White {
                    advanced_pawns.shifted_up()
                } else {
                    advanced_pawns.shifted_down()
                };

            // single pushes (and promotions)
            for to in advanced_pawns & empty_squares & quiet_mask
            {
                let from = Square::new((to.unwrap() as i32 + delta_pawn_single_push) as u32);

                if to.rank() == promotion_rank {
                    move_buffer.push(Move::new(from, to, BISHOP_PROMO_FLAG));
                    move_buffer.push(Move::new(from, to, KNIGHT_PROMO_FLAG));
                    move_buffer.push(Move::new(from, to, ROOK_PROMO_FLAG));
                    move_buffer.push(Move::new(from, to, QUEEN_PROMO_FLAG));
                } else {
                    move_buffer.push(Move::new(from, to, QUIET_FLAG));
                }
            }

            // double pushes
            // BUGFIX: don't double push through other pieces!
            for to in double_advanced_pawns & empty_squares & double_pawn_push_rank & quiet_mask {

                let from = Square::new((to.unwrap() as i32 + delta_pawn_double_push) as u32);

                move_buffer.push(Move::new(from, to, DOUBLE_PAWN_PUSH_FLAG));
            }

            let pawns_that_can_capture = friendly_pawns & !pinned_nondiagonally;

            // captures (and capture-promotions)
            for from in pawns_that_can_capture
            {
                let pawn_attack_pattern = PAWN_ATTACKS[friendly_color as usize][from.idx()] & capture_mask;

                for to in pawn_attack_pattern & opponent_pieces
                {
                    if to.rank() == promotion_rank {
                        move_buffer.push(Move::new(from, to, BISHOP_PROMO_CAPTURE_FLAG));
                        move_buffer.push(Move::new(from, to, KNIGHT_PROMO_CAPTURE_FLAG));
                        move_buffer.push(Move::new(from, to, ROOK_PROMO_CAPTURE_FLAG));
                        move_buffer.push(Move::new(from, to, QUEEN_PROMO_CAPTURE_FLAG));
                    } else {
                        move_buffer.push(Move::new(from, to, CAPTURE_FLAG));
                    }
                }

                match game.ep_square {
                    //TODO: en-passante discovered check test
                    Some(sq) =>
                        if (pawn_attack_pattern & sq.bitrep()).nonempty() {
                            move_buffer.push(Move::new(from, sq, EP_CAPTURE_FLAG));
                        },
                    None => {}
                }
            }
        }

        /***********/
        /* KNIGHTS */
        /***********/
        {
            for from in game.board.get_pieces(friendly_color, Knight) & !pinned
            {
                let knight_moves = KNIGHT_TABLE[from.idx()];

                /* quiets */
                for to in knight_moves & empty_squares & quiet_mask {
                    move_buffer.push(Move::new(from, to, QUIET_FLAG));
                }

                /* captures */
                for to in knight_moves & opponent_pieces & capture_mask {
                    move_buffer.push(Move::new(from, to, CAPTURE_FLAG));
                }
            }
        }

        /***********/
        /* BISHOPS */
        /***********/

        {
            let friendly_bishops = game.board.get_pieces(friendly_color, Bishop);

            // UNPINNED
            for from in friendly_bishops & !pinned
            {
                let bishop_moves = get_bishop_rays(from, occupied_squares);

                // quiets
                for to in bishop_moves & empty_squares & quiet_mask {
                    move_buffer.push(Move::new(from, to, QUIET_FLAG));
                }

                // captures
                for to in bishop_moves & opponent_pieces & capture_mask {
                    move_buffer.push(Move::new(from, to, CAPTURE_FLAG));
                }
            }

            // PINNED
            for from in friendly_bishops & pinned_diagonally
            {
                let bishop_moves = get_bishop_rays(from, occupied_squares) & self.diag_pin_map[from.idx()];

                // quiets
                for to in bishop_moves & empty_squares & quiet_mask {
                    move_buffer.push(Move::new(from, to, QUIET_FLAG));
                }

                // captures
                for to in bishop_moves & opponent_pieces & capture_mask {
                    move_buffer.push(Move::new(from, to, CAPTURE_FLAG));
                }
            }

        }

        /*********/
        /* ROOKS */
        /*********/

        {
            let friendly_rooks = game.board.get_pieces(friendly_color, Rook);

            // unpinned
            for from in friendly_rooks & !pinned
            {
                let rook_moves = get_rook_rays(from, occupied_squares);

                /* quiets */
                for to in rook_moves & empty_squares & quiet_mask {
                    move_buffer.push(Move::new(from, to, QUIET_FLAG));
                }

                /* captures */
                for to in rook_moves & opponent_pieces & capture_mask {
                    move_buffer.push(Move::new(from, to, CAPTURE_FLAG));
                }
            }

            // pinned
            for from in friendly_rooks & pinned_nondiagonally
            {
                let rook_moves = get_rook_rays(from, occupied_squares) & self.nondiag_pin_map[from.idx()];

                /* quiets */
                for to in rook_moves & empty_squares & quiet_mask {
                    move_buffer.push(Move::new(from, to, QUIET_FLAG));
                }

                /* captures */
                for to in rook_moves & opponent_pieces & capture_mask {
                    move_buffer.push(Move::new(from, to, CAPTURE_FLAG));
                }
            }
        }

        /*********/
        /* QUEEN */
        /*********/
        {
            let friendly_queens = game.board.get_pieces(friendly_color, Queen);

            for from in friendly_queens & !pinned
            {
                let queen_moves = get_queen_rays(from, occupied_squares);

                /* quiets */
                for to in queen_moves & empty_squares & quiet_mask {
                    move_buffer.push(Move::new(from, to, QUIET_FLAG));
                }

                /* captures */
                for to in queen_moves & opponent_pieces & capture_mask {
                    move_buffer.push(Move::new(from, to, CAPTURE_FLAG));
                }
            }

            for from in friendly_queens & pinned
            {
                let queen_moves = get_queen_rays(from, occupied_squares)
                    & self.nondiag_pin_map[from.idx()] & self.diag_pin_map[from.idx()];

                /* quiets */
                for to in queen_moves & empty_squares & quiet_mask {
                    move_buffer.push(Move::new(from, to, QUIET_FLAG));
                }

                /* captures */
                for to in queen_moves & opponent_pieces & capture_mask {
                    move_buffer.push(Move::new(from, to, CAPTURE_FLAG));
                }
            }
        }

        /********/
        /* KING */
        /********/

        {
            /* quiets */
            for to in king_moves & empty_squares & !king_danger_squares {
                move_buffer.push(Move::new(king_square, to, QUIET_FLAG));
            }

            /* captures */
            for to in king_moves & opponent_pieces & capture_mask {
                move_buffer.push(Move::new(king_square, to, CAPTURE_FLAG));
            }

            /* castling */
            let has_kingside_castle_rights = game.castling_rights.intersects(CastlingRights::WHITE_KINGSIDE);
            let has_queenside_castle_rights = game.castling_rights.intersects(CastlingRights::WHITE_QUEENSIDE);

            if has_kingside_castle_rights && !in_check {
                let kingside_castle_path_open = (occupied_squares & WHITE_KINGSIDE_CASTLE_BITS).empty();

                if kingside_castle_path_open {
                    let mut castle_path_is_safe: bool = true;

                    if (WHITE_KINGSIDE_CASTLE_BITS & king_danger_squares).nonempty() {
                        castle_path_is_safe = false;
                    }

                    if castle_path_is_safe {
                        move_buffer.push(Move::new(king_square, Square::new(1), KING_CASTLE_FLAG));
                    }
                }
            }

            if has_queenside_castle_rights && !in_check {
                let queenside_castle_path_open = (occupied_squares & WHITE_QUEENSIDE_CASTLE_BITS).empty();

                if queenside_castle_path_open {
                    let mut castle_path_is_safe: bool = true;

                    if (WHITE_QUEENSIDE_CASTLE_BITS & king_danger_squares).nonempty() {
                        castle_path_is_safe = false;
                    }

                    if castle_path_is_safe {
                        move_buffer.push(Move::new(king_square, Square::new(5), QUEEN_CASTLE_FLAG));
                    }
                }
            }
        }

        // MOVE GENERATION FOR PINNED PIECES

    }
}
<commit_msg>updating comments<commit_after>use core::*;
use bitboard::*;
use moves::*;
use board::*;
use tables::*;
use game::*;

pub struct MoveGen {
    diag_pin_map: [Bitboard; 64],
    nondiag_pin_map: [Bitboard; 64]
}

impl MoveGen {
    pub fn new() -> MoveGen {
        return MoveGen { diag_pin_map: [Bitboard::new(0); 64], nondiag_pin_map: [Bitboard::new(0); 64] };
    }

    pub fn fill_move_buffer(&mut self, game: &Game, move_buffer: &mut Vec<Move>) {
        move_buffer.clear();

        use Color::*;
        use PieceType::*;

        let friendly_color   = game.to_move;
        let opponent_color  = !friendly_color;

        let empty_squares    = game.board.unoccupied();
        let occupied_squares = game.board.occupied();
        let friendly_pieces  = game.board.occupied_by(friendly_color);
        let opponent_pieces  = game.board.occupied_by(!friendly_color);

        let king_square     = game.board.get_king_square(friendly_color);
        let king_attackers = game.board.attackers(king_square, opponent_color);
        let check_multiplicity = king_attackers.population();
        let in_check = check_multiplicity > 0;
        let king_moves = KING_TABLE[king_square.idx()];
        let king_danger_squares = game.board.attacked(!friendly_color, true);

        if check_multiplicity > 1 {
            for to in king_moves & empty_squares & !king_danger_squares {
                move_buffer.push(Move::new(king_square, to, QUIET_FLAG));
            }
            return;
        }

        let mut capture_mask = opponent_pieces;
        let mut quiet_mask = empty_squares;

        if check_multiplicity == 1 {
            capture_mask = king_attackers;

            let checker_square = king_attackers.bitscan_forward();
            if game.board.piece_at(checker_square).unwrap().ptype.is_slider() {
                quiet_mask = ray_between_squares(king_square, checker_square);
            } else {
                quiet_mask = Bitboard::new(0);
            }
        }

        let mut pinned_diagonally = Bitboard::new(0);
        let mut pinned_nondiagonally = Bitboard::new(0);

        // NOTE: stored *_pin_map arrays could potentially be a source of strange bugs.
        // NOTE: easy to debug though, just clear them each call and see if results change.

        {
            let opRQ = game.board.get_pieces(opponent_color, Rook) | game.board.get_pieces(opponent_color, Queen);
            let mut pinner = xray_rook_attacks(occupied_squares, friendly_pieces, king_square) & opRQ;
            for pinner_square in pinner {
                let connecting_bits = ray_between_squares(king_square, pinner_square);
                let pinned_bit = connecting_bits & friendly_pieces;
                self.nondiag_pin_map[pinned_bit.bitscan_forward().idx()] = connecting_bits;
                assert!(pinned_bit.population() == 1);
                pinned_nondiagonally |= pinned_bit;
            }

            let opBQ = game.board.get_pieces(opponent_color, Bishop) | game.board.get_pieces(opponent_color, Queen);
            pinner = xray_bishop_attacks(occupied_squares, friendly_pieces, king_square) & opBQ;
            for pinner_square in pinner {
                let connecting_bits = ray_between_squares(king_square, pinner_square);
                let pinned_bit = connecting_bits & friendly_pieces;
                self.diag_pin_map[pinned_bit.bitscan_forward().idx()] = connecting_bits;
                assert!(pinned_bit.population() == 1);
                pinned_diagonally |= pinned_bit;
            }
        }

        let pinned = pinned_diagonally | pinned_nondiagonally;

        let friendly_pawns = game.board.get_pieces(friendly_color, Pawn);
        let delta_pawn_single_push: i32 = if game.to_move == White { -8 } else { 8 };
        let delta_pawn_double_push: i32 = if game.to_move == White { -16 } else { 16 };
        let double_pawn_push_rank = if game.to_move == White { RANK4 } else { RANK5 };
        let promotion_rank = if game.to_move == White { 8 } else { 1 };

        /*********/
        /* PAWNS */
        /*********/
        {
            let advanceable_pawns = friendly_pawns & !pinned_diagonally;

            let advanced_pawns =
                if friendly_color == White {
                    advanceable_pawns.shifted_up() & empty_squares
                } else {
                    advanceable_pawns.shifted_down() & empty_squares
                };

            let double_advanced_pawns =
                if friendly_color == White {
                    advanced_pawns.shifted_up()
                } else {
                    advanced_pawns.shifted_down()
                };

            // single pushes (and promotions)
            for to in advanced_pawns & empty_squares & quiet_mask
            {
                let from = Square::new((to.unwrap() as i32 + delta_pawn_single_push) as u32);

                if to.rank() == promotion_rank {
                    move_buffer.push(Move::new(from, to, BISHOP_PROMO_FLAG));
                    move_buffer.push(Move::new(from, to, KNIGHT_PROMO_FLAG));
                    move_buffer.push(Move::new(from, to, ROOK_PROMO_FLAG));
                    move_buffer.push(Move::new(from, to, QUEEN_PROMO_FLAG));
                } else {
                    move_buffer.push(Move::new(from, to, QUIET_FLAG));
                }
            }

            // double pushes
            for to in double_advanced_pawns & empty_squares & double_pawn_push_rank & quiet_mask {

                let from = Square::new((to.unwrap() as i32 + delta_pawn_double_push) as u32);

                move_buffer.push(Move::new(from, to, DOUBLE_PAWN_PUSH_FLAG));
            }

            let pawns_that_can_capture = friendly_pawns & !pinned_nondiagonally;

            // captures (and capture-promotions)
            for from in pawns_that_can_capture
            {
                let pawn_attack_pattern = PAWN_ATTACKS[friendly_color as usize][from.idx()] & capture_mask;

                for to in pawn_attack_pattern & opponent_pieces
                {
                    if to.rank() == promotion_rank {
                        move_buffer.push(Move::new(from, to, BISHOP_PROMO_CAPTURE_FLAG));
                        move_buffer.push(Move::new(from, to, KNIGHT_PROMO_CAPTURE_FLAG));
                        move_buffer.push(Move::new(from, to, ROOK_PROMO_CAPTURE_FLAG));
                        move_buffer.push(Move::new(from, to, QUEEN_PROMO_CAPTURE_FLAG));
                    } else {
                        move_buffer.push(Move::new(from, to, CAPTURE_FLAG));
                    }
                }

                match game.ep_square {
                    //TODO: en-passante discovered check test
                    Some(sq) =>
                        if (pawn_attack_pattern & sq.bitrep()).nonempty() {
                            move_buffer.push(Move::new(from, sq, EP_CAPTURE_FLAG));
                        },
                    None => {}
                }
            }
        }

        /***********/
        /* KNIGHTS */
        /***********/
        {
            for from in game.board.get_pieces(friendly_color, Knight) & !pinned
            {
                let knight_moves = KNIGHT_TABLE[from.idx()];

                /* quiets */
                for to in knight_moves & empty_squares & quiet_mask {
                    move_buffer.push(Move::new(from, to, QUIET_FLAG));
                }

                /* captures */
                for to in knight_moves & opponent_pieces & capture_mask {
                    move_buffer.push(Move::new(from, to, CAPTURE_FLAG));
                }
            }
        }

        /***********/
        /* BISHOPS */
        /***********/

        {
            let friendly_bishops = game.board.get_pieces(friendly_color, Bishop);

            // UNPINNED
            for from in friendly_bishops & !pinned
            {
                let bishop_moves = get_bishop_rays(from, occupied_squares);

                // quiets
                for to in bishop_moves & empty_squares & quiet_mask {
                    move_buffer.push(Move::new(from, to, QUIET_FLAG));
                }

                // captures
                for to in bishop_moves & opponent_pieces & capture_mask {
                    move_buffer.push(Move::new(from, to, CAPTURE_FLAG));
                }
            }

            // PINNED
            for from in friendly_bishops & pinned_diagonally
            {
                let bishop_moves = get_bishop_rays(from, occupied_squares) & self.diag_pin_map[from.idx()];

                // quiets
                for to in bishop_moves & empty_squares & quiet_mask {
                    move_buffer.push(Move::new(from, to, QUIET_FLAG));
                }

                // captures
                for to in bishop_moves & opponent_pieces & capture_mask {
                    move_buffer.push(Move::new(from, to, CAPTURE_FLAG));
                }
            }

        }

        /*********/
        /* ROOKS */
        /*********/

        {
            let friendly_rooks = game.board.get_pieces(friendly_color, Rook);

            // unpinned
            for from in friendly_rooks & !pinned
            {
                let rook_moves = get_rook_rays(from, occupied_squares);

                /* quiets */
                for to in rook_moves & empty_squares & quiet_mask {
                    move_buffer.push(Move::new(from, to, QUIET_FLAG));
                }

                /* captures */
                for to in rook_moves & opponent_pieces & capture_mask {
                    move_buffer.push(Move::new(from, to, CAPTURE_FLAG));
                }
            }

            // pinned
            for from in friendly_rooks & pinned_nondiagonally
            {
                let rook_moves = get_rook_rays(from, occupied_squares) & self.nondiag_pin_map[from.idx()];

                /* quiets */
                for to in rook_moves & empty_squares & quiet_mask {
                    move_buffer.push(Move::new(from, to, QUIET_FLAG));
                }

                /* captures */
                for to in rook_moves & opponent_pieces & capture_mask {
                    move_buffer.push(Move::new(from, to, CAPTURE_FLAG));
                }
            }
        }

        /*********/
        /* QUEEN */
        /*********/
        {
            let friendly_queens = game.board.get_pieces(friendly_color, Queen);

            for from in friendly_queens & !pinned
            {
                let queen_moves = get_queen_rays(from, occupied_squares);

                /* quiets */
                for to in queen_moves & empty_squares & quiet_mask {
                    move_buffer.push(Move::new(from, to, QUIET_FLAG));
                }

                /* captures */
                for to in queen_moves & opponent_pieces & capture_mask {
                    move_buffer.push(Move::new(from, to, CAPTURE_FLAG));
                }
            }

            for from in friendly_queens & pinned
            {
                let queen_moves = get_queen_rays(from, occupied_squares)
                    & self.nondiag_pin_map[from.idx()] & self.diag_pin_map[from.idx()];

                /* quiets */
                for to in queen_moves & empty_squares & quiet_mask {
                    move_buffer.push(Move::new(from, to, QUIET_FLAG));
                }

                /* captures */
                for to in queen_moves & opponent_pieces & capture_mask {
                    move_buffer.push(Move::new(from, to, CAPTURE_FLAG));
                }
            }
        }

        /********/
        /* KING */
        /********/

        {
            /* quiets */
            for to in king_moves & empty_squares & !king_danger_squares {
                move_buffer.push(Move::new(king_square, to, QUIET_FLAG));
            }

            /* captures */
            for to in king_moves & opponent_pieces & capture_mask {
                move_buffer.push(Move::new(king_square, to, CAPTURE_FLAG));
            }

            /* castling */
            let has_kingside_castle_rights = game.castling_rights.intersects(CastlingRights::WHITE_KINGSIDE);
            let has_queenside_castle_rights = game.castling_rights.intersects(CastlingRights::WHITE_QUEENSIDE);

            if has_kingside_castle_rights && !in_check {
                let kingside_castle_path_open = (occupied_squares & WHITE_KINGSIDE_CASTLE_BITS).empty();

                if kingside_castle_path_open {
                    let mut castle_path_is_safe: bool = true;

                    if (WHITE_KINGSIDE_CASTLE_BITS & king_danger_squares).nonempty() {
                        castle_path_is_safe = false;
                    }

                    if castle_path_is_safe {
                        move_buffer.push(Move::new(king_square, Square::new(1), KING_CASTLE_FLAG));
                    }
                }
            }

            if has_queenside_castle_rights && !in_check {
                let queenside_castle_path_open = (occupied_squares & WHITE_QUEENSIDE_CASTLE_BITS).empty();

                if queenside_castle_path_open {
                    let mut castle_path_is_safe: bool = true;

                    if (WHITE_QUEENSIDE_CASTLE_BITS & king_danger_squares).nonempty() {
                        castle_path_is_safe = false;
                    }

                    if castle_path_is_safe {
                        move_buffer.push(Move::new(king_square, Square::new(5), QUEEN_CASTLE_FLAG));
                    }
                }
            }
        }

        // MOVE GENERATION FOR PINNED PIECES

    }
}
<|endoftext|>


<commit_before>use cell::GlobalCell;

pub static STACK: GlobalCell<StackImpl> = GlobalCell::new();

/// Uni.rs network stack
pub struct Stack;

impl Stack {
    #[doc(hidden)]
    pub fn init() {
        STACK.set(StackImpl::new());
    }
}

pub struct StackImpl;

unsafe impl Sync for StackImpl {}

impl StackImpl {
    pub fn new() -> Self {
        StackImpl
    }
}
<commit_msg>net: Add Stack::enqueue_rx_packet()<commit_after>use sync::Arc;

use vec_deque::VecDeque;

use cell::GlobalCell;

use sync::spin::{InterruptSpinLock, RwLock};

use thread::WaitQueue;

use net::Interface;

use net::Packet;

const MAX_QUEUE_SIZE: usize = 512;

pub static STACK: GlobalCell<StackImpl> = GlobalCell::new();

/// Uni.rs network stack
pub struct Stack;

impl Stack {
    #[doc(hidden)]
    pub fn init() {
        STACK.set(StackImpl::new());
    }

    #[inline]
    /// Enqueue a packet inside the `rx_queue` and notify `rx_wait`.
    ///
    /// Returns false if the `rx_queue` is full and therefor was not enqueued,
    /// true otherwise.
    ///
    /// Note: This is safe to be called from interrupt context. Indeed the
    /// `rx_queue` is not resizable so no allocation will be performed by this
    /// function.
    pub fn enqueue_rx_packet(intf: *const Arc<RwLock<Interface>>,
                             packet: Packet) -> bool {
        STACK.as_mut().enqueue_rx_packet(intf, packet)
    }
}

pub struct StackImpl {
    /// Contains packets to be processed
    rx_queue: InterruptSpinLock<VecDeque<(*const Arc<RwLock<Interface>>, Packet)>>,
    /// Used to wait for packet to arrive in the rx_queue
    rx_wait: WaitQueue,
}

// rx_wait is Sync
// rx_queue is protected by a spin lock
unsafe impl Sync for StackImpl {}

impl StackImpl {
    pub fn new() -> Self {
        StackImpl {
            rx_queue: InterruptSpinLock::new(VecDeque::with_capacity(MAX_QUEUE_SIZE)),
            rx_wait: WaitQueue::new(),
        }
    }

    pub fn enqueue_rx_packet(&mut self, intf: *const Arc<RwLock<Interface>>,
                             packet: Packet) -> bool {
        let mut locked_rx_queue = self.rx_queue.lock();

        if locked_rx_queue.len() == MAX_QUEUE_SIZE {
            // Queue is full, we don't want to cause a reallocation in
            // interruption context. So we don't enqueue the packet
            return false;
        }

        // Enqueue the packet
        locked_rx_queue.push_back((intf, packet));

        // Wake up network thread
        self.rx_wait.unblock();

        true
    }
}
<|endoftext|>


<commit_before>/// Sure tree scanning from the filesystem.
use crate::{
    escape::Escape, node::SureNode, progress::ScanProgress, surefs::encode_atts, suretree::AttMap,
    Error, Result,
};
use log::error;
use std::{
    collections::VecDeque,
    fs::{self, symlink_metadata, Metadata},
    os::unix::prelude::*,
    path::{Path, PathBuf},
};

pub fn walk<P: AsRef<Path>>(root: P) -> Result<()> {
    for entry in scan_fs(root)? {
        let entry = entry?;
        println!("{:?}", entry);
    }

    Ok(())
}

/// A filesystem scanner walks a filesystem, iterating over a tree as it is
/// encountered.
pub fn scan_fs<P: AsRef<Path>>(root: P) -> Result<ScanIterator> {
    let root = root.as_ref().to_path_buf();
    let meta = symlink_metadata(&root)?;

    if !meta.is_dir() {
        return Err(Error::RootMustBeDir);
    }

    let atts = encode_atts(&root, &meta);
    let root_dev = meta.dev();
    let mut todo = VecDeque::new();
    todo.push_back(AugNode::SubDir {
        path: root,
        name: "__root__".to_string(),
        meta,
        atts,
    });

    let si = ScanIterator {
        todo,
        root_dev,
        progress: ScanProgress::new(),
    };

    Ok(si)
}

pub struct ScanIterator {
    todo: VecDeque<AugNode>,
    root_dev: u64,
    progress: ScanProgress,
}

impl Iterator for ScanIterator {
    type Item = Result<SureNode>;

    fn next(&mut self) -> Option<Result<SureNode>> {
        match self.todo.pop_front() {
            None => None,
            Some(AugNode::Normal(e)) => Some(Ok(e)),
            Some(AugNode::SubDir {
                path,
                name,
                atts,
                meta,
            }) => {
                // Push the contents of this directory.  Unless we have
                // crossed a mountpoint.
                if !meta.is_dir() || meta.dev() == self.root_dev {
                    match self.push_dir(&path) {
                        Ok(()) => (),
                        Err(e) => return Some(Err(e)),
                    };
                } else {
                    self.push_empty_dir();
                }

                Some(Ok(SureNode::Enter { name, atts }))
            }
        }
    }
}

impl ScanIterator {
    fn push_dir(&mut self, path: &Path) -> Result<()> {
        let mut entries = vec![];

        for entry in fs::read_dir(path)? {
            let entry = entry?;
            entries.push(entry);
        }

        // Sort by inode first.  This helps performance on some filesystems
        // (such as ext4).
        entries.sort_by_key(|a| a.ino());

        let mut files: Vec<_> = entries
            .iter()
            .filter_map(|e| match e.metadata() {
                Ok(m) => {
                    let path = e.path();
                    let atts = encode_atts(&path, &m);

                    Some(OneFile {
                        path,
                        meta: m,
                        atts,
                    })
                }
                Err(err) => {
                    error!("Unable to stat file: {:?} ({})", e.path(), err);
                    None
                }
            })
            .collect();

        // Sort them back by name.
        files.sort_by(|a, b| a.path.file_name().cmp(&b.path.file_name()));

        let (dirs, files): (Vec<_>, Vec<_>) = files.into_iter().partition(|n| n.meta.is_dir());

        self.progress.update(
            dirs.len() as u64,
            files.len() as u64,
            files.iter().map(|x| x.meta.len()).sum(),
        );

        self.todo.push_front(AugNode::Normal(SureNode::Leave));

        // The files in reverse order.
        for f in files.into_iter().rev() {
            self.todo.push_front(AugNode::Normal(SureNode::File {
                name: f.path.file_name().unwrap().as_bytes().escaped(),
                atts: f.atts,
            }));
        }

        self.todo.push_front(AugNode::Normal(SureNode::Sep));

        // The dirs in reverse order.
        for d in dirs.into_iter().rev() {
            let name = d.path.file_name().unwrap().as_bytes().escaped();
            self.todo.push_front(AugNode::SubDir {
                path: d.path,
                name,
                meta: d.meta,
                atts: d.atts,
            });
        }

        Ok(())
    }

    /// Pushes the Sep and Leave needed to make an empty directory work.
    /// Used when skipping directories that cross mountpoints.
    fn push_empty_dir(&mut self) {
        self.todo.push_front(AugNode::Normal(SureNode::Leave));
        self.todo.push_front(AugNode::Normal(SureNode::Sep));
    }
}

struct OneFile {
    path: PathBuf,
    meta: Metadata,
    atts: AttMap,
}

/// Augmented entries.  This intersperses regular nodes with special ones
/// containing enough information to add subdirectories.
enum AugNode {
    Normal(SureNode),
    SubDir {
        path: PathBuf,
        name: String,
        meta: Metadata,
        atts: AttMap,
    },
}
<commit_msg>Handle directories that can't be read from<commit_after>/// Sure tree scanning from the filesystem.
use crate::{
    escape::Escape, node::SureNode, progress::ScanProgress, surefs::encode_atts, suretree::AttMap,
    Error, Result,
};
use log::error;
use std::{
    collections::VecDeque,
    fs::{self, symlink_metadata, Metadata},
    os::unix::prelude::*,
    path::{Path, PathBuf},
};

pub fn walk<P: AsRef<Path>>(root: P) -> Result<()> {
    for entry in scan_fs(root)? {
        let entry = entry?;
        println!("{:?}", entry);
    }

    Ok(())
}

/// A filesystem scanner walks a filesystem, iterating over a tree as it is
/// encountered.
pub fn scan_fs<P: AsRef<Path>>(root: P) -> Result<ScanIterator> {
    let root = root.as_ref().to_path_buf();
    let meta = symlink_metadata(&root)?;

    if !meta.is_dir() {
        return Err(Error::RootMustBeDir);
    }

    let atts = encode_atts(&root, &meta);
    let root_dev = meta.dev();
    let mut todo = VecDeque::new();
    todo.push_back(AugNode::SubDir {
        path: root,
        name: "__root__".to_string(),
        meta,
        atts,
    });

    let si = ScanIterator {
        todo,
        root_dev,
        progress: ScanProgress::new(),
    };

    Ok(si)
}

pub struct ScanIterator {
    todo: VecDeque<AugNode>,
    root_dev: u64,
    progress: ScanProgress,
}

impl Iterator for ScanIterator {
    type Item = Result<SureNode>;

    fn next(&mut self) -> Option<Result<SureNode>> {
        match self.todo.pop_front() {
            None => None,
            Some(AugNode::Normal(e)) => Some(Ok(e)),
            Some(AugNode::SubDir {
                path,
                name,
                atts,
                meta,
            }) => {
                // Push the contents of this directory.  Unless we have
                // crossed a mountpoint.
                if !meta.is_dir() || meta.dev() == self.root_dev {
                    match self.push_dir(&path) {
                        Ok(()) => (),
                        Err(e) => return Some(Err(e)),
                    };
                } else {
                    self.push_empty_dir();
                }

                Some(Ok(SureNode::Enter { name, atts }))
            }
        }
    }
}

impl ScanIterator {
    fn push_dir(&mut self, path: &Path) -> Result<()> {
        let mut entries = vec![];

        match fs::read_dir(path) {
            Ok(dir) => {
                for entry in dir {
                    let entry = match entry {
                        Ok(ent) => ent,
                        Err(err) => {
                            error!("Unable to read from dir: {:?} ({})", path, err);
                            break;
                        }
                    };
                    entries.push(entry);
                }
            }
            Err(e) => {
                // Warn about the issue, but otherwise continue, with just an empty directory.
                error!("Unable to read dir: {:?} ({})", path, e);
            }
        };

        // Sort by inode first.  This helps performance on some filesystems
        // (such as ext4).
        entries.sort_by_key(|a| a.ino());

        let mut files: Vec<_> = entries
            .iter()
            .filter_map(|e| match e.metadata() {
                Ok(m) => {
                    let path = e.path();
                    let atts = encode_atts(&path, &m);

                    Some(OneFile {
                        path,
                        meta: m,
                        atts,
                    })
                }
                Err(err) => {
                    error!("Unable to stat file: {:?} ({})", e.path(), err);
                    None
                }
            })
            .collect();

        // Sort them back by name.
        files.sort_by(|a, b| a.path.file_name().cmp(&b.path.file_name()));

        let (dirs, files): (Vec<_>, Vec<_>) = files.into_iter().partition(|n| n.meta.is_dir());

        self.progress.update(
            dirs.len() as u64,
            files.len() as u64,
            files.iter().map(|x| x.meta.len()).sum(),
        );

        self.todo.push_front(AugNode::Normal(SureNode::Leave));

        // The files in reverse order.
        for f in files.into_iter().rev() {
            self.todo.push_front(AugNode::Normal(SureNode::File {
                name: f.path.file_name().unwrap().as_bytes().escaped(),
                atts: f.atts,
            }));
        }

        self.todo.push_front(AugNode::Normal(SureNode::Sep));

        // The dirs in reverse order.
        for d in dirs.into_iter().rev() {
            let name = d.path.file_name().unwrap().as_bytes().escaped();
            self.todo.push_front(AugNode::SubDir {
                path: d.path,
                name,
                meta: d.meta,
                atts: d.atts,
            });
        }

        Ok(())
    }

    /// Pushes the Sep and Leave needed to make an empty directory work.
    /// Used when skipping directories that cross mountpoints.
    fn push_empty_dir(&mut self) {
        self.todo.push_front(AugNode::Normal(SureNode::Leave));
        self.todo.push_front(AugNode::Normal(SureNode::Sep));
    }
}

struct OneFile {
    path: PathBuf,
    meta: Metadata,
    atts: AttMap,
}

/// Augmented entries.  This intersperses regular nodes with special ones
/// containing enough information to add subdirectories.
enum AugNode {
    Normal(SureNode),
    SubDir {
        path: PathBuf,
        name: String,
        meta: Metadata,
        atts: AttMap,
    },
}
<|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::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::VideoTrackListBinding::{self, VideoTrackListMethods};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::Dom;
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::eventtarget::EventTarget;
use crate::dom::videotrack::VideoTrack;
use crate::dom::window::Window;
use crate::task_source::TaskSource;
use dom_struct::dom_struct;

#[dom_struct]
pub struct VideoTrackList {
    eventtarget: EventTarget,
    tracks: DomRefCell<Vec<Dom<VideoTrack>>>,
}

impl VideoTrackList {
    pub fn new_inherited(tracks: &[&VideoTrack]) -> VideoTrackList {
        VideoTrackList {
            eventtarget: EventTarget::new_inherited(),
            tracks: DomRefCell::new(tracks.iter().map(|track| Dom::from_ref(&**track)).collect()),
        }
    }

    pub fn new(window: &Window, tracks: &[&VideoTrack]) -> DomRoot<VideoTrackList> {
        reflect_dom_object(
            Box::new(VideoTrackList::new_inherited(tracks)),
            window,
            VideoTrackListBinding::Wrap,
        )
    }

    pub fn len(&self) -> usize {
        self.tracks.borrow().len()
    }

    pub fn item(&self, idx: usize) -> Option<DomRoot<VideoTrack>> {
        self.tracks
            .borrow()
            .get(idx)
            .map(|track| DomRoot::from_ref(&**track))
    }

    pub fn selected_index(&self) -> Option<usize> {
        self.tracks
            .borrow()
            .iter()
            .position(|track| track.selected())
    }

    // TODO(#22799) Integrate DOM Audio and Video track selection with media player.
    pub fn set_selected(&self, idx: usize, value: bool) {
        let track = match self.item(idx) {
            Some(t) => t,
            None => return,
        };

        // If the chosen tracks selected status is the same as the new status, return early.
        if track.selected() == value {
            return;
        }

        let global = &self.global();
        let this = Trusted::new(self);
        let (source, canceller) = global
            .as_window()
            .task_manager()
            .media_element_task_source_with_canceller();

        if let Some(current) = self.selected_index() {
            if current != idx {
                self.tracks.borrow()[current].set_selected(false);
                track.set_selected(true);

                let _ = source.queue_with_canceller(
                    task!(media_track_change: move || {
                        let this = this.root();
                        this.upcast::<EventTarget>().fire_event(atom!("change"));
                    }),
                    &canceller,
                );
            } else {
                self.tracks.borrow()[current].set_selected(false);

                let _ = source.queue_with_canceller(
                    task!(media_track_change: move || {
                        let this = this.root();
                        this.upcast::<EventTarget>().fire_event(atom!("change"));
                    }),
                    &canceller,
                );
            }
        } else {
            track.set_selected(true);

            let _ = source.queue_with_canceller(
                task!(media_track_change: move || {
                    let this = this.root();
                    this.upcast::<EventTarget>().fire_event(atom!("change"));
                }),
                &canceller,
            );
        }
    }

    pub fn add(&self, track: &VideoTrack) {
        self.tracks.borrow_mut().push(Dom::from_ref(track));
        if track.selected() {
            if let Some(idx) = self.selected_index() {
                self.set_selected(idx, false);
            }
        }
    }

    pub fn clear(&self) {
        self.tracks.borrow_mut().clear();
    }
}

impl VideoTrackListMethods for VideoTrackList {
    // https://html.spec.whatwg.org/multipage/#dom-videotracklist-length
    fn Length(&self) -> u32 {
        self.len() as u32
    }

    // https://html.spec.whatwg.org/multipage/#dom-tracklist-item
    fn IndexedGetter(&self, idx: u32) -> Option<DomRoot<VideoTrack>> {
        self.item(idx as usize)
    }

    // https://html.spec.whatwg.org/multipage/#dom-videotracklist-gettrackbyid
    fn GetTrackById(&self, id: DOMString) -> Option<DomRoot<VideoTrack>> {
        self.tracks
            .borrow()
            .iter()
            .find(|track| track.id() == id)
            .map(|track| DomRoot::from_ref(&**track))
    }

    // https://html.spec.whatwg.org/multipage/#dom-videotrack-selected
    fn SelectedIndex(&self) -> i32 {
        if let Some(idx) = self.selected_index() {
            return idx as i32;
        }
        return -1;
    }

    // https://html.spec.whatwg.org/multipage/#handler-tracklist-onchange
    event_handler!(change, GetOnchange, SetOnchange);

    // https://html.spec.whatwg.org/multipage/#handler-tracklist-onaddtrack
    event_handler!(addtrack, GetOnaddtrack, SetOnaddtrack);

    // https://html.spec.whatwg.org/multipage/#handler-tracklist-onremovetrack
    event_handler!(removetrack, GetOnremovetrack, SetOnremovetrack);
}
<commit_msg>Auto merge of #22966 - Goncalerta:22962, r=jdm<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::VideoTrackListBinding::{self, VideoTrackListMethods};
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::refcounted::Trusted;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::Dom;
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::eventtarget::EventTarget;
use crate::dom::videotrack::VideoTrack;
use crate::dom::window::Window;
use crate::task_source::TaskSource;
use dom_struct::dom_struct;

#[dom_struct]
pub struct VideoTrackList {
    eventtarget: EventTarget,
    tracks: DomRefCell<Vec<Dom<VideoTrack>>>,
}

impl VideoTrackList {
    pub fn new_inherited(tracks: &[&VideoTrack]) -> VideoTrackList {
        VideoTrackList {
            eventtarget: EventTarget::new_inherited(),
            tracks: DomRefCell::new(tracks.iter().map(|track| Dom::from_ref(&**track)).collect()),
        }
    }

    pub fn new(window: &Window, tracks: &[&VideoTrack]) -> DomRoot<VideoTrackList> {
        reflect_dom_object(
            Box::new(VideoTrackList::new_inherited(tracks)),
            window,
            VideoTrackListBinding::Wrap,
        )
    }

    pub fn len(&self) -> usize {
        self.tracks.borrow().len()
    }

    pub fn item(&self, idx: usize) -> Option<DomRoot<VideoTrack>> {
        self.tracks
            .borrow()
            .get(idx)
            .map(|track| DomRoot::from_ref(&**track))
    }

    pub fn selected_index(&self) -> Option<usize> {
        self.tracks
            .borrow()
            .iter()
            .position(|track| track.selected())
    }

    // TODO(#22799) Integrate DOM Audio and Video track selection with media player.
    pub fn set_selected(&self, idx: usize, value: bool) {
        let track = match self.item(idx) {
            Some(t) => t,
            None => return,
        };

        // If the chosen tracks selected status is the same as the new status, return early.
        if track.selected() == value {
            return;
        }

        let global = &self.global();
        let this = Trusted::new(self);
        let (source, canceller) = global
            .as_window()
            .task_manager()
            .media_element_task_source_with_canceller();

        if let Some(current) = self.selected_index() {
            self.tracks.borrow()[current].set_selected(false);
        }

        track.set_selected(value);

        let _ = source.queue_with_canceller(
            task!(media_track_change: move || {
                let this = this.root();
                this.upcast::<EventTarget>().fire_event(atom!("change"));
            }),
            &canceller,
        );
    }

    pub fn add(&self, track: &VideoTrack) {
        self.tracks.borrow_mut().push(Dom::from_ref(track));
        if track.selected() {
            if let Some(idx) = self.selected_index() {
                self.set_selected(idx, false);
            }
        }
    }

    pub fn clear(&self) {
        self.tracks.borrow_mut().clear();
    }
}

impl VideoTrackListMethods for VideoTrackList {
    // https://html.spec.whatwg.org/multipage/#dom-videotracklist-length
    fn Length(&self) -> u32 {
        self.len() as u32
    }

    // https://html.spec.whatwg.org/multipage/#dom-tracklist-item
    fn IndexedGetter(&self, idx: u32) -> Option<DomRoot<VideoTrack>> {
        self.item(idx as usize)
    }

    // https://html.spec.whatwg.org/multipage/#dom-videotracklist-gettrackbyid
    fn GetTrackById(&self, id: DOMString) -> Option<DomRoot<VideoTrack>> {
        self.tracks
            .borrow()
            .iter()
            .find(|track| track.id() == id)
            .map(|track| DomRoot::from_ref(&**track))
    }

    // https://html.spec.whatwg.org/multipage/#dom-videotrack-selected
    fn SelectedIndex(&self) -> i32 {
        if let Some(idx) = self.selected_index() {
            return idx as i32;
        }
        return -1;
    }

    // https://html.spec.whatwg.org/multipage/#handler-tracklist-onchange
    event_handler!(change, GetOnchange, SetOnchange);

    // https://html.spec.whatwg.org/multipage/#handler-tracklist-onaddtrack
    event_handler!(addtrack, GetOnaddtrack, SetOnaddtrack);

    // https://html.spec.whatwg.org/multipage/#handler-tracklist-onremovetrack
    event_handler!(removetrack, GetOnremovetrack, SetOnremovetrack);
}
<|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::attr::{Attr, AttrValue};
use dom::bindings::codegen::InheritTypes::ElementCast;
use dom::bindings::codegen::InheritTypes::HTMLAnchorElementCast;
use dom::bindings::codegen::InheritTypes::HTMLAreaElementCast;
use dom::bindings::codegen::InheritTypes::HTMLBodyElementCast;
use dom::bindings::codegen::InheritTypes::HTMLButtonElementCast;
use dom::bindings::codegen::InheritTypes::HTMLCanvasElementCast;
use dom::bindings::codegen::InheritTypes::HTMLElementCast;
use dom::bindings::codegen::InheritTypes::HTMLFieldSetElementCast;
use dom::bindings::codegen::InheritTypes::HTMLIFrameElementCast;
use dom::bindings::codegen::InheritTypes::HTMLImageElementCast;
use dom::bindings::codegen::InheritTypes::HTMLInputElementCast;
use dom::bindings::codegen::InheritTypes::HTMLLinkElementCast;
use dom::bindings::codegen::InheritTypes::HTMLObjectElementCast;
use dom::bindings::codegen::InheritTypes::HTMLOptGroupElementCast;
use dom::bindings::codegen::InheritTypes::HTMLOptionElementCast;
use dom::bindings::codegen::InheritTypes::HTMLScriptElementCast;
use dom::bindings::codegen::InheritTypes::HTMLSelectElementCast;
use dom::bindings::codegen::InheritTypes::HTMLStyleElementCast;
use dom::bindings::codegen::InheritTypes::HTMLTableElementCast;
use dom::bindings::codegen::InheritTypes::HTMLTableCellElementCast;
use dom::bindings::codegen::InheritTypes::HTMLTableRowElementCast;
use dom::bindings::codegen::InheritTypes::HTMLTableSectionElementCast;
use dom::bindings::codegen::InheritTypes::HTMLTextAreaElementCast;
use dom::bindings::codegen::InheritTypes::HTMLTitleElementCast;
use dom::bindings::js::JSRef;
use dom::document::Document;
use dom::element::Element;
use dom::element::ElementTypeId;
use dom::event::Event;
use dom::htmlanchorelement::HTMLAnchorElement;
use dom::htmlareaelement::HTMLAreaElement;
use dom::htmlbodyelement::HTMLBodyElement;
use dom::htmlbuttonelement::HTMLButtonElement;
use dom::htmlcanvaselement::HTMLCanvasElement;
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
use dom::htmlfieldsetelement::HTMLFieldSetElement;
use dom::htmliframeelement::HTMLIFrameElement;
use dom::htmlimageelement::HTMLImageElement;
use dom::htmlinputelement::HTMLInputElement;
use dom::htmllinkelement::HTMLLinkElement;
use dom::htmlobjectelement::HTMLObjectElement;
use dom::htmloptgroupelement::HTMLOptGroupElement;
use dom::htmloptionelement::HTMLOptionElement;
use dom::htmlscriptelement::HTMLScriptElement;
use dom::htmlselectelement::HTMLSelectElement;
use dom::htmlstyleelement::HTMLStyleElement;
use dom::htmltableelement::HTMLTableElement;
use dom::htmltablecellelement::{HTMLTableCellElement, HTMLTableCellElementTypeId};
use dom::htmltablerowelement::HTMLTableRowElement;
use dom::htmltablesectionelement::HTMLTableSectionElement;
use dom::htmltextareaelement::HTMLTextAreaElement;
use dom::htmltitleelement::HTMLTitleElement;
use dom::node::{Node, NodeHelpers, NodeTypeId, CloneChildrenFlag};

use servo_util::str::DOMString;

use string_cache::Atom;

/// Trait to allow DOM nodes to opt-in to overriding (or adding to) common
/// behaviours. Replicates the effect of C++ virtual methods.
pub trait VirtualMethods {
    /// Returns self as the superclass of the implementation for this trait,
    /// if any.
    fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods>;

    /// Called when changing or adding attributes, after the attribute's value
    /// has been updated.
    fn after_set_attr(&self, attr: JSRef<Attr>) {
        match self.super_type() {
            Some(ref s) => s.after_set_attr(attr),
            _ => (),
        }
    }

    /// Called when changing or removing attributes, before any modification
    /// has taken place.
    fn before_remove_attr(&self, attr: JSRef<Attr>) {
        match self.super_type() {
            Some(ref s) => s.before_remove_attr(attr),
            _ => (),
        }
    }

    /// Returns the right AttrValue variant for the attribute with name `name`
    /// on this element.
    fn parse_plain_attribute(&self, name: &Atom, value: DOMString) -> AttrValue {
        match self.super_type() {
            Some(ref s) => s.parse_plain_attribute(name, value),
            _ => AttrValue::String(value),
        }
    }

    /// Called when a Node is appended to a tree, where 'tree_in_doc' indicates
    /// whether the tree is part of a Document.
    fn bind_to_tree(&self, tree_in_doc: bool) {
        match self.super_type() {
            Some(ref s) => s.bind_to_tree(tree_in_doc),
            _ => (),
        }
    }

    /// Called when a Node is removed from a tree, where 'tree_in_doc'
    /// indicates whether the tree is part of a Document.
    fn unbind_from_tree(&self, tree_in_doc: bool) {
        match self.super_type() {
            Some(ref s) => s.unbind_from_tree(tree_in_doc),
            _ => (),
        }
    }

    /// Called on the parent when a node is added to its child list.
    fn child_inserted(&self, child: JSRef<Node>) {
        match self.super_type() {
            Some(ref s) => s.child_inserted(child),
            _ => (),
        }
    }

    /// Called during event dispatch after the bubbling phase completes.
    fn handle_event(&self, event: JSRef<Event>) {
        match self.super_type() {
            Some(s) => {
                s.handle_event(event);
            }
            _ => (),
        }
    }

    /// https://dom.spec.whatwg.org/#concept-node-clone (step 5)
    fn cloning_steps(&self, copy: JSRef<Node>, maybe_doc: Option<JSRef<Document>>,
                     clone_children: CloneChildrenFlag) {
        match self.super_type() {
            Some(ref s) => s.cloning_steps(copy, maybe_doc, clone_children),
            _ => (),
        }
    }
}

/// Obtain a VirtualMethods instance for a given Node-derived object. Any
/// method call on the trait object will invoke the corresponding method on the
/// concrete type, propagating up the parent hierarchy unless otherwise
/// interrupted.
pub fn vtable_for<'a>(node: &'a JSRef<'a, Node>) -> &'a (VirtualMethods + 'a) {
    match node.type_id() {
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAnchorElement)) => {
            let element: &'a JSRef<'a, HTMLAnchorElement> = HTMLAnchorElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAreaElement)) => {
            let element: &'a JSRef<'a, HTMLAreaElement> = HTMLAreaElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBodyElement)) => {
            let element: &'a JSRef<'a, HTMLBodyElement> = HTMLBodyElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLButtonElement)) => {
            let element: &'a JSRef<'a, HTMLButtonElement> = HTMLButtonElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLCanvasElement)) => {
            let element: &'a JSRef<'a, HTMLCanvasElement> = HTMLCanvasElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLFieldSetElement)) => {
            let element: &'a JSRef<'a, HTMLFieldSetElement> = HTMLFieldSetElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLImageElement)) => {
            let element: &'a JSRef<'a, HTMLImageElement> = HTMLImageElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLIFrameElement)) => {
            let element: &'a JSRef<'a, HTMLIFrameElement> = HTMLIFrameElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement)) => {
            let element: &'a JSRef<'a, HTMLInputElement> = HTMLInputElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLinkElement)) => {
            let element: &'a JSRef<'a, HTMLLinkElement> = HTMLLinkElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLObjectElement)) => {
            let element: &'a JSRef<'a, HTMLObjectElement> = HTMLObjectElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLOptGroupElement)) => {
            let element: &'a JSRef<'a, HTMLOptGroupElement> = HTMLOptGroupElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLOptionElement)) => {
            let element: &'a JSRef<'a, HTMLOptionElement> = HTMLOptionElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLScriptElement)) => {
            let element: &'a JSRef<'a, HTMLScriptElement> = HTMLScriptElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSelectElement)) => {
            let element: &'a JSRef<'a, HTMLSelectElement> = HTMLSelectElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLStyleElement)) => {
            let element: &'a JSRef<'a, HTMLStyleElement> = HTMLStyleElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableElement)) => {
            let element: &'a JSRef<'a, HTMLTableElement> =
                HTMLTableElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(
                            HTMLElementTypeId::HTMLTableCellElement(
                            HTMLTableCellElementTypeId::HTMLTableDataCellElement))) |
        NodeTypeId::Element(ElementTypeId::HTMLElement(
                            HTMLElementTypeId::HTMLTableCellElement(
                            HTMLTableCellElementTypeId::HTMLTableHeaderCellElement))) => {
            let element: &'a JSRef<'a, HTMLTableCellElement> =
                HTMLTableCellElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableRowElement)) => {
            let element: &'a JSRef<'a, HTMLTableRowElement> =
                HTMLTableRowElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableSectionElement)) => {
            let element: &'a JSRef<'a, HTMLTableSectionElement> =
                HTMLTableSectionElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement)) => {
            let element: &'a JSRef<'a, HTMLTextAreaElement> = HTMLTextAreaElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTitleElement)) => {
            let element: &'a JSRef<'a, HTMLTitleElement> =
                HTMLTitleElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::Element) => {
            let element: &'a JSRef<'a, Element> = ElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(_) => {
            let element: &'a JSRef<'a, HTMLElement> = HTMLElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        _ => {
            node as &'a (VirtualMethods + 'a)
        }
    }
}
<commit_msg>Simplify vtable_for.<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::bindings::codegen::InheritTypes::ElementCast;
use dom::bindings::codegen::InheritTypes::HTMLAnchorElementCast;
use dom::bindings::codegen::InheritTypes::HTMLAreaElementCast;
use dom::bindings::codegen::InheritTypes::HTMLBodyElementCast;
use dom::bindings::codegen::InheritTypes::HTMLButtonElementCast;
use dom::bindings::codegen::InheritTypes::HTMLCanvasElementCast;
use dom::bindings::codegen::InheritTypes::HTMLElementCast;
use dom::bindings::codegen::InheritTypes::HTMLFieldSetElementCast;
use dom::bindings::codegen::InheritTypes::HTMLIFrameElementCast;
use dom::bindings::codegen::InheritTypes::HTMLImageElementCast;
use dom::bindings::codegen::InheritTypes::HTMLInputElementCast;
use dom::bindings::codegen::InheritTypes::HTMLLinkElementCast;
use dom::bindings::codegen::InheritTypes::HTMLObjectElementCast;
use dom::bindings::codegen::InheritTypes::HTMLOptGroupElementCast;
use dom::bindings::codegen::InheritTypes::HTMLOptionElementCast;
use dom::bindings::codegen::InheritTypes::HTMLScriptElementCast;
use dom::bindings::codegen::InheritTypes::HTMLSelectElementCast;
use dom::bindings::codegen::InheritTypes::HTMLStyleElementCast;
use dom::bindings::codegen::InheritTypes::HTMLTableElementCast;
use dom::bindings::codegen::InheritTypes::HTMLTableCellElementCast;
use dom::bindings::codegen::InheritTypes::HTMLTableRowElementCast;
use dom::bindings::codegen::InheritTypes::HTMLTableSectionElementCast;
use dom::bindings::codegen::InheritTypes::HTMLTextAreaElementCast;
use dom::bindings::codegen::InheritTypes::HTMLTitleElementCast;
use dom::bindings::js::JSRef;
use dom::document::Document;
use dom::element::Element;
use dom::element::ElementTypeId;
use dom::event::Event;
use dom::htmlanchorelement::HTMLAnchorElement;
use dom::htmlareaelement::HTMLAreaElement;
use dom::htmlbodyelement::HTMLBodyElement;
use dom::htmlbuttonelement::HTMLButtonElement;
use dom::htmlcanvaselement::HTMLCanvasElement;
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
use dom::htmlfieldsetelement::HTMLFieldSetElement;
use dom::htmliframeelement::HTMLIFrameElement;
use dom::htmlimageelement::HTMLImageElement;
use dom::htmlinputelement::HTMLInputElement;
use dom::htmllinkelement::HTMLLinkElement;
use dom::htmlobjectelement::HTMLObjectElement;
use dom::htmloptgroupelement::HTMLOptGroupElement;
use dom::htmloptionelement::HTMLOptionElement;
use dom::htmlscriptelement::HTMLScriptElement;
use dom::htmlselectelement::HTMLSelectElement;
use dom::htmlstyleelement::HTMLStyleElement;
use dom::htmltableelement::HTMLTableElement;
use dom::htmltablecellelement::HTMLTableCellElement;
use dom::htmltablerowelement::HTMLTableRowElement;
use dom::htmltablesectionelement::HTMLTableSectionElement;
use dom::htmltextareaelement::HTMLTextAreaElement;
use dom::htmltitleelement::HTMLTitleElement;
use dom::node::{Node, NodeHelpers, NodeTypeId, CloneChildrenFlag};

use servo_util::str::DOMString;

use string_cache::Atom;

/// Trait to allow DOM nodes to opt-in to overriding (or adding to) common
/// behaviours. Replicates the effect of C++ virtual methods.
pub trait VirtualMethods {
    /// Returns self as the superclass of the implementation for this trait,
    /// if any.
    fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods>;

    /// Called when changing or adding attributes, after the attribute's value
    /// has been updated.
    fn after_set_attr(&self, attr: JSRef<Attr>) {
        match self.super_type() {
            Some(ref s) => s.after_set_attr(attr),
            _ => (),
        }
    }

    /// Called when changing or removing attributes, before any modification
    /// has taken place.
    fn before_remove_attr(&self, attr: JSRef<Attr>) {
        match self.super_type() {
            Some(ref s) => s.before_remove_attr(attr),
            _ => (),
        }
    }

    /// Returns the right AttrValue variant for the attribute with name `name`
    /// on this element.
    fn parse_plain_attribute(&self, name: &Atom, value: DOMString) -> AttrValue {
        match self.super_type() {
            Some(ref s) => s.parse_plain_attribute(name, value),
            _ => AttrValue::String(value),
        }
    }

    /// Called when a Node is appended to a tree, where 'tree_in_doc' indicates
    /// whether the tree is part of a Document.
    fn bind_to_tree(&self, tree_in_doc: bool) {
        match self.super_type() {
            Some(ref s) => s.bind_to_tree(tree_in_doc),
            _ => (),
        }
    }

    /// Called when a Node is removed from a tree, where 'tree_in_doc'
    /// indicates whether the tree is part of a Document.
    fn unbind_from_tree(&self, tree_in_doc: bool) {
        match self.super_type() {
            Some(ref s) => s.unbind_from_tree(tree_in_doc),
            _ => (),
        }
    }

    /// Called on the parent when a node is added to its child list.
    fn child_inserted(&self, child: JSRef<Node>) {
        match self.super_type() {
            Some(ref s) => s.child_inserted(child),
            _ => (),
        }
    }

    /// Called during event dispatch after the bubbling phase completes.
    fn handle_event(&self, event: JSRef<Event>) {
        match self.super_type() {
            Some(s) => {
                s.handle_event(event);
            }
            _ => (),
        }
    }

    /// https://dom.spec.whatwg.org/#concept-node-clone (step 5)
    fn cloning_steps(&self, copy: JSRef<Node>, maybe_doc: Option<JSRef<Document>>,
                     clone_children: CloneChildrenFlag) {
        match self.super_type() {
            Some(ref s) => s.cloning_steps(copy, maybe_doc, clone_children),
            _ => (),
        }
    }
}

/// Obtain a VirtualMethods instance for a given Node-derived object. Any
/// method call on the trait object will invoke the corresponding method on the
/// concrete type, propagating up the parent hierarchy unless otherwise
/// interrupted.
pub fn vtable_for<'a>(node: &'a JSRef<'a, Node>) -> &'a (VirtualMethods + 'a) {
    match node.type_id() {
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAnchorElement)) => {
            let element: &'a JSRef<'a, HTMLAnchorElement> = HTMLAnchorElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLAreaElement)) => {
            let element: &'a JSRef<'a, HTMLAreaElement> = HTMLAreaElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBodyElement)) => {
            let element: &'a JSRef<'a, HTMLBodyElement> = HTMLBodyElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLButtonElement)) => {
            let element: &'a JSRef<'a, HTMLButtonElement> = HTMLButtonElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLCanvasElement)) => {
            let element: &'a JSRef<'a, HTMLCanvasElement> = HTMLCanvasElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLFieldSetElement)) => {
            let element: &'a JSRef<'a, HTMLFieldSetElement> = HTMLFieldSetElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLImageElement)) => {
            let element: &'a JSRef<'a, HTMLImageElement> = HTMLImageElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLIFrameElement)) => {
            let element: &'a JSRef<'a, HTMLIFrameElement> = HTMLIFrameElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement)) => {
            let element: &'a JSRef<'a, HTMLInputElement> = HTMLInputElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLLinkElement)) => {
            let element: &'a JSRef<'a, HTMLLinkElement> = HTMLLinkElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLObjectElement)) => {
            let element: &'a JSRef<'a, HTMLObjectElement> = HTMLObjectElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLOptGroupElement)) => {
            let element: &'a JSRef<'a, HTMLOptGroupElement> = HTMLOptGroupElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLOptionElement)) => {
            let element: &'a JSRef<'a, HTMLOptionElement> = HTMLOptionElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLScriptElement)) => {
            let element: &'a JSRef<'a, HTMLScriptElement> = HTMLScriptElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLSelectElement)) => {
            let element: &'a JSRef<'a, HTMLSelectElement> = HTMLSelectElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLStyleElement)) => {
            let element: &'a JSRef<'a, HTMLStyleElement> = HTMLStyleElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableElement)) => {
            let element: &'a JSRef<'a, HTMLTableElement> =
                HTMLTableElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableCellElement(_))) => {
            let element: &'a JSRef<'a, HTMLTableCellElement> =
                HTMLTableCellElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableRowElement)) => {
            let element: &'a JSRef<'a, HTMLTableRowElement> =
                HTMLTableRowElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableSectionElement)) => {
            let element: &'a JSRef<'a, HTMLTableSectionElement> =
                HTMLTableSectionElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement)) => {
            let element: &'a JSRef<'a, HTMLTextAreaElement> = HTMLTextAreaElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTitleElement)) => {
            let element: &'a JSRef<'a, HTMLTitleElement> =
                HTMLTitleElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(ElementTypeId::Element) => {
            let element: &'a JSRef<'a, Element> = ElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        NodeTypeId::Element(_) => {
            let element: &'a JSRef<'a, HTMLElement> = HTMLElementCast::to_borrowed_ref(node).unwrap();
            element as &'a (VirtualMethods + 'a)
        }
        _ => {
            node as &'a (VirtualMethods + 'a)
        }
    }
}
<|endoftext|>