/* 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 browser::{self, ServoCefBrowserExtensions};
use eutil::Downcast;
use interfaces::{CefBrowser, CefBrowserHost, CefClient, cef_browser_t, cef_browser_host_t, cef_client_t};
use types::cef_event_flags_t::{EVENTFLAG_ALT_DOWN, EVENTFLAG_CONTROL_DOWN, EVENTFLAG_SHIFT_DOWN};
use types::cef_key_event_type_t::{KEYEVENT_CHAR, KEYEVENT_KEYDOWN, KEYEVENT_KEYUP, KEYEVENT_RAWKEYDOWN};
use types::{cef_mouse_button_type_t, cef_mouse_event, cef_rect_t, cef_key_event, cef_window_handle_t};
use webrender_api::ScrollLocation;
use wrappers::CefWrap;

use compositing::windowing::{WindowEvent, MouseWindowEvent};
use euclid::{TypedPoint2D, TypedVector2D, TypedSize2D};
use libc::{c_double, c_int};
use msg::constellation_msg::{self, KeyModifiers, KeyState};
use script_traits::{MouseButton, TouchEventType};
use std::cell::{Cell, RefCell};
use std::char;

pub struct ServoCefBrowserHost {
    /// A reference to the browser.
    pub browser: RefCell<Option<CefBrowser>>,
    /// A reference to the client.
    pub client: CefClient,
    /// flag for return value of prepare_for_composite
    pub composite_ok: Cell<bool>,
}

// From blink ui/events/keycodes/keyboard_codes_posix.h.
#[allow(dead_code)]
#[allow(non_snake_case)]
mod KeyboardCode {
    pub const VKEY_BACK : u8 = 0x08;
    pub const VKEY_TAB : u8 = 0x09;
    pub const VKEY_BACKTAB : u8 = 0x0A;
    pub const VKEY_CLEAR : u8 = 0x0C;
    pub const VKEY_RETURN : u8 = 0x0D;
    pub const VKEY_SHIFT : u8 = 0x10;
    pub const VKEY_CONTROL : u8 = 0x11;
    pub const VKEY_MENU : u8 = 0x12;
    pub const VKEY_PAUSE : u8 = 0x13;
    pub const VKEY_CAPITAL : u8 = 0x14;
    pub const VKEY_KANA : u8 = 0x15;
    //VKEY_HANGUL = 0x15,
    pub const VKEY_JUNJA : u8 = 0x17;
    pub const VKEY_FINAL : u8 = 0x18;
    pub const VKEY_HANJA : u8 = 0x19;
    //VKEY_KANJI = 0x19,
    pub const VKEY_ESCAPE : u8 = 0x1B;
    pub const VKEY_CONVERT : u8 = 0x1C;
    pub const VKEY_NONCONVERT : u8 = 0x1D;
    pub const VKEY_ACCEPT : u8 = 0x1E;
    pub const VKEY_MODECHANGE : u8 = 0x1F;
    pub const VKEY_SPACE : u8 = 0x20;
    pub const VKEY_PRIOR : u8 = 0x21;
    pub const VKEY_NEXT : u8 = 0x22;
    pub const VKEY_END : u8 = 0x23;
    pub const VKEY_HOME : u8 = 0x24;
    pub const VKEY_LEFT : u8 = 0x25;
    pub const VKEY_UP : u8 = 0x26;
    pub const VKEY_RIGHT : u8 = 0x27;
    pub const VKEY_DOWN : u8 = 0x28;
    pub const VKEY_SELECT : u8 = 0x29;
    pub const VKEY_PRINT : u8 = 0x2A;
    pub const VKEY_EXECUTE : u8 = 0x2B;
    pub const VKEY_SNAPSHOT : u8 = 0x2C;
    pub const VKEY_INSERT : u8 = 0x2D;
    pub const VKEY_DELETE : u8 = 0x2E;
    pub const VKEY_HELP : u8 = 0x2F;
    pub const VKEY_0 : u8 = 0x30;
    pub const VKEY_1 : u8 = 0x31;
    pub const VKEY_2 : u8 = 0x32;
    pub const VKEY_3 : u8 = 0x33;
    pub const VKEY_4 : u8 = 0x34;
    pub const VKEY_5 : u8 = 0x35;
    pub const VKEY_6 : u8 = 0x36;
    pub const VKEY_7 : u8 = 0x37;
    pub const VKEY_8 : u8 = 0x38;
    pub const VKEY_9 : u8 = 0x39;
    pub const VKEY_A : u8 = 0x41;
    pub const VKEY_B : u8 = 0x42;
    pub const VKEY_C : u8 = 0x43;
    pub const VKEY_D : u8 = 0x44;
    pub const VKEY_E : u8 = 0x45;
    pub const VKEY_F : u8 = 0x46;
    pub const VKEY_G : u8 = 0x47;
    pub const VKEY_H : u8 = 0x48;
    pub const VKEY_I : u8 = 0x49;
    pub const VKEY_J : u8 = 0x4A;
    pub const VKEY_K : u8 = 0x4B;
    pub const VKEY_L : u8 = 0x4C;
    pub const VKEY_M : u8 = 0x4D;
    pub const VKEY_N : u8 = 0x4E;
    pub const VKEY_O : u8 = 0x4F;
    pub const VKEY_P : u8 = 0x50;
    pub const VKEY_Q : u8 = 0x51;
    pub const VKEY_R : u8 = 0x52;
    pub const VKEY_S : u8 = 0x53;
    pub const VKEY_T : u8 = 0x54;
    pub const VKEY_U : u8 = 0x55;
    pub const VKEY_V : u8 = 0x56;
    pub const VKEY_W : u8 = 0x57;
    pub const VKEY_X : u8 = 0x58;
    pub const VKEY_Y : u8 = 0x59;
    pub const VKEY_Z : u8 = 0x5A;
    pub const VKEY_LWIN : u8 = 0x5B;
    pub const VKEY_RWIN : u8 = 0x5C;
    pub const VKEY_APPS : u8 = 0x5D;
    pub const VKEY_SLEEP : u8 = 0x5F;
    pub const VKEY_NUMPAD0 : u8 = 0x60;
    pub const VKEY_NUMPAD1 : u8 = 0x61;
    pub const VKEY_NUMPAD2 : u8 = 0x62;
    pub const VKEY_NUMPAD3 : u8 = 0x63;
    pub const VKEY_NUMPAD4 : u8 = 0x64;
    pub const VKEY_NUMPAD5 : u8 = 0x65;
    pub const VKEY_NUMPAD6 : u8 = 0x66;
    pub const VKEY_NUMPAD7 : u8 = 0x67;
    pub const VKEY_NUMPAD8 : u8 = 0x68;
    pub const VKEY_NUMPAD9 : u8 = 0x69;
    pub const VKEY_MULTIPLY : u8 = 0x6A;
    pub const VKEY_ADD : u8 = 0x6B;
    pub const VKEY_SEPARATOR : u8 = 0x6C;
    pub const VKEY_SUBTRACT : u8 = 0x6D;
    pub const VKEY_DECIMAL : u8 = 0x6E;
    pub const VKEY_DIVIDE : u8 = 0x6F;
    pub const VKEY_F1 : u8 = 0x70;
    pub const VKEY_F2 : u8 = 0x71;
    pub const VKEY_F3 : u8 = 0x72;
    pub const VKEY_F4 : u8 = 0x73;
    pub const VKEY_F5 : u8 = 0x74;
    pub const VKEY_F6 : u8 = 0x75;
    pub const VKEY_F7 : u8 = 0x76;
    pub const VKEY_F8 : u8 = 0x77;
    pub const VKEY_F9 : u8 = 0x78;
    pub const VKEY_F10 : u8 = 0x79;
    pub const VKEY_F11 : u8 = 0x7A;
    pub const VKEY_F12 : u8 = 0x7B;
    pub const VKEY_F13 : u8 = 0x7C;
    pub const VKEY_F14 : u8 = 0x7D;
    pub const VKEY_F15 : u8 = 0x7E;
    pub const VKEY_F16 : u8 = 0x7F;
    pub const VKEY_F17 : u8 = 0x80;
    pub const VKEY_F18 : u8 = 0x81;
    pub const VKEY_F19 : u8 = 0x82;
    pub const VKEY_F20 : u8 = 0x83;
    pub const VKEY_F21 : u8 = 0x84;
    pub const VKEY_F22 : u8 = 0x85;
    pub const VKEY_F23 : u8 = 0x86;
    pub const VKEY_F24 : u8 = 0x87;
    pub const VKEY_NUMLOCK : u8 = 0x90;
    pub const VKEY_SCROLL : u8 = 0x91;
    pub const VKEY_LSHIFT : u8 = 0xA0;
    pub const VKEY_RSHIFT : u8 = 0xA1;
    pub const VKEY_LCONTROL : u8 = 0xA2;
    pub const VKEY_RCONTROL : u8 = 0xA3;
    pub const VKEY_LMENU : u8 = 0xA4;
    pub const VKEY_RMENU : u8 = 0xA5;
    pub const VKEY_BROWSER_BACK : u8 = 0xA6;
    pub const VKEY_BROWSER_FORWARD : u8 = 0xA7;
    pub const VKEY_BROWSER_REFRESH : u8 = 0xA8;
    pub const VKEY_BROWSER_STOP : u8 = 0xA9;
    pub const VKEY_BROWSER_SEARCH : u8 = 0xAA;
    pub const VKEY_BROWSER_FAVORITES : u8 = 0xAB;
    pub const VKEY_BROWSER_HOME : u8 = 0xAC;
    pub const VKEY_VOLUME_MUTE : u8 = 0xAD;
    pub const VKEY_VOLUME_DOWN : u8 = 0xAE;
    pub const VKEY_VOLUME_UP : u8 = 0xAF;
    pub const VKEY_MEDIA_NEXT_TRACK : u8 = 0xB0;
    pub const VKEY_MEDIA_PREV_TRACK : u8 = 0xB1;
    pub const VKEY_MEDIA_STOP : u8 = 0xB2;
    pub const VKEY_MEDIA_PLAY_PAUSE : u8 = 0xB3;
    pub const VKEY_MEDIA_LAUNCH_MAIL : u8 = 0xB4;
    pub const VKEY_MEDIA_LAUNCH_MEDIA_SELECT : u8 = 0xB5;
    pub const VKEY_MEDIA_LAUNCH_APP1 : u8 = 0xB6;
    pub const VKEY_MEDIA_LAUNCH_APP2 : u8 = 0xB7;
    pub const VKEY_OEM_1 : u8 = 0xBA;
    pub const VKEY_OEM_PLUS : u8 = 0xBB;
    pub const VKEY_OEM_COMMA : u8 = 0xBC;
    pub const VKEY_OEM_MINUS : u8 = 0xBD;
    pub const VKEY_OEM_PERIOD : u8 = 0xBE;
    pub const VKEY_OEM_2 : u8 = 0xBF;
    pub const VKEY_OEM_3 : u8 = 0xC0;
    pub const VKEY_OEM_4 : u8 = 0xDB;
    pub const VKEY_OEM_5 : u8 = 0xDC;
    pub const VKEY_OEM_6 : u8 = 0xDD;
    pub const VKEY_OEM_7 : u8 = 0xDE;
    pub const VKEY_OEM_8 : u8 = 0xDF;
    pub const VKEY_OEM_102 : u8 = 0xE2;
    pub const VKEY_OEM_103 : u8 = 0xE3;  // GTV KEYCODE_MEDIA_REWIND
    pub const VKEY_OEM_104 : u8 = 0xE4;  // GTV KEYCODE_MEDIA_FAST_FORWARD
    pub const VKEY_PROCESSKEY : u8 = 0xE5;
    pub const VKEY_PACKET : u8 = 0xE7;
    pub const VKEY_DBE_SBCSCHAR : u8 = 0xF3;
    pub const VKEY_DBE_DBCSCHAR : u8 = 0xF4;
    pub const VKEY_ATTN : u8 = 0xF6;
    pub const VKEY_CRSEL : u8 = 0xF7;
    pub const VKEY_EXSEL : u8 = 0xF8;
    pub const VKEY_EREOF : u8 = 0xF9;
    pub const VKEY_PLAY : u8 = 0xFA;
    pub const VKEY_ZOOM : u8 = 0xFB;
    pub const VKEY_NONAME : u8 = 0xFC;
    pub const VKEY_PA1 : u8 = 0xFD;
    pub const VKEY_OEM_CLEAR : u8 = 0xFE;
    pub const VKEY_UNKNOWN : u8 = 0x0;

    // POSIX specific VKEYs. Note that as of Windows SDK 7.1, 0x97-9F, 0xD8-DA,
    // and 0xE8 are unassigned.
    pub const VKEY_WLAN : u8 = 0x97;
    pub const VKEY_POWER : u8 = 0x98;
    pub const VKEY_BRIGHTNESS_DOWN : u8 = 0xD8;
    pub const VKEY_BRIGHTNESS_UP : u8 = 0xD9;
    pub const VKEY_KBD_BRIGHTNESS_DOWN : u8 = 0xDA;
    pub const VKEY_KBD_BRIGHTNESS_UP : u8 = 0xE8;

    // Windows does not have a specific key code for AltGr. We use the unused 0xE1
    // (VK_OEM_AX) code to represent AltGr, matching the behaviour of Firefox on
    // Linux.
    pub const VKEY_ALTGR : u8 = 0xE1;
    // Windows does not have a specific key code for Compose. We use the unused
    // 0xE6 (VK_ICO_CLEAR) code to represent Compose.
    pub const VKEY_COMPOSE : u8 = 0xE6;
}

// this is way too much work to do 100% correctly right now.
// see xkb_keyboard_layout_engine.cc -> XkbKeyboardLayoutEngine::Lookup in chromium for details
fn get_key_msg(keycode: c_int, character: u16) -> Option<constellation_msg::Key> {
    match keycode as u8 {
        KeyboardCode::VKEY_BACK => Some(constellation_msg::Key::Backspace),
        KeyboardCode::VKEY_RIGHT => Some(constellation_msg::Key::Right),
        KeyboardCode::VKEY_LEFT => Some(constellation_msg::Key::Left),
        KeyboardCode::VKEY_UP => Some(constellation_msg::Key::Up),
        KeyboardCode::VKEY_DOWN => Some(constellation_msg::Key::Down),
        KeyboardCode::VKEY_RSHIFT => Some(constellation_msg::Key::RightShift),
        KeyboardCode::VKEY_SHIFT | KeyboardCode::VKEY_LSHIFT => Some(constellation_msg::Key::LeftShift),
        KeyboardCode::VKEY_RCONTROL => Some(constellation_msg::Key::RightControl),
        KeyboardCode::VKEY_CONTROL | KeyboardCode::VKEY_LCONTROL => Some(constellation_msg::Key::LeftControl),
        KeyboardCode::VKEY_LWIN => Some(constellation_msg::Key::LeftSuper),
        KeyboardCode::VKEY_RWIN => Some(constellation_msg::Key::RightSuper),
        KeyboardCode::VKEY_MENU => Some(constellation_msg::Key::LeftAlt),
        KeyboardCode::VKEY_APPS => Some(constellation_msg::Key::Menu),
        KeyboardCode::VKEY_ALTGR => Some(constellation_msg::Key::RightAlt), //not sure if correct...
        KeyboardCode::VKEY_ESCAPE => Some(constellation_msg::Key::Escape),
        KeyboardCode::VKEY_INSERT => Some(constellation_msg::Key::Insert),
        KeyboardCode::VKEY_DELETE => Some(constellation_msg::Key::Delete),
        KeyboardCode::VKEY_NEXT => Some(constellation_msg::Key::PageUp),
        KeyboardCode::VKEY_PRIOR => Some(constellation_msg::Key::PageDown),
        KeyboardCode::VKEY_HOME => Some(constellation_msg::Key::Home),
        KeyboardCode::VKEY_END => Some(constellation_msg::Key::End),
        KeyboardCode::VKEY_CAPITAL => Some(constellation_msg::Key::CapsLock),
        KeyboardCode::VKEY_F1 => Some(constellation_msg::Key::F1),
        KeyboardCode::VKEY_F2 => Some(constellation_msg::Key::F2),
        KeyboardCode::VKEY_F3 => Some(constellation_msg::Key::F3),
        KeyboardCode::VKEY_F4 => Some(constellation_msg::Key::F4),
        KeyboardCode::VKEY_F5 => Some(constellation_msg::Key::F5),
        KeyboardCode::VKEY_F6 => Some(constellation_msg::Key::F6),
        KeyboardCode::VKEY_F7 => Some(constellation_msg::Key::F7),
        KeyboardCode::VKEY_F8 => Some(constellation_msg::Key::F8),
        KeyboardCode::VKEY_F9 => Some(constellation_msg::Key::F9),
        KeyboardCode::VKEY_F10 => Some(constellation_msg::Key::F10),
        KeyboardCode::VKEY_F11 => Some(constellation_msg::Key::F11),
        KeyboardCode::VKEY_F12 => Some(constellation_msg::Key::F12),
        KeyboardCode::VKEY_F13 => Some(constellation_msg::Key::F13),
        KeyboardCode::VKEY_F14 => Some(constellation_msg::Key::F14),
        KeyboardCode::VKEY_F15 => Some(constellation_msg::Key::F15),
        KeyboardCode::VKEY_F16 => Some(constellation_msg::Key::F16),
        KeyboardCode::VKEY_F17 => Some(constellation_msg::Key::F17),
        KeyboardCode::VKEY_F18 => Some(constellation_msg::Key::F18),
        KeyboardCode::VKEY_F19 => Some(constellation_msg::Key::F19),
        KeyboardCode::VKEY_F20 => Some(constellation_msg::Key::F20),
        KeyboardCode::VKEY_F21 => Some(constellation_msg::Key::F21),
        KeyboardCode::VKEY_F22 => Some(constellation_msg::Key::F22),
        KeyboardCode::VKEY_F23 => Some(constellation_msg::Key::F23),
        KeyboardCode::VKEY_F24 => Some(constellation_msg::Key::F24),
        KeyboardCode::VKEY_NUMPAD0 => Some(constellation_msg::Key::Kp0),
        KeyboardCode::VKEY_NUMPAD1 => Some(constellation_msg::Key::Kp1),
        KeyboardCode::VKEY_NUMPAD2 => Some(constellation_msg::Key::Kp2),
        KeyboardCode::VKEY_NUMPAD3 => Some(constellation_msg::Key::Kp3),
        KeyboardCode::VKEY_NUMPAD4 => Some(constellation_msg::Key::Kp4),
        KeyboardCode::VKEY_NUMPAD5 => Some(constellation_msg::Key::Kp5),
        KeyboardCode::VKEY_NUMPAD6 => Some(constellation_msg::Key::Kp6),
        KeyboardCode::VKEY_NUMPAD7 => Some(constellation_msg::Key::Kp7),
        KeyboardCode::VKEY_NUMPAD8 => Some(constellation_msg::Key::Kp8),
        KeyboardCode::VKEY_NUMPAD9 => Some(constellation_msg::Key::Kp9),
        KeyboardCode::VKEY_DECIMAL => Some(constellation_msg::Key::KpDecimal),
        KeyboardCode::VKEY_DIVIDE => Some(constellation_msg::Key::KpDivide),
        KeyboardCode::VKEY_MULTIPLY => Some(constellation_msg::Key::KpMultiply),
        KeyboardCode::VKEY_SUBTRACT => Some(constellation_msg::Key::KpSubtract),
        KeyboardCode::VKEY_ADD => Some(constellation_msg::Key::KpAdd),
        KeyboardCode::VKEY_NUMLOCK => Some(constellation_msg::Key::NumLock),
        KeyboardCode::VKEY_PRINT => Some(constellation_msg::Key::PrintScreen),
        KeyboardCode::VKEY_PAUSE => Some(constellation_msg::Key::Pause),
        //VKEY_BACK
        _ => { match character as u8 {
                 b'[' => Some(constellation_msg::Key::LeftBracket),
                 b']' => Some(constellation_msg::Key::RightBracket),
                 b'=' => Some(constellation_msg::Key::Equal),
                 b';' => Some(constellation_msg::Key::Semicolon),
                 b'/' => Some(constellation_msg::Key::Slash),
                 b'.' => Some(constellation_msg::Key::Period),
                 b'-' => Some(constellation_msg::Key::Minus),
                 b',' => Some(constellation_msg::Key::Comma),
                 b'\'' => Some(constellation_msg::Key::Apostrophe),
                 b'\\' => Some(constellation_msg::Key::Backslash),
                 b'`' => Some(constellation_msg::Key::GraveAccent),
                 b'\t' => Some(constellation_msg::Key::Tab),
                 b'a' | b'A' => Some(constellation_msg::Key::A),
                 b'b' | b'B' => Some(constellation_msg::Key::B),
                 b'c' | b'C' => Some(constellation_msg::Key::C),
                 b'd' | b'D' => Some(constellation_msg::Key::D),
                 b'e' | b'E' => Some(constellation_msg::Key::E),
                 b'f' | b'F' => Some(constellation_msg::Key::F),
                 b'g' | b'G' => Some(constellation_msg::Key::G),
                 b'h' | b'H' => Some(constellation_msg::Key::H),
                 b'i' | b'I' => Some(constellation_msg::Key::I),
                 b'j' | b'J' => Some(constellation_msg::Key::J),
                 b'k' | b'K' => Some(constellation_msg::Key::K),
                 b'l' | b'L' => Some(constellation_msg::Key::L),
                 b'm' | b'M' => Some(constellation_msg::Key::M),
                 b'n' | b'N' => Some(constellation_msg::Key::N),
                 b'o' | b'O' => Some(constellation_msg::Key::O),
                 b'p' | b'P' => Some(constellation_msg::Key::P),
                 b'q' | b'Q' => Some(constellation_msg::Key::Q),
                 b'r' | b'R' => Some(constellation_msg::Key::R),
                 b's' | b'S' => Some(constellation_msg::Key::S),
                 b't' | b'T' => Some(constellation_msg::Key::T),
                 b'u' | b'U' => Some(constellation_msg::Key::U),
                 b'v' | b'V' => Some(constellation_msg::Key::V),
                 b'w' | b'W' => Some(constellation_msg::Key::W),
                 b'x' | b'X' => Some(constellation_msg::Key::X),
                 b'y' | b'Y' => Some(constellation_msg::Key::Y),
                 b'z' | b'Z' => Some(constellation_msg::Key::Z),
                 b'0' => Some(constellation_msg::Key::Num0),
                 b'1' => Some(constellation_msg::Key::Num1),
                 b'2' => Some(constellation_msg::Key::Num2),
                 b'3' => Some(constellation_msg::Key::Num3),
                 b'4' => Some(constellation_msg::Key::Num4),
                 b'5' => Some(constellation_msg::Key::Num5),
                 b'6' => Some(constellation_msg::Key::Num6),
                 b'7' => Some(constellation_msg::Key::Num7),
                 b'8' => Some(constellation_msg::Key::Num8),
                 b'9' => Some(constellation_msg::Key::Num9),
                 b'\n' | b'\r' => Some(constellation_msg::Key::Enter),
                 b' ' => Some(constellation_msg::Key::Space),
                 _ => None
             }
        }
    }
}

// unhandled
//pub enum Key {
    //World1,
    //World2,
    //ScrollLock,
    //KpEnter,
    //KpEqual,
    //RightAlt,
//}

full_cef_class_impl! {
    ServoCefBrowserHost : CefBrowserHost, cef_browser_host_t {
        fn get_client(&this,) -> *mut cef_client_t {{
            this.downcast().client.clone()
        }}
        fn get_browser(&this,) -> *mut cef_browser_t {{
            let browser = this.downcast().browser.borrow_mut();
            browser.clone().unwrap()
        }}

        fn was_resized(&this,) -> () {{
            let mut rect = cef_rect_t::zero();
            if cfg!(target_os="macos") {
                if check_ptr_exist!(this.get_client(), get_render_handler) &&
                   check_ptr_exist!(this.get_client().get_render_handler(), get_backing_rect) {
                    this.get_client()
                        .get_render_handler()
                        .get_backing_rect(this.downcast().browser.borrow().clone().unwrap(), &mut rect);
                }
            } else if check_ptr_exist!(this.get_client(), get_render_handler) &&
               check_ptr_exist!(this.get_client().get_render_handler(), get_view_rect) {
                this.get_client()
                    .get_render_handler()
                    .get_view_rect(this.downcast().browser.borrow().clone().unwrap(), &mut rect);
               }
            let size = TypedSize2D::new(rect.width as u32, rect.height as u32);
            this.downcast().send_window_event(WindowEvent::Resize(size));
        }}

        fn close_browser(&this, _force: c_int [c_int],) -> () {{
            browser::close(this.downcast().browser.borrow_mut().take().unwrap());
        }}

        fn send_focus_event(&this, focus: c_int [c_int],) -> () {{
            let focus: c_int = focus;
            if focus != 0 {
                this.downcast().send_window_event(WindowEvent::Refresh);
            }
        }}

        fn send_key_event(&this, event: *const cef_key_event [&cef_key_event],) -> () {{
            let event: &cef_key_event = event;
            let key = match get_key_msg((*event).windows_key_code, (*event).character) {
                Some(keycode) => keycode,
                None => {
                    error!("Unhandled keycode({}) passed!", (*event).windows_key_code);
                    return;
                }
            };
            let key_state = match (*event).t {
                // in tests with cef-real, this event had no effect
                KEYEVENT_RAWKEYDOWN => return,
                KEYEVENT_KEYDOWN => KeyState::Pressed,
                KEYEVENT_CHAR => KeyState::Repeated,
                KEYEVENT_KEYUP => KeyState::Released,
            };
            let mut key_modifiers = KeyModifiers::empty();
            if (*event).modifiers & EVENTFLAG_SHIFT_DOWN as u32 != 0 {
               key_modifiers = key_modifiers | constellation_msg::SHIFT;
            }
            if (*event).modifiers & EVENTFLAG_CONTROL_DOWN as u32 != 0 {
               key_modifiers = key_modifiers | constellation_msg::CONTROL;
            }
            if (*event).modifiers & EVENTFLAG_ALT_DOWN as u32 != 0 {
               key_modifiers = key_modifiers | constellation_msg::ALT;
            }
            let ch = char::from_u32((*event).character as u32);
            this.downcast().send_window_event(WindowEvent::KeyEvent(ch, key, key_state, key_modifiers))
        }}

        fn send_mouse_click_event(&this,
                                  event: *const cef_mouse_event [&cef_mouse_event],
                                  mouse_button_type: cef_mouse_button_type_t [cef_mouse_button_type_t],
                                  mouse_up: c_int [c_int],
                                  _click_count: c_int [c_int],)
                                  -> () {{
            let event: &cef_mouse_event = event;
            let mouse_button_type: cef_mouse_button_type_t = mouse_button_type;
            let mouse_up: c_int = mouse_up;
            let button_type = match mouse_button_type {
                cef_mouse_button_type_t::MBT_LEFT => MouseButton::Left,
                cef_mouse_button_type_t::MBT_MIDDLE => MouseButton::Middle,
                cef_mouse_button_type_t::MBT_RIGHT => MouseButton::Right,
            };
            let point = TypedPoint2D::new((*event).x as f32, (*event).y as f32);
            if mouse_up != 0 {
                this.downcast().send_window_event(WindowEvent::MouseWindowEventClass(
                    MouseWindowEvent::Click(button_type, point)))
            } else {
                this.downcast().send_window_event(WindowEvent::MouseWindowEventClass(
                    MouseWindowEvent::MouseUp(button_type, point)))
            }
        }}

        fn send_mouse_move_event(&this, event: *const cef_mouse_event [&cef_mouse_event],
                                 _mouse_exited: c_int [c_int],)
                                 -> () {{
            let event: &cef_mouse_event = event;
            let point = TypedPoint2D::new((*event).x as f32, (*event).y as f32);
            this.downcast().send_window_event(WindowEvent::MouseWindowMoveEventClass(point))
        }}

        fn send_mouse_wheel_event(&this,
                                  event: *const cef_mouse_event [&cef_mouse_event],
                                  delta_x: c_int [c_int],
                                  delta_y: c_int [c_int],)
                                  -> () {{
            let event: &cef_mouse_event = event;
            let delta_x: c_int = delta_x;
            let delta_y: c_int = delta_y;
            let delta = TypedVector2D::new(delta_x as f32, delta_y as f32);
            let origin = TypedPoint2D::new((*event).x as i32, (*event).y as i32);
            this.downcast().send_window_event(WindowEvent::Scroll(ScrollLocation::Delta(delta),
                                                                  origin,
                                                                  TouchEventType::Move))
        }}

        fn get_zoom_level(&this,) -> c_double {{
            this.downcast().pinch_zoom_level() as c_double
        }}

        fn set_zoom_level(&this, new_zoom_level: c_double [c_double],) -> () {{
            let new_zoom_level: c_double = new_zoom_level;
            let old_zoom_level = this.get_zoom_level();
            this.downcast().send_window_event(WindowEvent::PinchZoom((new_zoom_level / old_zoom_level) as f32))
        }}

        fn initialize_compositing(&_this,) -> () {{
        }}

        fn composite(&this,) -> () {{
            this.downcast().composite_ok.set(true);
            this.downcast().send_window_event(WindowEvent::Refresh);
            this.downcast().composite_ok.set(false);
        }}

        fn get_window_handle(&this,) -> cef_window_handle_t {{
            let t = this.downcast();
            let browser = t.browser.borrow();
            browser::get_window(&browser.as_ref().unwrap()) as cef_window_handle_t
        }}
    }
}

impl ServoCefBrowserHost {
    pub fn new(client: CefClient) -> ServoCefBrowserHost {
        ServoCefBrowserHost {
            browser: RefCell::new(None),
            client: client,
            composite_ok: Cell::new(false),
        }
    }

    fn send_window_event(&self, event: WindowEvent) {
        self.browser.borrow_mut().as_mut().unwrap().send_window_event(event);
    }

    fn pinch_zoom_level(&self) -> f32 {
        self.browser.borrow_mut().as_mut().unwrap().pinch_zoom_level()
    }
}

pub trait ServoCefBrowserHostExtensions {
    fn set_browser(&self, browser: CefBrowser);
}

impl ServoCefBrowserHostExtensions for CefBrowserHost {
    fn set_browser(&self, browser: CefBrowser) {
        *self.downcast().browser.borrow_mut() = Some(browser)
    }
}

