Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Popup: Fixes closing the popup unexpectedly by race conditions. #668

Closed
wants to merge 4 commits into from

5 participants

Rafael Xavier de Souza Jörn Zaefferer Mike Sherov Scott González Steven G. Harms
Rafael Xavier de Souza
Collaborator
  • A focusout followed by a focusin sometimes trigger events in reverse order, but should not close the popup nevertheless. Or a click-in-the-popup followed by a focusout should also avoid closing it unexpectedly;
  • _closeTry and _closeVet work together to close the popup, but making sure no other event vets it;
added some commits May 26, 2012
Rafael Xavier de Souza Popup: Added _closeTry and _closeVet methods. Fixes closing the popup…
… unexpectedly by race conditions.

- A focusout followed by a focusin sometimes trigger events in reverse order,
  but should not close the popup nevertheless. Or a click-in-the-popup followed
  by a focusout should also avoid closing it unexpectedly;
- _closeTry and _closeVet work together to close the popup, but making sure no
  other event vets it;
c6cce9c
Rafael Xavier de Souza Popup: Binds document mousedown instead of click. The click event is …
…triggered when the mouse button is released, which virtually always exceeds _closeTry delay, leading to erroneous popup close.
96c6b94
Rafael Xavier de Souza Popup: Manually binding document instead of using _bind. Because on d…
…estroy, it doesn't unbind other popup instances.
aa135de
Rafael Xavier de Souza Popup: Lowered the closing delay from 150ms to 50ms. Makes response f…
…aster since the _closeTry and _closeVet approach is working smooth.
a3cd4fd
Jörn Zaefferer
Owner

_closeVet and _closeTry are weird named that don't match any convention used in other components. Should try to come up with something else.

Rafael Xavier de Souza
Collaborator

I agree they are not common names. Can you guys help me with suggestions please? For now, I can help by summarizing the implementation dynamics below:

Currently, the popup closes unexpectedly due to race conditions. The easiest example to observe this behavior (before this patch) is to click in the popup and hold the button down. The popup will close, because the popup loses focus and is not followed by a "vetting" event like mouseup. Another example happened to me when I had nested popups: the reason the popup was closing unexpectedly was because the focusin event (of the inner popup) was occasionally being triggered before the focusout (of the parent popup).

The order of the events doesn't matter. But, the time-distance between them. So, to solve the above scenario, basically what I did was:
a) Closing events (like focusout, or clicking outside the popup) still "tries" to close the popup unless it's "vetted" (like it was implemented before);
b) A "vetting" event (like focusin, or clicking in the popup) still cancels any previous closing attempt, and also creates a temporary shield against short future closing events;

Rafael Xavier de Souza
Collaborator

_closeTry and _closeAbort?

Mike Sherov
Collaborator

@rxaviers, do you have unit tests and a test case for this pull request? I'd like to know whether to close this or if you're going to continue to pursue this PR. Thanks man!

Rafael Xavier de Souza
Collaborator

Hi @mikesherov, I think this is still worth. Although, it's a little old and it needs a review. I will do it and provide the unit test soon.

Jörn Zaefferer
Owner

@rxaviers still want to update this? Would have to starting using _on/_off instead of bind and unbind, along with the tests.

Rafael Xavier de Souza
Collaborator
Scott González
Owner

Closing due to inactivity. If you get around to the other updates, we can re-open or start a new PR. The fate of popup isn't clear to me right now.

Scott González scottgonzalez closed this April 11, 2013
Rafael Xavier de Souza
Collaborator

Makes sense... Sorry for the inactivity in here. I will (hopefully) update this and reopen.
PS: the quickest test is: open demos/popup/animation.html, click Log In to open the popup. Then, click on the 'Username' text and keep your mouse clicked. Note, It will close the popup unexpectedly. My fix prevents this race condition and any other.

Jörn Zaefferer
Owner

I tend to killing popup. We don't use it in Menubar and that's probably not going to change, its very unlikely that we ever want to adopt it in dialog, we don't get feature request for menu to support and in the end we can just write some more custom popup code for the new datepicker.

I hope @sgharms gets around to look at menubar and popup together. Maybe he has some input.

Steven G. Harms

@jzaefferer I'm finally all caught up on my PR's so now I can look at others' :).

I can confirm @rxaviers 's reported error on origin/menubar branch, but on the other hand I see no reason why we would want to use popup in menubar. I agree, it's a neat feature, but it doesn't make sense in the context of a menubar (as @jzaefferer mentioned; no users are asking for it).

If you were to kill it here, it wouldn't affect menubar, that I can see.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 4 unique commits by 1 author.

May 26, 2012
Rafael Xavier de Souza Popup: Added _closeTry and _closeVet methods. Fixes closing the popup…
… unexpectedly by race conditions.

- A focusout followed by a focusin sometimes trigger events in reverse order,
  but should not close the popup nevertheless. Or a click-in-the-popup followed
  by a focusout should also avoid closing it unexpectedly;
- _closeTry and _closeVet work together to close the popup, but making sure no
  other event vets it;
c6cce9c
Rafael Xavier de Souza Popup: Binds document mousedown instead of click. The click event is …
…triggered when the mouse button is released, which virtually always exceeds _closeTry delay, leading to erroneous popup close.
96c6b94
Rafael Xavier de Souza Popup: Manually binding document instead of using _bind. Because on d…
…estroy, it doesn't unbind other popup instances.
aa135de
Rafael Xavier de Souza Popup: Lowered the closing delay from 150ms to 50ms. Makes response f…
…aster since the _closeTry and _closeVet approach is working smooth.
a3cd4fd
This page is out of date. Refresh to see the latest.

Showing 1 changed file with 38 additions and 16 deletions. Show diff stats Hide diff stats

  1. 54  ui/jquery.ui.popup.js
54  ui/jquery.ui.popup.js
@@ -89,7 +89,7 @@ $.widget( "ui.popup", {
89 89
 					case $.ui.keyCode.UP:
90 90
 						// prevent scrolling
91 91
 						event.preventDefault();
92  
-						clearTimeout( this.closeTimer );
  92
+						this._closeVet();
93 93
 						this._delay(function() {
94 94
 							this.open( event );
95 95
 							this.focusPopup( event );
@@ -114,7 +114,7 @@ $.widget( "ui.popup", {
114 114
 					return;
115 115
 				}
116 116
 				this.open( event );
117  
-				clearTimeout( this.closeTimer );
  117
+				this._closeVet();
118 118
 				this._delay( function() {
119 119
 					if ( !noFocus ) {
120 120
 						this.focusPopup();
@@ -164,17 +164,10 @@ $.widget( "ui.popup", {
164 164
 
165 165
 		this._bind({
166 166
 			focusout: function( event ) {
167  
-				// use a timer to allow click to clear it and letting that
168  
-				// handle the closing instead of opening again
169  
-				this.closeTimer = this._delay( function() {
170  
-					this.close( event );
171  
-				}, 150);
  167
+				this._closeTry( event );
172 168
 			},
173 169
 			focusin: function( event ) {
174  
-				clearTimeout( this.closeTimer );
175  
-			},
176  
-			mouseup: function( event ) {
177  
-				clearTimeout( this.closeTimer );
  170
+				this._closeVet();
178 171
 			}
179 172
 		});
180 173
 
@@ -187,15 +180,43 @@ $.widget( "ui.popup", {
187 180
 			}
188 181
 		});
189 182
 
190  
-		this._bind( this.document, {
191  
-			click: function( event ) {
192  
-				if ( this.isOpen && !$( event.target ).closest( this.element.add( this.options.trigger ) ).length ) {
193  
-					this.close( event );
  183
+		this.outsideHandler = $.proxy ( function ( event ) {
  184
+			if ( this.isOpen ) {
  185
+				if ( $( event.target ).closest( this.element.add( this.options.trigger ) ).length ) {
  186
+					this._closeVet();
  187
+				}
  188
+				else {
  189
+					this._closeTry( event );
194 190
 				}
195 191
 			}
196  
-		});
  192
+		}, this );
  193
+		$( this.document ).mousedown( this.outsideHandler );
197 194
 	},
198 195
 
  196
+	// Unless vetted, close. Use a timer to allow veto in a timed window.
  197
+	// Note that a focusout followed by a focusin sometimes trigger events in reverse order, but should not close the popup nevertheless. Or a click-in-the-popup followed by a focusout should also avoid closing it unexpectedly.
  198
+	_closeTry: function( event ) {
  199
+		if ( this.closeVeto || this.closeTimer ) {
  200
+			return;
  201
+		}
  202
+		this.closeTimer = this._delay( function() {
  203
+			this.close( event );
  204
+			this.closeTimer = null;
  205
+		}, 50 );
  206
+	},
  207
+
  208
+	// Vets any close attempt in a timed window.
  209
+	_closeVet: function() {
  210
+		this.closeVeto = true;
  211
+		this._delay( function() {
  212
+			this.closeVeto = false;
  213
+		}, 50 );
  214
+		if ( this.closeTimer ) {
  215
+			clearTimeout( this.closeTimer );
  216
+			this.closeTimer = null;
  217
+		}
  218
+	},
  219
+ 
199 220
 	_destroy: function() {
200 221
 		this.element
201 222
 			.show()
@@ -203,6 +224,7 @@ $.widget( "ui.popup", {
203 224
 			.removeAttr( "aria-hidden" )
204 225
 			.removeAttr( "aria-expanded" )
205 226
 			.unbind( "keypress.ui-popup");
  227
+		$( this.document ).unbind( "mousedown", this.outsideHandler );
206 228
 
207 229
 		this.options.trigger
208 230
 			.removeAttr( "aria-haspopup" )
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.