@@ -155,7 +155,17 @@ define(function LiveDevelopment(require, exports, module) {
155155 /** @type {Object.<string: {HTMLDocument|CSSDocument}> } */
156156 var _relatedDocuments = { } ;
157157
158- var _openDeferred ; // promise returned for each call to open()
158+ /**
159+ * Promise returned for each call to open()
160+ * @type {jQuery.Deferred }
161+ */
162+ var _openDeferred ;
163+
164+ /**
165+ * Promise returned for each call to close()
166+ * @type {jQuery.Deferred }
167+ */
168+ var _closeDeferred ;
159169
160170 // Disallow re-entrancy of loadAgents()
161171 var _loadAgentsPromise ;
@@ -165,6 +175,10 @@ define(function LiveDevelopment(require, exports, module) {
165175 * @type {BaseServer }
166176 */
167177 var _server ;
178+
179+ function _isPromisePending ( promise ) {
180+ return promise && promise . state ( ) === "pending" ;
181+ }
168182
169183 function _isHtmlFileExt ( ext ) {
170184 return ( FileUtils . isStaticHtmlFileExt ( ext ) ||
@@ -754,7 +768,8 @@ define(function LiveDevelopment(require, exports, module) {
754768 closePromise = new $ . Deferred ( ) . resolve ( ) ;
755769 }
756770
757- closePromise . done ( function ( ) {
771+ // Disconnect WebSocket if connected
772+ closePromise . always ( function ( ) {
758773 if ( Inspector . connected ( ) ) {
759774 Inspector . disconnect ( ) . always ( deferred . resolve ) ;
760775 } else {
@@ -770,10 +785,19 @@ define(function LiveDevelopment(require, exports, module) {
770785 * Close the connection and the associated window asynchronously
771786 * @param {boolean } doCloseWindow Use true to close the window/tab in the browser
772787 * @param {?string } reason Optional string key suffix to display to user (see LIVE_DEV_* keys)
773- * @return {jQuery.Promise } Resolves once the connection is closed
788+ * @return {jQuery.Promise } Always return a resolved promise once the connection is closed
774789 */
775790 function _close ( doCloseWindow , reason ) {
776- var deferred = $ . Deferred ( ) ;
791+ if ( _closeDeferred ) {
792+ return _closeDeferred ;
793+ } else {
794+ _closeDeferred = new $ . Deferred ( ) ;
795+ _closeDeferred . always ( function ( ) {
796+ _closeDeferred = null ;
797+ } ) ;
798+ }
799+
800+ var promise = _closeDeferred . promise ( ) ;
777801
778802 /*
779803 * Finish closing the live development connection, including setting
@@ -785,27 +809,29 @@ define(function LiveDevelopment(require, exports, module) {
785809 var closeDeferred = ( brackets . platform === "mac" ) ? NativeApp . closeLiveBrowser ( ) : $ . Deferred ( ) . resolve ( ) ;
786810 closeDeferred . done ( function ( ) {
787811 _setStatus ( STATUS_INACTIVE , reason || "explicit_close" ) ;
788- deferred . resolve ( ) ;
812+ _closeDeferred . resolve ( ) ;
789813 } ) . fail ( function ( err ) {
790814 if ( err ) {
791815 reason += " (" + err + ")" ;
792816 }
793817 _setStatus ( STATUS_INACTIVE , reason || "explicit_close" ) ;
794- deferred . resolve ( ) ;
818+ _closeDeferred . resolve ( ) ;
795819 } ) ;
796820 }
797821
798- if ( _openDeferred && _openDeferred . state ( ) === "pending" ) {
822+ if ( _isPromisePending ( _openDeferred ) ) {
799823 // Reject calls to open if requests are still pending
800824 _openDeferred . reject ( ) ;
801- } else if ( exports . status === STATUS_INACTIVE ) {
825+ }
826+
827+ if ( exports . status === STATUS_INACTIVE ) {
802828 // Ignore close if status is inactive
803- deferred . resolve ( ) ;
829+ _closeDeferred . resolve ( ) ;
804830 } else {
805- _doInspectorDisconnect ( doCloseWindow ) . done ( cleanup ) ;
831+ _doInspectorDisconnect ( doCloseWindow ) . always ( cleanup ) ;
806832 }
807833
808- return deferred . promise ( ) ;
834+ return promise ;
809835 }
810836
811837 // WebInspector Event: Page.frameNavigated
@@ -1180,15 +1206,25 @@ define(function LiveDevelopment(require, exports, module) {
11801206 * @return {jQuery.Promise } Resolves once live preview is open
11811207 */
11821208 function open ( restart ) {
1209+ // If close() is still pending, wait for close to finish before opening
1210+ if ( _isPromisePending ( _closeDeferred ) ) {
1211+ return _closeDeferred . then ( function ( ) {
1212+ return open ( restart ) ;
1213+ } ) ;
1214+ }
1215+
11831216 if ( ! restart ) {
1184- _openDeferred = new $ . Deferred ( ) ;
1217+ // Return existing promise if it is still pending
1218+ if ( _isPromisePending ( _openDeferred ) ) {
1219+ return _openDeferred ;
1220+ } else {
1221+ _openDeferred = new $ . Deferred ( ) ;
1222+ _openDeferred . always ( function ( ) {
1223+ _openDeferred = null ;
1224+ } ) ;
1225+ }
11851226 }
11861227
1187- // Cleanup deferred when finished
1188- _openDeferred . always ( function ( ) {
1189- _openDeferred = null ;
1190- } ) ;
1191-
11921228 // TODO: need to run _onDocumentChange() after load if doc != currentDocument here? Maybe not, since activeEditorChange
11931229 // doesn't trigger it, while inline editors can still cause edits in doc other than currentDoc...
11941230 _getInitialDocFromCurrent ( ) . done ( function ( doc ) {
0 commit comments