<commit_before>//! Symbols exported by this binary and available for dynamic linking are encoded in mach-o binaries using a special trie
//!
//! **Note**: the trie is constructed lazily in case it won't be used, and since computing exports will require allocation, to compute the exports, you need call the export trie's [exports()](struct.ExportTrie.html#method.exports) method.

// TODO:
// (1) Weak of regular_symbol_info type probably needs to be added ?
// (3) /usr/lib/libstdc++.6.0.9.dylib has flag 0xc at many offsets... they're weak

use core::ops::Range;
use scroll::{self, Pread, Uleb128};
use error;
use core::fmt::{self, Debug};
use mach::load_command;

type Flag = u64;

 // "The following are used on the flags byte of a terminal node
 // in the export information."
pub const EXPORT_SYMBOL_FLAGS_KIND_MASK         : Flag = 0x03;
pub const EXPORT_SYMBOL_FLAGS_KIND_REGULAR      : Flag = 0x00;
pub const EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE     : Flag = 0x02; // this is a symbol not present in the loader.h but only in the dyld compressed image loader source code, and only available with a #def macro for export flags but libobjc. def has this
pub const EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL : Flag = 0x01;
pub const EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION   : Flag = 0x04;
pub const EXPORT_SYMBOL_FLAGS_REEXPORT          : Flag = 0x08;
pub const EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER : Flag = 0x10;

#[derive(Debug)]
pub enum SymbolKind {
    Regular,
    Absolute,
    ThreadLocal,
    UnknownSymbolKind(Flag),
}

impl SymbolKind {
    pub fn new(kind: Flag) -> SymbolKind {
        match kind & EXPORT_SYMBOL_FLAGS_KIND_MASK {
            0x00 => SymbolKind::Regular,
            0x01 => SymbolKind::ThreadLocal,
            0x02 => SymbolKind::Absolute,
            _    => SymbolKind::UnknownSymbolKind(kind),
        }
    }
    pub fn to_str(kind: SymbolKind) -> &'static str {
        match kind {
            SymbolKind::Regular => "Regular",
            SymbolKind::Absolute => "Absolute",
            SymbolKind::ThreadLocal => "Thread_LOCAL",
            SymbolKind::UnknownSymbolKind(_k) => "Unknown",
        }
    }
}

#[derive(Debug)]
/// An export can be a regular export, a re-export, or a stub
pub enum ExportInfo<'a> {
    /// A regular exported symbol, which is an address where it is found, and the flags associated with it
    Regular {
        address: u64,
        flags: Flag,
    },
    /// if lib_symbol_name None then same symbol name, otherwise reexport of lib_symbol_name with name in the trie
    /// "If the string is zero length, then the symbol is re-export from the specified dylib with the same name"
    Reexport {
        lib: &'a str,
        lib_symbol_name: Option<&'a str>,
        flags: Flag,
    },
    /// If the flags is `EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER`, then following the flags are two `Uleb128`s: the stub offset and the resolver offset. The stub is used by non-lazy pointers.  The resolver is used by lazy pointers and must be called to get the actual address to use
    Stub {
        stub_offset: scroll::Uleb128,
        resolver_offset: scroll::Uleb128,
        flags: Flag,
    },
}

impl<'a> ExportInfo<'a> {
    /// Parse out the export info from `bytes`, at `offset`
    pub fn parse(bytes: &'a [u8], libs: &[&'a str], flags: Flag, mut offset: usize) -> error::Result<ExportInfo<'a>> {
        use self::ExportInfo::*;
        let regular = |offset| -> error::Result<ExportInfo> {
            let address = bytes.pread::<Uleb128>(offset)?;
            Ok(Regular {
                address: address.into(),
                flags:   flags
            })
        };
        let reexport = |mut offset| -> error::Result<ExportInfo> {
            let lib_ordinal: u64 = {
                let tmp = bytes.pread::<Uleb128>(offset)?;
                offset += tmp.size();
                tmp.into()
            };
            let lib_symbol_name = bytes.pread::<&str>(offset)?;
            let lib = libs[lib_ordinal as usize];
            let lib_symbol_name = if lib_symbol_name == "" { None } else { Some (lib_symbol_name)};
            Ok(Reexport {
                lib: lib,
                lib_symbol_name: lib_symbol_name,
                flags: flags
            })
        };
        match SymbolKind::new(flags) {
            SymbolKind::Regular => {
                if flags & EXPORT_SYMBOL_FLAGS_REEXPORT != 0 {
                    reexport(offset)
                } else if flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER != 0 { // 0x10
                    let stub_offset = bytes.pread::<Uleb128>(offset)?;
                    offset += stub_offset.size();
                    let resolver_offset = bytes.pread::<Uleb128>(offset)?;
                    Ok(Stub {
                        stub_offset: stub_offset,
                        resolver_offset: resolver_offset,
                        flags: flags
                    })
                    // else if (flags = kEXPORT_SYMBOL_FLAGS_WEAK_DEFINITION) then (*0x40 unused*)
                } else {
                    regular(offset)
                }
            },
            SymbolKind::ThreadLocal | SymbolKind::Absolute => {
                if flags & EXPORT_SYMBOL_FLAGS_REEXPORT != 0 {
                    reexport(offset)
                } else {
                    regular(offset)
                }
            },
            SymbolKind::UnknownSymbolKind(_kind) => {
                // 0x5f causes errors, but parsing as regular symbol resolves...
                //Err(error::Error::Malformed(format!("Unknown kind {:#x} from flags {:#x} in get_symbol_type at offset {}", kind, flags, offset)))
                regular(offset)
            }
        }
    }
}

#[derive(Debug)]
/// A finalized symbolic export reconstructed from the export trie
pub struct Export<'a> {
    /// The reconsituted export name which dyld matches against
    pub name: String,
    /// The export info in the node data
    pub info: ExportInfo<'a>,
    /// How large this export is
    pub size: usize,
    /// The offset this symbol export is found in the binary
    pub offset: u64,
}

impl<'a> Export<'a> {
    /// Create a new export from `name` and `info`
    pub fn new<'b> (name: String, info: ExportInfo<'b>) -> Export<'b> {
        let offset = match info {
            ExportInfo::Regular { address, .. } => address,
            _ => 0x0,
        };
        Export { name: name, info: info, size: 0, offset: offset }
    }
}

/// An export trie efficiently encodes all of the symbols exported by this binary for dynamic linking
pub struct ExportTrie<'a> {
    data: &'a [u8],
    location: Range<usize>,
}

impl<'a> ExportTrie<'a> {

    #[inline]
    fn walk_nodes<'b>(&'b self, libs: &[&'b str], branches: Vec<(String, usize)>, acc: &mut Vec<Export<'b>>) -> error::Result<()> {
        for (symbol, next_node) in branches {
            self.walk_trie(libs, symbol, next_node, acc)?;
        }
        Ok(())
    }

    // current_symbol can be a str iiuc
    fn walk_branches<'b>(&'b self, nbranches: usize, current_symbol: String, mut offset: usize) -> error::Result<Vec<(String, usize)>> {
        let mut branches = Vec::with_capacity(nbranches);
        //println!("\t@{:#x}", *offset);
        for _i in 0..nbranches {
            // additional offset calculations are relative to the base we received
            let offset = &mut offset;
            let string = self.data.pread::<&str>(*offset)?;
            let mut key = current_symbol.clone();
            key.push_str(string);
            // +1 for null terminator
            *offset = *offset + string.len() + 1;
            //println!("\t({}) string_len: {} offset: {:#x}", i, string.len(), *offset);
            // value is relative to export trie base
            let next_node = Uleb128::read(&self.data, offset)? as usize + self.location.start;
            //println!("\t({}) string: {} next_node: {:#x}", _i, key, next_node);
            branches.push((key, next_node));
        }
        Ok(branches)
    }

    fn walk_trie<'b>(&'b self, libs: &[&'b str], current_symbol: String, start: usize, exports: &mut Vec<Export<'b>>) -> error::Result<()> {
        if start < self.location.end {
            let offset = &mut start.clone();
            let terminal_size = Uleb128::read(&self.data, offset)?;
            // let mut input = String::new();
            // ::std::io::stdin().read_line(&mut input).unwrap();
            // println!("@ {:#x} node: {:#x} current_symbol: {}", start, terminal_size, current_symbol);
            if terminal_size == 0 {
                let nbranches = Uleb128::read(&self.data, offset)? as usize;
                //println!("\t@ {:#x} BRAN {}", *offset, nbranches);
                let branches = self.walk_branches(nbranches, current_symbol, *offset)?;
                self.walk_nodes(libs, branches, exports)
            } else { // terminal node, but the tricky part is that they can have children...
                let pos = *offset;
                let children_start = &mut (pos + terminal_size as usize);
                let nchildren = Uleb128::read(&self.data, children_start)? as usize;
                let flags = Uleb128::read(&self.data, offset)?;
                //println!("\t@ {:#x} TERM {} flags: {:#x}", *offset, nchildren, flags);
                let info = ExportInfo::parse(&self.data, libs, flags, *offset)?;
                let export = Export::new(current_symbol.clone(), info);
                //println!("\t{:?}", &export);
                exports.push(export);
                if nchildren == 0 {
                    // this branch is done
                    Ok(())
                } else {
                    // more branches to walk
                    let branches = self.walk_branches(nchildren, current_symbol, *children_start)?;
                    self.walk_nodes(libs, branches, exports)
                }
            }
        } else { Ok(()) }
    }

    /// Walk the export trie for symbols exported by this binary, using the provided `libs` to resolve re-exports
    pub fn exports<'b>(&'b self, libs: &[&'b str]) -> error::Result<Vec<Export<'b>>> {
        let offset = self.location.start.clone();
        let current_symbol = String::new();
        let mut exports = Vec::new();
        self.walk_trie(libs, current_symbol, offset, &mut exports)?;
        Ok(exports)
    }

    /// Create a new, lazy, zero-copy export trie from the `DyldInfo` `command`
    pub fn new(bytes: &'a [u8], command: &load_command::DyldInfoCommand) -> Self {
        let start = command.export_off as usize;
        let end = (command.export_size + command.export_off) as usize;
        ExportTrie {
            data: bytes.as_ref(),
            location: start..end,
        }
    }
}

impl<'a> Debug for ExportTrie<'a> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        writeln!(fmt, "ExportTrie {{")?;
        writeln!(fmt, "  Location: {:#x}..{:#x}", self.location.start, self.location.end)?;
        writeln!(fmt, "}}")
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn export_trie () {
        const EXPORTS: [u8; 64] = [0x00,0x01,0x5f,0x00,0x05,0x00,0x02,0x5f,0x6d,0x68,0x5f,0x65,0x78,0x65,0x63,0x75,0x74,0x65,0x5f,0x68,0x65,0x61,0x64,0x65,0x72,0x00,0x1f,0x6d,0x61,0x00,0x23,0x02,0x00,0x00,0x00,0x00,0x02,0x78,0x69,0x6d,0x75,0x6d,0x00,0x30,0x69,0x6e,0x00,0x35,0x03,0x00,0xc0,0x1e,0x00,0x03,0x00,0xd0,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00];
        let exports = &EXPORTS[..];
        let libs = vec!["/usr/lib/libderp.so", "/usr/lib/libthuglife.so"];
        let mut command = load_command::DyldInfoCommand::default();
        command.export_size = exports.len() as u32;
        let trie = ExportTrie::new(&exports, &command);
        println!("trie: {:#?}", &trie);
        let exports = trie.exports(&libs).unwrap();
        println!("len: {} exports: {:#?}", exports.len(), &exports);
        assert_eq!(exports.len() as usize, 3usize)
    }
}
<commit_msg>mach.exports: add explicit lifetime annotation because 1.15 -> 1.16 has some undocumented lifetime elision fix :facepunch:<commit_after>//! Symbols exported by this binary and available for dynamic linking are encoded in mach-o binaries using a special trie
//!
//! **Note**: the trie is constructed lazily in case it won't be used, and since computing exports will require allocation, to compute the exports, you need call the export trie's [exports()](struct.ExportTrie.html#method.exports) method.

// TODO:
// (1) Weak of regular_symbol_info type probably needs to be added ?
// (3) /usr/lib/libstdc++.6.0.9.dylib has flag 0xc at many offsets... they're weak

use core::ops::Range;
use scroll::{self, Pread, Uleb128};
use error;
use core::fmt::{self, Debug};
use mach::load_command;

type Flag = u64;

 // "The following are used on the flags byte of a terminal node
 // in the export information."
pub const EXPORT_SYMBOL_FLAGS_KIND_MASK         : Flag = 0x03;
pub const EXPORT_SYMBOL_FLAGS_KIND_REGULAR      : Flag = 0x00;
pub const EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE     : Flag = 0x02; // this is a symbol not present in the loader.h but only in the dyld compressed image loader source code, and only available with a #def macro for export flags but libobjc. def has this
pub const EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL : Flag = 0x01;
pub const EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION   : Flag = 0x04;
pub const EXPORT_SYMBOL_FLAGS_REEXPORT          : Flag = 0x08;
pub const EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER : Flag = 0x10;

#[derive(Debug)]
pub enum SymbolKind {
    Regular,
    Absolute,
    ThreadLocal,
    UnknownSymbolKind(Flag),
}

impl SymbolKind {
    pub fn new(kind: Flag) -> SymbolKind {
        match kind & EXPORT_SYMBOL_FLAGS_KIND_MASK {
            0x00 => SymbolKind::Regular,
            0x01 => SymbolKind::ThreadLocal,
            0x02 => SymbolKind::Absolute,
            _    => SymbolKind::UnknownSymbolKind(kind),
        }
    }
    pub fn to_str(kind: SymbolKind) -> &'static str {
        match kind {
            SymbolKind::Regular => "Regular",
            SymbolKind::Absolute => "Absolute",
            SymbolKind::ThreadLocal => "Thread_LOCAL",
            SymbolKind::UnknownSymbolKind(_k) => "Unknown",
        }
    }
}

#[derive(Debug)]
/// An export can be a regular export, a re-export, or a stub
pub enum ExportInfo<'a> {
    /// A regular exported symbol, which is an address where it is found, and the flags associated with it
    Regular {
        address: u64,
        flags: Flag,
    },
    /// if lib_symbol_name None then same symbol name, otherwise reexport of lib_symbol_name with name in the trie
    /// "If the string is zero length, then the symbol is re-export from the specified dylib with the same name"
    Reexport {
        lib: &'a str,
        lib_symbol_name: Option<&'a str>,
        flags: Flag,
    },
    /// If the flags is `EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER`, then following the flags are two `Uleb128`s: the stub offset and the resolver offset. The stub is used by non-lazy pointers.  The resolver is used by lazy pointers and must be called to get the actual address to use
    Stub {
        stub_offset: scroll::Uleb128,
        resolver_offset: scroll::Uleb128,
        flags: Flag,
    },
}

impl<'a> ExportInfo<'a> {
    /// Parse out the export info from `bytes`, at `offset`
    pub fn parse(bytes: &'a [u8], libs: &[&'a str], flags: Flag, mut offset: usize) -> error::Result<ExportInfo<'a>> {
        use self::ExportInfo::*;
        let regular = |offset| -> error::Result<ExportInfo> {
            let address = bytes.pread::<Uleb128>(offset)?;
            Ok(Regular {
                address: address.into(),
                flags:   flags
            })
        };
        let reexport = |mut offset| -> error::Result<ExportInfo<'a>> {
            let lib_ordinal: u64 = {
                let tmp = bytes.pread::<Uleb128>(offset)?;
                offset += tmp.size();
                tmp.into()
            };
            let lib_symbol_name = bytes.pread::<&str>(offset)?;
            let lib = libs[lib_ordinal as usize];
            let lib_symbol_name = if lib_symbol_name == "" { None } else { Some (lib_symbol_name)};
            Ok(Reexport {
                lib: lib,
                lib_symbol_name: lib_symbol_name,
                flags: flags
            })
        };
        match SymbolKind::new(flags) {
            SymbolKind::Regular => {
                if flags & EXPORT_SYMBOL_FLAGS_REEXPORT != 0 {
                    reexport(offset)
                } else if flags & EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER != 0 { // 0x10
                    let stub_offset = bytes.pread::<Uleb128>(offset)?;
                    offset += stub_offset.size();
                    let resolver_offset = bytes.pread::<Uleb128>(offset)?;
                    Ok(Stub {
                        stub_offset: stub_offset,
                        resolver_offset: resolver_offset,
                        flags: flags
                    })
                    // else if (flags = kEXPORT_SYMBOL_FLAGS_WEAK_DEFINITION) then (*0x40 unused*)
                } else {
                    regular(offset)
                }
            },
            SymbolKind::ThreadLocal | SymbolKind::Absolute => {
                if flags & EXPORT_SYMBOL_FLAGS_REEXPORT != 0 {
                    reexport(offset)
                } else {
                    regular(offset)
                }
            },
            SymbolKind::UnknownSymbolKind(_kind) => {
                // 0x5f causes errors, but parsing as regular symbol resolves...
                //Err(error::Error::Malformed(format!("Unknown kind {:#x} from flags {:#x} in get_symbol_type at offset {}", kind, flags, offset)))
                regular(offset)
            }
        }
    }
}

#[derive(Debug)]
/// A finalized symbolic export reconstructed from the export trie
pub struct Export<'a> {
    /// The reconsituted export name which dyld matches against
    pub name: String,
    /// The export info in the node data
    pub info: ExportInfo<'a>,
    /// How large this export is
    pub size: usize,
    /// The offset this symbol export is found in the binary
    pub offset: u64,
}

impl<'a> Export<'a> {
    /// Create a new export from `name` and `info`
    pub fn new<'b> (name: String, info: ExportInfo<'b>) -> Export<'b> {
        let offset = match info {
            ExportInfo::Regular { address, .. } => address,
            _ => 0x0,
        };
        Export { name: name, info: info, size: 0, offset: offset }
    }
}

/// An export trie efficiently encodes all of the symbols exported by this binary for dynamic linking
pub struct ExportTrie<'a> {
    data: &'a [u8],
    location: Range<usize>,
}

impl<'a> ExportTrie<'a> {

    #[inline]
    fn walk_nodes<'b>(&'b self, libs: &[&'b str], branches: Vec<(String, usize)>, acc: &mut Vec<Export<'b>>) -> error::Result<()> {
        for (symbol, next_node) in branches {
            self.walk_trie(libs, symbol, next_node, acc)?;
        }
        Ok(())
    }

    // current_symbol can be a str iiuc
    fn walk_branches<'b>(&'b self, nbranches: usize, current_symbol: String, mut offset: usize) -> error::Result<Vec<(String, usize)>> {
        let mut branches = Vec::with_capacity(nbranches);
        //println!("\t@{:#x}", *offset);
        for _i in 0..nbranches {
            // additional offset calculations are relative to the base we received
            let offset = &mut offset;
            let string = self.data.pread::<&str>(*offset)?;
            let mut key = current_symbol.clone();
            key.push_str(string);
            // +1 for null terminator
            *offset = *offset + string.len() + 1;
            //println!("\t({}) string_len: {} offset: {:#x}", i, string.len(), *offset);
            // value is relative to export trie base
            let next_node = Uleb128::read(&self.data, offset)? as usize + self.location.start;
            //println!("\t({}) string: {} next_node: {:#x}", _i, key, next_node);
            branches.push((key, next_node));
        }
        Ok(branches)
    }

    fn walk_trie<'b>(&'b self, libs: &[&'b str], current_symbol: String, start: usize, exports: &mut Vec<Export<'b>>) -> error::Result<()> {
        if start < self.location.end {
            let offset = &mut start.clone();
            let terminal_size = Uleb128::read(&self.data, offset)?;
            // let mut input = String::new();
            // ::std::io::stdin().read_line(&mut input).unwrap();
            // println!("@ {:#x} node: {:#x} current_symbol: {}", start, terminal_size, current_symbol);
            if terminal_size == 0 {
                let nbranches = Uleb128::read(&self.data, offset)? as usize;
                //println!("\t@ {:#x} BRAN {}", *offset, nbranches);
                let branches = self.walk_branches(nbranches, current_symbol, *offset)?;
                self.walk_nodes(libs, branches, exports)
            } else { // terminal node, but the tricky part is that they can have children...
                let pos = *offset;
                let children_start = &mut (pos + terminal_size as usize);
                let nchildren = Uleb128::read(&self.data, children_start)? as usize;
                let flags = Uleb128::read(&self.data, offset)?;
                //println!("\t@ {:#x} TERM {} flags: {:#x}", *offset, nchildren, flags);
                let info = ExportInfo::parse(&self.data, libs, flags, *offset)?;
                let export = Export::new(current_symbol.clone(), info);
                //println!("\t{:?}", &export);
                exports.push(export);
                if nchildren == 0 {
                    // this branch is done
                    Ok(())
                } else {
                    // more branches to walk
                    let branches = self.walk_branches(nchildren, current_symbol, *children_start)?;
                    self.walk_nodes(libs, branches, exports)
                }
            }
        } else { Ok(()) }
    }

    /// Walk the export trie for symbols exported by this binary, using the provided `libs` to resolve re-exports
    pub fn exports<'b>(&'b self, libs: &[&'b str]) -> error::Result<Vec<Export<'b>>> {
        let offset = self.location.start.clone();
        let current_symbol = String::new();
        let mut exports = Vec::new();
        self.walk_trie(libs, current_symbol, offset, &mut exports)?;
        Ok(exports)
    }

    /// Create a new, lazy, zero-copy export trie from the `DyldInfo` `command`
    pub fn new(bytes: &'a [u8], command: &load_command::DyldInfoCommand) -> Self {
        let start = command.export_off as usize;
        let end = (command.export_size + command.export_off) as usize;
        ExportTrie {
            data: bytes.as_ref(),
            location: start..end,
        }
    }
}

impl<'a> Debug for ExportTrie<'a> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        writeln!(fmt, "ExportTrie {{")?;
        writeln!(fmt, "  Location: {:#x}..{:#x}", self.location.start, self.location.end)?;
        writeln!(fmt, "}}")
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn export_trie () {
        const EXPORTS: [u8; 64] = [0x00,0x01,0x5f,0x00,0x05,0x00,0x02,0x5f,0x6d,0x68,0x5f,0x65,0x78,0x65,0x63,0x75,0x74,0x65,0x5f,0x68,0x65,0x61,0x64,0x65,0x72,0x00,0x1f,0x6d,0x61,0x00,0x23,0x02,0x00,0x00,0x00,0x00,0x02,0x78,0x69,0x6d,0x75,0x6d,0x00,0x30,0x69,0x6e,0x00,0x35,0x03,0x00,0xc0,0x1e,0x00,0x03,0x00,0xd0,0x1e,0x00,0x00,0x00,0x00,0x00,0x00,0x00];
        let exports = &EXPORTS[..];
        let libs = vec!["/usr/lib/libderp.so", "/usr/lib/libthuglife.so"];
        let mut command = load_command::DyldInfoCommand::default();
        command.export_size = exports.len() as u32;
        let trie = ExportTrie::new(&exports, &command);
        println!("trie: {:#?}", &trie);
        let exports = trie.exports(&libs).unwrap();
        println!("len: {} exports: {:#?}", exports.len(), &exports);
        assert_eq!(exports.len() as usize, 3usize)
    }
}
<|endoftext|>


<commit_before>//! Dynamically linked symbolic imports

// table of tuples:
// <seg-index, seg-offset, type, symbol-library-ordinal, symbol-name, addend>
// symbol flags are undocumented

use core::ops::Range;
use core::fmt::{self, Debug};
use scroll::{Sleb128, Uleb128, Pread};

use container;
use error;
use mach::load_command;
use mach::bind_opcodes;
use mach::segment;

#[derive(Debug)]
/// Import binding information generated by running the Finite State Automaton programmed via `bind_opcodes`
struct BindInformation<'a> {
  seg_index:              u8,
  seg_offset:             u64,
  bind_type:              u8,
  symbol_library_ordinal: u8,
  symbol_name:            &'a str,
  symbol_flags:           u8,
  addend:                 i64,
  special_dylib:          u8, // seeing self = 0 assuming this means the symbol is imported from itself, because its... libSystem.B.dylib?
}

impl<'a> BindInformation<'a> {
    pub fn new (is_lazy: bool) -> Self {
        let mut bind_info = BindInformation::default();
        let bind_type = if is_lazy { bind_opcodes::BIND_TYPE_POINTER } else { 0x0 };
        bind_info.bind_type = bind_type;
        bind_info
    }
    pub fn is_weak(&self) -> bool {
        self.symbol_flags & bind_opcodes::BIND_SYMBOL_FLAGS_WEAK_IMPORT != 0
    }
}

impl<'a> Default for BindInformation<'a> {
    fn default() -> Self {
        BindInformation {
            seg_index:     0,
            seg_offset:    0x0,
            bind_type:     0x0,
            special_dylib: 1,
            symbol_library_ordinal: 0,
            symbol_name: "",
            symbol_flags: 0,
            addend: 0
        }
    }
}

#[derive(Debug)]
/// An dynamically linked symbolic import
pub struct Import<'a> {
    /// The symbol name dyld uses to resolve this import
    pub name:    &'a str,
    /// The library this symbol belongs to (thanks to two-level namespaces)
    pub dylib:   &'a str,
    ///  Whether the symbol is lazily resolved or not
    pub is_lazy: bool,
    /// The offset in the binary this import is found
    pub offset:  u64,
    /// The size of this import
    pub size:    usize,
    /// The virtual memory address at which this import is found
    pub address: u64,
    /// The addend of this import
    pub addend:  i64,
    /// Whether this import is weak
    pub is_weak: bool,
}

impl<'a> Import<'a> {
    /// Create a new import from the import binding information in `bi`
    fn new(bi: &BindInformation<'a>, libs: &[&'a str], segments: &[segment::Segment]) -> Import<'a> {
        let (offset, address) = {
            let segment = &segments[bi.seg_index as usize];
            (
                segment.fileoff + bi.seg_offset,
                segment.vmaddr + bi.seg_offset
            )
        };
        let size = if bi.is_lazy() { 8 } else { 0 };
        Import {
            name: bi.symbol_name,
            dylib: libs[bi.symbol_library_ordinal as usize],
            is_lazy: bi.is_lazy(),
            offset: offset,
            size: size,
            address: address,
            addend: bi.addend,
            is_weak: bi.is_weak()
        }
    }
}

/// An interpreter for mach BIND opcodes.
/// Runs on prebound (non lazy) symbols (usually dylib extern consts and extern variables),
/// and lazy symbols (usually dylib functions)
pub struct BindInterpreter<'a> {
    data: &'a [u8],
    location: Range<usize>,
    lazy_location: Range<usize>,
}

impl<'a> Debug for BindInterpreter<'a> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        writeln!(fmt, "BindInterpreter {{")?;
        writeln!(fmt, "  Location: {:#x}..{:#x}", self.location.start, self.location.end)?;
        writeln!(fmt, "  Lazy Location: {:#x}..{:#x}", self.lazy_location.start, self.lazy_location.end)?;
        writeln!(fmt, "}}")
    }
}

impl<'a> BindInterpreter<'a> {
    /// Construct a new import binding interpreter from `bytes` and the load `command`
    pub fn new(bytes: &'a [u8], command: &load_command::DyldInfoCommand) -> Self {
        let get_pos = |off: u32, size: u32| -> Range<usize> {
            off as usize..(off + size) as usize
        };
        let location = get_pos(command.bind_off, command.bind_size);
        let lazy_location = get_pos(command.lazy_bind_off, command.lazy_bind_size);
        BindInterpreter {
            data: bytes.as_ref(),
            location: location,
            lazy_location: lazy_location,
        }
    }
    /// Return the imports in this binary
    pub fn imports(&self, libs: &[&'a str], segments: &[segment::Segment], ctx: &container::Ctx) -> error::Result<Vec<Import<'a>>>{
        let mut imports = Vec::new();
        self.run(false, libs, segments, ctx, &mut imports)?;
        self.run( true, libs, segments, ctx, &mut imports)?;
        Ok(imports)
    }
    fn run(&self, is_lazy: bool, libs: &[&'a str], segments: &[segment::Segment], ctx: &container::Ctx, imports: &mut Vec<Import<'a>>) -> error::Result<()>{
        use mach::bind_opcodes::*;
        let location = if is_lazy {
            &self.location
        } else {
            &self.lazy_location
        };
        let mut bind_info = BindInformation::new(is_lazy);
        let mut offset = &mut location.start.clone();
        while *offset < location.end {
            let opcode = self.data.gread::<i8>(offset)? as bind_opcodes::Opcode;
            // let mut input = String::new();
            // ::std::io::stdin().read_line(&mut input).unwrap();
            // println!("opcode: {} ({:#x}) offset: {:#x}\n {:?}", opcode_to_str(opcode & BIND_OPCODE_MASK), opcode, *offset - location.start - 1, &bind_info);
            match opcode & BIND_OPCODE_MASK {
                // we do nothing, don't update our records, and add a new, fresh record
                BIND_OPCODE_DONE => {
                    bind_info = BindInformation::new(is_lazy);
                },
                BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {
	            let symbol_library_ordinal = opcode & BIND_IMMEDIATE_MASK;
	            bind_info.symbol_library_ordinal = symbol_library_ordinal;
                },
                BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
	            let symbol_library_ordinal = Uleb128::read(&self.data, offset)?;
	            bind_info.symbol_library_ordinal = symbol_library_ordinal as u8;
                },
                BIND_OPCODE_SET_DYLIB_SPECIAL_IMM => {
                    // dyld puts the immediate into the symbol_library_ordinal field...
                    let special_dylib = opcode & BIND_IMMEDIATE_MASK;
                    // Printf.printf "special_dylib: 0x%x\n" special_dylib
                    bind_info.special_dylib = special_dylib;
                },
                BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
	            let symbol_flags = opcode & BIND_IMMEDIATE_MASK;
	            let symbol_name = self.data.pread::<&str>(*offset)?;
                    *offset = *offset + symbol_name.len() + 1; // second time this \0 caused debug woes
	            bind_info.symbol_name = symbol_name;
                    bind_info.symbol_flags = symbol_flags;
                },
                BIND_OPCODE_SET_TYPE_IMM => {
	            let bind_type = opcode & BIND_IMMEDIATE_MASK;
	            bind_info.bind_type = bind_type;
                },
                BIND_OPCODE_SET_ADDEND_SLEB => {
                    let addend = Sleb128::read(&self.data, offset)?;
                    bind_info.addend = addend;
                },
                BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
	            let seg_index = opcode & BIND_IMMEDIATE_MASK;
                    // dyld sets the address to the segActualLoadAddress(segIndex) + uleb128
                    // address = segActualLoadAddress(segmentIndex) + read_uleb128(p, end);
	            let seg_offset = Uleb128::read(&self.data, offset)?;
	            bind_info.seg_index = seg_index;
                    bind_info.seg_offset = seg_offset;
                },
                BIND_OPCODE_ADD_ADDR_ULEB => {
	            let addr = Uleb128::read(&self.data, offset)?;
	            let seg_offset = bind_info.seg_offset.wrapping_add(addr);
	            bind_info.seg_offset = seg_offset;
                },
                // record the record by placing its value into our list
                BIND_OPCODE_DO_BIND => {
                    // from dyld:
                    //      if ( address >= segmentEndAddress )
	            // throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
	            // (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last);
	            // address += sizeof(intptr_t);
                    imports.push(Import::new(&bind_info, libs, segments));
                    let seg_offset = bind_info.seg_offset.wrapping_add(ctx.size() as u64);
                    bind_info.seg_offset = seg_offset;
                },
                BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB => {
                    // dyld:
	            // if ( address >= segmentEndAddress )
	            // throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
	            // (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last);
	            // address += read_uleb128(p, end) + sizeof(intptr_t);
                    // we bind the old record, then increment bind info address for the next guy, plus the ptr offset *)
                    imports.push(Import::new(&bind_info, libs, segments));
                    let addr = Uleb128::read(&self.data, offset)?;
                    let seg_offset = bind_info.seg_offset.wrapping_add(addr).wrapping_add(ctx.size() as u64);
                    bind_info.seg_offset = seg_offset;
                },
                BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED => {
                    // dyld:
                    // if ( address >= segmentEndAddress )
	            // throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
	            // (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last);
	            // address += immediate*sizeof(intptr_t) + sizeof(intptr_t);
	            // break;
                    // similarly, we bind the old record, then perform address manipulation for the next record
                    imports.push(Import::new(&bind_info, libs, segments));
	            let scale = opcode & BIND_IMMEDIATE_MASK;
                    let size = ctx.size() as u64;
                    let seg_offset = bind_info.seg_offset.wrapping_add(scale as u64 * size).wrapping_add(size);
                    bind_info.seg_offset = seg_offset;
                },
                BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB => {
                    // dyld:
                    // count = read_uleb128(p, end);
	            // skip = read_uleb128(p, end);
	            // for (uint32_t i=0; i < count; ++i) {
	            // if ( address >= segmentEndAddress )
	            // throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
	            // (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last);
	            // address += skip + sizeof(intptr_t);
	            // }
	            // break;
                    let count = Uleb128::read(&self.data, offset)?;
                    let skip =  Uleb128::read(&self.data, offset)?;
                    let skip_plus_size = skip + ctx.size() as u64;
                    for _i  in 0..count {
                        imports.push(Import::new(&bind_info, libs, segments));
                        let seg_offset = bind_info.seg_offset.wrapping_add(skip_plus_size);
                        bind_info.seg_offset = seg_offset;
                    }
                },
                _ => {
                }
            }
        }
        Ok(())
    }
}
<commit_msg>Fix (?) lazy imports<commit_after>//! Dynamically linked symbolic imports

// table of tuples:
// <seg-index, seg-offset, type, symbol-library-ordinal, symbol-name, addend>
// symbol flags are undocumented

use core::ops::Range;
use core::fmt::{self, Debug};
use scroll::{Sleb128, Uleb128, Pread};

use container;
use error;
use mach::load_command;
use mach::bind_opcodes;
use mach::segment;

#[derive(Debug)]
/// Import binding information generated by running the Finite State Automaton programmed via `bind_opcodes`
struct BindInformation<'a> {
  seg_index:              u8,
  seg_offset:             u64,
  bind_type:              u8,
  symbol_library_ordinal: u8,
  symbol_name:            &'a str,
  symbol_flags:           u8,
  addend:                 i64,
  special_dylib:          u8, // seeing self = 0 assuming this means the symbol is imported from itself, because its... libSystem.B.dylib?
  is_lazy:                bool,
}

impl<'a> BindInformation<'a> {
    pub fn new (is_lazy: bool) -> Self {
        let mut bind_info = BindInformation::default();
        if is_lazy {
            bind_info.is_lazy = true;
            bind_info.bind_type = bind_opcodes::BIND_TYPE_POINTER;
        }
        bind_info
    }
    pub fn is_weak(&self) -> bool {
        self.symbol_flags & bind_opcodes::BIND_SYMBOL_FLAGS_WEAK_IMPORT != 0
    }
}

impl<'a> Default for BindInformation<'a> {
    fn default() -> Self {
        BindInformation {
            seg_index:     0,
            seg_offset:    0x0,
            bind_type:     0x0,
            special_dylib: 1,
            symbol_library_ordinal: 0,
            symbol_name: "",
            symbol_flags: 0,
            addend: 0,
            is_lazy: false
        }
    }
}

#[derive(Debug)]
/// An dynamically linked symbolic import
pub struct Import<'a> {
    /// The symbol name dyld uses to resolve this import
    pub name:    &'a str,
    /// The library this symbol belongs to (thanks to two-level namespaces)
    pub dylib:   &'a str,
    ///  Whether the symbol is lazily resolved or not
    pub is_lazy: bool,
    /// The offset in the binary this import is found
    pub offset:  u64,
    /// The size of this import
    pub size:    usize,
    /// The virtual memory address at which this import is found
    pub address: u64,
    /// The addend of this import
    pub addend:  i64,
    /// Whether this import is weak
    pub is_weak: bool,
}

impl<'a> Import<'a> {
    /// Create a new import from the import binding information in `bi`
    fn new(bi: &BindInformation<'a>, libs: &[&'a str], segments: &[segment::Segment]) -> Import<'a> {
        let (offset, address) = {
            let segment = &segments[bi.seg_index as usize];
            (
                segment.fileoff + bi.seg_offset,
                segment.vmaddr + bi.seg_offset
            )
        };
        let size = if bi.is_lazy { 8 } else { 0 };
        Import {
            name: bi.symbol_name,
            dylib: libs[bi.symbol_library_ordinal as usize],
            is_lazy: bi.is_lazy,
            offset: offset,
            size: size,
            address: address,
            addend: bi.addend,
            is_weak: bi.is_weak()
        }
    }
}

/// An interpreter for mach BIND opcodes.
/// Runs on prebound (non lazy) symbols (usually dylib extern consts and extern variables),
/// and lazy symbols (usually dylib functions)
pub struct BindInterpreter<'a> {
    data: &'a [u8],
    location: Range<usize>,
    lazy_location: Range<usize>,
}

impl<'a> Debug for BindInterpreter<'a> {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        writeln!(fmt, "BindInterpreter {{")?;
        writeln!(fmt, "  Location: {:#x}..{:#x}", self.location.start, self.location.end)?;
        writeln!(fmt, "  Lazy Location: {:#x}..{:#x}", self.lazy_location.start, self.lazy_location.end)?;
        writeln!(fmt, "}}")
    }
}

impl<'a> BindInterpreter<'a> {
    /// Construct a new import binding interpreter from `bytes` and the load `command`
    pub fn new(bytes: &'a [u8], command: &load_command::DyldInfoCommand) -> Self {
        let get_pos = |off: u32, size: u32| -> Range<usize> {
            off as usize..(off + size) as usize
        };
        let location = get_pos(command.bind_off, command.bind_size);
        let lazy_location = get_pos(command.lazy_bind_off, command.lazy_bind_size);
        BindInterpreter {
            data: bytes.as_ref(),
            location: location,
            lazy_location: lazy_location,
        }
    }
    /// Return the imports in this binary
    pub fn imports(&self, libs: &[&'a str], segments: &[segment::Segment], ctx: &container::Ctx) -> error::Result<Vec<Import<'a>>>{
        let mut imports = Vec::new();
        self.run(false, libs, segments, ctx, &mut imports)?;
        self.run( true, libs, segments, ctx, &mut imports)?;
        Ok(imports)
    }
    fn run(&self, is_lazy: bool, libs: &[&'a str], segments: &[segment::Segment], ctx: &container::Ctx, imports: &mut Vec<Import<'a>>) -> error::Result<()>{
        use mach::bind_opcodes::*;
        let location = if is_lazy {
            &self.lazy_location
        } else {
            &self.location
        };
        let mut bind_info = BindInformation::new(is_lazy);
        let mut offset = &mut location.start.clone();
        while *offset < location.end {
            let opcode = self.data.gread::<i8>(offset)? as bind_opcodes::Opcode;
            // let mut input = String::new();
            // ::std::io::stdin().read_line(&mut input).unwrap();
            // println!("opcode: {} ({:#x}) offset: {:#x}\n {:?}", opcode_to_str(opcode & BIND_OPCODE_MASK), opcode, *offset - location.start - 1, &bind_info);
            match opcode & BIND_OPCODE_MASK {
                // we do nothing, don't update our records, and add a new, fresh record
                BIND_OPCODE_DONE => {
                    bind_info = BindInformation::new(is_lazy);
                },
                BIND_OPCODE_SET_DYLIB_ORDINAL_IMM => {
	            let symbol_library_ordinal = opcode & BIND_IMMEDIATE_MASK;
	            bind_info.symbol_library_ordinal = symbol_library_ordinal;
                },
                BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB => {
	            let symbol_library_ordinal = Uleb128::read(&self.data, offset)?;
	            bind_info.symbol_library_ordinal = symbol_library_ordinal as u8;
                },
                BIND_OPCODE_SET_DYLIB_SPECIAL_IMM => {
                    // dyld puts the immediate into the symbol_library_ordinal field...
                    let special_dylib = opcode & BIND_IMMEDIATE_MASK;
                    // Printf.printf "special_dylib: 0x%x\n" special_dylib
                    bind_info.special_dylib = special_dylib;
                },
                BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM => {
	            let symbol_flags = opcode & BIND_IMMEDIATE_MASK;
	            let symbol_name = self.data.pread::<&str>(*offset)?;
                    *offset = *offset + symbol_name.len() + 1; // second time this \0 caused debug woes
	            bind_info.symbol_name = symbol_name;
                    bind_info.symbol_flags = symbol_flags;
                },
                BIND_OPCODE_SET_TYPE_IMM => {
	            let bind_type = opcode & BIND_IMMEDIATE_MASK;
	            bind_info.bind_type = bind_type;
                },
                BIND_OPCODE_SET_ADDEND_SLEB => {
                    let addend = Sleb128::read(&self.data, offset)?;
                    bind_info.addend = addend;
                },
                BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB => {
	            let seg_index = opcode & BIND_IMMEDIATE_MASK;
                    // dyld sets the address to the segActualLoadAddress(segIndex) + uleb128
                    // address = segActualLoadAddress(segmentIndex) + read_uleb128(p, end);
	            let seg_offset = Uleb128::read(&self.data, offset)?;
	            bind_info.seg_index = seg_index;
                    bind_info.seg_offset = seg_offset;
                },
                BIND_OPCODE_ADD_ADDR_ULEB => {
	            let addr = Uleb128::read(&self.data, offset)?;
	            let seg_offset = bind_info.seg_offset.wrapping_add(addr);
	            bind_info.seg_offset = seg_offset;
                },
                // record the record by placing its value into our list
                BIND_OPCODE_DO_BIND => {
                    // from dyld:
                    //      if ( address >= segmentEndAddress )
	            // throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
	            // (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last);
	            // address += sizeof(intptr_t);
                    imports.push(Import::new(&bind_info, libs, segments));
                    let seg_offset = bind_info.seg_offset.wrapping_add(ctx.size() as u64);
                    bind_info.seg_offset = seg_offset;
                },
                BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB => {
                    // dyld:
	            // if ( address >= segmentEndAddress )
	            // throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
	            // (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last);
	            // address += read_uleb128(p, end) + sizeof(intptr_t);
                    // we bind the old record, then increment bind info address for the next guy, plus the ptr offset *)
                    imports.push(Import::new(&bind_info, libs, segments));
                    let addr = Uleb128::read(&self.data, offset)?;
                    let seg_offset = bind_info.seg_offset.wrapping_add(addr).wrapping_add(ctx.size() as u64);
                    bind_info.seg_offset = seg_offset;
                },
                BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED => {
                    // dyld:
                    // if ( address >= segmentEndAddress )
	            // throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
	            // (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last);
	            // address += immediate*sizeof(intptr_t) + sizeof(intptr_t);
	            // break;
                    // similarly, we bind the old record, then perform address manipulation for the next record
                    imports.push(Import::new(&bind_info, libs, segments));
	            let scale = opcode & BIND_IMMEDIATE_MASK;
                    let size = ctx.size() as u64;
                    let seg_offset = bind_info.seg_offset.wrapping_add(scale as u64 * size).wrapping_add(size);
                    bind_info.seg_offset = seg_offset;
                },
                BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB => {
                    // dyld:
                    // count = read_uleb128(p, end);
	            // skip = read_uleb128(p, end);
	            // for (uint32_t i=0; i < count; ++i) {
	            // if ( address >= segmentEndAddress )
	            // throwBadBindingAddress(address, segmentEndAddress, segmentIndex, start, end, p);
	            // (this->*handler)(context, address, type, symbolName, symboFlags, addend, libraryOrdinal, "", &last);
	            // address += skip + sizeof(intptr_t);
	            // }
	            // break;
                    let count = Uleb128::read(&self.data, offset)?;
                    let skip =  Uleb128::read(&self.data, offset)?;
                    let skip_plus_size = skip + ctx.size() as u64;
                    for _i  in 0..count {
                        imports.push(Import::new(&bind_info, libs, segments));
                        let seg_offset = bind_info.seg_offset.wrapping_add(skip_plus_size);
                        bind_info.seg_offset = seg_offset;
                    }
                },
                _ => {
                }
            }
        }
        Ok(())
    }
}
<|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/. */

//! CSS block layout.

use layout::box::{RenderBox};
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
use layout::display_list_builder::{FlowDisplayListBuilderMethods};
use layout::flow::{BlockFlow, FlowContext, FlowData, InlineBlockFlow};
use layout::inline::InlineLayout;
use layout::model::{MaybeAuto, Specified, Auto};

use core::cell::Cell;
use geom::point::Point2D;
use geom::rect::Rect;
use gfx::display_list::DisplayList;
use gfx::geometry::Au;
use gfx::geometry;
use servo_util::tree::{TreeNodeRef, TreeUtils};

pub struct BlockFlowData {
    /// Data common to all flows.
    common: FlowData,

    /// The associated render box.
    box: Option<RenderBox>,

    /// Whether this block flow is the root flow.
    is_root: bool
}

impl BlockFlowData {
    pub fn new(common: FlowData) -> BlockFlowData {
        BlockFlowData {
            common: common,
            box: None,
            is_root: false
        }
    }

    pub fn new_root(common: FlowData) -> BlockFlowData {
        BlockFlowData {
            common: common,
            box: None,
            is_root: true
        }
    }

    pub fn teardown(&mut self) {
        self.common.teardown();
        for self.box.each |box| {
            box.teardown();
        }
        self.box = None;
    }
}

pub trait BlockLayout {
    fn starts_root_flow(&self) -> bool;
    fn starts_block_flow(&self) -> bool;
}

impl BlockLayout for FlowContext {
    fn starts_root_flow(&self) -> bool {
        match *self {
            BlockFlow(info) => info.is_root,
            _ => false
        }
    }

    fn starts_block_flow(&self) -> bool {
        match *self {
            BlockFlow(*) | InlineBlockFlow(*) => true,
            _ => false
        }
    }
}

impl BlockFlowData {
    /* Recursively (bottom-up) determine the context's preferred and
    minimum widths.  When called on this context, all child contexts
    have had their min/pref widths set. This function must decide
    min/pref widths based on child context widths and dimensions of
    any boxes it is responsible for flowing.  */

    /* TODO: floats */
    /* TODO: absolute contexts */
    /* TODO: inline-blocks */
    pub fn bubble_widths_block(@mut self, ctx: &LayoutContext) {
        let mut min_width = Au(0);
        let mut pref_width = Au(0);

        /* find max width from child block contexts */
        for BlockFlow(self).each_child |child_ctx| {
            assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow());

            do child_ctx.with_base |child_node| {
                min_width = geometry::max(min_width, child_node.min_width);
                pref_width = geometry::max(pref_width, child_node.pref_width);
            }
        }

        /* if not an anonymous block context, add in block box's widths.
           these widths will not include child elements, just padding etc. */
        self.box.map(|&box| {
            //Can compute border width here since it doesn't depend on anything
            let style = box.style();
            do box.with_model |model| {
                model.compute_borders(style)
            }
            min_width = min_width.add(&box.get_min_width(ctx));
            pref_width = pref_width.add(&box.get_pref_width(ctx));
        });

        self.common.min_width = min_width;
        self.common.pref_width = pref_width;
    }

    /// Computes left and right margins and width based on CSS 2.1 secion 10.3.3.
    /// Requires borders and padding to already be computed
    priv fn compute_horiz( &self,
                            width: MaybeAuto,
                            left_margin: MaybeAuto,
                            right_margin: MaybeAuto,
                            available_width: Au) -> (Au, Au, Au) {

        //If width is not 'auto', and width + margins > available_width, all 'auto' margins are treated as '0'
        let (left_margin, right_margin) = match width{
            Auto => (left_margin, right_margin),
            Specified(width) => {
                let left = left_margin.spec_or_default(Au(0));
                let right = right_margin.spec_or_default(Au(0));

                if((left + right + width) > available_width) {
                    (Specified(left), Specified(right))
                } else {
                    (left_margin, right_margin)
                }
            }
        };

        //Invariant: left_margin_Au + width_Au + right_margin_Au == available_width
        let (left_margin_Au, width_Au, right_margin_Au) = match (left_margin, width, right_margin) {
            //If all have a computed value other than 'auto', the system is over-constrained and we need to discard a margin.
            //if direction is ltr, ignore the specified right margin and solve for it. If it is rtl, ignore the specified
            //left margin. FIXME(eatkinson): this assumes the direction is ltr
            (Specified(margin_l), Specified(width), Specified(_margin_r)) => (margin_l, width, available_width - (margin_l + width )),

            //If exactly one value is 'auto', solve for it
            (Auto, Specified(width), Specified(margin_r)) => (available_width - (width + margin_r), width, margin_r),
            (Specified(margin_l), Auto, Specified(margin_r)) => (margin_l, available_width - (margin_l + margin_r), margin_r),
            (Specified(margin_l), Specified(width), Auto) => (margin_l, width, available_width - (margin_l + width)),

            //If width is set to 'auto', any other 'auto' value becomes '0', and width is solved for
            (Auto, Auto, Specified(margin_r)) => (Au(0), available_width - margin_r, margin_r),
            (Specified(margin_l), Auto, Auto) => (margin_l, available_width - margin_l, Au(0)),
            (Auto, Auto, Auto) => (Au(0), available_width, Au(0)),

            //If left and right margins are auto, they become equal
            (Auto, Specified(width), Auto) => {
                let margin = (available_width - width).scale_by(0.5);
                (margin, width, margin)
            }

        };
        //return values in same order as params
        (width_Au, left_margin_Au, right_margin_Au)
    }

    /// Recursively (top-down) determines the actual width of child contexts and boxes. When called
    /// on this context, the context has had its width set by the parent context.
    ///
    /// Dual boxes consume some width first, and the remainder is assigned to all child (block)
    /// contexts.
    pub fn assign_widths_block(@mut self, ctx: &LayoutContext) {
        debug!("assign_widths_block: assigning width for flow %?",  self.common.id);
        if self.is_root {
            debug!("Setting root position");
            self.common.position.origin = Au::zero_point();
            self.common.position.size.width = ctx.screen_size.size.width;
        }

        //position was set to the containing block by the flow's parent
        let mut remaining_width = self.common.position.size.width;
        let mut x_offset = Au(0);

        for self.box.each |&box| {
            let style = box.style();
            do box.with_model |model| {
                // Can compute padding here since we know containing block width.
                model.compute_padding(style, remaining_width);

                // Margins are 0 right now so model.noncontent_width() is just borders + padding.
                let available_width = remaining_width - model.noncontent_width();

                // Top and bottom margins for blocks are 0 if auto.
                let margin_top = MaybeAuto::from_margin(style.margin_top(),
                                                        remaining_width).spec_or_default(Au(0));
                let margin_bottom = MaybeAuto::from_margin(style.margin_bottom(),
                                                           remaining_width).spec_or_default(Au(0));

                let (width, margin_left, margin_right) =
                    (MaybeAuto::from_width(style.width(), remaining_width),
                     MaybeAuto::from_margin(style.margin_left(), remaining_width),
                     MaybeAuto::from_margin(style.margin_right(), remaining_width));

                // FIXME(pcwalton): We discard the width here. Is that correct?
                let (_, margin_left, margin_right) = self.compute_horiz(width,
                                                                        margin_left,
                                                                        margin_right,
                                                                        available_width);

                model.margin.top = margin_top;
                model.margin.right = margin_right;
                model.margin.bottom = margin_bottom;
                model.margin.left = margin_left;

                x_offset = model.offset();
                remaining_width = width;
            }

            do box.with_mut_base |base| {
                //The associated box is the border box of this flow
                base.position.origin.x = base.model.margin.left;

                let pb = base.model.padding.left + base.model.padding.right +
                    base.model.border.left + base.model.border.right;
                base.position.size.width = remaining_width + pb;
            }
        }

        for BlockFlow(self).each_child |kid| {
            assert!(kid.starts_block_flow() || kid.starts_inline_flow());

            do kid.with_mut_base |child_node| {
                child_node.position.origin.x = x_offset;
                child_node.position.size.width = remaining_width;
            }
        }
    }

    pub fn assign_height_block(@mut self, ctx: &LayoutContext) {
        let mut cur_y = Au(0);
        let mut top_offset = Au(0);

        for self.box.each |&box| {
            do box.with_model |model| {
                top_offset = model.margin.top + model.border.top + model.padding.top;
                cur_y += top_offset;
            }
        }

        for BlockFlow(self).each_child |kid| {
            do kid.with_mut_base |child_node| {
                child_node.position.origin.y = cur_y;
                cur_y += child_node.position.size.height;
            }
        }

        let height = if self.is_root {
            Au::max(ctx.screen_size.size.height, cur_y)
        } else {
            cur_y - top_offset
        };

        let mut pb = Au(0);
        self.box.map(|&box| {
            do box.with_mut_base |base| {
                //The associated box is the border box of this flow
                base.position.origin.y = base.model.margin.top;

                pb = base.model.padding.top + base.model.padding.bottom +
                    base.model.border.top + base.model.border.bottom;
                base.position.size.height = height + pb;
            }
        });

        //TODO(eatkinson): compute heights using the 'height' property.
        self.common.position.size.height = height + pb;

    }

    pub fn build_display_list_block<E:ExtraDisplayListData>(@mut self,
                                                            builder: &DisplayListBuilder,
                                                            dirty: &Rect<Au>,
                                                            offset: &Point2D<Au>,
                                                            list: &Cell<DisplayList<E>>) {
        // add box that starts block context
        self.box.map(|&box| {
            box.build_display_list(builder, dirty, offset, list)
        });

        // TODO: handle any out-of-flow elements

        // go deeper into the flow tree
        let flow = BlockFlow(self);
        for flow.each_child |child| {
            flow.build_display_list_for_child(builder, child, dirty, offset, list)
        }
    }
}

<commit_msg>Vertical margins now contribute to content height.<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/. */

//! CSS block layout.

use layout::box::{RenderBox};
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
use layout::display_list_builder::{FlowDisplayListBuilderMethods};
use layout::flow::{BlockFlow, FlowContext, FlowData, InlineBlockFlow};
use layout::inline::InlineLayout;
use layout::model::{MaybeAuto, Specified, Auto};

use core::cell::Cell;
use geom::point::Point2D;
use geom::rect::Rect;
use gfx::display_list::DisplayList;
use gfx::geometry::Au;
use gfx::geometry;
use servo_util::tree::{TreeNodeRef, TreeUtils};

pub struct BlockFlowData {
    /// Data common to all flows.
    common: FlowData,

    /// The associated render box.
    box: Option<RenderBox>,

    /// Whether this block flow is the root flow.
    is_root: bool
}

impl BlockFlowData {
    pub fn new(common: FlowData) -> BlockFlowData {
        BlockFlowData {
            common: common,
            box: None,
            is_root: false
        }
    }

    pub fn new_root(common: FlowData) -> BlockFlowData {
        BlockFlowData {
            common: common,
            box: None,
            is_root: true
        }
    }

    pub fn teardown(&mut self) {
        self.common.teardown();
        for self.box.each |box| {
            box.teardown();
        }
        self.box = None;
    }
}

pub trait BlockLayout {
    fn starts_root_flow(&self) -> bool;
    fn starts_block_flow(&self) -> bool;
}

impl BlockLayout for FlowContext {
    fn starts_root_flow(&self) -> bool {
        match *self {
            BlockFlow(info) => info.is_root,
            _ => false
        }
    }

    fn starts_block_flow(&self) -> bool {
        match *self {
            BlockFlow(*) | InlineBlockFlow(*) => true,
            _ => false
        }
    }
}

impl BlockFlowData {
    /* Recursively (bottom-up) determine the context's preferred and
    minimum widths.  When called on this context, all child contexts
    have had their min/pref widths set. This function must decide
    min/pref widths based on child context widths and dimensions of
    any boxes it is responsible for flowing.  */

    /* TODO: floats */
    /* TODO: absolute contexts */
    /* TODO: inline-blocks */
    pub fn bubble_widths_block(@mut self, ctx: &LayoutContext) {
        let mut min_width = Au(0);
        let mut pref_width = Au(0);

        /* find max width from child block contexts */
        for BlockFlow(self).each_child |child_ctx| {
            assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow());

            do child_ctx.with_base |child_node| {
                min_width = geometry::max(min_width, child_node.min_width);
                pref_width = geometry::max(pref_width, child_node.pref_width);
            }
        }

        /* if not an anonymous block context, add in block box's widths.
           these widths will not include child elements, just padding etc. */
        self.box.map(|&box| {
            //Can compute border width here since it doesn't depend on anything
            let style = box.style();
            do box.with_model |model| {
                model.compute_borders(style)
            }
            min_width = min_width.add(&box.get_min_width(ctx));
            pref_width = pref_width.add(&box.get_pref_width(ctx));
        });

        self.common.min_width = min_width;
        self.common.pref_width = pref_width;
    }

    /// Computes left and right margins and width based on CSS 2.1 secion 10.3.3.
    /// Requires borders and padding to already be computed
    priv fn compute_horiz( &self,
                            width: MaybeAuto,
                            left_margin: MaybeAuto,
                            right_margin: MaybeAuto,
                            available_width: Au) -> (Au, Au, Au) {

        //If width is not 'auto', and width + margins > available_width, all 'auto' margins are treated as '0'
        let (left_margin, right_margin) = match width{
            Auto => (left_margin, right_margin),
            Specified(width) => {
                let left = left_margin.spec_or_default(Au(0));
                let right = right_margin.spec_or_default(Au(0));

                if((left + right + width) > available_width) {
                    (Specified(left), Specified(right))
                } else {
                    (left_margin, right_margin)
                }
            }
        };

        //Invariant: left_margin_Au + width_Au + right_margin_Au == available_width
        let (left_margin_Au, width_Au, right_margin_Au) = match (left_margin, width, right_margin) {
            //If all have a computed value other than 'auto', the system is over-constrained and we need to discard a margin.
            //if direction is ltr, ignore the specified right margin and solve for it. If it is rtl, ignore the specified
            //left margin. FIXME(eatkinson): this assumes the direction is ltr
            (Specified(margin_l), Specified(width), Specified(_margin_r)) => (margin_l, width, available_width - (margin_l + width )),

            //If exactly one value is 'auto', solve for it
            (Auto, Specified(width), Specified(margin_r)) => (available_width - (width + margin_r), width, margin_r),
            (Specified(margin_l), Auto, Specified(margin_r)) => (margin_l, available_width - (margin_l + margin_r), margin_r),
            (Specified(margin_l), Specified(width), Auto) => (margin_l, width, available_width - (margin_l + width)),

            //If width is set to 'auto', any other 'auto' value becomes '0', and width is solved for
            (Auto, Auto, Specified(margin_r)) => (Au(0), available_width - margin_r, margin_r),
            (Specified(margin_l), Auto, Auto) => (margin_l, available_width - margin_l, Au(0)),
            (Auto, Auto, Auto) => (Au(0), available_width, Au(0)),

            //If left and right margins are auto, they become equal
            (Auto, Specified(width), Auto) => {
                let margin = (available_width - width).scale_by(0.5);
                (margin, width, margin)
            }

        };
        //return values in same order as params
        (width_Au, left_margin_Au, right_margin_Au)
    }

    /// Recursively (top-down) determines the actual width of child contexts and boxes. When called
    /// on this context, the context has had its width set by the parent context.
    ///
    /// Dual boxes consume some width first, and the remainder is assigned to all child (block)
    /// contexts.
    pub fn assign_widths_block(@mut self, ctx: &LayoutContext) {
        debug!("assign_widths_block: assigning width for flow %?",  self.common.id);
        if self.is_root {
            debug!("Setting root position");
            self.common.position.origin = Au::zero_point();
            self.common.position.size.width = ctx.screen_size.size.width;
        }

        //position was set to the containing block by the flow's parent
        let mut remaining_width = self.common.position.size.width;
        let mut x_offset = Au(0);

        for self.box.each |&box| {
            let style = box.style();
            do box.with_model |model| {
                // Can compute padding here since we know containing block width.
                model.compute_padding(style, remaining_width);

                // Margins are 0 right now so model.noncontent_width() is just borders + padding.
                let available_width = remaining_width - model.noncontent_width();

                // Top and bottom margins for blocks are 0 if auto.
                let margin_top = MaybeAuto::from_margin(style.margin_top(),
                                                        remaining_width).spec_or_default(Au(0));
                let margin_bottom = MaybeAuto::from_margin(style.margin_bottom(),
                                                           remaining_width).spec_or_default(Au(0));

                let (width, margin_left, margin_right) =
                    (MaybeAuto::from_width(style.width(), remaining_width),
                     MaybeAuto::from_margin(style.margin_left(), remaining_width),
                     MaybeAuto::from_margin(style.margin_right(), remaining_width));

                // FIXME(pcwalton): We discard the width here. Is that correct?
                let (_, margin_left, margin_right) = self.compute_horiz(width,
                                                                        margin_left,
                                                                        margin_right,
                                                                        available_width);

                model.margin.top = margin_top;
                model.margin.right = margin_right;
                model.margin.bottom = margin_bottom;
                model.margin.left = margin_left;

                x_offset = model.offset();
                remaining_width = width;
            }

            do box.with_mut_base |base| {
                //The associated box is the border box of this flow
                base.position.origin.x = base.model.margin.left;

                let pb = base.model.padding.left + base.model.padding.right +
                    base.model.border.left + base.model.border.right;
                base.position.size.width = remaining_width + pb;
            }
        }

        for BlockFlow(self).each_child |kid| {
            assert!(kid.starts_block_flow() || kid.starts_inline_flow());

            do kid.with_mut_base |child_node| {
                child_node.position.origin.x = x_offset;
                child_node.position.size.width = remaining_width;
            }
        }
    }

    pub fn assign_height_block(@mut self, ctx: &LayoutContext) {
        let mut cur_y = Au(0);
        let mut top_offset = Au(0);

        for self.box.each |&box| {
            do box.with_model |model| {
                top_offset = model.margin.top + model.border.top + model.padding.top;
                cur_y += top_offset;
            }
        }

        for BlockFlow(self).each_child |kid| {
            do kid.with_mut_base |child_node| {
                child_node.position.origin.y = cur_y;
                cur_y += child_node.position.size.height;
            }
        }

        let height = if self.is_root {
            Au::max(ctx.screen_size.size.height, cur_y)
        } else {
            cur_y - top_offset
        };

        let mut noncontent_height = Au(0);
        self.box.map(|&box| {
            do box.with_mut_base |base| {
                //The associated box is the border box of this flow
                base.position.origin.y = base.model.margin.top;

                noncontent_height = base.model.padding.top + base.model.padding.bottom +
                    base.model.border.top + base.model.border.bottom;
                base.position.size.height = height + noncontent_height;

                noncontent_height += base.model.margin.top + base.model.margin.bottom;
            }
        });

        //TODO(eatkinson): compute heights using the 'height' property.
        self.common.position.size.height = height + noncontent_height;

    }

    pub fn build_display_list_block<E:ExtraDisplayListData>(@mut self,
                                                            builder: &DisplayListBuilder,
                                                            dirty: &Rect<Au>,
                                                            offset: &Point2D<Au>,
                                                            list: &Cell<DisplayList<E>>) {
        // add box that starts block context
        self.box.map(|&box| {
            box.build_display_list(builder, dirty, offset, list)
        });

        // TODO: handle any out-of-flow elements

        // go deeper into the flow tree
        let flow = BlockFlow(self);
        for flow.each_child |child| {
            flow.build_display_list_for_child(builder, child, dirty, offset, list)
        }
    }
}

<|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 layout::box::{RenderBox};
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
use layout::flow::{FloatFlow, FlowData};
use layout::model::{MaybeAuto};
use layout::float_context::{FloatContext, PlacementInfo, FloatType};

use std::cell::Cell;
use geom::point::Point2D;
use geom::rect::Rect;
use gfx::display_list::DisplayList;
use gfx::geometry::Au;
use gfx::geometry;
use servo_util::tree::{TreeNodeRef, TreeUtils};

pub struct FloatFlowData {
    /// Data common to all flows.
    common: FlowData,

    /// The associated render box.
    box: Option<RenderBox>,

    containing_width: Au,

    /// Offset relative to where the parent tried to position this flow
    rel_pos: Point2D<Au>,

    /// Left or right?
    float_type: FloatType,

    /// Index into the box list for inline floats
    index: Option<uint>,

}

impl FloatFlowData {
    pub fn new(common: FlowData, float_type: FloatType) -> FloatFlowData {
        FloatFlowData {
            common: common,
            containing_width: Au(0),
            box: None,
            index: None,
            float_type: float_type,
            rel_pos: Point2D(Au(0), Au(0)),
        }
    }

    pub fn teardown(&mut self) {
        self.common.teardown();
        for self.box.iter().advance |box| {
            box.teardown();
        }
        self.box = None;
        self.index = None;
    }
}

impl FloatFlowData {
    pub fn bubble_widths_float(@mut self, ctx: &LayoutContext) {
        let mut min_width = Au(0);
        let mut pref_width = Au(0);

        self.common.num_floats = 1;

        for FloatFlow(self).each_child |child_ctx| {
            //assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow());

            do child_ctx.with_mut_base |child_node| {
                min_width = geometry::max(min_width, child_node.min_width);
                pref_width = geometry::max(pref_width, child_node.pref_width);
                child_node.floats_in = FloatContext::new(child_node.num_floats);
            }
        }

        self.box.map(|&box| {
            let style = box.style();
            do box.with_model |model| {
                model.compute_borders(style)
            }

            min_width = min_width.add(&box.get_min_width(ctx));
            pref_width = pref_width.add(&box.get_pref_width(ctx));
        });

        self.common.min_width = min_width;
        self.common.pref_width = pref_width;
    }

    pub fn assign_widths_float(@mut self, _: &LayoutContext) {
        debug!("assign_widths_float: assigning width for flow %?",  self.common.id);
        // position.size.width is set by parent even though we don't know
        // position.origin yet.
        let mut remaining_width = self.common.position.size.width;
        self.containing_width = remaining_width;
        let mut x_offset = Au(0);

        for self.box.iter().advance |&box| {
            let style = box.style();
            do box.with_model |model| {
                // Can compute padding here since we know containing block width.
                model.compute_padding(style, remaining_width);

                // Margins for floats are 0 if auto.
                let margin_top = MaybeAuto::from_margin(style.margin_top(),
                                                        remaining_width,
                                                        style.font_size()).specified_or_zero();
                let margin_bottom = MaybeAuto::from_margin(style.margin_bottom(),
                                                           remaining_width,
                                                           style.font_size()).specified_or_zero();
                let margin_left = MaybeAuto::from_margin(style.margin_left(),
                                                         remaining_width,
                                                         style.font_size()).specified_or_zero();
                let margin_right = MaybeAuto::from_margin(style.margin_right(),
                                                          remaining_width,
                                                          style.font_size()).specified_or_zero();

                let shrink_to_fit = geometry::min(self.common.pref_width,
                                                  geometry::max(self.common.min_width,
                                                                remaining_width));

                let width = MaybeAuto::from_width(style.width(),
                                                  remaining_width,
                                                  style.font_size()).specified_or_default(shrink_to_fit);
                debug!("assign_widths_float -- width: %?", width);

                model.margin.top = margin_top;
                model.margin.right = margin_right;
                model.margin.bottom = margin_bottom;
                model.margin.left = margin_left;

                x_offset = model.offset();
                remaining_width = width;
            }

            do box.with_mut_base |base| {
                //The associated box is the border box of this flow
                base.position.origin.x = base.model.margin.left;

                let pb = base.model.padding.left + base.model.padding.right +
                    base.model.border.left + base.model.border.right;
                base.position.size.width = remaining_width + pb;
            }
        }

        self.common.position.size.width = remaining_width;

        for FloatFlow(self).each_child |kid| {
            //assert!(kid.starts_block_flow() || kid.starts_inline_flow());

            do kid.with_mut_base |child_node| {
                child_node.position.origin.x = x_offset;
                child_node.position.size.width = remaining_width;
            }
        }
    }

    pub fn assign_height_float(@mut self, ctx: &mut LayoutContext) {
        for FloatFlow(self).each_child |kid| {
            kid.assign_height(ctx);
        }

        let mut cur_y = Au(0);
        let mut top_offset = Au(0);

        for self.box.iter().advance |&box| {
            do box.with_model |model| {
                top_offset = model.margin.top + model.border.top + model.padding.top;
                cur_y = cur_y + top_offset;
            }
        }

        for FloatFlow(self).each_child |kid| {
            do kid.with_mut_base |child_node| {
                child_node.position.origin.y = cur_y;
                cur_y = cur_y + child_node.position.size.height;
            };
        }

        let mut height = cur_y - top_offset;

        let mut noncontent_width = Au(0);
        let mut noncontent_height = Au(0);
        let mut full_noncontent_width = Au(0);
        let mut full_noncontent_height = Au(0);
        self.box.map(|&box| {
            do box.with_mut_base |base| {
                //The associated box is the border box of this flow
                base.position.origin.y = base.model.margin.top;

                noncontent_width = base.model.padding.left + base.model.padding.right +
                    base.model.border.left + base.model.border.right;
                noncontent_height = base.model.padding.top + base.model.padding.bottom +
                    base.model.border.top + base.model.border.bottom;
                base.position.size.height = height + noncontent_height;

                full_noncontent_width = noncontent_width + base.model.margin.left + base.model.margin.right;
                full_noncontent_height = noncontent_height + base.model.margin.top + base.model.margin.bottom;
            }
        });

        //TODO(eatkinson): compute heights properly using the 'height' property.
        for self.box.iter().advance |&box| {
            let height_prop =
                MaybeAuto::from_height(box.style().height(),
                                       Au(0),
                                       box.style().font_size()).specified_or_zero();

            height = geometry::max(height, height_prop) + noncontent_height;
            debug!("assign_height_float -- height: %?", height);
            do box.with_mut_base |base| {
                base.position.size.height = height;
            }
        }

        let info = PlacementInfo {
            width: self.common.position.size.width + full_noncontent_width,
            height: height + full_noncontent_height,
            ceiling: Au(0),
            max_width: self.containing_width,
            f_type: self.float_type,
        };

        // Place the float and return the FloatContext back to the parent flow.
        // After, grab the position and use that to set our position.
        self.common.floats_out = self.common.floats_in.add_float(&info);
        self.rel_pos = self.common.floats_out.last_float_pos();
    }

    pub fn build_display_list_float<E:ExtraDisplayListData>(@mut self,
                                                            builder: &DisplayListBuilder,
                                                            dirty: &Rect<Au>,
                                                            list: &Cell<DisplayList<E>>)
                                                            -> bool {

        let abs_rect = Rect(self.common.abs_position, self.common.position.size);
        if !abs_rect.intersects(dirty) {
            return false;
        }

        let offset = self.common.abs_position + self.rel_pos;
        // add box that starts block context
        self.box.map(|&box| {
            box.build_display_list(builder, dirty, &offset, list)
        });

        // TODO: handle any out-of-flow elements

        // go deeper into the flow tree
        let flow = FloatFlow(self);
        for flow.each_child |child| {
            do child.with_mut_base |base| {
                base.abs_position = offset + base.position.origin;
            }
        }

        true
    }
}

<commit_msg>Fix the handling of flow_contexts in floats.<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 layout::box::{RenderBox};
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
use layout::flow::{FloatFlow, FlowData};
use layout::model::{MaybeAuto};
use layout::float_context::{FloatContext, PlacementInfo, FloatType};

use std::cell::Cell;
use geom::point::Point2D;
use geom::rect::Rect;
use gfx::display_list::DisplayList;
use gfx::geometry::Au;
use gfx::geometry;
use servo_util::tree::{TreeNodeRef, TreeUtils};

pub struct FloatFlowData {
    /// Data common to all flows.
    common: FlowData,

    /// The associated render box.
    box: Option<RenderBox>,

    containing_width: Au,

    /// Offset relative to where the parent tried to position this flow
    rel_pos: Point2D<Au>,

    /// Left or right?
    float_type: FloatType,

    /// Index into the box list for inline floats
    index: Option<uint>,

}

impl FloatFlowData {
    pub fn new(common: FlowData, float_type: FloatType) -> FloatFlowData {
        FloatFlowData {
            common: common,
            containing_width: Au(0),
            box: None,
            index: None,
            float_type: float_type,
            rel_pos: Point2D(Au(0), Au(0)),
        }
    }

    pub fn teardown(&mut self) {
        self.common.teardown();
        for self.box.iter().advance |box| {
            box.teardown();
        }
        self.box = None;
        self.index = None;
    }
}

impl FloatFlowData {
    pub fn bubble_widths_float(@mut self, ctx: &LayoutContext) {
        let mut min_width = Au(0);
        let mut pref_width = Au(0);
        let mut num_floats = 1;

        for FloatFlow(self).each_child |child_ctx| {
            //assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow());

            do child_ctx.with_mut_base |child_node| {
                min_width = geometry::max(min_width, child_node.min_width);
                pref_width = geometry::max(pref_width, child_node.pref_width);

                num_floats = num_floats + child_node.num_floats;
            }
        }

        self.common.num_floats = num_floats;

        self.box.map(|&box| {
            let style = box.style();
            do box.with_model |model| {
                model.compute_borders(style)
            }

            min_width = min_width.add(&box.get_min_width(ctx));
            pref_width = pref_width.add(&box.get_pref_width(ctx));
        });

        self.common.min_width = min_width;
        self.common.pref_width = pref_width;
    }

    pub fn assign_widths_float(@mut self, _: &LayoutContext) {
        debug!("assign_widths_float: assigning width for flow %?",  self.common.id);
        // position.size.width is set by parent even though we don't know
        // position.origin yet.
        let mut remaining_width = self.common.position.size.width;
        self.containing_width = remaining_width;
        let mut x_offset = Au(0);

        for self.box.iter().advance |&box| {
            let style = box.style();
            do box.with_model |model| {
                // Can compute padding here since we know containing block width.
                model.compute_padding(style, remaining_width);

                // Margins for floats are 0 if auto.
                let margin_top = MaybeAuto::from_margin(style.margin_top(),
                                                        remaining_width,
                                                        style.font_size()).specified_or_zero();
                let margin_bottom = MaybeAuto::from_margin(style.margin_bottom(),
                                                           remaining_width,
                                                           style.font_size()).specified_or_zero();
                let margin_left = MaybeAuto::from_margin(style.margin_left(),
                                                         remaining_width,
                                                         style.font_size()).specified_or_zero();
                let margin_right = MaybeAuto::from_margin(style.margin_right(),
                                                          remaining_width,
                                                          style.font_size()).specified_or_zero();

                let shrink_to_fit = geometry::min(self.common.pref_width,
                                                  geometry::max(self.common.min_width,
                                                                remaining_width));

                let width = MaybeAuto::from_width(style.width(),
                                                  remaining_width,
                                                  style.font_size()).specified_or_default(shrink_to_fit);
                debug!("assign_widths_float -- width: %?", width);

                model.margin.top = margin_top;
                model.margin.right = margin_right;
                model.margin.bottom = margin_bottom;
                model.margin.left = margin_left;

                x_offset = model.offset();
                remaining_width = width;
            }

            do box.with_mut_base |base| {
                //The associated box is the border box of this flow
                base.position.origin.x = base.model.margin.left;

                let pb = base.model.padding.left + base.model.padding.right +
                    base.model.border.left + base.model.border.right;
                base.position.size.width = remaining_width + pb;
            }
        }

        self.common.position.size.width = remaining_width;

        for FloatFlow(self).each_child |kid| {
            //assert!(kid.starts_block_flow() || kid.starts_inline_flow());

            do kid.with_mut_base |child_node| {
                child_node.position.origin.x = x_offset;
                child_node.position.size.width = remaining_width;
            }
        }
    }

    pub fn assign_height_float(@mut self, ctx: &mut LayoutContext) {
        let mut float_ctx = FloatContext::new(self.common.num_floats);
        for FloatFlow(self).each_child |kid| {
            do kid.with_mut_base |child_node| {
                child_node.floats_in = float_ctx.clone();
            }
            kid.assign_height(ctx);
            do kid.with_mut_base |child_node| {
                float_ctx = child_node.floats_out.clone();
            }
        }

        let mut cur_y = Au(0);
        let mut top_offset = Au(0);

        for self.box.iter().advance |&box| {
            do box.with_model |model| {
                top_offset = model.margin.top + model.border.top + model.padding.top;
                cur_y = cur_y + top_offset;
            }
        }

        for FloatFlow(self).each_child |kid| {
            do kid.with_mut_base |child_node| {
                child_node.position.origin.y = cur_y;
                cur_y = cur_y + child_node.position.size.height;
            };
        }

        let mut height = cur_y - top_offset;

        let mut noncontent_width = Au(0);
        let mut noncontent_height = Au(0);
        let mut full_noncontent_width = Au(0);
        let mut full_noncontent_height = Au(0);
        self.box.map(|&box| {
            do box.with_mut_base |base| {
                //The associated box is the border box of this flow
                base.position.origin.y = base.model.margin.top;

                noncontent_width = base.model.padding.left + base.model.padding.right +
                    base.model.border.left + base.model.border.right;
                noncontent_height = base.model.padding.top + base.model.padding.bottom +
                    base.model.border.top + base.model.border.bottom;
                base.position.size.height = height + noncontent_height;

                full_noncontent_width = noncontent_width + base.model.margin.left + base.model.margin.right;
                full_noncontent_height = noncontent_height + base.model.margin.top + base.model.margin.bottom;
            }
        });

        //TODO(eatkinson): compute heights properly using the 'height' property.
        for self.box.iter().advance |&box| {
            let height_prop =
                MaybeAuto::from_height(box.style().height(),
                                       Au(0),
                                       box.style().font_size()).specified_or_zero();

            height = geometry::max(height, height_prop) + noncontent_height;
            debug!("assign_height_float -- height: %?", height);
            do box.with_mut_base |base| {
                base.position.size.height = height;
            }
        }

        let info = PlacementInfo {
            width: self.common.position.size.width + full_noncontent_width,
            height: height + full_noncontent_height,
            ceiling: Au(0),
            max_width: self.containing_width,
            f_type: self.float_type,
        };

        // Place the float and return the FloatContext back to the parent flow.
        // After, grab the position and use that to set our position.
        self.common.floats_out = self.common.floats_in.add_float(&info);
        self.rel_pos = self.common.floats_out.last_float_pos();
    }

    pub fn build_display_list_float<E:ExtraDisplayListData>(@mut self,
                                                            builder: &DisplayListBuilder,
                                                            dirty: &Rect<Au>,
                                                            list: &Cell<DisplayList<E>>)
                                                            -> bool {

        let abs_rect = Rect(self.common.abs_position, self.common.position.size);
        if !abs_rect.intersects(dirty) {
            return false;
        }

        let offset = self.common.abs_position + self.rel_pos;
        // add box that starts block context
        self.box.map(|&box| {
            box.build_display_list(builder, dirty, &offset, list)
        });

        // TODO: handle any out-of-flow elements

        // go deeper into the flow tree
        let flow = FloatFlow(self);
        for flow.each_child |child| {
            do child.with_mut_base |base| {
                base.abs_position = offset + base.position.origin;
            }
        }

        true
    }
}

<|endoftext|>


<commit_before>
use std::io::Result;
use self::super::Github;
use rep::Label;
use deployments::Deployments;
use labels::Labels;
use pullrequests::PullRequests;
use rustc_serialize::json;
use issues::{IssueRef, Issues};

pub struct Repository<'a> {
  github: &'a Github<'a>,
  owner: &'static str,
  repo: &'static str
}

impl<'a> Repository<'a> {
  pub fn new(
    github: &'a Github<'a>, owner: &'static str, repo: &'static str) -> Repository<'a> {
    Repository {
      github: github,
      owner: owner,
      repo: repo
    }
  }

  fn path(&self, more: &str) -> String {
    format!("/repos/{}/{}{}", self.owner, self.repo, more)
  }

  /// get a list of labels associated with this repository ref
  pub fn labels(&self) -> Labels {
    Labels::new(self.github, self.owner, self.repo)
  }

  /// get a list of deployments associated with this repository ref
  pub fn deployments(&self) -> Deployments {
    Deployments::new(self.github, self.owner, self.repo)
  }

  /// get a list of pulls associated with this repository ref
  pub fn pulls(&self) -> PullRequests {
    PullRequests::new(self.github, self.owner, self.repo)
  }

  /// get a reference to a specific github issue associated with this repoistory ref
  pub fn issue(&self, number: &'static i64) -> IssueRef {
    IssueRef::new(self.github, self.owner, self.repo, number)
  }

  pub fn issues(&self) -> Issues {
    Issues::new(self.github, self.owner, self.repo)
  }
}
<commit_msg>removed things not in use<commit_after>use self::super::Github;
use deployments::Deployments;
use labels::Labels;
use pullrequests::PullRequests;
use issues::{IssueRef, Issues};

pub struct Repository<'a> {
  github: &'a Github<'a>,
  owner: &'static str,
  repo: &'static str
}

impl<'a> Repository<'a> {
  pub fn new(
    github: &'a Github<'a>, owner: &'static str, repo: &'static str) -> Repository<'a> {
    Repository {
      github: github,
      owner: owner,
      repo: repo
    }
  }

  /// get a list of labels associated with this repository ref
  pub fn labels(&self) -> Labels {
    Labels::new(self.github, self.owner, self.repo)
  }

  /// get a list of deployments associated with this repository ref
  pub fn deployments(&self) -> Deployments {
    Deployments::new(self.github, self.owner, self.repo)
  }

  /// get a list of pulls associated with this repository ref
  pub fn pulls(&self) -> PullRequests {
    PullRequests::new(self.github, self.owner, self.repo)
  }

  /// get a reference to a specific github issue associated with this repoistory ref
  pub fn issue(&self, number: &'static i64) -> IssueRef {
    IssueRef::new(self.github, self.owner, self.repo, number)
  }

  pub fn issues(&self) -> Issues {
    Issues::new(self.github, self.owner, self.repo)
  }
}
<|endoftext|>