incorrect CSS media query pointer: coarse (not fine) on Windows 10 desktop/laptop with trackpad/touchpad and no external mouse
Categories
(Core :: Widget: Win32, defect, P2)
Tracking
()
People
(Reporter: lsemprini, Unassigned)
References
Details
Attachments
(4 files)
User Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0
Steps to reproduce:
On a Windows 10 laptop with a trackpad but not an external mouse, Firefox gives INCORRECT false results for:
window.matchMedia("(pointer: fine").matches -> false - WRONG
window.matchMedia("(any-pointer: fine").matches -> false - WRONG
You can easily check/reproduce this on this handy page:
https://patrickhlauke.github.io/touch/pointer-hover-any-pointer-any-hover/
Notice that as soon as you plug in an external mouse (even without reloading the page), the incorrect "coarse" values become "fine." But with only a trackpad, the values incorrectly show as "coarse."
This is in direct violation of the CSS Media Queries spec, section 7, "Interaction Media Features" which states unequivocally:
https://www.w3.org/TR/mediaqueries-4/#mf-interaction
"Typical examples of devices matching combinations of pointer and hover:
"pointer: fine" mouse, touch pad, advanced stylus digitizers (Surface, Samsung Note, Wacom Intuos Pro, etc) "
Notice how mouse and trackpad/touchpad both MUST return "fine," not "coarse."
Chrome returns "fine" as expected.
I tracked this bug down to the exact line of code, which was added on 4 Oct 2018 and released on Firefox/Mozilla 64 as part of a shaky Windows Tablet workaround (which was not intended to affect desktop, but did).
The bug "fixed" was:
https://bugzilla.mozilla.org/show_bug.cgi?id=1493128
and
https://bugzilla.mozilla.org/show_bug.cgi?id=1495938
And the incorrect code is in widget/windows/WinUtils.cpp:
https://hg.mozilla.org/mozilla-central/rev/bf098432003adaa3862f8271de486ee3bdbd8362
static bool
IsMousePresent()
{
- return ::GetSystemMetrics(SM_MOUSEPRESENT);
- if (!::GetSystemMetrics(SM_MOUSEPRESENT)) {
- return false;
- }
- DWORD count = InputDeviceUtils::CountMouseDevices();
- if (!count) {
- return false;
- }
- // If there is a mouse device and if this machine is a tablet or has a
- // digitizer, that's counted as the mouse device.
- // FIXME: Bug 1495938: We should drop this heuristic way once we find out a
- // reliable way to tell there is no mouse or not.
- if (count == 1 &&
-
(WinUtils::IsTouchDeviceSupportPresent() ||
-
IsTabletDevice())) {
- return false;
- }
note SM_MOUSEPRESENT is NOT the problem: it always returns 1 on laptops with trackpads.
It is the last piece of code above that returns false when there is only a trackpad present, and this causes the calling code to return Coarse instead of Fine:
if (IsMousePresent()) {
- return PointerCapabilities::Fine|
- return PointerCapabilities::Fine |
PointerCapabilities::Hover;
}
if (IsTouchDeviceSupportPresent()) {
return PointerCapabilities::Coarse;
}
Note that WinUtils::IsTouchDeviceSupportPresent() is:
WinUtils::IsTouchDeviceSupportPresent()
{
int32_t touchCapabilities = ::GetSystemMetrics(SM_DIGITIZER);
return (touchCapabilities & NID_READY) &&
(touchCapabilities & (NID_EXTERNAL_TOUCH | NID_INTEGRATED_TOUCH));
}
and I verified this is TRUE on a laptop (HP Omen Windows 10) with a trackpad.
So this is not correct. Need to find a different workaround for the tablet problem.
This code is being incorrectly applied on non-Tablet devices.
It happens that my laptop does not have a touchscreen, but that is not really relevant. On any Windows 10 laptop with a trackpad/touchpad, these media queries should return "fine" as per the spec.
Actual results:
When external mouse is not plugged in but trackpad/touchpad exists:
window.matchMedia("(pointer: fine").matches -> false - WRONG
window.matchMedia("(any-pointer: fine").matches -> false - WRONG
When external mouse is plugged in and trackpad/touchpad exists:
window.matchMedia("(pointer: fine").matches -> true - RIGHT
window.matchMedia("(any-pointer: fine").matches -> false - RIGHT
Expected results:
Regardless of whether external mouse is plugged in or not, when touchpad/trackpad exists:
window.matchMedia("(pointer: fine").matches -> true - RIGHT
window.matchMedia("(any-pointer: fine").matches -> true - RIGHT
Reporter | ||
Comment 1•5 years ago
|
||
Oh, and just to be clear, the spec behavior is definitely better because people use this CSS media query to decide whether to present big finger-sized UI elements or tiny mouse/trackpad-sized UI elements.
Reporter | ||
Comment 2•5 years ago
|
||
Ah, and also in case anyone wonders, my Windows 10 laptop is NOT in "tablet mode" (which can be toggled using a tile in the weird Windows 10 "action center" menu that comes up when you click the icon at the lower right of the screen). And as I mentioned above, my laptop is not a touchscreen device, but that's not actually relevant (even if it were, the results for "pointer" and "any-pointer" should be "fine" whenever a touchpad/trackpad is present).
Comment 3•5 years ago
|
||
Louis, thanks for this bug report.
I can understand your irritation. Do you have tablet devices and do you mind sharing the results on the devices? I have no tablet devices so that I can't check it unfortunately. IIRC, with the way you suggested the result was not what we expected at all. As far as I know Windows didn't provide any good way to detect/distinguish mouse/touch pad devices there. Things may have changed since then.
Reporter | ||
Comment 4•5 years ago
|
||
Hi Hiroyuki!
Unfortunately no I don't have any Windows tablet devices.
I'm not even 100% sure what type of device your original change was meant for, perhaps the Surface devices? If we can identify the type of Windows devices for which we need the heuristic, perhaps we can add an additional test so that regular old laptops that have trackpads/touchpads are not affected by the heuristic.
It seems likely that something in Windows changed between 2018 and now.
My best guess is that the behavior of ::GetSystemMetrics(SM_DIGITIZER) changed in some Windows update.
I tested whether perhaps the Windows code called by InputDeviceUtils::CountMouseDevices() changed behavior in some Windows update. That code is:
+DWORD
+InputDeviceUtils::CountMouseDevices()
+{
+ HDEVINFO hdev = SetupDiGetClassDevs(&GUID_DEVINTERFACE_MOUSE,
+ nullptr,
+ nullptr,
+ DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
+ if (hdev == INVALID_HANDLE_VALUE) {
+ return 0;
+ }
+
+ DWORD count = 0;
+ SP_INTERFACE_DEVICE_DATA info = {};
+ info.cbSize = sizeof(SP_INTERFACE_DEVICE_DATA);
+ while (SetupDiEnumDeviceInterfaces(hdev,
+ nullptr,
+ &GUID_DEVINTERFACE_MOUSE,
+ count,
+ &info)) {
+ if (info.Flags & SPINT_ACTIVE) {
+ count++;
+ }
+ }
+ SetupDiDestroyDeviceInfoList(hdev);
+ return count;
+}
I thought perhaps this code now says that there are 0 mice in Windows when only a trackpad is present, whereas it used to say there is 1 mouse when only a trackpad is present. If that were the case, then code that causes the incorrect "coarse" value would actually be just above the heuristic:
DWORD count = InputDeviceUtils::CountMouseDevices();
if (!count) {
return false;
}
HOWEVER it is not the case. I wrote a small C++ program with the contents of InputDeviceUtils::CountMouseDevices() and when I have only a trackpad, Windows reports 1 device.
Here is the code in case it is of use to anyone:
#include <stdlib.h>
#include <stdio.h>
#include <type_traits>
#include <functional>
#include <assert.h>
#include <windows.h>
#include <dbt.h>
#include <hidclass.h>
#include <ntddmou.h>
#include <setupapi.h>
#include <Ntddmou.h>
const GUID LOCAL__GUID_DEVINTERFACE_MOUSE =
{0x378DE44C, 0x56EF, 0x11D1, 0xBC, 0x8C, 0x00, 0xA0, 0xC9, 0x14, 0x05, 0xDD};
// from ms dox:
// https://docs.microsoft.com/vi-vn/windows-hardware/drivers/install/guid-devinterface-mouse
/*
{ 378DE44C -56EF- 11D1- BC 8C- 00 A0 C9 14 05 DD}
*/
void doit()
{
printf("mouse=%lx\n", GetSystemMetrics(SM_MOUSEPRESENT));
printf("dig=%lx\n", GetSystemMetrics(SM_DIGITIZER));
printf("dig_ready=%lx\n", GetSystemMetrics(SM_DIGITIZER)&NID_READY);
printf("dig_ext=%lx\n", GetSystemMetrics(SM_DIGITIZER)&NID_EXTERNAL_TOUCH);
printf("dig_int=%lx\n", GetSystemMetrics(SM_DIGITIZER)&NID_INTEGRATED_TOUCH);
HDEVINFO hdev = SetupDiGetClassDevs(&LOCAL__GUID_DEVINTERFACE_MOUSE,
nullptr,
nullptr,
DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);
if (hdev == INVALID_HANDLE_VALUE) {
assert(0);
}
DWORD count = 0;
SP_INTERFACE_DEVICE_DATA info = {};
info.cbSize = sizeof(SP_INTERFACE_DEVICE_DATA);
while (SetupDiEnumDeviceInterfaces(hdev,
nullptr,
&LOCAL__GUID_DEVINTERFACE_MOUSE,
count,
&info))
{
if (info.Flags & SPINT_ACTIVE) {
count++;
}
}
SetupDiDestroyDeviceInfoList(hdev);
printf("count is %d", count);
}
int main()
{
doit();
}
Reporter | ||
Comment 5•5 years ago
|
||
BTW that test code needs to be linked with SetupApi.lib to compile.
Comment 6•5 years ago
|
||
Thanks for testing. Yes, we'd like to know a way to tell the difference between Surface's touch screen (and other touch screens) and trackpad/touchpad. As you pointed out, IsTouchDeviceSupportPresent seems to be an overkill for some devices.
Reporter | ||
Comment 7•5 years ago
|
||
There are a lot of things you could try.
What is it exactly that makes Surfaces (and the other devices you want to capture) different from regular laptops? Do they run a different kind/verison of Windows and can you detect that?
Remember that we don't need/want to detect laptops that have touchscreens, because those laptops are also going to have trackpads and so we want the "fine" return value. We ONLY want to detect Surface-like devices that have ONLY a touchscreen and not necessarily a mouse and no trackpad/touchpad.
Dox for GetSystemMetrics(SM_DIGITIZER) explain the flags. Perhaps one of them varies based on touchpad/trackpad vs. touch screen?
https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages
TABLET_CONFIG_NONE 0x00000000 The input digitizer does not have touch capabilities.
NID_INTEGRATED_TOUCH 0x00000001 An integrated touch digitizer is used for input.
NID_EXTERNAL_TOUCH 0x00000002 An external touch digitizer is used for input.
NID_INTEGRATED_PEN 0x00000004 An integrated pen digitizer is used for input.
NID_EXTERNAL_PEN 0x00000008 An external pen digitizer is used for input.
NID_MULTI_INPUT 0x00000040 An input digitizer with support for multiple inputs is used for input.
NID_READY 0x00000080 The input digitizer is ready for input. If this value is unset, it may mean that the tablet service is stopped, the digitizer is not supported, or digitizer drivers have not been installed.
Checking the NID_* values is a useful way of checking the capabilities of a user's computer to configure your application for touch, pen, or non-tablet input. For example, if you have a dynamic user interface (UI) and want to automatically configure some of it, you could check for NID_INTEGRATED_TOUCH, NID_MULTITOUCH, and could get the maximum number of touches the first time that a user runs your application.
On my laptop the return value is 0xc1 so NID_INTEGRATED_TOUCH|NID_MULTI_INPUT|NID_READY
Another idea is maybe GetSystemMetrics(LOCAL_MAXTOUCHES_INDEX)
const int LOCAL_MAXTOUCHES_INDEX = 95;
printf("mt=%lx\n", GetSystemMetrics(LOCAL_MAXTOUCHES_INDEX));
...but on my laptop (which does NOT have a touchscreen) it returns 2 (since I guess my touchscreen can do limited multi-touch gestures).
TouchCapabilities.TouchPresent seems to be a likely answer but not sure how to access it from a Win32 program:
https://stackoverflow.com/questions/13004911/how-to-specifically-detect-a-touch-screen-display-on-windows-8
https://docs.microsoft.com/en-us/uwp/api/Windows.Devices.Input.TouchCapabilities?redirectedfrom=MSDN&view=winrt-19041#Windows_Devices_Input_TouchCapabilities_TouchPresent
Reporter | ||
Comment 8•5 years ago
|
||
...but on my laptop (which does NOT have a touchscreen) it returns 2 (since I guess my touchscreen can do limited multi-touch gestures).
sorry, that should say my touchpad/trackpad can do limited multi-touch gestures.
Comment 9•5 years ago
|
||
Bugbug thinks this bug should belong to this component, but please revert this change in case of error.
Comment 10•5 years ago
|
||
The severity field is not set for this bug.
:jimm, could you have a look please?
For more information, please visit auto_nag documentation.
![]() |
||
Updated•5 years ago
|
Comment 11•4 years ago
|
||
Comment 12•4 years ago
|
||
Comment 13•4 years ago
|
||
Comment 14•4 years ago
|
||
Just wanted to add that same issue is found on Win10 desktop machines. Chrome (v 86.04240.75) and Edge (obviously) report correct results using test page (https://patrickhlauke.github.io/touch/pointer-hover-any-pointer-any-hover/) -- but Firefox (v 81.0.2) does not. See screenshots attached. Also note Win10 System Properties screen -- perhaps that is part of the problem (but only for FF.)
Comment 15•4 years ago
|
||
Happy to report that Fireforx (v 82.0) update resolves the issue for Win10 desktop. New test = only diff between Chrome and FF now is how any-pointer: coarse
returns false
for FF (correct). Time to file bug with Chromium about their incorrect results for same.
Comment 16•4 years ago
|
||
Comment 17•4 years ago
|
||
The issue persists in FF 82.0.2 (64bit) on Windows 10. I'll keep an eye on the progress, if you need any further info, just ask!
Comment 18•3 years ago
|
||
I'm going to remove the good-first-bug
tag from this bug since it requires testing across a variety of hardware, which new contributors don't necessarily have access to.
Comment 19•3 years ago
|
||
This general issue of wrong input media query values seems to also currently apply to desktop PCs that have graphics tablets attached (even if there is a mouse also present). Unplugging my tablet causes the test above to display the correct values, fixing some sites, and plugging it back in causes it to display incorrect values again, breaking some sites. Annoyingly, the main site that's broken by this for me is pixiv, an art site, and artists are more likely to have graphics tablets. (Various navigation arrows on pixiv disappear if the browser is in "coarse pointer, no hover" mode.)
A desktop PC with a graphics tablet attached should not be treated the same way as a tablet PC that has no mouse. In particular, graphics tablets are "precise" and do let the user "hover" the cursor over things, unlike touch screens. In the possible case that there's no way for the browser to know whether a tablet input device is a graphics tablet or a touch screen, it should base the media queries based on recent input event data rather than hardware presence. If there's no way to detect what kind of hardware the user is using based on input events, or the hardware lies about its capabilities, then there needs to be a user-facing toggle or override.
Using 103.b3 (developer edition) on Windows 10. "Tablet mode" is not enabled in Windows, but Windows Ink is enabled in my tablet software's settings.
Comment 20•2 years ago
|
||
On Firefox 104.0.2, both @media (hover: hover) and @media (pointer: fine) do not work for me on a Windows 10, a graphic tablet driver installed but not currently plugged.
Comment 21•2 years ago
|
||
OS:
Edition Windows 10 Pro
Version 22H2
Installiert am 10.06.2020
Betriebssystembuild 19045.2130
Leistung Windows Feature Experience Pack 120.2212.4180.0
Firefox-Build: 106.0.3 (64-Bit)
Input:
-Touchpad
-Mouse
-Touchscreen
Results:
pointer:none false
pointer:coarse true
pointer:fine false
hover:none true
hover:hover false
any-pointer:none false
any-pointer:coarse true
any-pointer:fine true
any-hover:none false
any-hover:hover true
Comment 22•1 years ago
|
||
Hi all,
Good news! The any-pointer
and any-hover
portions of this bug were fixed in Bug 1813979.
The pointer
and hover
are a bit more complex, but I will be fixing them as part of Bug 1851244.
Description
•