<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 app_units::Au;
use font::{FontHandleMethods, FontMetrics, FontTableMethods};
use font::{FontTableTag, FractionalPixel, GPOS, GSUB, KERN};
use freetype::freetype::{FT_Done_Face, FT_New_Memory_Face};
use freetype::freetype::{FT_F26Dot6, FT_Face, FT_FaceRec};
use freetype::freetype::{FT_Get_Char_Index, FT_Get_Postscript_Name};
use freetype::freetype::{FT_Get_Kerning, FT_Get_Sfnt_Table, FT_Load_Sfnt_Table};
use freetype::freetype::{FT_GlyphSlot, FT_Library, FT_Long, FT_ULong};
use freetype::freetype::{FT_Int32, FT_Kerning_Mode, FT_STYLE_FLAG_BOLD, FT_STYLE_FLAG_ITALIC};
use freetype::freetype::{FT_Load_Glyph, FT_Set_Char_Size};
use freetype::freetype::{FT_SizeRec, FT_Size_Metrics, FT_UInt, FT_Vector};
use freetype::freetype::FT_Sfnt_Tag;
use freetype::tt_os2::TT_OS2;
use platform::font_context::FontContextHandle;
use platform::font_template::FontTemplateData;
use std::{mem, ptr};
use std::os::raw::{c_char, c_long};
use std::sync::Arc;
use style::computed_values::font_stretch::T as FontStretch;
use style::computed_values::font_weight::T as FontWeight;
use super::c_str_to_string;
use text::glyph::GlyphId;
use text::util::fixed_to_float;

// This constant is not present in the freetype
// bindings due to bindgen not handling the way
// the macro is defined.
const FT_LOAD_TARGET_LIGHT: FT_Int32 = 1 << 16;

// Default to slight hinting, which is what most
// Linux distros use by default, and is a better
// default than no hinting.
// TODO(gw): Make this configurable.
const GLYPH_LOAD_FLAGS: FT_Int32 = FT_LOAD_TARGET_LIGHT;

fn fixed_to_float_ft(f: i32) -> f64 {
    fixed_to_float(6, f)
}

#[derive(Debug)]
pub struct FontTable {
    buffer: Vec<u8>,
}

impl FontTableMethods for FontTable {
    fn buffer(&self) -> &[u8] {
        &self.buffer
    }
}

#[derive(Debug)]
pub struct FontHandle {
    // The font binary. This must stay valid for the lifetime of the font,
    // if the font is created using FT_Memory_Face.
    font_data: Arc<FontTemplateData>,
    face: FT_Face,
    handle: FontContextHandle,
    can_do_fast_shaping: bool,
}

impl Drop for FontHandle {
    fn drop(&mut self) {
        assert!(!self.face.is_null());
        unsafe {
            if !FT_Done_Face(self.face).succeeded() {
                panic!("FT_Done_Face failed");
            }
        }
    }
}

impl FontHandleMethods for FontHandle {
    fn new_from_template(fctx: &FontContextHandle,
                       template: Arc<FontTemplateData>,
                       pt_size: Option<Au>)
                        -> Result<FontHandle, ()> {
        let ft_ctx: FT_Library = fctx.ctx.ctx;
        if ft_ctx.is_null() { return Err(()); }

        return create_face_from_buffer(ft_ctx, &template.bytes, pt_size).map(|face| {
            let mut handle = FontHandle {
                  face: face,
                  font_data: template.clone(),
                  handle: fctx.clone(),
                  can_do_fast_shaping: false,
            };
            // TODO (#11310): Implement basic support for GPOS and GSUB.
            handle.can_do_fast_shaping = handle.has_table(KERN) &&
                                         !handle.has_table(GPOS) &&
                                         !handle.has_table(GSUB);
            handle
        });

        fn create_face_from_buffer(lib: FT_Library, buffer: &[u8], pt_size: Option<Au>)
                                   -> Result<FT_Face, ()> {
            unsafe {
                let mut face: FT_Face = ptr::null_mut();
                let face_index = 0 as FT_Long;
                let result = FT_New_Memory_Face(lib, buffer.as_ptr(), buffer.len() as FT_Long,
                                                face_index, &mut face);

                if !result.succeeded() || face.is_null() {
                    return Err(());
                }
                if let Some(s) = pt_size {
                    FontHandle::set_char_size(face, s).or(Err(()))?
                }
                Ok(face)
            }
        }
    }
    fn template(&self) -> Arc<FontTemplateData> {
        self.font_data.clone()
    }
    fn family_name(&self) -> String {
        unsafe {
            c_str_to_string((*self.face).family_name as *const c_char)
        }
    }
    fn face_name(&self) -> Option<String> {
        unsafe {
            let name = FT_Get_Postscript_Name(self.face) as *const c_char;

            if !name.is_null() {
                Some(c_str_to_string(name))
            } else {
                None
            }
        }
    }
    fn is_italic(&self) -> bool {
        unsafe { (*self.face).style_flags & FT_STYLE_FLAG_ITALIC as c_long != 0 }
    }
    fn boldness(&self) -> FontWeight {
        let default_weight = FontWeight::normal();
        if unsafe { (*self.face).style_flags & FT_STYLE_FLAG_BOLD as c_long == 0 } {
            default_weight
        } else {
            unsafe {
                let os2 = FT_Get_Sfnt_Table(self.face, FT_Sfnt_Tag::FT_SFNT_OS2) as *mut TT_OS2;
                let valid = !os2.is_null() && (*os2).version != 0xffff;
                if valid {
                    let weight =(*os2).usWeightClass as i32;
                    if weight < 10 {
                        FontWeight::from_int(weight * 100).unwrap()
                    } else if weight >= 100 && weight < 1000 {
                        FontWeight::from_int(weight / 100 * 100).unwrap()
                    } else {
                        default_weight
                    }
                } else {
                    default_weight
                }
            }
        }
    }
    fn stretchiness(&self) -> FontStretch {
        // TODO(pcwalton): Implement this.
        FontStretch::Normal
    }

    fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
        assert!(!self.face.is_null());
        unsafe {
            let idx = FT_Get_Char_Index(self.face, codepoint as FT_ULong);
            if idx != 0 as FT_UInt {
                Some(idx as GlyphId)
            } else {
                debug!("Invalid codepoint: {}", codepoint);
                None
            }
        }
    }

    fn glyph_h_kerning(&self, first_glyph: GlyphId, second_glyph: GlyphId)
                       -> FractionalPixel {
        assert!(!self.face.is_null());
        let mut delta = FT_Vector { x: 0, y: 0 };
        unsafe {
            FT_Get_Kerning(self.face, first_glyph, second_glyph,
                           FT_Kerning_Mode::FT_KERNING_DEFAULT as FT_UInt,
                           &mut delta);
        }
        fixed_to_float_ft(delta.x as i32)
    }

    fn can_do_fast_shaping(&self) -> bool {
        self.can_do_fast_shaping
    }

    fn glyph_h_advance(&self, glyph: GlyphId) -> Option<FractionalPixel> {
        assert!(!self.face.is_null());
        unsafe {
            let res =  FT_Load_Glyph(self.face,
                                     glyph as FT_UInt,
                                     GLYPH_LOAD_FLAGS);
            if res.succeeded() {
                let void_glyph = (*self.face).glyph;
                let slot: FT_GlyphSlot = mem::transmute(void_glyph);
                assert!(!slot.is_null());
                let advance = (*slot).metrics.horiAdvance;
                debug!("h_advance for {} is {}", glyph, advance);
                let advance = advance as i32;
                Some(fixed_to_float_ft(advance) as FractionalPixel)
            } else {
                debug!("Unable to load glyph {}. reason: {:?}", glyph, res);
                None
            }
        }
    }

    fn metrics(&self) -> FontMetrics {
        /* TODO(Issue #76): complete me */
        let face = self.face_rec_mut();

        let underline_size = self.font_units_to_au(face.underline_thickness as f64);
        let underline_offset = self.font_units_to_au(face.underline_position as f64);
        let em_size = self.font_units_to_au(face.units_per_EM as f64);
        let ascent = self.font_units_to_au(face.ascender as f64);
        let descent = self.font_units_to_au(face.descender as f64);
        let max_advance = self.font_units_to_au(face.max_advance_width as f64);

        // 'leading' is supposed to be the vertical distance between two baselines,
        // reflected by the height attribute in freetype.  On OS X (w/ CTFont),
        // leading represents the distance between the bottom of a line descent to
        // the top of the next line's ascent or: (line_height - ascent - descent),
        // see http://stackoverflow.com/a/5635981 for CTFont implementation.
        // Convert using a formula similar to what CTFont returns for consistency.
        let height = self.font_units_to_au(face.height as f64);
        let leading = height - (ascent + descent);

        let mut strikeout_size = Au(0);
        let mut strikeout_offset = Au(0);
        let mut x_height = Au(0);
        unsafe {
            let os2 = FT_Get_Sfnt_Table(face, FT_Sfnt_Tag::FT_SFNT_OS2) as *mut TT_OS2;
            let valid = !os2.is_null() && (*os2).version != 0xffff;
            if valid {
               strikeout_size = self.font_units_to_au((*os2).yStrikeoutSize as f64);
               strikeout_offset = self.font_units_to_au((*os2).yStrikeoutPosition as f64);
               x_height = self.font_units_to_au((*os2).sxHeight as f64);
            }
        }

        let average_advance = self.glyph_index('0')
                                  .and_then(|idx| self.glyph_h_advance(idx))
                                  .map_or(max_advance, |advance| self.font_units_to_au(advance));

        let metrics = FontMetrics {
            underline_size:   underline_size,
            underline_offset: underline_offset,
            strikeout_size:   strikeout_size,
            strikeout_offset: strikeout_offset,
            leading:          leading,
            x_height:         x_height,
            em_size:          em_size,
            ascent:           ascent,
            descent:          -descent, // linux font's seem to use the opposite sign from mac
            max_advance:      max_advance,
            average_advance:  average_advance,
            line_gap:         height,
        };

        debug!("Font metrics (@{}px): {:?}", em_size.to_f32_px(), metrics);
        metrics
    }

    fn table_for_tag(&self, tag: FontTableTag) -> Option<FontTable> {
        let tag = tag as FT_ULong;

        unsafe {
            // Get the length
            let mut len = 0;
            if !FT_Load_Sfnt_Table(self.face, tag, 0, ptr::null_mut(), &mut len).succeeded() {
                return None
            }
            // Get the bytes
            let mut buf = vec![0u8; len as usize];
            if !FT_Load_Sfnt_Table(self.face, tag, 0, buf.as_mut_ptr(), &mut len).succeeded() {
                return None
            }
            Some(FontTable { buffer: buf })
        }
    }
}

impl<'a> FontHandle {
    fn set_char_size(face: FT_Face, pt_size: Au) -> Result<(), ()>{
        let char_size = pt_size.to_f64_px() * 64.0 + 0.5;

        unsafe {
            let result = FT_Set_Char_Size(face, char_size as FT_F26Dot6, 0, 0, 0);
            if result.succeeded() { Ok(()) } else { Err(()) }
        }
    }

    fn has_table(&self, tag: FontTableTag) -> bool {
        unsafe {
            FT_Load_Sfnt_Table(self.face, tag as FT_ULong, 0, ptr::null_mut(), &mut 0).succeeded()
        }
    }

    fn face_rec_mut(&'a self) -> &'a mut FT_FaceRec {
        unsafe {
            &mut (*self.face)
        }
    }

    fn font_units_to_au(&self, value: f64) -> Au {
        let face = self.face_rec_mut();

        // face.size is a *c_void in the bindings, presumably to avoid
        // recursive structural types
        let size: &FT_SizeRec = unsafe { mem::transmute(&(*face.size)) };
        let metrics: &FT_Size_Metrics = &(*size).metrics;

        let em_size = face.units_per_EM as f64;
        let x_scale = (metrics.x_ppem as f64) / em_size as f64;

        // If this isn't true then we're scaling one of the axes wrong
        assert!(metrics.x_ppem == metrics.y_ppem);

        Au::from_f64_px(value * x_scale)
    }
}
<commit_msg>Use specific assertion for gfx platform freetype font<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 app_units::Au;
use font::{FontHandleMethods, FontMetrics, FontTableMethods};
use font::{FontTableTag, FractionalPixel, GPOS, GSUB, KERN};
use freetype::freetype::{FT_Done_Face, FT_New_Memory_Face};
use freetype::freetype::{FT_F26Dot6, FT_Face, FT_FaceRec};
use freetype::freetype::{FT_Get_Char_Index, FT_Get_Postscript_Name};
use freetype::freetype::{FT_Get_Kerning, FT_Get_Sfnt_Table, FT_Load_Sfnt_Table};
use freetype::freetype::{FT_GlyphSlot, FT_Library, FT_Long, FT_ULong};
use freetype::freetype::{FT_Int32, FT_Kerning_Mode, FT_STYLE_FLAG_BOLD, FT_STYLE_FLAG_ITALIC};
use freetype::freetype::{FT_Load_Glyph, FT_Set_Char_Size};
use freetype::freetype::{FT_SizeRec, FT_Size_Metrics, FT_UInt, FT_Vector};
use freetype::freetype::FT_Sfnt_Tag;
use freetype::tt_os2::TT_OS2;
use platform::font_context::FontContextHandle;
use platform::font_template::FontTemplateData;
use std::{mem, ptr};
use std::os::raw::{c_char, c_long};
use std::sync::Arc;
use style::computed_values::font_stretch::T as FontStretch;
use style::computed_values::font_weight::T as FontWeight;
use super::c_str_to_string;
use text::glyph::GlyphId;
use text::util::fixed_to_float;

// This constant is not present in the freetype
// bindings due to bindgen not handling the way
// the macro is defined.
const FT_LOAD_TARGET_LIGHT: FT_Int32 = 1 << 16;

// Default to slight hinting, which is what most
// Linux distros use by default, and is a better
// default than no hinting.
// TODO(gw): Make this configurable.
const GLYPH_LOAD_FLAGS: FT_Int32 = FT_LOAD_TARGET_LIGHT;

fn fixed_to_float_ft(f: i32) -> f64 {
    fixed_to_float(6, f)
}

#[derive(Debug)]
pub struct FontTable {
    buffer: Vec<u8>,
}

impl FontTableMethods for FontTable {
    fn buffer(&self) -> &[u8] {
        &self.buffer
    }
}

#[derive(Debug)]
pub struct FontHandle {
    // The font binary. This must stay valid for the lifetime of the font,
    // if the font is created using FT_Memory_Face.
    font_data: Arc<FontTemplateData>,
    face: FT_Face,
    handle: FontContextHandle,
    can_do_fast_shaping: bool,
}

impl Drop for FontHandle {
    fn drop(&mut self) {
        assert!(!self.face.is_null());
        unsafe {
            if !FT_Done_Face(self.face).succeeded() {
                panic!("FT_Done_Face failed");
            }
        }
    }
}

impl FontHandleMethods for FontHandle {
    fn new_from_template(fctx: &FontContextHandle,
                       template: Arc<FontTemplateData>,
                       pt_size: Option<Au>)
                        -> Result<FontHandle, ()> {
        let ft_ctx: FT_Library = fctx.ctx.ctx;
        if ft_ctx.is_null() { return Err(()); }

        return create_face_from_buffer(ft_ctx, &template.bytes, pt_size).map(|face| {
            let mut handle = FontHandle {
                  face: face,
                  font_data: template.clone(),
                  handle: fctx.clone(),
                  can_do_fast_shaping: false,
            };
            // TODO (#11310): Implement basic support for GPOS and GSUB.
            handle.can_do_fast_shaping = handle.has_table(KERN) &&
                                         !handle.has_table(GPOS) &&
                                         !handle.has_table(GSUB);
            handle
        });

        fn create_face_from_buffer(lib: FT_Library, buffer: &[u8], pt_size: Option<Au>)
                                   -> Result<FT_Face, ()> {
            unsafe {
                let mut face: FT_Face = ptr::null_mut();
                let face_index = 0 as FT_Long;
                let result = FT_New_Memory_Face(lib, buffer.as_ptr(), buffer.len() as FT_Long,
                                                face_index, &mut face);

                if !result.succeeded() || face.is_null() {
                    return Err(());
                }
                if let Some(s) = pt_size {
                    FontHandle::set_char_size(face, s).or(Err(()))?
                }
                Ok(face)
            }
        }
    }
    fn template(&self) -> Arc<FontTemplateData> {
        self.font_data.clone()
    }
    fn family_name(&self) -> String {
        unsafe {
            c_str_to_string((*self.face).family_name as *const c_char)
        }
    }
    fn face_name(&self) -> Option<String> {
        unsafe {
            let name = FT_Get_Postscript_Name(self.face) as *const c_char;

            if !name.is_null() {
                Some(c_str_to_string(name))
            } else {
                None
            }
        }
    }
    fn is_italic(&self) -> bool {
        unsafe { (*self.face).style_flags & FT_STYLE_FLAG_ITALIC as c_long != 0 }
    }
    fn boldness(&self) -> FontWeight {
        let default_weight = FontWeight::normal();
        if unsafe { (*self.face).style_flags & FT_STYLE_FLAG_BOLD as c_long == 0 } {
            default_weight
        } else {
            unsafe {
                let os2 = FT_Get_Sfnt_Table(self.face, FT_Sfnt_Tag::FT_SFNT_OS2) as *mut TT_OS2;
                let valid = !os2.is_null() && (*os2).version != 0xffff;
                if valid {
                    let weight =(*os2).usWeightClass as i32;
                    if weight < 10 {
                        FontWeight::from_int(weight * 100).unwrap()
                    } else if weight >= 100 && weight < 1000 {
                        FontWeight::from_int(weight / 100 * 100).unwrap()
                    } else {
                        default_weight
                    }
                } else {
                    default_weight
                }
            }
        }
    }
    fn stretchiness(&self) -> FontStretch {
        // TODO(pcwalton): Implement this.
        FontStretch::Normal
    }

    fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
        assert!(!self.face.is_null());
        unsafe {
            let idx = FT_Get_Char_Index(self.face, codepoint as FT_ULong);
            if idx != 0 as FT_UInt {
                Some(idx as GlyphId)
            } else {
                debug!("Invalid codepoint: {}", codepoint);
                None
            }
        }
    }

    fn glyph_h_kerning(&self, first_glyph: GlyphId, second_glyph: GlyphId)
                       -> FractionalPixel {
        assert!(!self.face.is_null());
        let mut delta = FT_Vector { x: 0, y: 0 };
        unsafe {
            FT_Get_Kerning(self.face, first_glyph, second_glyph,
                           FT_Kerning_Mode::FT_KERNING_DEFAULT as FT_UInt,
                           &mut delta);
        }
        fixed_to_float_ft(delta.x as i32)
    }

    fn can_do_fast_shaping(&self) -> bool {
        self.can_do_fast_shaping
    }

    fn glyph_h_advance(&self, glyph: GlyphId) -> Option<FractionalPixel> {
        assert!(!self.face.is_null());
        unsafe {
            let res =  FT_Load_Glyph(self.face,
                                     glyph as FT_UInt,
                                     GLYPH_LOAD_FLAGS);
            if res.succeeded() {
                let void_glyph = (*self.face).glyph;
                let slot: FT_GlyphSlot = mem::transmute(void_glyph);
                assert!(!slot.is_null());
                let advance = (*slot).metrics.horiAdvance;
                debug!("h_advance for {} is {}", glyph, advance);
                let advance = advance as i32;
                Some(fixed_to_float_ft(advance) as FractionalPixel)
            } else {
                debug!("Unable to load glyph {}. reason: {:?}", glyph, res);
                None
            }
        }
    }

    fn metrics(&self) -> FontMetrics {
        /* TODO(Issue #76): complete me */
        let face = self.face_rec_mut();

        let underline_size = self.font_units_to_au(face.underline_thickness as f64);
        let underline_offset = self.font_units_to_au(face.underline_position as f64);
        let em_size = self.font_units_to_au(face.units_per_EM as f64);
        let ascent = self.font_units_to_au(face.ascender as f64);
        let descent = self.font_units_to_au(face.descender as f64);
        let max_advance = self.font_units_to_au(face.max_advance_width as f64);

        // 'leading' is supposed to be the vertical distance between two baselines,
        // reflected by the height attribute in freetype.  On OS X (w/ CTFont),
        // leading represents the distance between the bottom of a line descent to
        // the top of the next line's ascent or: (line_height - ascent - descent),
        // see http://stackoverflow.com/a/5635981 for CTFont implementation.
        // Convert using a formula similar to what CTFont returns for consistency.
        let height = self.font_units_to_au(face.height as f64);
        let leading = height - (ascent + descent);

        let mut strikeout_size = Au(0);
        let mut strikeout_offset = Au(0);
        let mut x_height = Au(0);
        unsafe {
            let os2 = FT_Get_Sfnt_Table(face, FT_Sfnt_Tag::FT_SFNT_OS2) as *mut TT_OS2;
            let valid = !os2.is_null() && (*os2).version != 0xffff;
            if valid {
               strikeout_size = self.font_units_to_au((*os2).yStrikeoutSize as f64);
               strikeout_offset = self.font_units_to_au((*os2).yStrikeoutPosition as f64);
               x_height = self.font_units_to_au((*os2).sxHeight as f64);
            }
        }

        let average_advance = self.glyph_index('0')
                                  .and_then(|idx| self.glyph_h_advance(idx))
                                  .map_or(max_advance, |advance| self.font_units_to_au(advance));

        let metrics = FontMetrics {
            underline_size:   underline_size,
            underline_offset: underline_offset,
            strikeout_size:   strikeout_size,
            strikeout_offset: strikeout_offset,
            leading:          leading,
            x_height:         x_height,
            em_size:          em_size,
            ascent:           ascent,
            descent:          -descent, // linux font's seem to use the opposite sign from mac
            max_advance:      max_advance,
            average_advance:  average_advance,
            line_gap:         height,
        };

        debug!("Font metrics (@{}px): {:?}", em_size.to_f32_px(), metrics);
        metrics
    }

    fn table_for_tag(&self, tag: FontTableTag) -> Option<FontTable> {
        let tag = tag as FT_ULong;

        unsafe {
            // Get the length
            let mut len = 0;
            if !FT_Load_Sfnt_Table(self.face, tag, 0, ptr::null_mut(), &mut len).succeeded() {
                return None
            }
            // Get the bytes
            let mut buf = vec![0u8; len as usize];
            if !FT_Load_Sfnt_Table(self.face, tag, 0, buf.as_mut_ptr(), &mut len).succeeded() {
                return None
            }
            Some(FontTable { buffer: buf })
        }
    }
}

impl<'a> FontHandle {
    fn set_char_size(face: FT_Face, pt_size: Au) -> Result<(), ()>{
        let char_size = pt_size.to_f64_px() * 64.0 + 0.5;

        unsafe {
            let result = FT_Set_Char_Size(face, char_size as FT_F26Dot6, 0, 0, 0);
            if result.succeeded() { Ok(()) } else { Err(()) }
        }
    }

    fn has_table(&self, tag: FontTableTag) -> bool {
        unsafe {
            FT_Load_Sfnt_Table(self.face, tag as FT_ULong, 0, ptr::null_mut(), &mut 0).succeeded()
        }
    }

    fn face_rec_mut(&'a self) -> &'a mut FT_FaceRec {
        unsafe {
            &mut (*self.face)
        }
    }

    fn font_units_to_au(&self, value: f64) -> Au {
        let face = self.face_rec_mut();

        // face.size is a *c_void in the bindings, presumably to avoid
        // recursive structural types
        let size: &FT_SizeRec = unsafe { mem::transmute(&(*face.size)) };
        let metrics: &FT_Size_Metrics = &(*size).metrics;

        let em_size = face.units_per_EM as f64;
        let x_scale = (metrics.x_ppem as f64) / em_size as f64;

        // If this isn't true then we're scaling one of the axes wrong
        assert_eq!(metrics.x_ppem, metrics.y_ppem);

        Au::from_f64_px(value * x_scale)
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::conversions::{ToJSValConvertible, root_from_handleobject};
use dom::bindings::js::{JS, Root, RootedReference};
use dom::bindings::proxyhandler::{fill_property_descriptor, get_property_descriptor};
use dom::bindings::reflector::{Reflectable, MutReflectable, Reflector};
use dom::bindings::str::DOMString;
use dom::bindings::trace::JSTraceable;
use dom::bindings::utils::WindowProxyHandler;
use dom::bindings::utils::get_array_index_from_id;
use dom::document::Document;
use dom::element::Element;
use dom::window::Window;
use js::JSCLASS_IS_GLOBAL;
use js::glue::{CreateWrapperProxyHandler, ProxyTraps, NewWindowProxy};
use js::glue::{GetProxyPrivate, SetProxyExtra, GetProxyExtra};
use js::jsapi::{Handle, HandleId, HandleObject, HandleValue};
use js::jsapi::{JSAutoCompartment, JSContext, JSErrNum, JSFreeOp, JSObject};
use js::jsapi::{JSPROP_READONLY, JSTracer, JS_DefinePropertyById};
use js::jsapi::{JS_ForwardGetPropertyTo, JS_ForwardSetPropertyTo, JS_GetClass};
use js::jsapi::{JS_GetOwnPropertyDescriptorById, JS_HasPropertyById};
use js::jsapi::{MutableHandle, MutableHandleObject, MutableHandleValue};
use js::jsapi::{ObjectOpResult, PropertyDescriptor};
use js::jsval::{UndefinedValue, PrivateValue};
use msg::constellation_msg::PipelineId;
use std::cell::Cell;
use url::Url;

#[dom_struct]
pub struct BrowsingContext {
    reflector: Reflector,

    /// Pipeline id associated with this context.
    id: PipelineId,

    /// Indicates if reflow is required when reloading.
    needs_reflow: Cell<bool>,

    /// Stores this context's session history
    history: DOMRefCell<Vec<SessionHistoryEntry>>,

    /// The index of the active session history entry
    active_index: Cell<usize>,

    /// Stores the child browsing contexts (ex. iframe browsing context)
    children: DOMRefCell<Vec<JS<BrowsingContext>>>,

    frame_element: Option<JS<Element>>,
}

impl BrowsingContext {
    pub fn new_inherited(frame_element: Option<&Element>, id: PipelineId) -> BrowsingContext {
        BrowsingContext {
            reflector: Reflector::new(),
            id: id,
            needs_reflow: Cell::new(true),
            history: DOMRefCell::new(vec![]),
            active_index: Cell::new(0),
            children: DOMRefCell::new(vec![]),
            frame_element: frame_element.map(JS::from_ref),
        }
    }

    #[allow(unsafe_code)]
    pub fn new(window: &Window, frame_element: Option<&Element>, id: PipelineId) -> Root<BrowsingContext> {
        unsafe {
            let WindowProxyHandler(handler) = window.windowproxy_handler();
            assert!(!handler.is_null());

            let cx = window.get_cx();
            let parent = window.reflector().get_jsobject();
            assert!(!parent.get().is_null());
            assert!(((*JS_GetClass(parent.get())).flags & JSCLASS_IS_GLOBAL) != 0);
            let _ac = JSAutoCompartment::new(cx, parent.get());
            rooted!(in(cx) let window_proxy = NewWindowProxy(cx, parent, handler));
            assert!(!window_proxy.is_null());

            let object = box BrowsingContext::new_inherited(frame_element, id);

            let raw = Box::into_raw(object);
            SetProxyExtra(window_proxy.get(), 0, &PrivateValue(raw as *const _));

            (*raw).init_reflector(window_proxy.get());

            Root::from_ref(&*raw)
        }
    }

    pub fn init(&self, document: &Document) {
        assert!(self.history.borrow().is_empty());
        assert_eq!(self.active_index.get(), 0);
        self.history.borrow_mut().push(SessionHistoryEntry::new(document, document.url().clone(), document.Title()));
    }

    pub fn push_history(&self, document: &Document) {
        let mut history = self.history.borrow_mut();
        // Clear all session history entries after the active index
        history.drain((self.active_index.get() + 1)..);
        history.push(SessionHistoryEntry::new(document, document.url().clone(), document.Title()));
        self.active_index.set(self.active_index.get() + 1);
        assert_eq!(self.active_index.get(), history.len() - 1);
    }

    pub fn active_document(&self) -> Root<Document> {
        Root::from_ref(&self.history.borrow()[self.active_index.get()].document)
    }

    pub fn maybe_active_document(&self) -> Option<Root<Document>> {
        self.history.borrow().get(self.active_index.get()).map(|entry| {
            Root::from_ref(&*entry.document)
        })
    }

    pub fn active_window(&self) -> Root<Window> {
        Root::from_ref(self.active_document().window())
    }

    pub fn frame_element(&self) -> Option<&Element> {
        self.frame_element.r()
    }

    pub fn window_proxy(&self) -> *mut JSObject {
        let window_proxy = self.reflector.get_jsobject();
        assert!(!window_proxy.get().is_null());
        window_proxy.get()
    }

    pub fn remove(&self, id: PipelineId) -> Option<Root<BrowsingContext>> {
        let remove_idx = self.children
                             .borrow()
                             .iter()
                             .position(|context| context.id == id);
        match remove_idx {
            Some(idx) => Some(Root::from_ref(&*self.children.borrow_mut().remove(idx))),
            None => {
                self.children
                    .borrow_mut()
                    .iter_mut()
                    .filter_map(|context| context.remove(id))
                    .next()
            }
        }
    }

    pub fn set_reflow_status(&self, status: bool) -> bool {
        let old = self.needs_reflow.get();
        self.needs_reflow.set(status);
        old
    }

    pub fn pipeline_id(&self) -> PipelineId {
        self.id
    }

    pub fn push_child_context(&self, context: &BrowsingContext) {
        self.children.borrow_mut().push(JS::from_ref(&context));
    }

    pub fn find_child_by_id(&self, pipeline_id: PipelineId) -> Option<Root<Window>> {
        self.children.borrow().iter().find(|context| {
            let window = context.active_window();
            window.pipeline_id() == pipeline_id
        }).map(|context| context.active_window())
    }

    pub fn clear_session_history(&self) {
        self.active_index.set(0);
        self.history.borrow_mut().clear();
    }

    pub fn iter(&self) -> ContextIterator {
        ContextIterator {
            stack: vec!(Root::from_ref(self)),
        }
    }

    pub fn find(&self, id: PipelineId) -> Option<Root<BrowsingContext>> {
        if self.id == id {
            return Some(Root::from_ref(self));
        }

        self.children.borrow()
                     .iter()
                     .filter_map(|c| c.find(id))
                     .next()
    }
}

pub struct ContextIterator {
    stack: Vec<Root<BrowsingContext>>,
}

impl Iterator for ContextIterator {
    type Item = Root<BrowsingContext>;

    fn next(&mut self) -> Option<Root<BrowsingContext>> {
        let popped = self.stack.pop();
        if let Some(ref context) = popped {
            self.stack.extend(context.children.borrow()
                                              .iter()
                                              .map(|c| Root::from_ref(&**c)));
        }
        popped
    }
}

// This isn't a DOM struct, just a convenience struct
// without a reflector, so we don't mark this as #[dom_struct]
#[must_root]
#[privatize]
#[derive(JSTraceable, HeapSizeOf)]
pub struct SessionHistoryEntry {
    document: JS<Document>,
    url: Url,
    title: DOMString,
}

impl SessionHistoryEntry {
    fn new(document: &Document, url: Url, title: DOMString) -> SessionHistoryEntry {
        SessionHistoryEntry {
            document: JS::from_ref(document),
            url: url,
            title: title,
        }
    }
}

#[allow(unsafe_code)]
unsafe fn getSubframeWindow(cx: *mut JSContext,
                            proxy: HandleObject,
                            id: HandleId)
                            -> Option<Root<Window>> {
    let index = get_array_index_from_id(cx, id);
    if let Some(index) = index {
        rooted!(in(cx) let target = GetProxyPrivate(*proxy.ptr).to_object());
        let win = root_from_handleobject::<Window>(target.handle()).unwrap();
        let mut found = false;
        return win.IndexedGetter(index, &mut found);
    }

    None
}

#[allow(unsafe_code)]
unsafe extern "C" fn getOwnPropertyDescriptor(cx: *mut JSContext,
                                              proxy: HandleObject,
                                              id: HandleId,
                                              mut desc: MutableHandle<PropertyDescriptor>)
                                              -> bool {
    let window = getSubframeWindow(cx, proxy, id);
    if let Some(window) = window {
        rooted!(in(cx) let mut val = UndefinedValue());
        window.to_jsval(cx, val.handle_mut());
        desc.value = val.get();
        fill_property_descriptor(&mut desc, proxy.get(), JSPROP_READONLY);
        return true;
    }

    rooted!(in(cx) let target = GetProxyPrivate(proxy.get()).to_object());
    if !JS_GetOwnPropertyDescriptorById(cx, target.handle(), id, desc) {
        return false;
    }

    assert!(desc.obj.is_null() || desc.obj == target.get());
    if desc.obj == target.get() {
        // FIXME(#11868) Should assign to desc.obj, desc.get() is a copy.
        desc.get().obj = proxy.get();
    }

    true
}

#[allow(unsafe_code)]
unsafe extern "C" fn defineProperty(cx: *mut JSContext,
                                    proxy: HandleObject,
                                    id: HandleId,
                                    desc: Handle<PropertyDescriptor>,
                                    res: *mut ObjectOpResult)
                                    -> bool {
    if get_array_index_from_id(cx, id).is_some() {
        // Spec says to Reject whether this is a supported index or not,
        // since we have no indexed setter or indexed creator.  That means
        // throwing in strict mode (FIXME: Bug 828137), doing nothing in
        // non-strict mode.
        (*res).code_ = JSErrNum::JSMSG_CANT_DEFINE_WINDOW_ELEMENT as ::libc::uintptr_t;
        return true;
    }

    rooted!(in(cx) let target = GetProxyPrivate(*proxy.ptr).to_object());
    JS_DefinePropertyById(cx, target.handle(), id, desc, res)
}

#[allow(unsafe_code)]
unsafe extern "C" fn has(cx: *mut JSContext,
                         proxy: HandleObject,
                         id: HandleId,
                         bp: *mut bool)
                         -> bool {
    let window = getSubframeWindow(cx, proxy, id);
    if window.is_some() {
        *bp = true;
        return true;
    }

    rooted!(in(cx) let target = GetProxyPrivate(*proxy.ptr).to_object());
    let mut found = false;
    if !JS_HasPropertyById(cx, target.handle(), id, &mut found) {
        return false;
    }

    *bp = found;
    true
}

#[allow(unsafe_code)]
unsafe extern "C" fn get(cx: *mut JSContext,
                         proxy: HandleObject,
                         receiver: HandleValue,
                         id: HandleId,
                         vp: MutableHandleValue)
                         -> bool {
    let window = getSubframeWindow(cx, proxy, id);
    if let Some(window) = window {
        window.to_jsval(cx, vp);
        return true;
    }

    rooted!(in(cx) let target = GetProxyPrivate(*proxy.ptr).to_object());
    JS_ForwardGetPropertyTo(cx, target.handle(), id, receiver, vp)
}

#[allow(unsafe_code)]
unsafe extern "C" fn set(cx: *mut JSContext,
                         proxy: HandleObject,
                         id: HandleId,
                         v: HandleValue,
                         receiver: HandleValue,
                         res: *mut ObjectOpResult)
                         -> bool {
    if get_array_index_from_id(cx, id).is_some() {
        // Reject (which means throw if and only if strict) the set.
        (*res).code_ = JSErrNum::JSMSG_READ_ONLY as ::libc::uintptr_t;
        return true;
    }

    rooted!(in(cx) let target = GetProxyPrivate(*proxy.ptr).to_object());
    JS_ForwardSetPropertyTo(cx,
                            target.handle(),
                            id,
                            v,
                            receiver,
                            res)
}

#[allow(unsafe_code)]
unsafe extern "C" fn get_prototype_if_ordinary(_: *mut JSContext,
                                               _: HandleObject,
                                               is_ordinary: *mut bool,
                                               _: MutableHandleObject)
                                               -> bool {
    // Window's [[GetPrototypeOf]] trap isn't the ordinary definition:
    //
    //   https://html.spec.whatwg.org/multipage/#windowproxy-getprototypeof
    //
    // We nonetheless can implement it with a static [[Prototype]], because
    // wrapper-class handlers (particularly, XOW in FilteringWrapper.cpp) supply
    // all non-ordinary behavior.
    //
    // But from a spec point of view, it's the exact same object in both cases --
    // only the observer's changed.  So this getPrototypeIfOrdinary trap on the
    // non-wrapper object *must* report non-ordinary, even if static [[Prototype]]
    // usually means ordinary.
    *is_ordinary = false;
    return true;
}

static PROXY_HANDLER: ProxyTraps = ProxyTraps {
    enter: None,
    getOwnPropertyDescriptor: Some(getOwnPropertyDescriptor),
    defineProperty: Some(defineProperty),
    ownPropertyKeys: None,
    delete_: None,
    enumerate: None,
    getPrototypeIfOrdinary: Some(get_prototype_if_ordinary),
    preventExtensions: None,
    isExtensible: None,
    has: Some(has),
    get: Some(get),
    set: Some(set),
    call: None,
    construct: None,
    getPropertyDescriptor: Some(get_property_descriptor),
    hasOwn: None,
    getOwnEnumerablePropertyKeys: None,
    nativeCall: None,
    hasInstance: None,
    objectClassIs: None,
    className: None,
    fun_toString: None,
    boxedValue_unbox: None,
    defaultValue: None,
    trace: Some(trace),
    finalize: Some(finalize),
    objectMoved: None,
    isCallable: None,
    isConstructor: None,
};

#[allow(unsafe_code)]
unsafe extern fn finalize(_fop: *mut JSFreeOp, obj: *mut JSObject) {
    let this = GetProxyExtra(obj, 0).to_private() as *mut BrowsingContext;
    assert!(!this.is_null());
    let _ = Box::from_raw(this);
    debug!("BrowsingContext finalize: {:p}", this);
}

#[allow(unsafe_code)]
unsafe extern fn trace(trc: *mut JSTracer, obj: *mut JSObject) {
    let this = GetProxyExtra(obj, 0).to_private() as *const BrowsingContext;
    if this.is_null() {
        // GC during obj creation
        return;
    }
    (*this).trace(trc);
}

#[allow(unsafe_code)]
pub fn new_window_proxy_handler() -> WindowProxyHandler {
    unsafe {
        WindowProxyHandler(CreateWrapperProxyHandler(&PROXY_HANDLER))
    }
}
<commit_msg>Fix my typo on commit. Rest of the fixes will be here soon.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use dom::bindings::conversions::{ToJSValConvertible, root_from_handleobject};
use dom::bindings::js::{JS, Root, RootedReference};
use dom::bindings::proxyhandler::{fill_property_descriptor, get_property_descriptor};
use dom::bindings::reflector::{Reflectable, MutReflectable, Reflector};
use dom::bindings::str::DOMString;
use dom::bindings::trace::JSTraceable;
use dom::bindings::utils::WindowProxyHandler;
use dom::bindings::utils::get_array_index_from_id;
use dom::document::Document;
use dom::element::Element;
use dom::window::Window;
use js::JSCLASS_IS_GLOBAL;
use js::glue::{CreateWrapperProxyHandler, ProxyTraps, NewWindowProxy};
use js::glue::{GetProxyPrivate, SetProxyExtra, GetProxyExtra};
use js::jsapi::{Handle, HandleId, HandleObject, HandleValue};
use js::jsapi::{JSAutoCompartment, JSContext, JSErrNum, JSFreeOp, JSObject};
use js::jsapi::{JSPROP_READONLY, JSTracer, JS_DefinePropertyById};
use js::jsapi::{JS_ForwardGetPropertyTo, JS_ForwardSetPropertyTo, JS_GetClass};
use js::jsapi::{JS_GetOwnPropertyDescriptorById, JS_HasPropertyById};
use js::jsapi::{MutableHandle, MutableHandleObject, MutableHandleValue};
use js::jsapi::{ObjectOpResult, PropertyDescriptor};
use js::jsval::{UndefinedValue, PrivateValue};
use msg::constellation_msg::PipelineId;
use std::cell::Cell;
use url::Url;

#[dom_struct]
pub struct BrowsingContext {
    reflector: Reflector,

    /// Pipeline id associated with this context.
    id: PipelineId,

    /// Indicates if reflow is required when reloading.
    needs_reflow: Cell<bool>,

    /// Stores this context's session history
    history: DOMRefCell<Vec<SessionHistoryEntry>>,

    /// The index of the active session history entry
    active_index: Cell<usize>,

    /// Stores the child browsing contexts (ex. iframe browsing context)
    children: DOMRefCell<Vec<JS<BrowsingContext>>>,

    frame_element: Option<JS<Element>>,
}

impl BrowsingContext {
    pub fn new_inherited(frame_element: Option<&Element>, id: PipelineId) -> BrowsingContext {
        BrowsingContext {
            reflector: Reflector::new(),
            id: id,
            needs_reflow: Cell::new(true),
            history: DOMRefCell::new(vec![]),
            active_index: Cell::new(0),
            children: DOMRefCell::new(vec![]),
            frame_element: frame_element.map(JS::from_ref),
        }
    }

    #[allow(unsafe_code)]
    pub fn new(window: &Window, frame_element: Option<&Element>, id: PipelineId) -> Root<BrowsingContext> {
        unsafe {
            let WindowProxyHandler(handler) = window.windowproxy_handler();
            assert!(!handler.is_null());

            let cx = window.get_cx();
            let parent = window.reflector().get_jsobject();
            assert!(!parent.get().is_null());
            assert!(((*JS_GetClass(parent.get())).flags & JSCLASS_IS_GLOBAL) != 0);
            let _ac = JSAutoCompartment::new(cx, parent.get());
            rooted!(in(cx) let window_proxy = NewWindowProxy(cx, parent, handler));
            assert!(!window_proxy.is_null());

            let object = box BrowsingContext::new_inherited(frame_element, id);

            let raw = Box::into_raw(object);
            SetProxyExtra(window_proxy.get(), 0, &PrivateValue(raw as *const _));

            (*raw).init_reflector(window_proxy.get());

            Root::from_ref(&*raw)
        }
    }

    pub fn init(&self, document: &Document) {
        assert!(self.history.borrow().is_empty());
        assert_eq!(self.active_index.get(), 0);
        self.history.borrow_mut().push(SessionHistoryEntry::new(document, document.url().clone(), document.Title()));
    }

    pub fn push_history(&self, document: &Document) {
        let mut history = self.history.borrow_mut();
        // Clear all session history entries after the active index
        history.drain((self.active_index.get() + 1)..);
        history.push(SessionHistoryEntry::new(document, document.url().clone(), document.Title()));
        self.active_index.set(self.active_index.get() + 1);
        assert_eq!(self.active_index.get(), history.len() - 1);
    }

    pub fn active_document(&self) -> Root<Document> {
        Root::from_ref(&self.history.borrow()[self.active_index.get()].document)
    }

    pub fn maybe_active_document(&self) -> Option<Root<Document>> {
        self.history.borrow().get(self.active_index.get()).map(|entry| {
            Root::from_ref(&*entry.document)
        })
    }

    pub fn active_window(&self) -> Root<Window> {
        Root::from_ref(self.active_document().window())
    }

    pub fn frame_element(&self) -> Option<&Element> {
        self.frame_element.r()
    }

    pub fn window_proxy(&self) -> *mut JSObject {
        let window_proxy = self.reflector.get_jsobject();
        assert!(!window_proxy.get().is_null());
        window_proxy.get()
    }

    pub fn remove(&self, id: PipelineId) -> Option<Root<BrowsingContext>> {
        let remove_idx = self.children
                             .borrow()
                             .iter()
                             .position(|context| context.id == id);
        match remove_idx {
            Some(idx) => Some(Root::from_ref(&*self.children.borrow_mut().remove(idx))),
            None => {
                self.children
                    .borrow_mut()
                    .iter_mut()
                    .filter_map(|context| context.remove(id))
                    .next()
            }
        }
    }

    pub fn set_reflow_status(&self, status: bool) -> bool {
        let old = self.needs_reflow.get();
        self.needs_reflow.set(status);
        old
    }

    pub fn pipeline_id(&self) -> PipelineId {
        self.id
    }

    pub fn push_child_context(&self, context: &BrowsingContext) {
        self.children.borrow_mut().push(JS::from_ref(&context));
    }

    pub fn find_child_by_id(&self, pipeline_id: PipelineId) -> Option<Root<Window>> {
        self.children.borrow().iter().find(|context| {
            let window = context.active_window();
            window.pipeline_id() == pipeline_id
        }).map(|context| context.active_window())
    }

    pub fn clear_session_history(&self) {
        self.active_index.set(0);
        self.history.borrow_mut().clear();
    }

    pub fn iter(&self) -> ContextIterator {
        ContextIterator {
            stack: vec!(Root::from_ref(self)),
        }
    }

    pub fn find(&self, id: PipelineId) -> Option<Root<BrowsingContext>> {
        if self.id == id {
            return Some(Root::from_ref(self));
        }

        self.children.borrow()
                     .iter()
                     .filter_map(|c| c.find(id))
                     .next()
    }
}

pub struct ContextIterator {
    stack: Vec<Root<BrowsingContext>>,
}

impl Iterator for ContextIterator {
    type Item = Root<BrowsingContext>;

    fn next(&mut self) -> Option<Root<BrowsingContext>> {
        let popped = self.stack.pop();
        if let Some(ref context) = popped {
            self.stack.extend(context.children.borrow()
                                              .iter()
                                              .map(|c| Root::from_ref(&**c)));
        }
        popped
    }
}

// This isn't a DOM struct, just a convenience struct
// without a reflector, so we don't mark this as #[dom_struct]
#[must_root]
#[privatize]
#[derive(JSTraceable, HeapSizeOf)]
pub struct SessionHistoryEntry {
    document: JS<Document>,
    url: Url,
    title: DOMString,
}

impl SessionHistoryEntry {
    fn new(document: &Document, url: Url, title: DOMString) -> SessionHistoryEntry {
        SessionHistoryEntry {
            document: JS::from_ref(document),
            url: url,
            title: title,
        }
    }
}

#[allow(unsafe_code)]
unsafe fn GetSubframeWindow(cx: *mut JSContext,
                            proxy: HandleObject,
                            id: HandleId)
                            -> Option<Root<Window>> {
    let index = get_array_index_from_id(cx, id);
    if let Some(index) = index {
        rooted!(in(cx) let target = GetProxyPrivate(*proxy.ptr).to_object());
        let win = root_from_handleobject::<Window>(target.handle()).unwrap();
        let mut found = false;
        return win.IndexedGetter(index, &mut found);
    }

    None
}

#[allow(unsafe_code)]
unsafe extern "C" fn getOwnPropertyDescriptor(cx: *mut JSContext,
                                              proxy: HandleObject,
                                              id: HandleId,
                                              mut desc: MutableHandle<PropertyDescriptor>)
                                              -> bool {
    let window = GetSubframeWindow(cx, proxy, id);
    if let Some(window) = window {
        rooted!(in(cx) let mut val = UndefinedValue());
        window.to_jsval(cx, val.handle_mut());
        desc.value = val.get();
        fill_property_descriptor(&mut desc, proxy.get(), JSPROP_READONLY);
        return true;
    }

    rooted!(in(cx) let target = GetProxyPrivate(proxy.get()).to_object());
    if !JS_GetOwnPropertyDescriptorById(cx, target.handle(), id, desc) {
        return false;
    }

    assert!(desc.obj.is_null() || desc.obj == target.get());
    if desc.obj == target.get() {
        // FIXME(#11868) Should assign to desc.obj, desc.get() is a copy.
        desc.get().obj = proxy.get();
    }

    true
}

#[allow(unsafe_code)]
unsafe extern "C" fn defineProperty(cx: *mut JSContext,
                                    proxy: HandleObject,
                                    id: HandleId,
                                    desc: Handle<PropertyDescriptor>,
                                    res: *mut ObjectOpResult)
                                    -> bool {
    if get_array_index_from_id(cx, id).is_some() {
        // Spec says to Reject whether this is a supported index or not,
        // since we have no indexed setter or indexed creator.  That means
        // throwing in strict mode (FIXME: Bug 828137), doing nothing in
        // non-strict mode.
        (*res).code_ = JSErrNum::JSMSG_CANT_DEFINE_WINDOW_ELEMENT as ::libc::uintptr_t;
        return true;
    }

    rooted!(in(cx) let target = GetProxyPrivate(*proxy.ptr).to_object());
    JS_DefinePropertyById(cx, target.handle(), id, desc, res)
}

#[allow(unsafe_code)]
unsafe extern "C" fn has(cx: *mut JSContext,
                         proxy: HandleObject,
                         id: HandleId,
                         bp: *mut bool)
                         -> bool {
    let window = GetSubframeWindow(cx, proxy, id);
    if window.is_some() {
        *bp = true;
        return true;
    }

    rooted!(in(cx) let target = GetProxyPrivate(*proxy.ptr).to_object());
    let mut found = false;
    if !JS_HasPropertyById(cx, target.handle(), id, &mut found) {
        return false;
    }

    *bp = found;
    true
}

#[allow(unsafe_code)]
unsafe extern "C" fn get(cx: *mut JSContext,
                         proxy: HandleObject,
                         receiver: HandleValue,
                         id: HandleId,
                         vp: MutableHandleValue)
                         -> bool {
    let window = GetSubframeWindow(cx, proxy, id);
    if let Some(window) = window {
        window.to_jsval(cx, vp);
        return true;
    }

    rooted!(in(cx) let target = GetProxyPrivate(*proxy.ptr).to_object());
    JS_ForwardGetPropertyTo(cx, target.handle(), id, receiver, vp)
}

#[allow(unsafe_code)]
unsafe extern "C" fn set(cx: *mut JSContext,
                         proxy: HandleObject,
                         id: HandleId,
                         v: HandleValue,
                         receiver: HandleValue,
                         res: *mut ObjectOpResult)
                         -> bool {
    if get_array_index_from_id(cx, id).is_some() {
        // Reject (which means throw if and only if strict) the set.
        (*res).code_ = JSErrNum::JSMSG_READ_ONLY as ::libc::uintptr_t;
        return true;
    }

    rooted!(in(cx) let target = GetProxyPrivate(*proxy.ptr).to_object());
    JS_ForwardSetPropertyTo(cx,
                            target.handle(),
                            id,
                            v,
                            receiver,
                            res)
}

#[allow(unsafe_code)]
unsafe extern "C" fn get_prototype_if_ordinary(_: *mut JSContext,
                                               _: HandleObject,
                                               is_ordinary: *mut bool,
                                               _: MutableHandleObject)
                                               -> bool {
    // Window's [[GetPrototypeOf]] trap isn't the ordinary definition:
    //
    //   https://html.spec.whatwg.org/multipage/#windowproxy-getprototypeof
    //
    // We nonetheless can implement it with a static [[Prototype]], because
    // wrapper-class handlers (particularly, XOW in FilteringWrapper.cpp) supply
    // all non-ordinary behavior.
    //
    // But from a spec point of view, it's the exact same object in both cases --
    // only the observer's changed.  So this getPrototypeIfOrdinary trap on the
    // non-wrapper object *must* report non-ordinary, even if static [[Prototype]]
    // usually means ordinary.
    *is_ordinary = false;
    return true;
}

static PROXY_HANDLER: ProxyTraps = ProxyTraps {
    enter: None,
    getOwnPropertyDescriptor: Some(getOwnPropertyDescriptor),
    defineProperty: Some(defineProperty),
    ownPropertyKeys: None,
    delete_: None,
    enumerate: None,
    getPrototypeIfOrdinary: Some(get_prototype_if_ordinary),
    preventExtensions: None,
    isExtensible: None,
    has: Some(has),
    get: Some(get),
    set: Some(set),
    call: None,
    construct: None,
    getPropertyDescriptor: Some(get_property_descriptor),
    hasOwn: None,
    getOwnEnumerablePropertyKeys: None,
    nativeCall: None,
    hasInstance: None,
    objectClassIs: None,
    className: None,
    fun_toString: None,
    boxedValue_unbox: None,
    defaultValue: None,
    trace: Some(trace),
    finalize: Some(finalize),
    objectMoved: None,
    isCallable: None,
    isConstructor: None,
};

#[allow(unsafe_code)]
unsafe extern fn finalize(_fop: *mut JSFreeOp, obj: *mut JSObject) {
    let this = GetProxyExtra(obj, 0).to_private() as *mut BrowsingContext;
    assert!(!this.is_null());
    let _ = Box::from_raw(this);
    debug!("BrowsingContext finalize: {:p}", this);
}

#[allow(unsafe_code)]
unsafe extern fn trace(trc: *mut JSTracer, obj: *mut JSObject) {
    let this = GetProxyExtra(obj, 0).to_private() as *const BrowsingContext;
    if this.is_null() {
        // GC during obj creation
        return;
    }
    (*this).trace(trc);
}

#[allow(unsafe_code)]
pub fn new_window_proxy_handler() -> WindowProxyHandler {
    unsafe {
        WindowProxyHandler(CreateWrapperProxyHandler(&PROXY_HANDLER))
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::activation::Activatable;
use dom::bindings::codegen::Bindings::DOMTokenListBinding::DOMTokenListMethods;
use dom::bindings::codegen::Bindings::HTMLAreaElementBinding;
use dom::bindings::codegen::Bindings::HTMLAreaElementBinding::HTMLAreaElementMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{MutNullableJS, Root};
use dom::bindings::str::DOMString;
use dom::document::Document;
use dom::domtokenlist::DOMTokenList;
use dom::element::Element;
use dom::event::Event;
use dom::eventtarget::EventTarget;
use dom::htmlanchorelement::follow_hyperlink;
use dom::htmlelement::HTMLElement;
use dom::node::{Node, document_from_node};
use dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
use euclid::Point2D;
use html5ever::{LocalName, Prefix};
use net_traits::ReferrerPolicy;
use std::default::Default;
use std::f32;
use style::attr::AttrValue;

#[derive(PartialEq)]
#[derive(Debug)]
pub enum Area {
    Circle { left: f32, top: f32, radius: f32 },
    Rectangle { top_left: (f32, f32), bottom_right: (f32, f32) },
    Polygon { points: Vec<f32> },
}

pub enum Shape {
    Circle,
    Rectangle,
    Polygon,
}

// https://html.spec.whatwg.org/multipage/#rules-for-parsing-a-list-of-floating-point-numbers
// https://html.spec.whatwg.org/multipage/#image-map-processing-model
impl Area {
    pub fn parse(coord: &str, target: Shape) -> Option<Area> {
        let points_count =  match target {
            Shape::Circle => 3,
            Shape::Rectangle => 4,
            Shape::Polygon => 0,
        };

        let size = coord.len();
        let num = coord.as_bytes();
        let mut index = 0;

        // Step 4: Walk till char is not a delimiter
        while index < size {
            let val = num[index];
            match val {
                b',' | b';' | b' ' | b'\t' | b'\n' | 0x0C | b'\r'  => {},
                _ => break,
            }

            index += 1;
        }

        //This vector will hold all parsed coordinates
        let mut number_list = Vec::new();
        let mut array = Vec::new();
        let ar_ref = &mut array;

        // Step 5: walk till end of string
        while index < size {
            // Step 5.1: walk till we hit a valid char i.e., 0 to 9, dot or dash, e, E
            while index < size {
                let val = num[index];
                match val {
                    b'0'...b'9' | b'.' | b'-' | b'E' | b'e' => break,
                    _ => {},
                }

                index += 1;
            }

            // Step 5.2: collect valid symbols till we hit another delimiter
            while index < size {
                let val = num[index];

                match val {
                    b',' | b';' | b' ' | b'\t' | b'\n' | 0x0C | b'\r' => break,
                    _ => (*ar_ref).push(val),
                }

                index += 1;
            }

            // The input does not consist any valid charecters
            if (*ar_ref).is_empty() {
                break;
            }

            // Convert String to float
            match String::from_utf8((*ar_ref).clone()).unwrap().parse::<f32>() {
                Ok(v) => number_list.push(v),
                Err(_) => number_list.push(0.0),
            };

            (*ar_ref).clear();

            // For rectangle and circle, stop parsing once we have three
            // and four coordinates respectively
            if points_count > 0 && number_list.len() == points_count {
                break;
            }
        }

        let final_size = number_list.len();

        match target {
            Shape::Circle => {
                if final_size == 3 {
                    if number_list[2] <= 0.0 {
                        None
                    } else {
                        Some(Area::Circle {
                                 left: number_list[0],
                                 top: number_list[1],
                                 radius: number_list[2]
                             })
                    }
                } else {
                    None
                }
            },

            Shape::Rectangle => {
                if final_size == 4 {
                    if number_list[0] > number_list[2] {
                        number_list.swap(0, 2);
                    }

                    if number_list[1] > number_list[3] {
                        number_list.swap(1, 3);
                    }

                    Some(Area::Rectangle {
                             top_left: (number_list[0], number_list[1]),
                             bottom_right: (number_list[2], number_list[3])
                         })
                } else {
                    None
                }
            },

            Shape::Polygon => {
                if final_size >= 6 {
                    if final_size % 2 != 0 {
                        // Drop last element if there are odd number of coordinates
                        number_list.remove(final_size - 1);
                    }
                    Some(Area::Polygon { points: number_list })
                } else {
                    None
                }
            },
        }
    }

    pub fn hit_test(&self, p: Point2D<f32>) -> bool {
        match *self {
            Area::Circle { left, top, radius } => {
                (p.x - left) * (p.x - left) +
                (p.y - top) * (p.y - top) -
                radius * radius <= 0.0
            },

            Area::Rectangle { top_left, bottom_right } => {
                p.x <= bottom_right.0 && p.x >= top_left.0 &&
                p.y <= bottom_right.1 && p.y >= top_left.1
            },

            //TODO polygon hit_test
            _ => false,
        }
    }

    pub fn absolute_coords(&self, p: Point2D<f32>) -> Area {
        match *self {
            Area::Rectangle { top_left, bottom_right } => {
                Area::Rectangle {
                    top_left: (top_left.0 + p.x, top_left.1 + p.y),
                    bottom_right: (bottom_right.0 + p.x, bottom_right.1 + p.y)
                }
            },
            Area::Circle { left, top, radius } => {
                Area::Circle {
                    left: (left + p.x),
                    top: (top + p.y),
                    radius: radius
                }
            },
            Area::Polygon { ref points } => {
//                let new_points = Vec::new();
                let iter = points.iter().enumerate().map(|(index, point)| {
                    match index % 2 {
                        0 => point + p.x as f32,
                        _ => point + p.y as f32,
                    }
                });
                Area::Polygon { points: iter.collect::<Vec<_>>() }
            },
        }
    }
}

#[dom_struct]
pub struct HTMLAreaElement {
    htmlelement: HTMLElement,
    rel_list: MutNullableJS<DOMTokenList>,
}

impl HTMLAreaElement {
    fn new_inherited(local_name: LocalName, prefix: Option<Prefix>, document: &Document) -> HTMLAreaElement {
        HTMLAreaElement {
            htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
            rel_list: Default::default(),
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(local_name: LocalName,
               prefix: Option<Prefix>,
               document: &Document) -> Root<HTMLAreaElement> {
        Node::reflect_node(box HTMLAreaElement::new_inherited(local_name, prefix, document),
                           document,
                           HTMLAreaElementBinding::Wrap)
    }

    pub fn get_shape_from_coords(&self) -> Option<Area> {
       let elem = self.upcast::<Element>();
       let shape = elem.get_string_attribute(&"shape".into());
       let shp: Shape = match shape.to_lowercase().as_ref() {
           "circle" => Shape::Circle,
           "circ" => Shape::Circle,
           "rectangle" => Shape::Rectangle,
           "rect" => Shape::Rectangle,
           "polygon" => Shape::Rectangle,
           "poly" => Shape::Polygon,
           _ => return None,
        };
        if elem.has_attribute(&"coords".into()) {
            let attribute = elem.get_string_attribute(&"coords".into());
            Area::parse(&attribute, shp)
        } else {
            None
        }
    }
}

impl VirtualMethods for HTMLAreaElement {
    fn super_type(&self) -> Option<&VirtualMethods> {
        Some(self.upcast::<HTMLElement>() as &VirtualMethods)
    }

    fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
        match name {
            &local_name!("rel") => AttrValue::from_serialized_tokenlist(value.into()),
            _ => self.super_type().unwrap().parse_plain_attribute(name, value),
        }
    }
}

impl HTMLAreaElementMethods for HTMLAreaElement {
    // https://html.spec.whatwg.org/multipage/#dom-area-rellist
    fn RelList(&self) -> Root<DOMTokenList> {
        self.rel_list.or_init(|| {
            DOMTokenList::new(self.upcast(), &local_name!("rel"))
        })
    }
}

impl Activatable for HTMLAreaElement {
    // https://html.spec.whatwg.org/multipage/#the-area-element:activation-behaviour
    fn as_element(&self) -> &Element {
        self.upcast::<Element>()
    }

    fn is_instance_activatable(&self) -> bool {
        self.as_element().has_attribute(&local_name!("href"))
    }

    fn pre_click_activation(&self) {
    }

    fn canceled_activation(&self) {
    }

    fn implicit_submission(&self, _ctrl_key: bool, _shift_key: bool,
                           _alt_key: bool, _meta_key: bool) {
    }

    fn activation_behavior(&self, _event: &Event, _target: &EventTarget) {
        // Step 1
        let doc = document_from_node(self);
        if !doc.is_fully_active() {
            return;
        }
        // Step 2
        // TODO : We should be choosing a browsing context and navigating to it.
        // Step 3
        let referrer_policy = match self.RelList().Contains("noreferrer".into()) {
            true => Some(ReferrerPolicy::NoReferrer),
            false => None,
        };
        follow_hyperlink(self.upcast::<Element>(), None, referrer_policy);
    }
}
<commit_msg>Clean up Area::parse (#15834)<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::activation::Activatable;
use dom::bindings::codegen::Bindings::DOMTokenListBinding::DOMTokenListMethods;
use dom::bindings::codegen::Bindings::HTMLAreaElementBinding;
use dom::bindings::codegen::Bindings::HTMLAreaElementBinding::HTMLAreaElementMethods;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{MutNullableJS, Root};
use dom::bindings::str::DOMString;
use dom::document::Document;
use dom::domtokenlist::DOMTokenList;
use dom::element::Element;
use dom::event::Event;
use dom::eventtarget::EventTarget;
use dom::htmlanchorelement::follow_hyperlink;
use dom::htmlelement::HTMLElement;
use dom::node::{Node, document_from_node};
use dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
use euclid::Point2D;
use html5ever::{LocalName, Prefix};
use net_traits::ReferrerPolicy;
use std::default::Default;
use std::f32;
use std::str;
use style::attr::AttrValue;

#[derive(PartialEq)]
#[derive(Debug)]
pub enum Area {
    Circle { left: f32, top: f32, radius: f32 },
    Rectangle { top_left: (f32, f32), bottom_right: (f32, f32) },
    Polygon { points: Vec<f32> },
}

pub enum Shape {
    Circle,
    Rectangle,
    Polygon,
}

// https://html.spec.whatwg.org/multipage/#rules-for-parsing-a-list-of-floating-point-numbers
// https://html.spec.whatwg.org/multipage/#image-map-processing-model
impl Area {
    pub fn parse(coord: &str, target: Shape) -> Option<Area> {
        let points_count =  match target {
            Shape::Circle => 3,
            Shape::Rectangle => 4,
            Shape::Polygon => 0,
        };

        let size = coord.len();
        let num = coord.as_bytes();
        let mut index = 0;

        // Step 4: Walk till char is not a delimiter
        while index < size {
            let val = num[index];
            match val {
                b',' | b';' | b' ' | b'\t' | b'\n' | 0x0C | b'\r'  => {},
                _ => break,
            }

            index += 1;
        }

        //This vector will hold all parsed coordinates
        let mut number_list = Vec::new();
        let mut array = Vec::new();

        // Step 5: walk till end of string
        while index < size {
            // Step 5.1: walk till we hit a valid char i.e., 0 to 9, dot or dash, e, E
            while index < size {
                let val = num[index];
                match val {
                    b'0'...b'9' | b'.' | b'-' | b'E' | b'e' => break,
                    _ => {},
                }

                index += 1;
            }

            // Step 5.2: collect valid symbols till we hit another delimiter
            while index < size {
                let val = num[index];

                match val {
                    b',' | b';' | b' ' | b'\t' | b'\n' | 0x0C | b'\r' => break,
                    _ => array.push(val),
                }

                index += 1;
            }

            // The input does not consist any valid charecters
            if array.is_empty() {
                break;
            }

            // Convert String to float
            match str::from_utf8(&array).ok().and_then(|s| s.parse::<f32>().ok()) {
                Some(v) => number_list.push(v),
                None => number_list.push(0.0),
            };

            array.clear();

            // For rectangle and circle, stop parsing once we have three
            // and four coordinates respectively
            if points_count > 0 && number_list.len() == points_count {
                break;
            }
        }

        let final_size = number_list.len();

        match target {
            Shape::Circle => {
                if final_size == 3 {
                    if number_list[2] <= 0.0 {
                        None
                    } else {
                        Some(Area::Circle {
                                 left: number_list[0],
                                 top: number_list[1],
                                 radius: number_list[2]
                             })
                    }
                } else {
                    None
                }
            },

            Shape::Rectangle => {
                if final_size == 4 {
                    if number_list[0] > number_list[2] {
                        number_list.swap(0, 2);
                    }

                    if number_list[1] > number_list[3] {
                        number_list.swap(1, 3);
                    }

                    Some(Area::Rectangle {
                             top_left: (number_list[0], number_list[1]),
                             bottom_right: (number_list[2], number_list[3])
                         })
                } else {
                    None
                }
            },

            Shape::Polygon => {
                if final_size >= 6 {
                    if final_size % 2 != 0 {
                        // Drop last element if there are odd number of coordinates
                        number_list.remove(final_size - 1);
                    }
                    Some(Area::Polygon { points: number_list })
                } else {
                    None
                }
            },
        }
    }

    pub fn hit_test(&self, p: Point2D<f32>) -> bool {
        match *self {
            Area::Circle { left, top, radius } => {
                (p.x - left) * (p.x - left) +
                (p.y - top) * (p.y - top) -
                radius * radius <= 0.0
            },

            Area::Rectangle { top_left, bottom_right } => {
                p.x <= bottom_right.0 && p.x >= top_left.0 &&
                p.y <= bottom_right.1 && p.y >= top_left.1
            },

            //TODO polygon hit_test
            _ => false,
        }
    }

    pub fn absolute_coords(&self, p: Point2D<f32>) -> Area {
        match *self {
            Area::Rectangle { top_left, bottom_right } => {
                Area::Rectangle {
                    top_left: (top_left.0 + p.x, top_left.1 + p.y),
                    bottom_right: (bottom_right.0 + p.x, bottom_right.1 + p.y)
                }
            },
            Area::Circle { left, top, radius } => {
                Area::Circle {
                    left: (left + p.x),
                    top: (top + p.y),
                    radius: radius
                }
            },
            Area::Polygon { ref points } => {
//                let new_points = Vec::new();
                let iter = points.iter().enumerate().map(|(index, point)| {
                    match index % 2 {
                        0 => point + p.x as f32,
                        _ => point + p.y as f32,
                    }
                });
                Area::Polygon { points: iter.collect::<Vec<_>>() }
            },
        }
    }
}

#[dom_struct]
pub struct HTMLAreaElement {
    htmlelement: HTMLElement,
    rel_list: MutNullableJS<DOMTokenList>,
}

impl HTMLAreaElement {
    fn new_inherited(local_name: LocalName, prefix: Option<Prefix>, document: &Document) -> HTMLAreaElement {
        HTMLAreaElement {
            htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
            rel_list: Default::default(),
        }
    }

    #[allow(unrooted_must_root)]
    pub fn new(local_name: LocalName,
               prefix: Option<Prefix>,
               document: &Document) -> Root<HTMLAreaElement> {
        Node::reflect_node(box HTMLAreaElement::new_inherited(local_name, prefix, document),
                           document,
                           HTMLAreaElementBinding::Wrap)
    }

    pub fn get_shape_from_coords(&self) -> Option<Area> {
       let elem = self.upcast::<Element>();
       let shape = elem.get_string_attribute(&"shape".into());
       let shp: Shape = match shape.to_lowercase().as_ref() {
           "circle" => Shape::Circle,
           "circ" => Shape::Circle,
           "rectangle" => Shape::Rectangle,
           "rect" => Shape::Rectangle,
           "polygon" => Shape::Rectangle,
           "poly" => Shape::Polygon,
           _ => return None,
        };
        if elem.has_attribute(&"coords".into()) {
            let attribute = elem.get_string_attribute(&"coords".into());
            Area::parse(&attribute, shp)
        } else {
            None
        }
    }
}

impl VirtualMethods for HTMLAreaElement {
    fn super_type(&self) -> Option<&VirtualMethods> {
        Some(self.upcast::<HTMLElement>() as &VirtualMethods)
    }

    fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue {
        match name {
            &local_name!("rel") => AttrValue::from_serialized_tokenlist(value.into()),
            _ => self.super_type().unwrap().parse_plain_attribute(name, value),
        }
    }
}

impl HTMLAreaElementMethods for HTMLAreaElement {
    // https://html.spec.whatwg.org/multipage/#dom-area-rellist
    fn RelList(&self) -> Root<DOMTokenList> {
        self.rel_list.or_init(|| {
            DOMTokenList::new(self.upcast(), &local_name!("rel"))
        })
    }
}

impl Activatable for HTMLAreaElement {
    // https://html.spec.whatwg.org/multipage/#the-area-element:activation-behaviour
    fn as_element(&self) -> &Element {
        self.upcast::<Element>()
    }

    fn is_instance_activatable(&self) -> bool {
        self.as_element().has_attribute(&local_name!("href"))
    }

    fn pre_click_activation(&self) {
    }

    fn canceled_activation(&self) {
    }

    fn implicit_submission(&self, _ctrl_key: bool, _shift_key: bool,
                           _alt_key: bool, _meta_key: bool) {
    }

    fn activation_behavior(&self, _event: &Event, _target: &EventTarget) {
        // Step 1
        let doc = document_from_node(self);
        if !doc.is_fully_active() {
            return;
        }
        // Step 2
        // TODO : We should be choosing a browsing context and navigating to it.
        // Step 3
        let referrer_policy = match self.RelList().Contains("noreferrer".into()) {
            true => Some(ReferrerPolicy::NoReferrer),
            false => None,
        };
        follow_hyperlink(self.upcast::<Element>(), None, referrer_policy);
    }
}
<|endoftext|>


<commit_before>#[cfg(feature = "ndarray_volumes")]
extern crate ndarray;
#[cfg(feature = "ndarray_volumes")]
extern crate nifti;
#[cfg(feature = "ndarray_volumes")]
extern crate tempfile;

#[cfg(feature = "ndarray_volumes")]
mod tests {
    use std::{
        fs::File,
        io::Read,
        ops::{Add, Mul},
        path::{Path, PathBuf},
    };

    use ndarray::{Array, Array2, Axis, Dimension, IxDyn, ShapeBuilder};
    use tempfile::tempdir;

    use nifti::{
        header::MAGIC_CODE_NIP1,
        object::NiftiObject,
        writer::{write_nifti, write_rgb_nifti},
        InMemNiftiObject, IntoNdArray, NiftiHeader, NiftiType,
    };

    fn get_temporary_path(ext: &str) -> PathBuf {
        let dir = tempdir().unwrap();
        let mut path = dir.into_path();
        if !ext.is_empty() {
            path.push(ext);
        }
        path
    }

    pub fn generate_nifti_header(
        dim: [u16; 8],
        scl_slope: f32,
        scl_inter: f32,
        datatype: i16,
    ) -> NiftiHeader {
        NiftiHeader {
            dim,
            datatype,
            bitpix: (NiftiType::Float32.size_of() * 8) as i16,
            magic: *MAGIC_CODE_NIP1,
            scl_slope,
            scl_inter,
            ..NiftiHeader::default()
        }
    }

    fn read_as_ndarray<P, D>(path: P) -> Array<f32, D>
    where
        P: AsRef<Path>,
        D: Dimension,
    {
        let nifti_object = InMemNiftiObject::from_file(path).expect("Nifti file is unreadable.");
        let volume = nifti_object.into_volume();
        let dyn_data = volume.into_ndarray().unwrap();
        dyn_data.into_dimensionality::<D>().unwrap()
    }

    fn test_write_read(arr: &Array<f32, IxDyn>, path: &str) {
        let path = get_temporary_path(path);
        let mut dim = [1; 8];
        dim[0] = arr.ndim() as u16;
        for (i, s) in arr.shape().iter().enumerate() {
            dim[i + 1] = *s as u16;
        }
        let header = generate_nifti_header(dim, 1.0, 0.0, 16);
        write_nifti(&path, &arr, Some(&header)).unwrap();

        let read_nifti: Array2<f32> = read_as_ndarray(path);
        assert!(read_nifti.all_close(&arr, 1e-10));
    }

    fn f_order_array() -> Array<f32, IxDyn> {
        let dim = vec![4, 4];
        let vec = (0..16).map(|x| x as f32).collect();
        Array::from_shape_vec(IxDyn(&dim).f(), vec).unwrap()
    }

    fn c_order_array() -> Array<f32, IxDyn> {
        let dim = vec![4, 4];
        let vec = (0..16).map(|x| x as f32).collect();
        Array::from_shape_vec(IxDyn(&dim), vec).unwrap()
    }

    #[test]
    fn fortran_writing() {
        // Test .nii
        let arr = f_order_array();
        test_write_read(&arr, "test.nii");
        let mut arr = f_order_array();
        arr.invert_axis(Axis(1));
        test_write_read(&arr, "test_non_contiguous.nii");

        // Test .nii.gz
        let arr = f_order_array();
        test_write_read(&arr, "test.nii.gz");
        let mut arr = f_order_array();
        arr.invert_axis(Axis(1));
        test_write_read(&arr, "test_non_contiguous.nii.gz");
    }

    #[test]
    fn c_writing() {
        // Test .nii
        let arr = c_order_array();
        test_write_read(&arr, "test.nii");

        let mut arr = c_order_array();
        arr.invert_axis(Axis(1));
        test_write_read(&arr, "test_non_contiguous.nii");

        // Test .nii.gz
        let arr = c_order_array();
        test_write_read(&arr, "test.nii.gz");

        let mut arr = c_order_array();
        arr.invert_axis(Axis(1));
        test_write_read(&arr, "test_non_contiguous.nii.gz");
    }

    #[test]
    fn header_slope_inter() {
        let arr = f_order_array();
        let slope = 2.2;
        let inter = 101.1;

        let path = get_temporary_path("test_slope_inter.nii");
        let mut dim = [1; 8];
        dim[0] = arr.ndim() as u16;
        for (i, s) in arr.shape().iter().enumerate() {
            dim[i + 1] = *s as u16;
        }
        let header = generate_nifti_header(dim, slope, inter, 16);
        let transformed_data = arr.mul(slope).add(inter);
        write_nifti(&path, &transformed_data, Some(&header)).unwrap();

        let read_nifti: Array2<f32> = read_as_ndarray(path);
        assert!(read_nifti.all_close(&transformed_data, 1e-10));
    }

    #[test]
    fn write_3d_rgb() {
        let mut data = Array::from_elem((3, 3, 3), [0u8, 0u8, 0u8]);
        data[(0, 0, 0)] = [55, 55, 0];
        data[(0, 0, 1)] = [55, 0, 55];
        data[(0, 1, 0)] = [0, 55, 55];

        let path = get_temporary_path("rgb.nii");
        write_rgb_nifti(&path, &data, None).unwrap();

        // Until we are able to read RGB images, we simply compare the bytes of the newly created
        // image to the bytes of the prepared 3D RGB image in ressources/rgb/.
        let mut rgb_bytes = vec![];
        let _ = File::open(path)
            .unwrap()
            .read_to_end(&mut rgb_bytes)
            .unwrap();
        let mut gt_bytes = vec![];
        let _ = File::open("resources/rgb/3D.nii")
            .unwrap()
            .read_to_end(&mut gt_bytes)
            .unwrap();
        assert_eq!(rgb_bytes, gt_bytes);
    }

    #[test]
    fn write_4d_rgb() {
        let mut data = Array::from_elem((3, 3, 3, 2), [0u8, 0u8, 0u8]);
        data[(0, 0, 0, 0)] = [55, 55, 0];
        data[(0, 0, 1, 0)] = [55, 0, 55];
        data[(0, 1, 0, 0)] = [0, 55, 55];
        data[(0, 0, 0, 1)] = [55, 55, 0];
        data[(0, 1, 0, 1)] = [55, 0, 55];
        data[(1, 0, 0, 1)] = [0, 55, 55];

        let path = get_temporary_path("rgb.nii");
        write_rgb_nifti(&path, &data, None).unwrap();

        // Until we are able to read RGB images, we simply compare the bytes of the newly created
        // image to the bytes of the prepared 4D RGB image in ressources/rgb/.
        let mut rgb_bytes = vec![];
        let _ = File::open(path)
            .unwrap()
            .read_to_end(&mut rgb_bytes)
            .unwrap();
        let mut gt_bytes = vec![];
        let _ = File::open("resources/rgb/4D.nii")
            .unwrap()
            .read_to_end(&mut gt_bytes)
            .unwrap();
        assert_eq!(rgb_bytes, gt_bytes);
    }
}
<commit_msg>Add hdr test<commit_after>#[cfg(feature = "ndarray_volumes")]
extern crate ndarray;
#[cfg(feature = "ndarray_volumes")]
extern crate nifti;
#[cfg(feature = "ndarray_volumes")]
extern crate tempfile;

#[cfg(feature = "ndarray_volumes")]
mod tests {
    use std::{
        fs::File,
        io::Read,
        ops::{Add, Mul},
        path::{Path, PathBuf},
    };

    use ndarray::{Array, Array2, Axis, Dimension, IxDyn, ShapeBuilder};
    use tempfile::tempdir;

    use nifti::{
        header::MAGIC_CODE_NIP1,
        object::NiftiObject,
        writer::{write_nifti, write_rgb_nifti},
        InMemNiftiObject, IntoNdArray, NiftiHeader, NiftiType,
    };

    fn get_temporary_path(ext: &str) -> PathBuf {
        let dir = tempdir().unwrap();
        let mut path = dir.into_path();
        if !ext.is_empty() {
            path.push(ext);
        }
        path
    }

    pub fn generate_nifti_header(
        dim: [u16; 8],
        scl_slope: f32,
        scl_inter: f32,
        datatype: i16,
    ) -> NiftiHeader {
        NiftiHeader {
            dim,
            datatype,
            bitpix: (NiftiType::Float32.size_of() * 8) as i16,
            magic: *MAGIC_CODE_NIP1,
            scl_slope,
            scl_inter,
            ..NiftiHeader::default()
        }
    }

    fn read_as_ndarray<P, D>(path: P) -> Array<f32, D>
    where
        P: AsRef<Path>,
        D: Dimension,
    {
        let nifti_object = InMemNiftiObject::from_file(path).expect("Nifti file is unreadable.");
        let volume = nifti_object.into_volume();
        let dyn_data = volume.into_ndarray().unwrap();
        dyn_data.into_dimensionality::<D>().unwrap()
    }

    fn test_write_read(arr: &Array<f32, IxDyn>, path: &str) {
        let path = get_temporary_path(path);
        let mut dim = [1; 8];
        dim[0] = arr.ndim() as u16;
        for (i, s) in arr.shape().iter().enumerate() {
            dim[i + 1] = *s as u16;
        }
        let header = generate_nifti_header(dim, 1.0, 0.0, 16);
        write_nifti(&path, &arr, Some(&header)).unwrap();

        let read_nifti: Array2<f32> = read_as_ndarray(path);
        assert!(read_nifti.all_close(&arr, 1e-10));
    }

    fn f_order_array() -> Array<f32, IxDyn> {
        let dim = vec![4, 4];
        let vec = (0..16).map(|x| x as f32).collect();
        Array::from_shape_vec(IxDyn(&dim).f(), vec).unwrap()
    }

    fn c_order_array() -> Array<f32, IxDyn> {
        let dim = vec![4, 4];
        let vec = (0..16).map(|x| x as f32).collect();
        Array::from_shape_vec(IxDyn(&dim), vec).unwrap()
    }

    #[test]
    fn fortran_writing() {
        // Test .nii
        let arr = f_order_array();
        test_write_read(&arr, "test.nii");
        let mut arr = f_order_array();
        arr.invert_axis(Axis(1));
        test_write_read(&arr, "test_non_contiguous.nii");

        // Test .nii.gz
        let arr = f_order_array();
        test_write_read(&arr, "test.nii.gz");
        let mut arr = f_order_array();
        arr.invert_axis(Axis(1));
        test_write_read(&arr, "test_non_contiguous.nii.gz");
    }

    #[test]
    fn c_writing() {
        // Test .nii
        let arr = c_order_array();
        test_write_read(&arr, "test.nii");

        let mut arr = c_order_array();
        arr.invert_axis(Axis(1));
        test_write_read(&arr, "test_non_contiguous.nii");

        // Test .nii.gz
        let arr = c_order_array();
        test_write_read(&arr, "test.nii.gz");

        let mut arr = c_order_array();
        arr.invert_axis(Axis(1));
        test_write_read(&arr, "test_non_contiguous.nii.gz");
    }

    #[test]
    fn header_slope_inter() {
        let arr = f_order_array();
        let slope = 2.2;
        let inter = 101.1;

        let path = get_temporary_path("test_slope_inter.nii");
        let mut dim = [1; 8];
        dim[0] = arr.ndim() as u16;
        for (i, s) in arr.shape().iter().enumerate() {
            dim[i + 1] = *s as u16;
        }
        let header = generate_nifti_header(dim, slope, inter, 16);
        let transformed_data = arr.mul(slope).add(inter);
        write_nifti(&path, &transformed_data, Some(&header)).unwrap();

        let read_nifti: Array2<f32> = read_as_ndarray(path);
        assert!(read_nifti.all_close(&transformed_data, 1e-10));
    }

    #[test]
    fn write_hdr_standard() {
        let mut data = Array::zeros((10, 11, 12));
        data[(5, 0, 0)] = 1.0;
        data[(6, 0, 0)] = 2.0;

        for fname in &["3d.hdr", "3d.hdr.gz"] {
            let path = get_temporary_path(fname);
            write_nifti(&path, &data, None).unwrap();
            let data_read = read_as_ndarray(path);
            assert_eq!(data, data_read);
        }
    }

    #[test]
    fn write_3d_rgb() {
        let mut data = Array::from_elem((3, 3, 3), [0u8, 0u8, 0u8]);
        data[(0, 0, 0)] = [55, 55, 0];
        data[(0, 0, 1)] = [55, 0, 55];
        data[(0, 1, 0)] = [0, 55, 55];

        let path = get_temporary_path("rgb.nii");
        write_rgb_nifti(&path, &data, None).unwrap();

        // Until we are able to read RGB images, we simply compare the bytes of the newly created
        // image to the bytes of the prepared 3D RGB image in ressources/rgb/.
        let mut rgb_bytes = vec![];
        let _ = File::open(path)
            .unwrap()
            .read_to_end(&mut rgb_bytes)
            .unwrap();
        let mut gt_bytes = vec![];
        let _ = File::open("resources/rgb/3D.nii")
            .unwrap()
            .read_to_end(&mut gt_bytes)
            .unwrap();
        assert_eq!(rgb_bytes, gt_bytes);
    }

    #[test]
    fn write_4d_rgb() {
        let mut data = Array::from_elem((3, 3, 3, 2), [0u8, 0u8, 0u8]);
        data[(0, 0, 0, 0)] = [55, 55, 0];
        data[(0, 0, 1, 0)] = [55, 0, 55];
        data[(0, 1, 0, 0)] = [0, 55, 55];
        data[(0, 0, 0, 1)] = [55, 55, 0];
        data[(0, 1, 0, 1)] = [55, 0, 55];
        data[(1, 0, 0, 1)] = [0, 55, 55];

        let path = get_temporary_path("rgb.nii");
        write_rgb_nifti(&path, &data, None).unwrap();

        // Until we are able to read RGB images, we simply compare the bytes of the newly created
        // image to the bytes of the prepared 4D RGB image in ressources/rgb/.
        let mut rgb_bytes = vec![];
        let _ = File::open(path)
            .unwrap()
            .read_to_end(&mut rgb_bytes)
            .unwrap();
        let mut gt_bytes = vec![];
        let _ = File::open("resources/rgb/4D.nii")
            .unwrap()
            .read_to_end(&mut gt_bytes)
            .unwrap();
        assert_eq!(rgb_bytes, gt_bytes);
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
#![feature(plugin)]
#![plugin(rand_macros)]

extern crate rand;

use rand::Rng;

#[derive_Rand]
struct Foo {
    x: u8,
    y: isize
}

#[derive_Rand]
enum Bar {
    X(char),
    Y(f64)
}

#[test]
fn smoke() {
    let mut rng = rand::XorShiftRng::new_unseeded();

    // check nothing horrible happens internally:
    for _ in 0..100 {
        let _: Foo = rng.gen();
        let _: Bar = rng.gen();
    }
}
<commit_msg>Update to rust master<commit_after>#![allow(dead_code)]
#![feature(plugin, custom_derive)]
#![plugin(rand_macros)]

extern crate rand;

use rand::Rng;

#[derive_Rand]
struct Foo {
    x: u8,
    y: isize
}

#[derive_Rand]
enum Bar {
    X(char),
    Y(f64)
}

#[test]
fn smoke() {
    let mut rng = rand::XorShiftRng::new_unseeded();

    // check nothing horrible happens internally:
    for _ in 0..100 {
        let _: Foo = rng.gen();
        let _: Bar = rng.gen();
    }
}
<|endoftext|>