WordPress.org

Make WordPress Core

Changeset 34269


Ignore:
Timestamp:
09/17/2015 07:41:35 PM (4 years ago)
Author:
westonruter
Message:

Customize: Reduce peak memory usage by JSON-encoding settings and controls individually.

When there are hundreds of settings and controls (e.g. nav menu items and widget instances) the resulting object that is JSON-encoded can become very large, and wp_json_encode() can consume a lot of memory to serialize it. By breaking down the serialization into multiple calls the peak memory usage can be kept in line.

Moves logic out of wp-admin/customize.php into the WP_Customize_Manager class with new methods:

  • is_ios()
  • get_document_title_template()
  • get_preview_url()/set_preview_url()
  • get_return_url()/set_return_url()
  • get_autofocus()/set_autofocus()
  • customize_pane_settings()

Includes unit tests for these methods, for which the logic was formerly untestable in customize.php.

Fixes #33898.

Location:
trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/customize.php

    r34093 r34269  
    2121}
    2222
    23 wp_reset_vars( array( 'url', 'return' ) );
    24 $url = wp_unslash( $url );
    25 $url = wp_validate_redirect( $url, home_url( '/' ) );
    26 if ( $return ) {
    27     $return = wp_unslash( $return );
    28     $return = remove_query_arg( wp_removable_query_args(), $return );
    29     $return = wp_validate_redirect( $return );
     23wp_reset_vars( array( 'url', 'return', 'autofocus' ) );
     24if ( ! empty( $url ) ) {
     25    $wp_customize->set_preview_url( wp_unslash( $url ) );
    3026}
    31 if ( ! $return ) {
    32     if ( $url ) {
    33         $return = $url;
    34     } elseif ( current_user_can( 'edit_theme_options' ) || current_user_can( 'switch_themes' ) ) {
    35         $return = admin_url( 'themes.php' );
    36     } else {
    37         $return = admin_url();
    38     }
     27if ( ! empty( $return ) ) {
     28    $wp_customize->set_return_url( wp_unslash( $return ) );
     29}
     30if ( ! empty( $autofocus ) && is_array( $autofocus ) ) {
     31    $wp_customize->set_autofocus( wp_unslash( $autofocus ) );
    3932}
    4033
     
    8477endif;
    8578
    86 $is_ios = wp_is_mobile() && preg_match( '/iPad|iPod|iPhone/', $_SERVER['HTTP_USER_AGENT'] );
    87 
    88 if ( $is_ios ) {
     79if ( $wp_customize->is_ios() ) {
    8980    $body_class .= ' ios';
    9081}
     
    9586$body_class .= ' locale-' . sanitize_html_class( strtolower( str_replace( '_', '-', get_locale() ) ) );
    9687
    97 if ( $wp_customize->is_theme_active() ) {
    98     $document_title_tmpl = _x( 'Customize: %s', 'Placeholder is the document title from the preview' );
    99 } else {
    100     $document_title_tmpl = _x( 'Live Preview: %s', 'Placeholder is the document title from the preview' );
    101 }
    102 $document_title_tmpl = html_entity_decode( $document_title_tmpl, ENT_QUOTES, 'UTF-8' ); // because exported to JS and assigned to document.title
    103 $admin_title = sprintf( $document_title_tmpl, __( 'Loading…' ) );
     88$admin_title = sprintf( $wp_customize->get_document_title_template(), __( 'Loading…' ) );
    10489
    10590?><title><?php echo $admin_title; ?></title>
    10691
    10792<script type="text/javascript">
    108 var ajaxurl = '<?php echo admin_url( 'admin-ajax.php', 'relative' ); ?>';
     93var ajaxurl = <?php echo wp_json_encode( admin_url( 'admin-ajax.php', 'relative' ) ); ?>;
    10994</script>
    11095
     
    138123                <span class="preview"><?php _e( 'Preview' ); ?></span>
    139124            </a>
    140             <a class="customize-controls-close" href="<?php echo esc_url( $return ); ?>">
     125            <a class="customize-controls-close" href="<?php echo esc_url( $wp_customize->get_return_url() ); ?>">
    141126                <span class="screen-reader-text"><?php _e( 'Cancel' ); ?></span>
    142127            </a>
     
    173158    <?php
    174159
    175     // Render Panel, Section, and Control templates.
    176     $wp_customize->render_panel_templates();
    177     $wp_customize->render_section_templates();
    178     $wp_customize->render_control_templates();
    179 
    180160    /**
    181      * Print Customizer control scripts in the footer.
     161     * Print templates, control scripts, and settings in the footer.
    182162     *
    183163     * @since 3.4.0
    184164     */
    185165    do_action( 'customize_controls_print_footer_scripts' );
    186 
    187     /*
    188      * If the frontend and the admin are served from the same domain, load the
    189      * preview over ssl if the Customizer is being loaded over ssl. This avoids
    190      * insecure content warnings. This is not attempted if the admin and frontend
    191      * are on different domains to avoid the case where the frontend doesn't have
    192      * ssl certs. Domain mapping plugins can allow other urls in these conditions
    193      * using the customize_allowed_urls filter.
    194      */
    195 
    196     $allowed_urls = array( home_url('/') );
    197     $admin_origin = parse_url( admin_url() );
    198     $home_origin  = parse_url( home_url() );
    199     $cross_domain = ( strtolower( $admin_origin[ 'host' ] ) != strtolower( $home_origin[ 'host' ] ) );
    200 
    201     if ( is_ssl() && ! $cross_domain )
    202         $allowed_urls[] = home_url( '/', 'https' );
    203 
    204     /**
    205      * Filter the list of URLs allowed to be clicked and followed in the Customizer preview.
    206      *
    207      * @since 3.4.0
    208      *
    209      * @param array $allowed_urls An array of allowed URLs.
    210      */
    211     $allowed_urls = array_unique( apply_filters( 'customize_allowed_urls', $allowed_urls ) );
    212 
    213     $login_url = add_query_arg( array(
    214         'interim-login' => 1,
    215         'customize-login' => 1
    216     ), wp_login_url() );
    217 
    218     // Prepare Customizer settings to pass to JavaScript.
    219     $settings = array(
    220         'theme'    => array(
    221             'stylesheet' => $wp_customize->get_stylesheet(),
    222             'active'     => $wp_customize->is_theme_active(),
    223         ),
    224         'url'      => array(
    225             'preview'       => esc_url_raw( $url ? $url : home_url( '/' ) ),
    226             'parent'        => esc_url_raw( admin_url() ),
    227             'activated'     => esc_url_raw( home_url( '/' ) ),
    228             'ajax'          => esc_url_raw( admin_url( 'admin-ajax.php', 'relative' ) ),
    229             'allowed'       => array_map( 'esc_url_raw', $allowed_urls ),
    230             'isCrossDomain' => $cross_domain,
    231             'home'          => esc_url_raw( home_url( '/' ) ),
    232             'login'         => esc_url_raw( $login_url ),
    233         ),
    234         'browser'  => array(
    235             'mobile' => wp_is_mobile(),
    236             'ios'    => $is_ios,
    237         ),
    238         'settings' => array(),
    239         'controls' => array(),
    240         'panels'   => array(),
    241         'sections' => array(),
    242         'nonce'    => array(
    243             'save'    => wp_create_nonce( 'save-customize_' . $wp_customize->get_stylesheet() ),
    244             'preview' => wp_create_nonce( 'preview-customize_' . $wp_customize->get_stylesheet() )
    245         ),
    246         'autofocus' => array(),
    247         'documentTitleTmpl' => $document_title_tmpl,
    248     );
    249 
    250     // Prepare Customize Setting objects to pass to JavaScript.
    251     foreach ( $wp_customize->settings() as $id => $setting ) {
    252         if ( $setting->check_capabilities() ) {
    253             $settings['settings'][ $id ] = array(
    254                 'value'     => $setting->js_value(),
    255                 'transport' => $setting->transport,
    256                 'dirty'     => $setting->dirty,
    257             );
    258         }
    259     }
    260 
    261     // Prepare Customize Control objects to pass to JavaScript.
    262     foreach ( $wp_customize->controls() as $id => $control ) {
    263         if ( $control->check_capabilities() ) {
    264             $settings['controls'][ $id ] = $control->json();
    265         }
    266     }
    267 
    268     // Prepare Customize Section objects to pass to JavaScript.
    269     foreach ( $wp_customize->sections() as $id => $section ) {
    270         if ( $section->check_capabilities() ) {
    271             $settings['sections'][ $id ] = $section->json();
    272         }
    273     }
    274 
    275     // Prepare Customize Panel objects to pass to JavaScript.
    276     foreach ( $wp_customize->panels() as $panel_id => $panel ) {
    277         if ( $panel->check_capabilities() ) {
    278             $settings['panels'][ $panel_id ] = $panel->json();
    279             foreach ( $panel->sections as $section_id => $section ) {
    280                 if ( $section->check_capabilities() ) {
    281                     $settings['sections'][ $section_id ] = $section->json();
    282                 }
    283             }
    284         }
    285     }
    286 
    287     // Pass to frontend the Customizer construct being deeplinked
    288     if ( isset( $_GET['autofocus'] ) ) {
    289         $autofocus = wp_unslash( $_GET['autofocus'] );
    290         if ( is_array( $autofocus ) ) {
    291             foreach ( $autofocus as $type => $id ) {
    292                 if ( isset( $settings[ $type . 's' ][ $id ] ) ) {
    293                     $settings['autofocus'][ $type ] = $id;
    294                 }
    295             }
    296         }
    297     }
    298 
    299166    ?>
    300     <script type="text/javascript">
    301         var _wpCustomizeSettings = <?php echo wp_json_encode( $settings ); ?>;
    302     </script>
    303167</div>
    304168</body>
  • trunk/src/wp-includes/class-wp-customize-manager.php

    r34233 r34269  
    9595
    9696    /**
     97     * Initial URL being previewed.
     98     *
     99     * @since 4.4.0
     100     * @access protected
     101     * @var string
     102     */
     103    protected $preview_url;
     104
     105    /**
     106     * URL to link the user to when closing the Customizer.
     107     *
     108     * @since 4.4.0
     109     * @access protected
     110     * @var string
     111     */
     112    protected $return_url;
     113
     114    /**
     115     * Mapping of 'panel', 'section', 'control' to the ID which should be autofocused.
     116     *
     117     * @since 4.4.0
     118     * @access protected
     119     * @var array
     120     */
     121    protected $autofocus = array();
     122
     123    /**
    97124     * Unsanitized values for Customize Settings parsed from $_POST['customized'].
    98125     *
     
    140167        add_action( 'customize_controls_init',            array( $this, 'prepare_controls' ) );
    141168        add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_control_scripts' ) );
     169
     170        // Render Panel, Section, and Control templates.
     171        add_action( 'customize_controls_print_footer_scripts', array( $this, 'render_panel_templates' ), 1 );
     172        add_action( 'customize_controls_print_footer_scripts', array( $this, 'render_section_templates' ), 1 );
     173        add_action( 'customize_controls_print_footer_scripts', array( $this, 'render_control_templates' ), 1 );
     174
     175        // Export the settings to JS via the _wpCustomizeSettings variable.
     176        add_action( 'customize_controls_print_footer_scripts', array( $this, 'customize_pane_settings' ), 1000 );
    142177    }
    143178
     
    622657    public function customize_preview_settings() {
    623658        $settings = array(
    624             'values'  => array(),
    625659            'channel' => wp_unslash( $_POST['customize_messenger_channel'] ),
    626660            'activePanels' => array(),
     
    639673        }
    640674
    641         foreach ( $this->settings as $id => $setting ) {
    642             if ( $setting->check_capabilities() ) {
    643                 $settings['values'][ $id ] = $setting->js_value();
    644             }
    645         }
    646675        foreach ( $this->panels as $panel_id => $panel ) {
    647676            if ( $panel->check_capabilities() ) {
     
    668697        <script type="text/javascript">
    669698            var _wpCustomizeSettings = <?php echo wp_json_encode( $settings ); ?>;
     699            _wpCustomizeSettings.values = {};
     700            (function( v ) {
     701                <?php
     702                /*
     703                 * Serialize settings separately from the initial _wpCustomizeSettings
     704                 * serialization in order to avoid a peak memory usage spike.
     705                 * @todo We may not even need to export the values at all since the pane syncs them anyway.
     706                 */
     707                foreach ( $this->settings as $id => $setting ) {
     708                    if ( $setting->check_capabilities() ) {
     709                        printf(
     710                            "v[%s] = %s;\n",
     711                            wp_json_encode( $id ),
     712                            wp_json_encode( $setting->js_value() )
     713                        );
     714                    }
     715                }
     716                ?>
     717            })( _wpCustomizeSettings.values );
    670718        </script>
    671719        <?php
     
    12631311            $control->enqueue();
    12641312        }
     1313    }
     1314
     1315    /**
     1316     * Return whether the user agent is iOS.
     1317     *
     1318     * @since 4.4.0
     1319     * @access public
     1320     *
     1321     * @return bool
     1322     */
     1323    public function is_ios() {
     1324        return wp_is_mobile() && preg_match( '/iPad|iPod|iPhone/', $_SERVER['HTTP_USER_AGENT'] );
     1325    }
     1326
     1327    /**
     1328     * Get the template string for the Customizer pane document title.
     1329     *
     1330     * @since 4.4.0
     1331     * @access public
     1332     *
     1333     * @return string
     1334     */
     1335    public function get_document_title_template() {
     1336        if ( $this->is_theme_active() ) {
     1337            $document_title_tmpl = _x( 'Customize: %s', 'Placeholder is the document title from the preview' );
     1338        } else {
     1339            $document_title_tmpl = _x( 'Live Preview: %s', 'Placeholder is the document title from the preview' );
     1340        }
     1341        $document_title_tmpl = html_entity_decode( $document_title_tmpl, ENT_QUOTES, 'UTF-8' ); // Because exported to JS and assigned to document.title.
     1342        return $document_title_tmpl;
     1343    }
     1344
     1345    /**
     1346     * Set the initial URL to be previewed.
     1347     *
     1348     * URL is validated.
     1349     *
     1350     * @since 4.4.0
     1351     * @access public
     1352     *
     1353     * @param string $preview_url  URL to be previewed.
     1354     */
     1355    public function set_preview_url( $preview_url ) {
     1356        $this->preview_url = wp_validate_redirect( $preview_url, home_url( '/' ) );
     1357    }
     1358
     1359    /**
     1360     * Get the initial URL to be previewed.
     1361     *
     1362     * @since 4.4.0
     1363     * @access public
     1364     *
     1365     * @return string
     1366     */
     1367    public function get_preview_url() {
     1368        if ( empty( $this->preview_url ) ) {
     1369            $preview_url = home_url( '/' );
     1370        } else {
     1371            $preview_url = $this->preview_url;
     1372        }
     1373        return $preview_url;
     1374    }
     1375
     1376    /**
     1377     * Set URL to link the user to when closing the Customizer.
     1378     *
     1379     * URL is validated.
     1380     *
     1381     * @since 4.4.0
     1382     * @access public
     1383     *
     1384     * @param string $return_url  URL for return link.
     1385     */
     1386    public function set_return_url( $return_url ) {
     1387        $return_url = remove_query_arg( wp_removable_query_args(), $return_url );
     1388        $return_url = wp_validate_redirect( $return_url );
     1389        $this->return_url = $return_url;
     1390    }
     1391
     1392    /**
     1393     * Get URL to link the user to when closing the Customizer.
     1394     *
     1395     * @since 4.4.0
     1396     * @access public
     1397     *
     1398     * @return string
     1399     */
     1400    public function get_return_url() {
     1401        if ( $this->return_url ) {
     1402            $return_url = $this->return_url;
     1403        } else if ( $this->preview_url ) {
     1404            $return_url = $this->preview_url;
     1405        } else if ( current_user_can( 'edit_theme_options' ) || current_user_can( 'switch_themes' ) ) {
     1406            $return_url = admin_url( 'themes.php' );
     1407        } else {
     1408            $return_url = admin_url();
     1409        }
     1410        return $return_url;
     1411    }
     1412
     1413    /**
     1414     * Set the autofocused constructs.
     1415     *
     1416     * @param array $autofocus {
     1417     *     Mapping of 'panel', 'section', 'control' to the ID which should be autofocused.
     1418     *
     1419     *     @type string [$control]  ID for control to be autofocused.
     1420     *     @type string [$section]  ID for section to be autofocused.
     1421     *     @type string [$panel]    ID for panel to be autofocused.
     1422     * }
     1423     */
     1424    public function set_autofocus( $autofocus ) {
     1425        $this->autofocus = array_filter( wp_array_slice_assoc( $autofocus, array( 'panel', 'section', 'control' ) ), 'is_string' );
     1426    }
     1427
     1428    /**
     1429     * Get the autofocused constructs.
     1430     *
     1431     * @since 4.4.0
     1432     * @access public
     1433     *
     1434     * @return array {
     1435     *     Mapping of 'panel', 'section', 'control' to the ID which should be autofocused.
     1436     *
     1437     *     @type string [$control]  ID for control to be autofocused.
     1438     *     @type string [$section]  ID for section to be autofocused.
     1439     *     @type string [$panel]    ID for panel to be autofocused.
     1440     * }
     1441     */
     1442    public function get_autofocus() {
     1443        return $this->autofocus;
     1444    }
     1445
     1446    /**
     1447     * Print JavaScript settings for parent window.
     1448     *
     1449     * @since 4.3.0
     1450     */
     1451    public function customize_pane_settings() {
     1452        /*
     1453         * If the frontend and the admin are served from the same domain, load the
     1454         * preview over ssl if the Customizer is being loaded over ssl. This avoids
     1455         * insecure content warnings. This is not attempted if the admin and frontend
     1456         * are on different domains to avoid the case where the frontend doesn't have
     1457         * ssl certs. Domain mapping plugins can allow other urls in these conditions
     1458         * using the customize_allowed_urls filter.
     1459         */
     1460
     1461        $allowed_urls = array( home_url( '/' ) );
     1462        $admin_origin = parse_url( admin_url() );
     1463        $home_origin  = parse_url( home_url() );
     1464        $cross_domain = ( strtolower( $admin_origin['host'] ) !== strtolower( $home_origin['host'] ) );
     1465
     1466        if ( is_ssl() && ! $cross_domain ) {
     1467            $allowed_urls[] = home_url( '/', 'https' );
     1468        }
     1469
     1470        /**
     1471         * Filter the list of URLs allowed to be clicked and followed in the Customizer preview.
     1472         *
     1473         * @since 3.4.0
     1474         *
     1475         * @param array $allowed_urls An array of allowed URLs.
     1476         */
     1477        $allowed_urls = array_unique( apply_filters( 'customize_allowed_urls', $allowed_urls ) );
     1478
     1479        $login_url = add_query_arg( array(
     1480            'interim-login' => 1,
     1481            'customize-login' => 1,
     1482        ), wp_login_url() );
     1483
     1484        // Prepare Customizer settings to pass to JavaScript.
     1485        $settings = array(
     1486            'theme'    => array(
     1487                'stylesheet' => $this->get_stylesheet(),
     1488                'active'     => $this->is_theme_active(),
     1489            ),
     1490            'url'      => array(
     1491                'preview'       => esc_url_raw( $this->get_preview_url() ),
     1492                'parent'        => esc_url_raw( admin_url() ),
     1493                'activated'     => esc_url_raw( home_url( '/' ) ),
     1494                'ajax'          => esc_url_raw( admin_url( 'admin-ajax.php', 'relative' ) ),
     1495                'allowed'       => array_map( 'esc_url_raw', $allowed_urls ),
     1496                'isCrossDomain' => $cross_domain,
     1497                'home'          => esc_url_raw( home_url( '/' ) ),
     1498                'login'         => esc_url_raw( $login_url ),
     1499            ),
     1500            'browser'  => array(
     1501                'mobile' => wp_is_mobile(),
     1502                'ios'    => $this->is_ios(),
     1503            ),
     1504            'panels'   => array(),
     1505            'sections' => array(),
     1506            'nonce'    => array(
     1507                'save'    => wp_create_nonce( 'save-customize_' . $this->get_stylesheet() ),
     1508                'preview' => wp_create_nonce( 'preview-customize_' . $this->get_stylesheet() ),
     1509            ),
     1510            'autofocus' => array(),
     1511            'documentTitleTmpl' => $this->get_document_title_template(),
     1512        );
     1513
     1514        // Prepare Customize Section objects to pass to JavaScript.
     1515        foreach ( $this->sections() as $id => $section ) {
     1516            if ( $section->check_capabilities() ) {
     1517                $settings['sections'][ $id ] = $section->json();
     1518            }
     1519        }
     1520
     1521        // Prepare Customize Panel objects to pass to JavaScript.
     1522        foreach ( $this->panels() as $panel_id => $panel ) {
     1523            if ( $panel->check_capabilities() ) {
     1524                $settings['panels'][ $panel_id ] = $panel->json();
     1525                foreach ( $panel->sections as $section_id => $section ) {
     1526                    if ( $section->check_capabilities() ) {
     1527                        $settings['sections'][ $section_id ] = $section->json();
     1528                    }
     1529                }
     1530            }
     1531        }
     1532
     1533        // Pass to frontend the Customizer construct being deeplinked.
     1534        foreach ( $this->get_autofocus() as $type => $id ) {
     1535            $can_autofocus = (
     1536                ( 'control' === $type && $this->get_control( $id ) && $this->get_control( $id )->check_capabilities() )
     1537                ||
     1538                ( 'section' === $type && isset( $settings['sections'][ $id ] ) )
     1539                ||
     1540                ( 'panel' === $type && isset( $settings['panels'][ $id ] ) )
     1541            );
     1542            if ( $can_autofocus ) {
     1543                $settings['autofocus'][ $type ] = $id;
     1544            }
     1545        }
     1546
     1547        ?>
     1548        <script type="text/javascript">
     1549            var _wpCustomizeSettings = <?php echo wp_json_encode( $settings ); ?>;
     1550            _wpCustomizeSettings.controls = {};
     1551            _wpCustomizeSettings.settings = {};
     1552            <?php
     1553
     1554            // Serialize settings one by one to improve memory usage.
     1555            echo "(function ( s ){\n";
     1556            foreach ( $this->settings() as $setting ) {
     1557                if ( $setting->check_capabilities() ) {
     1558                    printf(
     1559                        "s[%s] = %s;\n",
     1560                        wp_json_encode( $setting->id ),
     1561                        wp_json_encode( array(
     1562                            'value'     => $setting->js_value(),
     1563                            'transport' => $setting->transport,
     1564                            'dirty'     => $setting->dirty,
     1565                        ) )
     1566                    );
     1567                }
     1568            }
     1569            echo "})( _wpCustomizeSettings.settings );\n";
     1570
     1571            // Serialize controls one by one to improve memory usage.
     1572            echo "(function ( c ){\n";
     1573            foreach ( $this->controls() as $control ) {
     1574                if ( $control->check_capabilities() ) {
     1575                    printf(
     1576                        "c[%s] = %s;\n",
     1577                        wp_json_encode( $control->id ),
     1578                        wp_json_encode( $control->json() )
     1579                    );
     1580                }
     1581            }
     1582            echo "})( _wpCustomizeSettings.controls );\n";
     1583        ?>
     1584        </script>
     1585        <?php
    12651586    }
    12661587
  • trunk/tests/phpunit/tests/customize/manager.php

    r31622 r34269  
    11<?php
     2/**
     3 * WP_Customize_Manager tests.
     4 *
     5 * @package WordPress
     6 */
    27
    38/**
     
    813class Tests_WP_Customize_Manager extends WP_UnitTestCase {
    914
     15    /**
     16     * Customize manager instance re-instantiated with each test.
     17     *
     18     * @var WP_Customize_Manager
     19     */
     20    public $manager;
     21
     22    /**
     23     * Symbol.
     24     *
     25     * @var stdClass
     26     */
     27    public $undefined;
     28
     29    /**
     30     * Set up test.
     31     */
    1032    function setUp() {
    1133        parent::setUp();
     
    1638    }
    1739
     40    /**
     41     * Tear down test.
     42     */
    1843    function tearDown() {
    1944        $this->manager = null;
     
    157182    /**
    158183     * In lieu of closures, callback for customize_dynamic_setting_args filter added for test_register_dynamic_settings().
     184     *
     185     * @param array  $setting_args Setting args.
     186     * @param string $setting_id   Setting ID.
     187     * @return array
    159188     */
    160189    function filter_customize_dynamic_setting_args_for_test_dynamic_settings( $setting_args, $setting_id ) {
     
    169198    /**
    170199     * In lieu of closures, callback for customize_dynamic_setting_class filter added for test_register_dynamic_settings().
     200     *
     201     * @param string $setting_class Setting class.
     202     * @param string $setting_id    Setting ID.
     203     * @param array  $setting_args  Setting args.
     204     * @return string
    171205     */
    172206    function filter_customize_dynamic_setting_class_for_test_dynamic_settings( $setting_class, $setting_id, $setting_args ) {
     
    176210        return $setting_class;
    177211    }
     212
     213    /**
     214     * Test is_ios() method.
     215     *
     216     * @see WP_Customize_Manager::is_ios()
     217     */
     218    function test_is_ios() {
     219        $this->markTestSkipped( 'WP_Customize_Manager::is_ios() cannot be tested because it uses wp_is_mobile() which contains a static var.' );
     220    }
     221
     222    /**
     223     * Test get_document_title_template() method.
     224     *
     225     * @see WP_Customize_Manager::get_document_title_template()
     226     */
     227    function test_get_document_title_template() {
     228        $tpl = $this->manager->get_document_title_template();
     229        $this->assertContains( '%s', $tpl );
     230    }
     231
     232    /**
     233     * Test get_preview_url()/set_preview_url methods.
     234     *
     235     * @see WP_Customize_Manager::get_preview_url()
     236     * @see WP_Customize_Manager::set_preview_url()
     237     */
     238    function test_preview_url() {
     239        $this->assertEquals( home_url( '/' ), $this->manager->get_preview_url() );
     240        $preview_url = home_url( '/foo/bar/baz/' );
     241        $this->manager->set_preview_url( $preview_url );
     242        $this->assertEquals( $preview_url, $this->manager->get_preview_url() );
     243        $this->manager->set_preview_url( 'http://illegalsite.example.com/food/' );
     244        $this->assertEquals( home_url( '/' ), $this->manager->get_preview_url() );
     245    }
     246
     247    /**
     248     * Test get_return_url()/set_return_url() methods.
     249     *
     250     * @see WP_Customize_Manager::get_return_url()
     251     * @see WP_Customize_Manager::set_return_url()
     252     */
     253    function test_return_url() {
     254        wp_set_current_user( $this->factory->user->create( array( 'role' => 'author' ) ) );
     255        $this->assertEquals( get_admin_url(), $this->manager->get_return_url() );
     256
     257        wp_set_current_user( $this->factory->user->create( array( 'role' => 'administrator' ) ) );
     258        $this->assertTrue( current_user_can( 'edit_theme_options' ) );
     259        $this->assertEquals( admin_url( 'themes.php' ), $this->manager->get_return_url() );
     260
     261        $preview_url = home_url( '/foo/' );
     262        $this->manager->set_preview_url( $preview_url );
     263        $this->assertEquals( $preview_url, $this->manager->get_return_url() );
     264
     265        $this->manager->set_return_url( admin_url( 'edit.php?trashed=1' ) );
     266        $this->assertEquals( admin_url( 'edit.php' ), $this->manager->get_return_url() );
     267    }
     268
     269    /**
     270     * Test get_autofocus()/set_autofocus() methods.
     271     *
     272     * @see WP_Customize_Manager::get_autofocus()
     273     * @see WP_Customize_Manager::set_autofocus()
     274     */
     275    function test_autofocus() {
     276        $this->assertEmpty( $this->manager->get_autofocus() );
     277
     278        $this->manager->set_autofocus( array( 'unrecognized' => 'food' ) );
     279        $this->assertEmpty( $this->manager->get_autofocus() );
     280
     281        $autofocus = array( 'control' => 'blogname' );
     282        $this->manager->set_autofocus( $autofocus );
     283        $this->assertEquals( $autofocus, $this->manager->get_autofocus() );
     284
     285        $autofocus = array( 'section' => 'colors' );
     286        $this->manager->set_autofocus( $autofocus );
     287        $this->assertEquals( $autofocus, $this->manager->get_autofocus() );
     288
     289        $autofocus = array( 'panel' => 'widgets' );
     290        $this->manager->set_autofocus( $autofocus );
     291        $this->assertEquals( $autofocus, $this->manager->get_autofocus() );
     292
     293        $autofocus = array( 'control' => array( 'blogname', 'blogdescription' ) );
     294        $this->manager->set_autofocus( $autofocus );
     295        $this->assertEmpty( $this->manager->get_autofocus() );
     296    }
     297
     298    /**
     299     * Test customize_pane_settings() method.
     300     *
     301     * @see WP_Customize_Manager::customize_pane_settings()
     302     */
     303    function test_customize_pane_settings() {
     304        wp_set_current_user( $this->factory->user->create( array( 'role' => 'administrator' ) ) );
     305        $this->manager->register_controls();
     306        $this->manager->prepare_controls();
     307        $autofocus = array( 'control' => 'blogname' );
     308        $this->manager->set_autofocus( $autofocus );
     309
     310        ob_start();
     311        $this->manager->customize_pane_settings();
     312        $content = ob_get_clean();
     313
     314        $this->assertContains( 'var _wpCustomizeSettings =', $content );
     315        $this->assertContains( '"blogname"', $content );
     316        $this->assertContains( '_wpCustomizeSettings.controls', $content );
     317        $this->assertContains( '_wpCustomizeSettings.settings', $content );
     318        $this->assertContains( '</script>', $content );
     319
     320        $this->assertNotEmpty( preg_match( '#var _wpCustomizeSettings\s*=\s*({.*?});\s*\n#', $content, $matches ) );
     321        $json = $matches[1];
     322        $data = json_decode( $json, true );
     323        $this->assertNotEmpty( $data );
     324
     325        $this->assertEqualSets( array( 'theme', 'url', 'browser', 'panels', 'sections', 'nonce', 'autofocus', 'documentTitleTmpl' ), array_keys( $data ) );
     326        $this->assertEquals( $autofocus, $data['autofocus'] );
     327        $this->assertArrayHasKey( 'save', $data['nonce'] );
     328        $this->assertArrayHasKey( 'preview', $data['nonce'] );
     329    }
    178330}
Note: See TracChangeset for help on using the changeset viewer.