-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
AG-35842 Add new scriptlet — 'prevent-canvas'. #451
Squashed commit of the following: commit 4f1f97e Author: Adam Wróblewski <adam@adguard.com> Date: Fri Sep 27 10:19:05 2024 +0200 Remove unnecessary line commit 9486855 Author: Adam Wróblewski <adam@adguard.com> Date: Fri Sep 27 09:14:37 2024 +0200 Add prevent-canvas to compatibility-table.json commit a935c1e Merge: 6ccf2cb 1838d7b Author: Adam Wróblewski <adam@adguard.com> Date: Thu Sep 26 09:38:52 2024 +0200 Merge branch 'master' into feature/AG-35842 commit 6ccf2cb Author: Adam Wróblewski <adam@adguard.com> Date: Thu Sep 26 09:36:41 2024 +0200 Fix description commit 84e27b1 Author: Adam Wróblewski <adam@adguard.com> Date: Thu Sep 26 09:32:43 2024 +0200 Add types commit 7966487 Author: Adam Wróblewski <adam@adguard.com> Date: Wed Sep 25 14:20:34 2024 +0200 Add prevent-canvas scriptlet
- Loading branch information
Showing
6 changed files
with
222 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -352,6 +352,7 @@ | |
"ubo": "multiup.js" | ||
}, | ||
{ | ||
"adg": "prevent-canvas", | ||
"ubo": "prevent-canvas.js" | ||
}, | ||
{ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import { | ||
hit, | ||
logMessage, | ||
parseMatchArg, | ||
isValidMatchStr, | ||
// following helpers are needed for helpers above | ||
toRegExp, | ||
escapeRegExp, | ||
isValidStrPattern, | ||
} from '../helpers'; | ||
|
||
/** | ||
* @scriptlet prevent-canvas | ||
* | ||
* @description | ||
* Prevents calls to `HTMLCanvasElement.prototype.getContext` and returns `null`. | ||
* | ||
* Related UBO scriptlet: | ||
* https://github.com/gorhill/uBlock/wiki/Resources-Library#prevent-canvasjs- | ||
* | ||
* ### Syntax | ||
* | ||
* ```adblock | ||
* example.org#%#//scriptlet('prevent-canvas'[, contextType]) | ||
* ``` | ||
* | ||
* - `contextType` — optional, string matching the context type (e.g., '2d', 'webgl'); | ||
* by default it matches all context types. | ||
* It can be a string pattern or a regular expression pattern. | ||
* If the pattern starts with `!`, it will be negated. | ||
* | ||
* ### Examples | ||
* | ||
* 1. Prevent all canvas contexts | ||
* | ||
* ```adblock | ||
* example.org#%#//scriptlet('prevent-canvas') | ||
* ``` | ||
* | ||
* 1. Prevent only '2d' canvas contexts | ||
* | ||
* ```adblock | ||
* example.org#%#//scriptlet('prevent-canvas', '2d') | ||
* ``` | ||
* | ||
* 1. Prevent all canvas contexts except '2d' | ||
* | ||
* ```adblock | ||
* example.org#%#//scriptlet('prevent-canvas', '!2d') | ||
* ``` | ||
* | ||
* @added unknown. | ||
*/ | ||
export function preventCanvas(source: Source, contextType?: string) { | ||
const handlerWrapper = ( | ||
target: HTMLCanvasElement['getContext'], | ||
thisArg: HTMLCanvasElement, | ||
argumentsList: string[], | ||
) => { | ||
const type = argumentsList[0]; | ||
let shouldPrevent = false; | ||
if (!contextType) { | ||
shouldPrevent = true; | ||
} else if (isValidMatchStr(contextType)) { | ||
const { isInvertedMatch, matchRegexp } = parseMatchArg(contextType); | ||
shouldPrevent = matchRegexp.test(type) !== isInvertedMatch; | ||
} else { | ||
logMessage(source, `Invalid contextType parameter: ${contextType}`); | ||
shouldPrevent = false; | ||
} | ||
if (shouldPrevent) { | ||
hit(source); | ||
return null; | ||
} | ||
return Reflect.apply(target, thisArg, argumentsList); | ||
}; | ||
|
||
const canvasHandler = { | ||
apply: handlerWrapper, | ||
}; | ||
|
||
window.HTMLCanvasElement.prototype.getContext = new Proxy( | ||
window.HTMLCanvasElement.prototype.getContext, | ||
canvasHandler, | ||
); | ||
} | ||
|
||
preventCanvas.names = [ | ||
'prevent-canvas', | ||
// aliases are needed for matching the related scriptlet converted into our syntax | ||
'prevent-canvas.js', | ||
'ubo-prevent-canvas.js', | ||
'ubo-prevent-canvas', | ||
]; | ||
|
||
preventCanvas.injections = [ | ||
hit, | ||
logMessage, | ||
parseMatchArg, | ||
isValidMatchStr, | ||
toRegExp, | ||
escapeRegExp, | ||
isValidStrPattern, | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
/* eslint-disable no-underscore-dangle */ | ||
import { runScriptlet, clearGlobalProps } from '../helpers'; | ||
|
||
const { test, module } = QUnit; | ||
const name = 'prevent-canvas'; | ||
|
||
const nativeCanvas = window.HTMLCanvasElement.prototype.getContext; | ||
|
||
const beforeEach = () => { | ||
window.__debug = () => { | ||
window.hit = 'FIRED'; | ||
}; | ||
}; | ||
|
||
const afterEach = () => { | ||
window.HTMLCanvasElement.prototype.getContext = nativeCanvas; | ||
clearGlobalProps('hit', '__debug'); | ||
}; | ||
|
||
module(name, { beforeEach, afterEach }); | ||
|
||
test('Checking if alias name works', (assert) => { | ||
const adgParams = { | ||
name, | ||
engine: 'test', | ||
verbose: true, | ||
}; | ||
const uboParams = { | ||
name: 'ubo-prevent-canvas.js', | ||
engine: 'test', | ||
verbose: true, | ||
}; | ||
|
||
const codeByAdgParams = window.scriptlets.invoke(adgParams); | ||
const codeByUboParams = window.scriptlets.invoke(uboParams); | ||
|
||
assert.strictEqual(codeByAdgParams, codeByUboParams, 'ubo name - ok'); | ||
}); | ||
|
||
test('should return null for any context type when no contextType is specified', (assert) => { | ||
runScriptlet(name); | ||
const canvas = document.createElement('canvas'); | ||
const context = canvas.getContext('2d'); | ||
assert.strictEqual(context, null); | ||
assert.strictEqual(window.hit, 'FIRED', 'hit fired'); | ||
}); | ||
|
||
test('should return null for specified context type', (assert) => { | ||
runScriptlet(name, ['2d']); | ||
const canvas = document.createElement('canvas'); | ||
const context = canvas.getContext('2d'); | ||
assert.strictEqual(context, null); | ||
assert.strictEqual(window.hit, 'FIRED', 'hit fired'); | ||
}); | ||
|
||
test('should return original context for non-matching context type', (assert) => { | ||
runScriptlet(name, ['webgl']); | ||
const canvas = document.createElement('canvas'); | ||
const context = canvas.getContext('2d'); | ||
assert.ok(context.direction); | ||
}); | ||
|
||
test('should return null for context type matched by regexp', (assert) => { | ||
const regexp = /2d|webgl/; | ||
runScriptlet(name, [`${regexp}`]); | ||
|
||
const canvas2d = document.createElement('canvas'); | ||
const context2d = canvas2d.getContext('2d'); | ||
|
||
const canvasWebgl = document.createElement('canvas'); | ||
const contextWebgl = canvasWebgl.getContext('webgl'); | ||
|
||
assert.strictEqual(context2d, null); | ||
assert.strictEqual(contextWebgl, null); | ||
assert.strictEqual(window.hit, 'FIRED', 'hit fired'); | ||
}); | ||
|
||
test('should return original context for negated context type', (assert) => { | ||
const negated = '!2d'; | ||
runScriptlet(name, [negated]); | ||
|
||
const canvas2d = document.createElement('canvas'); | ||
const context2d = canvas2d.getContext('2d'); | ||
|
||
const canvasWebgl = document.createElement('canvas'); | ||
const contextWebgl = canvasWebgl.getContext('webgl'); | ||
|
||
assert.ok(context2d.direction); | ||
assert.strictEqual(contextWebgl, null); | ||
assert.strictEqual(window.hit, 'FIRED', 'hit fired'); | ||
}); | ||
|
||
test('should return original context for negated context type regexp', (assert) => { | ||
const negated = '!/2d/'; | ||
runScriptlet(name, [negated]); | ||
|
||
const canvas2d = document.createElement('canvas'); | ||
const context2d = canvas2d.getContext('2d'); | ||
|
||
const canvasWebgl = document.createElement('canvas'); | ||
const contextWebgl = canvasWebgl.getContext('webgl'); | ||
|
||
assert.ok(context2d.direction); | ||
assert.strictEqual(contextWebgl, null); | ||
assert.strictEqual(window.hit, 'FIRED', 'hit fired'); | ||
}); |