diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index e0caae3f98db..3e786e94d937 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -420,6 +420,10 @@ impl IOCompositor { self.scroll_fragment_to_point(pipeline_id, layer_id, point); } + (Msg::Status(message), ShutdownState::NotShuttingDown) => { + self.window.status(message); + } + (Msg::LoadStart(back, forward), ShutdownState::NotShuttingDown) => { self.window.load_start(back, forward); } diff --git a/components/compositing/compositor_task.rs b/components/compositing/compositor_task.rs index 422c5be4f3bc..d559630bfcf8 100644 --- a/components/compositing/compositor_task.rs +++ b/components/compositing/compositor_task.rs @@ -199,6 +199,8 @@ pub enum Msg { ReturnUnusedNativeSurfaces(Vec), /// Collect memory reports and send them back to the given mem::ReportsChan. CollectMemoryReports(mem::ReportsChan), + /// A status message to be displayed by the browser chrome. + Status(Option), } impl Debug for Msg { @@ -229,6 +231,7 @@ impl Debug for Msg { Msg::HeadParsed => write!(f, "HeadParsed"), Msg::ReturnUnusedNativeSurfaces(..) => write!(f, "ReturnUnusedNativeSurfaces"), Msg::CollectMemoryReports(..) => write!(f, "CollectMemoryReports"), + Msg::Status(..) => write!(f, "Status"), } } } diff --git a/components/compositing/constellation.rs b/components/compositing/constellation.rs index 429690702b12..86d1df9dc07c 100644 --- a/components/compositing/constellation.rs +++ b/components/compositing/constellation.rs @@ -514,6 +514,10 @@ impl Constellation { debug!("constellation got create-WebGL-paint-task message"); self.handle_create_webgl_paint_task_msg(&size, attributes, sender) } + ConstellationMsg::NodeStatus(message) => { + debug!("constellation got NodeStatus message"); + self.compositor_proxy.send(CompositorMsg::Status(message)); + } } true } diff --git a/components/compositing/headless.rs b/components/compositing/headless.rs index 7cda9ed635a5..d56c50de1d27 100644 --- a/components/compositing/headless.rs +++ b/components/compositing/headless.rs @@ -97,6 +97,7 @@ impl CompositorEventListener for NullCompositor { Msg::AssignPaintedBuffers(..) | Msg::ChangeRunningAnimationsState(..) | Msg::ScrollFragmentPoint(..) | + Msg::Status(..) | Msg::LoadStart(..) | Msg::LoadComplete(..) | Msg::ScrollTimeout(..) | diff --git a/components/compositing/windowing.rs b/components/compositing/windowing.rs index 218facad4402..6ad7790edaa7 100644 --- a/components/compositing/windowing.rs +++ b/components/compositing/windowing.rs @@ -107,6 +107,8 @@ 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 chrome should display a status message. + fn status(&self, Option); /// 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. diff --git a/components/msg/constellation_msg.rs b/components/msg/constellation_msg.rs index 36bd82793197..97d673deecbe 100644 --- a/components/msg/constellation_msg.rs +++ b/components/msg/constellation_msg.rs @@ -269,6 +269,8 @@ pub enum Msg { CreateWebGLPaintTask(Size2D, GLContextAttributes, IpcSender, usize), String>>), + /// Status message to be displayed in the chrome, eg. a link URL on mouseover. + NodeStatus(Option), } #[derive(Clone, Eq, PartialEq, Deserialize, Serialize)] diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 9254d67b0f2e..369622c0d6f4 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -21,6 +21,7 @@ use document_loader::{LoadType, DocumentLoader, NotifierData}; use dom::bindings::cell::DOMRefCell; +use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods; use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, DocumentReadyState}; use dom::bindings::codegen::InheritTypes::{ElementCast, EventTargetCast, NodeCast, EventCast}; use dom::bindings::conversions::FromJSValConvertible; @@ -88,7 +89,7 @@ use js::jsapi::{JS_GetRuntime, JS_SetGCCallback, JSGCStatus, JSAutoRequest, SetD use js::jsapi::{SetDOMProxyInformation, DOMProxyShadowsResult, HandleObject, HandleId, RootedValue}; use js::jsval::UndefinedValue; use js::rust::Runtime; -use url::Url; +use url::{Url, UrlParser}; use libc; use std::any::Any; @@ -1532,11 +1533,48 @@ impl ScriptTask { } let page = get_page(&self.root_page(), pipeline_id); let document = page.document(); + + let mut prev_mouse_over_targets: RootedVec> = RootedVec::new(); + for target in self.mouse_over_targets.borrow_mut().iter() { + prev_mouse_over_targets.push(target.clone()); + } + // We temporarily steal the list of targets over which the mouse is to pass it to // handle_mouse_move_event() in a safe RootedVec container. let mut mouse_over_targets = RootedVec::new(); std_mem::swap(&mut *self.mouse_over_targets.borrow_mut(), &mut *mouse_over_targets); document.r().handle_mouse_move_event(self.js_runtime.rt(), point, &mut mouse_over_targets); + + // Notify Constellation about anchors that are no longer mouse over targets. + for target in prev_mouse_over_targets.iter() { + if !mouse_over_targets.contains(target) { + if target.root().r().is_anchor_element() { + let event = ConstellationMsg::NodeStatus(None); + let ConstellationChan(ref chan) = self.constellation_chan; + chan.send(event).unwrap(); + break; + } + } + } + + // Notify Constellation about the topmost anchor mouse over target. + for target in mouse_over_targets.iter() { + let target = target.root(); + if target.r().is_anchor_element() { + let element = ElementCast::to_ref(target.r()).unwrap(); + let status = element.get_attribute(&ns!(""), &atom!("href")) + .and_then(|href| { + let value = href.r().Value(); + let url = document.r().url(); + UrlParser::new().base_url(&url).parse(&value).map(|url| url.serialize()).ok() + }); + let event = ConstellationMsg::NodeStatus(status); + let ConstellationChan(ref chan) = self.constellation_chan; + chan.send(event).unwrap(); + break; + } + } + std_mem::swap(&mut *self.mouse_over_targets.borrow_mut(), &mut *mouse_over_targets); } diff --git a/ports/cef/window.rs b/ports/cef/window.rs index 291770224690..2149181ed16e 100644 --- a/ports/cef/window.rs +++ b/ports/cef/window.rs @@ -316,6 +316,9 @@ impl WindowMethods for Window { browser.downcast().favicons.borrow_mut().push(url.to_string().clone()); } + fn status(&self, _: Option) { + } + fn load_start(&self, back: bool, forward: bool) { let browser = self.cef_browser.borrow(); let browser = match *browser { diff --git a/ports/glutin/window.rs b/ports/glutin/window.rs index e84792112815..0e28d1abd855 100644 --- a/ports/glutin/window.rs +++ b/ports/glutin/window.rs @@ -554,6 +554,9 @@ impl WindowMethods for Window { fn set_page_url(&self, _: Url) { } + fn status(&self, _: Option) { + } + fn load_start(&self, _: bool, _: bool) { } @@ -778,6 +781,9 @@ impl WindowMethods for Window { fn set_favicon(&self, _: Url) { } + fn status(&self, _: Option) { + } + 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 0d94ddaa7a43..88ba3ceca96f 100644 --- a/ports/gonk/src/window.rs +++ b/ports/gonk/src/window.rs @@ -803,6 +803,9 @@ impl WindowMethods for Window { fn set_page_url(&self, _: Url) { } + fn status(&self, _: Option) { + } + fn load_start(&self, _: bool, _: bool) { }