Skip to content

Commit 225bde3

Browse files
committed
Manipulation: Make an HTML interception point
Fixes gh-1747 Closes gh-2203
1 parent 4b27ae1 commit 225bde3

File tree

3 files changed

+85
-50
lines changed

3 files changed

+85
-50
lines changed

src/manipulation.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,10 @@ function fixInput( src, dest ) {
159159
}
160160

161161
jQuery.extend({
162+
htmlPrefilter: function( html ) {
163+
return html.replace( rxhtmlTag, "<$1></$2>" );
164+
},
165+
162166
clone: function( elem, dataAndEvents, deepDataAndEvents ) {
163167
var i, l, srcElements, destElements,
164168
clone = elem.cloneNode( true ),
@@ -230,7 +234,7 @@ jQuery.extend({
230234
// Deserialize a standard representation
231235
tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
232236
wrap = wrapMap[ tag ] || wrapMap._default;
233-
tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[ 2 ];
237+
tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];
234238

235239
// Descend through wrappers to the right content
236240
j = wrap[ 0 ];
@@ -422,7 +426,7 @@ jQuery.fn.extend({
422426
if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
423427
!wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) {
424428

425-
value = value.replace( rxhtmlTag, "<$1></$2>" );
429+
value = jQuery.htmlPrefilter( value );
426430

427431
try {
428432
for ( ; i < l; i++ ) {

test/unit/core.js

+4-8
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

+75-40
Original file line numberDiff line numberDiff line change
@@ -2080,7 +2080,7 @@ test( "jQuery.cleanData eliminates all private data (gh-2127)", function() {
20802080
div.remove();
20812081
});
20822082

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

20852085
expect( 1 );
20862086

@@ -2099,42 +2099,43 @@ test( "jQuery.buildFragment - no plain-text caching (Bug #6779)", function() {
20992099
$f.remove();
21002100
});
21012101

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

21042104
expect( 3 );
21052105

2106-
jQuery([
2107-
"<script type='text/javascript'>",
2108-
"<!--",
2109-
"ok( true, '<!-- handled' );",
2110-
"//-->",
2111-
"</script>"
2112-
].join("\n")).appendTo("#qunit-fixture");
2113-
jQuery([
2114-
"<script type='text/javascript'>",
2115-
"<![CDATA[",
2116-
"ok( true, '<![CDATA[ handled' );",
2117-
"//]]>",
2118-
"</script>"
2119-
].join("\n")).appendTo("#qunit-fixture");
2120-
jQuery([
2121-
"<script type='text/javascript'>",
2122-
"<!--//--><![CDATA[//><!--",
2123-
"ok( true, '<!--//--><![CDATA[//><!-- (Drupal case) handled' );",
2124-
"//--><!]]>",
2125-
"</script>"
2126-
].join("\n")).appendTo("#qunit-fixture");
2127-
});
2128-
2129-
test( "jQuery.buildFragment - plain objects are not a document #8950", function() {
2130-
2131-
expect( 1 );
2132-
2133-
try {
2134-
jQuery( "<input type='hidden'>", {} );
2135-
ok( true, "Does not allow attribute object to be treated like a doc object" );
2136-
} catch ( e ) {}
2137-
});
2106+
jQuery( [
2107+
"<script type='text/javascript'>",
2108+
"<!--",
2109+
"ok( true, '<!-- handled' );",
2110+
"//-->",
2111+
"</script>"
2112+
].join( "\n" ) ).appendTo( "#qunit-fixture" );
2113+
2114+
jQuery( [
2115+
"<script type='text/javascript'>",
2116+
"<![CDATA[",
2117+
"ok( true, '<![CDATA[ handled' );",
2118+
"//]]>",
2119+
"</script>"
2120+
].join( "\n" ) ).appendTo( "#qunit-fixture" );
2121+
2122+
jQuery( [
2123+
"<script type='text/javascript'>",
2124+
"<!--//--><![CDATA[//><!--",
2125+
"ok( true, '<!--//--><![CDATA[//><!-- (Drupal case) handled' );",
2126+
"//--><!]]>",
2127+
"</script>"
2128+
].join( "\n" ) ).appendTo( "#qunit-fixture" );
2129+
});
2130+
2131+
testIframeWithCallback(
2132+
"domManip tolerates window-valued document[0] in IE9/10 (trac-12266)",
2133+
"manipulation/iframe-denied.html",
2134+
function( test ) {
2135+
expect( 1 );
2136+
ok( test.status, test.description );
2137+
}
2138+
);
21382139

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

@@ -2296,12 +2297,6 @@ test( "manipulate mixed jQuery and text (#12384, #12346)", function() {
22962297
equal( div.find("*").length, 3, "added 2 paragraphs after inner div" );
22972298
});
22982299

2299-
testIframeWithCallback( "buildFragment works even if document[0] is iframe's window object in IE9/10 (#12266)", "manipulation/iframe-denied.html", function( test ) {
2300-
expect( 1 );
2301-
2302-
ok( test.status, test.description );
2303-
});
2304-
23052300
test( "script evaluation (#11795)", function() {
23062301

23072302
expect( 13 );
@@ -2385,6 +2380,46 @@ test( "jQuery._evalUrl (#12838)", function() {
23852380
jQuery._evalUrl = evalUrl;
23862381
});
23872382

2383+
test( "jQuery.htmlPrefilter (gh-1747)", function( assert ) {
2384+
2385+
assert.expect( 5 );
2386+
2387+
var expectedArgument,
2388+
invocations = 0,
2389+
htmlPrefilter = jQuery.htmlPrefilter,
2390+
fixture = jQuery( "<div/>" ).appendTo( "#qunit-fixture" ),
2391+
poison = "<script>jQuery.htmlPrefilter.assert.ok( false, 'script not executed' );</script>",
2392+
done = assert.async();
2393+
2394+
jQuery.htmlPrefilter = function( html ) {
2395+
invocations++;
2396+
assert.equal( html, expectedArgument, "Expected input" );
2397+
2398+
// Remove <script> and <del> elements
2399+
return htmlPrefilter.apply( this, arguments )
2400+
.replace( /<(script|del)(?=[\s>])[\w\W]*?<\/\1\s*>/ig, "" );
2401+
};
2402+
jQuery.htmlPrefilter.assert = assert;
2403+
2404+
expectedArgument = "A-" + poison + "B-" + poison + poison + "C-";
2405+
fixture.html( expectedArgument );
2406+
2407+
expectedArgument = "D-" + poison + "E-" + "<del/><div>" + poison + poison + "</div>" + "F-";
2408+
fixture.append( expectedArgument );
2409+
2410+
expectedArgument = poison;
2411+
fixture.find( "div" ).replaceWith( expectedArgument );
2412+
2413+
assert.equal( invocations, 3, "htmlPrefilter invoked for all DOM manipulations" );
2414+
assert.equal( fixture.html(), "A-B-C-D-E-F-", "htmlPrefilter modified HTML" );
2415+
2416+
// Allow asynchronous script execution to generate assertions
2417+
setTimeout( function() {
2418+
jQuery.htmlPrefilter = htmlPrefilter;
2419+
done();
2420+
}, 100 );
2421+
});
2422+
23882423
test( "insertAfter, insertBefore, etc do not work when destination is original element. Element is removed (#4087)", function() {
23892424

23902425
expect( 10 );

0 commit comments

Comments
 (0)