Skip to content

Commit 20dd606

Browse files
committed
Add trusted-create-element scriptlet
As discussed with filter list maintainers. * @Scriptlet trusted-create-element * * @description * Element(s) from a parsed HTML string are added as child element(s) to a * specific parent element in the DOM. * * @param parent * A CSS selector identifying the element to which created element(s) will be * added. * * @param html * An HTML string to be parsed using DOMParser, and which resulting elements * are to be added as child element(s). * * @param duration * Optional. If specified, the time in ms after which the added elements will * be removed. No removal will occur if not specified.
1 parent a06f09a commit 20dd606

File tree

2 files changed

+113
-0
lines changed

2 files changed

+113
-0
lines changed

src/js/resources/create-element.js

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/*******************************************************************************
2+
3+
uBlock Origin - a comprehensive, efficient content blocker
4+
Copyright (C) 2025-present Raymond Hill
5+
6+
This program is free software: you can redistribute it and/or modify
7+
it under the terms of the GNU General Public License as published by
8+
the Free Software Foundation, either version 3 of the License, or
9+
(at your option) any later version.
10+
11+
This program is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
GNU General Public License for more details.
15+
16+
You should have received a copy of the GNU General Public License
17+
along with this program. If not, see {http://www.gnu.org/licenses/}.
18+
19+
Home: https://github.com/gorhill/uBlock
20+
21+
*/
22+
23+
import { registerScriptlet } from './base.js';
24+
import { safeSelf } from './safe-self.js';
25+
26+
/******************************************************************************/
27+
28+
/**
29+
* @scriptlet trusted-create-element
30+
*
31+
* @description
32+
* Element(s) from a parsed HTML string are added as child element(s) to a
33+
* specific parent element in the DOM.
34+
*
35+
* @param parent
36+
* A CSS selector identifying the element to which created element(s) will be
37+
* added.
38+
*
39+
* @param html
40+
* An HTML string to be parsed using DOMParser, and which resulting elements
41+
* are to be added as child element(s).
42+
*
43+
* @param duration
44+
* Optional. If specified, the time in ms after which the added elements will
45+
* be removed. No removal will occur if not specified.
46+
*
47+
* */
48+
49+
function trustedCreateElement(
50+
parentSelector,
51+
htmlStr = '',
52+
durationStr = ''
53+
) {
54+
if ( parentSelector === '' ) { return; }
55+
if ( htmlStr === '' ) { return; }
56+
const safe = safeSelf();
57+
const logPrefix = safe.makeLogPrefix('trusted-create-element', parentSelector, htmlStr, durationStr);
58+
// We do not want to recursively create elements
59+
self.trustedCreateElement = true;
60+
let ancestor = self.frameElement;
61+
while ( ancestor !== null ) {
62+
const doc = ancestor.ownerDocument;
63+
if ( doc === null ) { break; }
64+
const win = doc.defaultView;
65+
if ( win === null ) { break; }
66+
if ( win.trustedCreateElement ) { return; }
67+
ancestor = ancestor.frameElement;
68+
}
69+
const duration = parseInt(durationStr, 10);
70+
const domParser = new DOMParser();
71+
const externalDoc = domParser.parseFromString(htmlStr, 'text/html');
72+
const docFragment = new DocumentFragment();
73+
const toRemove = [];
74+
for ( const external of externalDoc.querySelectorAll('body > *') ) {
75+
const imported = document.adoptNode(external);
76+
docFragment.append(imported);
77+
if ( isNaN(duration) ) { continue; }
78+
toRemove.push(imported);
79+
}
80+
if ( docFragment.childElementCount === 0 ) { return; }
81+
const remove = ( ) => {
82+
for ( const elem of toRemove ) {
83+
elem.remove();
84+
}
85+
safe.uboLog(logPrefix, 'Element(s) removed');
86+
};
87+
const append = ( ) => {
88+
const parent = document.querySelector(parentSelector);
89+
if ( parent === null ) { return false; }
90+
parent.append(docFragment);
91+
safe.uboLog(logPrefix, 'Element(s) appended');
92+
if ( toRemove.length === 0 ) { return true; }
93+
setTimeout(remove, duration);
94+
return true;
95+
};
96+
if ( append() ) { return; }
97+
const observer = new MutationObserver(( ) => {
98+
if ( append() === false ) { return; }
99+
observer.disconnect();
100+
});
101+
observer.observe(document, { childList: true, subtree: true });
102+
}
103+
registerScriptlet(trustedCreateElement, {
104+
name: 'trusted-create-element.js',
105+
requiresTrust: true,
106+
dependencies: [
107+
safeSelf,
108+
],
109+
world: 'ISOLATED',
110+
});
111+
112+
/******************************************************************************/

src/js/resources/scriptlets.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
*/
2222

2323
import './attribute.js';
24+
import './create-element.js';
2425
import './href-sanitizer.js';
2526
import './json-edit.js';
2627
import './json-prune.js';

0 commit comments

Comments
 (0)