Make WordPress Core

Opened 10 years ago

Closed 10 years ago

#31501 closed defect (bug) (fixed)

Customizer Save race condition when attempting to publish too soon after updating widget form fields with multiple edits

Reported by: westonruter's profile westonruter Owned by: ocean90's profile ocean90
Milestone: 4.2 Priority: normal
Severity: normal Version: 3.9
Component: Customize Keywords: has-patch
Focuses: javascript Cc:

Description (last modified by westonruter)

On a slow connection or if a widget update callback takes any amount of processing time complete, multiple edits to one or more form fields will have multiple simultaneous update_widget Ajax requests open at a time. The last one to return would have the winning instance data to supply for the widget form: a race condition. Before an update widget request is kicked off, any previous request should be aborted so that only the last user-submitted data is used in the form instance.

Additionally, when invoking the Customizer Save functionality, it needs to hold off on gathering up the {{customized}} data to POST to the server until the {{processing}} state is cleared-out; right now it is incorrectly obtaining the data to send before any widgets have finished processing to get their settings from the server over Ajax.

Attachments (2)

31501.diff (1.4 KB) - added by westonruter 10 years ago.
https://github.com/xwp/wordpress-develop/pull/75
31501.2.diff (555 bytes) - added by westonruter 10 years ago.
https://github.com/xwp/wordpress-develop/pull/77

Download all attachments as: .zip

Change History (9)

#1 @westonruter
10 years ago

  • Description modified (diff)

#2 @westonruter
10 years ago

  • Keywords has-patch added
  • Owner set to ocean90
  • Status changed from new to reviewing

In 31501.diff:

  • Abort a previous updateWidget request when starting a new one
  • Defer gathering customized data until processing state is properly zeroed-out

Example widget that illustrates current problem: https://gist.github.com/westonruter/3b472e1491be0bb6bfb4
Try updating the two fields back and forth quickly, pause a moment to see them appear in the preview, then type some more and then immediately hit Save & Publish. The data that ends up getting saved is consistently not the last data that was entered.

#3 @usability.idealist
10 years ago

oha. this could be one of the reasons why we've been experiencing "Customizer Preview works, but is not saving anything" issue with CC2 lately.

We've got 150+ settings, so that might indeed a possibility.

cu, w0lf.

Version 1, edited 10 years ago by usability.idealist (previous) (next) (diff)

#4 @ocean90
10 years ago

  • Resolution set to fixed
  • Status changed from reviewing to closed

In 31706:

Customizer: Prevent a race condition when attempting to publish too soon after updating widget form fields with multiple edits.

props westonruter.
fixes #31501.

#5 @ocean90
10 years ago

  • Milestone changed from 4.1.2 to 4.2

#6 @westonruter
10 years ago

  • Resolution fixed deleted
  • Status changed from closed to reopened

I found a defect that the patch for this ticket helped highlight, due to the new jqxhr.abort() call, but it would have happened before as well in case of a network error. The problem is when adding a new widget, the processing state will get incremented to prevent the Customizer from saving the settings before the server returns with the sanitized setting. If you immediately start making changes to the newly added widget, then this will cause the jqshr.abort() to fire which then results in the jqxhr.fail() callback to fire in the updateWidget method. The updateWidget method used when adding a new widget currently has a complete callback that throws any jqxhr error. If this happens, as in the case of abort, then the processing decrement in the jqxhr.always() callback will fail to run, and the processing state will get stuck at a non-zero number. The result is that the settings can then never be saved.

The only reason why I added the throw of the error was to show something on the console.

So I suggest removing it in 31501.2.diff to prevent the jqxhr.always() callback from indeed not always getting called.

#7 @ocean90
10 years ago

  • Resolution set to fixed
  • Status changed from reopened to closed

In 31816:

Customizer: Remove a throw error call which prevents further actions, like jqxhr.always().

props westonruter.
fixes #31501.

Note: See TracTickets for help on using tickets.