mirror of
https://github.com/zen-browser/desktop.git
synced 2026-08-04 06:08:31 +00:00
gh-14830: Fixed dragging on text makes the entire window to move (gh-14833)
This commit is contained in:
@@ -23,10 +23,10 @@
|
||||
# How far (in CSS pixels) the mouse may travel past the window bounds after
|
||||
# leaving the window before the hovered sidebar/toolbar is collapsed
|
||||
- name: zen.view.compact.outside-window-edge-offset.horizontal
|
||||
value: 250
|
||||
value: 200
|
||||
|
||||
- name: zen.view.compact.outside-window-edge-offset.vertical
|
||||
value: 150
|
||||
value: 100
|
||||
|
||||
- name: zen.view.compact.animate-sidebar
|
||||
value: true
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
value: true
|
||||
|
||||
- name: zen.view.drag-window-from-content.height-percentage
|
||||
value: 30
|
||||
value: 10
|
||||
|
||||
- name: zen.view.borderless-fullscreen
|
||||
value: true
|
||||
|
||||
@@ -998,7 +998,7 @@ window.gZenCompactModeManager = {
|
||||
try {
|
||||
lazy.zenMouseTracker.registerWindow(window, screenEdge, maxEdgeOffset);
|
||||
} catch (e) {
|
||||
// The platform can't track the global mouse position (e.g. Wayland)
|
||||
// The platform can't track the global mouse position (e.g. Linux)
|
||||
return false;
|
||||
}
|
||||
this._outsideTrackedElement = target;
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ZenMouseTracker.h"
|
||||
#include "ZenMouseTrackerInternal.h"
|
||||
|
||||
#include "mozilla/WidgetUtilsGtk.h"
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
#include <glib.h>
|
||||
|
||||
namespace zen {
|
||||
|
||||
// X11 has no way to observe global pointer motion events without pulling in
|
||||
// extra library dependencies (XInput2 raw events need libXi, XRecord needs
|
||||
// libXtst), so we poll the pointer from a GLib timeout instead. This only
|
||||
// runs during the short periods a window is registered, i.e. while the
|
||||
// pointer is outside of the window with an edge element held open.
|
||||
static guint sTimerId = 0;
|
||||
|
||||
static gboolean OnTimer(gpointer) {
|
||||
GdkDevice* pointer = mozilla::widget::GdkGetPointer();
|
||||
if (pointer) {
|
||||
gint x = 0, y = 0;
|
||||
gdk_device_get_position(pointer, nullptr, &x, &y);
|
||||
ZenMouseTracker::OnNativePointerMove(
|
||||
mozilla::DesktopPoint(float(x), float(y)));
|
||||
}
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
nsresult ZenNativeMouseMonitor::Start() {
|
||||
if (!mozilla::widget::GdkIsX11Display()) {
|
||||
// Wayland doesn't expose the pointer position while it is outside of our
|
||||
// surfaces
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
if (!sTimerId) {
|
||||
sTimerId = g_timeout_add(1000 / 60, OnTimer, nullptr);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void ZenNativeMouseMonitor::Stop() {
|
||||
if (sTimerId) {
|
||||
g_source_remove(sTimerId);
|
||||
sTimerId = 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace zen
|
||||
@@ -7,7 +7,10 @@
|
||||
|
||||
#include "nscore.h"
|
||||
|
||||
#if defined(XP_MACOSX) || defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
|
||||
// On Linux there's no reliable way to observe the global pointer (Wayland
|
||||
// doesn't expose it at all), so we don't track there and callers fall back
|
||||
// to their timeout based behavior
|
||||
#if defined(XP_MACOSX) || defined(XP_WIN)
|
||||
# define NS_ZEN_CAN_TRACK_POINTER 1
|
||||
#endif
|
||||
|
||||
@@ -25,7 +28,7 @@ class ZenNativeMouseMonitor final {
|
||||
* @brief Start delivering pointer moves. Safe to call while already
|
||||
* started.
|
||||
* @throws NS_ERROR_NOT_AVAILABLE when the platform cannot observe the
|
||||
* global pointer (e.g. on Wayland).
|
||||
* global pointer (e.g. on Linux).
|
||||
*/
|
||||
static nsresult Start();
|
||||
/**
|
||||
|
||||
@@ -33,9 +33,5 @@ if CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
|
||||
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "windows":
|
||||
SOURCES += ["ZenMouseTrackerWin.cpp"]
|
||||
|
||||
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
|
||||
SOURCES += ["ZenMouseTrackerGtk.cpp"]
|
||||
CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"]
|
||||
|
||||
FINAL_LIBRARY = "xul"
|
||||
XPIDL_MODULE = "zen_compact_mode"
|
||||
|
||||
@@ -28,7 +28,7 @@ interface nsIZenMouseTracker : nsISupports {
|
||||
* @param maxEdgeOffset How far (in CSS pixels) the pointer may travel past
|
||||
* screenEdge before the exit notification is fired.
|
||||
* @throws NS_ERROR_NOT_AVAILABLE when the platform cannot track the global
|
||||
* pointer position (e.g. on Wayland).
|
||||
* pointer position (e.g. on Linux).
|
||||
*/
|
||||
void registerWindow(in mozIDOMWindowProxy window, in ACString screenEdge,
|
||||
in float maxEdgeOffset);
|
||||
|
||||
@@ -10,36 +10,25 @@ XPCOMUtils.defineLazyPreferenceGetter(
|
||||
lazy,
|
||||
"dragRegionHeightPercentage",
|
||||
"zen.view.drag-window-from-content.height-percentage",
|
||||
30
|
||||
10
|
||||
);
|
||||
|
||||
// A small threshold to allow for minor mouse jitter during a normal click.
|
||||
// Anything beyond this is considered an intentional window drag.
|
||||
const DRAG_START_THRESHOLD_PX = 4;
|
||||
XPCOMUtils.defineLazyServiceGetter(
|
||||
lazy,
|
||||
"zenWindowDragUtils",
|
||||
"@mozilla.org/zen/window-drag-utils;1",
|
||||
Ci.nsIZenWindowDragUtils
|
||||
);
|
||||
|
||||
const kInteractiveTags = new Set([
|
||||
"a",
|
||||
"area",
|
||||
"audio",
|
||||
"button",
|
||||
"canvas",
|
||||
"details",
|
||||
"dialog",
|
||||
"embed",
|
||||
"frame",
|
||||
"iframe",
|
||||
"img",
|
||||
"input",
|
||||
"label",
|
||||
"menu",
|
||||
"object",
|
||||
"optgroup",
|
||||
"option",
|
||||
"select",
|
||||
"summary",
|
||||
"textarea",
|
||||
"video",
|
||||
]);
|
||||
// Movement below this is considered a click, not a window drag. Fast
|
||||
// clicks commonly slide a few pixels (especially on trackpads), and once
|
||||
// the native move starts the OS swallows the mouseup — so keep this
|
||||
// comfortably above click jitter or clicks in the region get lost.
|
||||
const DRAG_START_THRESHOLD_PX = 10;
|
||||
|
||||
// Content that drives its own mouse interaction without being
|
||||
// interactive HTML content in the spec sense.
|
||||
const kAppContentTags = new Set(["audio", "canvas", "video"]);
|
||||
|
||||
const kInteractiveRoles = new Set([
|
||||
"button",
|
||||
@@ -282,21 +271,41 @@ export class ZenWindowDragChild extends JSWindowActorChild {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return this.#hasInteractiveCursor(target);
|
||||
return (
|
||||
this.#hasInteractiveCursor(target) || this.#isOverSelectableText(event)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A drag starting over selectable text should select it, not move the
|
||||
* window. rangeParent is the caret position Gecko computed for the
|
||||
* event, so this also covers empty space on the same line, where
|
||||
* dragging extends a selection.
|
||||
*
|
||||
* @param {MouseEvent} event
|
||||
*/
|
||||
#isOverSelectableText(event) {
|
||||
const node = event.rangeParent;
|
||||
if (node?.nodeType !== Node.TEXT_NODE) {
|
||||
return false;
|
||||
}
|
||||
const parent = node.parentElement;
|
||||
return (
|
||||
!parent ||
|
||||
this.contentWindow.getComputedStyle(parent).userSelect !== "none"
|
||||
);
|
||||
}
|
||||
|
||||
#isInteractiveElement(element) {
|
||||
if (kInteractiveTags.has(element.localName)) {
|
||||
// Gecko's own notion of interactive, editable or draggable content.
|
||||
if (lazy.zenWindowDragUtils.isInteractiveContent(element)) {
|
||||
return true;
|
||||
}
|
||||
// Covers [draggable="true"] and elements draggable by default,
|
||||
// like links and images.
|
||||
if (element.draggable) {
|
||||
return true;
|
||||
}
|
||||
if (element.isContentEditable) {
|
||||
if (kAppContentTags.has(element.localName)) {
|
||||
return true;
|
||||
}
|
||||
// Declarative signals the engine check doesn't cover: ARIA widget
|
||||
// roles and explicit tab stops.
|
||||
if (
|
||||
element.tabIndex >= 0 &&
|
||||
element !== this.document.body &&
|
||||
@@ -305,19 +314,7 @@ export class ZenWindowDragChild extends JSWindowActorChild {
|
||||
return true;
|
||||
}
|
||||
const role = element.getAttribute?.("role");
|
||||
if (role && kInteractiveRoles.has(role.toLowerCase())) {
|
||||
return true;
|
||||
}
|
||||
// Inline event handlers are a strong hint of a custom widget.
|
||||
if (
|
||||
element.onclick ||
|
||||
element.onmousedown ||
|
||||
element.onpointerdown ||
|
||||
element.ondragstart
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return !!role && kInteractiveRoles.has(role.toLowerCase());
|
||||
}
|
||||
|
||||
#hasInteractiveCursor(element) {
|
||||
|
||||
13
src/zen/window-drag/components.conf
Normal file
13
src/zen/window-drag/components.conf
Normal file
@@ -0,0 +1,13 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
Classes = [
|
||||
{
|
||||
'cid': '{13df8214-70b4-4d7b-af8c-86081cbc5801}',
|
||||
'interfaces': ['nsIZenWindowDragUtils'],
|
||||
'contract_ids': ['@mozilla.org/zen/window-drag-utils;1'],
|
||||
'type': 'zen::nsZenWindowDragUtils',
|
||||
'headers': ['mozilla/nsZenWindowDragUtils.h'],
|
||||
},
|
||||
]
|
||||
@@ -6,3 +6,22 @@ FINAL_TARGET_FILES.actors += [
|
||||
"actors/ZenWindowDragChild.sys.mjs",
|
||||
"actors/ZenWindowDragParent.sys.mjs",
|
||||
]
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
"nsIZenWindowDragUtils.idl",
|
||||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
"nsZenWindowDragUtils.h",
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
"nsZenWindowDragUtils.cpp",
|
||||
]
|
||||
|
||||
XPCOM_MANIFESTS += [
|
||||
"components.conf",
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = "xul"
|
||||
XPIDL_MODULE = "zen_window_drag"
|
||||
|
||||
23
src/zen/window-drag/nsIZenWindowDragUtils.idl
Normal file
23
src/zen/window-drag/nsIZenWindowDragUtils.idl
Normal file
@@ -0,0 +1,23 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
webidl Node;
|
||||
|
||||
/**
|
||||
* @brief Utilities for deciding when a content-area gesture may move
|
||||
* the window.
|
||||
*/
|
||||
[scriptable, uuid(595ba6e8-abe2-4619-acad-cf492306f608)]
|
||||
interface nsIZenWindowDragUtils : nsISupports {
|
||||
/**
|
||||
* @brief Whether the node is content the page expects the user to
|
||||
* interact with: interactive HTML content (links, form controls,
|
||||
* etc.), editable content, or content that can be dragged
|
||||
* (draggable attribute, links and loaded images).
|
||||
* @param node The node to check.
|
||||
*/
|
||||
boolean isInteractiveContent(in Node node);
|
||||
};
|
||||
33
src/zen/window-drag/nsZenWindowDragUtils.cpp
Normal file
33
src/zen/window-drag/nsZenWindowDragUtils.cpp
Normal file
@@ -0,0 +1,33 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsZenWindowDragUtils.h"
|
||||
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIContent.h"
|
||||
|
||||
namespace zen {
|
||||
|
||||
NS_IMPL_ISUPPORTS(nsZenWindowDragUtils, nsIZenWindowDragUtils)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsZenWindowDragUtils::IsInteractiveContent(nsINode* aNode, bool* aResult) {
|
||||
*aResult = false;
|
||||
NS_ENSURE_ARG_POINTER(aNode);
|
||||
|
||||
nsIContent* content = nsIContent::FromNode(aNode);
|
||||
if (!content) {
|
||||
return NS_OK;
|
||||
}
|
||||
if (content->IsEditable() || nsContentUtils::ContentIsDraggable(content)) {
|
||||
*aResult = true;
|
||||
return NS_OK;
|
||||
}
|
||||
mozilla::dom::Element* element = mozilla::dom::Element::FromNode(content);
|
||||
*aResult = element && element->IsInteractiveHTMLContent();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace zen
|
||||
27
src/zen/window-drag/nsZenWindowDragUtils.h
Normal file
27
src/zen/window-drag/nsZenWindowDragUtils.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef zen_nsZenWindowDragUtils_h_
|
||||
#define zen_nsZenWindowDragUtils_h_
|
||||
|
||||
#include "nsIZenWindowDragUtils.h"
|
||||
|
||||
#define ZEN_WINDOW_DRAG_UTILS_CONTRACTID "@mozilla.org/zen/window-drag-utils;1"
|
||||
|
||||
namespace zen {
|
||||
|
||||
class nsZenWindowDragUtils final : public nsIZenWindowDragUtils {
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIZENWINDOWDRAGUTILS
|
||||
|
||||
public:
|
||||
nsZenWindowDragUtils() = default;
|
||||
|
||||
private:
|
||||
~nsZenWindowDragUtils() = default;
|
||||
};
|
||||
|
||||
} // namespace zen
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user