servo: Merge #6175 - Embedding: revenge of cargo (from zmike:embedding-REVENGE_OF_CARGO); r=larsbergstrom
authorMike Blumenkrantz <zmike@osg.samsung.com>
Wed, 27 May 2015 14:17:32 -0500
changeset 383083 5e379a694d689be79130e75397a1c2bb57f734f4
parent 383082 17fe75d37a17170729d9accf8797387a85aa47e5
child 383084 c22544e4a61daa113883c9a716d9227d97ffd1dc
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)
reviewerslarsbergstrom
servo: Merge #6175 - Embedding: revenge of cargo (from zmike:embedding-REVENGE_OF_CARGO); r=larsbergstrom Adds a bunch more embedding interfaces/callbacks/functionality @larsbergstrom Source-Repo: https://github.com/servo/servo Source-Revision: 94ebfe81e454ca6dd68210b4475b4091e836dd1a
servo/components/compositing/compositor.rs
servo/components/compositing/compositor_task.rs
servo/components/compositing/constellation.rs
servo/components/compositing/headless.rs
servo/components/compositing/windowing.rs
servo/components/msg/constellation_msg.rs
servo/components/net/lib.rs
servo/components/net/net_error_list.rs
servo/components/script/script_task.rs
servo/components/servo/lib.rs
servo/components/util/opts.rs
servo/ports/cef/Cargo.lock
servo/ports/cef/browser.rs
servo/ports/cef/browser_host.rs
servo/ports/cef/core.rs
servo/ports/cef/eutil.rs
servo/ports/cef/frame.rs
servo/ports/cef/interfaces/cef_browser_process_handler.rs
servo/ports/cef/lib.rs
servo/ports/cef/types.rs
servo/ports/cef/window.rs
servo/ports/cef/wrappers.rs
servo/ports/glutin/Cargo.toml
servo/ports/glutin/lib.rs
servo/ports/glutin/window.rs
servo/ports/gonk/src/window.rs
--- a/servo/components/compositing/compositor.rs
+++ b/servo/components/compositing/compositor.rs
@@ -379,28 +379,32 @@ impl<Window: WindowMethods> IOCompositor
                 }
             }
 
             (Msg::ScrollFragmentPoint(pipeline_id, layer_id, point),
              ShutdownState::NotShuttingDown) => {
                 self.scroll_fragment_to_point(pipeline_id, layer_id, point);
             }
 
-            (Msg::LoadComplete, ShutdownState::NotShuttingDown) => {
+            (Msg::LoadStart(back, forward), ShutdownState::NotShuttingDown) => {
+                self.window.load_start(back, forward);
+            }
+
+            (Msg::LoadComplete(back, forward), ShutdownState::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(CompositingReason::Headless);
                 }
 
                 // Inform the embedder that the load has finished.
                 //
                 // TODO(pcwalton): Specify which frame's load completed.
-                self.window.load_end();
+                self.window.load_end(back, forward);
             }
 
             (Msg::ScrollTimeout(timestamp), ShutdownState::NotShuttingDown) => {
                 debug!("scroll timeout, drawing unpainted content!");
                 match self.composition_request {
                     CompositionRequest::CompositeOnScrollTimeout(this_timestamp) => {
                         if timestamp == this_timestamp {
                             self.composition_request = CompositionRequest::CompositeNow(
@@ -873,24 +877,23 @@ impl<Window: WindowMethods> IOCompositor
 
         self.scene.set_root_layer_size(new_size.as_f32());
         self.send_window_size();
     }
 
     fn on_load_url_window_event(&mut self, url_string: String) {
         debug!("osmain: loading URL `{}`", url_string);
         self.got_load_complete_message = false;
-        let root_pipeline_id = match self.scene.root {
-            Some(ref layer) => layer.get_pipeline_id(),
-            None => panic!("Compositor: Received WindowEvent::LoadUrl without initialized compositor \
-                           layers"),
+        let url = Url::parse(&url_string).unwrap();
+        self.window.set_page_url(url.clone());
+        let msg = match self.scene.root {
+            Some(ref layer) => ConstellationMsg::LoadUrl(layer.get_pipeline_id(), LoadData::new(url)),
+            None => ConstellationMsg::InitLoadUrl(url)
         };
 
-        let msg = ConstellationMsg::LoadUrl(root_pipeline_id,
-            LoadData::new(Url::parse(&url_string).unwrap()));
         let ConstellationChan(ref chan) = self.constellation_chan;
         chan.send(msg).unwrap()
     }
 
     fn on_mouse_window_event_class(&self, mouse_window_event: MouseWindowEvent) {
         let point = match mouse_window_event {
             MouseWindowEvent::Click(_, p) => p,
             MouseWindowEvent::MouseDown(_, p) => p,
--- a/servo/components/compositing/compositor_task.rs
+++ b/servo/components/compositing/compositor_task.rs
@@ -155,18 +155,20 @@ pub enum Msg {
     /// 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 URL.
     ChangePageUrl(PipelineId, Url),
     /// Alerts the compositor that the given pipeline has changed whether it is running animations.
     ChangeRunningAnimationsState(PipelineId, AnimationState),
     /// Replaces the current frame tree, typically called during main frame navigation.
     SetFrameTree(SendableFrameTree, Sender<()>, ConstellationChan),
-    /// The load of a page has completed.
-    LoadComplete,
+    /// The load of a page has begun: (can go back, can go forward).
+    LoadStart(bool, bool),
+    /// The load of a page has completed: (can go back, can go forward).
+    LoadComplete(bool, bool),
     /// Indicates that the scrolling timeout with the given starting timestamp has happened and a
     /// composite should happen. (See the `scrolling` module.)
     ScrollTimeout(u64),
     RecompositeAfterScroll,
     /// Sends an unconsumed key event back to the compositor.
     KeyEvent(Key, KeyState, KeyModifiers),
     /// Changes the cursor.
     SetCursor(Cursor),
@@ -189,17 +191,18 @@ impl Debug for Msg {
             Msg::InitializeLayersForPipeline(..) => write!(f, "InitializeLayersForPipeline"),
             Msg::SetLayerRect(..) => write!(f, "SetLayerRect"),
             Msg::ScrollFragmentPoint(..) => write!(f, "ScrollFragmentPoint"),
             Msg::AssignPaintedBuffers(..) => write!(f, "AssignPaintedBuffers"),
             Msg::ChangeRunningAnimationsState(..) => write!(f, "ChangeRunningAnimationsState"),
             Msg::ChangePageTitle(..) => write!(f, "ChangePageTitle"),
             Msg::ChangePageUrl(..) => write!(f, "ChangePageUrl"),
             Msg::SetFrameTree(..) => write!(f, "SetFrameTree"),
-            Msg::LoadComplete => write!(f, "LoadComplete"),
+            Msg::LoadComplete(..) => write!(f, "LoadComplete"),
+            Msg::LoadStart(..) => write!(f, "LoadStart"),
             Msg::ScrollTimeout(..) => write!(f, "ScrollTimeout"),
             Msg::RecompositeAfterScroll => write!(f, "RecompositeAfterScroll"),
             Msg::KeyEvent(..) => write!(f, "KeyEvent"),
             Msg::SetCursor(..) => write!(f, "SetCursor"),
             Msg::CreatePng(..) => write!(f, "CreatePng"),
             Msg::PaintTaskExited(..) => write!(f, "PaintTaskExited"),
             Msg::ViewportConstrained(..) => write!(f, "ViewportConstrained"),
             Msg::IsReadyToSaveImageReply(..) => write!(f, "IsReadyToSaveImageReply"),
--- a/servo/components/compositing/constellation.rs
+++ b/servo/components/compositing/constellation.rs
@@ -386,19 +386,19 @@ impl<LTF: LayoutTaskFactory, STF: Script
             // If there is already a pending page (self.pending_frames), it will not be overridden;
             // However, if the id is not encompassed by another change, it will be.
             ConstellationMsg::LoadUrl(source_id, load_data) => {
                 debug!("constellation got URL load message");
                 self.handle_load_url_msg(source_id, load_data);
             }
             // A page loaded through one of several methods above has completed all parsing,
             // script, and reflow messages have been sent.
-            ConstellationMsg::LoadComplete => {
+            ConstellationMsg::LoadComplete(pipeline_id) => {
                 debug!("constellation got load complete message");
-                self.handle_load_complete_msg()
+                self.handle_load_complete_msg(&pipeline_id)
             }
             // Handle a forward or back request
             ConstellationMsg::Navigate(pipeline_info, direction) => {
                 debug!("constellation got navigation message");
                 self.handle_navigate_msg(pipeline_info, direction);
             }
             // Notification that painting has finished and is requesting permission to paint.
             ConstellationMsg::PainterReady(pipeline_id) => {
@@ -514,18 +514,20 @@ impl<LTF: LayoutTaskFactory, STF: Script
                               LoadData::new(Url::parse("about:failure").unwrap()));
 
         self.push_pending_frame(new_pipeline_id, Some(pipeline_id));
     }
 
     fn handle_init_load(&mut self, url: Url) {
         let window_rect = Rect(Point2D::zero(), self.window_size.visible_viewport);
         let root_pipeline_id =
-            self.new_pipeline(None, Some(window_rect), None, LoadData::new(url));
+            self.new_pipeline(None, Some(window_rect), None, LoadData::new(url.clone()));
+        self.handle_load_start_msg(&root_pipeline_id);
         self.push_pending_frame(root_pipeline_id, None);
+        self.compositor_proxy.send(CompositorMsg::ChangePageUrl(root_pipeline_id, url));
     }
 
     fn handle_frame_rect_msg(&mut self, containing_pipeline_id: PipelineId, subpage_id: SubpageId,
                              rect: TypedRect<PagePx, f32>) {
         // Store the new rect inside the pipeline
         let (pipeline_id, script_chan) = {
             // Find the pipeline that corresponds to this rectangle. It's possible that this
             // pipeline may have already exited before we process this message, so just
@@ -624,16 +626,17 @@ impl<LTF: LayoutTaskFactory, STF: Script
 
     fn handle_load_url_msg(&mut self, source_id: PipelineId, load_data: LoadData) {
         // If this load targets an iframe, its framing element may exist
         // in a separate script task than the framed document that initiated
         // the new load. The framing element must be notified about the
         // requested change so it can update its internal state.
         match self.pipeline(source_id).parent_info {
             Some((parent_pipeline_id, subpage_id)) => {
+                self.handle_load_start_msg(&source_id);
                 // Message the constellation to find the script task for this iframe
                 // and issue an iframe load through there.
                 let parent_pipeline = self.pipeline(parent_pipeline_id);
                 let ScriptControlChan(ref script_channel) = parent_pipeline.script_chan;
                 script_channel.send(ConstellationControlMsg::Navigate(parent_pipeline_id,
                                                                       subpage_id,
                                                                       load_data)).unwrap();
             }
@@ -641,33 +644,57 @@ impl<LTF: LayoutTaskFactory, STF: Script
                 // Make sure no pending page would be overridden.
                 for frame_change in &self.pending_frames {
                     if frame_change.old_pipeline_id == Some(source_id) {
                         // id that sent load msg is being changed already; abort
                         return;
                     }
                 }
 
+                self.handle_load_start_msg(&source_id);
                 // Being here means either there are no pending frames, or none of the pending
                 // changes would be overridden by changing the subframe associated with source_id.
 
                 // Create the new pipeline
                 let window_rect = self.pipeline(source_id).rect;
                 let new_pipeline_id = self.new_pipeline(None, window_rect, None, load_data);
                 self.push_pending_frame(new_pipeline_id, Some(source_id));
 
                 // Send message to ScriptTask that will suspend all timers
                 let old_pipeline = self.pipelines.get(&source_id).unwrap();
                 old_pipeline.freeze();
             }
         }
     }
 
-    fn handle_load_complete_msg(&mut self) {
-        self.compositor_proxy.send(CompositorMsg::LoadComplete);
+    fn handle_load_start_msg(&mut self, pipeline_id: &PipelineId) {
+        let mut back;
+        let mut forward;
+        let frameid = self.pipeline_to_frame_map.get(pipeline_id);
+        match frameid {
+            Some(frame_id) => {
+                forward = if !self.frame(*frame_id).next.is_empty() { true }
+                          else { false };
+                back = if !self.frame(*frame_id).prev.is_empty() { true }
+                       else { false };
+            },
+            None => return
+        };
+        self.compositor_proxy.send(CompositorMsg::LoadStart(back, forward));
+    }
+
+    fn handle_load_complete_msg(&mut self, pipeline_id: &PipelineId) {
+        let frame_id = match self.pipeline_to_frame_map.get(pipeline_id) {
+            Some(frame) => *frame,
+            None => return
+        };
+
+        let forward = !self.mut_frame(frame_id).next.is_empty();
+        let back = !self.mut_frame(frame_id).prev.is_empty();
+        self.compositor_proxy.send(CompositorMsg::LoadComplete(back, forward));
         if let Some(ref reply_chan) = self.webdriver.load_channel {
             reply_chan.send(webdriver_traits::LoadComplete).unwrap();
         }
         self.webdriver.load_channel = None;
     }
 
     fn handle_navigate_msg(&mut self,
                            pipeline_info: Option<(PipelineId, SubpageId)>,
--- a/servo/components/compositing/headless.rs
+++ b/servo/components/compositing/headless.rs
@@ -92,17 +92,18 @@ impl CompositorEventListener for NullCom
             // we'll notice and think about whether it needs a response, like
             // SetFrameTree.
 
             Msg::InitializeLayersForPipeline(..) |
             Msg::SetLayerRect(..) |
             Msg::AssignPaintedBuffers(..) |
             Msg::ChangeRunningAnimationsState(..) |
             Msg::ScrollFragmentPoint(..) |
-            Msg::LoadComplete |
+            Msg::LoadStart(..) |
+            Msg::LoadComplete(..) |
             Msg::ScrollTimeout(..) |
             Msg::RecompositeAfterScroll |
             Msg::ChangePageTitle(..) |
             Msg::ChangePageUrl(..) |
             Msg::KeyEvent(..) |
             Msg::SetCursor(..) |
             Msg::ViewportConstrained(..) => {}
             Msg::CreatePng(..) |
--- a/servo/components/compositing/windowing.rs
+++ b/servo/components/compositing/windowing.rs
@@ -7,16 +7,17 @@
 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 msg::constellation_msg::{Key, KeyState, KeyModifiers};
+use net::net_error_list::NetError;
 use script_traits::MouseButton;
 use url::Url;
 use util::cursor::Cursor;
 use util::geometry::ScreenPx;
 use std::fmt::{Error, Formatter, Debug};
 use std::rc::Rc;
 
 #[derive(Clone)]
@@ -98,18 +99,22 @@ pub trait WindowMethods {
     fn size(&self) -> TypedSize2D<ScreenPx, f32>;
     /// Presents the window to the screen (perhaps by page flipping).
     fn present(&self);
 
     /// 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_url(&self, url: Url);
+    /// Called when the browser has started loading a frame.
+    fn load_start(&self, back: bool, forward: bool);
     /// Called when the browser is done loading a frame.
-    fn load_end(&self);
+    fn load_end(&self, back: bool, forward: bool);
+    /// Called when the browser encounters an error while loading a URL
+    fn load_error(&self, code: NetError, url: String);
 
     /// 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
--- a/servo/components/msg/constellation_msg.rs
+++ b/servo/components/msg/constellation_msg.rs
@@ -206,17 +206,17 @@ pub enum FocusType {
     Parent,     // Focusing a parent element (an iframe)
 }
 
 /// Messages from the compositor and script to the constellation.
 pub enum Msg {
     Exit,
     Failure(Failure),
     InitLoadUrl(Url),
-    LoadComplete,
+    LoadComplete(PipelineId),
     FrameRect(PipelineId, SubpageId, Rect<f32>),
     LoadUrl(PipelineId, LoadData),
     ScriptLoadedURLInIFrame(Url, PipelineId, SubpageId, Option<SubpageId>, IFrameSandboxState),
     Navigate(Option<(PipelineId, SubpageId)>, NavigationDirection),
     PainterReady(PipelineId),
     ResizedWindow(WindowSizeData),
     KeyEvent(Key, KeyState, KeyModifiers),
     /// Requests that the constellation inform the compositor of the title of the pipeline
--- a/servo/components/net/lib.rs
+++ b/servo/components/net/lib.rs
@@ -34,16 +34,17 @@ extern crate regex;
 
 pub mod about_loader;
 pub mod file_loader;
 pub mod http_loader;
 pub mod data_loader;
 pub mod cookie;
 pub mod cookie_storage;
 pub mod image_cache_task;
+pub mod net_error_list;
 pub mod pub_domains;
 pub mod resource_task;
 pub mod storage_task;
 pub mod mime_classifier;
 
 /// An implementation of the [Fetch spec](https://fetch.spec.whatwg.org/)
 pub mod fetch {
     #![allow(dead_code, unused)] // XXXManishearth this is only temporary until the Fetch mod starts being used
new file mode 100644
--- /dev/null
+++ b/servo/components/net/net_error_list.rs
@@ -0,0 +1,203 @@
+/* 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/. */
+
+// see https://github.com/adobe/chromium/blob/master/net/base/net_error_list.h
+#[allow(dead_code, non_camel_case_types)]
+pub enum NetError {
+    IO_PENDING = 1,
+    FAILED = 2,
+    ABORTED = 3,
+    INVALID_ARGUMENT = 4,
+    INVALID_HANDLE = 5,
+    FILE_NOT_FOUND = 6,
+    TIMED_OUT = 7,
+    FILE_TOO_BIG = 8,
+    UNEXPECTED = 9,
+    ACCESS_DENIED = 10,
+    NOT_IMPLEMENTED = 11,
+    INSUFFICIENT_RESOURCES = 12,
+    OUT_OF_MEMORY = 13,
+    UPLOAD_FILE_CHANGED = 14,
+    SOCKET_NOT_CONNECTED = 15,
+    FILE_EXISTS = 16,
+    FILE_PATH_TOO_LONG = 17,
+    FILE_NO_SPACE = 18,
+    FILE_VIRUS_INFECTED = 19,
+    BLOCKED_BY_CLIENT = 20,
+    NETWORK_CHANGED = 21,
+    BLOCKED_BY_ADMINISTRATOR = 22,
+    SOCKET_IS_CONNECTED = 23,
+    BLOCKED_ENROLLMENT_CHECK_PENDING = 24,
+    UPLOAD_STREAM_REWIND_NOT_SUPPORTED = 25,
+    CONNECTION_CLOSED = 100,
+    CONNECTION_RESET = 101,
+    CONNECTION_REFUSED = 102,
+    CONNECTION_ABORTED = 103,
+    CONNECTION_FAILED = 104,
+    NAME_NOT_RESOLVED = 105,
+    INTERNET_DISCONNECTED = 106,
+    SSL_PROTOCOL_ERROR = 107,
+    ADDRESS_INVALID = 108,
+    ADDRESS_UNREACHABLE = 109,
+    SSL_CLIENT_AUTH_CERT_NEEDED = 110,
+    TUNNEL_CONNECTION_FAILED = 111,
+    NO_SSL_VERSIONS_ENABLED = 112,
+    SSL_VERSION_OR_CIPHER_MISMATCH = 113,
+    SSL_RENEGOTIATION_REQUESTED = 114,
+    PROXY_AUTH_UNSUPPORTED = 115,
+    CERT_ERROR_IN_SSL_RENEGOTIATION = 116,
+    BAD_SSL_CLIENT_AUTH_CERT = 117,
+    CONNECTION_TIMED_OUT = 118,
+    HOST_RESOLVER_QUEUE_TOO_LARGE = 119,
+    SOCKS_CONNECTION_FAILED = 120,
+    SOCKS_CONNECTION_HOST_UNREACHABLE = 121,
+    NPN_NEGOTIATION_FAILED = 122,
+    SSL_NO_RENEGOTIATION = 123,
+    WINSOCK_UNEXPECTED_WRITTEN_BYTES = 124,
+    SSL_DECOMPRESSION_FAILURE_ALERT = 125,
+    SSL_BAD_RECORD_MAC_ALERT = 126,
+    PROXY_AUTH_REQUESTED = 127,
+    SSL_UNSAFE_NEGOTIATION = 128,
+    SSL_WEAK_SERVER_EPHEMERAL_DH_KEY = 129,
+    PROXY_CONNECTION_FAILED = 130,
+    MANDATORY_PROXY_CONFIGURATION_FAILED = 131,
+    PRECONNECT_MAX_SOCKET_LIMIT = 133,
+    SSL_CLIENT_AUTH_PRIVATE_KEY_ACCESS_DENIED = 134,
+    SSL_CLIENT_AUTH_CERT_NO_PRIVATE_KEY = 135,
+    PROXY_CERTIFICATE_INVALID = 136,
+    NAME_RESOLUTION_FAILED = 137,
+    NETWORK_ACCESS_DENIED = 138,
+    TEMPORARILY_THROTTLED = 139,
+    HTTPS_PROXY_TUNNEL_RESPONSE = 140,
+    SSL_CLIENT_AUTH_SIGNATURE_FAILED = 141,
+    MSG_TOO_BIG = 142,
+    SPDY_SESSION_ALREADY_EXISTS = 143,
+    WS_PROTOCOL_ERROR = 145,
+    ADDRESS_IN_USE = 147,
+    SSL_HANDSHAKE_NOT_COMPLETED = 148,
+    SSL_BAD_PEER_PUBLIC_KEY = 149,
+    SSL_PINNED_KEY_NOT_IN_CERT_CHAIN = 150,
+    CLIENT_AUTH_CERT_TYPE_UNSUPPORTED = 151,
+    ORIGIN_BOUND_CERT_GENERATION_TYPE_MISMATCH = 152,
+    SSL_DECRYPT_ERROR_ALERT = 153,
+    WS_THROTTLE_QUEUE_TOO_LARGE = 154,
+    SSL_SERVER_CERT_CHANGED = 156,
+    SSL_INAPPROPRIATE_FALLBACK = 157,
+    CT_NO_SCTS_VERIFIED_OK = 158,
+    SSL_UNRECOGNIZED_NAME_ALERT = 159,
+    SOCKET_SET_RECEIVE_BUFFER_SIZE_ERROR = 160,
+    SOCKET_SET_SEND_BUFFER_SIZE_ERROR = 161,
+    SOCKET_RECEIVE_BUFFER_SIZE_UNCHANGEABLE = 162,
+    SOCKET_SEND_BUFFER_SIZE_UNCHANGEABLE = 163,
+    SSL_CLIENT_AUTH_CERT_BAD_FORMAT = 164,
+    SSL_FALLBACK_BEYOND_MINIMUM_VERSION = 165,
+    CERT_COMMON_NAME_INVALID = 200,
+    CERT_DATE_INVALID = 201,
+    CERT_AUTHORITY_INVALID = 202,
+    CERT_CONTAINS_ERRORS = 203,
+    CERT_NO_REVOCATION_MECHANISM = 204,
+    CERT_UNABLE_TO_CHECK_REVOCATION = 205,
+    CERT_REVOKED = 206,
+    CERT_INVALID = 207,
+    CERT_WEAK_SIGNATURE_ALGORITHM = 208,
+    CERT_NON_UNIQUE_NAME = 210,
+    CERT_WEAK_KEY = 211,
+    CERT_NAME_CONSTRAINT_VIOLATION = 212,
+    CERT_VALIDITY_TOO_LONG = 213,
+    CERT_END = 214,
+    INVALID_URL = 300,
+    DISALLOWED_URL_SCHEME = 301,
+    UNKNOWN_URL_SCHEME = 302,
+    TOO_MANY_REDIRECTS = 310,
+    UNSAFE_REDIRECT = 311,
+    UNSAFE_PORT = 312,
+    INVALID_RESPONSE = 320,
+    INVALID_CHUNKED_ENCODING = 321,
+    METHOD_NOT_SUPPORTED = 322,
+    UNEXPECTED_PROXY_AUTH = 323,
+    EMPTY_RESPONSE = 324,
+    RESPONSE_HEADERS_TOO_BIG = 325,
+    PAC_STATUS_NOT_OK = 326,
+    PAC_SCRIPT_FAILED = 327,
+    REQUEST_RANGE_NOT_SATISFIABLE = 328,
+    MALFORMED_IDENTITY = 329,
+    CONTENT_DECODING_FAILED = 330,
+    NETWORK_IO_SUSPENDED = 331,
+    SYN_REPLY_NOT_RECEIVED = 332,
+    ENCODING_CONVERSION_FAILED = 333,
+    UNRECOGNIZED_FTP_DIRECTORY_LISTING_FORMAT = 334,
+    INVALID_SPDY_STREAM = 335,
+    NO_SUPPORTED_PROXIES = 336,
+    SPDY_PROTOCOL_ERROR = 337,
+    INVALID_AUTH_CREDENTIALS = 338,
+    UNSUPPORTED_AUTH_SCHEME = 339,
+    ENCODING_DETECTION_FAILED = 340,
+    MISSING_AUTH_CREDENTIALS = 341,
+    UNEXPECTED_SECURITY_LIBRARY_STATUS = 342,
+    MISCONFIGURED_AUTH_ENVIRONMENT = 343,
+    UNDOCUMENTED_SECURITY_LIBRARY_STATUS = 344,
+    RESPONSE_BODY_TOO_BIG_TO_DRAIN = 345,
+    RESPONSE_HEADERS_MULTIPLE_CONTENT_LENGTH = 346,
+    INCOMPLETE_SPDY_HEADERS = 347,
+    PAC_NOT_IN_DHCP = 348,
+    RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION = 349,
+    RESPONSE_HEADERS_MULTIPLE_LOCATION = 350,
+    SPDY_SERVER_REFUSED_STREAM = 351,
+    SPDY_PING_FAILED = 352,
+    CONTENT_LENGTH_MISMATCH = 354,
+    INCOMPLETE_CHUNKED_ENCODING = 355,
+    QUIC_PROTOCOL_ERROR = 356,
+    RESPONSE_HEADERS_TRUNCATED = 357,
+    QUIC_HANDSHAKE_FAILED = 358,
+    REQUEST_FOR_SECURE_RESOURCE_OVER_INSECURE_QUIC = 359,
+    SPDY_INADEQUATE_TRANSPORT_SECURITY = 360,
+    SPDY_FLOW_CONTROL_ERROR = 361,
+    SPDY_FRAME_SIZE_ERROR = 362,
+    SPDY_COMPRESSION_ERROR = 363,
+    PROXY_AUTH_REQUESTED_WITH_NO_CONNECTION = 364,
+    HTTP_1_1_REQUIRED = 365,
+    PROXY_HTTP_1_1_REQUIRED = 366,
+    CACHE_MISS = 400,
+    CACHE_READ_FAILURE = 401,
+    CACHE_WRITE_FAILURE = 402,
+    CACHE_OPERATION_NOT_SUPPORTED = 403,
+    CACHE_OPEN_FAILURE = 404,
+    CACHE_CREATE_FAILURE = 405,
+    CACHE_RACE = 406,
+    CACHE_CHECKSUM_READ_FAILURE = 407,
+    CACHE_CHECKSUM_MISMATCH = 408,
+    CACHE_LOCK_TIMEOUT = 409,
+    INSECURE_RESPONSE = 501,
+    NO_PRIVATE_KEY_FOR_CERT = 502,
+    ADD_USER_CERT_FAILED = 503,
+    FTP_FAILED = 601,
+    FTP_SERVICE_UNAVAILABLE = 602,
+    FTP_TRANSFER_ABORTED = 603,
+    FTP_FILE_BUSY = 604,
+    FTP_SYNTAX_ERROR = 605,
+    FTP_COMMAND_NOT_SUPPORTED = 606,
+    FTP_BAD_COMMAND_SEQUENCE = 607,
+    PKCS12_IMPORT_BAD_PASSWORD = 701,
+    PKCS12_IMPORT_FAILED = 702,
+    IMPORT_CA_CERT_NOT_CA = 703,
+    IMPORT_CERT_ALREADY_EXISTS = 704,
+    IMPORT_CA_CERT_FAILED = 705,
+    IMPORT_SERVER_CERT_FAILED = 706,
+    PKCS12_IMPORT_INVALID_MAC = 707,
+    PKCS12_IMPORT_INVALID_FILE = 708,
+    PKCS12_IMPORT_UNSUPPORTED = 709,
+    KEY_GENERATION_FAILED = 710,
+    ORIGIN_BOUND_CERT_GENERATION_FAILED = 711,
+    PRIVATE_KEY_EXPORT_FAILED = 712,
+    SELF_SIGNED_CERT_GENERATION_FAILED = 713,
+    CERT_DATABASE_CHANGED = 714,
+    CHANNEL_ID_IMPORT_FAILED = 715,
+    DNS_MALFORMED_RESPONSE = 800,
+    DNS_SERVER_REQUIRES_TCP = 801,
+    DNS_SERVER_FAILED = 802,
+    DNS_TIMED_OUT = 803,
+    DNS_CACHE_MISS = 804,
+    DNS_SEARCH_EMPTY = 805,
+    DNS_SORT_ERROR = 806,
+}
--- a/servo/components/script/script_task.rs
+++ b/servo/components/script/script_task.rs
@@ -955,17 +955,17 @@ impl ScriptTask {
         doc.mut_loader().inhibit_events();
 
         // https://html.spec.whatwg.org/multipage/#the-end step 7
         let addr: Trusted<Document> = Trusted::new(self.get_cx(), doc, self.chan.clone());
         let handler = box DocumentProgressHandler::new(addr.clone(), DocumentProgressTask::Load);
         self.chan.send(ScriptMsg::RunnableMsg(handler)).unwrap();
 
         let ConstellationChan(ref chan) = self.constellation_chan;
-        chan.send(ConstellationMsg::LoadComplete).unwrap();
+        chan.send(ConstellationMsg::LoadComplete(pipeline)).unwrap();
     }
 
     /// Handles a timer that fired.
     fn handle_fire_timer_msg(&self, id: PipelineId, timer_id: TimerId) {
         let page = self.root_page();
         let page = page.find(id).expect("ScriptTask: received fire timer msg for a
             pipeline ID not associated with this script task. This is a bug.");
         let window = page.window().root();
--- a/servo/components/servo/lib.rs
+++ b/servo/components/servo/lib.rs
@@ -163,15 +163,18 @@ fn create_constellation(opts: opts::Opts
         image_cache_task,
         font_cache_task,
         time_profiler_chan,
         mem_profiler_chan,
         devtools_chan,
         storage_task);
 
     // Send the URL command to the constellation.
-    {
-        let ConstellationChan(ref chan) = constellation_chan;
-        chan.send(ConstellationMsg::InitLoadUrl(opts.url.clone())).unwrap();
-    }
+    match opts.url {
+        Some(url) => {
+            let ConstellationChan(ref chan) = constellation_chan;
+            chan.send(ConstellationMsg::InitLoadUrl(url.clone())).unwrap();
+        },
+        None => ()
+    };
 
     constellation_chan
 }
--- a/servo/components/util/opts.rs
+++ b/servo/components/util/opts.rs
@@ -19,17 +19,17 @@ use std::io::{self, Write};
 use std::mem;
 use std::ptr;
 use url::{self, Url};
 
 /// Global flags for Servo, currently set on the command line.
 #[derive(Clone)]
 pub struct Opts {
     /// The initial URL to load.
-    pub url: Url,
+    pub url: Option<Url>,
 
     /// How many threads to use for CPU painting (`-t`).
     ///
     /// Note that painting is sequentialized when using GPU painting.
     pub paint_threads: usize,
 
     /// True to use GPU painting via Skia-GL, false to use CPU painting via Skia (`-g`). Note that
     /// compositing is always done on the GPU.
@@ -193,17 +193,17 @@ fn args_fail(msg: &str) {
 #[cfg(target_os="android")]
 static FORCE_CPU_PAINTING: bool = true;
 
 #[cfg(not(target_os="android"))]
 static FORCE_CPU_PAINTING: bool = false;
 
 pub fn default_opts() -> Opts {
     Opts {
-        url: Url::parse("about:blank").unwrap(),
+        url: Some(Url::parse("about:blank").unwrap()),
         paint_threads: 1,
         gpu_painting: false,
         tile_size: 512,
         device_pixels_per_px: None,
         time_profiler_period: None,
         mem_profiler_period: None,
         enable_experimental: false,
         layout_threads: 1,
@@ -365,17 +365,17 @@ pub fn from_cmdline_args(args: &[String]
             TypedSize2D(res[0], res[1])
         }
         None => {
             TypedSize2D(800, 600)
         }
     };
 
     let opts = Opts {
-        url: url,
+        url: Some(url),
         paint_threads: paint_threads,
         gpu_painting: gpu_painting,
         tile_size: tile_size,
         device_pixels_per_px: device_pixels_per_px,
         time_profiler_period: time_profiler_period,
         mem_profiler_period: mem_profiler_period,
         enable_experimental: opt_match.opt_present("e"),
         layout_threads: layout_threads,
--- a/servo/ports/cef/Cargo.lock
+++ b/servo/ports/cef/Cargo.lock
@@ -474,16 +474,17 @@ dependencies = [
  "compositing 0.0.1",
  "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)",
  "glutin 0.0.26 (git+https://github.com/servo/glutin?branch=servo)",
  "layers 0.1.0 (git+https://github.com/servo/rust-layers)",
  "libc 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
  "msg 0.0.1",
+ "net 0.0.1",
  "script_traits 0.0.1",
  "time 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)",
  "url 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
  "util 0.0.1",
  "x11 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
--- a/servo/ports/cef/browser.rs
+++ b/servo/ports/cef/browser.rs
@@ -5,16 +5,17 @@
 use browser_host::{ServoCefBrowserHost, ServoCefBrowserHostExtensions};
 use eutil::Downcast;
 use frame::{ServoCefFrame, ServoCefFrameExtensions};
 use interfaces::{CefBrowser, CefBrowserHost, CefClient, CefFrame, CefRequestContext};
 use interfaces::{cef_browser_t, cef_browser_host_t, cef_client_t, cef_frame_t};
 use interfaces::{cef_request_context_t};
 use servo::Browser;
 use types::{cef_browser_settings_t, cef_string_t, cef_window_info_t, cef_window_handle_t};
+use util::task::spawn_named;
 use window;
 use wrappers::CefWrap;
 
 use compositing::windowing::{WindowNavigateMsg, WindowEvent};
 use glutin_app;
 use libc::c_int;
 use std::cell::{Cell, RefCell, BorrowState};
 use std::ptr;
@@ -57,16 +58,28 @@ impl ServoBrowser {
 }
 
 cef_class_impl! {
     ServoCefBrowser : CefBrowser, cef_browser_t {
         fn get_host(&this,) -> *mut cef_browser_host_t {{
             this.downcast().host.clone()
         }}
 
+        fn can_go_back(&this,) -> c_int {{
+            this.downcast().back.get() as c_int
+        }}
+
+        fn can_go_forward(&this,) -> c_int {{
+            this.downcast().forward.get() as c_int
+        }}
+
+        fn is_loading(&this,) -> c_int {{
+            this.downcast().loading.get() as c_int
+        }}
+
         fn go_back(&this,) -> () {{
             this.send_window_event(WindowEvent::Navigation(WindowNavigateMsg::Back));
         }}
 
         fn go_forward(&this,) -> () {{
             this.send_window_event(WindowEvent::Navigation(WindowNavigateMsg::Forward));
         }}
 
@@ -87,16 +100,22 @@ pub struct ServoCefBrowser {
     /// A reference to the browser's host.
     pub host: CefBrowserHost,
     /// A reference to the browser client.
     pub client: CefClient,
     /// the glutin window when using windowed rendering
     pub window: Option<Rc<glutin_app::window::Window>>,
     /// Whether the on-created callback has fired yet.
     pub callback_executed: Cell<bool>,
+    /// whether the browser can navigate back
+    pub back: Cell<bool>,
+    /// whether the browser can navigate forward
+    pub forward: Cell<bool>,
+    /// whether the browser is loading
+    pub loading: Cell<bool>,
     /// the display system window handle: only to be used with host.get_window_handle()
     window_handle: cef_window_handle_t,
 
     id: isize,
     servo_browser: RefCell<ServoBrowser>,
     message_queue: RefCell<Vec<WindowEvent>>,
 }
 
@@ -127,16 +146,19 @@ impl ServoCefBrowser {
             frame: frame,
             host: host,
             client: client,
             window: glutin_window,
             callback_executed: Cell::new(false),
             servo_browser: RefCell::new(servo_browser),
             message_queue: RefCell::new(vec!()),
             id: id,
+            back: Cell::new(false),
+            forward: Cell::new(false),
+            loading: Cell::new(false),
             window_handle: window_handle,
         }
     }
 }
 
 pub trait ServoCefBrowserExtensions {
     fn init(&self, window_info: &cef_window_info_t);
     fn send_window_event(&self, event: WindowEvent);
@@ -238,31 +260,32 @@ pub fn browser_callback_after_created(br
         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);
+    browser.downcast().frame.load();
 }
 
 fn browser_host_create(window_info: &cef_window_info_t,
                        client: CefClient,
                        url: *const cef_string_t,
                        callback_executed: bool)
                        -> CefBrowser {
     let browser = ServoCefBrowser::new(window_info, client).as_cef_interface();
     browser.init(window_info);
+    if url != ptr::null() {
+       unsafe { browser.downcast().frame.set_url(CefWrap::to_rust(url)); }
+    }
     if callback_executed {
         browser_callback_after_created(browser.clone());
     }
-    //if url != ptr::null() {
-       //unsafe { browser.downcast().frame.load_url(CefWrap::to_rust(url)); }
-    //}
     BROWSERS.with(|browsers| {
         browsers.borrow_mut().push(browser.clone());
     });
     browser
 }
 
 cef_static_method_impls! {
     fn cef_browser_host_create_browser(window_info: *const cef_window_info_t,
@@ -270,16 +293,19 @@ cef_static_method_impls! {
                                        url: *const cef_string_t,
                                        _browser_settings: *const cef_browser_settings_t,
                                        _request_context: *mut cef_request_context_t)
                                        -> c_int {{
         let client: CefClient = client;
         let _browser_settings: &cef_browser_settings_t = _browser_settings;
         let _request_context: CefRequestContext = _request_context;
         browser_host_create(window_info, client, url, false);
+        spawn_named("async_browser_creation".to_owned(), move || {
+            window::app_wakeup();
+        });
         1i32
     }}
     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 {{
--- a/servo/ports/cef/browser_host.rs
+++ b/servo/ports/cef/browser_host.rs
@@ -1,36 +1,370 @@
 /* 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::{CefBrowser, CefBrowserHost, CefClient, cef_browser_t, cef_browser_host_t, cef_client_t};
 use types::{cef_mouse_button_type_t, cef_mouse_event, cef_rect_t, cef_key_event, cef_window_handle_t};
 use types::cef_key_event_type_t::{KEYEVENT_CHAR, KEYEVENT_KEYDOWN, KEYEVENT_KEYUP, KEYEVENT_RAWKEYDOWN};
+use types::cef_event_flags_t::{EVENTFLAG_ALT_DOWN, EVENTFLAG_CONTROL_DOWN, EVENTFLAG_SHIFT_DOWN};
 use browser::{self, ServoCefBrowserExtensions};
 use wrappers::CefWrap;
 
 use compositing::windowing::{WindowEvent, MouseWindowEvent};
 use geom::point::TypedPoint2D;
 use geom::size::TypedSize2D;
 use libc::{c_double, c_int};
 use msg::constellation_msg::{self, KeyModifiers, KeyState};
 use script_traits::MouseButton;
 use std::cell::{Cell, RefCell};
+use std::mem::transmute;
+use std::intrinsics;
 
 pub struct ServoCefBrowserHost {
     /// A reference to the browser.
     pub browser: RefCell<Option<CefBrowser>>,
     /// A reference to the client.
     pub client: CefClient,
     /// flag for return value of prepare_for_composite
     pub composite_ok: Cell<bool>,
 }
 
+// From blink ui/events/keycodes/keyboard_codes_posix.h.
+#[allow(dead_code)]
+enum KeyboardCode {
+  VKEY_BACK = 0x08,
+  VKEY_TAB = 0x09,
+  VKEY_BACKTAB = 0x0A,
+  VKEY_CLEAR = 0x0C,
+  VKEY_RETURN = 0x0D,
+  VKEY_SHIFT = 0x10,
+  VKEY_CONTROL = 0x11,
+  VKEY_MENU = 0x12,
+  VKEY_PAUSE = 0x13,
+  VKEY_CAPITAL = 0x14,
+  VKEY_KANA = 0x15,
+  //VKEY_HANGUL = 0x15,
+  VKEY_JUNJA = 0x17,
+  VKEY_FINAL = 0x18,
+  VKEY_HANJA = 0x19,
+  //VKEY_KANJI = 0x19,
+  VKEY_ESCAPE = 0x1B,
+  VKEY_CONVERT = 0x1C,
+  VKEY_NONCONVERT = 0x1D,
+  VKEY_ACCEPT = 0x1E,
+  VKEY_MODECHANGE = 0x1F,
+  VKEY_SPACE = 0x20,
+  VKEY_PRIOR = 0x21,
+  VKEY_NEXT = 0x22,
+  VKEY_END = 0x23,
+  VKEY_HOME = 0x24,
+  VKEY_LEFT = 0x25,
+  VKEY_UP = 0x26,
+  VKEY_RIGHT = 0x27,
+  VKEY_DOWN = 0x28,
+  VKEY_SELECT = 0x29,
+  VKEY_PRINT = 0x2A,
+  VKEY_EXECUTE = 0x2B,
+  VKEY_SNAPSHOT = 0x2C,
+  VKEY_INSERT = 0x2D,
+  VKEY_DELETE = 0x2E,
+  VKEY_HELP = 0x2F,
+  VKEY_0 = 0x30,
+  VKEY_1 = 0x31,
+  VKEY_2 = 0x32,
+  VKEY_3 = 0x33,
+  VKEY_4 = 0x34,
+  VKEY_5 = 0x35,
+  VKEY_6 = 0x36,
+  VKEY_7 = 0x37,
+  VKEY_8 = 0x38,
+  VKEY_9 = 0x39,
+  VKEY_A = 0x41,
+  VKEY_B = 0x42,
+  VKEY_C = 0x43,
+  VKEY_D = 0x44,
+  VKEY_E = 0x45,
+  VKEY_F = 0x46,
+  VKEY_G = 0x47,
+  VKEY_H = 0x48,
+  VKEY_I = 0x49,
+  VKEY_J = 0x4A,
+  VKEY_K = 0x4B,
+  VKEY_L = 0x4C,
+  VKEY_M = 0x4D,
+  VKEY_N = 0x4E,
+  VKEY_O = 0x4F,
+  VKEY_P = 0x50,
+  VKEY_Q = 0x51,
+  VKEY_R = 0x52,
+  VKEY_S = 0x53,
+  VKEY_T = 0x54,
+  VKEY_U = 0x55,
+  VKEY_V = 0x56,
+  VKEY_W = 0x57,
+  VKEY_X = 0x58,
+  VKEY_Y = 0x59,
+  VKEY_Z = 0x5A,
+  VKEY_LWIN = 0x5B,
+  VKEY_RWIN = 0x5C,
+  VKEY_APPS = 0x5D,
+  VKEY_SLEEP = 0x5F,
+  VKEY_NUMPAD0 = 0x60,
+  VKEY_NUMPAD1 = 0x61,
+  VKEY_NUMPAD2 = 0x62,
+  VKEY_NUMPAD3 = 0x63,
+  VKEY_NUMPAD4 = 0x64,
+  VKEY_NUMPAD5 = 0x65,
+  VKEY_NUMPAD6 = 0x66,
+  VKEY_NUMPAD7 = 0x67,
+  VKEY_NUMPAD8 = 0x68,
+  VKEY_NUMPAD9 = 0x69,
+  VKEY_MULTIPLY = 0x6A,
+  VKEY_ADD = 0x6B,
+  VKEY_SEPARATOR = 0x6C,
+  VKEY_SUBTRACT = 0x6D,
+  VKEY_DECIMAL = 0x6E,
+  VKEY_DIVIDE = 0x6F,
+  VKEY_F1 = 0x70,
+  VKEY_F2 = 0x71,
+  VKEY_F3 = 0x72,
+  VKEY_F4 = 0x73,
+  VKEY_F5 = 0x74,
+  VKEY_F6 = 0x75,
+  VKEY_F7 = 0x76,
+  VKEY_F8 = 0x77,
+  VKEY_F9 = 0x78,
+  VKEY_F10 = 0x79,
+  VKEY_F11 = 0x7A,
+  VKEY_F12 = 0x7B,
+  VKEY_F13 = 0x7C,
+  VKEY_F14 = 0x7D,
+  VKEY_F15 = 0x7E,
+  VKEY_F16 = 0x7F,
+  VKEY_F17 = 0x80,
+  VKEY_F18 = 0x81,
+  VKEY_F19 = 0x82,
+  VKEY_F20 = 0x83,
+  VKEY_F21 = 0x84,
+  VKEY_F22 = 0x85,
+  VKEY_F23 = 0x86,
+  VKEY_F24 = 0x87,
+  VKEY_NUMLOCK = 0x90,
+  VKEY_SCROLL = 0x91,
+  VKEY_LSHIFT = 0xA0,
+  VKEY_RSHIFT = 0xA1,
+  VKEY_LCONTROL = 0xA2,
+  VKEY_RCONTROL = 0xA3,
+  VKEY_LMENU = 0xA4,
+  VKEY_RMENU = 0xA5,
+  VKEY_BROWSER_BACK = 0xA6,
+  VKEY_BROWSER_FORWARD = 0xA7,
+  VKEY_BROWSER_REFRESH = 0xA8,
+  VKEY_BROWSER_STOP = 0xA9,
+  VKEY_BROWSER_SEARCH = 0xAA,
+  VKEY_BROWSER_FAVORITES = 0xAB,
+  VKEY_BROWSER_HOME = 0xAC,
+  VKEY_VOLUME_MUTE = 0xAD,
+  VKEY_VOLUME_DOWN = 0xAE,
+  VKEY_VOLUME_UP = 0xAF,
+  VKEY_MEDIA_NEXT_TRACK = 0xB0,
+  VKEY_MEDIA_PREV_TRACK = 0xB1,
+  VKEY_MEDIA_STOP = 0xB2,
+  VKEY_MEDIA_PLAY_PAUSE = 0xB3,
+  VKEY_MEDIA_LAUNCH_MAIL = 0xB4,
+  VKEY_MEDIA_LAUNCH_MEDIA_SELECT = 0xB5,
+  VKEY_MEDIA_LAUNCH_APP1 = 0xB6,
+  VKEY_MEDIA_LAUNCH_APP2 = 0xB7,
+  VKEY_OEM_1 = 0xBA,
+  VKEY_OEM_PLUS = 0xBB,
+  VKEY_OEM_COMMA = 0xBC,
+  VKEY_OEM_MINUS = 0xBD,
+  VKEY_OEM_PERIOD = 0xBE,
+  VKEY_OEM_2 = 0xBF,
+  VKEY_OEM_3 = 0xC0,
+  VKEY_OEM_4 = 0xDB,
+  VKEY_OEM_5 = 0xDC,
+  VKEY_OEM_6 = 0xDD,
+  VKEY_OEM_7 = 0xDE,
+  VKEY_OEM_8 = 0xDF,
+  VKEY_OEM_102 = 0xE2,
+  VKEY_OEM_103 = 0xE3,  // GTV KEYCODE_MEDIA_REWIND
+  VKEY_OEM_104 = 0xE4,  // GTV KEYCODE_MEDIA_FAST_FORWARD
+  VKEY_PROCESSKEY = 0xE5,
+  VKEY_PACKET = 0xE7,
+  VKEY_DBE_SBCSCHAR = 0xF3,
+  VKEY_DBE_DBCSCHAR = 0xF4,
+  VKEY_ATTN = 0xF6,
+  VKEY_CRSEL = 0xF7,
+  VKEY_EXSEL = 0xF8,
+  VKEY_EREOF = 0xF9,
+  VKEY_PLAY = 0xFA,
+  VKEY_ZOOM = 0xFB,
+  VKEY_NONAME = 0xFC,
+  VKEY_PA1 = 0xFD,
+  VKEY_OEM_CLEAR = 0xFE,
+  VKEY_UNKNOWN = 0,
+
+  // POSIX specific VKEYs. Note that as of Windows SDK 7.1, 0x97-9F, 0xD8-DA,
+  // and 0xE8 are unassigned.
+  VKEY_WLAN = 0x97,
+  VKEY_POWER = 0x98,
+  VKEY_BRIGHTNESS_DOWN = 0xD8,
+  VKEY_BRIGHTNESS_UP = 0xD9,
+  VKEY_KBD_BRIGHTNESS_DOWN = 0xDA,
+  VKEY_KBD_BRIGHTNESS_UP = 0xE8,
+
+  // Windows does not have a specific key code for AltGr. We use the unused 0xE1
+  // (VK_OEM_AX) code to represent AltGr, matching the behaviour of Firefox on
+  // Linux.
+  VKEY_ALTGR = 0xE1,
+  // Windows does not have a specific key code for Compose. We use the unused
+  // 0xE6 (VK_ICO_CLEAR) code to represent Compose.
+  VKEY_COMPOSE = 0xE6,
+}
+
+// this is way too much work to do 100% correctly right now.
+// see xkb_keyboard_layout_engine.cc -> XkbKeyboardLayoutEngine::Lookup in chromium for details
+fn get_key_msg(keycode: c_int, character: u16) -> Option<constellation_msg::Key> {
+    let code: KeyboardCode = unsafe { transmute(keycode as u8) };
+    match code {
+        KeyboardCode::VKEY_BACK => Some(constellation_msg::Key::Backspace),
+        KeyboardCode::VKEY_RIGHT => Some(constellation_msg::Key::Right),
+        KeyboardCode::VKEY_LEFT => Some(constellation_msg::Key::Left),
+        KeyboardCode::VKEY_UP => Some(constellation_msg::Key::Up),
+        KeyboardCode::VKEY_DOWN => Some(constellation_msg::Key::Down),
+        KeyboardCode::VKEY_RSHIFT => Some(constellation_msg::Key::RightShift),
+        KeyboardCode::VKEY_SHIFT | KeyboardCode::VKEY_LSHIFT => Some(constellation_msg::Key::LeftShift),
+        KeyboardCode::VKEY_RCONTROL => Some(constellation_msg::Key::RightControl),
+        KeyboardCode::VKEY_CONTROL | KeyboardCode::VKEY_LCONTROL => Some(constellation_msg::Key::LeftControl),
+        KeyboardCode::VKEY_LWIN => Some(constellation_msg::Key::LeftSuper),
+        KeyboardCode::VKEY_RWIN => Some(constellation_msg::Key::RightSuper),
+        KeyboardCode::VKEY_MENU => Some(constellation_msg::Key::LeftAlt),
+        KeyboardCode::VKEY_APPS => Some(constellation_msg::Key::Menu),
+        KeyboardCode::VKEY_ALTGR => Some(constellation_msg::Key::RightAlt), //not sure if correct...
+        KeyboardCode::VKEY_ESCAPE => Some(constellation_msg::Key::Escape),
+        KeyboardCode::VKEY_INSERT => Some(constellation_msg::Key::Insert),
+        KeyboardCode::VKEY_DELETE => Some(constellation_msg::Key::Delete),
+        KeyboardCode::VKEY_NEXT => Some(constellation_msg::Key::PageUp),
+        KeyboardCode::VKEY_PRIOR => Some(constellation_msg::Key::PageDown),
+        KeyboardCode::VKEY_HOME => Some(constellation_msg::Key::Home),
+        KeyboardCode::VKEY_END => Some(constellation_msg::Key::End),
+        KeyboardCode::VKEY_CAPITAL => Some(constellation_msg::Key::CapsLock),
+        KeyboardCode::VKEY_F1 => Some(constellation_msg::Key::F1),
+        KeyboardCode::VKEY_F2 => Some(constellation_msg::Key::F2),
+        KeyboardCode::VKEY_F3 => Some(constellation_msg::Key::F3),
+        KeyboardCode::VKEY_F4 => Some(constellation_msg::Key::F4),
+        KeyboardCode::VKEY_F5 => Some(constellation_msg::Key::F5),
+        KeyboardCode::VKEY_F6 => Some(constellation_msg::Key::F6),
+        KeyboardCode::VKEY_F7 => Some(constellation_msg::Key::F7),
+        KeyboardCode::VKEY_F8 => Some(constellation_msg::Key::F8),
+        KeyboardCode::VKEY_F9 => Some(constellation_msg::Key::F9),
+        KeyboardCode::VKEY_F10 => Some(constellation_msg::Key::F10),
+        KeyboardCode::VKEY_F11 => Some(constellation_msg::Key::F11),
+        KeyboardCode::VKEY_F12 => Some(constellation_msg::Key::F12),
+        KeyboardCode::VKEY_F13 => Some(constellation_msg::Key::F13),
+        KeyboardCode::VKEY_F14 => Some(constellation_msg::Key::F14),
+        KeyboardCode::VKEY_F15 => Some(constellation_msg::Key::F15),
+        KeyboardCode::VKEY_F16 => Some(constellation_msg::Key::F16),
+        KeyboardCode::VKEY_F17 => Some(constellation_msg::Key::F17),
+        KeyboardCode::VKEY_F18 => Some(constellation_msg::Key::F18),
+        KeyboardCode::VKEY_F19 => Some(constellation_msg::Key::F19),
+        KeyboardCode::VKEY_F20 => Some(constellation_msg::Key::F20),
+        KeyboardCode::VKEY_F21 => Some(constellation_msg::Key::F21),
+        KeyboardCode::VKEY_F22 => Some(constellation_msg::Key::F22),
+        KeyboardCode::VKEY_F23 => Some(constellation_msg::Key::F23),
+        KeyboardCode::VKEY_F24 => Some(constellation_msg::Key::F24),
+        KeyboardCode::VKEY_NUMPAD0 => Some(constellation_msg::Key::Kp0),
+        KeyboardCode::VKEY_NUMPAD1 => Some(constellation_msg::Key::Kp1),
+        KeyboardCode::VKEY_NUMPAD2 => Some(constellation_msg::Key::Kp2),
+        KeyboardCode::VKEY_NUMPAD3 => Some(constellation_msg::Key::Kp3),
+        KeyboardCode::VKEY_NUMPAD4 => Some(constellation_msg::Key::Kp4),
+        KeyboardCode::VKEY_NUMPAD5 => Some(constellation_msg::Key::Kp5),
+        KeyboardCode::VKEY_NUMPAD6 => Some(constellation_msg::Key::Kp6),
+        KeyboardCode::VKEY_NUMPAD7 => Some(constellation_msg::Key::Kp7),
+        KeyboardCode::VKEY_NUMPAD8 => Some(constellation_msg::Key::Kp8),
+        KeyboardCode::VKEY_NUMPAD9 => Some(constellation_msg::Key::Kp9),
+        KeyboardCode::VKEY_DECIMAL => Some(constellation_msg::Key::KpDecimal),
+        KeyboardCode::VKEY_DIVIDE => Some(constellation_msg::Key::KpDivide),
+        KeyboardCode::VKEY_MULTIPLY => Some(constellation_msg::Key::KpMultiply),
+        KeyboardCode::VKEY_SUBTRACT => Some(constellation_msg::Key::KpSubtract),
+        KeyboardCode::VKEY_ADD => Some(constellation_msg::Key::KpAdd),
+        KeyboardCode::VKEY_NUMLOCK => Some(constellation_msg::Key::NumLock),
+        KeyboardCode::VKEY_PRINT => Some(constellation_msg::Key::PrintScreen),
+        KeyboardCode::VKEY_PAUSE => Some(constellation_msg::Key::Pause),
+        //VKEY_BACK
+        _ => { match character as u8 {
+                 b'[' => Some(constellation_msg::Key::LeftBracket),
+                 b']' => Some(constellation_msg::Key::RightBracket),
+                 b'=' => Some(constellation_msg::Key::Equal),
+                 b';' => Some(constellation_msg::Key::Semicolon),
+                 b'/' => Some(constellation_msg::Key::Slash),
+                 b'.' => Some(constellation_msg::Key::Period),
+                 b'-' => Some(constellation_msg::Key::Minus),
+                 b',' => Some(constellation_msg::Key::Comma),
+                 b'\'' => Some(constellation_msg::Key::Apostrophe),
+                 b'\\' => Some(constellation_msg::Key::Backslash),
+                 b'`' => Some(constellation_msg::Key::GraveAccent),
+                 b'\t' => Some(constellation_msg::Key::Tab),
+                 b'a' | b'A' => Some(constellation_msg::Key::A),
+                 b'b' | b'B' => Some(constellation_msg::Key::B),
+                 b'c' | b'C' => Some(constellation_msg::Key::C),
+                 b'd' | b'D' => Some(constellation_msg::Key::D),
+                 b'e' | b'E' => Some(constellation_msg::Key::E),
+                 b'f' | b'F' => Some(constellation_msg::Key::F),
+                 b'g' | b'G' => Some(constellation_msg::Key::G),
+                 b'h' | b'H' => Some(constellation_msg::Key::H),
+                 b'i' | b'I' => Some(constellation_msg::Key::I),
+                 b'j' | b'J' => Some(constellation_msg::Key::J),
+                 b'k' | b'K' => Some(constellation_msg::Key::K),
+                 b'l' | b'L' => Some(constellation_msg::Key::L),
+                 b'm' | b'M' => Some(constellation_msg::Key::M),
+                 b'n' | b'N' => Some(constellation_msg::Key::N),
+                 b'o' | b'O' => Some(constellation_msg::Key::O),
+                 b'p' | b'P' => Some(constellation_msg::Key::P),
+                 b'q' | b'Q' => Some(constellation_msg::Key::Q),
+                 b'r' | b'R' => Some(constellation_msg::Key::R),
+                 b's' | b'S' => Some(constellation_msg::Key::S),
+                 b't' | b'T' => Some(constellation_msg::Key::T),
+                 b'u' | b'U' => Some(constellation_msg::Key::U),
+                 b'v' | b'V' => Some(constellation_msg::Key::V),
+                 b'w' | b'W' => Some(constellation_msg::Key::W),
+                 b'x' | b'X' => Some(constellation_msg::Key::X),
+                 b'y' | b'Y' => Some(constellation_msg::Key::Y),
+                 b'z' | b'Z' => Some(constellation_msg::Key::Z),
+                 b'0' => Some(constellation_msg::Key::Num0),
+                 b'1' => Some(constellation_msg::Key::Num1),
+                 b'2' => Some(constellation_msg::Key::Num2),
+                 b'3' => Some(constellation_msg::Key::Num3),
+                 b'4' => Some(constellation_msg::Key::Num4),
+                 b'5' => Some(constellation_msg::Key::Num5),
+                 b'6' => Some(constellation_msg::Key::Num6),
+                 b'7' => Some(constellation_msg::Key::Num7),
+                 b'8' => Some(constellation_msg::Key::Num8),
+                 b'9' => Some(constellation_msg::Key::Num9),
+                 b'\n' | b'\r' => Some(constellation_msg::Key::Enter),
+                 b' ' => Some(constellation_msg::Key::Space),
+                 _ => None
+             }
+        }
+    }
+}
+
+// unhandled
+//pub enum Key {
+    //World1,
+    //World2,
+    //ScrollLock,
+    //KpEnter,
+    //KpEqual,
+    //RightAlt,
+//}
+
 full_cef_class_impl! {
     ServoCefBrowserHost : CefBrowserHost, cef_browser_host_t {
         fn get_client(&this,) -> *mut cef_client_t {{
             this.downcast().client.clone()
         }}
         fn get_browser(&this,) -> *mut cef_browser_t {{
             let browser = this.downcast().browser.borrow_mut();
             browser.clone().unwrap()
@@ -62,65 +396,41 @@ full_cef_class_impl! {
         fn send_focus_event(&this, focus: c_int [c_int],) -> () {{
             let focus: c_int = focus;
             if focus != 0 {
                 this.downcast().send_window_event(WindowEvent::Refresh);
             }
         }}
 
         fn send_key_event(&this, event: *const cef_key_event [&cef_key_event],) -> () {{
-            // FIXME(pcwalton): So awful. But it's nearly midnight here and I have to get
-            // Google working.
             let event: &cef_key_event = event;
-            let key = match (*event).character as u8 {
-                b'a' | b'A' => constellation_msg::Key::A,
-                b'b' | b'B' => constellation_msg::Key::B,
-                b'c' | b'C' => constellation_msg::Key::C,
-                b'd' | b'D' => constellation_msg::Key::D,
-                b'e' | b'E' => constellation_msg::Key::E,
-                b'f' | b'F' => constellation_msg::Key::F,
-                b'g' | b'G' => constellation_msg::Key::G,
-                b'h' | b'H' => constellation_msg::Key::H,
-                b'i' | b'I' => constellation_msg::Key::I,
-                b'j' | b'J' => constellation_msg::Key::J,
-                b'k' | b'K' => constellation_msg::Key::K,
-                b'l' | b'L' => constellation_msg::Key::L,
-                b'm' | b'M' => constellation_msg::Key::M,
-                b'n' | b'N' => constellation_msg::Key::N,
-                b'o' | b'O' => constellation_msg::Key::O,
-                b'p' | b'P' => constellation_msg::Key::P,
-                b'q' | b'Q' => constellation_msg::Key::Q,
-                b'r' | b'R' => constellation_msg::Key::R,
-                b's' | b'S' => constellation_msg::Key::S,
-                b't' | b'T' => constellation_msg::Key::T,
-                b'u' | b'U' => constellation_msg::Key::U,
-                b'v' | b'V' => constellation_msg::Key::V,
-                b'w' | b'W' => constellation_msg::Key::W,
-                b'x' | b'X' => constellation_msg::Key::X,
-                b'y' | b'Y' => constellation_msg::Key::Y,
-                b'z' | b'Z' => constellation_msg::Key::Z,
-                b'0' => constellation_msg::Key::Num0,
-                b'1' => constellation_msg::Key::Num1,
-                b'2' => constellation_msg::Key::Num2,
-                b'3' => constellation_msg::Key::Num3,
-                b'4' => constellation_msg::Key::Num4,
-                b'5' => constellation_msg::Key::Num5,
-                b'6' => constellation_msg::Key::Num6,
-                b'7' => constellation_msg::Key::Num7,
-                b'8' => constellation_msg::Key::Num8,
-                b'9' => constellation_msg::Key::Num9,
-                b'\n' | b'\r' => constellation_msg::Key::Enter,
-                _ => constellation_msg::Key::Space,
+            let key = match get_key_msg((*event).windows_key_code, (*event).character) {
+                Some(keycode) => keycode,
+                None => {
+                    error!("Unhandled keycode({}) passed!", (*event).windows_key_code);
+                    return;
+                }
             };
             let key_state = match (*event).t {
-                KEYEVENT_RAWKEYDOWN => KeyState::Pressed,
-                KEYEVENT_KEYDOWN | KEYEVENT_CHAR => KeyState::Repeated,
+                // in tests with cef-real, this event had no effect
+                KEYEVENT_RAWKEYDOWN => return,
+                KEYEVENT_KEYDOWN => KeyState::Pressed,
+                KEYEVENT_CHAR => KeyState::Repeated,
                 KEYEVENT_KEYUP => KeyState::Released,
             };
-            let key_modifiers = KeyModifiers::empty();  // TODO(pcwalton)
+            let mut key_modifiers = KeyModifiers::empty();
+            if (*event).modifiers & unsafe { intrinsics::discriminant_value(&EVENTFLAG_SHIFT_DOWN) as u32 } != 0 {
+               key_modifiers = key_modifiers | constellation_msg::SHIFT;
+            }
+            if (*event).modifiers & unsafe { intrinsics::discriminant_value(&EVENTFLAG_CONTROL_DOWN) as u32 } != 0 {
+               key_modifiers = key_modifiers | constellation_msg::CONTROL;
+            }
+            if (*event).modifiers & unsafe { intrinsics::discriminant_value(&EVENTFLAG_ALT_DOWN) as u32 } != 0 {
+               key_modifiers = key_modifiers | constellation_msg::ALT;
+            }
             this.downcast().send_window_event(WindowEvent::KeyEvent(key, key_state, key_modifiers))
         }}
 
         fn send_mouse_click_event(&this,
                                   event: *const cef_mouse_event [&cef_mouse_event],
                                   mouse_button_type: cef_mouse_button_type_t [cef_mouse_button_type_t],
                                   mouse_up: c_int [c_int],
                                   _click_count: c_int [c_int],)
--- a/servo/ports/cef/core.rs
+++ b/servo/ports/cef/core.rs
@@ -7,51 +7,58 @@ use interfaces::cef_app_t;
 use types::{cef_main_args_t, cef_settings_t};
 use window::init_window;
 
 use libc::{c_char, c_int, c_void};
 use util::opts;
 use std::ffi;
 use std::str;
 use browser;
-use std_url::Url;
 
 const MAX_RENDERING_THREADS: usize = 128;
 
-static HOME_URL: &'static str = "http://s27.postimg.org/vqbtrolyr/servo.jpg";
+//static HOME_URL: &'static str = "http://s27.postimg.org/vqbtrolyr/servo.jpg";
 
 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";
 
+pub static mut CEF_APP: *mut cef_app_t = 0 as *mut cef_app_t;
+
 
 #[no_mangle]
 pub extern "C" fn cef_initialize(args: *const cef_main_args_t,
                                  settings: *mut cef_settings_t,
                                  application: *mut cef_app_t,
                                  _windows_sandbox_info: *const c_void)
                                  -> c_int {
     if args.is_null() {
         return 0;
     }
+    unsafe {
+        if !CEF_APP.is_null() {
+            panic!("Attempting to call cef_initialize() multiple times!");
+        }
+    }
 
     unsafe {
         command_line_init((*args).argc, (*args).argv);
 
         if !application.is_null() {
             (*application).get_browser_process_handler.map(|cb| {
                     let handler = cb(application);
                     if !handler.is_null() {
                         (*handler).on_context_initialized.map(|hcb| hcb(handler));
                     }
             });
+            CEF_APP = application;
         }
     }
 
     let rendering_threads = unsafe {
         if ((*settings).rendering_threads as usize) < 1 {
             1
         } else if (*settings).rendering_threads as usize > MAX_RENDERING_THREADS {
             MAX_RENDERING_THREADS
@@ -62,17 +69,17 @@ pub extern "C" fn cef_initialize(args: *
 
     let mut temp_opts = opts::default_opts();
     temp_opts.paint_threads = rendering_threads;
     temp_opts.layout_threads = rendering_threads;
     temp_opts.headless = false;
     temp_opts.hard_fail = false;
     temp_opts.enable_text_antialiasing = true;
     temp_opts.resources_path = None;
-    temp_opts.url = Url::parse(HOME_URL).unwrap();
+    temp_opts.url = None;
     opts::set(temp_opts);
 
     if unsafe { (*settings).windowless_rendering_enabled != 0 } {
         init_window();
     }
 
     return 1
 }
--- a/servo/ports/cef/eutil.rs
+++ b/servo/ports/cef/eutil.rs
@@ -72,11 +72,12 @@ extern "C" fn servo_release(object: *mut
 unsafe fn servo_free(object: *mut cef_base_t) {
     libc::free(object as *mut c_void);
 }
 
 pub unsafe fn add_ref(c_object: *mut cef_base_t) {
     ((*c_object).add_ref.unwrap())(c_object);
 }
 
+#[no_mangle]
 pub extern "C" fn servo_test() -> c_int {
     1
 }
--- a/servo/ports/cef/frame.rs
+++ b/servo/ports/cef/frame.rs
@@ -48,15 +48,25 @@ full_cef_class_impl! {
             *this.title_visitor.borrow_mut() = Some(visitor);
             this.browser.borrow().as_ref().unwrap().get_title_for_main_frame();
         }}
     }
 }
 
 pub trait ServoCefFrameExtensions {
     fn set_browser(&self, browser: CefBrowser);
+    fn set_url(&self, url: &[u16]);
+    fn load(&self);
 }
 
 impl ServoCefFrameExtensions for CefFrame {
     fn set_browser(&self, browser: CefBrowser) {
         *self.downcast().browser.borrow_mut() = Some(browser)
     }
+    fn set_url(&self, url: &[u16]) {
+        let frame = self.downcast();
+        *frame.url.borrow_mut() = String::from_utf16(url).unwrap();
+    }
+    fn load(&self) {
+        let event = WindowEvent::LoadUrl(self.downcast().url.borrow().clone());
+        self.downcast().browser.borrow_mut().as_mut().unwrap().send_window_event(event);
+    }
 }
--- a/servo/ports/cef/interfaces/cef_browser_process_handler.rs
+++ b/servo/ports/cef/interfaces/cef_browser_process_handler.rs
@@ -90,16 +90,22 @@ pub struct _cef_browser_process_handler_
   //
   // Return the handler for printing on Linux. If a print handler is not
   // provided then printing will not be supported on the Linux platform.
   //
   pub get_print_handler: Option<extern "C" fn(
       this: *mut cef_browser_process_handler_t) -> *mut interfaces::cef_print_handler_t>,
 
   //
+  // Called when the application should call cef_do_message_loop_work()
+  //
+  pub on_work_available: Option<extern "C" fn(
+      this: *mut cef_browser_process_handler_t) -> ()>,
+
+  //
   // The reference count. This will only be present for Rust instances!
   //
   pub ref_count: u32,
 
   //
   // Extra data. This will only be present for Rust instances!
   //
   pub extra: u8,
@@ -248,16 +254,31 @@ impl CefBrowserProcessHandler {
       panic!("called a CEF method on a null object")
     }
     unsafe {
       CefWrap::to_rust(
         ((*self.c_object).get_print_handler.unwrap())(
           self.c_object))
     }
   }
+
+  //
+  // Called when the application should call cef_do_message_loop_work()
+  //
+  pub fn on_work_available(&self) -> () {
+    if self.c_object.is_null() ||
+       self.c_object as usize == mem::POST_DROP_USIZE {
+      panic!("called a CEF method on a null object")
+    }
+    unsafe {
+      CefWrap::to_rust(
+        ((*self.c_object).on_work_available.unwrap())(
+          self.c_object))
+    }
+  }
 } 
 
 impl CefWrap<*mut cef_browser_process_handler_t> for CefBrowserProcessHandler {
   fn to_c(rust_object: CefBrowserProcessHandler) -> *mut cef_browser_process_handler_t {
     rust_object.c_object_addrefed()
   }
   unsafe fn to_rust(c_object: *mut cef_browser_process_handler_t) -> CefBrowserProcessHandler {
     CefBrowserProcessHandler::from_c_object_addref(c_object)
--- a/servo/ports/cef/lib.rs
+++ b/servo/ports/cef/lib.rs
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #![feature(alloc)]
 #![feature(box_syntax)]
 #![feature(plugin)]
 #![feature(link_args)]
 #![feature(thread_local)]
 #![feature(core)]
+#![feature(convert)]
 #![feature(std_misc)]
 #![feature(rustc_private)]
 #![feature(collections)]
 #![feature(negate_unsigned)]
 #![feature(unicode)]
 #![feature(unsafe_no_drop_flag, filling_drop)]
 #![allow(non_camel_case_types)]
 
--- a/servo/ports/cef/types.rs
+++ b/servo/ports/cef/types.rs
@@ -4,16 +4,18 @@
 
 use libc::{c_uint, c_ushort, c_int, c_double, size_t};
 #[cfg(target_os="linux")]
 use libc::c_ulong;
 #[cfg(target_os="macos")]
 use libc::c_void;
 use libc::types::os::arch::c95::wchar_t;
 
+use net::net_error_list::NetError;
+
 pub use self::cef_rect as cef_rect_t;
 
 pub enum cef_string_map_t {}
 pub enum cef_string_multimap_t {}
 pub enum cef_string_list_t {}
 pub enum cef_text_input_context_t {}
 pub enum cef_event_handle_t {}
 
@@ -271,71 +273,17 @@ pub enum cef_urlrequest_status_t {
   //
   // 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,
-  ERR_FILE_TOO_BIG = -8,
-  ERR_UNEXPECTED = -9,
-  ERR_ACCESS_DENIED = -10,
-  ERR_NOT_IMPLEMENTED = -11,
-  ERR_CONNECTION_CLOSED = -100,
-  ERR_CONNECTION_RESET = -101,
-  ERR_CONNECTION_REFUSED = -102,
-  ERR_CONNECTION_ABORTED = -103,
-  ERR_CONNECTION_FAILED = -104,
-  ERR_NAME_NOT_RESOLVED = -105,
-  ERR_INTERNET_DISCONNECTED = -106,
-  ERR_SSL_PROTOCOL_ERROR = -107,
-  ERR_ADDRESS_INVALID = -108,
-  ERR_ADDRESS_UNREACHABLE = -109,
-  ERR_SSL_CLIENT_AUTH_CERT_NEEDED = -110,
-  ERR_TUNNEL_CONNECTION_FAILED = -111,
-  ERR_NO_SSL_VERSIONS_ENABLED = -112,
-  ERR_SSL_VERSION_OR_CIPHER_MISMATCH = -113,
-  ERR_SSL_RENEGOTIATION_REQUESTED = -114,
-  ERR_CERT_COMMON_NAME_INVALID = -200,
-  ERR_CERT_DATE_INVALID = -201,
-  ERR_CERT_AUTHORITY_INVALID = -202,
-  ERR_CERT_CONTAINS_ERRORS = -203,
-  ERR_CERT_NO_REVOCATION_MECHANISM = -204,
-  ERR_CERT_UNABLE_TO_CHECK_REVOCATION = -205,
-  ERR_CERT_REVOKED = -206,
-  ERR_CERT_INVALID = -207,
-  ERR_CERT_END = -208,
-  ERR_INVALID_URL = -300,
-  ERR_DISALLOWED_URL_SCHEME = -301,
-  ERR_UNKNOWN_URL_SCHEME = -302,
-  ERR_TOO_MANY_REDIRECTS = -310,
-  ERR_UNSAFE_REDIRECT = -311,
-  ERR_UNSAFE_PORT = -312,
-  ERR_INVALID_RESPONSE = -320,
-  ERR_INVALID_CHUNKED_ENCODING = -321,
-  ERR_METHOD_NOT_SUPPORTED = -322,
-  ERR_UNEXPECTED_PROXY_AUTH = -323,
-  ERR_EMPTY_RESPONSE = -324,
-  ERR_RESPONSE_HEADERS_TOO_BIG = -325,
-  ERR_CACHE_MISS = -400,
-  ERR_INSECURE_RESPONSE = -501,
-}
+pub type cef_errorcode_t = NetError;
 
 
 //
 // Key event types.
 //
 pub enum cef_key_event_type_t {
   KEYEVENT_RAWKEYDOWN = 0,
   KEYEVENT_KEYDOWN,
--- a/servo/ports/cef/window.rs
+++ b/servo/ports/cef/window.rs
@@ -2,32 +2,34 @@
  * 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 (Glutin).
 
+use core::CEF_APP;
 use eutil::Downcast;
-use interfaces::CefBrowser;
+use interfaces::{CefApp, CefBrowser};
 use render_handler::CefRenderHandlerExtensions;
 use rustc_unicode::str::Utf16Encoder;
 use types::{cef_cursor_handle_t, cef_cursor_type_t, cef_rect_t};
 use wrappers::CefWrap;
 
 use compositing::compositor_task::{self, CompositorProxy, CompositorReceiver};
 use compositing::windowing::{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_int, c_void};
+use libc::{c_char, c_void};
 use msg::constellation_msg::{Key, KeyModifiers};
+use net::net_error_list::NetError;
 use std::ptr;
 use std_url::Url;
 use util::cursor::Cursor;
 use util::geometry::ScreenPx;
 use std::cell::RefCell;
 use std::ffi::CString;
 use std::rc::Rc;
 use std::sync::mpsc::{Sender, channel};
@@ -309,66 +311,126 @@ impl WindowMethods for Window {
                         browser.get_host().get_client().get_render_handler().paint(browser.clone(), width, height);
                     }
                     false
                 }
             }
         }
     }
 
-    fn load_end(&self) {
+    fn load_start(&self, back: bool, forward: bool) {
+        // 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.downcast().loading.set(true);
+        browser.downcast().back.set(back);
+        browser.downcast().forward.set(forward);
+        if check_ptr_exist!(browser.get_host().get_client(), get_load_handler) &&
+           check_ptr_exist!(browser.get_host().get_client().get_load_handler(), on_loading_state_change) {
+            browser.get_host()
+                   .get_client()
+                   .get_load_handler()
+                   .on_loading_state_change((*browser).clone(), 1i32, back as i32, forward as i32);
+        }
+    }
+
+    fn load_end(&self, back: bool, forward: bool) {
         // 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.downcast().loading.set(false);
+        browser.downcast().back.set(back);
+        browser.downcast().forward.set(forward);
+        if check_ptr_exist!(browser.get_host().get_client(), get_load_handler) &&
+           check_ptr_exist!(browser.get_host().get_client().get_load_handler(), on_loading_state_change) {
+            browser.get_host()
+                   .get_client()
+                   .get_load_handler()
+                   .on_loading_state_change((*browser).clone(), 0i32, back as i32, forward as i32);
+        }
         if check_ptr_exist!(browser.get_host().get_client(), get_load_handler) &&
            check_ptr_exist!(browser.get_host().get_client().get_load_handler(), on_load_end) {
             browser.get_host()
                    .get_client()
                    .get_load_handler()
                    .on_load_end((*browser).clone(), browser.get_main_frame(), 200);
         }
     }
 
+    fn load_error(&self, code: NetError, url: String) {
+        let browser = self.cef_browser.borrow();
+        let browser = match *browser {
+            None => return,
+            Some(ref browser) => browser,
+        };
+        if check_ptr_exist!(browser.get_host().get_client(), get_load_handler) &&
+           check_ptr_exist!(browser.get_host().get_client().get_load_handler(), on_load_error) {
+            let utf16_chars: Vec<u16> = Utf16Encoder::new((url).chars()).collect();
+            browser.get_host()
+                   .get_client()
+                   .get_load_handler()
+                   .on_load_error((*browser).clone(), browser.get_main_frame(),
+                   code, &[], utf16_chars.as_slice());
+        }
+    }
+
     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();
+        let str = match string {
+            Some(s) => {
+                let utf16_chars: Vec<u16> = Utf16Encoder::new(s.chars()).collect();
+                utf16_chars
+            }
+            None => vec![]
+        };
+
+        if check_ptr_exist!(browser.get_host().get_client(), get_display_handler) &&
+           check_ptr_exist!(browser.get_host().get_client().get_display_handler(), on_title_change) {
+            browser.get_host().get_client().get_display_handler().on_title_change((*browser).clone(), str.as_slice());
+        }
         match &mut *title_visitor {
-            &mut None => {}
+            &mut None => {},
             &mut 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)
-                    }
-                }
+                visitor.visit(&str);
             }
-        }
+        };
     }
 
     fn set_page_url(&self, url: Url) {
+        // it seems to be the case that load start is always called
+        // IMMEDIATELY before address change, so just stick it here
+        on_load_start(self);
         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 servoframe = frame.downcast();
         // FIXME(https://github.com/rust-lang/rust/issues/23338)
-        let mut frame_url = frame.url.borrow_mut();
-        *frame_url = url.to_string()
+        let mut frame_url = servoframe.url.borrow_mut();
+        *frame_url = url.to_string();
+        let utf16_chars: Vec<u16> = Utf16Encoder::new((*frame_url).chars()).collect();
+        if check_ptr_exist!(browser.get_host().get_client(), get_display_handler) &&
+           check_ptr_exist!(browser.get_host().get_client().get_display_handler(), on_address_change) {
+            browser.get_host().get_client().get_display_handler().on_address_change((*browser).clone(), frame.clone(), utf16_chars.as_slice());
+        }
     }
 
     fn handle_key(&self, _: Key, _: KeyModifiers) {
         // TODO(negge)
     }
 
     fn set_cursor(&self, cursor: Cursor) {
         use types::{CefCursorInfo,cef_point_t,cef_size_t};
@@ -391,57 +453,79 @@ impl WindowMethods for Window {
     }
 }
 
 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};
-        use cocoa::appkit::{NSEvent, NSEventModifierFlags, NSEventSubtype};
-        use cocoa::base::nil;
-        use cocoa::foundation::{NSAutoreleasePool, NSPoint};
-
-        // Send a message and kick the OS event loop awake.
-        self.sender.send(msg).unwrap();
-
-        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),
-                NSEventModifierFlags::empty(),
-                0.0,
-                0,
-                nil,
-                NSEventSubtype::NSWindowExposedEventType,
-                0,
-                0);
-            NSApp().postEvent_atStart_(event, 0);
-            pool.drain();
-        }
-    }
-
-    #[cfg(target_os="linux")]
     fn send(&mut self, msg: compositor_task::Msg) {
         self.sender.send(msg).unwrap();
+        app_wakeup();
     }
 
     fn clone_compositor_proxy(&self) -> Box<CompositorProxy+Send> {
         box CefCompositorProxy {
             sender: self.sender.clone(),
         } as Box<CompositorProxy+Send>
     }
 }
 
+fn on_load_start(window: &Window) {
+    let browser = window.cef_browser.borrow();
+    let browser = match *browser {
+        None => return,
+        Some(ref browser) => browser,
+    };
+    if check_ptr_exist!(browser.get_host().get_client(), get_load_handler) &&
+       check_ptr_exist!(browser.get_host().get_client().get_load_handler(), on_load_start) {
+        browser.get_host()
+               .get_client()
+               .get_load_handler()
+               .on_load_start((*browser).clone(), browser.get_main_frame());
+    }
+}
+
+#[cfg(target_os="macos")]
+pub fn app_wakeup() {
+    use cocoa::appkit::{NSApp, NSApplication, NSApplicationDefined};
+    use cocoa::appkit::{NSEvent, NSEventModifierFlags, NSEventSubtype};
+    use cocoa::base::nil;
+    use cocoa::foundation::{NSAutoreleasePool, NSPoint};
+
+    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),
+            NSEventModifierFlags::empty(),
+            0.0,
+            0,
+            nil,
+            NSEventSubtype::NSWindowExposedEventType,
+            0,
+            0);
+        NSApp().postEvent_atStart_(event, 0);
+        pool.drain();
+    }
+}
+
+#[cfg(target_os="linux")]
+pub fn app_wakeup() {
+    unsafe { if CEF_APP.is_null() { return; } }
+    let capp = unsafe { CefApp::from_c_object_addref(CEF_APP) };
+    if unsafe { (*CEF_APP).get_browser_process_handler.is_some() } &&
+       check_ptr_exist!(capp.get_browser_process_handler(), on_work_available) {
+        capp.get_browser_process_handler().on_work_available();
+    }
+}
+
 #[cfg(target_os="linux")]
 pub fn init_window() {
     unsafe {
         assert!(XInitThreads() != 0);
         DISPLAY = XOpenDisplay(ptr::null()) as *mut c_void;
     }
 }
 #[cfg(not(target_os="linux"))]
--- a/servo/ports/cef/wrappers.rs
+++ b/servo/ports/cef/wrappers.rs
@@ -1,13 +1,13 @@
 /* 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::{cef_drag_data_t, cef_post_data_element_t, cef_v8value_t, CefPostDataElement};
+use interfaces::{cef_app_t, CefApp, cef_drag_data_t, cef_post_data_element_t, cef_v8value_t, CefPostDataElement};
 use interfaces::{CefV8Value};
 use interfaces::{cef_download_handler_t, cef_drag_handler_t, cef_context_menu_handler_t};
 use interfaces::{cef_dialog_handler_t, cef_focus_handler_t};
 use interfaces::{cef_load_handler_t, cef_request_handler_t};
 use interfaces::{cef_geolocation_handler_t, cef_jsdialog_handler_t, cef_keyboard_handler_t};
 use rustc_unicode::str::Utf16Encoder;
 use types::{cef_base_t, cef_browser_settings_t, CefBrowserSettings, cef_color_model_t};
 use types::{cef_context_menu_edit_state_flags_t};
@@ -93,16 +93,17 @@ macro_rules! cef_unimplemented_wrapper(
         }
     )
 );
 
 cef_pointer_wrapper!(());
 cef_pointer_wrapper!(*mut ());
 cef_pointer_wrapper!(*mut c_void);
 cef_pointer_wrapper!(c_void);
+cef_pointer_wrapper!(cef_app_t);
 cef_pointer_wrapper!(cef_base_t);
 cef_pointer_wrapper!(cef_browser_settings_t);
 cef_pointer_wrapper!(cef_cookie_t);
 cef_pointer_wrapper!(cef_cursor_info_t);
 cef_pointer_wrapper!(cef_geoposition_t);
 cef_pointer_wrapper!(cef_key_event);
 cef_pointer_wrapper!(cef_mouse_event);
 cef_pointer_wrapper!(cef_page_range_t);
@@ -132,16 +133,17 @@ cef_noop_wrapper!(*mut cef_focus_handler
 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_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!(CefApp);
 cef_noop_wrapper!(CefBrowserSettings);
 cef_noop_wrapper!(CefScreenInfo);
 cef_noop_wrapper!(CefRequestContextSettings);
 cef_noop_wrapper!(CefCursorInfo);
 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);
--- a/servo/ports/glutin/Cargo.toml
+++ b/servo/ports/glutin/Cargo.toml
@@ -21,16 +21,19 @@ path = "../../components/script_traits"
 git = "https://github.com/servo/rust-geom"
 
 [dependencies.layers]
 git = "https://github.com/servo/rust-layers"
 
 [dependencies.msg]
 path = "../../components/msg"
 
+[dependencies.net]
+path = "../../components/net"
+
 [dependencies.util]
 path = "../../components/util"
 
 [dependencies.glutin]
 git = "https://github.com/servo/glutin"
 branch = "servo"
 
 [dependencies.gleam]
--- a/servo/ports/glutin/lib.rs
+++ b/servo/ports/glutin/lib.rs
@@ -11,16 +11,17 @@
 extern crate cgl;
 extern crate compositing;
 extern crate geom;
 extern crate gleam;
 extern crate glutin;
 extern crate layers;
 extern crate libc;
 extern crate msg;
+extern crate net;
 #[cfg(feature = "window")] extern crate script_traits;
 extern crate time;
 extern crate util;
 extern crate egl;
 extern crate url;
 #[cfg(target_os="linux")] extern crate x11;
 
 use compositing::windowing::WindowEvent;
--- a/servo/ports/glutin/window.rs
+++ b/servo/ports/glutin/window.rs
@@ -9,16 +9,17 @@ use compositing::windowing::{WindowEvent
 use geom::scale_factor::ScaleFactor;
 use geom::size::TypedSize2D;
 use gleam::gl;
 use glutin;
 use layers::geometry::DevicePixel;
 use layers::platform::surface::NativeGraphicsMetadata;
 use msg::constellation_msg;
 use msg::constellation_msg::Key;
+use net::net_error_list::NetError;
 use std::mem;
 use std::rc::Rc;
 use std::sync::mpsc::{channel, Sender};
 use url::Url;
 use util::cursor::Cursor;
 use util::geometry::ScreenPx;
 
 use NestedEventLoopListener;
@@ -500,17 +501,23 @@ impl WindowMethods for Window {
         };
         let title = format!("{} - Servo", title);
         self.window.set_title(&title);
     }
 
     fn set_page_url(&self, _: Url) {
     }
 
-    fn load_end(&self) {
+    fn load_start(&self, _: bool, _: bool) {
+    }
+
+    fn load_end(&self, _: bool, _: bool) {
+    }
+
+    fn load_error(&self, _: NetError, _: String) {
     }
 
     /// Has no effect on Android.
     fn set_cursor(&self, c: Cursor) {
         use glutin::MouseCursor;
 
         let glutin_cursor = match c {
             Cursor::NoCursor => MouseCursor::NoneCursor,
@@ -681,17 +688,21 @@ impl WindowMethods for Window {
     }
 
     fn set_page_title(&self, _: Option<String>) {
     }
 
     fn set_page_url(&self, _: Url) {
     }
 
-    fn load_end(&self) {
+    fn load_start(&self, _: bool, _: bool) {
+    }
+    fn load_end(&self, _: bool, _: bool) {
+    }
+    fn load_error(&self, _: NetError, _: String) {
     }
 
     fn set_cursor(&self, _: Cursor) {
     }
 
     fn prepare_for_composite(&self, _width: usize, _height: usize) -> bool {
         true
     }
--- a/servo/ports/gonk/src/window.rs
+++ b/servo/ports/gonk/src/window.rs
@@ -7,16 +7,17 @@
 use compositing::compositor_task::{self, CompositorProxy, CompositorReceiver};
 use compositing::windowing::{WindowEvent, WindowMethods};
 use geom::scale_factor::ScaleFactor;
 use geom::size::TypedSize2D;
 use layers::geometry::DevicePixel;
 use layers::platform::surface::NativeGraphicsMetadata;
 use libc::c_int;
 use msg::constellation_msg::{Key, KeyModifiers};
+use net::net_error_list::NetError;
 use std::sync::mpsc::{channel, Sender, Receiver};
 use std::rc::Rc;
 use std::mem::transmute;
 use std::mem::size_of;
 use std::mem::zeroed;
 use std::ptr;
 use std::ffi::CString;
 use url::Url;
@@ -797,17 +798,23 @@ impl WindowMethods for Window {
     }
 
     fn set_page_title(&self, _: Option<String>) {
     }
 
     fn set_page_url(&self, _: Url) {
     }
 
-    fn load_end(&self) {
+    fn load_start(&self, _: bool, _: bool) {
+    }
+
+    fn load_end(&self, _: bool, _: bool) {
+    }
+
+    fn load_error(&self, _: NetError, _: String) {
     }
 
     fn hidpi_factor(&self) -> ScaleFactor<ScreenPx, DevicePixel, f32> {
         ScaleFactor::new(1.0)
     }
 
     fn native_metadata(&self) -> NativeGraphicsMetadata {
         NativeGraphicsMetadata {