Skip to content

Commit

Permalink
AG-27676 Add trusted-dispatch-event scriptlet. #382
Browse files Browse the repository at this point in the history
Squashed commit of the following:

commit f174452
Author: Adam Wróblewski <adam@adguard.com>
Date:   Fri Apr 12 10:52:11 2024 +0200

    Refactor trustedDispatchEvent and rename variables

commit 88a5dd8
Author: Slava Leleka <v.leleka@adguard.com>
Date:   Thu Apr 11 18:26:17 2024 +0300

    Update description

commit b8cf974
Author: Adam Wróblewski <adam@adguard.com>
Date:   Thu Apr 11 17:20:03 2024 +0200

    Update description

commit 3736c8d
Author: Adam Wróblewski <adam@adguard.com>
Date:   Thu Apr 11 16:32:35 2024 +0200

    Rename `element` to `target`

commit c6eee57
Author: Adam Wróblewski <adam@adguard.com>
Date:   Thu Apr 11 11:13:55 2024 +0200

    Use common event types in examples

commit edc0b49
Author: Slava Leleka <v.leleka@adguard.com>
Date:   Thu Apr 11 12:00:35 2024 +0300

    Update docs

commit cff3174
Author: Slava Leleka <v.leleka@adguard.com>
Date:   Thu Apr 11 12:00:01 2024 +0300

    Update changelog

commit c95fd26
Merge: 1f62260 62144fc
Author: Adam Wróblewski <adam@adguard.com>
Date:   Tue Apr 9 10:20:33 2024 +0200

    Merge branch 'master' into feature/AG-27676

commit 1f62260
Author: Adam Wróblewski <adam@adguard.com>
Date:   Fri Apr 5 13:36:03 2024 +0200

    Change to trusted scriptlet

commit 9382072
Author: Adam Wróblewski <adam@adguard.com>
Date:   Fri Apr 5 13:27:39 2024 +0200

    Add ability to dispatch events on the window object

commit 8007b06
Author: Adam Wróblewski <adam@adguard.com>
Date:   Thu Apr 4 16:07:47 2024 +0200

    Add dispatch-event scriptlet
  • Loading branch information
AdamWr committed Apr 12, 2024
1 parent 62144fc commit 1cd162b
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 2 deletions.
6 changes: 4 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,17 @@ The format is based on [Keep a Changelog], and this project adheres to [Semantic
<!-- TODO: change `@added unknown` tag due to the actual version -->
<!-- during new scriptlets or redirects releasing -->

## Unreleased
## [Unreleased]

### Added

- `isRedirectResourceCompatibleWithAdg()` method to check compatibility of redirect resources with AdGuard
without needing the full rule text [#420]
- `trusted-dispatch-event` scriptlet [#382]

[Unreleased]: https://github.com/AdguardTeam/Scriptlets/compare/v1.10.25...HEAD
[#420]: https://github.com/AdguardTeam/Scriptlets/issues/420

[#382]: https://github.com/AdguardTeam/Scriptlets/issues/382

## [v1.10.25] - 2024-03-28

Expand Down
1 change: 1 addition & 0 deletions src/scriptlets/scriptlets-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export * from './json-prune-fetch-response';
export * from './no-protected-audience';
export * from './trusted-suppress-native-method';
export * from './json-prune-xhr-response';
export * from './trusted-dispatch-event';
// redirects as scriptlets
// https://github.com/AdguardTeam/Scriptlets/issues/300
export * from './amazon-apstag';
Expand Down
105 changes: 105 additions & 0 deletions src/scriptlets/trusted-dispatch-event.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import {
hit,
} from '../helpers/index';

/**
* @trustedScriptlet trusted-dispatch-event
*
* @description
* Dispatches a custom event on a specified target.
*
* ### Syntax
* ```text
* example.org#%#//scriptlet('trusted-dispatch-event', event[, target])
* ```
*
* - `event` — required, name of the event to dispatch
* - `target` — optional, target on which event will be invoked. Possible values:
* - CSS selector — dispatch event on the element with the specified selector
* - `window` — dispatch event on the window object
* - if not set, then "document" is used — it's default value
*
* ### Examples
*
* 1. Dispatches a custom event "click" on the document.
*
* ```adblock
* example.org#%#//scriptlet('trusted-dispatch-event', 'click')
* ```
*
* 2. Dispatches a custom event "submit" on the element with the class "test".
*
* ```adblock
* example.org#%#//scriptlet('trusted-dispatch-event', 'submit', '.test')
* ```
*
* 3. Dispatches a custom event "load" on the window object.
*
* ```adblock
* example.org#%#//scriptlet('trusted-dispatch-event', 'load', 'window')
* ```
*
* @added unknown.
*/

export function trustedDispatchEvent(
source: Source,
event: string,
target: string,
) {
if (!event) {
return;
}

let hasBeenDispatched = false;

let eventTarget: typeof window | Document | Element | null = document;
if (target === 'window') {
eventTarget = window;
}

const events = new Set<string>();

const dispatch = () => {
const customEvent = new Event(event);

if (typeof target === 'string' && target !== 'window') {
eventTarget = document.querySelector(target);
}

const isEventAdded = events.has(event);
if (!hasBeenDispatched && isEventAdded && eventTarget) {
hasBeenDispatched = true;
hit(source);
eventTarget.dispatchEvent(customEvent);
}
};

const wrapper = (
eventListener: typeof EventTarget.prototype.addEventListener,
thisArg: Element,
args: string[],
) => {
const eventName = args[0];
if (thisArg && eventName) {
events.add(eventName);
setTimeout(() => {
dispatch();
}, 1);
}
return Reflect.apply(eventListener, thisArg, args);
};

const handler = {
apply: wrapper,
};
EventTarget.prototype.addEventListener = new Proxy(EventTarget.prototype.addEventListener, handler);
}

trustedDispatchEvent.names = [
'trusted-dispatch-event',
];

trustedDispatchEvent.injections = [
hit,
];
103 changes: 103 additions & 0 deletions tests/scriptlets/trusted-dispatch-event.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/* eslint-disable no-underscore-dangle, no-console */
import { runScriptlet, clearGlobalProps } from '../helpers';

const { test, module } = QUnit;
const name = 'trusted-dispatch-event';

const createElem = () => {
const div = document.createElement('div');
div.setAttribute('id', 'testElem');

document.body.appendChild(div);
return div;
};

const removeElem = () => {
const elem = document.getElementById('testElem');
if (elem) {
elem.remove();
}
};

const beforeEach = () => {
window.__debug = () => {
window.hit = 'FIRED';
};
};

const afterEach = () => {
clearGlobalProps('hit', '__debug');
removeElem();
};

module(name, { beforeEach, afterEach });

test('Dispatch event - document', (assert) => {
const event = 'testEvent1';

let eventFired = false;

const scriptletArgs = [event];
runScriptlet(name, scriptletArgs);

assert.strictEqual(eventFired, false, 'Event not fired yet');

document.addEventListener('testEvent1', () => {
eventFired = true;
});

const done = assert.async();
setTimeout(() => {
assert.strictEqual(eventFired, true, 'Event fired');
assert.strictEqual(window.hit, 'FIRED');
done();
}, 10);
});

test('Dispatch event - specific element', (assert) => {
const event = 'testEvent2';
const selector = '#testElem';

let eventFired = false;

const scriptletArgs = [event, selector];
runScriptlet(name, scriptletArgs);

const elem = createElem();

assert.strictEqual(eventFired, false, 'Event not fired yet');

elem.addEventListener('testEvent2', () => {
eventFired = true;
});

const done = assert.async();
setTimeout(() => {
assert.strictEqual(eventFired, true, 'Event fired');
assert.strictEqual(window.hit, 'FIRED');
done();
}, 10);
});

test('Dispatch event - window object', (assert) => {
const event = 'windowObj';
const selector = 'window';

let eventFired = false;

const scriptletArgs = [event, selector];
runScriptlet(name, scriptletArgs);

assert.strictEqual(eventFired, false, 'Event not fired yet');

window.addEventListener('windowObj', () => {
eventFired = true;
});

const done = assert.async();
setTimeout(() => {
assert.strictEqual(eventFired, true, 'Event fired');
assert.strictEqual(window.hit, 'FIRED');
done();
}, 10);
});

0 comments on commit 1cd162b

Please sign in to comment.