]> source.dussan.org Git - jquery-ui.git/commitdiff
Tabs: Use `CSS.escape` for sanitizing selectors
authorMichał Gołębiowski-Owczarek <m.goleb@gmail.com>
Sat, 26 Oct 2024 22:04:00 +0000 (00:04 +0200)
committerGitHub <noreply@github.com>
Sat, 26 Oct 2024 22:04:00 +0000 (00:04 +0200)
The previous private `_sanitizeSelector` API was not correctly escaping
backslashes and is now removed. The native API should always be correct.

Closes gh-2307

tests/unit/tabs/core.js
tests/unit/tabs/helper.js
ui/widgets/tabs.js

index ae670ff430363f15eee7ff86f97c343ba5d9b2b4..c2fd8904884c45b23fb30e6ac913a0e6ca469168 100644 (file)
@@ -84,6 +84,37 @@ QUnit.test( "non-tab list items", function( assert ) {
                "first actual tab is active" );
 } );
 
+QUnit.test( "ID escaping backslashes", function( assert ) {
+       assert.expect( 5 );
+
+       location.hash = "#fragment\b-2";
+
+       var element = $( "#tabs1" )
+               .find( "a[href='#fragment-2']" )
+                       .attr( "href", "#fragment\b-2" )
+               .end()
+               .find( "#fragment-2" )
+                       .attr( "id", "fragment\b-2" )
+               .end()
+               .tabs();
+       var tabs = element.find( ".ui-tabs-nav li" );
+       var anchors = tabs.find( ".ui-tabs-anchor" );
+       var panels = element.find( ".ui-tabs-panel" );
+
+       assert.strictEqual( element.tabs( "option", "active" ), 1,
+               "should set the active option" );
+
+       assert.strictEqual( anchors.length, 3, "should decorate all anchors" );
+       assert.strictEqual( panels.length, 3, "should decorate all panels" );
+
+       assert.strictEqual( panels.eq( 1 ).attr( "aria-labelledby" ), anchors.eq( 1 ).attr( "id" ),
+               "panel 2 aria-labelledby equals anchor 2 id" );
+       assert.strictEqual( tabs.eq( 1 ).attr( "aria-controls" ), "fragment\b-2",
+               "tab 2 aria-controls" );
+
+       location.hash = "";
+} );
+
 QUnit.test( "aria-controls", function( assert ) {
        assert.expect( 7 );
        var element = $( "#tabs1" ).tabs(),
index b3fb1d6fd42865334e13acb49f50a55c6002a4b4..4043d86d8fd4d524f02d77855145ff85916368cd 100644 (file)
@@ -58,8 +58,7 @@ return $.extend( helper, {
                var expected = $.makeArray( arguments ).slice( 2 ),
                        actual = tabs.find( ".ui-tabs-nav li" ).map( function() {
                                var tab = $( this ),
-                                       panel = $( $.ui.tabs.prototype._sanitizeSelector(
-                                               "#" + tab.attr( "aria-controls" ) ) ),
+                                       panel = $( "#" + CSS.escape( tab.attr( "aria-controls" ) ) ),
                                        tabIsActive = tab.hasClass( "ui-state-active" ),
                                        panelIsActive = panel.css( "display" ) !== "none";
 
index 72b868e4f05a7a776e21fcfe7d4977a9d67d2356..7b7907c327730aab15e056a8eeb8e767d7f3b704 100644 (file)
@@ -121,14 +121,14 @@ $.widget( "ui.tabs", {
        _initialActive: function() {
                var active = this.options.active,
                        collapsible = this.options.collapsible,
-                       locationHash = location.hash.substring( 1 );
+                       locationHashDecoded = decodeURIComponent( location.hash.substring( 1 ) );
 
                if ( active === null ) {
 
                        // check the fragment identifier in the URL
-                       if ( locationHash ) {
+                       if ( locationHashDecoded ) {
                                this.tabs.each( function( i, tab ) {
-                                       if ( $( tab ).attr( "aria-controls" ) === locationHash ) {
+                                       if ( $( tab ).attr( "aria-controls" ) === locationHashDecoded ) {
                                                active = i;
                                                return false;
                                        }
@@ -312,10 +312,6 @@ $.widget( "ui.tabs", {
                }
        },
 
-       _sanitizeSelector: function( hash ) {
-               return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : "";
-       },
-
        refresh: function() {
                var options = this.options,
                        lis = this.tablist.children( ":has(a[href])" );
@@ -434,9 +430,9 @@ $.widget( "ui.tabs", {
 
                        // Inline tab
                        if ( that._isLocal( anchor ) ) {
-                               selector = anchor.hash;
+                               selector = decodeURIComponent( anchor.hash );
                                panelId = selector.substring( 1 );
-                               panel = that.element.find( that._sanitizeSelector( selector ) );
+                               panel = that.element.find( "#" + CSS.escape( panelId ) );
 
                        // remote tab
                        } else {
@@ -874,7 +870,7 @@ $.widget( "ui.tabs", {
 
        _getPanelForTab: function( tab ) {
                var id = $( tab ).attr( "aria-controls" );
-               return this.element.find( this._sanitizeSelector( "#" + id ) );
+               return this.element.find( "#" + CSS.escape( id ) );
        }
 } );