Skip to content

Commit fb25bac

Browse files
committed
Manipulation: Make an HTML interception point
Fixes gh-1747 Closes gh-2203 (cherry picked from commit 225bde3) Conflicts: src/manipulation.js test/unit/manipulation.js
1 parent 4cafb58 commit fb25bac

File tree

3 files changed

+85
-51
lines changed

3 files changed

+85
-51
lines changed

src/manipulation.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,10 @@ function fixCloneNodeIssues( src, dest ) {
217217
}
218218

219219
jQuery.extend({
220+
htmlPrefilter: function( html ) {
221+
return html.replace( rxhtmlTag, "<$1></$2>" );
222+
},
223+
220224
clone: function( elem, dataAndEvents, deepDataAndEvents ) {
221225
var destElements, node, clone, i, srcElements,
222226
inPage = jQuery.contains( elem.ownerDocument, elem );
@@ -304,8 +308,7 @@ jQuery.extend({
304308
// Deserialize a standard representation
305309
tag = (rtagName.exec( elem ) || [ "", "" ])[ 1 ].toLowerCase();
306310
wrap = wrapMap[ tag ] || wrapMap._default;
307-
308-
tmp.innerHTML = wrap[1] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[2];
311+
tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];
309312

310313
// Descend through wrappers to the right content
311314
j = wrap[0];
@@ -549,7 +552,7 @@ jQuery.fn.extend({
549552
( support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
550553
!wrapMap[ (rtagName.exec( value ) || [ "", "" ])[ 1 ].toLowerCase() ] ) {
551554

552-
value = value.replace( rxhtmlTag, "<$1></$2>" );
555+
value = jQuery.htmlPrefilter( value );
553556

554557
try {
555558
for (; i < l; i++ ) {

test/unit/core.js

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ test("jQuery()", function() {
1919
img = jQuery("<img/>"),
2020
div = jQuery("<div/><hr/><code/><b/>"),
2121
exec = false,
22-
lng = "",
23-
expected = 22,
22+
expected = 23,
2423
attrObj = {
2524
"text": "test",
2625
"class": "test2",
@@ -141,12 +140,9 @@ test("jQuery()", function() {
141140
}
142141
equal( elem[0].defaultValue, "TEST", "Ensure cached nodes are cloned properly (Bug #6655)" );
143142

144-
// manually clean up detached elements
145-
elem.remove();
146-
147-
for ( i = 0; i < 128; i++ ) {
148-
lng += "12345678";
149-
}
143+
elem = jQuery( "<input type='hidden'>", {} );
144+
strictEqual( elem[ 0 ].ownerDocument, document,
145+
"Empty attributes object is not interpreted as a document (trac-8950)" );
150146
});
151147

152148
test("jQuery(selector, context)", function() {

test/unit/manipulation.js

Lines changed: 75 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2137,7 +2137,7 @@ test( "jQuery.cleanData", function() {
21372137
}
21382138
});
21392139

2140-
test( "jQuery.buildFragment - no plain-text caching (Bug #6779)", function() {
2140+
test( "domManip plain-text caching (trac-6779)", function() {
21412141

21422142
expect( 1 );
21432143

@@ -2156,42 +2156,43 @@ test( "jQuery.buildFragment - no plain-text caching (Bug #6779)", function() {
21562156
$f.remove();
21572157
});
21582158

2159-
test( "jQuery.html - execute scripts escaped with html comment or CDATA (#9221)", function() {
2159+
test( "domManip executes scripts containing html comments or CDATA (trac-9221)", function() {
21602160

21612161
expect( 3 );
21622162

2163-
jQuery([
2164-
"<script type='text/javascript'>",
2165-
"<!--",
2166-
"ok( true, '<!-- handled' );",
2167-
"//-->",
2168-
"</script>"
2169-
].join("\n")).appendTo("#qunit-fixture");
2170-
jQuery([
2171-
"<script type='text/javascript'>",
2172-
"<![CDATA[",
2173-
"ok( true, '<![CDATA[ handled' );",
2174-
"//]]>",
2175-
"</script>"
2176-
].join("\n")).appendTo("#qunit-fixture");
2177-
jQuery([
2178-
"<script type='text/javascript'>",
2179-
"<!--//--><![CDATA[//><!--",
2180-
"ok( true, '<!--//--><![CDATA[//><!-- (Drupal case) handled' );",
2181-
"//--><!]]>",
2182-
"</script>"
2183-
].join("\n")).appendTo("#qunit-fixture");
2184-
});
2185-
2186-
test( "jQuery.buildFragment - plain objects are not a document #8950", function() {
2187-
2188-
expect( 1 );
2189-
2190-
try {
2191-
jQuery( "<input type='hidden'>", {} );
2192-
ok( true, "Does not allow attribute object to be treated like a doc object" );
2193-
} catch ( e ) {}
2194-
});
2163+
jQuery( [
2164+
"<script type='text/javascript'>",
2165+
"<!--",
2166+
"ok( true, '<!-- handled' );",
2167+
"//-->",
2168+
"</script>"
2169+
].join( "\n" ) ).appendTo( "#qunit-fixture" );
2170+
2171+
jQuery( [
2172+
"<script type='text/javascript'>",
2173+
"<![CDATA[",
2174+
"ok( true, '<![CDATA[ handled' );",
2175+
"//]]>",
2176+
"</script>"
2177+
].join( "\n" ) ).appendTo( "#qunit-fixture" );
2178+
2179+
jQuery( [
2180+
"<script type='text/javascript'>",
2181+
"<!--//--><![CDATA[//><!--",
2182+
"ok( true, '<!--//--><![CDATA[//><!-- (Drupal case) handled' );",
2183+
"//--><!]]>",
2184+
"</script>"
2185+
].join( "\n" ) ).appendTo( "#qunit-fixture" );
2186+
});
2187+
2188+
testIframeWithCallback(
2189+
"domManip tolerates window-valued document[0] in IE9/10 (trac-12266)",
2190+
"manipulation/iframe-denied.html",
2191+
function( test ) {
2192+
expect( 1 );
2193+
ok( test.status, test.description );
2194+
}
2195+
);
21952196

21962197
test( "jQuery.clone - no exceptions for object elements #9587", function() {
21972198

@@ -2360,12 +2361,6 @@ test( "manipulate mixed jQuery and text (#12384, #12346)", function() {
23602361
equal( div.find("*").length, 3, "added 2 paragraphs after inner div" );
23612362
});
23622363

2363-
testIframeWithCallback( "buildFragment works even if document[0] is iframe's window object in IE9/10 (#12266)", "manipulation/iframe-denied.html", function( test ) {
2364-
expect( 1 );
2365-
2366-
ok( test.status, test.description );
2367-
});
2368-
23692364
test( "script evaluation (#11795)", function() {
23702365

23712366
expect( 13 );
@@ -2449,6 +2444,46 @@ test( "jQuery._evalUrl (#12838)", function() {
24492444
jQuery._evalUrl = evalUrl;
24502445
});
24512446

2447+
test( "jQuery.htmlPrefilter (gh-1747)", function( assert ) {
2448+
2449+
assert.expect( 5 );
2450+
2451+
var expectedArgument,
2452+
invocations = 0,
2453+
htmlPrefilter = jQuery.htmlPrefilter,
2454+
fixture = jQuery( "<div/>" ).appendTo( "#qunit-fixture" ),
2455+
poison = "<script>jQuery.htmlPrefilter.assert.ok( false, 'script not executed' );</script>",
2456+
done = assert.async();
2457+
2458+
jQuery.htmlPrefilter = function( html ) {
2459+
invocations++;
2460+
assert.equal( html, expectedArgument, "Expected input" );
2461+
2462+
// Remove <script> and <del> elements
2463+
return htmlPrefilter.apply( this, arguments )
2464+
.replace( /<(script|del)(?=[\s>])[\w\W]*?<\/\1\s*>/ig, "" );
2465+
};
2466+
jQuery.htmlPrefilter.assert = assert;
2467+
2468+
expectedArgument = "A-" + poison + "B-" + poison + poison + "C-";
2469+
fixture.html( expectedArgument );
2470+
2471+
expectedArgument = "D-" + poison + "E-" + "<del/><div>" + poison + poison + "</div>" + "F-";
2472+
fixture.append( expectedArgument );
2473+
2474+
expectedArgument = poison;
2475+
fixture.find( "div" ).replaceWith( expectedArgument );
2476+
2477+
assert.equal( invocations, 3, "htmlPrefilter invoked for all DOM manipulations" );
2478+
assert.equal( fixture.html(), "A-B-C-D-E-F-", "htmlPrefilter modified HTML" );
2479+
2480+
// Allow asynchronous script execution to generate assertions
2481+
setTimeout( function() {
2482+
jQuery.htmlPrefilter = htmlPrefilter;
2483+
done();
2484+
}, 100 );
2485+
});
2486+
24522487
test( "insertAfter, insertBefore, etc do not work when destination is original element. Element is removed (#4087)", function() {
24532488

24542489
expect( 10 );

0 commit comments

Comments
 (0)