Make WordPress Core

Ticket #28536: 28536.2.diff

File 28536.2.diff, 12.4 KB (added by westonruter, 11 years ago)

Fix Uncaught TypeError when accessing window. Use message passing instead of directly accessing parent window. Use message passing instead of directly accessing parent window. Commit: https://github.com/x-team/wordpress-develop/commit/0464541f300ea3889503abc2da8b08db45d17ffc PR: 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..11ef18a 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, popState;
     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                                if ( api.parent.targetWindow() ) {
     849                                        api.parent.send( 'pushstate', [ state, '', parentLocation ] );
     850                                } else {
     851                                        window.history.pushState( state, '', parentLocation );
     852                                }
     853
     854                                if ( api.settings.theme.active ) {
     855                                        backLink.prop( 'href', url );
     856                                }
     857                        };
     858                        this.previewUrl.bind( pushStatePreviewUrl );
     859
     860                        // Pop state
     861                        popState = function ( state, windowLocation ) {
     862                                var url, queryVars;
     863
     864                                queryVars = self.parseQueryVars( windowLocation.search.substr( 1 ) );
     865
     866                                // Handle hitting back to themes page after having reloaded on a page in the Customizer
     867                                if ( ! state && /themes\.php$/.test( windowLocation.pathname ) ) {
     868                                        location.replace( windowLocation.href );
     869                                        return;
     870                                }
     871
     872                                if ( state && state.customizePreviewUrl ) {
     873                                        url = state.customizePreviewUrl;
     874                                } else if ( queryVars.url ) {
     875                                        /*
     876                                         * When popped to initial state, then state is null and so
     877                                         * we don't have customizePreviewUrl, so we can grab it from
     878                                         * the URL query parameter. Note that the previewUrl value
     879                                         * has a validator in place which will prevent illegal
     880                                         * or malicious URLs from being injected into the preview.
     881                                         */
     882                                        url = queryVars.url;
     883                                } else {
     884                                        // Use the homepage as the preview URL as default
     885                                        url = api.settings.url.home;
     886                                }
     887                                self.previewUrl.unbind( pushStatePreviewUrl );
     888                                self.previewUrl( url );
     889                                self.previewUrl.bind( pushStatePreviewUrl );
     890
     891                                if ( api.settings.theme.active ) {
     892                                        backLink.prop( 'href', url );
     893                                }
     894                        };
     895
     896                        if ( api.parent.targetWindow() ) {
     897                                api.parent.bind( 'popstate', function ( args ) {
     898                                        popState.apply( null, args );
     899                                } );
     900                        } else {
     901                                $( window ).on( 'popstate', function ( e ) {
     902                                        popState( e.originalEvent.state, window.location );
     903                                } );
     904                        }
    797905                },
    798906
    799907                query: function() {},
     
    9131021                if ( ! $.support.postMessage || ( ! $.support.cors && api.settings.isCrossDomain ) )
    9141022                        return window.location = api.settings.url.fallback;
    9151023
    916                 var previewer, parent, topFocus,
     1024                var previewer, topFocus,
    9171025                        body = $( document.body ),
    9181026                        overlay = body.children('.wp-full-overlay');
    9191027
     
    9271035                        }
    9281036                });
    9291037
     1038                // Create a potential postMessage connection with the parent frame.
     1039                api.parent = new api.Messenger( {
     1040                        url: api.settings.url.parent,
     1041                        channel: 'loader'
     1042                } );
     1043
    9301044                // Initialize Previewer
    9311045                previewer = new api.Previewer({
    9321046                        container:   '#customize-preview',
     
    11131227                        event.preventDefault();
    11141228                });
    11151229
    1116                 // Create a potential postMessage connection with the parent frame.
    1117                 parent = new api.Messenger({
    1118                         url: api.settings.url.parent,
    1119                         channel: 'loader'
    1120                 });
    1121 
    11221230                // If we receive a 'back' event, we're inside an iframe.
    11231231                // Send any clicks to the 'Return' link to the parent page.
    1124                 parent.bind( 'back', function() {
     1232                api.parent.bind( 'back', function() {
    11251233                        $('.back').on( 'click.back', function( event ) {
    11261234                                event.preventDefault();
    1127                                 parent.send( 'close' );
     1235                                api.parent.send( 'close' );
    11281236                        });
    11291237                });
    11301238
    11311239                // Pass events through to the parent.
    11321240                api.bind( 'saved', function() {
    1133                         parent.send( 'saved' );
     1241                        api.parent.send( 'saved' );
    11341242                });
    11351243
    11361244                // When activated, let the loader handle redirecting the page.
    11371245                // If no loader exists, redirect the page ourselves (if a url exists).
    11381246                api.bind( 'activated', function() {
    1139                         if ( parent.targetWindow() )
    1140                                 parent.send( 'activated', api.settings.url.activated );
    1141                         else if ( api.settings.url.activated )
     1247                        if ( api.parent.targetWindow() ) {
     1248                                api.parent.send( 'activated', api.settings.url.activated );
     1249                        } else if ( api.settings.url.activated ) {
    11421250                                window.location = api.settings.url.activated;
     1251                        }
    11431252                });
    11441253
    11451254                // Initialize the connection with the parent frame.
    1146                 parent.send( 'ready' );
     1255                api.parent.send( 'ready' );
    11471256
    11481257                // Control visibility for default controls
    11491258                $.each({
  • 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..ce099e9 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();
    6466                },
    6567
    6668                open: function( src ) {
    67                         var hash;
     69                        var hash, messenger;
    6870
    6971                        if ( this.active )
    7072                                return;
    window.wp = window.wp || {}; 
    8082                        this.iframe.one( 'load', this.loaded );
    8183
    8284                        // Create a postMessage connection with the iframe.
    83                         this.messenger = new api.Messenger({
     85                        this.messenger = messenger = new api.Messenger({
    8486                                url: src,
    8587                                channel: 'loader',
    8688                                targetWindow: this.iframe[0].contentWindow
    8789                        });
    8890
    8991                        // Wait for the connection from the iframe before sending any postMessage events.
    90                         this.messenger.bind( 'ready', function() {
     92                        messenger.bind( 'ready', function() {
    9193                                Loader.messenger.send( 'back' );
    92                         });
    93 
    94                         this.messenger.bind( 'close', function() {
    95                                 if ( $.support.history )
     94                        } );
     95
     96                        messenger.bind( 'close', function() {
     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
    103                         this.messenger.bind( 'activated', function( location ) {
     117                        messenger.bind( 'activated', function( location ) {
    104118                                if ( location )
    105119                                        window.location = location;
    106120                        });
    107121
    108122                        hash = src.split('?')[1];
    109123
    110                         // Ensure we don't call pushState if the user hit the forward button.
    111                         if ( $.support.history && window.location.href !== src )
    112                                 history.pushState( { customize: src }, '', src );
    113                         else if ( ! $.support.history && $.support.hashchange && hash )
     124                        if ( $.support.history ) {
     125                                // Ensure we don't call pushState if the user hit the forward button.
     126                                if ( window.location.href !== src ) {
     127                                        history.pushState( { customize: src }, '', src );
     128                                }
     129
     130                                // Allow customizer to control history of parent
     131                                messenger.bind( 'pushstate', function( args ) {
     132                                        history.pushState.apply( history, args );
     133                                } );
     134
     135                                // Forward popstate events to customizer
     136                                $( window ).on( 'popstate', function ( e ) {
     137                                        messenger.send( 'popstate', [ e.originalEvent.state, window.location ] );
     138                                } );
     139
     140                        } else if ( $.support.hashchange && hash ) {
    114141                                window.location.hash = 'wp_customize=on&' + hash;
     142                        }
    115143
    116144                        this.trigger( 'open' );
    117145                },
    window.wp = window.wp || {}; 
    121149                },
    122150
    123151                close: function() {
    124                         if ( ! this.active )
     152                        if ( ! this.active ) {
    125153                                return;
     154                        }
    126155                        this.active = false;
    127156
    128157                        this.trigger( 'close' );
    129158
    130159                        // Return focus to link that was originally clicked.
    131                         if ( this.link )
     160                        if ( this.link ) {
    132161                                this.link.focus();
     162                        }
    133163                },
    134164
    135165                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        );