<commit_before>// APIs in FreeBSD 13 that have changed since 11.

pub type nlink_t = u64;
pub type dev_t = u64;
pub type ino_t = ::c_ulong;
pub type shmatt_t = ::c_uint;

s! {
    pub struct shmid_ds {
        pub shm_perm: ::ipc_perm,
        pub shm_segsz: ::size_t,
        pub shm_lpid: ::pid_t,
        pub shm_cpid: ::pid_t,
        pub shm_nattch: ::shmatt_t,
        pub shm_atime: ::time_t,
        pub shm_dtime: ::time_t,
        pub shm_ctime: ::time_t,
    }

    pub struct kevent {
        pub ident: ::uintptr_t,
        pub filter: ::c_short,
        pub flags: ::c_ushort,
        pub fflags: ::c_uint,
        pub data: ::intptr_t,
        pub udata: *mut ::c_void,
        pub ext: [u64; 4],
    }
}

s_no_extra_traits! {
    pub struct dirent {
        pub d_fileno: ::ino_t,
        pub d_off: ::off_t,
        pub d_reclen: u16,
        pub d_type: u8,
        d_pad0: u8,
        pub d_namlen: u16,
        d_pad1: u16,
        pub d_name: [::c_char; 256],
    }

    pub struct statfs {
        pub f_version: u32,
        pub f_type: u32,
        pub f_flags: u64,
        pub f_bsize: u64,
        pub f_iosize: u64,
        pub f_blocks: u64,
        pub f_bfree: u64,
        pub f_bavail: i64,
        pub f_files: u64,
        pub f_ffree: i64,
        pub f_syncwrites: u64,
        pub f_asyncwrites: u64,
        pub f_syncreads: u64,
        pub f_asyncreads: u64,
        f_spare: [u64; 10],
        pub f_namemax: u32,
        pub f_owner: ::uid_t,
        pub f_fsid: ::fsid_t,
        f_charspare: [::c_char; 80],
        pub f_fstypename: [::c_char; 16],
        pub f_mntfromname: [::c_char; 1024],
        pub f_mntonname: [::c_char; 1024],
    }
}

cfg_if! {
    if #[cfg(feature = "extra_traits")] {
        impl PartialEq for statfs {
            fn eq(&self, other: &statfs) -> bool {
                self.f_version == other.f_version
                    && self.f_type == other.f_type
                    && self.f_flags == other.f_flags
                    && self.f_bsize == other.f_bsize
                    && self.f_iosize == other.f_iosize
                    && self.f_blocks == other.f_blocks
                    && self.f_bfree == other.f_bfree
                    && self.f_bavail == other.f_bavail
                    && self.f_files == other.f_files
                    && self.f_ffree == other.f_ffree
                    && self.f_syncwrites == other.f_syncwrites
                    && self.f_asyncwrites == other.f_asyncwrites
                    && self.f_syncreads == other.f_syncreads
                    && self.f_asyncreads == other.f_asyncreads
                    && self.f_namemax == other.f_namemax
                    && self.f_owner == other.f_owner
                    && self.f_fsid == other.f_fsid
                    && self.f_fstypename == other.f_fstypename
                    && self
                    .f_mntfromname
                    .iter()
                    .zip(other.f_mntfromname.iter())
                    .all(|(a,b)| a == b)
                    && self
                    .f_mntonname
                    .iter()
                    .zip(other.f_mntonname.iter())
                    .all(|(a,b)| a == b)
            }
        }
        impl Eq for statfs {}
        impl ::fmt::Debug for statfs {
            fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
                f.debug_struct("statfs")
                    .field("f_bsize", &self.f_bsize)
                    .field("f_iosize", &self.f_iosize)
                    .field("f_blocks", &self.f_blocks)
                    .field("f_bfree", &self.f_bfree)
                    .field("f_bavail", &self.f_bavail)
                    .field("f_files", &self.f_files)
                    .field("f_ffree", &self.f_ffree)
                    .field("f_syncwrites", &self.f_syncwrites)
                    .field("f_asyncwrites", &self.f_asyncwrites)
                    .field("f_syncreads", &self.f_syncreads)
                    .field("f_asyncreads", &self.f_asyncreads)
                    .field("f_namemax", &self.f_namemax)
                    .field("f_owner", &self.f_owner)
                    .field("f_fsid", &self.f_fsid)
                    .field("f_fstypename", &self.f_fstypename)
                    .field("f_mntfromname", &&self.f_mntfromname[..])
                    .field("f_mntonname", &&self.f_mntonname[..])
                    .finish()
            }
        }
        impl ::hash::Hash for statfs {
            fn hash<H: ::hash::Hasher>(&self, state: &mut H) {
                self.f_version.hash(state);
                self.f_type.hash(state);
                self.f_flags.hash(state);
                self.f_bsize.hash(state);
                self.f_iosize.hash(state);
                self.f_blocks.hash(state);
                self.f_bfree.hash(state);
                self.f_bavail.hash(state);
                self.f_files.hash(state);
                self.f_ffree.hash(state);
                self.f_syncwrites.hash(state);
                self.f_asyncwrites.hash(state);
                self.f_syncreads.hash(state);
                self.f_asyncreads.hash(state);
                self.f_namemax.hash(state);
                self.f_owner.hash(state);
                self.f_fsid.hash(state);
                self.f_charspare.hash(state);
                self.f_fstypename.hash(state);
                self.f_mntfromname.hash(state);
                self.f_mntonname.hash(state);
            }
        }

        impl PartialEq for dirent {
            fn eq(&self, other: &dirent) -> bool {
                self.d_fileno == other.d_fileno
                    && self.d_off == other.d_off
                    && self.d_reclen == other.d_reclen
                    && self.d_type == other.d_type
                    && self.d_namlen == other.d_namlen
                    && self
                    .d_name[..self.d_namlen as _]
                    .iter()
                    .zip(other.d_name.iter())
                    .all(|(a,b)| a == b)
            }
        }
        impl Eq for dirent {}
        impl ::fmt::Debug for dirent {
            fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
                f.debug_struct("dirent")
                    .field("d_fileno", &self.d_fileno)
                    .field("d_off", &self.d_off)
                    .field("d_reclen", &self.d_reclen)
                    .field("d_type", &self.d_type)
                    .field("d_namlen", &self.d_namlen)
                    .field("d_name", &&self.d_name[..self.d_namlen as _])
                    .finish()
            }
        }
        impl ::hash::Hash for dirent {
            fn hash<H: ::hash::Hasher>(&self, state: &mut H) {
                self.d_fileno.hash(state);
                self.d_off.hash(state);
                self.d_reclen.hash(state);
                self.d_type.hash(state);
                self.d_namlen.hash(state);
                self.d_name[..self.d_namlen as _].hash(state);
            }
        }
    }
}

pub const F_ADD_SEALS: ::c_int = 19;
pub const F_GET_SEALS: ::c_int = 20;
pub const F_SEAL_SEAL: ::c_int = 0x0001;
pub const F_SEAL_SHRINK: ::c_int = 0x0002;
pub const F_SEAL_GROW: ::c_int = 0x0004;
pub const F_SEAL_WRITE: ::c_int = 0x0008;

pub const GRND_NONBLOCK: ::c_uint = 0x1;
pub const GRND_RANDOM: ::c_uint = 0x2;

pub const RAND_MAX: ::c_int = 0x7fff_ffff;

pub const SO_DOMAIN: ::c_int = 0x1019;

pub const EINTEGRITY: ::c_int = 97;
pub const ELAST: ::c_int = 97;
pub const GRND_INSECURE: ::c_uint = 0x4;

extern "C" {
    pub fn aio_readv(aiocbp: *mut ::aiocb) -> ::c_int;
    pub fn aio_writev(aiocbp: *mut ::aiocb) -> ::c_int;
    pub fn setgrent();
    pub fn mprotect(addr: *mut ::c_void, len: ::size_t, prot: ::c_int) -> ::c_int;
    pub fn freelocale(loc: ::locale_t);
    pub fn msgrcv(
        msqid: ::c_int,
        msgp: *mut ::c_void,
        msgsz: ::size_t,
        msgtyp: ::c_long,
        msgflg: ::c_int,
    ) -> ::ssize_t;
    pub fn clock_nanosleep(
        clk_id: ::clockid_t,
        flags: ::c_int,
        rqtp: *const ::timespec,
        rmtp: *mut ::timespec,
    ) -> ::c_int;

    pub fn fdatasync(fd: ::c_int) -> ::c_int;

    pub fn getrandom(buf: *mut ::c_void, buflen: ::size_t, flags: ::c_uint) -> ::ssize_t;
    pub fn getentropy(buf: *mut ::c_void, buflen: ::size_t) -> ::c_int;
    pub fn elf_aux_info(aux: ::c_int, buf: *mut ::c_void, buflen: ::c_int) -> ::c_int;
    pub fn setproctitle_fast(fmt: *const ::c_char, ...);
    pub fn timingsafe_bcmp(a: *const ::c_void, b: *const ::c_void, len: ::size_t) -> ::c_int;
    pub fn timingsafe_memcmp(a: *const ::c_void, b: *const ::c_void, len: ::size_t) -> ::c_int;
}

cfg_if! {
    if #[cfg(any(target_arch = "x86_64",
                 target_arch = "aarch64"))] {
        mod b64;
        pub use self::b64::*;
    }
}
<commit_msg>Auto merge of #2320 - devnexen:fbsd_sockcred2, r=JohnTitor<commit_after>// APIs in FreeBSD 13 that have changed since 11.

pub type nlink_t = u64;
pub type dev_t = u64;
pub type ino_t = ::c_ulong;
pub type shmatt_t = ::c_uint;

s! {
    pub struct shmid_ds {
        pub shm_perm: ::ipc_perm,
        pub shm_segsz: ::size_t,
        pub shm_lpid: ::pid_t,
        pub shm_cpid: ::pid_t,
        pub shm_nattch: ::shmatt_t,
        pub shm_atime: ::time_t,
        pub shm_dtime: ::time_t,
        pub shm_ctime: ::time_t,
    }

    pub struct kevent {
        pub ident: ::uintptr_t,
        pub filter: ::c_short,
        pub flags: ::c_ushort,
        pub fflags: ::c_uint,
        pub data: ::intptr_t,
        pub udata: *mut ::c_void,
        pub ext: [u64; 4],
    }

    pub struct sockcred2 {
        pub sc_version: ::c_int,
        pub sc_pid: ::pid_t,
        pub sc_uid: ::uid_t,
        pub sc_euid: ::uid_t,
        pub sc_gid: ::gid_t,
        pub sc_egid: ::gid_t,
        pub sc_ngroups: ::c_int,
        pub sc_groups: [::gid_t; 1],
    }
}

s_no_extra_traits! {
    pub struct dirent {
        pub d_fileno: ::ino_t,
        pub d_off: ::off_t,
        pub d_reclen: u16,
        pub d_type: u8,
        d_pad0: u8,
        pub d_namlen: u16,
        d_pad1: u16,
        pub d_name: [::c_char; 256],
    }

    pub struct statfs {
        pub f_version: u32,
        pub f_type: u32,
        pub f_flags: u64,
        pub f_bsize: u64,
        pub f_iosize: u64,
        pub f_blocks: u64,
        pub f_bfree: u64,
        pub f_bavail: i64,
        pub f_files: u64,
        pub f_ffree: i64,
        pub f_syncwrites: u64,
        pub f_asyncwrites: u64,
        pub f_syncreads: u64,
        pub f_asyncreads: u64,
        f_spare: [u64; 10],
        pub f_namemax: u32,
        pub f_owner: ::uid_t,
        pub f_fsid: ::fsid_t,
        f_charspare: [::c_char; 80],
        pub f_fstypename: [::c_char; 16],
        pub f_mntfromname: [::c_char; 1024],
        pub f_mntonname: [::c_char; 1024],
    }
}

cfg_if! {
    if #[cfg(feature = "extra_traits")] {
        impl PartialEq for statfs {
            fn eq(&self, other: &statfs) -> bool {
                self.f_version == other.f_version
                    && self.f_type == other.f_type
                    && self.f_flags == other.f_flags
                    && self.f_bsize == other.f_bsize
                    && self.f_iosize == other.f_iosize
                    && self.f_blocks == other.f_blocks
                    && self.f_bfree == other.f_bfree
                    && self.f_bavail == other.f_bavail
                    && self.f_files == other.f_files
                    && self.f_ffree == other.f_ffree
                    && self.f_syncwrites == other.f_syncwrites
                    && self.f_asyncwrites == other.f_asyncwrites
                    && self.f_syncreads == other.f_syncreads
                    && self.f_asyncreads == other.f_asyncreads
                    && self.f_namemax == other.f_namemax
                    && self.f_owner == other.f_owner
                    && self.f_fsid == other.f_fsid
                    && self.f_fstypename == other.f_fstypename
                    && self
                    .f_mntfromname
                    .iter()
                    .zip(other.f_mntfromname.iter())
                    .all(|(a,b)| a == b)
                    && self
                    .f_mntonname
                    .iter()
                    .zip(other.f_mntonname.iter())
                    .all(|(a,b)| a == b)
            }
        }
        impl Eq for statfs {}
        impl ::fmt::Debug for statfs {
            fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
                f.debug_struct("statfs")
                    .field("f_bsize", &self.f_bsize)
                    .field("f_iosize", &self.f_iosize)
                    .field("f_blocks", &self.f_blocks)
                    .field("f_bfree", &self.f_bfree)
                    .field("f_bavail", &self.f_bavail)
                    .field("f_files", &self.f_files)
                    .field("f_ffree", &self.f_ffree)
                    .field("f_syncwrites", &self.f_syncwrites)
                    .field("f_asyncwrites", &self.f_asyncwrites)
                    .field("f_syncreads", &self.f_syncreads)
                    .field("f_asyncreads", &self.f_asyncreads)
                    .field("f_namemax", &self.f_namemax)
                    .field("f_owner", &self.f_owner)
                    .field("f_fsid", &self.f_fsid)
                    .field("f_fstypename", &self.f_fstypename)
                    .field("f_mntfromname", &&self.f_mntfromname[..])
                    .field("f_mntonname", &&self.f_mntonname[..])
                    .finish()
            }
        }
        impl ::hash::Hash for statfs {
            fn hash<H: ::hash::Hasher>(&self, state: &mut H) {
                self.f_version.hash(state);
                self.f_type.hash(state);
                self.f_flags.hash(state);
                self.f_bsize.hash(state);
                self.f_iosize.hash(state);
                self.f_blocks.hash(state);
                self.f_bfree.hash(state);
                self.f_bavail.hash(state);
                self.f_files.hash(state);
                self.f_ffree.hash(state);
                self.f_syncwrites.hash(state);
                self.f_asyncwrites.hash(state);
                self.f_syncreads.hash(state);
                self.f_asyncreads.hash(state);
                self.f_namemax.hash(state);
                self.f_owner.hash(state);
                self.f_fsid.hash(state);
                self.f_charspare.hash(state);
                self.f_fstypename.hash(state);
                self.f_mntfromname.hash(state);
                self.f_mntonname.hash(state);
            }
        }

        impl PartialEq for dirent {
            fn eq(&self, other: &dirent) -> bool {
                self.d_fileno == other.d_fileno
                    && self.d_off == other.d_off
                    && self.d_reclen == other.d_reclen
                    && self.d_type == other.d_type
                    && self.d_namlen == other.d_namlen
                    && self
                    .d_name[..self.d_namlen as _]
                    .iter()
                    .zip(other.d_name.iter())
                    .all(|(a,b)| a == b)
            }
        }
        impl Eq for dirent {}
        impl ::fmt::Debug for dirent {
            fn fmt(&self, f: &mut ::fmt::Formatter) -> ::fmt::Result {
                f.debug_struct("dirent")
                    .field("d_fileno", &self.d_fileno)
                    .field("d_off", &self.d_off)
                    .field("d_reclen", &self.d_reclen)
                    .field("d_type", &self.d_type)
                    .field("d_namlen", &self.d_namlen)
                    .field("d_name", &&self.d_name[..self.d_namlen as _])
                    .finish()
            }
        }
        impl ::hash::Hash for dirent {
            fn hash<H: ::hash::Hasher>(&self, state: &mut H) {
                self.d_fileno.hash(state);
                self.d_off.hash(state);
                self.d_reclen.hash(state);
                self.d_type.hash(state);
                self.d_namlen.hash(state);
                self.d_name[..self.d_namlen as _].hash(state);
            }
        }
    }
}

pub const F_ADD_SEALS: ::c_int = 19;
pub const F_GET_SEALS: ::c_int = 20;
pub const F_SEAL_SEAL: ::c_int = 0x0001;
pub const F_SEAL_SHRINK: ::c_int = 0x0002;
pub const F_SEAL_GROW: ::c_int = 0x0004;
pub const F_SEAL_WRITE: ::c_int = 0x0008;

pub const GRND_NONBLOCK: ::c_uint = 0x1;
pub const GRND_RANDOM: ::c_uint = 0x2;

pub const RAND_MAX: ::c_int = 0x7fff_ffff;

pub const SO_DOMAIN: ::c_int = 0x1019;

pub const EINTEGRITY: ::c_int = 97;
pub const ELAST: ::c_int = 97;
pub const GRND_INSECURE: ::c_uint = 0x4;

pub const LOCAL_CREDS_PERSISTENT: ::c_int = 3;
pub const SCM_CREDS2: ::c_int = 0x08;

f! {
    pub fn SOCKCRED2SIZE(ngrps: usize) -> usize {
        let ngrps = if ngrps > 0 {
            ngrps - 1
        } else {
            0
        };
        ::mem::size_of::<sockcred2>() + ::mem::size_of::<::gid_t>() * ngrps
    }
}

extern "C" {
    pub fn aio_readv(aiocbp: *mut ::aiocb) -> ::c_int;
    pub fn aio_writev(aiocbp: *mut ::aiocb) -> ::c_int;
    pub fn setgrent();
    pub fn mprotect(addr: *mut ::c_void, len: ::size_t, prot: ::c_int) -> ::c_int;
    pub fn freelocale(loc: ::locale_t);
    pub fn msgrcv(
        msqid: ::c_int,
        msgp: *mut ::c_void,
        msgsz: ::size_t,
        msgtyp: ::c_long,
        msgflg: ::c_int,
    ) -> ::ssize_t;
    pub fn clock_nanosleep(
        clk_id: ::clockid_t,
        flags: ::c_int,
        rqtp: *const ::timespec,
        rmtp: *mut ::timespec,
    ) -> ::c_int;

    pub fn fdatasync(fd: ::c_int) -> ::c_int;

    pub fn getrandom(buf: *mut ::c_void, buflen: ::size_t, flags: ::c_uint) -> ::ssize_t;
    pub fn getentropy(buf: *mut ::c_void, buflen: ::size_t) -> ::c_int;
    pub fn elf_aux_info(aux: ::c_int, buf: *mut ::c_void, buflen: ::c_int) -> ::c_int;
    pub fn setproctitle_fast(fmt: *const ::c_char, ...);
    pub fn timingsafe_bcmp(a: *const ::c_void, b: *const ::c_void, len: ::size_t) -> ::c_int;
    pub fn timingsafe_memcmp(a: *const ::c_void, b: *const ::c_void, len: ::size_t) -> ::c_int;
}

cfg_if! {
    if #[cfg(any(target_arch = "x86_64",
                 target_arch = "aarch64"))] {
        mod b64;
        pub use self::b64::*;
    }
}
<|endoftext|>


<commit_before><commit_msg>Fix URI<commit_after><|endoftext|>


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

use dom::attr::AttrHelpers;
use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding;
use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
use dom::bindings::codegen::InheritTypes::{NodeCast, ElementCast};
use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLIFrameElementDerived};
use dom::bindings::js::{JSRef, Temporary, OptionalRootable};
use dom::bindings::trace::Traceable;
use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::Document;
use dom::element::{HTMLIFrameElementTypeId, Element};
use dom::element::AttributeHandlers;
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, NodeHelpers, ElementNodeTypeId, window_from_node};
use dom::virtualmethods::VirtualMethods;
use dom::window::Window;
use page::IterablePage;

use servo_msg::constellation_msg::{PipelineId, SubpageId};
use servo_msg::constellation_msg::{IFrameSandboxed, IFrameUnsandboxed};
use servo_msg::constellation_msg::{ConstellationChan, LoadIframeUrlMsg};
use servo_util::atom::Atom;
use servo_util::namespace::Null;
use servo_util::str::DOMString;

use std::ascii::StrAsciiExt;
use std::cell::Cell;
use url::{Url, UrlParser};

enum SandboxAllowance {
    AllowNothing = 0x00,
    AllowSameOrigin = 0x01,
    AllowTopNavigation = 0x02,
    AllowForms = 0x04,
    AllowScripts = 0x08,
    AllowPointerLock = 0x10,
    AllowPopups = 0x20
}

#[deriving(Encodable)]
pub struct HTMLIFrameElement {
    pub htmlelement: HTMLElement,
    pub size: Traceable<Cell<Option<IFrameSize>>>,
    pub sandbox: Traceable<Cell<Option<u8>>>,
}

impl HTMLIFrameElementDerived for EventTarget {
    fn is_htmliframeelement(&self) -> bool {
       self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLIFrameElementTypeId))
    }
}

#[deriving(Encodable)]
pub struct IFrameSize {
    pub pipeline_id: PipelineId,
    pub subpage_id: SubpageId,
}

pub trait HTMLIFrameElementHelpers {
    fn is_sandboxed(&self) -> bool;
    fn get_url(&self) -> Option<Url>;
    /// http://www.whatwg.org/html/#process-the-iframe-attributes
    fn process_the_iframe_attributes(&self);
}

impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> {
    fn is_sandboxed(&self) -> bool {
        self.sandbox.deref().get().is_some()
    }

    fn get_url(&self) -> Option<Url> {
        let element: &JSRef<Element> = ElementCast::from_ref(self);
        element.get_attribute(Null, "src").root().and_then(|src| {
            let window = window_from_node(self).root();
            UrlParser::new().base_url(&window.deref().page().get_url())
                .parse(src.deref().value().as_slice()).ok()
        })
    }

    fn process_the_iframe_attributes(&self) {
        match self.get_url() {
            Some(url) => {
                let sandboxed = if self.is_sandboxed() {
                    IFrameSandboxed
                } else {
                    IFrameUnsandboxed
                };

                // Subpage Id
                let window = window_from_node(self).root();
                let page = window.deref().page();
                let subpage_id = page.get_next_subpage_id();

                self.deref().size.deref().set(Some(IFrameSize {
                    pipeline_id: page.id,
                    subpage_id: subpage_id,
                }));

                let ConstellationChan(ref chan) = *page.constellation_chan.deref();
                chan.send(LoadIframeUrlMsg(url, page.id, subpage_id, sandboxed));
            }
            _ => ()
        }
    }
}

impl HTMLIFrameElement {
    pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLIFrameElement {
        HTMLIFrameElement {
            htmlelement: HTMLElement::new_inherited(HTMLIFrameElementTypeId, localName, document),
            size: Traceable::new(Cell::new(None)),
            sandbox: Traceable::new(Cell::new(None)),
        }
    }

    pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLIFrameElement> {
        let element = HTMLIFrameElement::new_inherited(localName, document);
        Node::reflect_node(box element, document, HTMLIFrameElementBinding::Wrap)
    }
}

impl<'a> HTMLIFrameElementMethods for JSRef<'a, HTMLIFrameElement> {
    fn Src(&self) -> DOMString {
        let element: &JSRef<Element> = ElementCast::from_ref(self);
        element.get_string_attribute("src")
    }

    fn SetSrc(&self, src: DOMString) {
        let element: &JSRef<Element> = ElementCast::from_ref(self);
        element.set_url_attribute("src", src)
    }

    fn Sandbox(&self) -> DOMString {
        let element: &JSRef<Element> = ElementCast::from_ref(self);
        element.get_string_attribute("sandbox")
    }

    fn SetSandbox(&self, sandbox: DOMString) {
        let element: &JSRef<Element> = ElementCast::from_ref(self);
        element.set_string_attribute("sandbox", sandbox);
    }

    fn GetContentWindow(&self) -> Option<Temporary<Window>> {
        self.size.deref().get().and_then(|size| {
            let window = window_from_node(self).root();
            let children = &*window.deref().page.children.deref().borrow();
            let child = children.iter().find(|child| {
                child.subpage_id.unwrap() == size.subpage_id
            });
            child.and_then(|page| {
                page.frame.deref().borrow().as_ref().map(|frame| {
                    Temporary::new(frame.window.clone())
                })
            })
        })
    }
}

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

    fn after_set_attr(&self, name: &Atom, value: DOMString) {
        match self.super_type() {
            Some(ref s) => s.after_set_attr(name, value.clone()),
            _ => (),
        }

        if "sandbox" == name.as_slice() {
            let mut modes = AllowNothing as u8;
            for word in value.as_slice().split(' ') {
                modes |= match word.to_ascii_lower().as_slice() {
                    "allow-same-origin" => AllowSameOrigin,
                    "allow-forms" => AllowForms,
                    "allow-pointer-lock" => AllowPointerLock,
                    "allow-popups" => AllowPopups,
                    "allow-scripts" => AllowScripts,
                    "allow-top-navigation" => AllowTopNavigation,
                    _ => AllowNothing
                } as u8;
            }
            self.deref().sandbox.deref().set(Some(modes));
        }

        if "src" == name.as_slice() {
            let node: &JSRef<Node> = NodeCast::from_ref(self);
            if node.is_in_doc() {
                self.process_the_iframe_attributes()
            }
        }
    }

    fn before_remove_attr(&self, name: &Atom, value: DOMString) {
        match self.super_type() {
            Some(ref s) => s.before_remove_attr(name, value),
            _ => (),
        }

        if "sandbox" == name.as_slice() {
            self.deref().sandbox.deref().set(None);
        }
    }

    fn bind_to_tree(&self, tree_in_doc: bool) {
        match self.super_type() {
            Some(ref s) => s.bind_to_tree(tree_in_doc),
            _ => (),
        }

        if tree_in_doc {
            self.process_the_iframe_attributes();
        }
    }
}

impl Reflectable for HTMLIFrameElement {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        self.htmlelement.reflector()
    }
}
<commit_msg>Make all iframes attached to a document default to about:blank.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::attr::AttrHelpers;
use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding;
use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
use dom::bindings::codegen::InheritTypes::{NodeCast, ElementCast};
use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLIFrameElementDerived};
use dom::bindings::js::{JSRef, Temporary, OptionalRootable};
use dom::bindings::trace::Traceable;
use dom::bindings::utils::{Reflectable, Reflector};
use dom::document::Document;
use dom::element::{HTMLIFrameElementTypeId, Element};
use dom::element::AttributeHandlers;
use dom::eventtarget::{EventTarget, NodeTargetTypeId};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, NodeHelpers, ElementNodeTypeId, window_from_node};
use dom::virtualmethods::VirtualMethods;
use dom::window::Window;
use page::IterablePage;

use servo_msg::constellation_msg::{PipelineId, SubpageId};
use servo_msg::constellation_msg::{IFrameSandboxed, IFrameUnsandboxed};
use servo_msg::constellation_msg::{ConstellationChan, LoadIframeUrlMsg};
use servo_util::atom::Atom;
use servo_util::namespace::Null;
use servo_util::str::DOMString;

use std::ascii::StrAsciiExt;
use std::cell::Cell;
use url::{Url, UrlParser};

enum SandboxAllowance {
    AllowNothing = 0x00,
    AllowSameOrigin = 0x01,
    AllowTopNavigation = 0x02,
    AllowForms = 0x04,
    AllowScripts = 0x08,
    AllowPointerLock = 0x10,
    AllowPopups = 0x20
}

#[deriving(Encodable)]
pub struct HTMLIFrameElement {
    pub htmlelement: HTMLElement,
    pub size: Traceable<Cell<Option<IFrameSize>>>,
    pub sandbox: Traceable<Cell<Option<u8>>>,
}

impl HTMLIFrameElementDerived for EventTarget {
    fn is_htmliframeelement(&self) -> bool {
       self.type_id == NodeTargetTypeId(ElementNodeTypeId(HTMLIFrameElementTypeId))
    }
}

#[deriving(Encodable)]
pub struct IFrameSize {
    pub pipeline_id: PipelineId,
    pub subpage_id: SubpageId,
}

pub trait HTMLIFrameElementHelpers {
    fn is_sandboxed(&self) -> bool;
    fn get_url(&self) -> Option<Url>;
    /// http://www.whatwg.org/html/#process-the-iframe-attributes
    fn process_the_iframe_attributes(&self);
}

impl<'a> HTMLIFrameElementHelpers for JSRef<'a, HTMLIFrameElement> {
    fn is_sandboxed(&self) -> bool {
        self.sandbox.deref().get().is_some()
    }

    fn get_url(&self) -> Option<Url> {
        let element: &JSRef<Element> = ElementCast::from_ref(self);
        element.get_attribute(Null, "src").root().and_then(|src| {
            let window = window_from_node(self).root();
            UrlParser::new().base_url(&window.deref().page().get_url())
                .parse(src.deref().value().as_slice()).ok()
        })
    }

    fn process_the_iframe_attributes(&self) {
        let url = match self.get_url() {
            Some(url) => url.clone(),
            None => Url::parse("about:blank").unwrap(),
        };

        let sandboxed = if self.is_sandboxed() {
            IFrameSandboxed
        } else {
            IFrameUnsandboxed
        };

        // Subpage Id
        let window = window_from_node(self).root();
        let page = window.deref().page();
        let subpage_id = page.get_next_subpage_id();

        self.deref().size.deref().set(Some(IFrameSize {
            pipeline_id: page.id,
            subpage_id: subpage_id,
        }));

        let ConstellationChan(ref chan) = *page.constellation_chan.deref();
        chan.send(LoadIframeUrlMsg(url, page.id, subpage_id, sandboxed));
    }
}

impl HTMLIFrameElement {
    pub fn new_inherited(localName: DOMString, document: &JSRef<Document>) -> HTMLIFrameElement {
        HTMLIFrameElement {
            htmlelement: HTMLElement::new_inherited(HTMLIFrameElementTypeId, localName, document),
            size: Traceable::new(Cell::new(None)),
            sandbox: Traceable::new(Cell::new(None)),
        }
    }

    pub fn new(localName: DOMString, document: &JSRef<Document>) -> Temporary<HTMLIFrameElement> {
        let element = HTMLIFrameElement::new_inherited(localName, document);
        Node::reflect_node(box element, document, HTMLIFrameElementBinding::Wrap)
    }
}

impl<'a> HTMLIFrameElementMethods for JSRef<'a, HTMLIFrameElement> {
    fn Src(&self) -> DOMString {
        let element: &JSRef<Element> = ElementCast::from_ref(self);
        element.get_string_attribute("src")
    }

    fn SetSrc(&self, src: DOMString) {
        let element: &JSRef<Element> = ElementCast::from_ref(self);
        element.set_url_attribute("src", src)
    }

    fn Sandbox(&self) -> DOMString {
        let element: &JSRef<Element> = ElementCast::from_ref(self);
        element.get_string_attribute("sandbox")
    }

    fn SetSandbox(&self, sandbox: DOMString) {
        let element: &JSRef<Element> = ElementCast::from_ref(self);
        element.set_string_attribute("sandbox", sandbox);
    }

    fn GetContentWindow(&self) -> Option<Temporary<Window>> {
        self.size.deref().get().and_then(|size| {
            let window = window_from_node(self).root();
            let children = &*window.deref().page.children.deref().borrow();
            let child = children.iter().find(|child| {
                child.subpage_id.unwrap() == size.subpage_id
            });
            child.and_then(|page| {
                page.frame.deref().borrow().as_ref().map(|frame| {
                    Temporary::new(frame.window.clone())
                })
            })
        })
    }
}

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

    fn after_set_attr(&self, name: &Atom, value: DOMString) {
        match self.super_type() {
            Some(ref s) => s.after_set_attr(name, value.clone()),
            _ => (),
        }

        if "sandbox" == name.as_slice() {
            let mut modes = AllowNothing as u8;
            for word in value.as_slice().split(' ') {
                modes |= match word.to_ascii_lower().as_slice() {
                    "allow-same-origin" => AllowSameOrigin,
                    "allow-forms" => AllowForms,
                    "allow-pointer-lock" => AllowPointerLock,
                    "allow-popups" => AllowPopups,
                    "allow-scripts" => AllowScripts,
                    "allow-top-navigation" => AllowTopNavigation,
                    _ => AllowNothing
                } as u8;
            }
            self.deref().sandbox.deref().set(Some(modes));
        }

        if "src" == name.as_slice() {
            let node: &JSRef<Node> = NodeCast::from_ref(self);
            if node.is_in_doc() {
                self.process_the_iframe_attributes()
            }
        }
    }

    fn before_remove_attr(&self, name: &Atom, value: DOMString) {
        match self.super_type() {
            Some(ref s) => s.before_remove_attr(name, value),
            _ => (),
        }

        if "sandbox" == name.as_slice() {
            self.deref().sandbox.deref().set(None);
        }
    }

    fn bind_to_tree(&self, tree_in_doc: bool) {
        match self.super_type() {
            Some(ref s) => s.bind_to_tree(tree_in_doc),
            _ => (),
        }

        if tree_in_doc {
            self.process_the_iframe_attributes();
        }
    }
}

impl Reflectable for HTMLIFrameElement {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        self.htmlelement.reflector()
    }
}
<|endoftext|>


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

use dom::attr::{Attr, AttrValue};
use dom::bindings::codegen::Bindings::HTMLOptionElementBinding::HTMLOptionElementMethods;
use dom::bindings::codegen::Bindings::HTMLSelectElementBinding;
use dom::bindings::codegen::Bindings::HTMLSelectElementBinding::HTMLSelectElementMethods;
use dom::bindings::codegen::UnionTypes::HTMLElementOrLong;
use dom::bindings::codegen::UnionTypes::HTMLOptionElementOrHTMLOptGroupElement;
use dom::bindings::conversions::Castable;
use dom::bindings::js::Root;
use dom::document::Document;
use dom::element::{AttributeMutation, Element, IN_ENABLED_STATE};
use dom::htmlelement::HTMLElement;
use dom::htmlfieldsetelement::HTMLFieldSetElement;
use dom::htmlformelement::{FormControl, HTMLFormElement};
use dom::htmloptionelement::HTMLOptionElement;
use dom::node::{Node, window_from_node};
use dom::validitystate::ValidityState;
use dom::virtualmethods::VirtualMethods;
use std::borrow::ToOwned;
use string_cache::Atom;
use util::str::DOMString;

#[dom_struct]
pub struct HTMLSelectElement {
    htmlelement: HTMLElement
}

static DEFAULT_SELECT_SIZE: u32 = 0;

impl HTMLSelectElement {
    fn new_inherited(localName: DOMString,
                     prefix: Option<DOMString>,
                     document: &Document) -> HTMLSelectElement {
        HTMLSelectElement {
            htmlelement:
                HTMLElement::new_inherited_with_state(IN_ENABLED_STATE,
                                                      localName, prefix, document)
        }
    }

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

    // https://html.spec.whatwg.org/multipage/#ask-for-a-reset
    pub fn ask_for_reset(&self) {
        if self.Multiple() {
            return;
        }

        let mut first_enabled: Option<Root<HTMLOptionElement>> = None;
        let mut last_selected: Option<Root<HTMLOptionElement>> = None;

        let node = self.upcast::<Node>();
        for opt in node.traverse_preorder().filter_map(Root::downcast::<HTMLOptionElement>) {
            if opt.Selected() {
                opt.set_selectedness(false);
                last_selected = Some(Root::from_ref(opt.r()));
            }
            let element =  opt.upcast::<Element>();
            if first_enabled.is_none() && !element.get_disabled_state() {
                first_enabled = Some(Root::from_ref(opt.r()));
            }
        }

        if last_selected.is_none() {
            if self.display_size() == 1 {
                // select the first enabled element
                if let Some(first_opt) = first_enabled {
                    first_opt.set_selectedness(true);
                }
            }
        } else {
            // >= 1 selected, reselect last one
            last_selected.unwrap().set_selectedness(true);
        }
    }

    pub fn pick_option(&self, picked: &HTMLOptionElement) {
        if !self.Multiple() {
            let node = self.upcast::<Node>();
            let picked = picked.upcast();
            for opt in node.traverse_preorder().filter_map(Root::downcast::<HTMLOptionElement>) {
                if opt.upcast::<HTMLElement>() != picked {
                    opt.set_selectedness(false);
                }
            }
        }
    }

    // https://html.spec.whatwg.org/multipage/#concept-select-size
    fn display_size(&self) -> u32 {
         if self.Size() == 0 {
             if self.Multiple() {
                 4
             } else {
                 1
             }
         } else {
             self.Size()
         }
     }
}

impl HTMLSelectElementMethods for HTMLSelectElement {
    // https://html.spec.whatwg.org/multipage/#dom-cva-validity
    fn Validity(&self) -> Root<ValidityState> {
        let window = window_from_node(self);
        ValidityState::new(window.r())
    }

    // Note: this function currently only exists for union.html.
    // https://html.spec.whatwg.org/multipage/#dom-select-add
    fn Add(&self, _element: HTMLOptionElementOrHTMLOptGroupElement, _before: Option<HTMLElementOrLong>) {
    }

    // https://html.spec.whatwg.org/multipage/#dom-fe-disabled
    make_bool_getter!(Disabled);

    // https://html.spec.whatwg.org/multipage/#dom-fe-disabled
    make_bool_setter!(SetDisabled, "disabled");

    // https://html.spec.whatwg.org/multipage/#dom-fae-form
    fn GetForm(&self) -> Option<Root<HTMLFormElement>> {
        self.form_owner()
    }

    // https://html.spec.whatwg.org/multipage/#dom-select-multiple
    make_bool_getter!(Multiple);

    // https://html.spec.whatwg.org/multipage/#dom-select-multiple
    make_bool_setter!(SetMultiple, "multiple");

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

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

    // https://html.spec.whatwg.org/multipage/#dom-select-size
    make_uint_getter!(Size, "size", DEFAULT_SELECT_SIZE);

    // https://html.spec.whatwg.org/multipage/#dom-select-size
    make_uint_setter!(SetSize, "size", DEFAULT_SELECT_SIZE);

    // https://html.spec.whatwg.org/multipage/#dom-select-type
    fn Type(&self) -> DOMString {
        if self.Multiple() {
            "select-multiple".to_owned()
        } else {
            "select-one".to_owned()
        }
    }
}

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

    fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
        self.super_type().unwrap().attribute_mutated(attr, mutation);
        if attr.local_name() == &atom!(disabled) {
            let el = self.upcast::<Element>();
            match mutation {
                AttributeMutation::Set(_) => {
                    el.set_disabled_state(true);
                    el.set_enabled_state(false);
                },
                AttributeMutation::Removed => {
                    el.set_disabled_state(false);
                    el.set_enabled_state(true);
                    el.check_ancestors_disabled_state_for_form_control();
                }
            }
        }
    }

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

        self.upcast::<Element>().check_ancestors_disabled_state_for_form_control();
    }

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

        let node = self.upcast::<Node>();
        let el = self.upcast::<Element>();
        if node.ancestors().any(|ancestor| ancestor.r().is::<HTMLFieldSetElement>()) {
            el.check_ancestors_disabled_state_for_form_control();
        } else {
            el.check_disabled_attribute();
        }
    }

    fn parse_plain_attribute(&self, local_name: &Atom, value: DOMString) -> AttrValue {
        match local_name {
            &atom!("size") => AttrValue::from_u32(value, DEFAULT_SELECT_SIZE),
            _ => self.super_type().unwrap().parse_plain_attribute(local_name, value),
        }
    }
}

impl FormControl for HTMLSelectElement {}
<commit_msg>Replace if-else with match.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::attr::{Attr, AttrValue};
use dom::bindings::codegen::Bindings::HTMLOptionElementBinding::HTMLOptionElementMethods;
use dom::bindings::codegen::Bindings::HTMLSelectElementBinding;
use dom::bindings::codegen::Bindings::HTMLSelectElementBinding::HTMLSelectElementMethods;
use dom::bindings::codegen::UnionTypes::HTMLElementOrLong;
use dom::bindings::codegen::UnionTypes::HTMLOptionElementOrHTMLOptGroupElement;
use dom::bindings::conversions::Castable;
use dom::bindings::js::Root;
use dom::document::Document;
use dom::element::{AttributeMutation, Element, IN_ENABLED_STATE};
use dom::htmlelement::HTMLElement;
use dom::htmlfieldsetelement::HTMLFieldSetElement;
use dom::htmlformelement::{FormControl, HTMLFormElement};
use dom::htmloptionelement::HTMLOptionElement;
use dom::node::{Node, window_from_node};
use dom::validitystate::ValidityState;
use dom::virtualmethods::VirtualMethods;
use std::borrow::ToOwned;
use string_cache::Atom;
use util::str::DOMString;

#[dom_struct]
pub struct HTMLSelectElement {
    htmlelement: HTMLElement
}

static DEFAULT_SELECT_SIZE: u32 = 0;

impl HTMLSelectElement {
    fn new_inherited(localName: DOMString,
                     prefix: Option<DOMString>,
                     document: &Document) -> HTMLSelectElement {
        HTMLSelectElement {
            htmlelement:
                HTMLElement::new_inherited_with_state(IN_ENABLED_STATE,
                                                      localName, prefix, document)
        }
    }

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

    // https://html.spec.whatwg.org/multipage/#ask-for-a-reset
    pub fn ask_for_reset(&self) {
        if self.Multiple() {
            return;
        }

        let mut first_enabled: Option<Root<HTMLOptionElement>> = None;
        let mut last_selected: Option<Root<HTMLOptionElement>> = None;

        let node = self.upcast::<Node>();
        for opt in node.traverse_preorder().filter_map(Root::downcast::<HTMLOptionElement>) {
            if opt.Selected() {
                opt.set_selectedness(false);
                last_selected = Some(Root::from_ref(opt.r()));
            }
            let element =  opt.upcast::<Element>();
            if first_enabled.is_none() && !element.get_disabled_state() {
                first_enabled = Some(Root::from_ref(opt.r()));
            }
        }

        match last_selected {
            Some(last_selected) => last_selected.set_selectedness(true),
            None => {
                if self.display_size() == 1 {
                    if let Some(first_enabled) = first_enabled {
                        first_enabled.set_selectedness(true);
                    }
                }
            }
        }
    }

    pub fn pick_option(&self, picked: &HTMLOptionElement) {
        if !self.Multiple() {
            let node = self.upcast::<Node>();
            let picked = picked.upcast();
            for opt in node.traverse_preorder().filter_map(Root::downcast::<HTMLOptionElement>) {
                if opt.upcast::<HTMLElement>() != picked {
                    opt.set_selectedness(false);
                }
            }
        }
    }

    // https://html.spec.whatwg.org/multipage/#concept-select-size
    fn display_size(&self) -> u32 {
         if self.Size() == 0 {
             if self.Multiple() {
                 4
             } else {
                 1
             }
         } else {
             self.Size()
         }
     }
}

impl HTMLSelectElementMethods for HTMLSelectElement {
    // https://html.spec.whatwg.org/multipage/#dom-cva-validity
    fn Validity(&self) -> Root<ValidityState> {
        let window = window_from_node(self);
        ValidityState::new(window.r())
    }

    // Note: this function currently only exists for union.html.
    // https://html.spec.whatwg.org/multipage/#dom-select-add
    fn Add(&self, _element: HTMLOptionElementOrHTMLOptGroupElement, _before: Option<HTMLElementOrLong>) {
    }

    // https://html.spec.whatwg.org/multipage/#dom-fe-disabled
    make_bool_getter!(Disabled);

    // https://html.spec.whatwg.org/multipage/#dom-fe-disabled
    make_bool_setter!(SetDisabled, "disabled");

    // https://html.spec.whatwg.org/multipage/#dom-fae-form
    fn GetForm(&self) -> Option<Root<HTMLFormElement>> {
        self.form_owner()
    }

    // https://html.spec.whatwg.org/multipage/#dom-select-multiple
    make_bool_getter!(Multiple);

    // https://html.spec.whatwg.org/multipage/#dom-select-multiple
    make_bool_setter!(SetMultiple, "multiple");

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

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

    // https://html.spec.whatwg.org/multipage/#dom-select-size
    make_uint_getter!(Size, "size", DEFAULT_SELECT_SIZE);

    // https://html.spec.whatwg.org/multipage/#dom-select-size
    make_uint_setter!(SetSize, "size", DEFAULT_SELECT_SIZE);

    // https://html.spec.whatwg.org/multipage/#dom-select-type
    fn Type(&self) -> DOMString {
        if self.Multiple() {
            "select-multiple".to_owned()
        } else {
            "select-one".to_owned()
        }
    }
}

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

    fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
        self.super_type().unwrap().attribute_mutated(attr, mutation);
        if attr.local_name() == &atom!(disabled) {
            let el = self.upcast::<Element>();
            match mutation {
                AttributeMutation::Set(_) => {
                    el.set_disabled_state(true);
                    el.set_enabled_state(false);
                },
                AttributeMutation::Removed => {
                    el.set_disabled_state(false);
                    el.set_enabled_state(true);
                    el.check_ancestors_disabled_state_for_form_control();
                }
            }
        }
    }

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

        self.upcast::<Element>().check_ancestors_disabled_state_for_form_control();
    }

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

        let node = self.upcast::<Node>();
        let el = self.upcast::<Element>();
        if node.ancestors().any(|ancestor| ancestor.r().is::<HTMLFieldSetElement>()) {
            el.check_ancestors_disabled_state_for_form_control();
        } else {
            el.check_disabled_attribute();
        }
    }

    fn parse_plain_attribute(&self, local_name: &Atom, value: DOMString) -> AttrValue {
        match local_name {
            &atom!("size") => AttrValue::from_u32(value, DEFAULT_SELECT_SIZE),
            _ => self.super_type().unwrap().parse_plain_attribute(local_name, value),
        }
    }
}

impl FormControl for HTMLSelectElement {}
<|endoftext|>


<commit_before>//! Provides facilities for notifying when any spec files have changed
//! on disk. This is how we know when to start, stop, or restart
//! services in response to the various `hab svc` commands.

use super::spec_dir::SpecDir;
use crate::error::{Error,
                   Result};
use habitat_core::env::Config as EnvConfig;
use notify::{DebouncedEvent,
             RecommendedWatcher,
             RecursiveMode,
             Watcher};
use std::{num::ParseIntError,
          str::FromStr,
          sync::mpsc,
          thread::Builder,
          time::Duration};

/// How long should we wait to consolidate filesystem events?
///
/// This should strike a balance between responsiveness and
/// too-granular a series of events.
///
/// See https://docs.rs/notify/4.0.6/notify/trait.Watcher.html#tymethod.new
struct SpecWatcherDelay(Duration);

impl From<Duration> for SpecWatcherDelay {
    fn from(d: Duration) -> SpecWatcherDelay { SpecWatcherDelay(d) }
}

impl Default for SpecWatcherDelay {
    fn default() -> Self {
        // There's nothing particularly magical about 2s, particularly
        // since we're monitoring at such a coarse level ("something
        // happened in this directory").
        //
        // Smaller is probably fine, but you wouldn't want to go much
        // higher, as this could extend the amount of time you'd need
        // to wait before realizing you need to take action on a
        // service.
        Duration::from_secs(2).into()
    }
}

impl FromStr for SpecWatcherDelay {
    type Err = ParseIntError;

    fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
        Ok(Duration::from_millis(s.parse()?).into())
    }
}

impl EnvConfig for SpecWatcherDelay {
    const ENVVAR: &'static str = "HAB_SPEC_WATCHER_DELAY_MS";
}

// TODO (CM): A strong argument could be made for folding the
// SpecWatcher functionality into SpecDir itself.

/// Provides an abstraction layer over filesystem notifications for
/// spec files.
pub struct SpecWatcher {
    // Not actually used; only holding onto it for lifetime / Drop
    // purposes (`Drop` kills the threads that the watcher spawns to do
    // its work).
    _watcher: RecommendedWatcher,
    channel:  mpsc::Receiver<DebouncedEvent>,
}

impl SpecWatcher {
    /// Start up a separate thread to listen for filesystem
    /// events.
    pub fn run(spec_dir: &SpecDir) -> Result<SpecWatcher> {
        // The act of creating a `notify::Watcher` creates threads on
        // its own. It does not, however, allow you to set the _names_
        // of those threads.
        //
        // We're creating a SpecWatcher in a thread just so we can get
        // some control over the name of the threads that the
        // underlying `notify::Watcher` creates (_that_ is what this
        // function's documentation is referring to), which makes
        // monitoring and reasoning about the overall Supervisor
        // process easier. There's no other reason than that; if the
        // `notify` crate allowed us to name the threads, we could
        // just use a slightly modified version of `SpecWatcher::new`
        // instead.

        // I'd rather not have to do this clone, but it's a side
        // effect of this thread business. Better to eat it here
        // rather than at the callsite; having this function take a
        // reference is the true API we want.
        let dir = spec_dir.clone();

        Builder::new().name(String::from("spec-watcher"))
                      .spawn(move || Self::new(&dir))?
                      .join()
                      .map_err(|_| {
                          error!("SpecWatcher spawning thread panicked!");
                          Error::SpecWatcherNotCreated
                      })?
    }

    /// Isolates the pure creation logic of a `SpecWatcher`, separate
    /// from the thread-based creation we use in `SpecWatcher::run` to
    /// get control over the names of the resulting threads. If we
    /// didn't care what the resulting watcher threads were named,
    /// we'd just use this directly.
    fn new(spec_dir: &SpecDir) -> Result<SpecWatcher> {
        let (tx, rx) = mpsc::channel();
        let delay = SpecWatcherDelay::configured_value();
        let mut watcher = RecommendedWatcher::new(tx, delay.0)?;
        watcher.watch(spec_dir, RecursiveMode::NonRecursive)?;
        Ok(SpecWatcher { _watcher: watcher,
                         channel:  rx, })
    }

    /// Returns `true` if _any_ filesystem events were detected in the
    /// watched directory.
    ///
    /// We are opting for this coarse granularity because it is
    /// difficult, if not impossible, to rely on the resulting stream
    /// of notification events as a 100% complete and accurate record
    /// of everything that takes place in the specs directory. For
    /// example, because of how we write files to temporary locations,
    /// then rename in order to achieve "atomic writes", it is
    /// possible to miss the events that would cause you to see this
    /// as a "rename"; instead, you would see a "new" file (even with
    /// a debounce time of 0 ms!)
    ///
    /// In any event, we would _still_ need to examine on-disk state
    /// of the directory and compare to our in-memory state to know
    /// exactly what to do in order to reconcile the two (i.e., you
    /// can't know whether to start, stop, or restart a service just
    /// by knowing that the spec file changed; you have to actually
    /// look at the current contents to figure that out).
    ///
    /// As a result, we're just using this as a coarse "something
    /// changed in the directory" signal. We are not filtering events
    /// to receive only those that affect `*.spec` files, so we may
    /// respond to modifications to temporary files, or indeed any
    /// file, within the specs directory (e.g., running `touch
    /// /hab/sup/default/specs/blahblah` would count as an event). It
    /// is possible to perform this filtering, of course, but it's not
    /// clear that the extra code would be worth it.
    pub fn has_events(&self) -> bool {
        let events = self.channel.try_iter().collect::<Vec<_>>();
        if events.is_empty() {
            false
        } else {
            trace!("SpecWatcher events: {:?}", events);
            true
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use habitat_common::locked_env_var;
    use std::{fs::File,
              io::{Error as IoError,
                   Write},
              result::Result as StdResult,
              thread};
    use tempfile::TempDir;

    locked_env_var!(HAB_SPEC_WATCHER_DELAY_MS, lock_delay_var);

    fn file_with_content<C>(dir: &TempDir, filename: &str, contents: C) -> StdResult<(), IoError>
        where C: Into<String>
    {
        let path = dir.path().join(filename);
        let mut buffer = File::create(&path)?;
        buffer.write_all(contents.into().as_bytes())
    }

    /// Sleep for the currently-configured debounce interval, plus a
    /// few milliseconds more, just to be certain our filesystem
    /// events have had plenty of time to process.
    fn wait_for_debounce_interval() {
        let wait_duration = SpecWatcherDelay::configured_value().0 + Duration::from_millis(2);
        error!("wait_for_debounce_interval: waiting {:?}", wait_duration);
        thread::sleep(wait_duration);
    }

    #[test]
    fn can_be_created() {
        let _delay = lock_delay_var();

        let dir = TempDir::new().expect("Could not create directory");
        let spec_dir = SpecDir::new(dir.path()).expect("Couldn't make SpecDir");
        assert!(SpecWatcher::run(&spec_dir).is_ok(),
                "Couldn't create a SpecWatcher!");
    }

    #[test]
    fn can_get_events_for_spec_files() {
        env_logger::try_init().ok();
        error!("can_get_events_for_spec_files starting");
        let _delay = lock_delay_var();

        let dir = TempDir::new().expect("Could not create directory");
        let spec_dir = SpecDir::new(dir.path()).expect("Couldn't make SpecDir");
        let sw = SpecWatcher::run(&spec_dir).expect("Couldn't create a SpecWatcher!");

        assert!(!sw.has_events(), "There should be no events to start");

        file_with_content(&dir, "foo.spec", "fooooooo").expect("couldn't create file");

        assert!(!sw.has_events(),
                "Need to allow for the debounce interval to pass before you can expect events");

        while !sw.has_events() {
            wait_for_debounce_interval();
        }

        assert!(!sw.has_events(),
                "Should be no more events after you've checked");
        error!("can_get_events_for_spec_files done");
    }

    /// Currently, the spec watcher will respond to changes to any
    /// file in the directory, whether it's a `*.spec` file or not.
    ///
    /// This would, for instance, pick up the temp files that
    /// operations like `hab svc stop` lay down before renaming them
    /// to their final `*.spec` form.
    #[test]
    fn can_get_events_for_non_spec_files() {
        env_logger::try_init().ok();
        error!("can_get_events_for_non_spec_files starting");
        let _delay = lock_delay_var();

        let dir = TempDir::new().expect("Could not create directory");
        let spec_dir = SpecDir::new(dir.path()).expect("Couldn't make SpecDir");
        let sw = SpecWatcher::run(&spec_dir).expect("Couldn't create a SpecWatcher!");

        assert!(!sw.has_events(), "There should be no events to start");

        file_with_content(&dir, "foo.abc123xyz", "fooooooo").expect("couldn't create file");

        assert!(!sw.has_events(),
                "Need to allow for the debounce interval to pass before you can expect events");

        while !sw.has_events() {
            wait_for_debounce_interval();
        }

        assert!(!sw.has_events(),
                "Should be no more events after you've checked");
        error!("can_get_events_for_non_spec_files done");
    }

    #[test]
    fn short_debounce_delays_also_work() {
        env_logger::try_init().ok();
        error!("short_debounce_delays_also_work starting");
        let delay = lock_delay_var();
        delay.set("1");

        // Just verifying that our delay variable works correctly
        assert_eq!(SpecWatcherDelay::configured_value().0,
                   Duration::from_millis(1));

        let dir = TempDir::new().expect("Could not create directory");
        let spec_dir = SpecDir::new(dir.path()).expect("Couldn't make SpecDir");
        let sw = SpecWatcher::run(&spec_dir).expect("Couldn't create a SpecWatcher!");

        assert!(!sw.has_events(), "There should be no events to start");

        file_with_content(&dir, "foo.spec", "fooooooo").expect("couldn't create file");

        assert!(!sw.has_events(),
                "Need to allow for the debounce interval to pass before you can expect events");

        while !sw.has_events() {
            wait_for_debounce_interval();
        }

        assert!(!sw.has_events(),
                "Should be no more events after you've checked");
        error!("short_debounce_delays_also_work starting");
    }
}
<commit_msg>Convert SpecWatcherDelay to use env_config_duration macro<commit_after>//! Provides facilities for notifying when any spec files have changed
//! on disk. This is how we know when to start, stop, or restart
//! services in response to the various `hab svc` commands.

use super::spec_dir::SpecDir;
use crate::error::{Error,
                   Result};
use notify::{DebouncedEvent,
             RecommendedWatcher,
             RecursiveMode,
             Watcher};
use std::{sync::mpsc,
          thread::Builder,
          time::Duration};

/// How long should we wait to consolidate filesystem events?
///
/// This should strike a balance between responsiveness and
/// too-granular a series of events.
///
/// See https://docs.rs/notify/4.0.6/notify/trait.Watcher.html#tymethod.new
habitat_core::env_config_duration!(SpecWatcherDelay,
                                   HAB_SPEC_WATCHER_DELAY_MS => from_millis,
                                   // There's nothing particularly magical about 2s, particularly
                                   // since we're monitoring at such a coarse level ("something
                                   // happened in this directory").
                                   //
                                   // Smaller is probably fine, but you wouldn't want to go much
                                   // higher, as this could extend the amount of time you'd need
                                   // to wait before realizing you need to take action on a
                                   // service.
                                   Duration::from_secs(2));

// TODO (CM): A strong argument could be made for folding the
// SpecWatcher functionality into SpecDir itself.

/// Provides an abstraction layer over filesystem notifications for
/// spec files.
pub struct SpecWatcher {
    // Not actually used; only holding onto it for lifetime / Drop
    // purposes (`Drop` kills the threads that the watcher spawns to do
    // its work).
    _watcher: RecommendedWatcher,
    channel:  mpsc::Receiver<DebouncedEvent>,
}

impl SpecWatcher {
    /// Start up a separate thread to listen for filesystem
    /// events.
    pub fn run(spec_dir: &SpecDir) -> Result<SpecWatcher> {
        // The act of creating a `notify::Watcher` creates threads on
        // its own. It does not, however, allow you to set the _names_
        // of those threads.
        //
        // We're creating a SpecWatcher in a thread just so we can get
        // some control over the name of the threads that the
        // underlying `notify::Watcher` creates (_that_ is what this
        // function's documentation is referring to), which makes
        // monitoring and reasoning about the overall Supervisor
        // process easier. There's no other reason than that; if the
        // `notify` crate allowed us to name the threads, we could
        // just use a slightly modified version of `SpecWatcher::new`
        // instead.

        // I'd rather not have to do this clone, but it's a side
        // effect of this thread business. Better to eat it here
        // rather than at the callsite; having this function take a
        // reference is the true API we want.
        let dir = spec_dir.clone();

        Builder::new().name(String::from("spec-watcher"))
                      .spawn(move || Self::new(&dir))?
                      .join()
                      .map_err(|_| {
                          error!("SpecWatcher spawning thread panicked!");
                          Error::SpecWatcherNotCreated
                      })?
    }

    /// Isolates the pure creation logic of a `SpecWatcher`, separate
    /// from the thread-based creation we use in `SpecWatcher::run` to
    /// get control over the names of the resulting threads. If we
    /// didn't care what the resulting watcher threads were named,
    /// we'd just use this directly.
    fn new(spec_dir: &SpecDir) -> Result<SpecWatcher> {
        let (tx, rx) = mpsc::channel();
        let delay = SpecWatcherDelay::configured_value();
        let mut watcher = RecommendedWatcher::new(tx, delay.0)?;
        watcher.watch(spec_dir, RecursiveMode::NonRecursive)?;
        Ok(SpecWatcher { _watcher: watcher,
                         channel:  rx, })
    }

    /// Returns `true` if _any_ filesystem events were detected in the
    /// watched directory.
    ///
    /// We are opting for this coarse granularity because it is
    /// difficult, if not impossible, to rely on the resulting stream
    /// of notification events as a 100% complete and accurate record
    /// of everything that takes place in the specs directory. For
    /// example, because of how we write files to temporary locations,
    /// then rename in order to achieve "atomic writes", it is
    /// possible to miss the events that would cause you to see this
    /// as a "rename"; instead, you would see a "new" file (even with
    /// a debounce time of 0 ms!)
    ///
    /// In any event, we would _still_ need to examine on-disk state
    /// of the directory and compare to our in-memory state to know
    /// exactly what to do in order to reconcile the two (i.e., you
    /// can't know whether to start, stop, or restart a service just
    /// by knowing that the spec file changed; you have to actually
    /// look at the current contents to figure that out).
    ///
    /// As a result, we're just using this as a coarse "something
    /// changed in the directory" signal. We are not filtering events
    /// to receive only those that affect `*.spec` files, so we may
    /// respond to modifications to temporary files, or indeed any
    /// file, within the specs directory (e.g., running `touch
    /// /hab/sup/default/specs/blahblah` would count as an event). It
    /// is possible to perform this filtering, of course, but it's not
    /// clear that the extra code would be worth it.
    pub fn has_events(&self) -> bool {
        let events = self.channel.try_iter().collect::<Vec<_>>();
        if events.is_empty() {
            false
        } else {
            trace!("SpecWatcher events: {:?}", events);
            true
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use habitat_common::locked_env_var;
    use std::{fs::File,
              io::{Error as IoError,
                   Write},
              result::Result as StdResult,
              thread};
    use tempfile::TempDir;

    locked_env_var!(HAB_SPEC_WATCHER_DELAY_MS, lock_delay_var);

    fn file_with_content<C>(dir: &TempDir, filename: &str, contents: C) -> StdResult<(), IoError>
        where C: Into<String>
    {
        let path = dir.path().join(filename);
        let mut buffer = File::create(&path)?;
        buffer.write_all(contents.into().as_bytes())
    }

    /// Sleep for the currently-configured debounce interval, plus a
    /// few milliseconds more, just to be certain our filesystem
    /// events have had plenty of time to process.
    fn wait_for_debounce_interval() {
        let wait_duration = SpecWatcherDelay::configured_value().0 + Duration::from_millis(2);
        error!("wait_for_debounce_interval: waiting {:?}", wait_duration);
        thread::sleep(wait_duration);
    }

    #[test]
    fn can_be_created() {
        let _delay = lock_delay_var();

        let dir = TempDir::new().expect("Could not create directory");
        let spec_dir = SpecDir::new(dir.path()).expect("Couldn't make SpecDir");
        assert!(SpecWatcher::run(&spec_dir).is_ok(),
                "Couldn't create a SpecWatcher!");
    }

    #[test]
    fn can_get_events_for_spec_files() {
        env_logger::try_init().ok();
        error!("can_get_events_for_spec_files starting");
        let _delay = lock_delay_var();

        let dir = TempDir::new().expect("Could not create directory");
        let spec_dir = SpecDir::new(dir.path()).expect("Couldn't make SpecDir");
        let sw = SpecWatcher::run(&spec_dir).expect("Couldn't create a SpecWatcher!");

        assert!(!sw.has_events(), "There should be no events to start");

        file_with_content(&dir, "foo.spec", "fooooooo").expect("couldn't create file");

        assert!(!sw.has_events(),
                "Need to allow for the debounce interval to pass before you can expect events");

        while !sw.has_events() {
            wait_for_debounce_interval();
        }

        assert!(!sw.has_events(),
                "Should be no more events after you've checked");
        error!("can_get_events_for_spec_files done");
    }

    /// Currently, the spec watcher will respond to changes to any
    /// file in the directory, whether it's a `*.spec` file or not.
    ///
    /// This would, for instance, pick up the temp files that
    /// operations like `hab svc stop` lay down before renaming them
    /// to their final `*.spec` form.
    #[test]
    fn can_get_events_for_non_spec_files() {
        env_logger::try_init().ok();
        error!("can_get_events_for_non_spec_files starting");
        let _delay = lock_delay_var();

        let dir = TempDir::new().expect("Could not create directory");
        let spec_dir = SpecDir::new(dir.path()).expect("Couldn't make SpecDir");
        let sw = SpecWatcher::run(&spec_dir).expect("Couldn't create a SpecWatcher!");

        assert!(!sw.has_events(), "There should be no events to start");

        file_with_content(&dir, "foo.abc123xyz", "fooooooo").expect("couldn't create file");

        assert!(!sw.has_events(),
                "Need to allow for the debounce interval to pass before you can expect events");

        while !sw.has_events() {
            wait_for_debounce_interval();
        }

        assert!(!sw.has_events(),
                "Should be no more events after you've checked");
        error!("can_get_events_for_non_spec_files done");
    }

    #[test]
    fn short_debounce_delays_also_work() {
        env_logger::try_init().ok();
        error!("short_debounce_delays_also_work starting");
        let delay = lock_delay_var();
        delay.set("1");

        // Just verifying that our delay variable works correctly
        assert_eq!(SpecWatcherDelay::configured_value().0,
                   Duration::from_millis(1));

        let dir = TempDir::new().expect("Could not create directory");
        let spec_dir = SpecDir::new(dir.path()).expect("Couldn't make SpecDir");
        let sw = SpecWatcher::run(&spec_dir).expect("Couldn't create a SpecWatcher!");

        assert!(!sw.has_events(), "There should be no events to start");

        file_with_content(&dir, "foo.spec", "fooooooo").expect("couldn't create file");

        assert!(!sw.has_events(),
                "Need to allow for the debounce interval to pass before you can expect events");

        while !sw.has_events() {
            wait_for_debounce_interval();
        }

        assert!(!sw.has_events(),
                "Should be no more events after you've checked");
        error!("short_debounce_delays_also_work starting");
    }
}
<|endoftext|>


<commit_before>#[macro_export(local_inner_macros)]
macro_rules! select_inner {
    (
        $( any ($any:expr) -> $var:ident => $abody:expr , )*
        $( one ($sub:expr) => $cbody:expr , )*
        nonblocking $nonblocking:literal => $bbody:expr ,
        error -> $e:ident => $ebody:expr
    ) => {
        {
            use $crate::ipc::InternalSubscription;
            use $crate::d7abi::SyscallErrorCode;
            let mut subs = ::alloc::vec::Vec::new();
            $(subs.push($sub.sub_id());)*
            $(subs.extend($any.iter().map(|v| v.sub_id()));)*
            match $crate::syscall::ipc_select(&subs, $nonblocking) {
                Ok(index) => 'select: loop { // TODO: remove "loop" when block labels are stabilized
                    let mut i = 0;
                    $(
                        if index == i {
                            break 'select $cbody;
                        }
                        i += 1;
                    )*
                    $(
                        if index <= i + $any.len() {
                            let $var = index - i;
                            break 'select $abody;
                        }
                    )*
                    ::core::unreachable!("Select returned unknown alternative");
                },
                Err(SyscallErrorCode::would_block) => $bbody ,
                Err($e) => $ebody ,
            }
        }
    };
}

#[macro_export]
macro_rules! select {
    (
        $( any ($any:expr) -> $var:ident => $abody:expr , )*
        $( one ($sub:expr) => $cbody:expr , )*
        would_block => $bbody:expr ,
        error -> $e:ident => $ebody:expr $(,)?
    ) => {$crate::select_inner!{
        $( any ($any) -> $var => $abody , )*
        $( one ($sub) => $cbody , )*
        nonblocking true => $bbody,
        error -> $e => $ebody
    }};

    (
        $( any ($any:expr) -> $var:ident => $abody:expr , )*
        $( one ($sub:expr) => $cbody:expr , )*
        error -> $e:ident => $ebody:expr $(,)?
    ) => {$crate::select_inner!{
        $( any ($any) -> $var => $abody , )*
        $( one ($sub) => $cbody , )*
        nonblocking false => {unreachable!("Nonblocking system call would_block")},
        error -> $e => $ebody
    }};

    // Panic-on-error variants

    (
        $( any ($any:expr) -> $var:ident => $abody:expr , )*
        $( one ($sub:expr) => $cbody:expr , )*
        would_block => $bbody:expr
    ) => {$crate::select_inner!{
        $( any ($any) -> $var => $abody , )*
        $( one ($sub) => $cbody , )*
        nonblocking true => $bbody,
        error -> err => { ::core::panic!("Unhandled error in select!: {:?}", err) }
    }};

    (
        $( any ($any:expr) -> $var:ident => $abody:expr , )*
        $( one ($sub:expr) => $cbody:expr ),*
    ) => {$crate::select_inner!{
        $( any ($any) -> $var => $abody , )*
        $( one ($sub) => $cbody , )*
        nonblocking false => {unreachable!()},
        error -> err => { ::core::panic!("Unhandled error in select!: {:?}", err) }
    }};

    (
        $( any ($any:expr) -> $var:ident => $abody:expr),*
    ) => {$crate::select_inner!{
        $( any ($any) -> $var => $abody , )*
        nonblocking false => {unreachable!()},
        error -> err => { ::core::panic!("Unhandled error in select!: {:?}", err) }
    }};
}
<commit_msg>Fix IPC select macro for multiple any() matchers<commit_after>#[macro_export(local_inner_macros)]
macro_rules! select_inner {
    (
        $( any ($any:expr) -> $var:ident => $abody:expr , )*
        $( one ($sub:expr) => $cbody:expr , )*
        nonblocking $nonblocking:literal => $bbody:expr ,
        error -> $e:ident => $ebody:expr
    ) => {
        {
            use $crate::ipc::InternalSubscription;
            use $crate::d7abi::SyscallErrorCode;
            let mut subs = ::alloc::vec::Vec::new();
            $(subs.push($sub.sub_id());)*
            $(subs.extend($any.iter().map(|v| v.sub_id()));)*
            match $crate::syscall::ipc_select(&subs, $nonblocking) {
                Ok(index) => 'select: loop { // TODO: remove "loop" when block labels are stabilized
                    let mut i = 0;
                    $(
                        if index == i {
                            break 'select $cbody;
                        }
                        i += 1;
                    )*
                    $(
                        if index <= i + $any.len() {
                            let $var = index - i;
                            break 'select $abody;
                        }
                        i += $any.len();
                    )*
                    ::core::unreachable!("Select returned unknown alternative");
                },
                Err(SyscallErrorCode::would_block) => $bbody ,
                Err($e) => $ebody ,
            }
        }
    };
}

#[macro_export]
macro_rules! select {
    (
        $( any ($any:expr) -> $var:ident => $abody:expr , )*
        $( one ($sub:expr) => $cbody:expr , )*
        would_block => $bbody:expr ,
        error -> $e:ident => $ebody:expr $(,)?
    ) => {$crate::select_inner!{
        $( any ($any) -> $var => $abody , )*
        $( one ($sub) => $cbody , )*
        nonblocking true => $bbody,
        error -> $e => $ebody
    }};

    (
        $( any ($any:expr) -> $var:ident => $abody:expr , )*
        $( one ($sub:expr) => $cbody:expr , )*
        error -> $e:ident => $ebody:expr $(,)?
    ) => {$crate::select_inner!{
        $( any ($any) -> $var => $abody , )*
        $( one ($sub) => $cbody , )*
        nonblocking false => {unreachable!("Nonblocking system call would_block")},
        error -> $e => $ebody
    }};

    // Panic-on-error variants

    (
        $( any ($any:expr) -> $var:ident => $abody:expr , )*
        $( one ($sub:expr) => $cbody:expr , )*
        would_block => $bbody:expr
    ) => {$crate::select_inner!{
        $( any ($any) -> $var => $abody , )*
        $( one ($sub) => $cbody , )*
        nonblocking true => $bbody,
        error -> err => { ::core::panic!("Unhandled error in select!: {:?}", err) }
    }};

    (
        $( any ($any:expr) -> $var:ident => $abody:expr , )*
        $( one ($sub:expr) => $cbody:expr ),*
    ) => {$crate::select_inner!{
        $( any ($any) -> $var => $abody , )*
        $( one ($sub) => $cbody , )*
        nonblocking false => {unreachable!()},
        error -> err => { ::core::panic!("Unhandled error in select!: {:?}", err) }
    }};

    (
        $( any ($any:expr) -> $var:ident => $abody:expr),*
    ) => {$crate::select_inner!{
        $( any ($any) -> $var => $abody , )*
        nonblocking false => {unreachable!()},
        error -> err => { ::core::panic!("Unhandled error in select!: {:?}", err) }
    }};
}
<|endoftext|>


<commit_before>mod wasm {
    use crate::connect::connect_active;
    use anyhow::{anyhow, Error, Result};
    use extendedmind_engine::{
        get_discovery_key, get_public_key, AutomergeBackend, Engine, Feed, FeedStore, FeedWrapper,
        RandomAccess, Storage, Store,
    };
    use futures::future::FutureExt;
    use futures::stream::StreamExt;
    use js_sys::Uint8Array;
    use log::*;
    use std::fmt::Debug;
    use wasm_bindgen::{prelude::*, JsCast};
    use wasm_bindgen_futures::spawn_local;
    use ws_stream_wasm::WsMeta;

    #[wasm_bindgen]
    pub fn double(i: i32) -> i32 {
        i * 2
    }

    #[wasm_bindgen]
    extern "C" {
        #[wasm_bindgen(catch)]
        fn triple(i: i32) -> Result<JsValue, JsValue>;
    }

    #[wasm_bindgen(js_name = "tripleFromJs")]
    pub fn triple_from_js(i: i32) -> i32 {
        unsafe { triple(i).unwrap().as_f64().unwrap().to_int_unchecked() }
    }

    #[wasm_bindgen(js_name = "connectToHub")]
    pub async fn connect_to_hub(address: String, public_key: String) -> Result<(), JsValue> {
        console_error_panic_hook::set_once();
        console_log::init_with_level(log::Level::Debug).unwrap();
        info!(
            "call: connect_to_hub, address: {}, public_key: {}",
            &address, &public_key
        );
        debug!("attempting to make websocket connection...");
        let (_ws_meta, ws_stream) = WsMeta::connect(
            format!(
                "ws://{}/extendedmind/hypercore/{}",
                address,
                get_discovery_key(get_public_key(public_key.as_str())),
            ),
            None,
        )
        .await
        .unwrap();
        debug!("...connection success, splitting stream...");
        let (reader, writer) = ws_stream.split();
        debug!("...split ready, creating engine...");

        spawn_local(async move {
            let engine = Engine::new_memory(true, Some(public_key.as_str())).await;
            debug!("...engine created, connecting...");
            connect_active(engine).await;
            debug!("...connected");
        });

        Ok(())
    }
}
<commit_msg>Cleaned up unnecessary imports<commit_after>mod wasm {
    use crate::connect::connect_active;
    use anyhow::Result;
    use extendedmind_engine::{get_discovery_key, get_public_key, Engine};
    use futures::stream::StreamExt;
    use log::*;
    use wasm_bindgen::prelude::*;
    use wasm_bindgen_futures::spawn_local;
    use ws_stream_wasm::WsMeta;

    #[wasm_bindgen]
    pub fn double(i: i32) -> i32 {
        i * 2
    }

    #[wasm_bindgen]
    extern "C" {
        #[wasm_bindgen(catch)]
        fn triple(i: i32) -> Result<JsValue, JsValue>;
    }

    #[wasm_bindgen(js_name = "tripleFromJs")]
    pub fn triple_from_js(i: i32) -> i32 {
        unsafe { triple(i).unwrap().as_f64().unwrap().to_int_unchecked() }
    }

    #[wasm_bindgen(js_name = "connectToHub")]
    pub async fn connect_to_hub(address: String, public_key: String) -> Result<(), JsValue> {
        console_error_panic_hook::set_once();
        console_log::init_with_level(log::Level::Debug).unwrap();
        info!(
            "call: connect_to_hub, address: {}, public_key: {}",
            &address, &public_key
        );
        debug!("attempting to make websocket connection...");
        let (_ws_meta, ws_stream) = WsMeta::connect(
            format!(
                "ws://{}/extendedmind/hypercore/{}",
                address,
                get_discovery_key(get_public_key(public_key.as_str())),
            ),
            None,
        )
        .await
        .unwrap();
        debug!("...connection success, splitting stream...");
        let (reader, writer) = ws_stream.split();
        debug!("...split ready, creating engine...");

        spawn_local(async move {
            let engine = Engine::new_memory(true, Some(public_key.as_str())).await;
            debug!("...engine created, connecting...");
            connect_active(engine).await;
            debug!("...connected");
        });

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