@@ -276,13 +276,39 @@ function inOwnerDocument(el) {
276
276
const rootNode = el . getRootNode ( ) ;
277
277
return rootNode instanceof ShadowRoot && el . ownerDocument . contains ( rootNode . host ) ;
278
278
}
279
+ /**
280
+ * Determine whether the given element is contained in a specific root documnet:
281
+ * either directly or with a shadow root in between or in an iframe.
282
+ */
283
+ function isAttachedToDocument ( element , documentElement ) {
284
+ let current = element ;
285
+ const shadowRoot = documentElement . defaultView . ShadowRoot ;
286
+ while ( current ) {
287
+ if ( current === documentElement ) {
288
+ return true ;
289
+ }
290
+ if ( current . parentNode ) {
291
+ current = current . parentNode ;
292
+ }
293
+ else if ( current instanceof shadowRoot && current . host ) {
294
+ current = current . host ;
295
+ }
296
+ else {
297
+ return false ;
298
+ }
299
+ }
300
+ return false ;
301
+ }
279
302
function validateTarget ( target ) {
280
303
// Get the document and HTMLElement corresponding to the target to allow mounting in iframes
281
304
const document = target && target . ownerDocument ;
282
305
if ( document ) {
306
+ if ( ! document . defaultView ) {
307
+ throw new OwlError ( "Cannot mount a component: the target document is not attached to a window (defaultView is missing)" ) ;
308
+ }
283
309
const HTMLElement = document . defaultView . HTMLElement ;
284
310
if ( target instanceof HTMLElement || target instanceof ShadowRoot ) {
285
- if ( ! document . body . contains ( target instanceof HTMLElement ? target : target . host ) ) {
311
+ if ( ! isAttachedToDocument ( target , document ) ) {
286
312
throw new OwlError ( "Cannot mount a component on a detached dom node" ) ;
287
313
}
288
314
return ;
@@ -319,12 +345,40 @@ async function loadFile(url) {
319
345
*/
320
346
class Markup extends String {
321
347
}
322
- /*
323
- * Marks a value as safe, that is, a value that can be injected as HTML directly.
324
- * It should be used to wrap the value passed to a t-out directive to allow a raw rendering.
325
- */
326
- function markup ( value ) {
327
- return new Markup ( value ) ;
348
+ function htmlEscape ( str ) {
349
+ if ( str instanceof Markup ) {
350
+ return str ;
351
+ }
352
+ if ( str === undefined ) {
353
+ return markup ( "" ) ;
354
+ }
355
+ if ( typeof str === "number" ) {
356
+ return markup ( String ( str ) ) ;
357
+ }
358
+ [
359
+ [ "&" , "&" ] ,
360
+ [ "<" , "<" ] ,
361
+ [ ">" , ">" ] ,
362
+ [ "'" , "'" ] ,
363
+ [ '"' , """ ] ,
364
+ [ "`" , "`" ] ,
365
+ ] . forEach ( ( pairs ) => {
366
+ str = String ( str ) . replace ( new RegExp ( pairs [ 0 ] , "g" ) , pairs [ 1 ] ) ;
367
+ } ) ;
368
+ return markup ( str ) ;
369
+ }
370
+ function markup ( valueOrStrings , ...placeholders ) {
371
+ if ( ! Array . isArray ( valueOrStrings ) ) {
372
+ return new Markup ( valueOrStrings ) ;
373
+ }
374
+ const strings = valueOrStrings ;
375
+ let acc = "" ;
376
+ let i = 0 ;
377
+ for ( ; i < placeholders . length ; ++ i ) {
378
+ acc += strings [ i ] + htmlEscape ( placeholders [ i ] ) ;
379
+ }
380
+ acc += strings [ i ] ;
381
+ return new Markup ( acc ) ;
328
382
}
329
383
330
384
function createEventHandler ( rawEvent ) {
@@ -5704,7 +5758,7 @@ function compile(template, options = {
5704
5758
}
5705
5759
5706
5760
// do not modify manually. This file is generated by the release script.
5707
- const version = "2.6.1 " ;
5761
+ const version = "2.7.0 " ;
5708
5762
5709
5763
// -----------------------------------------------------------------------------
5710
5764
// Scheduler
@@ -6172,9 +6226,9 @@ TemplateSet.prototype._compileTemplate = function _compileTemplate(name, templat
6172
6226
} ) ;
6173
6227
} ;
6174
6228
6175
- export { App , Component , EventBus , OwlError , __info__ , batched , blockDom , loadFile , markRaw , markup , mount , onError , onMounted , onPatched , onRendered , onWillDestroy , onWillPatch , onWillRender , onWillStart , onWillUnmount , onWillUpdateProps , reactive , status , toRaw , useChildSubEnv , useComponent , useEffect , useEnv , useExternalListener , useRef , useState , useSubEnv , validate , validateType , whenReady , xml } ;
6229
+ export { App , Component , EventBus , OwlError , __info__ , batched , blockDom , htmlEscape , loadFile , markRaw , markup , mount , onError , onMounted , onPatched , onRendered , onWillDestroy , onWillPatch , onWillRender , onWillStart , onWillUnmount , onWillUpdateProps , reactive , status , toRaw , useChildSubEnv , useComponent , useEffect , useEnv , useExternalListener , useRef , useState , useSubEnv , validate , validateType , whenReady , xml } ;
6176
6230
6177
6231
6178
- __info__ . date = '2025-03-05T08:37:58.580Z ' ;
6179
- __info__ . hash = '2b5cea9 ' ;
6232
+ __info__ . date = '2025-03-26T12:58:40.935Z ' ;
6233
+ __info__ . hash = 'e788e36 ' ;
6180
6234
__info__ . url = 'https://github.com/odoo/owl' ;
0 commit comments