WordPress.org

Make WordPress Core

Ticket #28536: 28536.1.diff

File 28536.1.diff, 8.2 KB (added by westonruter, 5 years ago)

When doing live preview, let default return be themes.php not url query param. pushState and popState on parent frame for Live Preview; update back link only outside Live Preview. Handle hitting back to themes page after having reloaded on a page in the Customizer. Close Live Preview customizer if no state or state w/o preview URL. When canceling Live Preview, pop history all the way to themes.php. Fix apparent race conditions where Window objects go away. Prevent clicking on illegal URLs from adding history. Ensure that popping to intial state restores original URL in preview. Additional commits: https://github.com/x-team/wordpress-develop/compare/e62df5e...def209e Pull request: https://github.com/x-team/wordpress-develop/pull/16

  • src/wp-admin/customize.php

    diff --git src/wp-admin/customize.php src/wp-admin/customize.php
    index 1faf371..cdb7351 100644
    define( 'IFRAME_REQUEST', true ); 
    1212/** Load WordPress Administration Bootstrap */
    1313require_once( dirname( __FILE__ ) . '/admin.php' );
    1414
    15 if ( ! current_user_can( 'edit_theme_options' ) )
     15if ( ! current_user_can( 'edit_theme_options' ) ) {
    1616        wp_die( __( 'Cheatin’ uh?' ) );
     17}
     18
     19global $wp_scripts, $wp_customize;
    1720
    1821wp_reset_vars( array( 'url', 'return' ) );
    1922$url = urldecode( $url );
    2023$url = wp_validate_redirect( $url, home_url( '/' ) );
    21 if ( $return )
     24
     25if ( $return ) {
    2226        $return = wp_validate_redirect( urldecode( $return ) );
    23 if ( ! $return )
     27} elseif ( ! $wp_customize->is_theme_active() ) {
     28        $return = admin_url( 'themes.php' );
     29} else {
    2430        $return = $url;
    25 
    26 global $wp_scripts, $wp_customize;
     31}
    2732
    2833$registered = $wp_scripts->registered;
    2934$wp_scripts = new WP_Scripts;
  • src/wp-admin/js/customize-controls.js

    diff --git src/wp-admin/js/customize-controls.js src/wp-admin/js/customize-controls.js
    index 4bfc252..63fbbe4 100644
     
    728728                                                }
    729729                                        }
    730730
    731                                         clearTimeout( timeout );
    732                                         timeout = setTimeout( callback, self.refreshBuffer );
     731                                        try {
     732                                                clearTimeout( timeout );
     733                                                timeout = setTimeout( callback, self.refreshBuffer );
     734                                        } catch ( e ) {
     735                                                // Handle Uncaught InvalidAccessError: Failed to execute 'setTimeout' on 'Window': No script context is available in which to execute the script.
     736                                                return callback();
     737                                        }
    733738                                };
    734739                        })( this );
    735740
     
    794799
    795800                        // Update the URL when the iframe sends a URL message.
    796801                        this.bind( 'url', this.previewUrl );
     802
     803                        this.addHistory();
     804                },
     805
     806                /**
     807                 * Given a URL query string, return the query vars contained within it
     808                 *
     809                 * @param {String} queryString
     810                 * @returns {Object}
     811                 */
     812                parseQueryVars: function ( queryString ) {
     813                        var queryVars = {};
     814                        if ( queryString ) {
     815                                $.each( queryString.split( '&' ), function () {
     816                                        var key, value, pair;
     817                                        pair = this.split( '=', 2 );
     818                                        key = decodeURIComponent( pair[0] );
     819                                        value = pair[1] ? decodeURIComponent( pair[1] ) : null;
     820                                        queryVars[ key ] = value;
     821                                } );
     822                        }
     823                        return queryVars;
     824                },
     825
     826                /**
     827                 * Add support for history for navigation in Customize preview
     828                 */
     829                addHistory: function () {
     830                        var self, backLink, pushStatePreviewUrl;
     831                        if ( ! history.pushState ) {
     832                                return;
     833                        }
     834                        self = this;
     835                        backLink = $( '.back.button:first' );
     836
     837                        // Push state
     838                        pushStatePreviewUrl = function ( url ) {
     839                                var state, parentLocation, queryVars;
     840                                state = { customizePreviewUrl: url };
     841                                parentLocation = location.pathname;
     842                                queryVars = self.parseQueryVars( location.search.substr( 1 ) );
     843                                queryVars.url = url;
     844                                parentLocation += '?' + $.param( queryVars );
     845                                parentLocation += location.hash;
     846
     847                                // parent frame is for the sake of customizer loaded into iframe for Live Preview
     848                                window.parent.history.pushState( state, '', parentLocation );
     849
     850                                if ( api.settings.theme.active ) {
     851                                        backLink.prop( 'href', url );
     852                                }
     853                        };
     854                        this.previewUrl.bind( pushStatePreviewUrl );
     855
     856                        // Pop state
     857                        $( window.parent ).on( 'popstate', function ( e ) {
     858                                var state, url, queryVars;
     859
     860                                state = e.originalEvent.state;
     861                                queryVars = self.parseQueryVars( location.search.substr( 1 ) );
     862
     863                                // Handle hitting back to themes page after having reloaded on a page in the Customizer
     864                                if ( ! state && /themes\.php$/.test( location.pathname ) ) {
     865                                        location.replace( location.href );
     866                                        return;
     867                                }
     868
     869                                if ( state && state.customizePreviewUrl ) {
     870                                        url = state.customizePreviewUrl;
     871                                } else if ( queryVars.url ) {
     872                                        /*
     873                                         * When popped to initial state, then state is null and so
     874                                         * we don't have customizePreviewUrl, so we can grab it from
     875                                         * the URL query parameter. Note that the previewUrl value
     876                                         * has a validator in place which will prevent illegal
     877                                         * or malicious URLs from being injected into the preview.
     878                                         */
     879                                        url = queryVars.url;
     880                                } else {
     881                                        // Use the homepage as the preview URL as default
     882                                        url = api.settings.url.home;
     883                                }
     884                                self.previewUrl.unbind( pushStatePreviewUrl );
     885                                self.previewUrl( url );
     886                                self.previewUrl.bind( pushStatePreviewUrl );
     887
     888                                if ( api.settings.theme.active ) {
     889                                        backLink.prop( 'href', url );
     890                                }
     891                        } );
     892
    797893                },
    798894
    799895                query: function() {},
  • src/wp-includes/js/customize-base.js

    diff --git src/wp-includes/js/customize-base.js src/wp-includes/js/customize-base.js
    index db573b5..b2e32c9 100644
    window.wp = window.wp || {}; 
    494494                 * @param  {object} options       Extend any instance parameter or method with this object.
    495495                 */
    496496                initialize: function( params, options ) {
    497                         // Target the parent frame by default, but only if a parent frame exists.
    498                         var defaultTarget = window.parent == window ? null : window.parent;
     497                        var defaultTarget;
     498                        try {
     499                                // Target the parent frame by default, but only if a parent frame exists.
     500                                defaultTarget = window.parent === window ? null : window.parent;
     501                        } catch ( e ) {
     502                                // Handle case where window goes away in race condition. Uncaught TypeError: Cannot read property 'parent' of null
     503                                defaultTarget = null;
     504                        }
    499505
    500506                        $.extend( this, options || {} );
    501507
  • src/wp-includes/js/customize-loader.js

    diff --git src/wp-includes/js/customize-loader.js src/wp-includes/js/customize-loader.js
    index cccf71a..3434960 100644
    window.wp = window.wp || {}; 
    3636                        });
    3737
    3838                        // Add navigation listeners.
    39                         if ( $.support.history )
     39                        if ( $.support.history ) {
    4040                                this.window.on( 'popstate', Loader.popstate );
     41                        }
    4142
    4243                        if ( $.support.hashchange ) {
    4344                                this.window.on( 'hashchange', Loader.hashchange );
    window.wp = window.wp || {}; 
    4748
    4849                popstate: function( e ) {
    4950                        var state = e.originalEvent.state;
    50                         if ( state && state.customize )
     51                        if ( state && state.customize ) {
    5152                                Loader.open( state.customize );
    52                         else if ( Loader.active )
     53                        } else if ( Loader.active && ( ! state || ! state.customizePreviewUrl ) ) {
    5354                                Loader.close();
     55                        }
    5456                },
    5557
    5658                hashchange: function() {
    5759                        var hash = window.location.toString().split('#')[1];
    5860
    5961                        if ( hash && 0 === hash.indexOf( 'wp_customize=on' ) )
    60                                 Loader.open( Loader.settings.url + '?' + hash );
     62                                Loader.open( Loader.settings.url.customize + '?' + hash );
    6163
    6264                        if ( ! hash && ! $.support.history )
    6365                                Loader.close();
    window.wp = window.wp || {}; 
    9294                        });
    9395
    9496                        this.messenger.bind( 'close', function() {
    95                                 if ( $.support.history )
     97                                var goBackToThemesPage;
     98                                if ( $.support.history ) {
     99                                        goBackToThemesPage = function ( e ) {
     100                                                var state;
     101                                                state = e.originalEvent.state;
     102                                                if ( state && ( state.customize || state.customizePreviewUrl ) ) {
     103                                                        history.back();
     104                                                } else {
     105                                                        $( window ).off( 'popstate', goBackToThemesPage );
     106                                                }
     107                                        };
     108                                        $( window ).on( 'popstate', goBackToThemesPage );
    96109                                        history.back();
    97                                 else if ( $.support.hashchange )
     110                                } else if ( $.support.hashchange ) {
    98111                                        window.location.hash = '';
    99                                 else
     112                                } else {
    100113                                        Loader.close();
     114                                }
    101115                        });
    102116
    103117                        this.messenger.bind( 'activated', function( location ) {
    window.wp = window.wp || {}; 
    121135                },
    122136
    123137                close: function() {
    124                         if ( ! this.active )
     138                        if ( ! this.active ) {
    125139                                return;
     140                        }
    126141                        this.active = false;
    127142
    128143                        this.trigger( 'close' );
    129144
    130145                        // Return focus to link that was originally clicked.
    131                         if ( this.link )
     146                        if ( this.link ) {
    132147                                this.link.focus();
     148                        }
    133149                },
    134150
    135151                closed: function() {
  • src/wp-includes/theme.php

    diff --git src/wp-includes/theme.php src/wp-includes/theme.php
    index 40acf6b..f27d79b 100644
    function _wp_customize_loader_settings() { 
    18711871        );
    18721872
    18731873        $settings = array(
    1874                 'url'           => esc_url( admin_url( 'customize.php' ) ),
     1874                'url'           => array(
     1875                        'customize' => esc_url_raw( admin_url( 'customize.php' ) ),
     1876                        'home'      => esc_url_raw( home_url( '/' ) ),
     1877                ),
    18751878                'isCrossDomain' => $cross_domain,
    18761879                'browser'       => $browser,
    18771880        );