servo: Merge #4583 - Add support for multiple cef browsers (multiple tabs), in-process for now (from glennw:cef-tabs); r=pcwalton
authorGlenn Watson <gw@intuitionlibrary.com>
Tue, 13 Jan 2015 12:27:48 -0700
changeset 382239 d405aef1a723fd3985bed036fc4a9bf985e69b2e
parent 382238 e4cad6721b3422a59eb109c81e61f54d3ec21568
child 382240 8e31ee5825333c06a1f1bd28b27e43ee47fbf9aa
push id7198
push userjlorenzo@mozilla.com
push dateTue, 18 Apr 2017 12:07:49 +0000
treeherdermozilla-beta@d57aa49c3948 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspcwalton
servo: Merge #4583 - Add support for multiple cef browsers (multiple tabs), in-process for now (from glennw:cef-tabs); r=pcwalton Source-Repo: https://github.com/servo/servo Source-Revision: 9b6d979f19d9e6433cf7c1da0f52ffcc2dac02a6
servo/ports/cef/browser.rs
servo/ports/cef/browser_host.rs
servo/ports/cef/core.rs
servo/ports/cef/frame.rs
--- a/servo/ports/cef/browser.rs
+++ b/servo/ports/cef/browser.rs
@@ -1,41 +1,74 @@
 /* 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_host::{ServoCefBrowserHost, ServoCefBrowserHostExtensions};
-use core::{mod, ServoCefGlobals, globals};
 use eutil::Downcast;
-use frame::ServoCefFrame;
+use frame::{ServoCefFrame, ServoCefFrameExtensions};
 use interfaces::{CefBrowser, CefBrowserHost, CefClient, CefFrame, CefRequestContext};
 use interfaces::{cef_browser_t, cef_browser_host_t, cef_client_t, cef_frame_t};
 use interfaces::{cef_request_context_t};
 use servo::Browser;
 use types::{cef_browser_settings_t, cef_string_t, cef_window_info_t};
 use window;
 
 use compositing::windowing::{WindowNavigateMsg, WindowEvent};
 use glfw_app;
 use libc::c_int;
 use servo_util::opts;
 use std::cell::{Cell, RefCell};
 
+thread_local!(pub static BROWSERS: RefCell<Vec<CefBrowser>> = RefCell::new(vec!()))
+
+pub enum ServoBrowser {
+    Invalid,
+    OnScreen(Browser<glfw_app::window::Window>),
+    OffScreen(Browser<window::Window>),
+}
+
+impl ServoBrowser {
+    fn handle_event(&mut self, event: WindowEvent) {
+        match *self {
+            ServoBrowser::OnScreen(ref mut browser) => { browser.handle_event(event); }
+            ServoBrowser::OffScreen(ref mut browser) => { browser.handle_event(event); }
+            ServoBrowser::Invalid => {}
+        }
+    }
+
+    pub fn get_title_for_main_frame(&self) {
+        match *self {
+            ServoBrowser::OnScreen(ref browser) => browser.get_title_for_main_frame(),
+            ServoBrowser::OffScreen(ref browser) => browser.get_title_for_main_frame(),
+            ServoBrowser::Invalid => {}
+        }
+    }
+
+    pub fn pinch_zoom_level(&self) -> f32 {
+        match *self {
+            ServoBrowser::OnScreen(ref browser) => browser.pinch_zoom_level(),
+            ServoBrowser::OffScreen(ref browser) => browser.pinch_zoom_level(),
+            ServoBrowser::Invalid => 1.0,
+        }
+    }
+}
+
 cef_class_impl! {
     ServoCefBrowser : CefBrowser, cef_browser_t {
         fn get_host(&this) -> *mut cef_browser_host_t {
             this.downcast().host.clone()
         }
 
-        fn go_back(&_this) -> () {
-            core::send_window_event(WindowEvent::Navigation(WindowNavigateMsg::Back));
+        fn go_back(&this) -> () {
+            this.send_window_event(WindowEvent::Navigation(WindowNavigateMsg::Back));
         }
 
-        fn go_forward(&_this) -> () {
-            core::send_window_event(WindowEvent::Navigation(WindowNavigateMsg::Forward));
+        fn go_forward(&this) -> () {
+            this.send_window_event(WindowEvent::Navigation(WindowNavigateMsg::Forward));
         }
 
         // Returns the main (top-level) frame for the browser window.
         fn get_main_frame(&this) -> *mut cef_frame_t {
             this.downcast().frame.clone()
         }
     }
 }
@@ -44,63 +77,103 @@ pub struct ServoCefBrowser {
     /// A reference to the browser's primary frame.
     pub frame: CefFrame,
     /// A reference to the browser's host.
     pub host: CefBrowserHost,
     /// A reference to the browser client.
     pub client: CefClient,
     /// Whether the on-created callback has fired yet.
     pub callback_executed: Cell<bool>,
+
+    servo_browser: RefCell<ServoBrowser>,
+    message_queue: RefCell<Vec<WindowEvent>>,
 }
 
 impl ServoCefBrowser {
     pub fn new(window_info: &cef_window_info_t, client: CefClient) -> ServoCefBrowser {
         let frame = ServoCefFrame::new().as_cef_interface();
         let host = ServoCefBrowserHost::new(client.clone()).as_cef_interface();
-        if window_info.windowless_rendering_enabled == 0 {
-            globals.with(|ref r| {
-                let glfw_window = glfw_app::create_window();
-                *r.borrow_mut() = Some(ServoCefGlobals::OnScreenGlobals(
-                    RefCell::new(glfw_window.clone()),
-                    RefCell::new(Browser::new(Some(glfw_window)))));
-            });
-        }
+
+        let servo_browser = if window_info.windowless_rendering_enabled == 0 {
+            let glfw_window = glfw_app::create_window();
+            let servo_browser = Browser::new(Some(glfw_window.clone()));
+            ServoBrowser::OnScreen(servo_browser)
+        } else {
+            ServoBrowser::Invalid
+        };
 
         ServoCefBrowser {
             frame: frame,
             host: host,
             client: client,
             callback_executed: Cell::new(false),
+            servo_browser: RefCell::new(servo_browser),
+            message_queue: RefCell::new(vec!()),
         }
     }
 }
 
-trait ServoCefBrowserExtensions {
+pub trait ServoCefBrowserExtensions {
     fn init(&self, window_info: &cef_window_info_t);
+    fn send_window_event(&self, event: WindowEvent);
+    fn get_title_for_main_frame(&self);
+    fn pinch_zoom_level(&self) -> f32;
 }
 
 impl ServoCefBrowserExtensions for CefBrowser {
     fn init(&self, window_info: &cef_window_info_t) {
         if window_info.windowless_rendering_enabled != 0 {
-            globals.with(|ref r| {
-                let window = window::Window::new();
-                let servo_browser = Browser::new(Some(window.clone()));
-                window.set_browser(self.clone());
-
-                *r.borrow_mut() = Some(ServoCefGlobals::OffScreenGlobals(
-                    RefCell::new(window),
-                    RefCell::new(servo_browser)));
-            });
+            let window = window::Window::new();
+            let servo_browser = Browser::new(Some(window.clone()));
+            window.set_browser(self.clone());
+            *self.downcast().servo_browser.borrow_mut() = ServoBrowser::OffScreen(servo_browser);
         }
 
         self.downcast().host.set_browser((*self).clone());
+        self.downcast().frame.set_browser((*self).clone());
+    }
+
+    fn send_window_event(&self, event: WindowEvent) {
+        self.downcast().message_queue.borrow_mut().push(event);
+
+        loop {
+            match self.downcast().servo_browser.try_borrow_mut() {
+                None => {
+                    // We're trying to send an event while processing another one. This will
+                    // cause general badness, so queue up that event instead of immediately
+                    // processing it.
+                    break
+                }
+                Some(ref mut browser) => {
+                    let event = match self.downcast().message_queue.borrow_mut().pop() {
+                        None => return,
+                        Some(event) => event,
+                    };
+                    browser.handle_event(event);
+                }
+            }
+        }
+    }
+
+    fn get_title_for_main_frame(&self) {
+        self.downcast().servo_browser.borrow().get_title_for_main_frame()
+    }
+
+    fn pinch_zoom_level(&self) -> f32 {
+        self.downcast().servo_browser.borrow().pinch_zoom_level()
     }
 }
 
-thread_local!(pub static GLOBAL_BROWSERS: RefCell<Vec<CefBrowser>> = RefCell::new(vec!()))
+pub fn update() {
+    BROWSERS.with(|browsers| {
+        for browser in browsers.borrow().iter() {
+            browser.send_window_event(WindowEvent::Idle);
+        }
+    });
+}
 
 pub fn browser_callback_after_created(browser: CefBrowser) {
     if browser.downcast().client.is_null_cef_object() {
         return
     }
     let client = browser.downcast().client.clone();
     let life_span_handler = client.get_life_span_handler();
     if life_span_handler.is_not_null_cef_object() {
@@ -117,17 +190,19 @@ fn browser_host_create(window_info: &cef
     urls.push("http://s27.postimg.org/vqbtrolyr/servo.jpg".into_string());
     let mut opts = opts::default_opts();
     opts.urls = urls;
     let browser = ServoCefBrowser::new(window_info, client).as_cef_interface();
     browser.init(window_info);
     if callback_executed {
         browser_callback_after_created(browser.clone());
     }
-    GLOBAL_BROWSERS.with(|ref r| r.borrow_mut().push(browser.clone()));
+    BROWSERS.with(|browsers| {
+        browsers.borrow_mut().push(browser.clone());
+    });
     browser
 }
 
 cef_static_method_impls! {
     fn cef_browser_host_create_browser(window_info: *const cef_window_info_t,
                                        client: *mut cef_client_t,
                                        _url: *const cef_string_t,
                                        _browser_settings: *const cef_browser_settings_t,
--- a/servo/ports/cef/browser_host.rs
+++ b/servo/ports/cef/browser_host.rs
@@ -1,17 +1,17 @@
 /* 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 core;
 use eutil::Downcast;
 use interfaces::{CefBrowser, CefBrowserHost, CefClient, cef_browser_host_t, cef_client_t};
 use types::{cef_mouse_button_type_t, cef_mouse_event, cef_rect_t, cef_key_event};
 use types::cef_key_event_type_t::{KEYEVENT_CHAR, KEYEVENT_KEYDOWN, KEYEVENT_KEYUP, KEYEVENT_RAWKEYDOWN};
+use browser::ServoCefBrowserExtensions;
 
 use compositing::windowing::{WindowEvent, MouseWindowEvent};
 use geom::point::TypedPoint2D;
 use geom::size::TypedSize2D;
 use libc::{c_double, c_int};
 use servo_msg::constellation_msg::{mod, KeyModifiers, KeyState};
 use std::cell::RefCell;
 
@@ -29,21 +29,30 @@ cef_class_impl! {
         }
 
         fn was_resized(&this) -> () {
             let mut rect = cef_rect_t::zero();
             this.get_client()
                 .get_render_handler()
                 .get_backing_rect(this.downcast().browser.borrow().clone().unwrap(), &mut rect);
             let size = TypedSize2D(rect.width as uint, rect.height as uint);
-            core::send_window_event(WindowEvent::Resize(size));
-            core::repaint_synchronously();
+            this.downcast().send_window_event(WindowEvent::Resize(size));
+        }
+
+        fn close_browser(&_this, _force: c_int) -> () {
+            // TODO: Clean shutdown.
         }
 
-        fn send_key_event(&_this, event: *const cef_key_event) -> () {
+        fn send_focus_event(&this, focus: c_int) -> () {
+            if focus != 0 {
+                this.downcast().send_window_event(WindowEvent::Refresh);
+            }
+        }
+
+        fn send_key_event(&this, event: *const cef_key_event) -> () {
             // FIXME(pcwalton): So awful. But it's nearly midnight here and I have to get
             // Google working.
             let event: &cef_key_event = event;
             let key = match (*event).character as u8 {
                 b'a' | b'A' => constellation_msg::Key::A,
                 b'b' | b'B' => constellation_msg::Key::B,
                 b'c' | b'C' => constellation_msg::Key::C,
                 b'd' | b'D' => constellation_msg::Key::D,
@@ -83,77 +92,85 @@ cef_class_impl! {
                 _ => constellation_msg::Key::Space,
             };
             let key_state = match (*event).t {
                 KEYEVENT_RAWKEYDOWN => KeyState::Pressed,
                 KEYEVENT_KEYDOWN | KEYEVENT_CHAR => KeyState::Repeated,
                 KEYEVENT_KEYUP => KeyState::Released,
             };
             let key_modifiers = KeyModifiers::empty();  // TODO(pcwalton)
-            core::send_window_event(WindowEvent::KeyEvent(key, key_state, key_modifiers))
+            this.downcast().send_window_event(WindowEvent::KeyEvent(key, key_state, key_modifiers))
         }
 
-        fn send_mouse_click_event(&_this,
+        fn send_mouse_click_event(&this,
                                   event: *const cef_mouse_event,
                                   mouse_button_type: cef_mouse_button_type_t,
                                   mouse_up: c_int,
                                   _click_count: c_int)
                                   -> () {
             let event: &cef_mouse_event = event;
             let button_type = mouse_button_type as uint;
             let point = TypedPoint2D((*event).x as f32, (*event).y as f32);
             if mouse_up != 0 {
-                core::send_window_event(WindowEvent::MouseWindowEventClass(
+                this.downcast().send_window_event(WindowEvent::MouseWindowEventClass(
                     MouseWindowEvent::Click(button_type, point)))
             } else {
-                core::send_window_event(WindowEvent::MouseWindowEventClass(
+                this.downcast().send_window_event(WindowEvent::MouseWindowEventClass(
                     MouseWindowEvent::MouseUp(button_type, point)))
             }
         }
 
-        fn send_mouse_move_event(&_this, event: *const cef_mouse_event, _mouse_exited: c_int)
+        fn send_mouse_move_event(&this, event: *const cef_mouse_event, _mouse_exited: c_int)
                                  -> () {
             let event: &cef_mouse_event = event;
             let point = TypedPoint2D((*event).x as f32, (*event).y as f32);
-            core::send_window_event(WindowEvent::MouseWindowMoveEventClass(point))
+            this.downcast().send_window_event(WindowEvent::MouseWindowMoveEventClass(point))
         }
 
-        fn send_mouse_wheel_event(&_this,
+        fn send_mouse_wheel_event(&this,
                                   event: *const cef_mouse_event,
                                   delta_x: c_int,
                                   delta_y: c_int)
                                   -> () {
             let event: &cef_mouse_event = event;
             let delta = TypedPoint2D(delta_x as f32, delta_y as f32);
             let origin = TypedPoint2D((*event).x as i32, (*event).y as i32);
-            core::send_window_event(WindowEvent::Scroll(delta, origin))
+            this.downcast().send_window_event(WindowEvent::Scroll(delta, origin))
         }
 
-        fn get_zoom_level(&_this) -> c_double {
-            core::pinch_zoom_level() as c_double
+        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) -> () {
             let old_zoom_level = this.get_zoom_level();
-            core::send_window_event(WindowEvent::PinchZoom((new_zoom_level / old_zoom_level) as f32))
+            this.downcast().send_window_event(WindowEvent::PinchZoom((new_zoom_level / old_zoom_level) as f32))
         }
 
-        fn initialize_compositing(&_this) -> () {
-            core::send_window_event(WindowEvent::InitializeCompositing);
+        fn initialize_compositing(&this) -> () {
+            this.downcast().send_window_event(WindowEvent::InitializeCompositing);
         }
     }
 }
 
 impl ServoCefBrowserHost {
     pub fn new(client: CefClient) -> ServoCefBrowserHost {
         ServoCefBrowserHost {
             browser: RefCell::new(None),
             client: client,
         }
     }
+
+    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) {
--- a/servo/ports/cef/core.rs
+++ b/servo/ports/cef/core.rs
@@ -1,45 +1,31 @@
 /* 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 command_line::command_line_init;
 use interfaces::cef_app_t;
 use types::{cef_main_args_t, cef_settings_t};
-use window;
 
-use compositing::windowing::WindowEvent;
 use geom::size::TypedSize2D;
-use glfw_app;
 use libc::{c_char, c_int, c_void};
-use servo::Browser;
+use rustrt::local::Local;
+use rustrt::task;
 use servo_util::opts;
 use servo_util::opts::RenderApi;
 use std::c_str::CString;
-use std::cell::RefCell;
-use std::rc::Rc;
 use std::rt;
+use browser;
 
 const MAX_RENDERING_THREADS: uint = 128;
 
 // TODO(pcwalton): Get the home page via the CEF API.
 static HOME_URL: &'static str = "http://s27.postimg.org/vqbtrolyr/servo.jpg";
 
-// TODO(pcwalton): Support multiple windows.
-pub enum ServoCefGlobals {
-    OnScreenGlobals(RefCell<Rc<glfw_app::window::Window>>,
-                    RefCell<Browser<glfw_app::window::Window>>),
-    OffScreenGlobals(RefCell<Rc<window::Window>>, RefCell<Browser<window::Window>>),
-}
-
-thread_local!(pub static globals: Rc<RefCell<Option<ServoCefGlobals>>> = Rc::new(RefCell::new(None)))
-
-thread_local!(pub static message_queue: Rc<RefCell<Vec<WindowEvent>>> = Rc::new(RefCell::new(vec!())))
-
 static CEF_API_HASH_UNIVERSAL: &'static [u8] = b"8efd129f4afc344bd04b2feb7f73a149b6c4e27f\0";
 #[cfg(target_os="windows")]
 static CEF_API_HASH_PLATFORM: &'static [u8] = b"5c7f3e50ff5265985d11dc1a466513e25748bedd\0";
 #[cfg(target_os="macos")]
 static CEF_API_HASH_PLATFORM: &'static [u8] = b"6813214accbf2ebfb6bdcf8d00654650b251bf3d\0";
 #[cfg(target_os="linux")]
 static CEF_API_HASH_PLATFORM: &'static [u8] = b"2bc564c3871965ef3a2531b528bda3e17fa17a6d\0";
 
@@ -48,16 +34,21 @@ fn resources_path() -> Option<String> {
     Some("../../servo/resources".into_string())
 }
 
 #[cfg(not(target_os="linux"))]
 fn resources_path() -> Option<String> {
     None
 }
 
+fn create_rust_task() {
+    let task = box task::Task::new(None, None);
+    Local::put(task);
+}
+
 #[no_mangle]
 pub extern "C" fn cef_initialize(args: *const cef_main_args_t,
                                  settings: *mut cef_settings_t,
                                  application: *mut cef_app_t,
                                  _windows_sandbox_info: *const c_void)
                                  -> c_int {
     if args.is_null() {
         return 0;
@@ -72,16 +63,18 @@ pub extern "C" fn cef_initialize(args: *
                     let handler = cb(application);
                     if handler.is_not_null() {
                         (*handler).on_context_initialized.map(|hcb| hcb(handler));
                     }
             });
         }
     }
 
+    create_rust_task();
+
     let urls = vec![HOME_URL.into_string()];
     opts::set_opts(opts::Opts {
         urls: urls,
         n_paint_threads: 1,
         gpu_painting: false,
         tile_size: 512,
         device_pixels_per_px: None,
         time_profiler_period: None,
@@ -119,119 +112,41 @@ pub extern "C" fn cef_initialize(args: *
 }
 
 #[no_mangle]
 pub extern "C" fn cef_shutdown() {
 }
 
 #[no_mangle]
 pub extern "C" fn cef_run_message_loop() {
-    globals.with(|ref r| {
-        let mut the_globals = r.borrow_mut();
-        match *the_globals.as_mut().unwrap() {
-            ServoCefGlobals::OnScreenGlobals(ref window, ref browser) => {
-                while browser.borrow_mut().handle_event(window.borrow_mut().wait_events()) {}
-            }
-            ServoCefGlobals::OffScreenGlobals(ref window, ref browser) => {
-                while browser.borrow_mut().handle_event(window.borrow_mut().wait_events()) {}
-            }
-        }
-    });
+    // GWTODO: Support blocking message loop
+    // again. Although, will it ever actually
+    // be used or will everything use the
+    // cef_do_message_loop_work function below
+    // as our current miniservo apps do?
+    unimplemented!()
 }
 
 #[no_mangle]
 pub extern "C" fn cef_do_message_loop_work() {
-    send_window_event(WindowEvent::Idle)
+    browser::update();
 }
 
 #[no_mangle]
 pub extern "C" fn cef_quit_message_loop() {
 }
 
 #[no_mangle]
 pub extern "C" fn cef_execute_process(_args: *const cef_main_args_t,
                                       _app: *mut cef_app_t,
                                       _windows_sandbox_info: *mut c_void)
                                       -> c_int {
    -1
 }
 
-pub fn send_window_event(event: WindowEvent) {
-    message_queue.with(|ref r| r.borrow_mut().push(event.clone()));
-
-    globals.with(|ref r| {
-        let mut the_globals = r.borrow_mut();
-        match &mut *the_globals {
-            &None => return,
-            &Some(ref mut the_globals) => loop {
-                match *the_globals {
-                    ServoCefGlobals::OnScreenGlobals(_, ref browser) => {
-                        match browser.try_borrow_mut() {
-                            None => {
-                                // We're trying to send an event while processing another one. This will
-                                // cause general badness, so queue up that event instead of immediately
-                                // processing it.
-                                break
-                            }
-                            Some(ref mut browser) => {
-                                let event = match message_queue.with(|ref r| r.borrow_mut().pop()) {
-                                    None => return,
-                                    Some(event) => event,
-                                };
-                                browser.handle_event(event);
-                            }
-                        }
-                    }
-                    ServoCefGlobals::OffScreenGlobals(_, ref browser) => {
-                        match browser.try_borrow_mut() {
-                            None => {
-                                // We're trying to send an event while processing another one. This will
-                                // cause general badness, so queue up that event instead of immediately
-                                // processing it.
-                                break
-                            }
-                            Some(ref mut browser) => {
-                                let event = match message_queue.with(|ref r| r.borrow_mut().pop()) {
-                                    None => return,
-                                    Some(event) => event,
-                                };
-                                browser.handle_event(event);
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    });
-}
-
-macro_rules! browser_method_delegate(
-    ( $( fn $method:ident ( ) -> $return_type:ty ; )* ) => (
-        $(
-            pub fn $method() -> $return_type {
-                globals.with(|ref r| {
-                    match r.borrow_mut().as_mut() {
-                        None => panic!("{}: no globals created", stringify!($method)),
-                        Some(&ServoCefGlobals::OnScreenGlobals(_, ref browser)) =>
-                            browser.borrow_mut().$method(),
-                        Some(&ServoCefGlobals::OffScreenGlobals(_, ref browser)) =>
-                            browser.borrow_mut().$method(),
-                    }
-                })
-            }
-        )*
-    )
-)
-
-browser_method_delegate! {
-    fn repaint_synchronously() -> ();
-    fn pinch_zoom_level() -> f32;
-    fn get_title_for_main_frame() -> ();
-}
-
 #[no_mangle]
 pub extern "C" fn cef_api_hash(entry: c_int) -> *const c_char {
     if entry == 0 {
         &CEF_API_HASH_PLATFORM[0] as *const u8 as *const c_char
     } else {
         &CEF_API_HASH_UNIVERSAL[0] as *const u8 as *const c_char
     }
 }
--- a/servo/ports/cef/frame.rs
+++ b/servo/ports/cef/frame.rs
@@ -1,45 +1,59 @@
 /* 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 eutil::Downcast;
-use interfaces::{CefFrame, CefStringVisitor, cef_frame_t, cef_string_visitor_t};
+use interfaces::{CefBrowser, CefFrame, CefStringVisitor, cef_frame_t, cef_string_visitor_t};
 use types::{cef_string_t, cef_string_userfree_t};
+use browser::ServoCefBrowserExtensions;
 
-use core;
 use compositing::windowing::WindowEvent;
 use std::cell::RefCell;
 
 pub struct ServoCefFrame {
     pub title_visitor: RefCell<Option<CefStringVisitor>>,
     pub url: RefCell<String>,
+
+    /// A reference to the browser.
+    pub browser: RefCell<Option<CefBrowser>>,
 }
 
 impl ServoCefFrame {
     pub fn new() -> ServoCefFrame {
         ServoCefFrame {
             title_visitor: RefCell::new(None),
             url: RefCell::new(String::new()),
+            browser: RefCell::new(None),
         }
     }
 }
 
 cef_class_impl! {
     ServoCefFrame : CefFrame, cef_frame_t {
         fn load_url(&this, url: *const cef_string_t) -> () {
             let this = this.downcast();
             *this.url.borrow_mut() = String::from_utf16(url).unwrap();
-            core::send_window_event(WindowEvent::LoadUrl(String::from_utf16(url).unwrap()));
+            let event = WindowEvent::LoadUrl(String::from_utf16(url).unwrap());
+            this.browser.borrow_mut().as_mut().unwrap().send_window_event(event);
         }
         fn get_url(&this) -> cef_string_userfree_t {
             let this = this.downcast();
             (*this.url.borrow()).clone()
         }
         fn get_text(&this, visitor: *mut cef_string_visitor_t) -> () {
             let this = this.downcast();
             *this.title_visitor.borrow_mut() = Some(visitor);
-            core::get_title_for_main_frame();
+            this.browser.borrow().as_ref().unwrap().get_title_for_main_frame();
         }
     }
 }
 
+pub trait ServoCefFrameExtensions {
+    fn set_browser(&self, browser: CefBrowser);
+}
+
+impl ServoCefFrameExtensions for CefFrame {
+    fn set_browser(&self, browser: CefBrowser) {
+        *self.downcast().browser.borrow_mut() = Some(browser)
+    }
+}
\ No newline at end of file