Skip to content

Commit a931b9d

Browse files
committed
[fixes #1571] Prevent simulated events from causing nested run-loops.
It turns out, jQuery's simulated events are triggered synchronously rather then asynchronously. This means, in some circumstances, when fired they can cause nested run-loops. This can have some very unexpected consequences. @krisselden / @stefanpenner
1 parent b7905e7 commit a931b9d

File tree

3 files changed

+63
-10
lines changed

3 files changed

+63
-10
lines changed

packages/ember-views/lib/system/event_dispatcher.js

+17-7
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,7 @@ Ember.EventDispatcher = Ember.Object.extend(/** @scope Ember.EventDispatcher.pro
152152
var actionId = Ember.$(evt.currentTarget).attr('data-ember-action'),
153153
action = Ember.Handlebars.ActionHelper.registeredActions[actionId];
154154

155-
// We have to check for action here since in some cases, jQuery will trigger
156-
// an event on `removeChild` (i.e. focusout) after we've already torn down the
157-
// action handlers for the view.
158-
if (action && action.eventName === eventName) {
155+
if (action.eventName === eventName) {
159156
return action.handler(evt);
160157
}
161158
}, this);
@@ -192,9 +189,22 @@ Ember.EventDispatcher = Ember.Object.extend(/** @scope Ember.EventDispatcher.pro
192189
},
193190

194191
_bubbleEvent: function(view, evt, eventName) {
195-
return Ember.run(function() {
196-
return view.handleEvent(eventName, evt);
197-
});
192+
if (Ember.run.currentRunLoop){
193+
// this works around an issue caused when simulated events
194+
// are triggerd. Simulated events occure synchronously rather
195+
// then on next tick. This causes an unexpected nested run-loop,
196+
// resulting in negative behaviour.
197+
//
198+
// for reference:
199+
// https://github.com/emberjs/ember.js/commit/aafb5eb5693dccf04dd0951385b4c6bb6db7ae46
200+
return Ember.run.schedule('actions', function(){
201+
return view.handleEvent(eventName, evt);
202+
});
203+
} else {
204+
return Ember.run(function() {
205+
return view.handleEvent(eventName, evt);
206+
});
207+
}
198208
},
199209

200210
destroy: function() {

packages/ember-views/tests/views/view/destroy_test.js

+45
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,48 @@ test("should teardown viewName on parentView when childView is destroyed", funct
2020
});
2121
});
2222

23+
module("Ember.View#destroy - synchronous synthesised event edge-case", {
24+
setup: function() {
25+
dispatcher = Ember.run(Ember.EventDispatcher, 'create');
26+
dispatcher.setup({}, '#qunit-fixture');
27+
},
28+
29+
teardown: function() {
30+
Ember.run(dispatcher, 'destroy');
31+
32+
if (parentView) {
33+
Ember.run(parentView, 'destroy');
34+
}
35+
}
36+
});
37+
38+
var parentView, dispatcher;
39+
40+
test("focusOut event caused by removal of an in-focus text field should fire on the actions queue", function() {
41+
expect(3);
42+
43+
var focusOutCount = 0;
44+
45+
parentView = Ember.View.create({
46+
focusOut: function(e) {
47+
focusOutCount++;
48+
},
49+
render: function(buffer) {
50+
buffer.push("<input>");
51+
}
52+
});
53+
54+
Ember.run(parentView, 'appendTo', '#qunit-fixture');
55+
56+
parentView.$('input').focus();
57+
58+
Ember.run(function(){
59+
parentView.$('input').remove();
60+
equal(focusOutCount, 0, 'focusOut was not called yet');
61+
});
62+
63+
equal(focusOutCount, 1, 'focusOut event was fired once, on next tick');
64+
65+
equal(Ember.$(':focus')[0], undefined, 'nothing is in focus');
66+
});
67+

packages/ember/tests/helpers/link_to_test.js

+1-3
Original file line numberDiff line numberDiff line change
@@ -332,9 +332,7 @@ test("The {{linkTo}} helper supports bubbles=false", function() {
332332
router.handleURL("/about");
333333
});
334334

335-
Ember.run(function() {
336-
Ember.$('#about-contact', '#qunit-fixture').click();
337-
});
335+
Ember.$('#about-contact', '#qunit-fixture').click();
338336

339337
equal(Ember.$("#contact", "#qunit-fixture").text(), "Contact", "precond - the link worked");
340338

0 commit comments

Comments
 (0)