mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1021804 - Long press on news story links invoke context menu, r=kats, wesj
This commit is contained in:
parent
1aee7a85c2
commit
42cbcfa51c
@ -108,7 +108,8 @@ public class GeckoEvent {
|
|||||||
TELEMETRY_UI_SESSION_STOP(43),
|
TELEMETRY_UI_SESSION_STOP(43),
|
||||||
TELEMETRY_UI_EVENT(44),
|
TELEMETRY_UI_EVENT(44),
|
||||||
GAMEPAD_ADDREMOVE(45),
|
GAMEPAD_ADDREMOVE(45),
|
||||||
GAMEPAD_DATA(46);
|
GAMEPAD_DATA(46),
|
||||||
|
LONG_PRESS(47);
|
||||||
|
|
||||||
public final int value;
|
public final int value;
|
||||||
|
|
||||||
@ -420,6 +421,16 @@ public class GeckoEvent {
|
|||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a GeckoEvent that contains the data from the LongPressEvent, to be
|
||||||
|
* dispatched in CSS pixels relative to gecko's scroll position.
|
||||||
|
*/
|
||||||
|
public static GeckoEvent createLongPressEvent(MotionEvent m) {
|
||||||
|
GeckoEvent event = GeckoEvent.get(NativeGeckoEvent.LONG_PRESS);
|
||||||
|
event.initMotionEvent(m, false);
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
private void initMotionEvent(MotionEvent m, boolean keepInViewCoordinates) {
|
private void initMotionEvent(MotionEvent m, boolean keepInViewCoordinates) {
|
||||||
mAction = m.getActionMasked();
|
mAction = m.getActionMasked();
|
||||||
mTime = (System.currentTimeMillis() - SystemClock.elapsedRealtime()) + m.getEventTime();
|
mTime = (System.currentTimeMillis() - SystemClock.elapsedRealtime()) + m.getEventTime();
|
||||||
|
@ -1344,7 +1344,8 @@ class JavaPanZoomController
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLongPress(MotionEvent motionEvent) {
|
public void onLongPress(MotionEvent motionEvent) {
|
||||||
sendPointToGecko("Gesture:LongPress", motionEvent);
|
GeckoEvent e = GeckoEvent.createLongPressEvent(motionEvent);
|
||||||
|
GeckoAppShell.sendEventToGecko(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -702,7 +702,7 @@ var SelectionHandler = {
|
|||||||
attachCaret: function sh_attachCaret(aElement) {
|
attachCaret: function sh_attachCaret(aElement) {
|
||||||
// Ensure it isn't disabled, isn't handled by Android native dialog, and is editable text element
|
// Ensure it isn't disabled, isn't handled by Android native dialog, and is editable text element
|
||||||
if (aElement.disabled || InputWidgetHelper.hasInputWidget(aElement) || !this.isElementEditableText(aElement)) {
|
if (aElement.disabled || InputWidgetHelper.hasInputWidget(aElement) || !this.isElementEditableText(aElement)) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._initTargetInfo(aElement, this.TYPE_CURSOR);
|
this._initTargetInfo(aElement, this.TYPE_CURSOR);
|
||||||
@ -722,6 +722,8 @@ var SelectionHandler = {
|
|||||||
handles: [this.HANDLE_TYPE_MIDDLE]
|
handles: [this.HANDLE_TYPE_MIDDLE]
|
||||||
});
|
});
|
||||||
this._updateMenu();
|
this._updateMenu();
|
||||||
|
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
// Target initialization for both TYPE_CURSOR and TYPE_SELECTION
|
// Target initialization for both TYPE_CURSOR and TYPE_SELECTION
|
||||||
|
@ -2044,16 +2044,17 @@ var NativeWindow = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
contextmenus: {
|
contextmenus: {
|
||||||
items: {}, // a list of context menu items that we may show
|
items: {}, // a list of context menu items that we may show
|
||||||
DEFAULT_HTML5_ORDER: -1, // Sort order for HTML5 context menu items
|
DEFAULT_HTML5_ORDER: -1, // Sort order for HTML5 context menu items
|
||||||
|
|
||||||
init: function() {
|
init: function() {
|
||||||
Services.obs.addObserver(this, "Gesture:LongPress", false);
|
BrowserApp.deck.addEventListener("contextmenu", this.show.bind(this), false);
|
||||||
},
|
},
|
||||||
|
|
||||||
uninit: function() {
|
uninit: function() {
|
||||||
Services.obs.removeObserver(this, "Gesture:LongPress");
|
BrowserApp.deck.removeEventListener("contextmenu", this.show.bind(this), false);
|
||||||
},
|
},
|
||||||
|
|
||||||
add: function() {
|
add: function() {
|
||||||
@ -2295,7 +2296,7 @@ var NativeWindow = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Returns true if there are any context menu items to show
|
// Returns true if there are any context menu items to show
|
||||||
shouldShow: function() {
|
_shouldShow: function() {
|
||||||
for (let context in this.menus) {
|
for (let context in this.menus) {
|
||||||
let menu = this.menus[context];
|
let menu = this.menus[context];
|
||||||
if (menu.length > 0) {
|
if (menu.length > 0) {
|
||||||
@ -2378,36 +2379,51 @@ var NativeWindow = {
|
|||||||
* any html5 context menus we are about to show, and fire some local notifications
|
* any html5 context menus we are about to show, and fire some local notifications
|
||||||
* for chrome consumers to do lazy menuitem construction
|
* for chrome consumers to do lazy menuitem construction
|
||||||
*/
|
*/
|
||||||
_sendToContent: function(x, y) {
|
show: function(event) {
|
||||||
let target = this._findTarget(x, y);
|
// Android Long-press / contextmenu event provides clientX/Y data. This is not provided
|
||||||
if (!target)
|
// by mochitest: test_browserElement_inproc_ContextmenuEvents.html.
|
||||||
|
if (!event.clientX || !event.clientY) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this._target = target;
|
// Find the target of the long-press / contextmenu event.
|
||||||
|
this._target = this._findTarget(event.clientX, event.clientY);
|
||||||
|
if (!this._target) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Services.obs.notifyObservers(null, "before-build-contextmenu", "");
|
// Try to build a list of contextmenu items. If successful, actually show the
|
||||||
this._buildMenu(x, y);
|
// native context menu by passing the list to Java.
|
||||||
|
this._buildMenu(event.clientX, event.clientY);
|
||||||
|
if (this._shouldShow()) {
|
||||||
|
BrowserEventHandler._cancelTapHighlight();
|
||||||
|
|
||||||
// only send the contextmenu event to content if we are planning to show a context menu (i.e. not on every long tap)
|
// Consume / preventDefault the event, and show the contextmenu.
|
||||||
if (this.shouldShow()) {
|
event.preventDefault();
|
||||||
let event = target.ownerDocument.createEvent("MouseEvent");
|
this._innerShow(this._target, event.clientX, event.clientY);
|
||||||
event.initMouseEvent("contextmenu", true, true, target.defaultView,
|
this._target = null;
|
||||||
0, x, y, x, y, false, false, false, false,
|
|
||||||
0, null);
|
|
||||||
target.ownerDocument.defaultView.addEventListener("contextmenu", this, false);
|
|
||||||
target.dispatchEvent(event);
|
|
||||||
} else {
|
|
||||||
this.menus = null;
|
|
||||||
Services.obs.notifyObservers({target: target, x: x, y: y}, "context-menu-not-shown", "");
|
|
||||||
|
|
||||||
if (SelectionHandler.canSelect(target)) {
|
return;
|
||||||
if (!SelectionHandler.startSelection(target, {
|
}
|
||||||
mode: SelectionHandler.SELECT_AT_POINT,
|
|
||||||
x: x,
|
// If no context-menu for long-press event, it may be meant to trigger text-selection.
|
||||||
y: y
|
this.menus = null;
|
||||||
})) {
|
Services.obs.notifyObservers(
|
||||||
SelectionHandler.attachCaret(target);
|
{target: this._target, x: event.clientX, y: event.clientY}, "context-menu-not-shown", "");
|
||||||
}
|
|
||||||
|
if (SelectionHandler.canSelect(this._target)) {
|
||||||
|
// If textSelection WORD is successful,
|
||||||
|
// consume / preventDefault the context menu event.
|
||||||
|
if (SelectionHandler.startSelection(this._target,
|
||||||
|
{ mode: SelectionHandler.SELECT_AT_POINT, x: event.clientX, y: event.clientY })) {
|
||||||
|
event.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If textSelection caret-attachment is successful,
|
||||||
|
// consume / preventDefault the context menu event.
|
||||||
|
if (SelectionHandler.attachCaret(this._target)) {
|
||||||
|
event.preventDefault();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -2477,17 +2493,6 @@ var NativeWindow = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Actually shows the native context menu by passing a list of context menu items to
|
|
||||||
// show to the Java.
|
|
||||||
_show: function(aEvent) {
|
|
||||||
let popupNode = this._target;
|
|
||||||
this._target = null;
|
|
||||||
if (aEvent.defaultPrevented || !popupNode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._innerShow(popupNode, aEvent.clientX, aEvent.clientY);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Walks the DOM tree to find a title from a node
|
// Walks the DOM tree to find a title from a node
|
||||||
_findTitle: function(node) {
|
_findTitle: function(node) {
|
||||||
let title = "";
|
let title = "";
|
||||||
@ -2645,20 +2650,6 @@ var NativeWindow = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Called when the contextmenu is done propagating to content. If the event wasn't cancelled, will show a contextmenu.
|
|
||||||
handleEvent: function(aEvent) {
|
|
||||||
BrowserEventHandler._cancelTapHighlight();
|
|
||||||
aEvent.target.ownerDocument.defaultView.removeEventListener("contextmenu", this, false);
|
|
||||||
this._show(aEvent);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Called when a long press is observed in the native Java frontend. Will start the process of generating/showing a contextmenu.
|
|
||||||
observe: function(aSubject, aTopic, aData) {
|
|
||||||
let data = JSON.parse(aData);
|
|
||||||
// content gets first crack at cancelling context menus
|
|
||||||
this._sendToContent(data.x, data.y);
|
|
||||||
},
|
|
||||||
|
|
||||||
// XXX - These are stolen from Util.js, we should remove them if we bring it back
|
// XXX - These are stolen from Util.js, we should remove them if we bring it back
|
||||||
makeURLAbsolute: function makeURLAbsolute(base, url) {
|
makeURLAbsolute: function makeURLAbsolute(base, url) {
|
||||||
// Note: makeURI() will throw if url is not a valid URI
|
// Note: makeURI() will throw if url is not a valid URI
|
||||||
|
@ -454,6 +454,7 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case MOTION_EVENT:
|
case MOTION_EVENT:
|
||||||
|
case LONG_PRESS:
|
||||||
mTime = jenv->GetLongField(jobj, jTimeField);
|
mTime = jenv->GetLongField(jobj, jTimeField);
|
||||||
mMetaState = jenv->GetIntField(jobj, jMetaStateField);
|
mMetaState = jenv->GetIntField(jobj, jMetaStateField);
|
||||||
mCount = jenv->GetIntField(jobj, jCountField);
|
mCount = jenv->GetIntField(jobj, jCountField);
|
||||||
|
@ -720,6 +720,7 @@ public:
|
|||||||
TELEMETRY_UI_EVENT = 44,
|
TELEMETRY_UI_EVENT = 44,
|
||||||
GAMEPAD_ADDREMOVE = 45,
|
GAMEPAD_ADDREMOVE = 45,
|
||||||
GAMEPAD_DATA = 46,
|
GAMEPAD_DATA = 46,
|
||||||
|
LONG_PRESS = 47,
|
||||||
dummy_java_enum_list_end
|
dummy_java_enum_list_end
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -865,6 +865,31 @@ nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LongPress events mostly trigger contextmenu options, but can also lead to
|
||||||
|
// textSelection processing.
|
||||||
|
case AndroidGeckoEvent::LONG_PRESS: {
|
||||||
|
win->UserActivity();
|
||||||
|
|
||||||
|
nsCOMPtr<nsIObserverService> obsServ = mozilla::services::GetObserverService();
|
||||||
|
obsServ->NotifyObservers(nullptr, "before-build-contextmenu", nullptr);
|
||||||
|
|
||||||
|
nsIntPoint pt;
|
||||||
|
const nsTArray<nsIntPoint>& points = ae->Points();
|
||||||
|
if (points.Length() > 0) {
|
||||||
|
pt = nsIntPoint(points[0].x, points[0].y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clamp our point within bounds, and locate the target element for the event.
|
||||||
|
pt.x = clamped(pt.x, 0, std::max(gAndroidBounds.width - 1, 0));
|
||||||
|
pt.y = clamped(pt.y, 0, std::max(gAndroidBounds.height - 1, 0));
|
||||||
|
nsWindow *target = win->FindWindowForPoint(pt);
|
||||||
|
if (target) {
|
||||||
|
// Send the contextmenu event to Gecko.
|
||||||
|
target->OnContextmenuEvent(ae);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case AndroidGeckoEvent::NATIVE_GESTURE_EVENT: {
|
case AndroidGeckoEvent::NATIVE_GESTURE_EVENT: {
|
||||||
nsIntPoint pt(0,0);
|
nsIntPoint pt(0,0);
|
||||||
const nsTArray<nsIntPoint>& points = ae->Points();
|
const nsTArray<nsIntPoint>& points = ae->Points();
|
||||||
@ -1000,6 +1025,36 @@ nsWindow::OnMouseEvent(AndroidGeckoEvent *ae)
|
|||||||
DispatchEvent(&event);
|
DispatchEvent(&event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsWindow::OnContextmenuEvent(AndroidGeckoEvent *ae)
|
||||||
|
{
|
||||||
|
nsRefPtr<nsWindow> kungFuDeathGrip(this);
|
||||||
|
|
||||||
|
CSSPoint pt;
|
||||||
|
const nsTArray<nsIntPoint>& points = ae->Points();
|
||||||
|
if (points.Length() > 0) {
|
||||||
|
pt = CSSPoint(points[0].x, points[0].y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the contextmenu event.
|
||||||
|
WidgetMouseEvent contextMenuEvent(true, NS_CONTEXTMENU, this,
|
||||||
|
WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
|
||||||
|
contextMenuEvent.refPoint =
|
||||||
|
LayoutDeviceIntPoint(RoundedToInt(pt * GetDefaultScale()));
|
||||||
|
|
||||||
|
nsEventStatus contextMenuStatus;
|
||||||
|
DispatchEvent(&contextMenuEvent, contextMenuStatus);
|
||||||
|
|
||||||
|
// If the contextmenu event was consumed (preventDefault issued), we follow with a
|
||||||
|
// touchcancel event. This avoids followup touchend events passsing through and
|
||||||
|
// triggering further element behaviour such as link-clicks.
|
||||||
|
if (contextMenuStatus == nsEventStatus_eConsumeNoDefault) {
|
||||||
|
WidgetTouchEvent canceltouchEvent = ae->MakeTouchEvent(this);
|
||||||
|
canceltouchEvent.message = NS_TOUCH_CANCEL;
|
||||||
|
DispatchEvent(&canceltouchEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool nsWindow::OnMultitouchEvent(AndroidGeckoEvent *ae)
|
bool nsWindow::OnMultitouchEvent(AndroidGeckoEvent *ae)
|
||||||
{
|
{
|
||||||
nsRefPtr<nsWindow> kungFuDeathGrip(this);
|
nsRefPtr<nsWindow> kungFuDeathGrip(this);
|
||||||
|
@ -48,6 +48,7 @@ public:
|
|||||||
|
|
||||||
nsWindow* FindWindowForPoint(const nsIntPoint& pt);
|
nsWindow* FindWindowForPoint(const nsIntPoint& pt);
|
||||||
|
|
||||||
|
void OnContextmenuEvent(mozilla::AndroidGeckoEvent *ae);
|
||||||
bool OnMultitouchEvent(mozilla::AndroidGeckoEvent *ae);
|
bool OnMultitouchEvent(mozilla::AndroidGeckoEvent *ae);
|
||||||
void OnNativeGestureEvent(mozilla::AndroidGeckoEvent *ae);
|
void OnNativeGestureEvent(mozilla::AndroidGeckoEvent *ae);
|
||||||
void OnMouseEvent(mozilla::AndroidGeckoEvent *ae);
|
void OnMouseEvent(mozilla::AndroidGeckoEvent *ae);
|
||||||
|
Loading…
Reference in New Issue
Block a user