diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 346666ac31b..f039d7bee27 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -384,7 +384,11 @@ impl IOCompositor { 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. @@ -395,7 +399,7 @@ impl IOCompositor { // 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) => { @@ -878,14 +882,13 @@ impl IOCompositor { 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() } diff --git a/components/compositing/compositor_task.rs b/components/compositing/compositor_task.rs index 904e69da83b..9a47fce1600 100644 --- a/components/compositing/compositor_task.rs +++ b/components/compositing/compositor_task.rs @@ -160,8 +160,10 @@ pub enum Msg { 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), @@ -194,7 +196,8 @@ impl Debug for Msg { 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"), diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs index af94cea02e9..6b79722b7e4 100644 --- a/components/compositing/constellation.rs +++ b/components/compositing/constellation.rs @@ -391,9 +391,9 @@ impl Constellation { } // 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) => { @@ -519,8 +519,10 @@ impl Constellation { 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, @@ -629,6 +631,7 @@ impl Constellation { // 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); @@ -646,6 +649,7 @@ impl Constellation { } } + 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. @@ -661,8 +665,31 @@ impl Constellation { } } - 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(); } diff --git a/components/compositing/headless.rs b/components/compositing/headless.rs index 2f9aa30e744..c58532e81ed 100644 --- a/components/compositing/headless.rs +++ b/components/compositing/headless.rs @@ -97,7 +97,8 @@ impl CompositorEventListener for NullCompositor { Msg::AssignPaintedBuffers(..) | Msg::ChangeRunningAnimationsState(..) | Msg::ScrollFragmentPoint(..) | - Msg::LoadComplete | + Msg::LoadStart(..) | + Msg::LoadComplete(..) | Msg::ScrollTimeout(..) | Msg::RecompositeAfterScroll | Msg::ChangePageTitle(..) | diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs index 196e1e2653f..1b1cac23025 100644 --- a/components/compositing/windowing.rs +++ b/components/compositing/windowing.rs @@ -12,6 +12,7 @@ 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; @@ -103,8 +104,12 @@ pub trait WindowMethods { fn set_page_title(&self, title: Option); /// 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; diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs index 623f801ddb4..8706a807cab 100644 --- a/components/msg/constellation_msg.rs +++ b/components/msg/constellation_msg.rs @@ -211,7 +211,7 @@ pub enum Msg { Exit, Failure(Failure), InitLoadUrl(Url), - LoadComplete, + LoadComplete(PipelineId), FrameRect(PipelineId, SubpageId, Rect), LoadUrl(PipelineId, LoadData), ScriptLoadedURLInIFrame(Url, PipelineId, SubpageId, Option, IFrameSandboxState), diff --git a/components/net/lib.rs b/components/net/lib.rs index d00f458b505..6e845bbff0a 100644 --- a/components/net/lib.rs +++ b/components/net/lib.rs @@ -39,6 +39,7 @@ 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; diff --git a/components/net/net_error_list.rs b/components/net/net_error_list.rs new file mode 100644 index 00000000000..a09be7b07de --- /dev/null +++ b/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, +} diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 573827cc267..cad737b4af4 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -960,7 +960,7 @@ impl ScriptTask { 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. diff --git a/components/servo/lib.rs b/components/servo/lib.rs index 4f254d5f682..1cc349de1dd 100644 --- a/components/servo/lib.rs +++ b/components/servo/lib.rs @@ -168,10 +168,13 @@ fn create_constellation(opts: opts::Opts, 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 } diff --git a/components/util/opts.rs b/components/util/opts.rs index f3e7487f468..0b19f815596 100644 --- a/components/util/opts.rs +++ b/components/util/opts.rs @@ -24,7 +24,7 @@ use url::{self, Url}; #[derive(Clone)] pub struct Opts { /// The initial URL to load. - pub url: Url, + pub url: Option, /// How many threads to use for CPU painting (`-t`). /// @@ -198,7 +198,7 @@ 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, @@ -370,7 +370,7 @@ pub fn from_cmdline_args(args: &[String]) -> bool { }; let opts = Opts { - url: url, + url: Some(url), paint_threads: paint_threads, gpu_painting: gpu_painting, tile_size: tile_size, diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index 9541567da8f..a9202a2bd16 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -479,6 +479,7 @@ dependencies = [ "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)", diff --git a/ports/cef/browser.rs b/ports/cef/browser.rs index cf868cb10d0..39139c5b8ff 100644 --- a/ports/cef/browser.rs +++ b/ports/cef/browser.rs @@ -10,6 +10,7 @@ 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; @@ -62,6 +63,18 @@ cef_class_impl! { 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)); }} @@ -92,6 +105,12 @@ pub struct ServoCefBrowser { pub window: Option>, /// Whether the on-created callback has fired yet. pub callback_executed: Cell, + /// whether the browser can navigate back + pub back: Cell, + /// whether the browser can navigate forward + pub forward: Cell, + /// whether the browser is loading + pub loading: Cell, /// the display system window handle: only to be used with host.get_window_handle() window_handle: cef_window_handle_t, @@ -132,6 +151,9 @@ impl ServoCefBrowser { 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, } } @@ -243,6 +265,7 @@ pub fn browser_callback_after_created(browser: CefBrowser) { 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, @@ -252,12 +275,12 @@ fn browser_host_create(window_info: &cef_window_info_t, -> 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()); }); @@ -275,6 +298,9 @@ cef_static_method_impls! { 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, diff --git a/ports/cef/browser_host.rs b/ports/cef/browser_host.rs index 680f7e8b99b..7c0e7f9f1ff 100644 --- a/ports/cef/browser_host.rs +++ b/ports/cef/browser_host.rs @@ -6,6 +6,7 @@ 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; @@ -16,6 +17,8 @@ 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. @@ -26,6 +29,337 @@ pub struct ServoCefBrowserHost { pub composite_ok: Cell, } +// 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 { + 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 {{ @@ -67,55 +401,31 @@ full_cef_class_impl! { }} 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)) }} diff --git a/ports/cef/core.rs b/ports/cef/core.rs index aa49bb17b51..04a88991838 100644 --- a/ports/cef/core.rs +++ b/ports/cef/core.rs @@ -12,11 +12,10 @@ 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")] @@ -26,6 +25,8 @@ static CEF_API_HASH_PLATFORM: &'static [u8] = b"6813214accbf2ebfb6bdcf8d00654650 #[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, @@ -36,6 +37,11 @@ pub extern "C" fn cef_initialize(args: *const cef_main_args_t, 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); @@ -47,6 +53,7 @@ pub extern "C" fn cef_initialize(args: *const cef_main_args_t, (*handler).on_context_initialized.map(|hcb| hcb(handler)); } }); + CEF_APP = application; } } @@ -67,7 +74,7 @@ pub extern "C" fn cef_initialize(args: *const cef_main_args_t, 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 } { diff --git a/ports/cef/eutil.rs b/ports/cef/eutil.rs index 1291a7dce09..39b30cfa481 100644 --- a/ports/cef/eutil.rs +++ b/ports/cef/eutil.rs @@ -77,6 +77,7 @@ 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 } diff --git a/ports/cef/frame.rs b/ports/cef/frame.rs index 61ae9de2821..e6a166be121 100644 --- a/ports/cef/frame.rs +++ b/ports/cef/frame.rs @@ -53,10 +53,20 @@ full_cef_class_impl! { 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); + } } diff --git a/ports/cef/interfaces/cef_browser_process_handler.rs b/ports/cef/interfaces/cef_browser_process_handler.rs index 672247169fd..49cc9e327b1 100644 --- a/ports/cef/interfaces/cef_browser_process_handler.rs +++ b/ports/cef/interfaces/cef_browser_process_handler.rs @@ -94,6 +94,12 @@ pub struct _cef_browser_process_handler_t { pub get_print_handler: Option *mut interfaces::cef_print_handler_t>, + // + // Called when the application should call cef_do_message_loop_work() + // + pub on_work_available: Option ()>, + // // The reference count. This will only be present for Rust instances! // @@ -253,6 +259,21 @@ impl CefBrowserProcessHandler { 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 { diff --git a/ports/cef/lib.rs b/ports/cef/lib.rs index 54225fd3ae7..bc26e01b3a3 100644 --- a/ports/cef/lib.rs +++ b/ports/cef/lib.rs @@ -8,6 +8,7 @@ #![feature(link_args)] #![feature(thread_local)] #![feature(core)] +#![feature(convert)] #![feature(std_misc)] #![feature(rustc_private)] #![feature(collections)] diff --git a/ports/cef/types.rs b/ports/cef/types.rs index 4369930b105..38accd438e1 100644 --- a/ports/cef/types.rs +++ b/ports/cef/types.rs @@ -9,6 +9,8 @@ use libc::c_ulong; 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 {} @@ -276,61 +278,7 @@ pub enum cef_urlrequest_status_t { -// -// 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; // diff --git a/ports/cef/window.rs b/ports/cef/window.rs index 8bf21db2ef4..05af8fdbf8f 100644 --- a/ports/cef/window.rs +++ b/ports/cef/window.rs @@ -7,8 +7,9 @@ //! 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}; @@ -21,8 +22,9 @@ 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; @@ -314,13 +316,42 @@ impl WindowMethods for Window { } } - 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() @@ -330,6 +361,23 @@ impl WindowMethods for Window { } } + 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 = 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) { let browser = self.cef_browser.borrow(); let browser = match *browser { @@ -339,31 +387,45 @@ impl WindowMethods for Window { let frame = browser.get_main_frame(); let frame = frame.downcast(); let mut title_visitor = frame.title_visitor.borrow_mut(); - match &mut *title_visitor { - &mut None => {} - &mut Some(ref mut visitor) => { - match string { - None => visitor.visit(&[]), - Some(string) => { - let utf16_chars: Vec = Utf16Encoder::new(string.chars()).collect(); - visitor.visit(&utf16_chars) - } - } + let str = match string { + Some(s) => { + let utf16_chars: Vec = 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 Some(ref mut visitor) => { + 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 = 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) { @@ -396,38 +458,9 @@ struct CefCompositorProxy { } 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 { @@ -437,6 +470,57 @@ impl CompositorProxy for CefCompositorProxy { } } +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 { diff --git a/ports/cef/wrappers.rs b/ports/cef/wrappers.rs index edeb664e2e5..2cc1d48fe63 100644 --- a/ports/cef/wrappers.rs +++ b/ports/cef/wrappers.rs @@ -2,7 +2,7 @@ * 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}; @@ -98,6 +98,7 @@ 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); @@ -137,6 +138,7 @@ 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); diff --git a/ports/glutin/Cargo.toml b/ports/glutin/Cargo.toml index f503ee86441..c816d777ec7 100644 --- a/ports/glutin/Cargo.toml +++ b/ports/glutin/Cargo.toml @@ -26,6 +26,9 @@ git = "https://github.com/servo/rust-layers" [dependencies.msg] path = "../../components/msg" +[dependencies.net] +path = "../../components/net" + [dependencies.util] path = "../../components/util" diff --git a/ports/glutin/lib.rs b/ports/glutin/lib.rs index 3d66e50a5e4..b360649255c 100644 --- a/ports/glutin/lib.rs +++ b/ports/glutin/lib.rs @@ -16,6 +16,7 @@ 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; diff --git a/ports/glutin/window.rs b/ports/glutin/window.rs index 8caa33d286e..64de0e784ed 100644 --- a/ports/glutin/window.rs +++ b/ports/glutin/window.rs @@ -14,6 +14,7 @@ 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}; @@ -505,7 +506,13 @@ impl WindowMethods for Window { 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. @@ -686,7 +693,11 @@ impl WindowMethods for Window { 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) { diff --git a/ports/gonk/src/window.rs b/ports/gonk/src/window.rs index 632f0257855..67254dd663c 100644 --- a/ports/gonk/src/window.rs +++ b/ports/gonk/src/window.rs @@ -12,6 +12,7 @@ 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; @@ -802,7 +803,13 @@ impl WindowMethods for Window { 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 {