Bug 1035774 - Add media feature keys for hover and pointer. r=heycam,emilio

Summary:
https://drafts.csswg.org/mediaqueries-4/#hover
https://drafts.csswg.org/mediaqueries-4/#pointer

In this patch series, we don't introduce any-hover and any-pointer media
features yet, but functionalities for them on each platform backends will be
introduced in this patch series, so eIntID_AllPointerCapabilities and relevant
stuff is added in this patch for the convenience that each platform backends
can be reviewed at once.

Differential Revision: https://phabricator.services.mozilla.com/D3296
This commit is contained in:
Hiroyuki Ikezoe 2018-08-14 17:13:04 +09:00
Родитель 2b08d1333b
Коммит ef03e2a299
10 изменённых файлов: 256 добавлений и 1 удалений

Просмотреть файл

@ -56,6 +56,7 @@ namespace mozilla {
class ComputedStyle;
class StyleSheet;
class ServoElementSnapshotTable;
enum class PointerCapabilities : uint8_t;
}
using mozilla::FontFamilyList;
using mozilla::FontFamilyName;
@ -750,6 +751,7 @@ uint32_t Gecko_MediaFeatures_GetColorDepth(nsIDocument*);
void Gecko_MediaFeatures_GetDeviceSize(nsIDocument*, nscoord* width, nscoord* height);
float Gecko_MediaFeatures_GetResolution(nsIDocument*);
bool Gecko_MediaFeatures_PrefersReducedMotion(nsIDocument*);
mozilla::PointerCapabilities Gecko_MediaFeatures_PrimaryPointerCapabilities(nsIDocument*);
float Gecko_MediaFeatures_GetDevicePixelRatio(nsIDocument*);
bool Gecko_MediaFeatures_HasSystemMetric(nsIDocument*,
nsAtom* metric,

Просмотреть файл

@ -257,6 +257,7 @@ whitelist-types = [
"mozilla::StyleShapeRadius",
"mozilla::StyleGrid.*",
"mozilla::UpdateAnimationsTasks",
"mozilla::PointerCapabilities",
"mozilla::LookAndFeel",
"mozilla::gfx::Float",
"mozilla::gfx::FontVariation",
@ -620,6 +621,7 @@ structs-types = [
"ComputedStyleStrong",
"EffectCompositor_CascadeLevel",
"UpdateAnimationsTasks",
"PointerCapabilities",
"ParsingMode",
"InheritTarget",
"DocumentMatchingFunction",

Просмотреть файл

@ -125,6 +125,17 @@ enum class InheritTarget {
PlaceholderFrame,
};
// Represents values for interaction media features.
// https://drafts.csswg.org/mediaqueries-4/#mf-interaction
enum class PointerCapabilities : uint8_t {
None = 0,
Coarse = 1 << 0,
Fine = 1 << 1,
Hover = 1 << 2,
};
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(PointerCapabilities)
// These measurements are obtained for both the UA cache and the Stylist, but
// not all the fields are used in both cases.
class ServoStyleSetSizes

Просмотреть файл

@ -262,6 +262,28 @@ Gecko_MediaFeatures_PrefersReducedMotion(nsIDocument* aDocument)
return LookAndFeel::GetInt(LookAndFeel::eIntID_PrefersReducedMotion, 0) == 1;
}
PointerCapabilities
Gecko_MediaFeatures_PrimaryPointerCapabilities(nsIDocument* aDocument)
{
// The default value is mouse-type pointer.
const PointerCapabilities kDefaultCapabilities =
PointerCapabilities::Fine | PointerCapabilities::Hover;
if (nsContentUtils::ShouldResistFingerprinting(aDocument)) {
return kDefaultCapabilities;
}
int32_t intValue;
nsresult rv =
LookAndFeel::GetInt(LookAndFeel::eIntID_PrimaryPointerCapabilities,
&intValue);
if (NS_FAILED(rv)) {
return kDefaultCapabilities;
}
return static_cast<PointerCapabilities>(intValue);
}
/* static */ void
nsMediaFeatures::InitSystemMetrics()
{

Просмотреть файл

@ -257,6 +257,7 @@ skip-if = android_version == '18' #debug-only failure; timed out #Android 4.3 aw
[test_media_queries_dynamic_xbl.html]
[test_media_query_list.html]
[test_media_query_serialization.html]
[test_mq_hover_and_pointer.html]
[test_moz_device_pixel_ratio.html]
[test_namespace_rule.html]
[test_non_content_accessible_properties.html]

Просмотреть файл

@ -0,0 +1,122 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=1035774
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 1035774</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<script src="/tests/SimpleTest/AddTask.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1035774">Mozilla Bug 1035774</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
<script>
const NO_POINTER = 0x00;
const COARSE_POINTER = 0x01;
const FINE_POINTER = 0x02;
const HOVER_CAPABLE_POINTER = 0x04;
add_task(async () => {
await SpecialPowers.pushPrefEnv({
set: [ ['privacy.resistFingerprinting', true] ]
});
// When resistFingerprinting is enabled, we pretend that the system has a
// mouse pointer.
ok(!matchMedia("(pointer: none)").matches,
"Doesn't match (pointer: none)");
ok(!matchMedia("(pointer: coarse)").matches,
"Doesn't match (pointer: coarse)");
ok(matchMedia("(pointer: fine)").matches, "Matches (pointer: fine)");
ok(matchMedia("(pointer)").matches, "Matches (pointer)");
ok(!matchMedia("(hover: none)").matches, "Doesn't match (hover: none)");
ok(matchMedia("(hover: hover)").matches, "Matches (hover: hover)");
ok(matchMedia("(hover)").matches, "Matches (hover)");
await SpecialPowers.flushPrefEnv();
});
add_task(async () => {
// No pointer.
await SpecialPowers.pushPrefEnv({
set: [ ['ui.primaryPointerCapabilities', NO_POINTER] ]
});
ok(matchMedia("(pointer: none)").matches, "Matches (pointer: none)");
ok(!matchMedia("(pointer: coarse)").matches,
"Doesn't match (pointer: coarse)");
ok(!matchMedia("(pointer: fine)").matches, "Doesn't match (pointer: fine)");
ok(!matchMedia("(pointer)").matches, "Matches (pointer)");
ok(matchMedia("(hover: none)").matches, "Matches (hover: none)");
ok(!matchMedia("(hover: hover)").matches, "Doesn't match (hover: hover)");
ok(!matchMedia("(hover)").matches, "Doesn't match (hover)");
});
add_task(async () => {
// Mouse type pointer.
await SpecialPowers.pushPrefEnv({
set: [ ['ui.primaryPointerCapabilities', FINE_POINTER | HOVER_CAPABLE_POINTER] ]
});
ok(!matchMedia("(pointer: none)").matches,
"Doesn't match (pointer: none)");
ok(!matchMedia("(pointer: coarse)").matches,
"Doesn't match (pointer: coarse)");
ok(matchMedia("(pointer: fine)").matches, "Matches (pointer: fine)");
ok(matchMedia("(pointer)").matches, "Matches (pointer)");
ok(!matchMedia("(hover: none)").matches, "Doesn't match (hover: none)");
ok(matchMedia("(hover: hover)").matches, "Matches (hover: hover)");
ok(matchMedia("(hover)").matches, "Matches (hover)");
});
add_task(async () => {
// Mouse type pointer.
await SpecialPowers.pushPrefEnv({
set: [ ['ui.primaryPointerCapabilities',
FINE_POINTER |
HOVER_CAPABLE_POINTER] ]
});
ok(!matchMedia("(pointer: none)").matches,
"Doesn't match (pointer: none)");
ok(!matchMedia("(pointer: coarse)").matches,
"Doesn't match (pointer: coarse)");
ok(matchMedia("(pointer: fine)").matches, "Matches (pointer: fine)");
ok(matchMedia("(pointer)").matches, "Matches (pointer)");
ok(!matchMedia("(hover: none)").matches, "Doesn't match (hover: none)");
ok(matchMedia("(hover: hover)").matches, "Matches (hover: hover)");
ok(matchMedia("(hover)").matches, "Matches (hover)");
});
add_task(async () => {
// Touch screen.
await SpecialPowers.pushPrefEnv({
set: [ ['ui.primaryPointerCapabilities', COARSE_POINTER] ]
});
ok(!matchMedia("(pointer: none)").matches, "Doesn't match (pointer: none)");
ok(matchMedia("(pointer: coarse)").matches, "Matches (pointer: coarse)");
ok(!matchMedia("(pointer: fine)").matches, "Doesn't match (pointer: fine)");
ok(matchMedia("(pointer)").matches, "Matches (pointer)");
ok(matchMedia("(hover: none)").matches, "Match (hover: none)");
ok(!matchMedia("(hover: hover)").matches, "Doesn't match (hover: hover)");
ok(!matchMedia("(hover)").matches, "Doesn't match (hover)");
});
</script>
</body>
</html>

Просмотреть файл

@ -8,6 +8,7 @@ use Atom;
use app_units::Au;
use euclid::Size2D;
use gecko_bindings::bindings;
use gecko_bindings::structs;
use media_queries::Device;
use media_queries::media_feature::{AllowsRanges, ParsingRequirements};
use media_queries::media_feature::{MediaFeatureDescription, Evaluator};
@ -300,6 +301,66 @@ fn eval_prefers_reduced_motion(device: &Device, query_value: Option<PrefersReduc
}
}
/// https://drafts.csswg.org/mediaqueries-4/#mf-interaction
bitflags! {
struct PointerCapabilities: u8 {
const COARSE = structs::PointerCapabilities_Coarse;
const FINE = structs::PointerCapabilities_Fine;
const HOVER = structs::PointerCapabilities_Hover;
}
}
#[derive(Debug, Copy, Clone, FromPrimitive, ToCss, Parse)]
#[repr(u8)]
enum Pointer {
None,
Coarse,
Fine,
}
fn primary_pointer_capabilities(device: &Device) -> PointerCapabilities {
PointerCapabilities::from_bits_truncate(
unsafe { bindings::Gecko_MediaFeatures_PrimaryPointerCapabilities(device.document()) }
)
}
/// https://drafts.csswg.org/mediaqueries-4/#pointer
fn eval_pointer(device: &Device, query_value: Option<Pointer>) -> bool {
let pointer_capabilities = primary_pointer_capabilities(device);
let query_value = match query_value {
Some(v) => v,
None => return !pointer_capabilities.is_empty(),
};
match query_value {
Pointer::None => pointer_capabilities.is_empty(),
Pointer::Coarse => pointer_capabilities.intersects(PointerCapabilities::COARSE),
Pointer::Fine => pointer_capabilities.intersects(PointerCapabilities::FINE),
}
}
#[derive(Debug, Copy, Clone, FromPrimitive, ToCss, Parse)]
#[repr(u8)]
enum Hover {
None,
Hover,
}
/// https://drafts.csswg.org/mediaqueries-4/#hover
fn eval_hover(device: &Device, query_value: Option<Hover>) -> bool {
let pointer_capabilities = primary_pointer_capabilities(device);
let can_hover = pointer_capabilities.intersects(PointerCapabilities::HOVER);
let query_value = match query_value {
Some(v) => v,
None => return can_hover,
};
match query_value {
Hover::None => !can_hover,
Hover::Hover => can_hover,
}
}
fn eval_moz_is_glyph(
device: &Device,
query_value: Option<bool>,
@ -390,7 +451,7 @@ lazy_static! {
/// to support new types in these entries and (2) ensuring that either
/// nsPresContext::MediaFeatureValuesChanged is called when the value that
/// would be returned by the evaluator function could change.
pub static ref MEDIA_FEATURES: [MediaFeatureDescription; 43] = [
pub static ref MEDIA_FEATURES: [MediaFeatureDescription; 45] = [
feature!(
atom!("width"),
AllowsRanges::Yes,
@ -509,6 +570,18 @@ lazy_static! {
keyword_evaluator!(eval_prefers_reduced_motion, PrefersReducedMotion),
ParsingRequirements::empty(),
),
feature!(
atom!("pointer"),
AllowsRanges::No,
keyword_evaluator!(eval_pointer, Pointer),
ParsingRequirements::empty(),
),
feature!(
atom!("hover"),
AllowsRanges::No,
keyword_evaluator!(eval_hover, Hover),
ParsingRequirements::empty(),
),
// Internal -moz-is-glyph media feature: applies only inside SVG glyphs.
// Internal because it is really only useful in the user agent anyway

Просмотреть файл

@ -447,7 +447,22 @@ public:
* 0: no-preference
* 1: reduce
*/
eIntID_PrefersReducedMotion,
/**
* Corresponding to PointerCapabilities in ServoTypes.h
* 0: None
* 1: Coarse
* 2: Fine
* 4: Hover
*/
eIntID_PrimaryPointerCapabilities,
/**
* Corresponding to union of PointerCapabilities values in ServoTypes.h
* E.g. if there is a mouse and a digitizer, the value will be
* 'Coarse | Fine | Hover'.
*/
eIntID_AllPointerCapabilities,
};
/**

Просмотреть файл

@ -141,6 +141,12 @@ nsLookAndFeelIntPref nsXPLookAndFeel::sIntPrefs[] =
{ "ui.prefersReducedMotion",
eIntID_PrefersReducedMotion,
false, 0 },
{ "ui.primaryPointerCapabilities",
eIntID_PrimaryPointerCapabilities,
false, 6 /* fine and hover-capable pointer, i.e. mouse-type */ },
{ "ui.allPointerCapabilities",
eIntID_AllPointerCapabilities,
false, 6 /* fine and hover-capable pointer, i.e. mouse-type */ },
};
nsLookAndFeelFloatPref nsXPLookAndFeel::sFloatPrefs[] =

Просмотреть файл

@ -1383,6 +1383,7 @@ STATIC_ATOMS = [
Atom("patternTransform", "patternTransform"),
Atom("patternUnits", "patternUnits"),
Atom("pc", "pc"),
Atom("pointer", "pointer"),
Atom("pointer_events", "pointer-events"),
Atom("points", "points"),
Atom("pointsAtX", "pointsAtX"),