diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 923324d1f3a9..d481f9f63b24 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -454,6 +454,10 @@ impl IOCompositor { self.composite_if_necessary(CompositingReason::Headless); } + (Msg::NewFavicon(url), ShutdownState::NotShuttingDown) => { + self.window.set_favicon(url); + } + // When we are shutting_down, we need to avoid performing operations // such as Paint that may crash because we have begun tearing down // the rest of our resources. diff --git a/components/compositing/compositor_task.rs b/components/compositing/compositor_task.rs index be134d751bcc..a58f838ec8c5 100644 --- a/components/compositing/compositor_task.rs +++ b/components/compositing/compositor_task.rs @@ -180,6 +180,8 @@ pub enum Msg { ViewportConstrained(PipelineId, ViewportConstraints), /// A reply to the compositor asking if the output image is stable. IsReadyToSaveImageReply(bool), + /// A favicon was detected + NewFavicon(Url), } impl Debug for Msg { @@ -206,6 +208,7 @@ impl Debug for Msg { Msg::PaintTaskExited(..) => write!(f, "PaintTaskExited"), Msg::ViewportConstrained(..) => write!(f, "ViewportConstrained"), Msg::IsReadyToSaveImageReply(..) => write!(f, "IsReadyToSaveImageReply"), + Msg::NewFavicon(..) => write!(f, "NewFavicon"), } } } diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs index 099a2031394d..9c1d07510c74 100644 --- a/components/compositing/constellation.rs +++ b/components/compositing/constellation.rs @@ -468,6 +468,10 @@ impl Constellation { debug!("constellation got remove iframe message"); self.handle_remove_iframe_msg(containing_pipeline_id, subpage_id); } + ConstellationMsg::NewFavicon(url) => { + debug!("constellation got new favicon message"); + self.compositor_proxy.send(CompositorMsg::NewFavicon(url)); + } } true } diff --git a/components/compositing/headless.rs b/components/compositing/headless.rs index c58532e81eda..075dcf7ba32c 100644 --- a/components/compositing/headless.rs +++ b/components/compositing/headless.rs @@ -109,6 +109,7 @@ impl CompositorEventListener for NullCompositor { Msg::CreatePng(..) | Msg::PaintTaskExited(..) | Msg::IsReadyToSaveImageReply(..) => {} + Msg::NewFavicon(..) => {} } true } diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs index 7ba7fffe4493..9c85611fa34c 100644 --- a/components/compositing/windowing.rs +++ b/components/compositing/windowing.rs @@ -138,4 +138,7 @@ pub trait WindowMethods { /// Does this window support a clipboard fn supports_clipboard(&self) -> bool; + + /// Add a favicon + fn set_favicon(&self, url: Url); } diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs index 6680804c3ef5..001d020ef427 100644 --- a/components/msg/constellation_msg.rs +++ b/components/msg/constellation_msg.rs @@ -248,6 +248,8 @@ pub enum Msg { IsReadyToSaveImage(HashMap), /// Notification that this iframe should be removed. RemoveIFrame(PipelineId, SubpageId), + /// Favicon detected + NewFavicon(Url), } #[derive(Clone, Eq, PartialEq)] diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs index c0d38221e2e9..a707942544ff 100644 --- a/components/script/dom/htmllinkelement.rs +++ b/components/script/dom/htmllinkelement.rs @@ -25,6 +25,8 @@ use dom::node::{Node, NodeHelpers, NodeTypeId, window_from_node}; use dom::virtualmethods::VirtualMethods; use dom::window::WindowHelpers; use layout_interface::{LayoutChan, Msg}; +use msg::constellation_msg::ConstellationChan; +use msg::constellation_msg::Msg as ConstellationMsg; use script_traits::StylesheetLoadResponder; use util::str::{DOMString, HTML_SPACE_CHARACTERS}; use style::media_queries::parse_media_query_list; @@ -85,6 +87,19 @@ fn is_stylesheet(value: &Option) -> bool { } } +/// Favicon spec usage in accordance with CEF implementation: +/// only url of icon is required/used +/// https://html.spec.whatwg.org/multipage/#rel-icon +fn is_favicon(value: &Option) -> bool { + match *value { + Some(ref value) => { + value.split(HTML_SPACE_CHARACTERS) + .any(|s| s.eq_ignore_ascii_case("icon")) + }, + None => false, + } +} + impl<'a> VirtualMethods for JSRef<'a, HTMLLinkElement> { fn super_type<'b>(&'b self) -> Option<&'b VirtualMethods> { let htmlelement: &JSRef = HTMLElementCast::from_borrowed_ref(self); @@ -105,7 +120,14 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLLinkElement> { let rel = get_attr(element, &atom!("rel")); match (rel, attr.local_name()) { - (ref rel, &atom!("href")) | (ref rel, &atom!("media")) => { + (ref rel, &atom!("href")) => { + if is_stylesheet(rel) { + self.handle_stylesheet_url(&attr.value()); + } else if is_favicon(rel) { + self.handle_favicon_url(&attr.value()); + } + } + (ref rel, &atom!("media")) => { if is_stylesheet(rel) { self.handle_stylesheet_url(&attr.value()); } @@ -136,6 +158,9 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLLinkElement> { (ref rel, Some(ref href)) if is_stylesheet(rel) => { self.handle_stylesheet_url(href); } + (ref rel, Some(ref href)) if is_favicon(rel) => { + self.handle_favicon_url(href); + } _ => {} } } @@ -144,6 +169,7 @@ impl<'a> VirtualMethods for JSRef<'a, HTMLLinkElement> { trait PrivateHTMLLinkElementHelpers { fn handle_stylesheet_url(self, href: &str); + fn handle_favicon_url(self, href: &str); } impl<'a> PrivateHTMLLinkElementHelpers for JSRef<'a, HTMLLinkElement> { @@ -174,6 +200,19 @@ impl<'a> PrivateHTMLLinkElementHelpers for JSRef<'a, HTMLLinkElement> { Err(e) => debug!("Parsing url {} failed: {}", href, e) } } + + fn handle_favicon_url(self, href: &str) { + let window = window_from_node(self).root(); + let window = window.r(); + match UrlParser::new().base_url(&window.get_url()).parse(href) { + Ok(url) => { + let ConstellationChan(ref chan) = window.constellation_chan(); + let event = ConstellationMsg::NewFavicon(url.clone()); + chan.send(event).unwrap(); + } + Err(e) => debug!("Parsing url {} failed: {}", href, e) + } + } } impl<'a> HTMLLinkElementMethods for JSRef<'a, HTMLLinkElement> { diff --git a/ports/cef/window.rs b/ports/cef/window.rs index 8af65a1c4e71..d9fef3d215ba 100644 --- a/ports/cef/window.rs +++ b/ports/cef/window.rs @@ -316,6 +316,9 @@ impl WindowMethods for Window { } } + fn set_favicon(&self, _: Url) { + } + fn load_start(&self, back: bool, forward: bool) { // FIXME(pcwalton): The status code 200 is a lie. let browser = self.cef_browser.borrow(); diff --git a/ports/glutin/window.rs b/ports/glutin/window.rs index ff3f53724dd9..635aed4c46e6 100644 --- a/ports/glutin/window.rs +++ b/ports/glutin/window.rs @@ -559,6 +559,9 @@ impl WindowMethods for Window { self.window.set_cursor(glutin_cursor); } + fn set_favicon(&self, _: Url) { + } + fn prepare_for_composite(&self, _width: usize, _height: usize) -> bool { true } @@ -707,6 +710,9 @@ impl WindowMethods for Window { fn set_cursor(&self, _: Cursor) { } + fn set_favicon(&self, _: Url) { + } + fn prepare_for_composite(&self, _width: usize, _height: usize) -> bool { true } diff --git a/ports/gonk/src/window.rs b/ports/gonk/src/window.rs index a2c9f6b020af..10734fa6cbb3 100644 --- a/ports/gonk/src/window.rs +++ b/ports/gonk/src/window.rs @@ -838,6 +838,9 @@ impl WindowMethods for Window { fn set_cursor(&self, _: Cursor) { } + fn set_favicon(&self, _: Url) { + } + fn prepare_for_composite(&self, _width: usize, _height: usize) -> bool { true }