servo: Merge #4193 - ports/cef: Implement accelerated compositing for the CEF port (from pcwalton:cef-redux); r=pcwalton,metajack
authorPatrick Walton <pcwalton@mimiga.net>
Wed, 10 Dec 2014 09:40:03 -0700
changeset 382062 515f014168d69337ee26522fa90f59274a0a21c0
parent 382061 30547f84836da32e910b5a6838e7012b8c8565d0
child 382063 2a800be4428dd40352cabb73cda76733aa50c0b4
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, metajack
servo: Merge #4193 - ports/cef: Implement accelerated compositing for the CEF port (from pcwalton:cef-redux); r=pcwalton,metajack @glennw, is it possible to rebase your stuff on top of this? Sorry for the mess. r? @metajack Source-Repo: https://github.com/servo/servo Source-Revision: ba06d50e3a611d1c27c6c9aa0b21441282911c3f
servo/components/compositing/compositor.rs
servo/components/compositing/compositor_task.rs
servo/components/compositing/constellation.rs
servo/components/compositing/headless.rs
servo/components/compositing/pipeline.rs
servo/components/compositing/windowing.rs
servo/components/msg/compositor_msg.rs
servo/components/msg/constellation_msg.rs
servo/components/script/dom/document.rs
servo/components/script/dom/htmltitleelement.rs
servo/components/script/dom/virtualmethods.rs
servo/components/script/page.rs
servo/components/script/script_task.rs
servo/components/script_traits/lib.rs
servo/components/servo/Cargo.lock
servo/components/servo/lib.rs
servo/components/servo/main.rs
servo/ports/android/glut_app/Cargo.lock
servo/ports/android/glut_app/window.rs
servo/ports/cef/Cargo.lock
servo/ports/cef/Cargo.toml
servo/ports/cef/browser.rs
servo/ports/cef/browser_host.rs
servo/ports/cef/command_line.rs
servo/ports/cef/core.rs
servo/ports/cef/frame.rs
servo/ports/cef/lib.rs
servo/ports/cef/render_handler.rs
servo/ports/cef/string.rs
servo/ports/cef/string_multimap.rs
servo/ports/cef/types.rs
servo/ports/cef/window.rs
servo/ports/cef/wrappers.rs
servo/ports/glfw/window.rs
servo/python/servo/build_commands.py
--- a/servo/components/compositing/compositor.rs
+++ b/servo/components/compositing/compositor.rs
@@ -1,30 +1,30 @@
 /* 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 compositor_layer::{CompositorData, CompositorLayer, DoesntWantScrollEvents};
-use compositor_layer::WantsScrollEvents;
-use compositor_task::{ChangeReadyState, ChangePaintState, CompositorEventListener};
-use compositor_task::{CompositorProxy, CompositorReceiver, CompositorTask};
-use compositor_task::{CreateOrUpdateDescendantLayer, CreateOrUpdateRootLayer, Exit};
-use compositor_task::{FrameTreeUpdateMsg, GetGraphicsMetadata, LayerProperties};
+use compositor_layer::{WantsScrollEvents};
+use compositor_task::{ChangePageLoadData, ChangePageTitle, ChangePaintState, ChangeReadyState};
+use compositor_task::{CompositorEventListener, CompositorProxy, CompositorReceiver};
+use compositor_task::{CompositorTask, CreateOrUpdateDescendantLayer, CreateOrUpdateRootLayer};
+use compositor_task::{Exit, FrameTreeUpdateMsg, GetGraphicsMetadata, LayerProperties};
 use compositor_task::{LoadComplete, Msg, Paint, PaintMsgDiscarded, ScrollFragmentPoint};
 use compositor_task::{ScrollTimeout, SetIds, SetLayerOrigin, ShutdownComplete};
-use constellation::{SendableFrameTree, FrameTreeDiff};
+use constellation::{FrameId, FrameTreeDiff, SendableFrameTree};
 use pipeline::CompositionPipeline;
 use scrolling::ScrollingTimerProxy;
 use windowing;
-use windowing::{IdleWindowEvent, LoadUrlWindowEvent, MouseWindowClickEvent};
-use windowing::{MouseWindowEvent, MouseWindowEventClass, MouseWindowMouseDownEvent};
-use windowing::{MouseWindowMouseUpEvent, MouseWindowMoveEventClass, NavigationWindowEvent};
+use windowing::{IdleWindowEvent, InitializeCompositingWindowEvent};
+use windowing::{KeyEvent, LoadUrlWindowEvent, MouseWindowClickEvent, MouseWindowEvent};
+use windowing::{MouseWindowEventClass, MouseWindowMouseDownEvent, MouseWindowMouseUpEvent};
+use windowing::{MouseWindowMoveEventClass, NavigationWindowEvent, PinchZoomWindowEvent};
 use windowing::{QuitWindowEvent, RefreshWindowEvent, ResizeWindowEvent, ScrollWindowEvent};
 use windowing::{WindowEvent, WindowMethods, WindowNavigateMsg, ZoomWindowEvent};
-use windowing::{PinchZoomWindowEvent, KeyEvent};
 
 use azure::azure_hl;
 use std::cmp;
 use std::mem;
 use std::num::Zero;
 use geom::point::{Point2D, TypedPoint2D};
 use geom::rect::{Rect, TypedRect};
 use geom::size::TypedSize2D;
@@ -35,43 +35,45 @@ use layers::layers::{BufferRequest, Laye
 use layers::rendergl;
 use layers::rendergl::RenderContext;
 use layers::scene::Scene;
 use png;
 use gleam::gl::types::{GLint, GLsizei};
 use gleam::gl;
 use script_traits::{ViewportMsg, ScriptControlChan};
 use servo_msg::compositor_msg::{Blank, Epoch, FinishedLoading, IdlePaintState, LayerId};
-use servo_msg::compositor_msg::{ReadyState, PaintingPaintState, PaintState, Scrollable};
-use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, LoadUrlMsg};
-use servo_msg::constellation_msg::{NavigateMsg, LoadData, PipelineId, ResizedWindowMsg};
-use servo_msg::constellation_msg::{WindowSizeData, KeyState, Key, KeyModifiers};
-use servo_msg::constellation_msg;
+use servo_msg::compositor_msg::{ReadyState, PaintState, PaintingPaintState, Scrollable};
+use servo_msg::constellation_msg::{mod, ConstellationChan, ExitMsg};
+use servo_msg::constellation_msg::{GetPipelineTitleMsg, Key, KeyModifiers, KeyState, LoadData};
+use servo_msg::constellation_msg::{LoadUrlMsg, NavigateMsg, PipelineId, ResizedWindowMsg};
+use servo_msg::constellation_msg::{WindowSizeData};
 use servo_util::geometry::{PagePx, ScreenPx, ViewportPx};
 use servo_util::memory::MemoryProfilerChan;
 use servo_util::opts;
 use servo_util::time::{profile, TimeProfilerChan};
 use servo_util::{memory, time};
 use std::collections::HashMap;
 use std::collections::hash_map::{Occupied, Vacant};
 use std::path::Path;
 use std::rc::Rc;
 use std::slice::bytes::copy_memory;
 use time::{precise_time_ns, precise_time_s};
 use url::Url;
 
+/// NB: Never block on the constellation, because sometimes the constellation blocks on us.
 pub struct IOCompositor<Window: WindowMethods> {
     /// The application window.
     window: Rc<Window>,
 
     /// The port on which we receive messages.
     port: Box<CompositorReceiver>,
 
-    /// The render context.
-    context: RenderContext,
+    /// The render context. This will be `None` if the windowing system has not yet sent us a
+    /// `PrepareRenderingEvent`.
+    context: Option<RenderContext>,
 
     /// The root pipeline.
     root_pipeline: Option<CompositionPipeline>,
 
     /// The canvas to paint a page.
     scene: Scene<CompositorData>,
 
     /// The application window size.
@@ -172,23 +174,20 @@ impl<Window: WindowMethods> IOCompositor
            memory_profiler_chan: MemoryProfilerChan)
            -> IOCompositor<Window> {
         // Create an initial layer tree.
         //
         // TODO: There should be no initial layer tree until the painter creates one from the
         // display list. This is only here because we don't have that logic in the painter yet.
         let window_size = window.framebuffer_size();
         let hidpi_factor = window.hidpi_factor();
-        let context = CompositorTask::create_graphics_context(&window.native_metadata());
-
-        let show_debug_borders = opts::get().show_debug_borders;
         IOCompositor {
             window: window,
             port: receiver,
-            context: rendergl::RenderContext::new(context, show_debug_borders),
+            context: None,
             root_pipeline: None,
             scene: Scene::new(Rect {
                 origin: Zero::zero(),
                 size: window_size.as_f32(),
             }),
             window_size: window_size,
             hidpi_factor: hidpi_factor,
             scrolling_timer: ScrollingTimerProxy::new(sender),
@@ -257,16 +256,24 @@ impl<Window: WindowMethods> IOCompositor
             (ChangeReadyState(pipeline_id, ready_state), NotShuttingDown) => {
                 self.change_ready_state(pipeline_id, ready_state);
             }
 
             (ChangePaintState(pipeline_id, paint_state), NotShuttingDown) => {
                 self.change_paint_state(pipeline_id, paint_state);
             }
 
+            (ChangePageTitle(pipeline_id, title), NotShuttingDown) => {
+                self.change_page_title(pipeline_id, title);
+            }
+
+            (ChangePageLoadData(frame_id, load_data), NotShuttingDown) => {
+                self.change_page_load_data(frame_id, load_data);
+            }
+
             (PaintMsgDiscarded, NotShuttingDown) => {
                 self.remove_outstanding_paint_msg();
             }
 
             (SetIds(frame_tree, response_chan, new_constellation_chan), NotShuttingDown) => {
                 self.set_frame_tree(&frame_tree,
                                     response_chan,
                                     new_constellation_chan);
@@ -300,23 +307,28 @@ impl<Window: WindowMethods> IOCompositor
                 }
                 self.remove_outstanding_paint_msg();
             }
 
             (ScrollFragmentPoint(pipeline_id, layer_id, point), NotShuttingDown) => {
                 self.scroll_fragment_to_point(pipeline_id, layer_id, point);
             }
 
-            (LoadComplete(..), NotShuttingDown) => {
+            (LoadComplete, NotShuttingDown) => {
                 self.got_load_complete_message = true;
 
                 // If we're painting in headless mode, schedule a recomposite.
                 if opts::get().output_file.is_some() {
                     self.composite_if_necessary();
                 }
+
+                // Inform the embedder that the load has finished.
+                //
+                // TODO(pcwalton): Specify which frame's load completed.
+                self.window.load_end();
             }
 
             (ScrollTimeout(timestamp), NotShuttingDown) => {
                 debug!("scroll timeout, drawing unpainted content!");
                 match self.composition_request {
                     CompositeOnScrollTimeout(this_timestamp) if timestamp == this_timestamp => {
                         self.composition_request = CompositeNow
                     }
@@ -366,16 +378,24 @@ impl<Window: WindowMethods> IOCompositor
             Vacant(entry) => {
                 entry.set(paint_state);
             }
         }
 
         self.window.set_paint_state(paint_state);
     }
 
+    fn change_page_title(&mut self, _: PipelineId, title: Option<String>) {
+        self.window.set_page_title(title);
+    }
+
+    fn change_page_load_data(&mut self, _: FrameId, load_data: LoadData) {
+        self.window.set_page_load_data(load_data);
+    }
+
     fn all_pipelines_in_idle_paint_state(&self) -> bool {
         if self.ready_states.len() == 0 {
             return false;
         }
         return self.paint_states.values().all(|&value| value == IdlePaintState);
     }
 
     fn has_paint_msg_tracking(&self) -> bool {
@@ -624,17 +644,21 @@ impl<Window: WindowMethods> IOCompositor
         }
     }
 
     fn handle_window_message(&mut self, event: WindowEvent) {
         match event {
             IdleWindowEvent => {}
 
             RefreshWindowEvent => {
-                self.composite_if_necessary()
+                self.composite();
+            }
+
+            InitializeCompositingWindowEvent => {
+                self.initialize_compositing();
             }
 
             ResizeWindowEvent(size) => {
                 self.on_resize_window_event(size);
             }
 
             LoadUrlWindowEvent(url_string) => {
                 self.on_load_url_window_event(url_string);
@@ -673,16 +697,18 @@ impl<Window: WindowMethods> IOCompositor
                 let ConstellationChan(ref chan) = self.constellation_chan;
                 chan.send(ExitMsg);
                 self.shutdown_state = ShuttingDown;
             }
         }
     }
 
     fn on_resize_window_event(&mut self, new_size: TypedSize2D<DevicePixel, uint>) {
+        debug!("compositor resizing to {}", new_size.to_untyped());
+
         // A size change could also mean a resolution change.
         let new_hidpi_factor = self.window.hidpi_factor();
         if self.hidpi_factor != new_hidpi_factor {
             self.hidpi_factor = new_hidpi_factor;
             self.update_zoom_transform();
         }
 
         if self.window_size == new_size {
@@ -955,16 +981,20 @@ impl<Window: WindowMethods> IOCompositor
         if !self.got_set_ids_message {
             return false;
         }
 
         return true;
     }
 
     fn composite(&mut self) {
+        if !self.window.prepare_for_composite() {
+            return
+        }
+
         let output_image = opts::get().output_file.is_some() &&
                             self.is_ready_to_paint_image_output();
 
         let mut framebuffer_ids = vec!();
         let mut texture_ids = vec!();
         let (width, height) = (self.window_size.width.get(), self.window_size.height.get());
 
         if output_image {
@@ -990,17 +1020,24 @@ impl<Window: WindowMethods> IOCompositor
             // Adjust the layer dimensions as necessary to correspond to the size of the window.
             self.scene.viewport = Rect {
                 origin: Zero::zero(),
                 size: self.window_size.as_f32(),
             };
             // paint the scene.
             match self.scene.root {
                 Some(ref layer) => {
-                    rendergl::render_scene(layer.clone(), self.context, &self.scene);
+                    match self.context {
+                        None => {
+                            debug!("compositor: not compositing because context not yet set up")
+                        }
+                        Some(context) => {
+                            rendergl::render_scene(layer.clone(), context, &self.scene);
+                        }
+                    }
                 }
                 None => {}
             }
         });
 
         if output_image {
             let path =
                 from_str::<Path>(opts::get().output_file.as_ref().unwrap().as_slice()).unwrap();
@@ -1048,16 +1085,22 @@ impl<Window: WindowMethods> IOCompositor
     }
 
     fn composite_if_necessary(&mut self) {
         if self.composition_request == NoCompositingNecessary {
             self.composition_request = CompositeNow
         }
     }
 
+    fn initialize_compositing(&mut self) {
+        let context = CompositorTask::create_graphics_context(&self.window.native_metadata());
+        let show_debug_borders = opts::get().show_debug_borders;
+        self.context = Some(rendergl::RenderContext::new(context, show_debug_borders))
+    }
+
     fn find_topmost_layer_at_point_for_layer(&self,
                                              layer: Rc<Layer<CompositorData>>,
                                              point: TypedPoint2D<LayerPixel, f32>)
                                              -> Option<HitTestResult> {
         let child_point = point - layer.bounds.borrow().origin;
         for child in layer.children().iter().rev() {
             let result = self.find_topmost_layer_at_point_for_layer(child.clone(), child_point);
             if result.is_some() {
@@ -1224,9 +1267,22 @@ impl<Window> CompositorEventListener for
         let TimeProfilerChan(ref time_profiler_chan) = self.time_profiler_chan;
         time_profiler_chan.send(time::ExitMsg);
 
         let MemoryProfilerChan(ref memory_profiler_chan) = self.memory_profiler_chan;
         memory_profiler_chan.send(memory::ExitMsg);
 
         self.scrolling_timer.shutdown();
     }
+
+    fn pinch_zoom_level(&self) -> f32 {
+        self.viewport_zoom.get() as f32
+    }
+
+    fn get_title_for_main_frame(&self) {
+        let root_pipeline_id = match self.root_pipeline {
+            None => return,
+            Some(ref root_pipeline) => root_pipeline.id,
+        };
+        let ConstellationChan(ref chan) = self.constellation_chan;
+        chan.send(GetPipelineTitleMsg(root_pipeline_id));
+    }
 }
--- a/servo/components/compositing/compositor_task.rs
+++ b/servo/components/compositing/compositor_task.rs
@@ -1,30 +1,30 @@
 /* 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/. */
 
 //! Communication with the compositor task.
 
 pub use windowing;
-pub use constellation::{SendableFrameTree, FrameTreeDiff};
+pub use constellation::{FrameId, SendableFrameTree, FrameTreeDiff};
 
 use compositor;
 use headless;
 use windowing::{WindowEvent, WindowMethods};
 
 use azure::azure_hl::{SourceSurfaceMethods, Color};
 use geom::point::Point2D;
 use geom::rect::Rect;
 use geom::size::Size2D;
 use layers::platform::surface::{NativeCompositingGraphicsContext, NativeGraphicsMetadata};
 use layers::layers::LayerBufferSet;
 use servo_msg::compositor_msg::{Epoch, LayerId, LayerMetadata, ReadyState};
 use servo_msg::compositor_msg::{PaintListener, PaintState, ScriptListener, ScrollPolicy};
-use servo_msg::constellation_msg::{ConstellationChan, PipelineId};
+use servo_msg::constellation_msg::{ConstellationChan, LoadData, PipelineId};
 use servo_util::memory::MemoryProfilerChan;
 use servo_util::time::TimeProfilerChan;
 use std::comm::{channel, Sender, Receiver};
 use std::fmt::{FormatError, Formatter, Show};
 use std::rc::Rc;
 
 /// Sends messages to the compositor. This is a trait supplied by the port because the method used
 /// to communicate with the compositor may have to kick OS event loops awake, communicate cross-
@@ -76,18 +76,23 @@ impl ScriptListener for Box<CompositorPr
         let (chan, port) = channel();
         self.send(Exit(chan));
         port.recv();
     }
 
     fn dup(&mut self) -> Box<ScriptListener+'static> {
         box self.clone_compositor_proxy() as Box<ScriptListener+'static>
     }
+
+    fn set_title(&mut self, pipeline_id: PipelineId, title: Option<String>) {
+        self.send(ChangePageTitle(pipeline_id, title))
+    }
 }
 
+/// Information about each layer that the compositor keeps.
 pub struct LayerProperties {
     pub pipeline_id: PipelineId,
     pub epoch: Epoch,
     pub id: LayerId,
     pub rect: Rect<f32>,
     pub background_color: Color,
     pub scroll_policy: ScrollPolicy,
 }
@@ -179,19 +184,23 @@ pub enum Msg {
     /// Scroll a page in a window
     ScrollFragmentPoint(PipelineId, LayerId, Point2D<f32>),
     /// Requests that the compositor paint the given layer buffer set for the given page size.
     Paint(PipelineId, Epoch, Vec<(LayerId, Box<LayerBufferSet>)>),
     /// Alerts the compositor to the current status of page loading.
     ChangeReadyState(PipelineId, ReadyState),
     /// Alerts the compositor to the current status of painting.
     ChangePaintState(PipelineId, PaintState),
-    /// Alerts the compositor that the PaintMsg has been discarded.
+    /// Alerts the compositor that the current page has changed its title.
+    ChangePageTitle(PipelineId, Option<String>),
+    /// Alerts the compositor that the current page has changed its load data (including URL).
+    ChangePageLoadData(FrameId, LoadData),
+    /// Alerts the compositor that a `PaintMsg` has been discarded.
     PaintMsgDiscarded,
-    /// Sets the channel to the current layout and paint tasks, along with their id
+    /// Sets the channel to the current layout and paint tasks, along with their ID.
     SetIds(SendableFrameTree, Sender<()>, ConstellationChan),
     /// Sends an updated version of the frame tree.
     FrameTreeUpdateMsg(FrameTreeDiff, Sender<()>),
     /// The load of a page has completed.
     LoadComplete,
     /// Indicates that the scrolling timeout with the given starting timestamp has happened and a
     /// composite should happen. (See the `scrolling` module.)
     ScrollTimeout(u64),
@@ -205,16 +214,18 @@ impl Show for Msg {
             GetGraphicsMetadata(..) => write!(f, "GetGraphicsMetadata"),
             CreateOrUpdateRootLayer(..) => write!(f, "CreateOrUpdateRootLayer"),
             CreateOrUpdateDescendantLayer(..) => write!(f, "CreateOrUpdateDescendantLayer"),
             SetLayerOrigin(..) => write!(f, "SetLayerOrigin"),
             ScrollFragmentPoint(..) => write!(f, "ScrollFragmentPoint"),
             Paint(..) => write!(f, "Paint"),
             ChangeReadyState(..) => write!(f, "ChangeReadyState"),
             ChangePaintState(..) => write!(f, "ChangePaintState"),
+            ChangePageTitle(..) => write!(f, "ChangePageTitle"),
+            ChangePageLoadData(..) => write!(f, "ChangePageLoadData"),
             PaintMsgDiscarded(..) => write!(f, "PaintMsgDiscarded"),
             SetIds(..) => write!(f, "SetIds"),
             FrameTreeUpdateMsg(..) => write!(f, "FrameTreeUpdateMsg"),
             LoadComplete => write!(f, "LoadComplete"),
             ScrollTimeout(..) => write!(f, "ScrollTimeout"),
         }
     }
 }
@@ -264,10 +275,13 @@ impl CompositorTask {
         }
     }
 }
 
 pub trait CompositorEventListener {
     fn handle_event(&mut self, event: WindowEvent) -> bool;
     fn repaint_synchronously(&mut self);
     fn shutdown(&mut self);
+    fn pinch_zoom_level(&self) -> f32;
+    /// Requests that the compositor send the title for the main frame as soon as possible.
+    fn get_title_for_main_frame(&self);
 }
 
--- a/servo/components/compositing/constellation.rs
+++ b/servo/components/compositing/constellation.rs
@@ -1,34 +1,35 @@
 /* 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 pipeline::{Pipeline, CompositionPipeline};
 
-use compositor_task::{CompositorProxy, FrameTreeUpdateMsg, LoadComplete, ShutdownComplete, SetLayerOrigin, SetIds};
+use compositor_task::{ChangePageLoadData, ChangePageTitle, CompositorProxy, FrameTreeUpdateMsg};
+use compositor_task::{LoadComplete, SetLayerOrigin, SetIds, ShutdownComplete};
 use devtools_traits;
 use devtools_traits::DevtoolsControlChan;
 use geom::rect::{Rect, TypedRect};
 use geom::scale_factor::ScaleFactor;
 use gfx::font_cache_task::FontCacheTask;
 use gfx::paint_task;
 use layers::geometry::DevicePixel;
 use layout_traits::{LayoutControlChan, LayoutTaskFactory, ExitNowMsg};
 use libc;
-use script_traits;
-use script_traits::{ResizeMsg, ResizeInactiveMsg, ExitPipelineMsg, SendEventMsg};
+use script_traits::{mod, GetTitleMsg, ResizeMsg, ResizeInactiveMsg, ExitPipelineMsg, SendEventMsg};
 use script_traits::{ScriptControlChan, ScriptTaskFactory};
 use servo_msg::compositor_msg::LayerId;
 use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, FailureMsg, Failure, FrameRectMsg};
+use servo_msg::constellation_msg::{GetPipelineTitleMsg};
 use servo_msg::constellation_msg::{IFrameSandboxState, IFrameUnsandboxed, InitLoadUrlMsg};
-use servo_msg::constellation_msg::{LoadCompleteMsg, LoadUrlMsg, LoadData, Msg, NavigateMsg};
-use servo_msg::constellation_msg::{NavigationType, PipelineId, PainterReadyMsg, ResizedWindowMsg};
+use servo_msg::constellation_msg::{KeyEvent, Key, KeyState, KeyModifiers, LoadCompleteMsg};
+use servo_msg::constellation_msg::{LoadData, LoadUrlMsg, Msg, NavigateMsg, NavigationType};
+use servo_msg::constellation_msg::{PainterReadyMsg, PipelineId, ResizedWindowMsg};
 use servo_msg::constellation_msg::{ScriptLoadedURLInIFrameMsg, SubpageId, WindowSizeData};
-use servo_msg::constellation_msg::{KeyEvent, Key, KeyState, KeyModifiers};
 use servo_msg::constellation_msg;
 use servo_net::image_cache_task::{ImageCacheTask, ImageCacheTaskClient};
 use servo_net::resource_task::ResourceTask;
 use servo_net::resource_task;
 use servo_net::storage_task::StorageTask;
 use servo_net::storage_task;
 use servo_util::geometry::{PagePx, ViewportPx};
 use servo_util::opts;
@@ -71,37 +72,52 @@ pub struct Constellation<LTF, STF> {
     /// A channel through which messages can be sent to the font cache.
     font_cache_task: FontCacheTask,
 
     navigation_context: NavigationContext,
 
     /// The next free ID to assign to a pipeline.
     next_pipeline_id: PipelineId,
 
+    /// The next free ID to assign to a frame.
+    next_frame_id: FrameId,
+
+    /// Navigation operations that are in progress.
     pending_frames: Vec<FrameChange>,
 
     pending_sizes: HashMap<(PipelineId, SubpageId), TypedRect<PagePx, f32>>,
 
     /// A channel through which messages can be sent to the time profiler.
     pub time_profiler_chan: TimeProfilerChan,
 
     pub window_size: WindowSizeData,
 }
 
-/// Stores the Id of the outermost frame's pipeline, along with a vector of children frames
+/// A unique ID used to identify a frame.
+pub struct FrameId(u32);
+
+/// One frame in the hierarchy.
 struct FrameTree {
+    /// The ID of this frame.
+    pub id: FrameId,
+    /// The pipeline for this frame.
     pub pipeline: Rc<Pipeline>,
+    /// The parent frame's pipeline.
     pub parent: RefCell<Option<Rc<Pipeline>>>,
+    /// A vector of child frames.
     pub children: RefCell<Vec<ChildFrameTree>>,
+    /// Whether this frame has a compositor layer.
     pub has_compositor_layer: Cell<bool>,
 }
 
 impl FrameTree {
-    fn new(pipeline: Rc<Pipeline>, parent_pipeline: Option<Rc<Pipeline>>) -> FrameTree {
+    fn new(id: FrameId, pipeline: Rc<Pipeline>, parent_pipeline: Option<Rc<Pipeline>>)
+           -> FrameTree {
         FrameTree {
+            id: id,
             pipeline: pipeline,
             parent: RefCell::new(parent_pipeline),
             children: RefCell::new(vec!()),
             has_compositor_layer: Cell::new(false),
         }
     }
 }
 
@@ -229,63 +245,67 @@ impl Iterator<Rc<FrameTree>> for FrameTr
             }
             None => None,
         }
     }
 }
 
 /// Represents the portion of a page that is changing in navigating.
 struct FrameChange {
+    /// The old pipeline ID.
     pub before: Option<PipelineId>,
+    /// The resulting frame tree after navigation.
     pub after: Rc<FrameTree>,
+    /// The kind of navigation that is occurring.
     pub navigation_type: NavigationType,
 }
 
 /// Stores the Id's of the pipelines previous and next in the browser's history
 struct NavigationContext {
-    pub previous: Vec<Rc<FrameTree>>,
-    pub next: Vec<Rc<FrameTree>>,
-    pub current: Option<Rc<FrameTree>>,
+    previous: Vec<Rc<FrameTree>>,
+    next: Vec<Rc<FrameTree>>,
+    current: Option<Rc<FrameTree>>,
 }
 
 impl NavigationContext {
     fn new() -> NavigationContext {
         NavigationContext {
             previous: vec!(),
             next: vec!(),
             current: None,
         }
     }
 
     /* Note that the following two methods can fail. They should only be called  *
      * when it is known that there exists either a previous page or a next page. */
 
-    fn back(&mut self) -> Rc<FrameTree> {
+    fn back(&mut self, compositor_proxy: &mut CompositorProxy) -> Rc<FrameTree> {
         self.next.push(self.current.take().unwrap());
         let prev = self.previous.pop().unwrap();
-        self.current = Some(prev.clone());
+        self.set_current(prev.clone(), compositor_proxy);
         prev
     }
 
-    fn forward(&mut self) -> Rc<FrameTree> {
+    fn forward(&mut self, compositor_proxy: &mut CompositorProxy) -> Rc<FrameTree> {
         self.previous.push(self.current.take().unwrap());
         let next = self.next.pop().unwrap();
-        self.current = Some(next.clone());
+        self.set_current(next.clone(), compositor_proxy);
         next
     }
 
     /// Loads a new set of page frames, returning all evicted frame trees
-    fn load(&mut self, frame_tree: Rc<FrameTree>) -> Vec<Rc<FrameTree>> {
+    fn load(&mut self, frame_tree: Rc<FrameTree>, compositor_proxy: &mut CompositorProxy)
+            -> Vec<Rc<FrameTree>> {
         debug!("navigating to {}", frame_tree.pipeline.id);
         let evicted = replace(&mut self.next, vec!());
         match self.current.take() {
             Some(current) => self.previous.push(current),
             None => (),
         }
-        self.current = Some(frame_tree);
+        self.set_current(frame_tree, compositor_proxy);
         evicted
     }
 
     /// Returns the frame trees whose keys are pipeline_id.
     fn find_all(&mut self, pipeline_id: PipelineId) -> Vec<Rc<FrameTree>> {
         let from_current = self.current.iter().filter_map(|frame_tree| {
             frame_tree.find(pipeline_id)
         });
@@ -303,16 +323,24 @@ impl NavigationContext {
         let from_next = self.next.iter();
         let from_prev = self.previous.iter();
 
         let mut all_contained = from_prev.chain(from_current).chain(from_next);
         all_contained.any(|frame_tree| {
             frame_tree.contains(pipeline_id)
         })
     }
+
+    /// Always use this method to set the currently-displayed frame. It correctly informs the
+    /// compositor of the new URLs.
+    fn set_current(&mut self, new_frame: Rc<FrameTree>, compositor_proxy: &mut CompositorProxy) {
+        self.current = Some(new_frame.clone());
+        compositor_proxy.send(ChangePageLoadData(new_frame.id,
+                                                 new_frame.pipeline.load_data.clone()));
+    }
 }
 
 impl<LTF: LayoutTaskFactory, STF: ScriptTaskFactory> Constellation<LTF, STF> {
     pub fn start(compositor_proxy: Box<CompositorProxy+Send>,
                  resource_task: ResourceTask,
                  image_cache_task: ImageCacheTask,
                  font_cache_task: FontCacheTask,
                  time_profiler_chan: TimeProfilerChan,
@@ -329,16 +357,17 @@ impl<LTF: LayoutTaskFactory, STF: Script
                 devtools_chan: devtools_chan,
                 resource_task: resource_task,
                 image_cache_task: image_cache_task,
                 font_cache_task: font_cache_task,
                 storage_task: storage_task,
                 pipelines: HashMap::new(),
                 navigation_context: NavigationContext::new(),
                 next_pipeline_id: PipelineId(0),
+                next_frame_id: FrameId(0),
                 pending_frames: vec!(),
                 pending_sizes: HashMap::new(),
                 time_profiler_chan: time_profiler_chan,
                 window_size: WindowSizeData {
                     visible_viewport: opts::get().initial_window_size.as_f32() * ScaleFactor(1.0),
                     initial_viewport: opts::get().initial_window_size.as_f32() * ScaleFactor(1.0),
                     device_pixel_ratio: ScaleFactor(1.0),
                 },
@@ -353,48 +382,55 @@ impl<LTF: LayoutTaskFactory, STF: Script
             let request = self.request_port.recv();
             if !self.handle_request(request) {
                 break;
             }
         }
     }
 
     /// Helper function for creating a pipeline
-    fn new_pipeline(&self,
+    fn new_pipeline(&mut self,
                     id: PipelineId,
                     subpage_id: Option<SubpageId>,
                     script_pipeline: Option<Rc<Pipeline>>,
                     load_data: LoadData)
                     -> Rc<Pipeline> {
-            let pipe = Pipeline::create::<LTF, STF>(id,
-                                                    subpage_id,
-                                                    self.chan.clone(),
-                                                    self.compositor_proxy.clone_compositor_proxy(),
-                                                    self.devtools_chan.clone(),
-                                                    self.image_cache_task.clone(),
-                                                    self.font_cache_task.clone(),
-                                                    self.resource_task.clone(),
-                                                    self.storage_task.clone(),
-                                                    self.time_profiler_chan.clone(),
-                                                    self.window_size,
-                                                    script_pipeline,
-                                                    load_data);
-            pipe.load();
-            Rc::new(pipe)
+        let pipe = Pipeline::create::<LTF, STF>(id,
+                                                subpage_id,
+                                                self.chan.clone(),
+                                                self.compositor_proxy.clone_compositor_proxy(),
+                                                self.devtools_chan.clone(),
+                                                self.image_cache_task.clone(),
+                                                self.font_cache_task.clone(),
+                                                self.resource_task.clone(),
+                                                self.storage_task.clone(),
+                                                self.time_profiler_chan.clone(),
+                                                self.window_size,
+                                                script_pipeline,
+                                                load_data.clone());
+        pipe.load();
+        Rc::new(pipe)
     }
 
-
-    /// Helper function for getting a unique pipeline Id
+    /// Helper function for getting a unique pipeline ID.
     fn get_next_pipeline_id(&mut self) -> PipelineId {
         let id = self.next_pipeline_id;
         let PipelineId(ref mut i) = self.next_pipeline_id;
         *i += 1;
         id
     }
 
+    /// Helper function for getting a unique frame ID.
+    fn get_next_frame_id(&mut self) -> FrameId {
+        let id = self.next_frame_id;
+        let FrameId(ref mut i) = self.next_frame_id;
+        *i += 1;
+        id
+    }
+
     /// Convenience function for getting the currently active frame tree.
     /// The currently active frame tree should always be the current painter
     fn current_frame<'a>(&'a self) -> &'a Option<Rc<FrameTree>> {
         &self.navigation_context.current
     }
 
     /// Returns both the navigation context and pending frame trees whose keys are pipeline_id.
     fn find_all(&mut self, pipeline_id: PipelineId) -> Vec<Rc<FrameTree>> {
@@ -460,16 +496,20 @@ impl<LTF: LayoutTaskFactory, STF: Script
             ResizedWindowMsg(new_size) => {
                 debug!("constellation got window resize message");
                 self.handle_resized_window_msg(new_size);
             }
             KeyEvent(key, state, modifiers) => {
                 debug!("constellation got key event message");
                 self.handle_key_msg(key, state, modifiers);
             }
+            GetPipelineTitleMsg(pipeline_id) => {
+                debug!("constellation got get-pipeline-title message");
+                self.handle_get_pipeline_title_msg(pipeline_id);
+            }
         }
         true
     }
 
     fn handle_exit(&mut self) {
         for (_id, ref pipeline) in self.pipelines.iter() {
             pipeline.exit();
         }
@@ -524,37 +564,48 @@ impl<LTF: LayoutTaskFactory, STF: Script
                     self.pending_frames.remove(idx);
                 },
                 None => break,
             }
         }
         debug!("creating replacement pipeline for about:failure");
 
         let new_id = self.get_next_pipeline_id();
+        let new_frame_id = self.get_next_frame_id();
         let pipeline = self.new_pipeline(new_id, subpage_id, None,
                                          LoadData::new(Url::parse("about:failure").unwrap()));
 
-        self.pending_frames.push(FrameChange{
-            before: Some(pipeline_id),
-            after: Rc::new(FrameTree::new(pipeline.clone(), None)),
-            navigation_type: constellation_msg::Load,
-        });
+        self.browse(Some(pipeline_id),
+                    Rc::new(FrameTree::new(new_frame_id, pipeline.clone(), None)),
+                    constellation_msg::Load);
 
         self.pipelines.insert(new_id, pipeline);
     }
 
+    /// Performs navigation. This pushes a `FrameChange` object onto the list of pending frames.
+    ///
+    /// TODO(pcwalton): Send a `BeforeBrowse` message to the embedder and allow cancellation.
+    fn browse(&mut self,
+              before: Option<PipelineId>,
+              after: Rc<FrameTree>,
+              navigation_type: NavigationType) {
+        self.pending_frames.push(FrameChange {
+            before: before,
+            after: after,
+            navigation_type: navigation_type,
+        });
+    }
+
     fn handle_init_load(&mut self, url: Url) {
         let next_pipeline_id = self.get_next_pipeline_id();
+        let next_frame_id = self.get_next_frame_id();
         let pipeline = self.new_pipeline(next_pipeline_id, None, None, LoadData::new(url));
-
-        self.pending_frames.push(FrameChange {
-            before: None,
-            after: Rc::new(FrameTree::new(pipeline.clone(), None)),
-            navigation_type: constellation_msg::Load,
-        });
+        self.browse(None,
+                    Rc::new(FrameTree::new(next_frame_id, pipeline.clone(), None)),
+                    constellation_msg::Load);
         self.pipelines.insert(pipeline.id, pipeline);
     }
 
     fn handle_frame_rect_msg(&mut self, pipeline_id: PipelineId, subpage_id: SubpageId,
                              rect: TypedRect<PagePx, f32>) {
         debug!("Received frame rect {} from {}, {}", rect, pipeline_id, subpage_id);
         let mut already_sent = HashSet::new();
 
@@ -685,25 +736,29 @@ impl<LTF: LayoutTaskFactory, STF: Script
             next_pipeline_id,
             Some(subpage_id),
             script_pipeline,
             LoadData::new(url)
         );
 
         let rect = self.pending_sizes.remove(&(source_pipeline_id, subpage_id));
         for frame_tree in frame_trees.iter() {
+            let next_frame_id = self.get_next_frame_id();
             frame_tree.children.borrow_mut().push(ChildFrameTree::new(
-                Rc::new(FrameTree::new(pipeline.clone(), Some(source_pipeline.clone()))),
+                Rc::new(FrameTree::new(next_frame_id,
+                                       pipeline.clone(),
+                                       Some(source_pipeline.clone()))),
                 rect));
         }
         self.pipelines.insert(pipeline.id, pipeline);
     }
 
     fn handle_load_url_msg(&mut self, source_id: PipelineId, load_data: LoadData) {
-        debug!("Constellation: received message to load {:s}", load_data.url.to_string());
+        let url = load_data.url.to_string();
+        debug!("Constellation: received message to load {:s}", url);
         // Make sure no pending page would be overridden.
         let source_frame = self.current_frame().as_ref().unwrap().find(source_id).expect(
             "Constellation: received a LoadUrlMsg from a pipeline_id associated
             with a pipeline not in the active frame tree. This should be
             impossible.");
 
         for frame_change in self.pending_frames.iter() {
             let old_id = frame_change.before.expect("Constellation: Received load msg
@@ -718,24 +773,23 @@ impl<LTF: LayoutTaskFactory, STF: Script
             }
         }
         // Being here means either there are no pending frames, or none of the pending
         // changes would be overriden by changing the subframe associated with source_id.
 
         let parent = source_frame.parent.clone();
         let subpage_id = source_frame.pipeline.subpage_id;
         let next_pipeline_id = self.get_next_pipeline_id();
-
+        let next_frame_id = self.get_next_frame_id();
         let pipeline = self.new_pipeline(next_pipeline_id, subpage_id, None, load_data);
-
-        self.pending_frames.push(FrameChange {
-            before: Some(source_id),
-            after: Rc::new(FrameTree::new(pipeline.clone(), parent.borrow().clone())),
-            navigation_type: constellation_msg::Load,
-        });
+        self.browse(Some(source_id),
+                    Rc::new(FrameTree::new(next_frame_id,
+                                           pipeline.clone(),
+                                           parent.borrow().clone())),
+                    constellation_msg::Load);
         self.pipelines.insert(pipeline.id, pipeline);
     }
 
     fn handle_navigate_msg(&mut self, direction: constellation_msg::NavigationDirection) {
         debug!("received message to navigate {}", direction);
 
         // TODO(tkuehn): what is the "critical point" beyond which pending frames
         // should not be cleared? Currently, the behavior is that forward/back
@@ -747,29 +801,29 @@ impl<LTF: LayoutTaskFactory, STF: Script
                     debug!("no next page to navigate to");
                     return;
                 } else {
                     let old = self.current_frame().as_ref().unwrap();
                     for frame in old.iter() {
                         frame.pipeline.revoke_paint_permission();
                     }
                 }
-                self.navigation_context.forward()
+                self.navigation_context.forward(&mut *self.compositor_proxy)
             }
             constellation_msg::Back => {
                 if self.navigation_context.previous.is_empty() {
                     debug!("no previous page to navigate to");
                     return;
                 } else {
                     let old = self.current_frame().as_ref().unwrap();
                     for frame in old.iter() {
                         frame.pipeline.revoke_paint_permission();
                     }
                 }
-                self.navigation_context.back()
+                self.navigation_context.back(&mut *self.compositor_proxy)
             }
         };
 
         for frame in destination_frame.iter() {
             frame.pipeline.load();
         }
         self.grant_paint_permission(destination_frame, constellation_msg::Navigate);
 
@@ -782,16 +836,26 @@ impl<LTF: LayoutTaskFactory, STF: Script
 
     fn handle_key_msg(&self, key: Key, state: KeyState, mods: KeyModifiers) {
         self.current_frame().as_ref().map(|frame| {
             let ScriptControlChan(ref chan) = frame.pipeline.script_chan;
             chan.send(SendEventMsg(frame.pipeline.id, script_traits::KeyEvent(key, state, mods)));
         });
     }
 
+    fn handle_get_pipeline_title_msg(&mut self, pipeline_id: PipelineId) {
+        match self.pipelines.get(&pipeline_id) {
+            None => self.compositor_proxy.send(ChangePageTitle(pipeline_id, None)),
+            Some(pipeline) => {
+                let ScriptControlChan(ref script_channel) = pipeline.script_chan;
+                script_channel.send(GetTitleMsg(pipeline_id));
+            }
+        }
+    }
+
     fn handle_painter_ready_msg(&mut self, pipeline_id: PipelineId) {
         debug!("Painter {} ready to send paint msg", pipeline_id);
         // This message could originate from a pipeline in the navigation context or
         // from a pending frame. The only time that we will grant paint permission is
         // when the message originates from a pending frame or the current frame.
 
         // Messages originating in the current frame are not navigations;
         // they may come from a page load in a subframe.
@@ -936,17 +1000,18 @@ impl<LTF: LayoutTaskFactory, STF: Script
         // Give permission to paint to the new frame and all child frames
         self.set_ids(&frame_tree);
 
         // Don't call navigation_context.load() on a Navigate type (or None, as in the case of
         // parsed iframes that finish loading)
         match navigation_type {
             constellation_msg::Load => {
                 debug!("evicting old frames due to load");
-                let evicted = self.navigation_context.load(frame_tree);
+                let evicted = self.navigation_context.load(frame_tree,
+                                                           &mut *self.compositor_proxy);
                 self.handle_evicted_frames(evicted);
             }
             _ => {
                 debug!("ignoring non-load navigation type");
             }
         }
     }
 
--- a/servo/components/compositing/headless.rs
+++ b/servo/components/compositing/headless.rs
@@ -1,16 +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 compositor_task::{GetGraphicsMetadata, CreateOrUpdateRootLayer, CreateOrUpdateDescendantLayer};
 use compositor_task::{Exit, ChangeReadyState, LoadComplete, Paint, ScrollFragmentPoint, SetIds};
 use compositor_task::{SetLayerOrigin, ShutdownComplete, ChangePaintState, PaintMsgDiscarded};
-use compositor_task::{CompositorEventListener, CompositorReceiver, ScrollTimeout, FrameTreeUpdateMsg};
+use compositor_task::{CompositorEventListener, CompositorReceiver, ScrollTimeout, ChangePageTitle};
+use compositor_task::{ChangePageLoadData, FrameTreeUpdateMsg};
 use windowing::WindowEvent;
 
 use geom::scale_factor::ScaleFactor;
 use geom::size::TypedSize2D;
 use servo_msg::constellation_msg::{ConstellationChan, ExitMsg, ResizedWindowMsg, WindowSizeData};
 use servo_util::memory::MemoryProfilerChan;
 use servo_util::memory;
 use servo_util::time::TimeProfilerChan;
@@ -99,24 +100,31 @@ impl CompositorEventListener for NullCom
             // Explicitly list ignored messages so that when we add a new one,
             // we'll notice and think about whether it needs a response, like
             // SetIds.
 
             CreateOrUpdateRootLayer(..) |
             CreateOrUpdateDescendantLayer(..) |
             SetLayerOrigin(..) | Paint(..) |
             ChangeReadyState(..) | ChangePaintState(..) | ScrollFragmentPoint(..) |
-            LoadComplete | PaintMsgDiscarded(..) | ScrollTimeout(..) => ()
+            LoadComplete | PaintMsgDiscarded(..) | ScrollTimeout(..) | ChangePageTitle(..) |
+            ChangePageLoadData(..) => ()
         }
         true
     }
 
     fn repaint_synchronously(&mut self) {}
 
     fn shutdown(&mut self) {
         // Drain compositor port, sometimes messages contain channels that are blocking
         // another task from finishing (i.e. SetIds)
         while self.port.try_recv_compositor_msg().is_some() {}
 
         self.time_profiler_chan.send(time::ExitMsg);
         self.memory_profiler_chan.send(memory::ExitMsg);
     }
+
+    fn pinch_zoom_level(&self) -> f32 {
+        1.0
+    }
+
+    fn get_title_for_main_frame(&self) {}
 }
--- a/servo/components/compositing/pipeline.rs
+++ b/servo/components/compositing/pipeline.rs
@@ -23,47 +23,49 @@ use std::rc::Rc;
 pub struct Pipeline {
     pub id: PipelineId,
     pub subpage_id: Option<SubpageId>,
     pub script_chan: ScriptControlChan,
     pub layout_chan: LayoutControlChan,
     pub paint_chan: PaintChan,
     pub layout_shutdown_port: Receiver<()>,
     pub paint_shutdown_port: Receiver<()>,
-    /// The most recently loaded page
+    /// Load data corresponding to the most recently-loaded page.
     pub load_data: LoadData,
+    /// The title of the most recently-loaded page.
+    pub title: Option<String>,
 }
 
 /// The subset of the pipeline that is needed for layer composition.
 #[deriving(Clone)]
 pub struct CompositionPipeline {
     pub id: PipelineId,
     pub script_chan: ScriptControlChan,
     pub paint_chan: PaintChan,
 }
 
 impl Pipeline {
     /// Starts a paint task, layout task, and possibly a script task.
     /// Returns the channels wrapped in a struct.
     /// If script_pipeline is not None, then subpage_id must also be not None.
-    pub fn create<LTF:LayoutTaskFactory, STF:ScriptTaskFactory>(
-                      id: PipelineId,
-                      subpage_id: Option<SubpageId>,
-                      constellation_chan: ConstellationChan,
-                      compositor_proxy: Box<CompositorProxy+'static+Send>,
-                      devtools_chan: Option<DevtoolsControlChan>,
-                      image_cache_task: ImageCacheTask,
-                      font_cache_task: FontCacheTask,
-                      resource_task: ResourceTask,
-                      storage_task: StorageTask,
-                      time_profiler_chan: TimeProfilerChan,
-                      window_size: WindowSizeData,
-                      script_pipeline: Option<Rc<Pipeline>>,
-                      load_data: LoadData)
-                      -> Pipeline {
+    pub fn create<LTF,STF>(id: PipelineId,
+                           subpage_id: Option<SubpageId>,
+                           constellation_chan: ConstellationChan,
+                           compositor_proxy: Box<CompositorProxy+'static+Send>,
+                           devtools_chan: Option<DevtoolsControlChan>,
+                           image_cache_task: ImageCacheTask,
+                           font_cache_task: FontCacheTask,
+                           resource_task: ResourceTask,
+                           storage_task: StorageTask,
+                           time_profiler_chan: TimeProfilerChan,
+                           window_size: WindowSizeData,
+                           script_pipeline: Option<Rc<Pipeline>>,
+                           load_data: LoadData)
+                           -> Pipeline
+                           where LTF: LayoutTaskFactory, STF:ScriptTaskFactory {
         let layout_pair = ScriptTaskFactory::create_layout_channel(None::<&mut STF>);
         let (paint_port, paint_chan) = PaintChan::new();
         let (paint_shutdown_chan, paint_shutdown_port) = channel();
         let (layout_shutdown_chan, layout_shutdown_port) = channel();
         let (pipeline_chan, pipeline_port) = channel();
 
         let failure = Failure {
             pipeline_id: id,
@@ -148,16 +150,17 @@ impl Pipeline {
             id: id,
             subpage_id: subpage_id,
             script_chan: script_chan,
             layout_chan: layout_chan,
             paint_chan: paint_chan,
             layout_shutdown_port: layout_shutdown_port,
             paint_shutdown_port: paint_shutdown_port,
             load_data: load_data,
+            title: None,
         }
     }
 
     pub fn load(&self) {
         let ScriptControlChan(ref chan) = self.script_chan;
         chan.send(LoadMsg(self.id, self.load_data.clone()));
     }
 
--- a/servo/components/compositing/windowing.rs
+++ b/servo/components/compositing/windowing.rs
@@ -6,18 +6,18 @@
 
 use compositor_task::{CompositorProxy, CompositorReceiver};
 
 use geom::point::TypedPoint2D;
 use geom::scale_factor::ScaleFactor;
 use geom::size::TypedSize2D;
 use layers::geometry::DevicePixel;
 use layers::platform::surface::NativeGraphicsMetadata;
-use servo_msg::constellation_msg::{Key, KeyState, KeyModifiers};
-use servo_msg::compositor_msg::{ReadyState, PaintState};
+use servo_msg::compositor_msg::{PaintState, ReadyState};
+use servo_msg::constellation_msg::{Key, KeyState, KeyModifiers, LoadData};
 use servo_util::geometry::ScreenPx;
 use std::fmt::{FormatError, Formatter, Show};
 use std::rc::Rc;
 
 pub enum MouseWindowEvent {
     MouseWindowClickEvent(uint, TypedPoint2D<DevicePixel, f32>),
     MouseWindowMouseDownEvent(uint, TypedPoint2D<DevicePixel, f32>),
     MouseWindowMouseUpEvent(uint, TypedPoint2D<DevicePixel, f32>),
@@ -32,27 +32,32 @@ pub enum WindowNavigateMsg {
 pub enum WindowEvent {
     /// Sent when no message has arrived, but the event loop was kicked for some reason (perhaps
     /// by another Servo subsystem).
     ///
     /// FIXME(pcwalton): This is kind of ugly and may not work well with multiprocess Servo.
     /// It's possible that this should be something like
     /// `CompositorMessageWindowEvent(compositor_task::Msg)` instead.
     IdleWindowEvent,
-    /// Sent when part of the window is marked dirty and needs to be redrawn.
+    /// Sent when part of the window is marked dirty and needs to be redrawn. Before sending this
+    /// message, the window must make the same GL context as in `PrepareRenderingEvent` current.
     RefreshWindowEvent,
+    /// Sent to initialize the GL context. The windowing system must have a valid, current GL
+    /// context when this message is sent.
+    InitializeCompositingWindowEvent,
     /// Sent when the window is resized.
     ResizeWindowEvent(TypedSize2D<DevicePixel, uint>),
     /// Sent when a new URL is to be loaded.
     LoadUrlWindowEvent(String),
     /// Sent when a mouse hit test is to be performed.
     MouseWindowEventClass(MouseWindowEvent),
     /// Sent when a mouse move.
     MouseWindowMoveEventClass(TypedPoint2D<DevicePixel, f32>),
-    /// Sent when the user scrolls. Includes the current cursor position.
+    /// Sent when the user scrolls. The first point is the delta and the second point is the
+    /// origin.
     ScrollWindowEvent(TypedPoint2D<DevicePixel, f32>, TypedPoint2D<DevicePixel, i32>),
     /// Sent when the user zooms.
     ZoomWindowEvent(f32),
     /// Simulated "pinch zoom" gesture for non-touch platforms (e.g. ctrl-scrollwheel).
     PinchZoomWindowEvent(f32),
     /// Sent when the user uses chrome navigation (i.e. backspace or shift-backspace).
     NavigationWindowEvent(WindowNavigateMsg),
     /// Sent when the user quits the application
@@ -61,16 +66,17 @@ pub enum WindowEvent {
     KeyEvent(Key, KeyState, KeyModifiers),
 }
 
 impl Show for WindowEvent {
     fn fmt(&self, f: &mut Formatter) -> Result<(),FormatError> {
         match *self {
             IdleWindowEvent => write!(f, "Idle"),
             RefreshWindowEvent => write!(f, "Refresh"),
+            InitializeCompositingWindowEvent => write!(f, "InitializeCompositing"),
             ResizeWindowEvent(..) => write!(f, "Resize"),
             KeyEvent(..) => write!(f, "Key"),
             LoadUrlWindowEvent(..) => write!(f, "LoadUrl"),
             MouseWindowEventClass(..) => write!(f, "Mouse"),
             MouseWindowMoveEventClass(..) => write!(f, "MouseMove"),
             ScrollWindowEvent(..) => write!(f, "Scroll"),
             ZoomWindowEvent(..) => write!(f, "Zoom"),
             PinchZoomWindowEvent(..) => write!(f, "PinchZoom"),
@@ -87,24 +93,35 @@ pub trait WindowMethods {
     fn size(&self) -> TypedSize2D<ScreenPx, f32>;
     /// Presents the window to the screen (perhaps by page flipping).
     fn present(&self);
 
     /// Sets the ready state of the current page.
     fn set_ready_state(&self, ready_state: ReadyState);
     /// Sets the paint state of the current page.
     fn set_paint_state(&self, paint_state: PaintState);
+    /// Sets the page title for the current page.
+    fn set_page_title(&self, title: Option<String>);
+    /// Sets the load data for the current page.
+    fn set_page_load_data(&self, load_data: LoadData);
+    /// Called when the browser is done loading a frame.
+    fn load_end(&self);
 
     /// Returns the hidpi factor of the monitor.
     fn hidpi_factor(&self) -> ScaleFactor<ScreenPx, DevicePixel, f32>;
 
     /// Gets the OS native graphics information for this window.
     fn native_metadata(&self) -> NativeGraphicsMetadata;
 
     /// Creates a channel to the compositor. The dummy parameter is needed because we don't have
     /// UFCS in Rust yet.
     ///
     /// This is part of the windowing system because its implementation often involves OS-specific
     /// magic to wake the up window's event loop.
     fn create_compositor_channel(_: &Option<Rc<Self>>)
                                  -> (Box<CompositorProxy+Send>, Box<CompositorReceiver>);
+
+    /// Requests that the window system prepare a composite. Typically this will involve making
+    /// some type of platform-specific graphics context current. Returns true if the composite may
+    /// proceed and false if it should not.
+    fn prepare_for_composite(&self) -> bool;
 }
 
--- a/servo/components/msg/compositor_msg.rs
+++ b/servo/components/msg/compositor_msg.rs
@@ -14,17 +14,17 @@ use constellation_msg::PipelineId;
 
 /// The status of the painter.
 #[deriving(PartialEq, Clone)]
 pub enum PaintState {
     IdlePaintState,
     PaintingPaintState,
 }
 
-#[deriving(Eq, Ord, PartialEq, PartialOrd, Clone)]
+#[deriving(Eq, Ord, PartialEq, PartialOrd, Clone, Show)]
 pub enum ReadyState {
     /// Informs the compositor that nothing has been done yet. Used for setting status
     Blank,
     /// Informs the compositor that a page is loading. Used for setting status
     Loading,
     /// Informs the compositor that a page is performing layout. Used for setting status
     PerformingLayout,
     /// Informs the compositor that a page is finished loading. Used for setting status
@@ -106,11 +106,13 @@ pub trait PaintListener for Sized? {
 /// The interface used by the script task to tell the compositor to update its ready state,
 /// which is used in displaying the appropriate message in the window's title.
 pub trait ScriptListener {
     fn set_ready_state(&mut self, PipelineId, ReadyState);
     fn scroll_fragment_point(&mut self,
                              pipeline_id: PipelineId,
                              layer_id: LayerId,
                              point: Point2D<f32>);
+    /// Informs the compositor that the title of the page with the given pipeline ID has changed.
+    fn set_title(&mut self, pipeline_id: PipelineId, new_title: Option<String>);
     fn close(&mut self);
     fn dup(&mut self) -> Box<ScriptListener+'static>;
 }
--- a/servo/components/msg/constellation_msg.rs
+++ b/servo/components/msg/constellation_msg.rs
@@ -200,16 +200,19 @@ pub enum Msg {
     LoadCompleteMsg,
     FrameRectMsg(PipelineId, SubpageId, Rect<f32>),
     LoadUrlMsg(PipelineId, LoadData),
     ScriptLoadedURLInIFrameMsg(Url, PipelineId, SubpageId, IFrameSandboxState),
     NavigateMsg(NavigationDirection),
     PainterReadyMsg(PipelineId),
     ResizedWindowMsg(WindowSizeData),
     KeyEvent(Key, KeyState, KeyModifiers),
+    /// Requests that the constellation inform the compositor of the title of the pipeline
+    /// immediately.
+    GetPipelineTitleMsg(PipelineId),
 }
 
 /// Similar to net::resource_task::LoadData
 /// can be passed to LoadUrlMsg to load a page with GET/POST
 /// parameters or headers
 #[deriving(Clone)]
 pub struct LoadData {
     pub url: Url,
--- a/servo/components/script/dom/document.rs
+++ b/servo/components/script/dom/document.rs
@@ -184,16 +184,17 @@ pub trait DocumentHelpers<'a> {
     fn register_named_element(self, element: JSRef<Element>, id: Atom);
     fn load_anchor_href(self, href: DOMString);
     fn find_fragment_node(self, fragid: DOMString) -> Option<Temporary<Element>>;
     fn set_ready_state(self, state: DocumentReadyState);
     fn get_focused_element(self) -> Option<Temporary<Element>>;
     fn begin_focus_transaction(self);
     fn request_focus(self, elem: JSRef<Element>);
     fn commit_focus_transaction(self);
+    fn send_title_to_compositor(self);
 }
 
 impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> {
     #[inline]
     fn window(self) -> Temporary<Window> {
         Temporary::new(self.window)
     }
 
@@ -364,16 +365,22 @@ impl<'a> DocumentHelpers<'a> for JSRef<'
     }
 
     /// Reassign the focus context to the element that last requested focus during this
     /// transaction, or none if no elements requested it.
     fn commit_focus_transaction(self) {
         //TODO: dispatch blur, focus, focusout, and focusin events
         self.focused.assign(self.possibly_focused.get());
     }
+
+    /// Sends this document's title to the compositor.
+    fn send_title_to_compositor(self) {
+        let window = self.window().root();
+        window.page().send_title_to_compositor();
+    }
 }
 
 #[deriving(PartialEq)]
 pub enum DocumentSource {
     FromParser,
     NotFromParser,
 }
 
@@ -980,8 +987,9 @@ impl<'a> DocumentMethods for JSRef<'a, D
     // https://html.spec.whatwg.org/multipage/dom.html#dom-document-readystate
     fn ReadyState(self) -> DocumentReadyState {
         self.ready_state.get()
     }
 
     global_event_handlers!()
     event_handler!(readystatechange, GetOnreadystatechange, SetOnreadystatechange)
 }
+
--- a/servo/components/script/dom/htmltitleelement.rs
+++ b/servo/components/script/dom/htmltitleelement.rs
@@ -1,24 +1,26 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use dom::bindings::codegen::Bindings::HTMLTitleElementBinding;
 use dom::bindings::codegen::Bindings::HTMLTitleElementBinding::HTMLTitleElementMethods;
 use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
-use dom::bindings::codegen::InheritTypes::{HTMLTitleElementDerived, NodeCast, TextCast};
+use dom::bindings::codegen::InheritTypes::{HTMLElementCast, HTMLTitleElementDerived, NodeCast};
+use dom::bindings::codegen::InheritTypes::{TextCast};
 use dom::bindings::js::{JSRef, Temporary};
 use dom::bindings::utils::{Reflectable, Reflector};
-use dom::document::Document;
+use dom::document::{Document, DocumentHelpers};
 use dom::element::HTMLTitleElementTypeId;
 use dom::eventtarget::{EventTarget, NodeTargetTypeId};
 use dom::htmlelement::HTMLElement;
 use dom::node::{Node, NodeHelpers, ElementNodeTypeId};
 use dom::text::Text;
+use dom::virtualmethods::VirtualMethods;
 use servo_util::str::DOMString;
 
 #[dom_struct]
 pub struct HTMLTitleElement {
     htmlelement: HTMLElement,
 }
 
 impl HTMLTitleElementDerived for EventTarget {
@@ -63,8 +65,24 @@ impl<'a> HTMLTitleElementMethods for JSR
     }
 }
 
 impl Reflectable for HTMLTitleElement {
     fn reflector<'a>(&'a self) -> &'a Reflector {
         self.htmlelement.reflector()
     }
 }
+
+impl<'a> VirtualMethods for JSRef<'a, HTMLTitleElement> {
+    fn super_type<'a>(&'a self) -> Option<&'a VirtualMethods> {
+        let htmlelement: &JSRef<HTMLElement> = HTMLElementCast::from_borrowed_ref(self);
+        Some(htmlelement as &VirtualMethods)
+    }
+
+    fn bind_to_tree(&self, is_in_doc: bool) {
+        let node: JSRef<Node> = NodeCast::from_ref(*self);
+        if is_in_doc {
+            let document = node.owner_doc().root();
+            document.send_title_to_compositor()
+        }
+    }
+}
+
--- a/servo/components/script/dom/virtualmethods.rs
+++ b/servo/components/script/dom/virtualmethods.rs
@@ -19,16 +19,17 @@ use dom::bindings::codegen::InheritTypes
 use dom::bindings::codegen::InheritTypes::HTMLObjectElementCast;
 use dom::bindings::codegen::InheritTypes::HTMLOptGroupElementCast;
 use dom::bindings::codegen::InheritTypes::HTMLOptionElementCast;
 use dom::bindings::codegen::InheritTypes::HTMLScriptElementCast;
 use dom::bindings::codegen::InheritTypes::HTMLSelectElementCast;
 use dom::bindings::codegen::InheritTypes::HTMLStyleElementCast;
 use dom::bindings::codegen::InheritTypes::HTMLTableCellElementCast;
 use dom::bindings::codegen::InheritTypes::HTMLTextAreaElementCast;
+use dom::bindings::codegen::InheritTypes::HTMLTitleElementCast;
 use dom::bindings::js::JSRef;
 use dom::document::Document;
 use dom::element::Element;
 use dom::element::ElementTypeId_;
 use dom::element::HTMLAnchorElementTypeId;
 use dom::element::HTMLAreaElementTypeId;
 use dom::element::HTMLBodyElementTypeId;
 use dom::element::HTMLButtonElementTypeId;
@@ -42,16 +43,17 @@ use dom::element::HTMLObjectElementTypeI
 use dom::element::HTMLOptGroupElementTypeId;
 use dom::element::HTMLOptionElementTypeId;
 use dom::element::HTMLScriptElementTypeId;
 use dom::element::HTMLSelectElementTypeId;
 use dom::element::HTMLStyleElementTypeId;
 use dom::element::HTMLTableDataCellElementTypeId;
 use dom::element::HTMLTableHeaderCellElementTypeId;
 use dom::element::HTMLTextAreaElementTypeId;
+use dom::element::HTMLTitleElementTypeId;
 use dom::event::Event;
 use dom::htmlanchorelement::HTMLAnchorElement;
 use dom::htmlareaelement::HTMLAreaElement;
 use dom::htmlbodyelement::HTMLBodyElement;
 use dom::htmlbuttonelement::HTMLButtonElement;
 use dom::htmlcanvaselement::HTMLCanvasElement;
 use dom::htmlelement::HTMLElement;
 use dom::htmlfieldsetelement::HTMLFieldSetElement;
@@ -62,16 +64,17 @@ use dom::htmllinkelement::HTMLLinkElemen
 use dom::htmlobjectelement::HTMLObjectElement;
 use dom::htmloptgroupelement::HTMLOptGroupElement;
 use dom::htmloptionelement::HTMLOptionElement;
 use dom::htmlscriptelement::HTMLScriptElement;
 use dom::htmlselectelement::HTMLSelectElement;
 use dom::htmlstyleelement::HTMLStyleElement;
 use dom::htmltablecellelement::HTMLTableCellElement;
 use dom::htmltextareaelement::HTMLTextAreaElement;
+use dom::htmltitleelement::HTMLTitleElement;
 use dom::node::{Node, NodeHelpers, ElementNodeTypeId, CloneChildrenFlag};
 
 use servo_util::str::DOMString;
 
 use string_cache::Atom;
 
 /// Trait to allow DOM nodes to opt-in to overriding (or adding to) common
 /// behaviours. Replicates the effect of C++ virtual methods.
@@ -227,16 +230,21 @@ pub fn vtable_for<'a>(node: &'a JSRef<'a
         ElementNodeTypeId(HTMLTableHeaderCellElementTypeId) => {
             let element: &'a JSRef<'a, HTMLTableCellElement> = HTMLTableCellElementCast::to_borrowed_ref(node).unwrap();
             element as &'a VirtualMethods + 'a
         }
         ElementNodeTypeId(HTMLTextAreaElementTypeId) => {
             let element: &'a JSRef<'a, HTMLTextAreaElement> = HTMLTextAreaElementCast::to_borrowed_ref(node).unwrap();
             element as &'a VirtualMethods + 'a
         }
+        ElementNodeTypeId(HTMLTitleElementTypeId) => {
+            let element: &'a JSRef<'a, HTMLTitleElement> =
+                HTMLTitleElementCast::to_borrowed_ref(node).unwrap();
+            element as &'a VirtualMethods + 'a
+        }
         ElementNodeTypeId(ElementTypeId_) => {
             let element: &'a JSRef<'a, Element> = ElementCast::to_borrowed_ref(node).unwrap();
             element as &'a VirtualMethods + 'a
         }
         ElementNodeTypeId(_) => {
             let element: &'a JSRef<'a, HTMLElement> = HTMLElementCast::to_borrowed_ref(node).unwrap();
             element as &'a VirtualMethods + 'a
         }
--- a/servo/components/script/page.rs
+++ b/servo/components/script/page.rs
@@ -16,17 +16,16 @@ use layout_interface::{
     GetRPCMsg, HitTestResponse, LayoutChan, LayoutRPC, MouseOverResponse, NoQuery,
     Reflow, ReflowForDisplay, ReflowForScriptQuery, ReflowGoal, ReflowMsg,
     ReflowQueryType, TrustedNodeAddress
 };
 use script_traits::{UntrustedNodeAddress, ScriptControlChan};
 
 use geom::{Point2D, Rect, Size2D};
 use js::rust::Cx;
-use servo_msg::compositor_msg::PerformingLayout;
 use servo_msg::compositor_msg::ScriptListener;
 use servo_msg::constellation_msg::{ConstellationChan, WindowSizeData};
 use servo_msg::constellation_msg::{PipelineId, SubpageId};
 use servo_net::resource_task::ResourceTask;
 use servo_net::storage_task::StorageTask;
 use servo_util::geometry::{Au, MAX_RECT};
 use servo_util::geometry;
 use servo_util::str::DOMString;
@@ -261,16 +260,27 @@ impl Page {
         }
 
         self.page_clip_rect.set(proposed_clip_rect);
 
         // If we didn't have a clip rect, the previous display doesn't need rebuilding
         // because it was built for infinite clip (MAX_RECT).
         had_clip_rect
     }
+
+    pub fn send_title_to_compositor(&self) {
+        match *self.frame() {
+            None => {}
+            Some(ref frame) => {
+                let window = frame.window.root();
+                let document = frame.document.root();
+                window.compositor().set_title(self.id, Some(document.Title()));
+            }
+        }
+    }
 }
 
 impl Iterator<Rc<Page>> for PageIterator {
     fn next(&mut self) -> Option<Rc<Page>> {
         match self.stack.pop() {
             Some(next) => {
                 for child in next.children.borrow().iter() {
                     self.stack.push(child.clone());
@@ -351,17 +361,17 @@ impl Page {
     /// Reflows the page if it's possible to do so. This method will wait until the layout task has
     /// completed its current action, join the layout task, and then request a new layout run. It
     /// won't wait for the new layout computation to finish.
     ///
     /// If there is no window size yet, the page is presumed invisible and no reflow is performed.
     pub fn reflow(&self,
                   goal: ReflowGoal,
                   script_chan: ScriptControlChan,
-                  compositor: &mut ScriptListener,
+                  _: &mut ScriptListener,
                   query_type: ReflowQueryType) {
         let root = match *self.frame() {
             None => return,
             Some(ref frame) => {
                 frame.document.root().GetDocumentElement()
             }
         };
 
@@ -371,19 +381,16 @@ impl Page {
                 debug!("avoided {:d} reflows", self.avoided_reflows.get());
                 self.avoided_reflows.set(0);
 
                 debug!("script: performing reflow for goal {}", goal);
 
                 // Now, join the layout so that they will see the latest changes we have made.
                 self.join_layout();
 
-                // Tell the user that we're performing layout.
-                compositor.set_ready_state(self.id, PerformingLayout);
-
                 // Layout will let us know when it's done.
                 let (join_chan, join_port) = channel();
                 let mut layout_join_port = self.layout_join_port.borrow_mut();
                 *layout_join_port = Some(join_port);
 
                 let last_reflow_id = &self.last_reflow_id;
                 last_reflow_id.set(last_reflow_id.get() + 1);
 
--- a/servo/components/script/script_task.rs
+++ b/servo/components/script/script_task.rs
@@ -38,20 +38,22 @@ use devtools;
 use devtools_traits::{DevtoolsControlChan, DevtoolsControlPort, NewGlobal, GetRootNode};
 use devtools_traits::{DevtoolScriptControlMsg, EvaluateJS, GetDocumentElement};
 use devtools_traits::{GetChildren, GetLayout, ModifyAttribute};
 use script_traits::{CompositorEvent, ResizeEvent, ReflowEvent, ClickEvent, MouseDownEvent};
 use script_traits::{MouseMoveEvent, MouseUpEvent, ConstellationControlMsg, ScriptTaskFactory};
 use script_traits::{ResizeMsg, AttachLayoutMsg, LoadMsg, ViewportMsg, SendEventMsg};
 use script_traits::{ResizeInactiveMsg, ExitPipelineMsg, NewLayoutInfo, OpaqueScriptLayoutChannel};
 use script_traits::{ScriptControlChan, ReflowCompleteMsg, UntrustedNodeAddress, KeyEvent};
-use servo_msg::compositor_msg::{FinishedLoading, LayerId, Loading};
+use script_traits::{GetTitleMsg};
+use servo_msg::compositor_msg::{FinishedLoading, LayerId, Loading, PerformingLayout};
 use servo_msg::compositor_msg::{ScriptListener};
-use servo_msg::constellation_msg::{ConstellationChan, LoadCompleteMsg, LoadUrlMsg, NavigationDirection};
-use servo_msg::constellation_msg::{LoadData, PipelineId, Failure, FailureMsg, WindowSizeData, Key, KeyState};
+use servo_msg::constellation_msg::{ConstellationChan, LoadCompleteMsg};
+use servo_msg::constellation_msg::{LoadData, LoadUrlMsg, NavigationDirection, PipelineId};
+use servo_msg::constellation_msg::{Failure, FailureMsg, WindowSizeData, Key, KeyState};
 use servo_msg::constellation_msg::{KeyModifiers, SUPER, SHIFT, CONTROL, ALT, Repeated, Pressed};
 use servo_msg::constellation_msg::{Released};
 use servo_msg::constellation_msg;
 use servo_net::image_cache_task::ImageCacheTask;
 use servo_net::resource_task::{ResourceTask, Load};
 use servo_net::resource_task::LoadData as NetLoadData;
 use servo_net::storage_task::StorageTask;
 use servo_util::geometry::to_frac_px;
@@ -545,16 +547,19 @@ impl ScriptTask {
                 FromScript(FireTimerMsg(FromWorker, _)) => panic!("Worker timeouts must not be sent to script task"),
                 FromScript(NavigateMsg(direction)) => self.handle_navigate_msg(direction),
                 FromConstellation(ReflowCompleteMsg(id, reflow_id)) => self.handle_reflow_complete_msg(id, reflow_id),
                 FromConstellation(ResizeInactiveMsg(id, new_size)) => self.handle_resize_inactive_msg(id, new_size),
                 FromConstellation(ExitPipelineMsg(id)) => if self.handle_exit_pipeline_msg(id) { return false },
                 FromConstellation(ViewportMsg(..)) => panic!("should have handled ViewportMsg already"),
                 FromScript(ExitWindowMsg(id)) => self.handle_exit_window_msg(id),
                 FromConstellation(ResizeMsg(..)) => panic!("should have handled ResizeMsg already"),
+                FromConstellation(GetTitleMsg(pipeline_id)) => {
+                    self.handle_get_title_msg(pipeline_id)
+                }
                 FromScript(XHRProgressMsg(addr, progress)) => XMLHttpRequest::handle_progress(addr, progress),
                 FromScript(XHRReleaseMsg(addr)) => XMLHttpRequest::handle_release(addr),
                 FromScript(DOMMessage(..)) => panic!("unexpected message"),
                 FromScript(WorkerPostMessage(addr, data, nbytes)) => Worker::handle_message(addr, data, nbytes),
                 FromScript(WorkerRelease(addr)) => Worker::handle_release(addr),
                 FromDevtools(EvaluateJS(id, s, reply)) => devtools::handle_evaluate_js(&*self.page.borrow(), id, s, reply),
                 FromDevtools(GetRootNode(id, reply)) => devtools::handle_get_root_node(&*self.page.borrow(), id, reply),
                 FromDevtools(GetDocumentElement(id, reply)) => devtools::handle_get_document_element(&*self.page.borrow(), id, reply),
@@ -656,16 +661,21 @@ impl ScriptTask {
         debug!("script task handling exit window msg");
 
         // TODO(tkuehn): currently there is only one window,
         // so this can afford to be naive and just shut down the
         // compositor. In the future it'll need to be smarter.
         self.compositor.borrow_mut().close();
     }
 
+    /// Handles a request for the window title.
+    fn handle_get_title_msg(&self, pipeline_id: PipelineId) {
+        get_page(&*self.page.borrow(), pipeline_id).send_title_to_compositor();
+    }
+
     /// Handles a request to exit the script task and shut down layout.
     /// Returns true if the script task should shut down and false otherwise.
     fn handle_exit_pipeline_msg(&self, id: PipelineId) -> bool {
         // If root is being exited, shut down all pages
         let page = self.page.borrow_mut();
         if page.id == id {
             debug!("shutting down layout for root page {}", id);
             *self.js_context.borrow_mut() = None;
@@ -779,16 +789,17 @@ impl ScriptTask {
             let jsval = window.evaluate_js_with_result(evalstr);
             let strval = FromJSValConvertible::from_jsval(self.get_cx(), jsval, Empty);
             (InputString(strval.unwrap_or("".to_string())), doc_url)
         };
 
         parse_html(*document, parser_input, &final_url);
 
         document.set_ready_state(DocumentReadyStateValues::Interactive);
+        self.compositor.borrow_mut().set_ready_state(pipeline_id, PerformingLayout);
 
         // Kick off the initial reflow of the page.
         debug!("kicking off initial reflow of {}", final_url);
         document.content_changed(NodeCast::from_ref(*document));
         window.flush_layout();
 
         {
             // No more reflow required
--- a/servo/components/script_traits/lib.rs
+++ b/servo/components/script_traits/lib.rs
@@ -60,17 +60,20 @@ pub enum ConstellationControlMsg {
     /// Notifies script that window has been resized but to not take immediate action.
     ResizeInactiveMsg(PipelineId, WindowSizeData),
     /// Notifies the script that a pipeline should be closed.
     ExitPipelineMsg(PipelineId),
     /// Sends a DOM event.
     SendEventMsg(PipelineId, CompositorEvent),
     /// Notifies script that reflow is finished.
     ReflowCompleteMsg(PipelineId, uint),
+    /// Notifies script of the viewport.
     ViewportMsg(PipelineId, Rect<f32>),
+    /// Requests that the script task immediately send the constellation the title of a pipeline.
+    GetTitleMsg(PipelineId),
 }
 
 /// Events from the compositor that the script task needs to know about
 pub enum CompositorEvent {
     ResizeEvent(WindowSizeData),
     ReflowEvent(SmallVec1<UntrustedNodeAddress>),
     ClickEvent(uint, Point2D<f32>),
     MouseDownEvent(uint, Point2D<f32>),
--- a/servo/components/servo/Cargo.lock
+++ b/servo/components/servo/Cargo.lock
@@ -53,17 +53,17 @@ version = "0.0.1"
 source = "git+https://github.com/servo/rust-cgl#7b7090729f65e2287c3d80651df02e547911b119"
 dependencies = [
  "gleam 0.0.1 (git+https://github.com/servo/gleam)",
 ]
 
 [[package]]
 name = "cocoa"
 version = "0.1.1"
-source = "git+https://github.com/servo/rust-cocoa#78b823bec1affcab20b6977e1057125088a6034c"
+source = "git+https://github.com/servo/rust-cocoa#084f8e1baf40391eb12819d16765af25ca96c7ec"
 
 [[package]]
 name = "compile_msg"
 version = "0.1.1"
 source = "git+https://github.com/huonw/compile_msg#f526abe54b49642bc1e969e6c2af1411798b6776"
 
 [[package]]
 name = "compositing"
@@ -389,17 +389,17 @@ dependencies = [
 [[package]]
 name = "khronos_api"
 version = "0.0.1"
 source = "git+https://github.com/bjz/gl-rs.git#79cd3b3f9f19aa0e39f6af572fc8673a6d9760bc"
 
 [[package]]
 name = "layers"
 version = "0.1.0"
-source = "git+https://github.com/servo/rust-layers#b068d2a96d54bf173b548aece36f5ea4ef9353cf"
+source = "git+https://github.com/servo/rust-layers#63d1093f2a01a6fb9599ea6d932aadf79598451f"
 dependencies = [
  "cgl 0.0.1 (git+https://github.com/servo/rust-cgl)",
  "core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)",
  "egl 0.1.0 (git+https://github.com/servo/rust-egl)",
  "geom 0.1.0 (git+https://github.com/servo/rust-geom)",
  "gleam 0.0.1 (git+https://github.com/servo/gleam)",
  "glx 0.0.1 (git+https://github.com/servo/rust-glx)",
  "io_surface 0.1.0 (git+https://github.com/servo/rust-io-surface)",
--- a/servo/components/servo/lib.rs
+++ b/servo/components/servo/lib.rs
@@ -148,13 +148,21 @@ impl<Window> Browser<Window> where Windo
     pub fn handle_event(&mut self, event: WindowEvent) -> bool {
         self.compositor.handle_event(event)
     }
 
     pub fn repaint_synchronously(&mut self) {
         self.compositor.repaint_synchronously()
     }
 
+    pub fn pinch_zoom_level(&self) -> f32 {
+        self.compositor.pinch_zoom_level()
+    }
+
+    pub fn get_title_for_main_frame(&self) {
+        self.compositor.get_title_for_main_frame()
+    }
+
     pub fn shutdown(mut self) {
         self.compositor.shutdown();
     }
 }
 
--- a/servo/components/servo/main.rs
+++ b/servo/components/servo/main.rs
@@ -25,17 +25,19 @@ extern crate compositing;
 use servo_util::opts;
 
 #[cfg(not(any(test,target_os="android")))]
 use servo_util::rtinstrument;
 
 #[cfg(not(any(test,target_os="android")))]
 use servo::Browser;
 #[cfg(not(any(test,target_os="android")))]
-use compositing::windowing::{IdleWindowEvent, ResizeWindowEvent, WindowEvent};
+use compositing::windowing::{IdleWindowEvent, InitializeCompositingWindowEvent, ResizeWindowEvent};
+#[cfg(not(any(test,target_os="android")))]
+use compositing::windowing::{WindowEvent};
 
 #[cfg(not(any(test,target_os="android")))]
 use std::os;
 
 #[cfg(not(any(test,target_os="android")))]
 struct BrowserWrapper {
     browser: Browser<app::window::Window>,
 }
@@ -60,16 +62,18 @@ fn start(argc: int, argv: *const *const 
                 None => {}
                 Some(ref window) => {
                     unsafe {
                         window.set_nested_event_loop_listener(&mut browser);
                     }
                 }
             }
 
+            browser.browser.handle_event(InitializeCompositingWindowEvent);
+
             loop {
                 let should_continue = match window {
                     None => browser.browser.handle_event(IdleWindowEvent),
                     Some(ref window) => {
                         let event = window.wait_events();
                         browser.browser.handle_event(event)
                     }
                 };
--- a/servo/ports/android/glut_app/Cargo.lock
+++ b/servo/ports/android/glut_app/Cargo.lock
@@ -322,17 +322,17 @@ dependencies = [
 [[package]]
 name = "khronos_api"
 version = "0.0.1"
 source = "git+https://github.com/bjz/gl-rs.git#79cd3b3f9f19aa0e39f6af572fc8673a6d9760bc"
 
 [[package]]
 name = "layers"
 version = "0.1.0"
-source = "git+https://github.com/servo/rust-layers#0f6edd58b3b572f2aac97567b752c15db5fa1982"
+source = "git+https://github.com/servo/rust-layers#63d1093f2a01a6fb9599ea6d932aadf79598451f"
 dependencies = [
  "cgl 0.0.1 (git+https://github.com/servo/rust-cgl)",
  "core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)",
  "egl 0.1.0 (git+https://github.com/servo/rust-egl)",
  "geom 0.1.0 (git+https://github.com/servo/rust-geom)",
  "gleam 0.0.1 (git+https://github.com/servo/gleam)",
  "glx 0.0.1 (git+https://github.com/servo/rust-glx)",
  "io_surface 0.1.0 (git+https://github.com/servo/rust-io-surface)",
--- a/servo/ports/android/glut_app/window.rs
+++ b/servo/ports/android/glut_app/window.rs
@@ -14,18 +14,18 @@ use compositing::windowing::{Forward, Ba
 use libc::{c_int, c_uchar};
 use std::cell::{Cell, RefCell};
 use std::rc::Rc;
 use geom::point::{Point2D, TypedPoint2D};
 use geom::scale_factor::ScaleFactor;
 use geom::size::TypedSize2D;
 use layers::geometry::DevicePixel;
 use layers::platform::surface::NativeGraphicsMetadata;
-use msg::compositor_msg::{IdlePaintState, PaintState};
-use msg::compositor_msg::{Blank, ReadyState};
+use msg::compositor_msg::{Blank, IdlePaintState, PaintState, ReadyState};
+use msg::constellation_msg::LoadData;
 use util::geometry::ScreenPx;
 
 use glut::glut::{ACTIVE_SHIFT, WindowHeight};
 use glut::glut::WindowWidth;
 use glut::glut;
 
 // static THROBBER: [char, ..8] = [ '⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷' ];
 
@@ -171,38 +171,58 @@ impl WindowMethods for Window {
              sender: sender,
          } as Box<CompositorProxy+Send>,
          box receiver as Box<CompositorReceiver>)
     }
 
     /// Sets the ready state.
     fn set_ready_state(&self, ready_state: ReadyState) {
         self.ready_state.set(ready_state);
-        //FIXME: set_window_title causes crash with Android version of freeGLUT. Temporarily blocked.
-        //self.update_window_title()
+        // FIXME: set_window_title causes crash with Android version of freeGLUT. Temporarily
+        // blocked.
+        //
+        // self.update_window_title()
     }
 
     /// Sets the paint state.
     fn set_paint_state(&self, paint_state: PaintState) {
         self.paint_state.set(paint_state);
-        //FIXME: set_window_title causes crash with Android version of freeGLUT. Temporarily blocked.
-        //self.update_window_title()
+        // FIXME: set_window_title causes crash with Android version of freeGLUT. Temporarily
+        // blocked.
+        //
+        // self.update_window_title()
     }
 
     fn hidpi_factor(&self) -> ScaleFactor<ScreenPx, DevicePixel, f32> {
-        //FIXME: Do nothing in GLUT now.
+        // FIXME: Do nothing in GLUT now.
         ScaleFactor(1.0)
     }
 
     fn native_metadata(&self) -> NativeGraphicsMetadata {
         use egl::egl::GetCurrentDisplay;
         NativeGraphicsMetadata {
             display: GetCurrentDisplay(),
         }
     }
+
+    fn set_page_title(&self, _: Option<String>) {
+        // TODO(pcwalton)
+    }
+
+    fn set_page_load_data(&self, _: LoadData) {
+        // TODO(pcwalton)
+    }
+
+    fn load_end(&self) {
+        // TODO(pcwalton)
+    }
+
+    fn prepare_for_composite(&self) -> bool {
+        true
+    }
 }
 
 impl Window {
     /// Helper function to set the window title in accordance with the ready state.
     // fn update_window_title(&self) {
     //     let throbber = THROBBER[self.throbber_frame];
     //     match self.ready_state {
     //         Blank => {
--- a/servo/ports/cef/Cargo.lock
+++ b/servo/ports/cef/Cargo.lock
@@ -1,13 +1,14 @@
 [root]
 name = "embedding"
 version = "0.0.1"
 dependencies = [
  "azure 0.1.0 (git+https://github.com/servo/rust-azure)",
+ "cocoa 0.1.1 (git+https://github.com/servo/rust-cocoa)",
  "core_graphics 0.1.0 (git+https://github.com/servo/rust-core-graphics)",
  "core_text 0.1.0 (git+https://github.com/servo/rust-core-text)",
  "devtools 0.0.1",
  "geom 0.1.0 (git+https://github.com/servo/rust-geom)",
  "gfx 0.0.1",
  "glfw 0.0.1 (git+https://github.com/servo/glfw-rs?ref=servo)",
  "glfw_app 0.0.1",
  "js 0.1.0 (git+https://github.com/servo/rust-mozjs)",
@@ -52,16 +53,21 @@ dependencies = [
 name = "cgl"
 version = "0.0.1"
 source = "git+https://github.com/servo/rust-cgl#7b7090729f65e2287c3d80651df02e547911b119"
 dependencies = [
  "gleam 0.0.1 (git+https://github.com/servo/gleam)",
 ]
 
 [[package]]
+name = "cocoa"
+version = "0.1.1"
+source = "git+https://github.com/servo/rust-cocoa#084f8e1baf40391eb12819d16765af25ca96c7ec"
+
+[[package]]
 name = "compositing"
 version = "0.0.1"
 dependencies = [
  "azure 0.1.0 (git+https://github.com/servo/rust-azure)",
  "core_graphics 0.1.0 (git+https://github.com/servo/rust-core-graphics)",
  "core_text 0.1.0 (git+https://github.com/servo/rust-core-text)",
  "devtools 0.0.1",
  "devtools_traits 0.0.1",
@@ -353,17 +359,17 @@ dependencies = [
 [[package]]
 name = "khronos_api"
 version = "0.0.1"
 source = "git+https://github.com/bjz/gl-rs.git#79cd3b3f9f19aa0e39f6af572fc8673a6d9760bc"
 
 [[package]]
 name = "layers"
 version = "0.1.0"
-source = "git+https://github.com/servo/rust-layers#b068d2a96d54bf173b548aece36f5ea4ef9353cf"
+source = "git+https://github.com/servo/rust-layers#0f6edd58b3b572f2aac97567b752c15db5fa1982"
 dependencies = [
  "cgl 0.0.1 (git+https://github.com/servo/rust-cgl)",
  "core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)",
  "egl 0.1.0 (git+https://github.com/servo/rust-egl)",
  "geom 0.1.0 (git+https://github.com/servo/rust-geom)",
  "gleam 0.0.1 (git+https://github.com/servo/gleam)",
  "glx 0.0.1 (git+https://github.com/servo/rust-glx)",
  "io_surface 0.1.0 (git+https://github.com/servo/rust-io-surface)",
--- a/servo/ports/cef/Cargo.toml
+++ b/servo/ports/cef/Cargo.toml
@@ -60,8 +60,11 @@ git = "https://github.com/servo/rust-png
 [dependencies.stb_image]
 git = "https://github.com/servo/rust-stb-image"
 
 [dependencies.core_graphics]
 git = "https://github.com/servo/rust-core-graphics"
 
 [dependencies.core_text]
 git = "https://github.com/servo/rust-core-text"
+
+[dependencies.cocoa]
+git = "https://github.com/servo/rust-cocoa"
--- a/servo/ports/cef/browser.rs
+++ b/servo/ports/cef/browser.rs
@@ -1,66 +1,122 @@
 /* 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 interfaces::{CefBrowser, CefClient, CefRequestContext, cef_browser_t, cef_client_t};
+use browser_host::{ServoCefBrowserHost, ServoCefBrowserHostExtensions};
+use core::{mod, OffScreenGlobals, OnScreenGlobals, globals};
+use eutil::Downcast;
+use frame::ServoCefFrame;
+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 eutil::Downcast;
+use compositing::windowing::{Back, Forward, NavigationWindowEvent};
 use glfw_app;
 use libc::c_int;
-use servo::Browser;
 use servo_util::opts;
 use std::cell::{Cell, RefCell};
-use std::rc::Rc;
+
+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(NavigationWindowEvent(Back));
+        }
+
+        fn go_forward(&_this) -> () {
+            core::send_window_event(NavigationWindowEvent(Forward));
+        }
+
+        // Returns the main (top-level) frame for the browser window.
+        fn get_main_frame(&this) -> *mut cef_frame_t {
+            this.downcast().frame.clone()
+        }
+    }
+}
 
 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,
-    pub servo_browser: RefCell<Option<Browser<glfw_app::window::Window>>>,
-    pub window: RefCell<Option<Rc<glfw_app::window::Window>>>,
+    /// Whether the on-created callback has fired yet.
     pub callback_executed: Cell<bool>,
 }
 
 impl ServoCefBrowser {
-    pub fn new(client: CefClient) -> 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 {
+            let glfw_window = glfw_app::create_window();
+            globals.replace(Some(OnScreenGlobals(RefCell::new(glfw_window.clone()),
+                                                 RefCell::new(Browser::new(Some(glfw_window))))));
+        }
+
         ServoCefBrowser {
+            frame: frame,
+            host: host,
             client: client,
-            servo_browser: RefCell::new(None),
-            window: RefCell::new(None),
             callback_executed: Cell::new(false),
         }
     }
 }
 
-cef_class_impl! {
-    ServoCefBrowser : CefBrowser, cef_browser_t {}
+trait ServoCefBrowserExtensions {
+    fn init(&self, window_info: &cef_window_info_t);
+}
+
+impl ServoCefBrowserExtensions for CefBrowser {
+    fn init(&self, window_info: &cef_window_info_t) {
+        if window_info.windowless_rendering_enabled != 0 {
+            let window = window::Window::new();
+            let servo_browser = Browser::new(Some(window.clone()));
+            window.set_browser(self.clone());
+            globals.replace(Some(OffScreenGlobals(RefCell::new(window),
+                                                  RefCell::new(servo_browser))));
+        }
+
+        self.downcast().host.set_browser((*self).clone());
+    }
 }
 
 local_data_key!(pub GLOBAL_BROWSERS: RefCell<Vec<CefBrowser>>)
 
 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() {
         life_span_handler.on_after_created(browser.clone());
     }
     browser.downcast().callback_executed.set(true);
 }
 
-fn browser_host_create(client: CefClient, callback_executed: bool) -> CefBrowser {
+fn browser_host_create(window_info: &cef_window_info_t,
+                       client: CefClient,
+                       callback_executed: bool)
+                       -> CefBrowser {
     let mut urls = Vec::new();
     urls.push("http://s27.postimg.org/vqbtrolyr/servo.jpg".to_string());
     let mut opts = opts::default_opts();
     opts.urls = urls;
-    let browser = ServoCefBrowser::new(client).as_cef_interface();
+    let browser = ServoCefBrowser::new(window_info, client).as_cef_interface();
+    browser.init(window_info);
     if callback_executed {
         browser_callback_after_created(browser.clone());
     }
     match GLOBAL_BROWSERS.replace(None) {
         Some(brs) => {
             brs.borrow_mut().push(browser.clone());
             GLOBAL_BROWSERS.replace(Some(brs));
         },
@@ -68,37 +124,35 @@ fn browser_host_create(client: CefClient
             let brs = RefCell::new(vec!(browser.clone()));
             GLOBAL_BROWSERS.replace(Some(brs));
         }
     }
     browser
 }
 
 cef_static_method_impls! {
-    fn cef_browser_host_create_browser(_window_info: *const cef_window_info_t,
+    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,
                                        _request_context: *mut cef_request_context_t)
                                        -> c_int {
-        let _window_info: &cef_window_info_t = _window_info;
         let client: CefClient = client;
         let _url: &[u16] = _url;
         let _browser_settings: &cef_browser_settings_t = _browser_settings;
         let _request_context: CefRequestContext = _request_context;
-        browser_host_create(client, false);
+        browser_host_create(window_info, client, false);
         1i32
     }
-
-    fn cef_browser_host_create_browser_sync(_window_info: *const cef_window_info_t,
+    fn cef_browser_host_create_browser_sync(window_info: *const cef_window_info_t,
                                             client: *mut cef_client_t,
                                             _url: *const cef_string_t,
                                             _browser_settings: *const cef_browser_settings_t,
                                             _request_context: *mut cef_request_context_t)
                                             -> *mut cef_browser_t {
-        let _window_info: &cef_window_info_t = _window_info;
         let client: CefClient = client;
         let _url: &[u16] = _url;
         let _browser_settings: &cef_browser_settings_t = _browser_settings;
         let _request_context: CefRequestContext = _request_context;
-        browser_host_create(client, true)
+        browser_host_create(window_info, client, true)
     }
 }
+
new file mode 100644
--- /dev/null
+++ b/servo/ports/cef/browser_host.rs
@@ -0,0 +1,158 @@
+/* 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::{KEYEVENT_CHAR, KEYEVENT_KEYDOWN, KEYEVENT_KEYUP, KEYEVENT_RAWKEYDOWN, cef_key_event};
+use types::{cef_mouse_button_type_t, cef_mouse_event, cef_rect_t};
+
+use compositing::windowing::{InitializeCompositingWindowEvent, KeyEvent, MouseWindowClickEvent};
+use compositing::windowing::{MouseWindowEventClass, MouseWindowMouseUpEvent, PinchZoomWindowEvent};
+use compositing::windowing::{ResizeWindowEvent, ScrollWindowEvent};
+use geom::point::TypedPoint2D;
+use geom::size::TypedSize2D;
+use libc::{c_double, c_int};
+use servo_msg::constellation_msg::{mod, KeyModifiers, Pressed, Released, Repeated};
+use std::cell::RefCell;
+
+pub struct ServoCefBrowserHost {
+    /// A reference to the browser.
+    pub browser: RefCell<Option<CefBrowser>>,
+    /// A reference to the client.
+    pub client: CefClient,
+}
+
+cef_class_impl! {
+    ServoCefBrowserHost : CefBrowserHost, cef_browser_host_t {
+        fn get_client(&this) -> *mut cef_client_t {
+            this.downcast().client.clone()
+        }
+
+        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(ResizeWindowEvent(size));
+            core::repaint_synchronously();
+        }
+
+        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::KeyA,
+                b'b' | b'B' => constellation_msg::KeyB,
+                b'c' | b'C' => constellation_msg::KeyC,
+                b'd' | b'D' => constellation_msg::KeyD,
+                b'e' | b'E' => constellation_msg::KeyE,
+                b'f' | b'F' => constellation_msg::KeyF,
+                b'g' | b'G' => constellation_msg::KeyG,
+                b'h' | b'H' => constellation_msg::KeyH,
+                b'i' | b'I' => constellation_msg::KeyI,
+                b'j' | b'J' => constellation_msg::KeyJ,
+                b'k' | b'K' => constellation_msg::KeyK,
+                b'l' | b'L' => constellation_msg::KeyL,
+                b'm' | b'M' => constellation_msg::KeyM,
+                b'n' | b'N' => constellation_msg::KeyN,
+                b'o' | b'O' => constellation_msg::KeyO,
+                b'p' | b'P' => constellation_msg::KeyP,
+                b'q' | b'Q' => constellation_msg::KeyQ,
+                b'r' | b'R' => constellation_msg::KeyR,
+                b's' | b'S' => constellation_msg::KeyS,
+                b't' | b'T' => constellation_msg::KeyT,
+                b'u' | b'U' => constellation_msg::KeyU,
+                b'v' | b'V' => constellation_msg::KeyV,
+                b'w' | b'W' => constellation_msg::KeyW,
+                b'x' | b'X' => constellation_msg::KeyX,
+                b'y' | b'Y' => constellation_msg::KeyY,
+                b'z' | b'Z' => constellation_msg::KeyZ,
+                b'0' => constellation_msg::Key0,
+                b'1' => constellation_msg::Key1,
+                b'2' => constellation_msg::Key2,
+                b'3' => constellation_msg::Key3,
+                b'4' => constellation_msg::Key4,
+                b'5' => constellation_msg::Key5,
+                b'6' => constellation_msg::Key6,
+                b'7' => constellation_msg::Key7,
+                b'8' => constellation_msg::Key8,
+                b'9' => constellation_msg::Key9,
+                b'\n' | b'\r' => constellation_msg::KeyEnter,
+                _ => constellation_msg::KeySpace,
+            };
+            let key_state = match (*event).t {
+                KEYEVENT_RAWKEYDOWN => Pressed,
+                KEYEVENT_KEYDOWN | KEYEVENT_CHAR => Repeated,
+                KEYEVENT_KEYUP => Released,
+            };
+            let key_modifiers = KeyModifiers::empty();  // TODO(pcwalton)
+            core::send_window_event(KeyEvent(key, key_state, key_modifiers))
+        }
+
+        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(MouseWindowEventClass(MouseWindowClickEvent(button_type,
+                                                                                    point)))
+            } else {
+                core::send_window_event(MouseWindowEventClass(MouseWindowMouseUpEvent(button_type,
+                                                                                      point)))
+            }
+        }
+
+        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(ScrollWindowEvent(delta, origin))
+        }
+
+        fn get_zoom_level(&_this) -> c_double {
+            core::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(PinchZoomWindowEvent((new_zoom_level / old_zoom_level) as f32))
+        }
+
+        fn initialize_compositing(&_this) -> () {
+            core::send_window_event(InitializeCompositingWindowEvent);
+        }
+    }
+}
+
+impl ServoCefBrowserHost {
+    pub fn new(client: CefClient) -> ServoCefBrowserHost {
+        ServoCefBrowserHost {
+            browser: RefCell::new(None),
+            client: client,
+        }
+    }
+}
+
+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)
+    }
+}
+
--- a/servo/ports/cef/command_line.rs
+++ b/servo/ports/cef/command_line.rs
@@ -41,17 +41,17 @@ pub fn command_line_init(argc: c_int, ar
         (*cl).cl.get_switch_value = Some(command_line_get_switch_value);
         GLOBAL_CMDLINE = Some(cl);
     }
 }
 
 #[no_mangle]
 pub extern "C" fn command_line_get_switch_value(cmd: *mut cef_command_line_t, name: *const cef_string_t) -> cef_string_userfree_t {
     if cmd.is_null() || name.is_null() {
-        return cef_string::empty_utf16_string()
+        return cef_string::empty_utf16_userfree_string()
     }
     unsafe {
         //technically cef_string_t can be any type of character size
         //but the default cef callback uses utf16, so I'm jumping on board the SS Copy
         let cl: *mut command_line_t = mem::transmute(cmd);
         let cs: *const cef_string_utf16_t = mem::transmute(name);
         let opt = String::from_utf16(CVec::new((*cs).str, (*cs).length as uint).as_slice()).unwrap();
             //debug!("opt: {}", opt);
@@ -62,21 +62,21 @@ pub extern "C" fn command_line_get_switc
                 let mut string = mem::uninitialized();
                 let arg = o.slice_from(opt.len() + 1).as_bytes();
                 arg.with_c_str(|c_str| {
                     cef_string_utf16_set(mem::transmute(c_str),
                                          arg.len() as size_t,
                                          &mut string,
                                          1);
                 });
-                return string
+                return cef_string::string_to_userfree_string(string)
             }
         }
     }
-    return cef_string::empty_utf16_string()
+    return cef_string::empty_utf16_userfree_string()
 }
 
 #[no_mangle]
 pub extern "C" fn cef_command_line_create() -> *mut cef_command_line_t {
         unsafe {
             let cl = command_line_new();
             (*cl).cl.get_switch_value = Some(command_line_get_switch_value);
             mem::transmute(cl)
--- a/servo/ports/cef/core.rs
+++ b/servo/ports/cef/core.rs
@@ -1,140 +1,273 @@
 /* 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::{GLOBAL_BROWSERS, browser_callback_after_created};
 use command_line::command_line_init;
 use interfaces::cef_app_t;
-use eutil::Downcast;
-use switches::{KPROCESSTYPE, KWAITFORDEBUGGER};
 use types::{cef_main_args_t, cef_settings_t};
+use window;
 
+use compositing::windowing::{IdleWindowEvent, WindowEvent};
+use geom::size::TypedSize2D;
 use glfw_app;
-use libc::funcs::c95::string::strlen;
 use libc::{c_char, c_int, c_void};
 use native;
+use rustrt::local::Local;
 use servo::Browser;
-use std::slice;
+use servo_util::opts;
+use servo_util::opts::OpenGL;
+use std::c_str::CString;
+use std::cell::RefCell;
+use std::rc::Rc;
+use std::rt;
+
+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>>),
+}
+
+local_data_key!(pub globals: ServoCefGlobals)
+
+local_data_key!(pub message_queue: RefCell<Vec<WindowEvent>>)
+
+// Copied from `libnative/lib.rs`.
+static OS_DEFAULT_STACK_ESTIMATE: uint = 2 * (1 << 20);
 
 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";
 
 #[no_mangle]
 pub extern "C" fn cef_initialize(args: *const cef_main_args_t,
-                                 _settings: *mut cef_settings_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;
     }
+
     unsafe {
+        rt::init((*args).argc as int, (*args).argv);
         command_line_init((*args).argc, (*args).argv);
-        (*application).get_browser_process_handler.map(|cb| {
-                let handler = cb(application);
-                if handler.is_not_null() {
-                    (*handler).on_context_initialized.map(|hcb| hcb(handler));
-                }
-        });
+
+        if !application.is_null() {
+            (*application).get_browser_process_handler.map(|cb| {
+                    let handler = cb(application);
+                    if handler.is_not_null() {
+                        (*handler).on_context_initialized.map(|hcb| hcb(handler));
+                    }
+            });
+        }
     }
+
+    create_rust_task();
+
+    message_queue.replace(Some(RefCell::new(Vec::new())));
+
+    let urls = vec![HOME_URL.to_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,
+        memory_profiler_period: None,
+        enable_experimental: false,
+        nonincremental_layout: false,
+        layout_threads: unsafe {
+            if ((*settings).rendering_threads as uint) < 1 {
+                1
+            } else if (*settings).rendering_threads as uint > MAX_RENDERING_THREADS {
+                MAX_RENDERING_THREADS
+            } else {
+                (*settings).rendering_threads as uint
+            }
+        },
+        output_file: None,
+        headless: false,
+        hard_fail: false,
+        bubble_inline_sizes_separately: false,
+        show_debug_borders: false,
+        show_debug_fragment_borders: false,
+        enable_text_antialiasing: true,
+        trace_layout: false,
+        devtools_port: None,
+        initial_window_size: TypedSize2D(800, 600),
+        profile_tasks: false,
+        user_agent: None,
+        dump_flow_tree: false,
+        validate_display_list_geometry: false,
+        render_api: OpenGL,
+    });
+
     return 1
 }
 
+// Copied from `libnative/lib.rs`.
+fn create_rust_task() {
+    let something_around_the_top_of_the_stack = 1;
+    let addr = &something_around_the_top_of_the_stack as *const int;
+    let my_stack_top = addr as uint;
+
+    // FIXME #11359 we just assume that this thread has a stack of a
+    // certain size, and estimate that there's at most 20KB of stack
+    // frames above our current position.
+
+    let my_stack_bottom = my_stack_top + 20000 - OS_DEFAULT_STACK_ESTIMATE;
+
+    let task = native::task::new((my_stack_bottom, my_stack_top), rt::thread::main_guard_page());
+    Local::put(task);
+}
+
 #[no_mangle]
 pub extern "C" fn cef_shutdown() {
 }
 
 #[no_mangle]
 pub extern "C" fn cef_run_message_loop() {
-    native::start(0, 0 as *const *const u8, proc() {
-        GLOBAL_BROWSERS.get().map(|refcellbrowsers| {
-            let browsers = refcellbrowsers.borrow();
-            let mut num = browsers.len();
-            for active_browser in browsers.iter() {
-                *active_browser.downcast().window.borrow_mut() =
-                    Some(glfw_app::create_window());
-                *active_browser.downcast().servo_browser.borrow_mut() =
-                    Some(Browser::new((*active_browser.downcast()
-                                                      .window
-                                                      .borrow()).clone()));
-                if !active_browser.downcast().callback_executed.get() {
-                    browser_callback_after_created((*active_browser).clone());
-                }
-            }
-            while num > 0 {
-                for active_browser in browsers.iter()
-                                              .filter(|&active_browser| {
-                                                  active_browser.downcast()
-                                                                .servo_browser
-                                                                .borrow()
-                                                                .is_some()
-                                              }) {
-                    let ref mut browser = active_browser.downcast();
-                    let mut servobrowser = browser.servo_browser.borrow_mut().take().unwrap();
-                    if !servobrowser.handle_event(browser.window
-                                                         .borrow_mut()
-                                                         .as_ref()
-                                                         .unwrap()
-                                                         .wait_events()) {
-                        servobrowser.shutdown();
-                        num -= 1;
-                    }
-                }
-            }
-        });
-    });
+    let mut the_globals = globals.get();
+    let the_globals = the_globals.as_mut().unwrap();
+    match **the_globals {
+        OnScreenGlobals(ref window, ref browser) => {
+            while browser.borrow_mut().handle_event(window.borrow_mut().wait_events()) {}
+        }
+        OffScreenGlobals(ref window, ref browser) => {
+            while browser.borrow_mut().handle_event(window.borrow_mut().wait_events()) {}
+        }
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn cef_do_message_loop_work() {
+    send_window_event(IdleWindowEvent)
 }
 
 #[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,
+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 {
-    unsafe {
-        if args.is_null() {
-            println!("args must be passed");
-            return -1;
-        }
-        for i in range(0u, (*args).argc as uint) {
-             let u = (*args).argv.offset(i as int) as *const u8;
-             slice::raw::buf_as_slice(u, strlen(u as *const i8) as uint, |s| {
-                 if s.starts_with("--".as_bytes()) {
-                     if s.slice_from(2) == KWAITFORDEBUGGER.as_bytes() {
-                         //FIXME: this is NOT functionally equivalent to chromium!
+   -1
+}
+
+pub fn send_window_event(event: WindowEvent) {
+    message_queue.get().as_mut().unwrap().borrow_mut().push(event);
 
-                         //this should be a pause() call with an installed signal
-                         //handler callback, something which is impossible now in rust
-                     } else if s.slice_from(2) == KPROCESSTYPE.as_bytes() {
-                         //TODO: run other process now
-                     }
-                 }
-             });
+    let mut the_globals = globals.get();
+    let the_globals = match the_globals.as_mut() {
+        None => return,
+        Some(the_globals) => the_globals,
+    };
+    loop {
+        match **the_globals {
+            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.get()
+                                                       .as_mut()
+                                                       .unwrap()
+                                                       .borrow_mut()
+                                                       .pop() {
+                            None => return,
+                            Some(event) => event,
+                        };
+                        browser.handle_event(event);
+                    }
+                }
+            }
+            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.get()
+                                                       .as_mut()
+                                                       .unwrap()
+                                                       .borrow_mut()
+                                                       .pop() {
+                            None => return,
+                            Some(event) => event,
+                        };
+                        browser.handle_event(event);
+                    }
+                }
+            }
         }
     }
-   //process type not specified, must be browser process (NOOP)
-   -1
+}
+
+macro_rules! browser_method_delegate(
+    ( $( fn $method:ident ( ) -> $return_type:ty ; )* ) => (
+        $(
+            pub fn $method() -> $return_type {
+                let mut the_globals = globals.get();
+                let the_globals = match the_globals.as_mut() {
+                    None => panic!("{}: no globals created", stringify!($method)),
+                    Some(the_globals) => the_globals,
+                };
+                match **the_globals {
+                    OnScreenGlobals(_, ref browser) => browser.borrow_mut().$method(),
+                    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
     }
 }
 
 #[no_mangle]
+pub extern "C" fn cef_log(_file: *const c_char,
+                          _line: c_int,
+                          _severity: c_int,
+                          message: *const c_char) {
+    unsafe {
+        println!("{}", CString::new(message, false))
+    }
+}
+
+#[no_mangle]
 pub extern "C" fn cef_get_min_log_level() -> c_int {
     0
 }
 
new file mode 100644
--- /dev/null
+++ b/servo/ports/cef/frame.rs
@@ -0,0 +1,45 @@
+/* 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 types::{cef_string_t, cef_string_userfree_t};
+
+use core;
+use compositing::windowing::LoadUrlWindowEvent;
+use std::cell::RefCell;
+
+pub struct ServoCefFrame {
+    pub title_visitor: RefCell<Option<CefStringVisitor>>,
+    pub url: RefCell<String>,
+}
+
+impl ServoCefFrame {
+    pub fn new() -> ServoCefFrame {
+        ServoCefFrame {
+            title_visitor: RefCell::new(None),
+            url: RefCell::new(String::new()),
+        }
+    }
+}
+
+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(LoadUrlWindowEvent(String::from_utf16(url).unwrap()));
+        }
+        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();
+        }
+    }
+}
+
--- a/servo/ports/cef/lib.rs
+++ b/servo/ports/cef/lib.rs
@@ -9,67 +9,77 @@
 
 #![feature(phase)]
 #[phase(plugin, link)]
 extern crate log;
 #[phase(plugin)]
 extern crate "plugins" as servo_plugins;
 
 extern crate servo;
+extern crate compositing;
 
 extern crate azure;
-extern crate compositing;
 extern crate geom;
 extern crate gfx;
+extern crate gleam;
 extern crate glfw;
 extern crate glfw_app;
 extern crate js;
 extern crate layers;
 extern crate png;
 extern crate script;
 
 extern crate "net" as servo_net;
 extern crate "msg" as servo_msg;
 extern crate "util" as servo_util;
 extern crate style;
 extern crate stb_image;
 
 extern crate green;
 extern crate native;
+extern crate rustrt;
 extern crate libc;
 extern crate "url" as std_url;
 
 #[cfg(target_os="macos")]
+extern crate cgl;
+#[cfg(target_os="macos")]
+extern crate cocoa;
+#[cfg(target_os="macos")]
 extern crate core_graphics;
 #[cfg(target_os="macos")]
 extern crate core_text;
 
 // Must come first.
 pub mod macros;
 
 pub mod browser;
+pub mod browser_host;
 pub mod command_line;
 pub mod cookie;
 pub mod core;
 pub mod drag_data;
 pub mod eutil;
+pub mod frame;
 pub mod interfaces;
 pub mod print_settings;
 pub mod process_message;
+pub mod render_handler;
 pub mod request;
 pub mod request_context;
 pub mod response;
 pub mod stream;
 pub mod string;
 pub mod string_list;
 pub mod string_map;
 pub mod string_multimap;
 pub mod stubs;
 pub mod switches;
 pub mod task;
 pub mod types;
 pub mod urlrequest;
+pub mod v8;
 pub mod values;
-pub mod v8;
+pub mod window;
 pub mod wrappers;
 pub mod xml_reader;
 pub mod zip_reader;
 
new file mode 100644
--- /dev/null
+++ b/servo/ports/cef/render_handler.rs
@@ -0,0 +1,19 @@
+/* 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 interfaces::{CefBrowser, CefRenderHandler};
+use types::PET_VIEW;
+
+use std::ptr;
+
+pub trait CefRenderHandlerExtensions {
+    fn paint(&self, browser: CefBrowser);
+}
+
+impl CefRenderHandlerExtensions for CefRenderHandler {
+    fn paint(&self, browser: CefBrowser) {
+        self.on_paint(browser, PET_VIEW, 0, ptr::null(), &mut (), 0, 0)
+    }
+}
+
--- a/servo/ports/cef/string.rs
+++ b/servo/ports/cef/string.rs
@@ -1,15 +1,15 @@
 /* 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::slice_to_str;
-use libc::{mod, size_t, c_int, c_ushort,c_void};
+use libc::{mod, size_t, c_int, c_ushort, c_void};
 use libc::types::os::arch::c95::wchar_t;
 use std::char;
 use std::mem;
 use std::ptr;
 use std::slice;
 use std::string;
 use types::{cef_string_utf16_t, cef_string_utf8_t, cef_string_wide_t};
 use types::{cef_string_userfree_utf16_t, cef_string_userfree_utf8_t, cef_string_userfree_wide_t};
@@ -49,17 +49,17 @@ pub extern "C" fn cef_string_userfree_wi
 pub extern "C" fn cef_string_userfree_utf8_free(cs: *mut cef_string_userfree_utf8_t) {
     unsafe {
         cef_string_utf8_clear(cs);
         libc::free(cs as *mut c_void)
     }
 }
 
 #[no_mangle]
-pub extern "C" fn cef_string_userfree_utf16_free(cs: *mut cef_string_userfree_utf16_t) {
+pub extern "C" fn cef_string_userfree_utf16_free(cs: cef_string_userfree_utf16_t) {
     unsafe {
         cef_string_utf16_clear(cs);
         libc::free(cs as *mut c_void)
     }
 }
 
 #[no_mangle]
 pub extern "C" fn cef_string_utf8_clear(cs: *mut cef_string_utf8_t) {
@@ -264,29 +264,16 @@ pub extern "C" fn cef_string_utf8_to_wid
          return cef_string_utf8_to_utf16(src, src_len, output as *mut cef_string_utf16_t);
     }
     slice_to_str(src, src_len as uint, |result| {
         let conv = result.chars().map(|c| c as u32).collect::<Vec<u32>>();
         cef_string_wide_set(conv.as_ptr() as *const wchar_t, conv.len() as size_t, output, 1)
     })
 }
 
-/// Wraps a borrowed reference to a UTF-16 CEF string.
-pub struct CefStringRef<'a> {
-    pub c_object: &'a *const cef_string_utf16_t,
-}
-
-impl<'a> CefStringRef<'a> {
-    pub unsafe fn from_c_object(c_object: &'a *const cef_string_utf16_t) -> CefStringRef<'a> {
-        CefStringRef {
-            c_object: c_object,
-        }
-    }
-}
-
 #[no_mangle]
 pub extern "C" fn cef_string_wide_to_utf8(src: *const wchar_t, src_len: size_t, output: *mut cef_string_utf8_t) -> c_int {
     if mem::size_of::<wchar_t>() == mem::size_of::<u16>() {
          return cef_string_utf16_to_utf8(src as *const u16, src_len, output);
     }
     unsafe {
        slice::raw::buf_as_slice(src, src_len as uint, |ustr| {
             let conv = ustr.iter().map(|&c| char::from_u32(c as u32).unwrap_or('\uFFFD')).collect::<String>();
@@ -316,8 +303,21 @@ pub extern "C" fn cef_string_ascii_to_wi
 pub fn empty_utf16_string() -> cef_string_utf16_t {
     cef_string_utf16_t {
         str: ptr::null_mut(),
         length: 0,
         dtor: None,
     }
 }
 
+pub fn string_to_userfree_string(string: cef_string_utf16_t) -> cef_string_userfree_utf16_t {
+    unsafe {
+        let allocation: cef_string_userfree_utf16_t =
+            mem::transmute(libc::malloc(mem::size_of::<cef_string_utf16_t>() as size_t));
+        ptr::write(allocation, string);
+        allocation
+    }
+}
+
+pub fn empty_utf16_userfree_string() -> cef_string_userfree_utf16_t {
+    string_to_userfree_string(empty_utf16_string())
+}
+
--- a/servo/ports/cef/string_multimap.rs
+++ b/servo/ports/cef/string_multimap.rs
@@ -3,17 +3,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 use eutil::slice_to_str;
 use libc::{c_int};
 use std::collections::TreeMap;
 use std::iter::AdditiveIterator;
 use std::mem;
 use std::string::String;
-use string::{cef_string_userfree_utf16_alloc,cef_string_userfree_utf16_free,cef_string_utf16_set};
+use string::{cef_string_userfree_utf16_alloc, cef_string_userfree_utf16_free};
+use string::{cef_string_utf16_set};
 use types::{cef_string_multimap_t,cef_string_t};
 
 fn string_multimap_to_treemap(smm: *mut cef_string_multimap_t) -> *mut TreeMap<String, Vec<*mut cef_string_t>> {
     smm as *mut TreeMap<String, Vec<*mut cef_string_t>>
 }
 
 //cef_string_multimap
 
--- a/servo/ports/cef/types.rs
+++ b/servo/ports/cef/types.rs
@@ -20,17 +20,16 @@ pub enum cef_event_handle_t {}
 //these all need to be done...
 pub enum cef_binary_value_t {}
 pub enum cef_dictionary_value_t {}
 pub enum cef_request_t {}
 pub enum cef_response_t {}
 pub enum cef_urlrequest_client_t {}
 pub enum cef_domnode_t {}
 pub enum cef_load_handler_t {}
-pub enum cef_request_context_t {}
 pub enum cef_browser_settings_t {}
 pub enum cef_v8context_t {}
 pub enum cef_v8exception_t {}
 pub enum cef_v8stack_trace_t {}
 pub enum cef_context_menu_handler_t {}
 pub enum cef_dialog_handler_t {}
 pub enum cef_download_handler_t {}
 pub enum cef_drag_handler_t {}
@@ -63,28 +62,28 @@ pub enum cef_v8exception_val {}
 pub type cef_v8exception = *mut cef_v8exception_val;
 pub enum cef_v8stack_trace_val {}
 pub type cef_v8stack_trace = *mut cef_v8stack_trace_val;
 
 pub type CefBrowserSettings = cef_browser_settings_t;
 pub type CefScreenInfo = cef_screen_info_t;
 
 pub type cef_string_t = cef_string_utf16; //FIXME: this is #defined...
-pub type cef_string_userfree_t = cef_string_t; //FIXME: this is #defined...
+pub type cef_string_userfree_t = *mut cef_string_t; //FIXME: this is #defined...
 
 pub struct cef_string_utf8 {
     pub str: *mut u8,
     pub length: size_t,
     pub dtor: Option<extern "C" fn(str: *mut u8)>,
 }
 pub type cef_string_utf8_t = cef_string_utf8;
 pub type cef_string_userfree_utf8_t = cef_string_utf8;
 
 pub type cef_string_utf16_t = cef_string_utf16;
-pub type cef_string_userfree_utf16_t = cef_string_utf16;
+pub type cef_string_userfree_utf16_t = *mut cef_string_utf16;
 pub struct cef_string_utf16 {
     pub str: *mut c_ushort,
     pub length: size_t,
     pub dtor: Option<extern "C" fn(str: *mut c_ushort)>,
 }
 
 pub type cef_string_wide_t = cef_string_wide;
 pub type cef_string_userfree_wide_t = cef_string_wide;
@@ -97,212 +96,212 @@ pub struct cef_string_wide {
 pub type cef_main_args_t = cef_main_args;
 pub struct cef_main_args {
   pub argc: c_int,
   pub argv: *const *const u8
 }
 
 pub type cef_color_t = c_uint;
 
-///
+//
 // Existing thread IDs.
-///
+//
 pub enum cef_thread_id_t {
 // BROWSER PROCESS THREADS -- Only available in the browser process.
 
-  ///
+  //
   // The main thread in the browser. This will be the same as the main
   // application thread if CefInitialize() is called with a
   // CefSettings.multi_threaded_message_loop value of false.
-  ///
+  //
   TID_UI,
 
-  ///
+  //
   // Used to interact with the database.
-  ///
+  //
   TID_DB,
 
-  ///
+  //
   // Used to interact with the file system.
-  ///
+  //
   TID_FILE,
 
-  ///
+  //
   // Used for file system operations that block user interactions.
   // Responsiveness of this thread affects users.
-  ///
+  //
   TID_FILE_USER_BLOCKING,
 
-  ///
+  //
   // Used to launch and terminate browser processes.
-  ///
+  //
   TID_PROCESS_LAUNCHER,
 
-  ///
+  //
   // Used to handle slow HTTP cache operations.
-  ///
+  //
   TID_CACHE,
 
-  ///
+  //
   // Used to process IPC and network messages.
-  ///
+  //
   TID_IO,
 
 // RENDER PROCESS THREADS -- Only available in the render process.
 
-  ///
+  //
   // The main thread in the renderer. Used for all WebKit and V8 interaction.
-  ///
+  //
   TID_RENDERER,
 }
 
-///
+//
 // Navigation types.
-///
+//
 pub enum cef_navigation_type_t {
   NAVIGATION_LINK_CLICKED = 0,
   NAVIGATION_FORM_SUBMITTED,
   NAVIGATION_BACK_FORWARD,
   NAVIGATION_RELOAD,
   NAVIGATION_FORM_RESUBMITTED,
   NAVIGATION_OTHER,
 }
 
-///
+//
 // Mouse button types.
-///
+//
 pub enum cef_mouse_button_type_t {
   MBT_LEFT   = 0,
   MBT_MIDDLE,
   MBT_RIGHT,
 }
 
-///
+//
 // Structure representing mouse event information.
-///
+//
 pub type cef_mouse_event_t = cef_mouse_event;
 pub type CefMouseEvent = cef_mouse_event_t;
 pub struct cef_mouse_event {
-    ///
+    //
     // X coordinate relative to the left side of the view.
-    ///
+    //
     pub x: c_int,
 
-    ///
+    //
     // Y coordinate relative to the top side of the view.
-    ///
+    //
     pub y: c_int,
 
-    ///
+    //
     // Bit flags describing any pressed modifier keys. See
     // cef_event_flags_t for values.
-    ///
+    //
     pub modifiers: c_uint,
 }
 
-///
+//
 // Post data elements may represent either bytes or files.
-///
+//
 pub enum cef_postdataelement_type_t {
   PDE_TYPE_EMPTY  = 0,
   PDE_TYPE_BYTES,
   PDE_TYPE_FILE,
 }
 
-///
+//
 // Flags used to customize the behavior of CefURLRequest.
-///
+//
 pub enum cef_urlrequest_flags_t {
-  ///
+  //
   // Default behavior.
-  ///
+  //
   UR_FLAG_NONE                      = 0,
 
-  ///
+  //
   // If set the cache will be skipped when handling the request.
-  ///
+  //
   UR_FLAG_SKIP_CACHE                = 1 << 0,
 
-  ///
+  //
   // If set user name, password, and cookies may be sent with the request.
-  ///
+  //
   UR_FLAG_ALLOW_CACHED_CREDENTIALS  = 1 << 1,
 
-  ///
+  //
   // If set cookies may be sent with the request and saved from the response.
   // UR_FLAG_ALLOW_CACHED_CREDENTIALS must also be set.
-  ///
+  //
   UR_FLAG_ALLOW_COOKIES             = 1 << 2,
 
-  ///
+  //
   // If set upload progress events will be generated when a request has a body.
-  ///
+  //
   UR_FLAG_REPORT_UPLOAD_PROGRESS    = 1 << 3,
 
-  ///
+  //
   // If set load timing info will be collected for the request.
-  ///
+  //
   UR_FLAG_REPORT_LOAD_TIMING        = 1 << 4,
 
-  ///
+  //
   // If set the headers sent and received for the request will be recorded.
-  ///
+  //
   UR_FLAG_REPORT_RAW_HEADERS        = 1 << 5,
 
-  ///
+  //
   // If set the CefURLRequestClient::OnDownloadData method will not be called.
-  ///
+  //
   UR_FLAG_NO_DOWNLOAD_DATA          = 1 << 6,
 
-  ///
+  //
   // If set 5XX redirect errors will be propagated to the observer instead of
   // automatically re-tried. This currently only applies for requests
   // originated in the browser process.
-  ///
+  //
   UR_FLAG_NO_RETRY_ON_5XX           = 1 << 7,
 }
 
 
-///
+//
 // Flags that represent CefURLRequest status.
-///
+//
 pub enum cef_urlrequest_status_t {
-  ///
+  //
   // Unknown status.
-  ///
+  //
   UR_UNKNOWN = 0,
 
-  ///
+  //
   // Request succeeded.
-  ///
+  //
   UR_SUCCESS,
 
-  ///
+  //
   // An IO request is pending, and the caller will be informed when it is
   // completed.
-  ///
+  //
   UR_IO_PENDING,
 
-  ///
+  //
   // Request was canceled programatically.
-  ///
+  //
   UR_CANCELED,
 
-  ///
+  //
   // Request failed for some reason.
-  ///
+  //
   UR_FAILED,
 }
 
 
 
-///
+//
 // Supported error code values. See net\base\net_error_list.h for complete
 // descriptions of the error codes.
-///
+//
 pub enum cef_errorcode_t {
   ERR_NONE = 0,
   ERR_FAILED = -2,
   ERR_ABORTED = -3,
   ERR_INVALID_ARGUMENT = -4,
   ERR_INVALID_HANDLE = -5,
   ERR_FILE_NOT_FOUND = -6,
   ERR_TIMED_OUT = -7,
@@ -346,94 +345,94 @@ pub enum cef_errorcode_t {
   ERR_UNEXPECTED_PROXY_AUTH = -323,
   ERR_EMPTY_RESPONSE = -324,
   ERR_RESPONSE_HEADERS_TOO_BIG = -325,
   ERR_CACHE_MISS = -400,
   ERR_INSECURE_RESPONSE = -501,
 }
 
 
-///
+//
 // Key event types.
-///
+//
 pub enum cef_key_event_type_t {
   KEYEVENT_RAWKEYDOWN = 0,
   KEYEVENT_KEYDOWN,
   KEYEVENT_KEYUP,
   KEYEVENT_CHAR
 }
 
-///
+//
 // Structure representing keyboard event information.
-///
+//
 pub type cef_key_event_t = cef_key_event;
 pub struct cef_key_event {
-  ///
+  //
   // The type of keyboard event.
-  ///
+  //
   pub t: cef_key_event_type_t,
 
-  ///
+  //
   // Bit flags describing any pressed modifier keys. See
   // cef_event_flags_t for values.
-  ///
+  //
   pub modifiers: c_uint,
 
-  ///
+  //
   // The Windows key code for the key event. This value is used by the DOM
   // specification. Sometimes it comes directly from the event (i.e. on
   // Windows) and sometimes it's determined using a mapping function. See
   // WebCore/platform/chromium/KeyboardCodes.h for the list of values.
-  ///
+  //
   pub windows_key_code: c_int,
 
-  ///
+  //
   // The actual key code genenerated by the platform.
-  ///
+  //
   pub native_key_code: c_int,
 
-  ///
+  //
   // Indicates whether the event is considered a "system key" event (see
   // http://msdn.microsoft.com/en-us/library/ms646286(VS.85).aspx for details).
   // This value will always be false on non-Windows platforms.
-  ///
+  //
   pub is_system_key: c_int,
 
-  ///
+  //
   // The character generated by the keystroke.
-  ///
+  //
   pub character: c_ushort, //FIXME: can be wchar_t also?
 
-  ///
+  //
   // Same as |character| but unmodified by any concurrently-held modifiers
   // (except shift). This is useful for working out shortcut keys.
-  ///
+  //
   pub unmodified_character: c_ushort, //FIXME: can be wchar_t also?
 
-  ///
+  //
   // True if the focus is currently on an editable field on the page. This is
   // useful for determining if standard key events should be intercepted.
-  ///
+  //
   pub focus_on_editable_field: c_int,
 }
 
 pub type CefKeyEvent = cef_key_event_t;
 
-///
+//
 // Structure representing a point.
-///
+//
 pub type cef_point_t = cef_point;
 pub struct cef_point {
   pub x: c_int,
   pub y: c_int,
 }
 
-///
+//
 // Structure representing a rectangle.
-///
+//
 pub struct cef_rect {
   pub x: c_int,
   pub y: c_int,
   pub width: c_int,
   pub height: c_int,
 }
 
 impl cef_rect {
@@ -442,295 +441,299 @@ impl cef_rect {
             x: 0,
             y: 0,
             width: 0,
             height: 0,
         }
     }
 }
 
-///
+//
 // Paint element types.
-///
+//
 pub enum cef_paint_element_type_t {
   PET_VIEW  = 0,
   PET_POPUP,
 }
 
-///
+//
 // DOM document types.
-///
+//
 pub enum cef_dom_document_type_t {
   DOM_DOCUMENT_TYPE_UNKNOWN = 0,
   DOM_DOCUMENT_TYPE_HTML,
   DOM_DOCUMENT_TYPE_XHTML,
   DOM_DOCUMENT_TYPE_PLUGIN,
 }
 
-///
+//
 // Supported file dialog modes.
-///
+//
 pub enum cef_file_dialog_mode_t {
-  ///
+  //
   // Requires that the file exists before allowing the user to pick it.
-  ///
+  //
   FILE_DIALOG_OPEN = 0,
 
-  ///
+  //
   // Like Open, but allows picking multiple files to open.
-  ///
+  //
   FILE_DIALOG_OPEN_MULTIPLE,
 
-  ///
+  //
   // Allows picking a nonexistent file, and prompts to overwrite if the file
   // already exists.
-  ///
+  //
   FILE_DIALOG_SAVE,
 }
 
-///
+//
 // Supported value types.
-///
+//
 pub enum cef_value_type_t {
   VTYPE_INVALID = 0,
   VTYPE_NULL,
   VTYPE_BOOL,
   VTYPE_INT,
   VTYPE_DOUBLE,
   VTYPE_STRING,
   VTYPE_BINARY,
   VTYPE_DICTIONARY,
   VTYPE_LIST,
 }
 
 pub type CefValueType = cef_value_type_t;
 
-///
+//
 // Existing process IDs.
-///
+//
 pub enum cef_process_id_t {
-  ///
+  //
   // Browser process.
-  ///
+  //
   PID_BROWSER,
-  ///
+  //
   // Renderer process.
-  ///
+  //
   PID_RENDERER,
 }
 pub type CefProcessId = cef_process_id_t;
 
-///
+//
 // Log severity levels.
-///
+//
 pub enum cef_log_severity_t {
-  ///
+  //
   // Default logging (currently INFO logging).
-  ///
+  //
   LOGSEVERITY_DEFAULT,
 
-  ///
+  //
   // Verbose logging.
-  ///
+  //
   LOGSEVERITY_VERBOSE,
 
-  ///
+  //
   // INFO logging.
-  ///
+  //
   LOGSEVERITY_INFO,
 
-  ///
+  //
   // WARNING logging.
-  ///
+  //
   LOGSEVERITY_WARNING,
 
-  ///
+  //
   // ERROR logging.
-  ///
+  //
   LOGSEVERITY_ERROR,
 
-  ///
+  //
   // ERROR_REPORT logging.
-  ///
+  //
   LOGSEVERITY_ERROR_REPORT,
 
-  ///
+  //
   // Completely disable logging.
-  ///
+  //
   LOGSEVERITY_DISABLE = 99
 }
 
 
-///
+//
 // Initialization settings. Specify NULL or 0 to get the recommended default
 // values. Many of these and other settings can also configured using command-
 // line switches.
-///
+//
 pub type cef_settings_t = cef_settings;
+
+#[repr(C)]
 pub struct cef_settings {
-  ///
+  //
   // Size of this structure.
-  ///
+  //
   pub size: size_t,
 
-  ///
+  //
   // Set to true (1) to use a single process for the browser and renderer. This
   // run mode is not officially supported by Chromium and is less stable than
   // the multi-process default. Also configurable using the "single-process"
   // command-line switch.
-  ///
+  //
   pub single_process: c_int,
 
-  ///
+  //
   // Set to true (1) to disable the sandbox for sub-processes. See
   // cef_sandbox_win.h for requirements to enable the sandbox on Windows. Also
   // configurable using the "no-sandbox" command-line switch.
-  ///
+  //
   pub no_sandbox: c_int,
 
-  ///
+  //
   // The path to a separate executable that will be launched for sub-processes.
   // By default the browser process executable is used. See the comments on
   // CefExecuteProcess() for details. Also configurable using the
   // "browser-subprocess-path" command-line switch.
-  ///
+  //
   pub browser_subprocess_path: cef_string_t,
 
-  ///
+  //
   // Set to true (1) to have the browser process message loop run in a separate
   // thread. If false (0) than the CefDoMessageLoopWork() function must be
   // called from your application message loop.
-  ///
+  //
   pub multi_threaded_message_loop: c_int,
 
-  ///
+
+  //
+  // Set to true (1) to enable windowless (off-screen) rendering support. Do not
+  // enable this value if the application does not use windowless rendering as
+  // it may reduce rendering performance on some systems.
+  //
+  pub windowless_rendering_enabled: c_int,
+
+  //
   // Set to true (1) to disable configuration of browser process features using
   // standard CEF and Chromium command-line arguments. Configuration can still
   // be specified using CEF data structures or via the
   // CefApp::OnBeforeCommandLineProcessing() method.
-  ///
+  //
   pub command_line_args_disabled: c_int,
 
-  ///
+  //
   // The location where cache data will be stored on disk. If empty an in-memory
   // cache will be used for some features and a temporary disk cache for others.
   // HTML5 databases such as localStorage will only persist across sessions if a
   // cache path is specified.
-  ///
+  //
   pub cache_path: cef_string_t,
 
-  ///
+  //
   // To persist session cookies (cookies without an expiry date or validity
   // interval) by default when using the global cookie manager set this value to
   // true. Session cookies are generally intended to be transient and most Web
   // browsers do not persist them. A |cache_path| value must also be specified to
   // enable this feature. Also configurable using the "persist-session-cookies"
   // command-line switch.
-  ///
+  //
   pub persist_session_cookies: c_int,
 
-  ///
+  //
   // Value that will be returned as the User-Agent HTTP header. If empty the
   // default User-Agent string will be used. Also configurable using the
   // "user-agent" command-line switch.
-  ///
+  //
   pub user_agent: cef_string_t,
 
-  ///
+  //
   // Value that will be inserted as the product portion of the default
   // User-Agent string. If empty the Chromium product version will be used. If
   // |userAgent| is specified this value will be ignored. Also configurable
   // using the "product-version" command-line switch.
-  ///
+  //
   pub product_version: cef_string_t,
 
-  ///
+  //
   // The locale string that will be passed to WebKit. If empty the default
   // locale of "en-US" will be used. This value is ignored on Linux where locale
   // is determined using environment variable parsing with the precedence order:
   // LANGUAGE, LC_ALL, LC_MESSAGES and LANG. Also configurable using the "lang"
   // command-line switch.
-  ///
+  //
   pub locale: cef_string_t,
 
-  ///
+  //
   // The directory and file name to use for the debug log. If empty, the
   // default name of "debug.log" will be used and the file will be written
   // to the application directory. Also configurable using the "log-file"
   // command-line switch.
-  ///
+  //
   pub log_file: cef_string_t,
 
-  ///
+  //
   // The log severity. Only messages of this severity level or higher will be
   // logged. Also configurable using the "log-severity" command-line switch with
   // a value of "verbose", "info", "warning", "error", "error-report" or
   // "disable".
-  ///
+  //
   pub log_severity: cef_log_severity_t,
 
-  ///
-  // Enable DCHECK in release mode to ease debugging. Also configurable using the
-  // "enable-release-dcheck" command-line switch.
-  ///
-  pub release_dcheck_enabled: c_int,
-
-  ///
+  //
   // Custom flags that will be used when initializing the V8 JavaScript engine.
   // The consequences of using custom flags may not be well tested. Also
   // configurable using the "js-flags" command-line switch.
-  ///
+  //
   pub javascript_flags: cef_string_t,
 
-  ///
+  //
   // The fully qualified path for the resources directory. If this value is
   // empty the cef.pak and/or devtools_resources.pak files must be located in
   // the module directory on Windows/Linux or the app bundle Resources directory
   // on Mac OS X. Also configurable using the "resources-dir-path" command-line
   // switch.
-  ///
+  //
   pub resources_dir_path: cef_string_t,
 
-  ///
+  //
   // The fully qualified path for the locales directory. If this value is empty
   // the locales directory must be located in the module directory. This value
   // is ignored on Mac OS X where pack files are always loaded from the app
   // bundle Resources directory. Also configurable using the "locales-dir-path"
   // command-line switch.
-  ///
+  //
   pub locales_dir_path: cef_string_t,
 
-  ///
+  //
   // Set to true (1) to disable loading of pack files for resources and locales.
   // A resource bundle handler must be provided for the browser and render
   // processes via CefApp::GetResourceBundleHandler() if loading of pack files
   // is disabled. Also configurable using the "disable-pack-loading" command-
   // line switch.
-  ///
+  //
   pub pack_loading_disabled: c_int,
 
-  ///
+  //
   // Set to a value between 1024 and 65535 to enable remote debugging on the
   // specified port. For example, if 8080 is specified the remote debugging URL
   // will be http://localhost:8080. CEF can be remotely debugged from any CEF or
   // Chrome browser window. Also configurable using the "remote-debugging-port"
   // command-line switch.
-  ///
+  //
   pub remote_debugging_port: c_int,
 
-  ///
+  //
   // The number of stack trace frames to capture for uncaught exceptions.
   // Specify a positive value to enable the CefV8ContextHandler::
   // OnUncaughtException() callback. Specify 0 (default value) and
   // OnUncaughtException() will not be called. Also configurable using the
   // "uncaught-exception-stack-size" command-line switch.
-  ///
+  //
   pub uncaught_exception_stack_size: c_int,
 
-  ///
+  //
   // By default CEF V8 references will be invalidated (the IsValid() method will
   // return false) after the owning context has been released. This reduces the
   // need for external record keeping and avoids crashes due to the use of V8
   // references after the associated context has been released.
   //
   // CEF currently offers two context safety implementations with different
   // performance characteristics. The default implementation (value of 0) uses a
   // map of hash values and should provide better performance in situations with
@@ -739,221 +742,272 @@ pub struct cef_settings {
   // in situations with a large number of contexts.
   //
   // If you need better performance in the creation of V8 references and you
   // plan to manually track context lifespan you can disable context safety by
   // specifying a value of -1.
   //
   // Also configurable using the "context-safety-implementation" command-line
   // switch.
-  ///
+  //
   pub context_safety_implementation: c_int,
 
-  ///
+  //
   // Set to true (1) to ignore errors related to invalid SSL certificates.
   // Enabling this setting can lead to potential security vulnerabilities like
   // "man in the middle" attacks. Applications that load content from the
   // internet should not enable this setting. Also configurable using the
   // "ignore-certificate-errors" command-line switch.
-  ///
+  //
   pub ignore_certificate_errors: c_int,
 
-  ///
+  //
   // Opaque background color used for accelerated content. By default the
   // background color will be white. Only the RGB compontents of the specified
   // value will be used. The alpha component must greater than 0 to enable use
   // of the background color but will be otherwise ignored.
-  ///
+  //
   pub background_color: cef_color_t,
+
+  //
+  // Determines how many rendering threads are used.
+  //
+  pub rendering_threads: c_int,
 }
 
-///
+//
 // Structure defining the reference count implementation functions. All
 // framework structures must include the cef_base_t structure first.
-///
+//
 pub type cef_base_t = cef_base;
 pub struct cef_base {
-  ///
+  //
   // Size of the data structure.
-  ///
+  //
   pub size: size_t,
 
-  ///
+  //
   // Increment the reference count.
-  ///
+  //
   pub add_ref: Option<extern "C" fn(base: *mut cef_base) -> c_int>,
 
-  ///
+  //
   // Decrement the reference count.  Delete this object when no references
   // remain.
-  ///
+  //
   pub release: Option<extern "C" fn(base: *mut cef_base) -> c_int>,
 
-  ///
+  //
   // Returns the current number of references.
-  ///
+  //
   pub get_refct: Option<extern "C" fn(base: *mut cef_base) -> c_int>,
 }
 
 pub type CefBase = *mut cef_base_t;
 
-///
+//
 // Class representing window information.
-///
+//
 pub type cef_window_info_t = cef_window_info;
+
+#[cfg(target_os="linux")]
 pub struct cef_window_info {
   pub x: c_uint,
   pub y: c_uint,
   pub width: c_uint,
   pub height: c_uint,
 
-  ///
+  //
   // Pointer for the parent window.
-  ///
+  //
   pub parent_window: cef_window_handle_t,
 
-  ///
+  //
   // Set to true (1) to create the browser using windowless (off-screen)
   // rendering. No window will be created for the browser and all rendering will
   // occur via the CefRenderHandler interface. The |parent_window| value will be
   // used to identify monitor info and to act as the parent window for dialogs,
   // context menus, etc. If |parent_window| is not provided then the main screen
   // monitor will be used and some functionality that requires a parent window
   // may not function correctly. In order to create windowless browsers the
   // CefSettings.windowless_rendering_enabled value must be set to true.
-  ///
+  //
   pub windowless_rendering_enabled: c_int,
 
-  ///
+  //
   // Set to true (1) to enable transparent painting in combination with
   // windowless rendering. When this value is true a transparent background
   // color will be used (RGBA=0x00000000). When this value is false the
   // background will be white and opaque.
-  ///
+  //
   pub transparent_painting_enabled: c_int,
 
-  ///
+  //
   // Pointer for the new browser window. Only used with windowed rendering.
-  ///
+  //
+  pub window: cef_window_handle_t
+}
+
+#[cfg(target_os="macos")]
+pub struct cef_window_info {
+  pub window_name: cef_string_t,
+  pub x: c_uint,
+  pub y: c_uint,
+  pub width: c_uint,
+  pub height: c_uint,
+
+  //
+  // Set to true (1) to create the view initially hidden.
+  //
+  pub hidden: c_int,
+
+  //
+  // Pointer for the parent window.
+  //
+  pub parent_window: cef_window_handle_t,
+
+  //
+  // Set to true (1) to create the browser using windowless (off-screen)
+  // rendering. No window will be created for the browser and all rendering will
+  // occur via the CefRenderHandler interface. The |parent_window| value will be
+  // used to identify monitor info and to act as the parent window for dialogs,
+  // context menus, etc. If |parent_window| is not provided then the main screen
+  // monitor will be used and some functionality that requires a parent window
+  // may not function correctly. In order to create windowless browsers the
+  // CefSettings.windowless_rendering_enabled value must be set to true.
+  //
+  pub windowless_rendering_enabled: c_int,
+
+  //
+  // Set to true (1) to enable transparent painting in combination with
+  // windowless rendering. When this value is true a transparent background
+  // color will be used (RGBA=0x00000000). When this value is false the
+  // background will be white and opaque.
+  //
+  pub transparent_painting_enabled: c_int,
+
+  //
+  // Pointer for the new browser window. Only used with windowed rendering.
+  //
   pub window: cef_window_handle_t
 }
 
 pub type CefWindowInfo = cef_window_info_t;
 
-///
+//
 // Supported menu item types.
-///
+//
 pub enum cef_menu_item_type_t {
   MENUITEMTYPE_NONE,
   MENUITEMTYPE_COMMAND,
   MENUITEMTYPE_CHECK,
   MENUITEMTYPE_RADIO,
   MENUITEMTYPE_SEPARATOR,
   MENUITEMTYPE_SUBMENU,
 }
 
-///
+//
 // Supported context menu type flags.
-///
+//
 pub enum cef_context_menu_type_flags_t {
-  ///
+  //
   // No node is selected.
-  ///
+  //
   CM_TYPEFLAG_NONE        = 0,
-  ///
+  //
   // The top page is selected.
-  ///
+  //
   CM_TYPEFLAG_PAGE        = 1 << 0,
-  ///
+  //
   // A subframe page is selected.
-  ///
+  //
   CM_TYPEFLAG_FRAME       = 1 << 1,
-  ///
+  //
   // A link is selected.
-  ///
+  //
   CM_TYPEFLAG_LINK        = 1 << 2,
-  ///
+  //
   // A media node is selected.
-  ///
+  //
   CM_TYPEFLAG_MEDIA       = 1 << 3,
-  ///
+  //
   // There is a textual or mixed selection that is selected.
-  ///
+  //
   CM_TYPEFLAG_SELECTION   = 1 << 4,
-  ///
+  //
   // An editable element is selected.
-  ///
+  //
   CM_TYPEFLAG_EDITABLE    = 1 << 5,
 }
 
-///
+//
 // Supported context menu media types.
-///
+//
 pub enum cef_context_menu_media_type_t {
-  ///
+  //
   // No special node is in context.
-  ///
+  //
   CM_MEDIATYPE_NONE,
-  ///
+  //
   // An image node is selected.
-  ///
+  //
   CM_MEDIATYPE_IMAGE,
-  ///
+  //
   // A video node is selected.
-  ///
+  //
   CM_MEDIATYPE_VIDEO,
-  ///
+  //
   // An audio node is selected.
-  ///
+  //
   CM_MEDIATYPE_AUDIO,
-  ///
+  //
   // A file node is selected.
-  ///
+  //
   CM_MEDIATYPE_FILE,
-  ///
+  //
   // A plugin node is selected.
-  ///
+  //
   CM_MEDIATYPE_PLUGIN,
 }
 
-///
+//
 // Supported context menu media state bit flags.
-///
+//
 pub enum cef_context_menu_media_state_flags_t {
   CM_MEDIAFLAG_NONE                  = 0,
   CM_MEDIAFLAG_ERROR                 = 1 << 0,
   CM_MEDIAFLAG_PAUSED                = 1 << 1,
   CM_MEDIAFLAG_MUTED                 = 1 << 2,
   CM_MEDIAFLAG_LOOP                  = 1 << 3,
   CM_MEDIAFLAG_CAN_SAVE              = 1 << 4,
   CM_MEDIAFLAG_HAS_AUDIO             = 1 << 5,
   CM_MEDIAFLAG_HAS_VIDEO             = 1 << 6,
   CM_MEDIAFLAG_CONTROL_ROOT_ELEMENT  = 1 << 7,
   CM_MEDIAFLAG_CAN_PRINT             = 1 << 8,
   CM_MEDIAFLAG_CAN_ROTATE            = 1 << 9,
 }
 
-///
+//
 // Supported context menu edit state bit flags.
-///
+//
 pub enum cef_context_menu_edit_state_flags_t {
   CM_EDITFLAG_NONE            = 0,
   CM_EDITFLAG_CAN_UNDO        = 1 << 0,
   CM_EDITFLAG_CAN_REDO        = 1 << 1,
   CM_EDITFLAG_CAN_CUT         = 1 << 2,
   CM_EDITFLAG_CAN_COPY        = 1 << 3,
   CM_EDITFLAG_CAN_PASTE       = 1 << 4,
   CM_EDITFLAG_CAN_DELETE      = 1 << 5,
   CM_EDITFLAG_CAN_SELECT_ALL  = 1 << 6,
   CM_EDITFLAG_CAN_TRANSLATE   = 1 << 7,
 }
 
-///
+//
 // Supported event bit flags.
-///
+//
 pub enum cef_event_flags_t {
   EVENTFLAG_NONE                = 0,
   EVENTFLAG_CAPS_LOCK_ON        = 1 << 0,
   EVENTFLAG_SHIFT_DOWN          = 1 << 1,
   EVENTFLAG_CONTROL_DOWN        = 1 << 2,
   EVENTFLAG_ALT_DOWN            = 1 << 3,
   EVENTFLAG_LEFT_MOUSE_BUTTON   = 1 << 4,
   EVENTFLAG_MIDDLE_MOUSE_BUTTON = 1 << 5,
@@ -961,116 +1015,116 @@ pub enum cef_event_flags_t {
   // Mac OS-X command key.
   EVENTFLAG_COMMAND_DOWN        = 1 << 7,
   EVENTFLAG_NUM_LOCK_ON         = 1 << 8,
   EVENTFLAG_IS_KEY_PAD          = 1 << 9,
   EVENTFLAG_IS_LEFT             = 1 << 10,
   EVENTFLAG_IS_RIGHT            = 1 << 11,
 }
 
-///
+//
 // Time information. Values should always be in UTC.
-///
+//
 #[repr(C)]
 pub struct _cef_time_t {
   year: c_int,          // Four digit year "2007"
   month: c_int,         // 1-based month (values 1 = January, etc.)
   day_of_week: c_int,   // 0-based day of week (0 = Sunday, etc.)
   day_of_month: c_int,  // 1-based day of month (1-31)
   hour: c_int,          // Hour within the current day (0-23)
   minute: c_int,        // Minute within the current hour (0-59)
   second: c_int,        // Second within the current minute (0-59 plus leap
                         //   seconds which may take it up to 60).
   millisecond: c_int,   // Milliseconds within the current second (0-999)
 }
 
 pub type cef_time_t = _cef_time_t;
 
-///
+//
 // DOM event processing phases.
-///
+//
 pub enum cef_dom_event_phase_t {
   DOM_EVENT_PHASE_UNKNOWN = 0,
   DOM_EVENT_PHASE_CAPTURING,
   DOM_EVENT_PHASE_AT_TARGET,
   DOM_EVENT_PHASE_BUBBLING,
 }
 
-///
+//
 // DOM node types.
-///
+//
 pub enum cef_dom_node_type_t {
   DOM_NODE_TYPE_UNSUPPORTED = 0,
   DOM_NODE_TYPE_ELEMENT,
   DOM_NODE_TYPE_ATTRIBUTE,
   DOM_NODE_TYPE_TEXT,
   DOM_NODE_TYPE_CDATA_SECTION,
   DOM_NODE_TYPE_PROCESSING_INSTRUCTIONS,
   DOM_NODE_TYPE_COMMENT,
   DOM_NODE_TYPE_DOCUMENT,
   DOM_NODE_TYPE_DOCUMENT_TYPE,
   DOM_NODE_TYPE_DOCUMENT_FRAGMENT,
 }
 
-///
+//
 // Focus sources.
-///
+//
 pub enum cef_focus_source_t {
-  ///
+  //
   // The source is explicit navigation via the API (LoadURL(), etc).
-  ///
+  //
   FOCUS_SOURCE_NAVIGATION = 0,
-  ///
+  //
   // The source is a system-generated focus event.
-  ///
+  //
   FOCUS_SOURCE_SYSTEM,
 }
 
-///
+//
 // Supported JavaScript dialog types.
-///
+//
 pub enum cef_jsdialog_type_t {
   JSDIALOGTYPE_ALERT = 0,
   JSDIALOGTYPE_CONFIRM,
   JSDIALOGTYPE_PROMPT,
 }
 
-///
+//
 // Structure representing a size.
-///
+//
 pub struct _cef_size_t {
   pub width: c_int,
   pub height: c_int,
 }
 
 pub type cef_size_t = _cef_size_t;
 
-///
+//
 // Structure representing a print job page range.
-///
+//
 pub struct _cef_page_range_t {
   pub from: c_int,
   pub to: c_int,
 }
 
 pub type cef_page_range_t = _cef_page_range_t;
 
-///
+//
 // Print job duplex mode values.
-///
+//
 pub enum cef_duplex_mode_t {
   DUPLEX_MODE_UNKNOWN = -1,
   DUPLEX_MODE_SIMPLEX,
   DUPLEX_MODE_LONG_EDGE,
   DUPLEX_MODE_SHORT_EDGE,
 }
 
-///
+//
 // Print job color mode values.
-///
+//
 pub enum cef_color_model_t {
   COLOR_MODEL_UNKNOWN,
   COLOR_MODEL_GRAY,
   COLOR_MODEL_COLOR,
   COLOR_MODEL_CMYK,
   COLOR_MODEL_CMY,
   COLOR_MODEL_KCMY,
   COLOR_MODEL_CMY_K,  // CMY_K represents CMY+K.
@@ -1085,396 +1139,396 @@ pub enum cef_color_model_t {
   COLOR_MODEL_HP_COLOR_BLACK,  // Used in HP color printer ppds.
   COLOR_MODEL_PRINTOUTMODE_NORMAL,  // Used in foomatic ppds.
   COLOR_MODEL_PRINTOUTMODE_NORMAL_GRAY,  // Used in foomatic ppds.
   COLOR_MODEL_PROCESSCOLORMODEL_CMYK,  // Used in canon printer ppds.
   COLOR_MODEL_PROCESSCOLORMODEL_GREYSCALE,  // Used in canon printer ppds.
   COLOR_MODEL_PROCESSCOLORMODEL_RGB,  // Used in canon printer ppds
 }
 
-///
+//
 // Resource type for a request.
-///
+//
 pub enum cef_resource_type_t {
-  ///
+  //
   // Top level page.
-  ///
+  //
   RT_MAIN_FRAME = 0,
 
-  ///
+  //
   // Frame or iframe.
-  ///
+  //
   RT_SUB_FRAME,
 
-  ///
+  //
   // CSS stylesheet.
-  ///
+  //
   RT_STYLESHEET,
 
-  ///
+  //
   // External script.
-  ///
+  //
   RT_SCRIPT,
 
-  ///
+  //
   // Image (jpg/gif/png/etc).
-  ///
+  //
   RT_IMAGE,
 
-  ///
+  //
   // Font.
-  ///
+  //
   RT_FONT_RESOURCE,
 
-  ///
+  //
   // Some other subresource. This is the default type if the actual type is
   // unknown.
-  ///
+  //
   RT_SUB_RESOURCE,
 
-  ///
+  //
   // Object (or embed) tag for a plugin, or a resource that a plugin requested.
-  ///
+  //
   RT_OBJECT,
 
-  ///
+  //
   // Media resource.
-  ///
+  //
   RT_MEDIA,
 
-  ///
+  //
   // Main resource of a dedicated worker.
-  ///
+  //
   RT_WORKER,
 
-  ///
+  //
   // Main resource of a shared worker.
-  ///
+  //
   RT_SHARED_WORKER,
 
-  ///
+  //
   // Explicitly requested prefetch.
-  ///
+  //
   RT_PREFETCH,
 
-  ///
+  //
   // Favicon.
-  ///
+  //
   RT_FAVICON,
 
-  ///
+  //
   // XMLHttpRequest.
-  ///
+  //
   RT_XHR,
 
-  ///
+  //
   // A request for a <ping>
-  ///
+  //
   RT_PING,
 
-  ///
+  //
   // Main resource of a service worker.
-  ///
+  //
   RT_SERVICE_WORKER,
 }
 
-///
+//
 // Transition type for a request. Made up of one source value and 0 or more
 // qualifiers.
-///
+//
 pub enum cef_transition_type_t {
-  ///
+  //
   // Source is a link click or the JavaScript window.open function. This is
   // also the default value for requests like sub-resource loads that are not
   // navigations.
-  ///
+  //
   TT_LINK = 0,
 
-  ///
+  //
   // Source is some other "explicit" navigation action such as creating a new
   // browser or using the LoadURL function. This is also the default value
   // for navigations where the actual type is unknown.
-  ///
+  //
   TT_EXPLICIT = 1,
 
-  ///
+  //
   // Source is a subframe navigation. This is any content that is automatically
   // loaded in a non-toplevel frame. For example, if a page consists of several
   // frames containing ads, those ad URLs will have this transition type.
   // The user may not even realize the content in these pages is a separate
   // frame, so may not care about the URL.
-  ///
+  //
   TT_AUTO_SUBFRAME = 3,
 
-  ///
+  //
   // Source is a subframe navigation explicitly requested by the user that will
   // generate new navigation entries in the back/forward list. These are
   // probably more important than frames that were automatically loaded in
   // the background because the user probably cares about the fact that this
   // link was loaded.
-  ///
+  //
   TT_MANUAL_SUBFRAME = 4,
 
-  ///
+  //
   // Source is a form submission by the user. NOTE: In some situations
   // submitting a form does not result in this transition type. This can happen
   // if the form uses a script to submit the contents.
-  ///
+  //
   TT_FORM_SUBMIT = 7,
 
-  ///
+  //
   // Source is a "reload" of the page via the Reload function or by re-visiting
   // the same URL. NOTE: This is distinct from the concept of whether a
   // particular load uses "reload semantics" (i.e. bypasses cached data).
-  ///
+  //
   TT_RELOAD = 8,
 
-  ///
+  //
   // General mask defining the bits used for the source values.
-  ///
+  //
   TT_SOURCE_MASK = 0xFF,
 
   // Qualifiers.
   // Any of the core values above can be augmented by one or more qualifiers.
   // These qualifiers further define the transition.
 
-  ///
+  //
   // Attempted to visit a URL but was blocked.
-  ///
+  //
   TT_BLOCKED_FLAG = 0x00800000,
 
-  ///
+  //
   // Used the Forward or Back function to navigate among browsing history.
-  ///
+  //
   TT_FORWARD_BACK_FLAG = 0x01000000,
 
-  ///
+  //
   // The beginning of a navigation chain.
-  ///
+  //
   TT_CHAIN_START_FLAG = 0x10000000,
 
-  ///
+  //
   // The last transition in a redirect chain.
-  ///
+  //
   TT_CHAIN_END_FLAG = 0x20000000,
 
-  ///
+  //
   // Redirects caused by JavaScript or a meta refresh tag on the page.
-  ///
+  //
   TT_CLIENT_REDIRECT_FLAG = 0x40000000,
 
-  ///
+  //
   // Redirects sent from the server by HTTP headers.
-  ///
+  //
   TT_SERVER_REDIRECT_FLAG = 0x80000000,
 
-  ///
+  //
   // Used to test whether a transition involves a redirect.
-  ///
+  //
   TT_IS_REDIRECT_MASK = 0xC0000000,
 
-  ///
+  //
   // General mask defining the bits used for the qualifiers.
-  ///
+  //
   TT_QUALIFIER_MASK = 0xFFFFFF00,
 }
 
-///
+//
 // Process termination status values.
-///
+//
 pub enum cef_termination_status_t {
-  ///
+  //
   // Non-zero exit status.
-  ///
+  //
   TS_ABNORMAL_TERMINATION,
 
-  ///
+  //
   // SIGKILL or task manager kill.
-  ///
+  //
   TS_PROCESS_WAS_KILLED,
 
-  ///
+  //
   // Segmentation fault.
-  ///
+  //
   TS_PROCESS_CRASHED,
 }
 
-///
+//
 // V8 access control values.
-///
+//
 pub enum cef_v8_accesscontrol_t {
   V8_ACCESS_CONTROL_DEFAULT               = 0,
   V8_ACCESS_CONTROL_ALL_CAN_READ          = 1,
   V8_ACCESS_CONTROL_ALL_CAN_WRITE         = 1 << 1,
   V8_ACCESS_CONTROL_PROHIBITS_OVERWRITING = 1 << 2
 }
 
-///
+//
 // V8 property attribute values.
-///
+//
 pub enum cef_v8_propertyattribute_t {
   V8_PROPERTY_ATTRIBUTE_NONE       = 0,       // Writeable, Enumerable,
                                               //   Configurable
   V8_PROPERTY_ATTRIBUTE_READONLY   = 1 << 0,  // Not writeable
   V8_PROPERTY_ATTRIBUTE_DONTENUM   = 1 << 1,  // Not enumerable
   V8_PROPERTY_ATTRIBUTE_DONTDELETE = 1 << 2   // Not configurable
 }
 
-///
+//
 // XML node types.
-///
+//
 pub enum cef_xml_node_type_t {
   XML_NODE_UNSUPPORTED = 0,
   XML_NODE_PROCESSING_INSTRUCTION,
   XML_NODE_DOCUMENT_TYPE,
   XML_NODE_ELEMENT_START,
   XML_NODE_ELEMENT_END,
   XML_NODE_ATTRIBUTE,
   XML_NODE_TEXT,
   XML_NODE_CDATA,
   XML_NODE_ENTITY_REFERENCE,
   XML_NODE_WHITESPACE,
   XML_NODE_COMMENT,
 }
 
-///
+//
 // Geoposition error codes.
-///
+//
 pub enum cef_geoposition_error_code_t {
   GEOPOSITON_ERROR_NONE = 0,
   GEOPOSITON_ERROR_PERMISSION_DENIED,
   GEOPOSITON_ERROR_POSITION_UNAVAILABLE,
   GEOPOSITON_ERROR_TIMEOUT,
 }
 
-///
+//
 // Structure representing geoposition information. The properties of this
 // structure correspond to those of the JavaScript Position object although
 // their types may differ.
-///
+//
 pub struct _cef_geoposition_t {
-  ///
+  //
   // Latitude in decimal degrees north (WGS84 coordinate frame).
-  ///
+  //
   pub latitude: c_double,
 
-  ///
+  //
   // Longitude in decimal degrees west (WGS84 coordinate frame).
-  ///
+  //
   pub longitude: c_double,
 
-  ///
+  //
   // Altitude in meters (above WGS84 datum).
-  ///
+  //
   pub altitude: c_double,
 
-  ///
+  //
   // Accuracy of horizontal position in meters.
-  ///
+  //
   pub accuracy: c_double,
 
-  ///
+  //
   // Accuracy of altitude in meters.
-  ///
+  //
   pub altitude_accuracy: c_double,
 
-  ///
+  //
   // Heading in decimal degrees clockwise from true north.
-  ///
+  //
   pub heading: c_double,
 
-  ///
+  //
   // Horizontal component of device velocity in meters per second.
-  ///
+  //
   pub speed: c_double,
 
-  ///
+  //
   // Time of position measurement in miliseconds since Epoch in UTC time. This
   // is taken from the host computer's system clock.
-  ///
+  //
   pub timestamp: cef_time_t,
 
-  ///
+  //
   // Error code, see enum above.
-  ///
+  //
   pub error_code: cef_geoposition_error_code_t,
 
-  ///
+  //
   // Human-readable error message.
-  ///
+  //
   pub error_message: cef_string_t,
 }
 
 pub type cef_geoposition_t = _cef_geoposition_t;
 
 pub type CefGeoposition = cef_geoposition_t;
 
-///
+//
 // Cookie information.
-///
+//
 pub struct _cef_cookie_t {
-  ///
+  //
   // The cookie name.
-  ///
+  //
   pub name: cef_string_t,
 
-  ///
+  //
   // The cookie value.
-  ///
+  //
   pub value: cef_string_t,
 
-  ///
+  //
   // If |domain| is empty a host cookie will be created instead of a domain
   // cookie. Domain cookies are stored with a leading "." and are visible to
   // sub-domains whereas host cookies are not.
-  ///
+  //
   pub domain: cef_string_t,
 
-  ///
+  //
   // If |path| is non-empty only URLs at or below the path will get the cookie
   // value.
-  ///
+  //
   pub path: cef_string_t,
 
-  ///
+  //
   // If |secure| is true the cookie will only be sent for HTTPS requests.
-  ///
+  //
   pub secure: c_int,
 
-  ///
+  //
   // If |httponly| is true the cookie will only be sent for HTTP requests.
-  ///
+  //
   pub httponly: c_int,
 
-  ///
+  //
   // The cookie creation date. This is automatically populated by the system on
   // cookie creation.
-  ///
+  //
   pub creation: cef_time_t,
 
-  ///
+  //
   // The cookie last access date. This is automatically populated by the system
   // on access.
-  ///
+  //
   pub last_access: cef_time_t,
 
-  ///
+  //
   // The cookie expiration date is only valid if |has_expires| is true.
-  ///
+  //
   pub has_expires: c_int,
   pub expires: cef_time_t,
 }
 
 pub type cef_cookie_t = _cef_cookie_t;
 
 pub type CefCookie = cef_cookie_t;
 
-///
+//
 // Popup window features.
-///
+//
 pub struct _cef_popup_features_t {
   pub x: c_int,
   pub x_set: c_int,
   pub y: c_int,
   pub y_set: c_int,
   pub width: c_int,
   pub width_set: c_int,
   pub height: c_int,
new file mode 100644
--- /dev/null
+++ b/servo/ports/cef/window.rs
@@ -0,0 +1,284 @@
+/* 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/. */
+
+//! Off-screen windows.
+//!
+//! This is used for off-screen rendering mode only; on-screen windows (the default embedding mode)
+//! are managed by a platform toolkit (GLFW or Glutin).
+
+use eutil::Downcast;
+use interfaces::CefBrowser;
+use render_handler::CefRenderHandlerExtensions;
+use types::cef_rect_t;
+use wrappers::Utf16Encoder;
+
+use compositing::compositor_task::{mod, CompositorProxy, CompositorReceiver};
+use compositing::windowing::{IdleWindowEvent, WindowEvent, WindowMethods};
+use geom::scale_factor::ScaleFactor;
+use geom::size::TypedSize2D;
+use gleam::gl;
+use layers::geometry::DevicePixel;
+use layers::platform::surface::NativeGraphicsMetadata;
+use libc::{c_char, c_void};
+use servo_msg::compositor_msg::{Blank, FinishedLoading, Loading, PerformingLayout, PaintState};
+use servo_msg::compositor_msg::{ReadyState};
+use servo_msg::constellation_msg::LoadData;
+use servo_util::geometry::ScreenPx;
+use std::cell::RefCell;
+use std::rc::Rc;
+
+#[cfg(target_os="macos")]
+use std::ptr;
+
+/// The type of an off-screen window.
+#[deriving(Clone)]
+pub struct Window {
+    cef_browser: RefCell<Option<CefBrowser>>,
+}
+
+impl Window {
+    /// Creates a new window.
+    pub fn new() -> Rc<Window> {
+        const RTLD_DEFAULT: *mut c_void = (-2) as *mut c_void;
+
+        extern {
+            fn dlsym(handle: *mut c_void, symbol: *const c_char) -> *mut c_void;
+        }
+
+        gl::load_with(|s| {
+            unsafe {
+                let c_str = s.to_c_str();
+                dlsym(RTLD_DEFAULT, c_str.as_ptr()) as *const c_void
+            }
+        });
+
+        Rc::new(Window {
+            cef_browser: RefCell::new(None),
+        })
+    }
+
+    /// Sets the current browser.
+    pub fn set_browser(&self, browser: CefBrowser) {
+        *self.cef_browser.borrow_mut() = Some(browser)
+    }
+
+    /// Currently unimplemented.
+    pub fn wait_events(&self) -> WindowEvent {
+        IdleWindowEvent
+    }
+}
+
+impl WindowMethods for Window {
+    fn framebuffer_size(&self) -> TypedSize2D<DevicePixel,uint> {
+        let browser = self.cef_browser.borrow();
+        match *browser {
+            None => TypedSize2D(400, 300),
+            Some(ref browser) => {
+                let mut rect = cef_rect_t::zero();
+                browser.get_host()
+                       .get_client()
+                       .get_render_handler()
+                       .get_backing_rect((*browser).clone(), &mut rect);
+                TypedSize2D(rect.width as uint, rect.height as uint)
+            }
+        }
+    }
+
+    fn size(&self) -> TypedSize2D<ScreenPx,f32> {
+        let browser = self.cef_browser.borrow();
+        match *browser {
+            None => TypedSize2D(400.0, 300.0),
+            Some(ref browser) => {
+                let mut rect = cef_rect_t::zero();
+                browser.get_host()
+                       .get_client()
+                       .get_render_handler()
+                       .get_view_rect((*browser).clone(), &mut rect);
+                TypedSize2D(rect.width as f32, rect.height as f32)
+            }
+        }
+    }
+
+    fn present(&self) {
+        let browser = self.cef_browser.borrow();
+        match *browser {
+            None => {}
+            Some(ref browser) => {
+                browser.get_host().get_client().get_render_handler().on_present(browser.clone());
+            }
+        }
+    }
+
+    fn set_ready_state(&self, ready_state: ReadyState) {
+        let browser = self.cef_browser.borrow();
+        let browser = match *browser {
+            None => return,
+            Some(ref browser) => browser,
+        };
+        let is_loading = match ready_state {
+            Blank | FinishedLoading => 0,
+            Loading | PerformingLayout => 1,
+        };
+        browser.get_host()
+               .get_client()
+               .get_load_handler()
+               .on_loading_state_change(browser.clone(), is_loading, 1, 1);
+    }
+
+    fn set_paint_state(&self, _: PaintState) {
+        // TODO(pcwalton)
+    }
+
+    fn hidpi_factor(&self) -> ScaleFactor<ScreenPx,DevicePixel,f32> {
+        let browser = self.cef_browser.borrow();
+        match *browser {
+            None => ScaleFactor(1.0),
+            Some(ref browser) => {
+                let mut view_rect = cef_rect_t::zero();
+                browser.get_host()
+                       .get_client()
+                       .get_render_handler()
+                       .get_view_rect((*browser).clone(), &mut view_rect);
+                let mut backing_rect = cef_rect_t::zero();
+                browser.get_host()
+                       .get_client()
+                       .get_render_handler()
+                       .get_backing_rect((*browser).clone(), &mut backing_rect);
+                ScaleFactor(backing_rect.width as f32 / view_rect.width as f32)
+            }
+        }
+    }
+
+    #[cfg(target_os="macos")]
+    fn native_metadata(&self) -> NativeGraphicsMetadata {
+        use cgl::{CGLGetCurrentContext, CGLGetPixelFormat};
+
+        // FIXME(pcwalton)
+        unsafe {
+            NativeGraphicsMetadata {
+                pixel_format: CGLGetPixelFormat(CGLGetCurrentContext()),
+            }
+        }
+    }
+
+    #[cfg(target_os="linux")]
+    fn native_metadata(&self) -> NativeGraphicsMetadata {
+        // TODO(pcwalton)
+        panic!()
+    }
+
+    fn create_compositor_channel(_: &Option<Rc<Window>>)
+                                 -> (Box<CompositorProxy+Send>, Box<CompositorReceiver>) {
+        let (sender, receiver) = channel();
+        (box CefCompositorProxy {
+             sender: sender,
+         } as Box<CompositorProxy+Send>,
+         box receiver as Box<CompositorReceiver>)
+    }
+
+    fn prepare_for_composite(&self) -> bool {
+        let browser = self.cef_browser.borrow();
+        match *browser {
+            None => {}
+            Some(ref browser) => {
+                browser.get_host().get_client().get_render_handler().paint(browser.clone());
+            }
+        }
+        true
+    }
+
+    fn load_end(&self) {
+        // FIXME(pcwalton): The status code 200 is a lie.
+        let browser = self.cef_browser.borrow();
+        let browser = match *browser {
+            None => return,
+            Some(ref browser) => browser,
+        };
+        browser.get_host()
+               .get_client()
+               .get_load_handler()
+               .on_load_end((*browser).clone(), browser.get_main_frame(), 200);
+    }
+
+    fn set_page_title(&self, string: Option<String>) {
+        let browser = self.cef_browser.borrow();
+        let browser = match *browser {
+            None => return,
+            Some(ref browser) => browser,
+        };
+        let frame = browser.get_main_frame();
+        let frame = frame.downcast();
+        let mut title_visitor = frame.title_visitor.borrow_mut();
+        match &mut *title_visitor {
+            &None => {}
+            &Some(ref mut visitor) => {
+                match string {
+                    None => visitor.visit(&[]),
+                    Some(string) => {
+                        let utf16_chars: Vec<u16> = Utf16Encoder::new(string.chars()).collect();
+                        visitor.visit(utf16_chars.as_slice())
+                    }
+                }
+            }
+        }
+    }
+
+    fn set_page_load_data(&self, load_data: LoadData) {
+        let browser = self.cef_browser.borrow();
+        let browser = match *browser {
+            None => return,
+            Some(ref browser) => browser,
+        };
+        let frame = browser.get_main_frame();
+        let frame = frame.downcast();
+        *frame.url.borrow_mut() = load_data.url.to_string()
+    }
+}
+
+struct CefCompositorProxy {
+    sender: Sender<compositor_task::Msg>,
+}
+
+impl CompositorProxy for CefCompositorProxy {
+    #[cfg(target_os="macos")]
+    fn send(&mut self, msg: compositor_task::Msg) {
+        use cocoa::appkit::{NSApp, NSApplication, NSApplicationDefined, NSAutoreleasePool};
+        use cocoa::appkit::{NSEvent, NSPoint};
+        use cocoa::base::nil;
+
+        // Send a message and kick the OS event loop awake.
+        self.sender.send(msg);
+
+        unsafe {
+            let pool = NSAutoreleasePool::new(nil);
+            let event =
+                NSEvent::otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2(
+                nil,
+                NSApplicationDefined,
+                NSPoint::new(0.0, 0.0),
+                0,
+                0.0,
+                0,
+                ptr::null_mut(),
+                0,
+                0,
+                0);
+            NSApp().postEvent_atStart_(event, false);
+            pool.drain();
+        }
+    }
+
+    #[cfg(target_os="linux")]
+    fn send(&mut self, msg: compositor_task::Msg) {
+        // FIXME(pcwalton): Kick the GTK event loop awake?
+        self.sender.send(msg);
+    }
+
+    fn clone_compositor_proxy(&self) -> Box<CompositorProxy+Send> {
+        box CefCompositorProxy {
+            sender: self.sender.clone(),
+        } as Box<CompositorProxy+Send>
+    }
+}
+
--- a/servo/ports/cef/wrappers.rs
+++ b/servo/ports/cef/wrappers.rs
@@ -16,19 +16,19 @@ use types::{cef_errorcode_t, cef_event_f
 use types::{cef_file_dialog_mode_t, cef_focus_handler_t, cef_focus_source_t};
 use types::{cef_geolocation_handler_t, cef_geoposition_t};
 use types::{cef_jsdialog_handler_t, cef_jsdialog_type_t};
 use types::{cef_key_event, cef_keyboard_handler_t};
 use types::{cef_load_handler_t, cef_menu_item_type_t, cef_mouse_button_type_t};
 use types::{cef_mouse_event, cef_navigation_type_t};
 use types::{cef_page_range_t, cef_paint_element_type_t, cef_point_t, cef_postdataelement_type_t};
 use types::{cef_popup_features_t, cef_process_id_t};
-use types::{cef_rect_t, cef_request_context_t, cef_request_handler_t};
+use types::{cef_rect_t, cef_request_handler_t};
 use types::{cef_resource_type_t};
-use types::{cef_screen_info_t, cef_size_t, cef_string_t};
+use types::{cef_screen_info_t, cef_size_t, cef_string_t, cef_string_userfree_t};
 use types::{cef_string_list_t, cef_string_map_t, cef_string_multimap_t, cef_string_utf16};
 use types::{cef_termination_status_t, cef_text_input_context_t, cef_thread_id_t};
 use types::{cef_time_t, cef_transition_type_t, cef_urlrequest_status_t};
 use types::{cef_v8_accesscontrol_t, cef_v8_propertyattribute_t, cef_value_type_t};
 use types::{cef_window_info_t, cef_xml_encoding_type_t, cef_xml_node_type_t};
 
 use libc::{mod, c_char, c_int, c_ushort, c_void};
 use std::collections::HashMap;
@@ -121,17 +121,16 @@ cef_noop_wrapper!(*mut cef_download_hand
 cef_noop_wrapper!(*mut cef_drag_data_t)
 cef_noop_wrapper!(*mut cef_drag_handler_t)
 cef_noop_wrapper!(*mut cef_event_handle_t)
 cef_noop_wrapper!(*mut cef_focus_handler_t)
 cef_noop_wrapper!(*mut cef_geolocation_handler_t)
 cef_noop_wrapper!(*mut cef_jsdialog_handler_t)
 cef_noop_wrapper!(*mut cef_keyboard_handler_t)
 cef_noop_wrapper!(*mut cef_load_handler_t)
-cef_noop_wrapper!(*mut cef_request_context_t)
 cef_noop_wrapper!(*mut cef_request_handler_t)
 cef_noop_wrapper!(*mut cef_string_list_t)
 cef_noop_wrapper!(*mut cef_string_utf16)
 cef_noop_wrapper!(c_int)
 cef_noop_wrapper!(cef_color_model_t)
 cef_noop_wrapper!(cef_context_menu_edit_state_flags_t)
 cef_noop_wrapper!(cef_context_menu_media_state_flags_t)
 cef_noop_wrapper!(cef_context_menu_media_type_t)
@@ -176,19 +175,18 @@ cef_unimplemented_wrapper!(*mut *mut cef
 cef_unimplemented_wrapper!(cef_string_list_t, Vec<String>)
 cef_unimplemented_wrapper!(cef_string_map_t, HashMap<String,String>)
 cef_unimplemented_wrapper!(cef_string_multimap_t, HashMap<String,Vec<String>>)
 cef_unimplemented_wrapper!(cef_string_t, String)
 
 impl<'a> CefWrap<*const cef_string_t> for &'a [u16] {
     fn to_c(buffer: &'a [u16]) -> *const cef_string_t {
         unsafe {
-            let ptr: *mut c_ushort =
-                mem::transmute(libc::calloc(1, ((buffer.len() * 2) + 1) as u64));
-            ptr::copy_memory(ptr, mem::transmute(buffer.as_ptr()), (buffer.len() * 2) as uint);
+            let ptr: *mut c_ushort = mem::transmute(libc::malloc(((buffer.len() + 1) * 2) as u64));
+            ptr::copy_memory(ptr, mem::transmute(buffer.as_ptr()), buffer.len());
             *ptr.offset(buffer.len() as int) = 0;
 
             // FIXME(pcwalton): This leaks!! We should instead have the caller pass some scratch
             // stack space to create the object in. What a botch.
             let boxed_string = box cef_string_utf16 {
                 str: ptr,
                 length: buffer.len() as u64,
                 dtor: Some(free_boxed_utf16_string),
@@ -235,16 +233,81 @@ impl<'a,'b> CefWrap<*mut *const c_char> 
     fn to_c(_: &'a mut &'b str) -> *mut *const c_char {
         panic!("unimplemented CEF type conversion: &'a mut &'b str")
     }
     unsafe fn to_rust(_: *mut *const c_char) -> &'a mut &'b str {
         panic!("unimplemented CEF type conversion: *mut *const c_char")
     }
 }
 
+impl<'a> CefWrap<cef_string_userfree_t> for String {
+    fn to_c(string: String) -> cef_string_userfree_t {
+        let utf16_chars: Vec<u16> = Utf16Encoder::new(string.chars()).collect();
+
+        let boxed_string;
+        unsafe {
+            let buffer = libc::malloc((mem::size_of::<c_ushort>() as u64) *
+                                      ((utf16_chars.len() + 1) as u64 + 1)) as *mut u16;
+            for (i, ch) in utf16_chars.iter().enumerate() {
+                *buffer.offset(i as int) = *ch
+            }
+            *buffer.offset(utf16_chars.len() as int) = 0;
+
+            boxed_string = libc::malloc(mem::size_of::<cef_string_utf16>() as u64) as
+                *mut cef_string_utf16;
+            ptr::write(&mut (*boxed_string).str, buffer);
+            ptr::write(&mut (*boxed_string).length, utf16_chars.len() as u64);
+            ptr::write(&mut (*boxed_string).dtor, Some(free_utf16_buffer));
+            mem::forget(utf16_chars);
+        }
+        boxed_string
+    }
+    unsafe fn to_rust(_: cef_string_userfree_t) -> String {
+        panic!("unimplemented CEF type conversion: cef_string_userfree_t")
+    }
+}
+
+extern "C" fn free_utf16_buffer(buffer: *mut c_ushort) {
+    unsafe {
+        libc::free(buffer as *mut c_void)
+    }
+}
+
+// TODO(pcwalton): Post Rust-upgrade, remove this and use `collections::str::Utf16Encoder`.
+pub struct Utf16Encoder<I> {
+    chars: I,
+    extra: u16,
+}
+
+impl<I> Utf16Encoder<I> {
+    pub fn new(chars: I) -> Utf16Encoder<I> where I: Iterator<char> {
+        Utf16Encoder {
+            chars: chars,
+            extra: 0,
+        }
+    }
+}
+
+impl<I> Iterator<u16> for Utf16Encoder<I> where I: Iterator<char> {
+    fn next(&mut self) -> Option<u16> {
+        if self.extra != 0 {
+            return Some(mem::replace(&mut self.extra, 0))
+        }
+
+        let mut buf = [0u16, ..2];
+        self.chars.next().map(|ch| {
+            let n = ch.encode_utf16(buf.as_mut_slice()).unwrap_or(0);
+            if n == 2 {
+                self.extra = buf[1]
+            }
+            buf[0]
+        })
+    }
+}
+
 impl<'a> CefWrap<cef_string_t> for &'a mut String {
     fn to_c(_: &'a mut String) -> cef_string_t {
         panic!("unimplemented CEF type conversion: &'a mut String")
     }
     unsafe fn to_rust(_: cef_string_t) -> &'a mut String {
         panic!("unimplemented CEF type conversion: cef_string_t")
     }
 }
--- a/servo/ports/glfw/window.rs
+++ b/servo/ports/glfw/window.rs
@@ -18,19 +18,19 @@ use compositing::windowing::{WindowEvent
 use geom::point::{Point2D, TypedPoint2D};
 use geom::scale_factor::ScaleFactor;
 use geom::size::TypedSize2D;
 use glfw::{mod, Context};
 use gleam::gl;
 use layers::geometry::DevicePixel;
 use layers::platform::surface::NativeGraphicsMetadata;
 use libc::c_int;
-use msg::compositor_msg::{FinishedLoading, Blank, Loading, PerformingLayout, ReadyState};
-use msg::compositor_msg::{IdlePaintState, PaintState, PaintingPaintState};
-use msg::constellation_msg;
+use msg::compositor_msg::{Blank, FinishedLoading, IdlePaintState, Loading, PaintState};
+use msg::compositor_msg::{PaintingPaintState, PerformingLayout, ReadyState};
+use msg::constellation_msg::{mod, LoadData};
 use std::cell::{Cell, RefCell};
 use std::comm::Receiver;
 use std::rc::Rc;
 use time::{mod, Timespec};
 use util::geometry::ScreenPx;
 
 /// The type of a window.
 pub struct Window {
@@ -92,24 +92,33 @@ impl Window {
         window.glfw_window.set_scroll_polling(true);
 
         glfw.set_swap_interval(1);
 
         Rc::new(window)
     }
 
     pub fn wait_events(&self) -> WindowEvent {
+        self.wait_or_poll_events(|glfw| glfw.wait_events())
+    }
+
+    pub fn poll_events(&self) -> WindowEvent {
+        self.wait_or_poll_events(|glfw| glfw.poll_events())
+    }
+
+    /// Helper method to factor out functionality from `poll_events` and `wait_events`.
+    fn wait_or_poll_events(&self, callback: |glfw: &glfw::Glfw|) -> WindowEvent {
         {
             let mut event_queue = self.event_queue.borrow_mut();
             if !event_queue.is_empty() {
                 return event_queue.remove(0).unwrap();
             }
         }
 
-        self.glfw.wait_events();
+        callback(&self.glfw);
         for (_, event) in glfw::flush_messages(&self.events) {
             self.handle_window_event(&self.glfw_window, event);
         }
 
         if self.glfw_window.should_close() {
             QuitWindowEvent
         } else {
             self.event_queue.borrow_mut().remove(0).unwrap_or(IdleWindowEvent)
@@ -191,16 +200,26 @@ impl WindowMethods for Window {
     fn create_compositor_channel(_: &Option<Rc<Window>>)
                                  -> (Box<CompositorProxy+Send>, Box<CompositorReceiver>) {
         let (sender, receiver) = channel();
         (box GlfwCompositorProxy {
              sender: sender,
          } as Box<CompositorProxy+Send>,
          box receiver as Box<CompositorReceiver>)
     }
+
+    fn prepare_for_composite(&self) -> bool {
+        true
+    }
+
+    fn load_end(&self) {}
+
+    fn set_page_title(&self, _: Option<String>) {}
+
+    fn set_page_load_data(&self, _: LoadData) {}
 }
 
 impl Window {
     fn handle_window_event(&self, window: &glfw::Window, event: glfw::WindowEvent) {
         match event {
             glfw::KeyEvent(key, _, action, mods) => {
                 if action == glfw::Press {
                     self.handle_key(key, mods);
--- a/servo/python/servo/build_commands.py
+++ b/servo/python/servo/build_commands.py
@@ -108,17 +108,17 @@ class MachCommands(CommandBase):
             opts += ["-j", jobs]
         if verbose:
             opts += ["-v"]
         if release:
             opts += ["--release"]
 
         build_start = time()
         with cd(path.join("ports", "cef")):
-            ret = subprocess.call(["cargo", "build"],
+            ret = subprocess.call(["cargo", "build"] + opts,
                                   env=self.build_env())
         elapsed = time() - build_start
 
         print("CEF build completed in %0.2fs" % elapsed)
 
         return ret
 
     @Command('build-tests',