Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into feature/AG-18849
- Loading branch information
Showing
7 changed files
with
179 additions
and
2 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
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 |
---|---|---|
|
@@ -201,6 +201,7 @@ | |
"ubo": "href-sanitizer.js" | ||
}, | ||
{ | ||
"adg": "call-nothrow", | ||
"ubo": "call-nothrow.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,102 @@ | ||
import { | ||
hit, | ||
getPropertyInChain, | ||
logMessage, | ||
// following helpers are needed for helpers above | ||
isEmptyObject, | ||
} from '../helpers/index'; | ||
|
||
/* eslint-disable max-len */ | ||
/** | ||
* @scriptlet call-nothrow | ||
* | ||
* @description | ||
* Prevents an exception from being thrown and returns undefined when a specific function is called. | ||
* | ||
* Related UBO scriptlet: | ||
* https://github.com/gorhill/uBlock/wiki/Resources-Library#call-nothrowjs- | ||
* | ||
* ### Syntax | ||
* | ||
* ```text | ||
* example.org#%#//scriptlet('call-nothrow', functionName) | ||
* ``` | ||
* | ||
* - `functionName` — required, the name of the function to trap | ||
* | ||
* ### Examples | ||
* | ||
* 1. Prevents an exception from being thrown when `Object.defineProperty` is called: | ||
* | ||
* ```adblock | ||
* example.org#%#//scriptlet('call-nothrow', 'Object.defineProperty') | ||
* ``` | ||
* | ||
* For instance, the following call normally throws an error, but the scriptlet catches it and returns undefined: | ||
* | ||
* ```javascript | ||
* Object.defineProperty(window, 'foo', { value: true }); | ||
* Object.defineProperty(window, 'foo', { value: false }); | ||
* ``` | ||
* | ||
* 2. Prevents an exception from being thrown when `JSON.parse` is called: | ||
* | ||
* ```adblock | ||
* example.org#%#//scriptlet('call-nothrow', 'JSON.parse') | ||
* ``` | ||
* | ||
* For instance, the following call normally throws an error, but the scriptlet catches it and returns undefined: | ||
* | ||
* ```javascript | ||
* JSON.parse('foo'); | ||
* ``` | ||
* | ||
* @added unknown. | ||
*/ | ||
/* eslint-enable max-len */ | ||
export function callNoThrow(source, functionName) { | ||
if (!functionName) { | ||
return; | ||
} | ||
|
||
const { base, prop } = getPropertyInChain(window, functionName); | ||
if (!base || !prop || typeof base[prop] !== 'function') { | ||
const message = `${functionName} is not a function`; | ||
logMessage(source, message); | ||
return; | ||
} | ||
|
||
const objectWrapper = (...args) => { | ||
let result; | ||
try { | ||
result = Reflect.apply(...args); | ||
} catch (e) { | ||
const message = `Error calling ${functionName}: ${e.message}`; | ||
logMessage(source, message); | ||
} | ||
hit(source); | ||
return result; | ||
}; | ||
|
||
const objectHandler = { | ||
apply: objectWrapper, | ||
}; | ||
|
||
base[prop] = new Proxy(base[prop], objectHandler); | ||
} | ||
|
||
callNoThrow.names = [ | ||
'call-nothrow', | ||
// aliases are needed for matching the related scriptlet converted into our syntax | ||
'call-nothrow.js', | ||
'ubo-call-nothrow.js', | ||
'ubo-call-nothrow', | ||
]; | ||
|
||
callNoThrow.injections = [ | ||
hit, | ||
getPropertyInChain, | ||
logMessage, | ||
// following helpers are needed for helpers above | ||
isEmptyObject, | ||
]; |
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,72 @@ | ||
/* eslint-disable no-underscore-dangle, no-console */ | ||
import { runScriptlet, clearGlobalProps } from '../helpers'; | ||
|
||
const { test, module } = QUnit; | ||
const name = 'call-nothrow'; | ||
|
||
const beforeEach = () => { | ||
window.__debug = () => { | ||
window.hit = 'FIRED'; | ||
}; | ||
}; | ||
|
||
const afterEach = () => { | ||
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-call-nothrow.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('call-nothrow - JSON.parse', (assert) => { | ||
let testPassed; | ||
|
||
runScriptlet(name, ['JSON.parse']); | ||
|
||
// JSON.parse('foo') throws an error, | ||
// so scriptlet should catch it and testPassed should be true | ||
try { | ||
JSON.parse('foo'); | ||
testPassed = true; | ||
} catch (e) { | ||
testPassed = false; | ||
} | ||
assert.strictEqual(testPassed, true, 'testPassed set to true'); | ||
assert.strictEqual(window.hit, 'FIRED', 'hit function fired'); | ||
}); | ||
|
||
test('call-nothrow - Object.defineProperty', (assert) => { | ||
let testPassed; | ||
const foo = {}; | ||
Object.defineProperty(foo, 'bar', { value: true }); | ||
|
||
runScriptlet(name, ['Object.defineProperty']); | ||
|
||
// Redefining foo.bar should throw an error, | ||
// so scriptlet should catch it and testPassed should be true | ||
try { | ||
Object.defineProperty(foo, 'bar', { value: false }); | ||
testPassed = true; | ||
} catch (e) { | ||
testPassed = false; | ||
} | ||
assert.strictEqual(testPassed, true, 'testPassed set to true'); | ||
assert.strictEqual(foo.bar, true, 'foo.bar set to true'); | ||
assert.strictEqual(window.hit, 'FIRED', 'hit function fired'); | ||
}); |
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