servo: Merge #9715 - script: Fix MouseOver handling (from emilio:mousemove); r=mbrubeck

Now we only query for the topmost node, and apply the hover state to all
of the parent elements.

This fixes things like #9705, where the hover state was applied only to
the children.

This also makes us more conformant with other browsers in the case of
taking in account margins and paddings.

For example, prior to this PR, when your mouse was over the inner
element, in the bottom part, `hover` styles didn't apply to the parent.

```html
<style>
div {
  padding: 10px;
  margin: 10px;
  height: 15px;
  background: blue;
}

div:hover {
  background: red;
}
</style>

<div>
  <div></div>
</div>
```

Fixes #9705

Source-Repo: https://github.com/servo/servo
Source-Revision: 056a7cf1a2284063e5d32c6627b86f6931957a74
This commit is contained in:
Emilio Cobos Álvarez
2016-03-03 22:25:35 +05:00
parent bb4b0a2bb3
commit 63d6f4eb6d
5 changed files with 138 additions and 141 deletions

View File

@@ -27,10 +27,10 @@ use dom::bindings::codegen::Bindings::DocumentBinding::{DocumentMethods, Documen
use dom::bindings::conversions::{FromJSValConvertible, StringificationBehavior};
use dom::bindings::global::GlobalRef;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{JS, RootCollection, trace_roots};
use dom::bindings::js::{JS, MutNullableHeap, Root, RootCollection, trace_roots};
use dom::bindings::js::{RootCollectionPtr, RootedReference};
use dom::bindings::refcounted::{LiveDOMReferences, Trusted, TrustedReference, trace_refcounted_objects};
use dom::bindings::trace::{JSTraceable, RootedVec, trace_traceables};
use dom::bindings::trace::{JSTraceable, trace_traceables};
use dom::bindings::utils::{DOM_CALLBACKS, WRAP_CALLBACKS};
use dom::browsingcontext::BrowsingContext;
use dom::document::{Document, DocumentProgressHandler, DocumentSource, FocusType, IsHTMLDocument};
@@ -91,7 +91,6 @@ use std::cell::{Cell, RefCell};
use std::collections::HashSet;
use std::io::{Write, stdout};
use std::marker::PhantomData;
use std::mem as std_mem;
use std::option::Option;
use std::ptr;
use std::rc::Rc;
@@ -543,7 +542,8 @@ pub struct ScriptThread {
/// The JavaScript runtime.
js_runtime: Rc<Runtime>,
mouse_over_targets: DOMRefCell<Vec<JS<Element>>>,
/// The topmost element over the mouse.
topmost_mouse_over_target: MutNullableHeap<JS<Element>>,
/// List of pipelines that have been owned and closed by this script thread.
closed_pipelines: DOMRefCell<HashSet<PipelineId>>,
@@ -789,7 +789,7 @@ impl ScriptThread {
devtools_sender: ipc_devtools_sender,
js_runtime: Rc::new(runtime),
mouse_over_targets: DOMRefCell::new(vec!()),
topmost_mouse_over_target: MutNullableHeap::new(Default::default()),
closed_pipelines: DOMRefCell::new(HashSet::new()),
scheduler_chan: state.scheduler_chan,
@@ -1979,46 +1979,55 @@ impl ScriptThread {
let page = get_page(&self.root_page(), pipeline_id);
let document = page.document();
let mut prev_mouse_over_targets: RootedVec<JS<Element>> = RootedVec::new();
for target in &*self.mouse_over_targets.borrow_mut() {
prev_mouse_over_targets.push(target.clone());
// Get the previous target temporarily
let prev_mouse_over_target = self.topmost_mouse_over_target.get();
document.handle_mouse_move_event(self.js_runtime.rt(), point,
&self.topmost_mouse_over_target);
// Short-circuit if nothing changed
if self.topmost_mouse_over_target.get() == prev_mouse_over_target {
return;
}
// 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.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 {
if !mouse_over_targets.contains(target) && target.is::<HTMLAnchorElement>() {
let event = ConstellationMsg::NodeStatus(None);
let ConstellationChan(ref chan) = self.constellation_chan;
chan.send(event).unwrap();
break;
}
}
let mut state_already_changed = false;
// Notify Constellation about the topmost anchor mouse over target.
for target in &*mouse_over_targets {
if target.is::<HTMLAnchorElement>() {
let status = target.get_attribute(&ns!(), &atom!("href"))
.and_then(|href| {
let value = href.value();
let url = document.url();
url.join(&value).map(|url| url.serialize()).ok()
});
if let Some(target) = self.topmost_mouse_over_target.get() {
if let Some(anchor) = target.upcast::<Node>()
.inclusive_ancestors()
.filter_map(Root::downcast::<HTMLAnchorElement>)
.next() {
let status = anchor.upcast::<Element>()
.get_attribute(&ns!(), &atom!("href"))
.and_then(|href| {
let value = href.value();
let url = document.url();
url.join(&value).map(|url| url.serialize()).ok()
});
let event = ConstellationMsg::NodeStatus(status);
let ConstellationChan(ref chan) = self.constellation_chan;
chan.send(event).unwrap();
break;
state_already_changed = true;
}
}
std_mem::swap(&mut *self.mouse_over_targets.borrow_mut(), &mut *mouse_over_targets);
// We might have to reset the anchor state
if !state_already_changed {
if let Some(target) = prev_mouse_over_target {
if let Some(_) = target.upcast::<Node>()
.inclusive_ancestors()
.filter_map(Root::downcast::<HTMLAnchorElement>)
.next() {
let event = ConstellationMsg::NodeStatus(None);
let ConstellationChan(ref chan) = self.constellation_chan;
chan.send(event).unwrap();
}
}
}
}
TouchEvent(event_type, identifier, point) => {
let handled = self.handle_touch_event(pipeline_id, event_type, identifier, point);
match event_type {