Skip to content

Commit e8cd301

Browse files
committed
Merge pull request #34573 from WoH/firefox-right-click-bug
Prevent non-primary mouse button clicks from triggering click events
1 parent ae965e7 commit e8cd301

File tree

7 files changed

+100
-6
lines changed

7 files changed

+100
-6
lines changed

actionview/CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,16 @@
1+
* Prevent non-primary mouse keys from triggering Rails UJS click handlers.
2+
Firefox fires click events even if the click was triggered by non-primary mouse keys such as right- or scroll-wheel-clicks.
3+
For example, right-clicking a link such as the one described below (with an underlying ajax request registered on click) should not cause that request to occur.
4+
5+
```
6+
<%= link_to 'Remote', remote_path, class: 'remote', remote: true, data: { type: :json } %>
7+
```
8+
9+
Fixes #34541
10+
11+
*Wolfgang Hobmaier*
12+
13+
114
## Rails 5.2.2 (December 04, 2018) ##
215
316
* No changes.

actionview/app/assets/javascripts/rails-ujs/features/remote.coffee

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,12 @@ Rails.formSubmitButtonClick = (e) ->
8282
setData(form, 'ujs:submit-button-formaction', button.getAttribute('formaction'))
8383
setData(form, 'ujs:submit-button-formmethod', button.getAttribute('formmethod'))
8484

85-
Rails.handleMetaClick = (e) ->
85+
Rails.preventInsignificantClick = (e) ->
8686
link = this
8787
method = (link.getAttribute('data-method') or 'GET').toUpperCase()
8888
data = link.getAttribute('data-params')
8989
metaClick = e.metaKey or e.ctrlKey
90-
e.stopImmediatePropagation() if metaClick and method is 'GET' and not data
90+
insignificantMetaClick = metaClick and method is 'GET' and not data
91+
primaryMouseKey = e.button is 0
92+
e.stopImmediatePropagation() if not primaryMouseKey or insignificantMetaClick
93+

actionview/app/assets/javascripts/rails-ujs/start.coffee

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
getData, $
44
refreshCSRFTokens, CSRFProtection
55
enableElement, disableElement, handleDisabledElement
6-
handleConfirm
7-
handleRemote, formSubmitButtonClick, handleMetaClick
6+
handleConfirm, preventInsignificantClick
7+
handleRemote, formSubmitButtonClick,
88
handleMethod
99
} = Rails
1010

@@ -35,13 +35,14 @@ Rails.start = ->
3535
delegate document, Rails.buttonDisableSelector, 'ajax:complete', enableElement
3636
delegate document, Rails.buttonDisableSelector, 'ajax:stopped', enableElement
3737

38+
delegate document, Rails.linkClickSelector, 'click', preventInsignificantClick
3839
delegate document, Rails.linkClickSelector, 'click', handleDisabledElement
3940
delegate document, Rails.linkClickSelector, 'click', handleConfirm
40-
delegate document, Rails.linkClickSelector, 'click', handleMetaClick
4141
delegate document, Rails.linkClickSelector, 'click', disableElement
4242
delegate document, Rails.linkClickSelector, 'click', handleRemote
4343
delegate document, Rails.linkClickSelector, 'click', handleMethod
4444

45+
delegate document, Rails.buttonClickSelector, 'click', preventInsignificantClick
4546
delegate document, Rails.buttonClickSelector, 'click', handleDisabledElement
4647
delegate document, Rails.buttonClickSelector, 'click', handleConfirm
4748
delegate document, Rails.buttonClickSelector, 'click', disableElement
@@ -60,6 +61,7 @@ Rails.start = ->
6061
delegate document, Rails.formSubmitSelector, 'ajax:send', disableElement
6162
delegate document, Rails.formSubmitSelector, 'ajax:complete', enableElement
6263

64+
delegate document, Rails.formInputClickSelector, 'click', preventInsignificantClick
6365
delegate document, Rails.formInputClickSelector, 'click', handleDisabledElement
6466
delegate document, Rails.formInputClickSelector, 'click', handleConfirm
6567
delegate document, Rails.formInputClickSelector, 'click', formSubmitButtonClick

actionview/test/ujs/public/test/data-disable-with.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,25 @@ asyncTest('ctrl-clicking on a link does not disables the link', 6, function() {
322322
start()
323323
})
324324

325+
asyncTest('right/mouse-wheel-clicking on a link does not disables the link', 10, function() {
326+
var link = $('a[data-disable-with]')
327+
328+
App.checkEnabledState(link, 'Click me')
329+
330+
link.triggerNative('click', { button: 1 })
331+
App.checkEnabledState(link, 'Click me')
332+
333+
link.triggerNative('click', { button: 1 })
334+
App.checkEnabledState(link, 'Click me')
335+
336+
link.triggerNative('click', { button: 2 })
337+
App.checkEnabledState(link, 'Click me')
338+
339+
link.triggerNative('click', { button: 2 })
340+
App.checkEnabledState(link, 'Click me')
341+
start()
342+
})
343+
325344
asyncTest('button[data-remote][data-disable-with] disables and re-enables', 6, function() {
326345
var button = $('button[data-remote][data-disable-with]')
327346

actionview/test/ujs/public/test/data-disable.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,25 @@ asyncTest('ctrl-clicking on a link does not disables the link', 6, function() {
250250
start()
251251
})
252252

253+
asyncTest('right/mouse-wheel-clicking on a link does not disable the link', 10, function() {
254+
var link = $('a[data-disable]')
255+
256+
App.checkEnabledState(link, 'Click me')
257+
258+
link.triggerNative('click', { button: 1 })
259+
App.checkEnabledState(link, 'Click me')
260+
261+
link.triggerNative('click', { button: 1 })
262+
App.checkEnabledState(link, 'Click me')
263+
264+
link.triggerNative('click', { button: 2 })
265+
App.checkEnabledState(link, 'Click me')
266+
267+
link.triggerNative('click', { button: 2 })
268+
App.checkEnabledState(link, 'Click me')
269+
start()
270+
})
271+
253272
asyncTest('button[data-remote][data-disable] disables and re-enables', 6, function() {
254273
var button = $('button[data-remote][data-disable]')
255274

actionview/test/ujs/public/test/data-remote.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,25 @@ asyncTest('ctrl-clicking on a link does not fire ajaxyness', 0, function() {
6363
setTimeout(function() { start() }, 13)
6464
})
6565

66+
asyncTest('right/mouse-wheel-clicking on a link does not fire ajaxyness', 0, function() {
67+
var link = $('a[data-remote]')
68+
69+
// Ideally, we'd setup an iframe to intercept normal link clicks
70+
// and add a test to make sure the iframe:loaded event is triggered.
71+
// However, jquery doesn't actually cause a native `click` event and
72+
// follow links using `trigger('click')`, it only fires bindings.
73+
link
74+
.removeAttr('data-params')
75+
.bindNative('ajax:beforeSend', function() {
76+
ok(false, 'ajax should not be triggered')
77+
})
78+
79+
link.triggerNative('click', { button: 1 })
80+
link.triggerNative('click', { button: 2 })
81+
82+
setTimeout(function() { start() }, 13)
83+
})
84+
6685
asyncTest('ctrl-clicking on a link still fires ajax for non-GET links and for links with "data-params"', 2, function() {
6786
var link = $('a[data-remote]')
6887

@@ -148,6 +167,25 @@ asyncTest('clicking on a button with data-remote attribute', 5, function() {
148167
.triggerNative('click')
149168
})
150169

170+
asyncTest('right/mouse-wheel-clicking on a button with data-remote attribute does not fire ajaxyness', 0, function() {
171+
var button = $('button[data-remote]')
172+
173+
// Ideally, we'd setup an iframe to intercept normal link clicks
174+
// and add a test to make sure the iframe:loaded event is triggered.
175+
// However, jquery doesn't actually cause a native `click` event and
176+
// follow links using `trigger('click')`, it only fires bindings.
177+
button
178+
.removeAttr('data-params')
179+
.bindNative('ajax:beforeSend', function() {
180+
ok(false, 'ajax should not be triggered')
181+
})
182+
183+
button.triggerNative('click', { button: 1 })
184+
button.triggerNative('click', { button: 2 })
185+
186+
setTimeout(function() { start() }, 13)
187+
})
188+
151189
asyncTest('changing a select option with data-remote attribute', 5, function() {
152190
buildSelect()
153191

actionview/test/ujs/public/test/settings.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ try {
7070
} catch (e) {
7171
_MouseEvent = function(type, options) {
7272
var evt = document.createEvent('MouseEvents')
73-
evt.initMouseEvent(type, options.bubbles, options.cancelable, window, options.detail, 0, 0, 80, 20, options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, 0, null)
73+
evt.initMouseEvent(type, options.bubbles, options.cancelable, window, options.detail, 0, 0, 80, 20, options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, null)
7474
return evt
7575
}
7676
}

0 commit comments

Comments
 (0)