From 26529a3653eb2b784c0d329384e8919c35ab882f Mon Sep 17 00:00:00 2001 From: kkapsner Date: Fri, 13 Jul 2018 16:58:13 +0200 Subject: [PATCH] Hide function replacement Fixes #206 --- lib/intercept.js | 228 ++++++++++++++-------------- lib/modifiedAPI.js | 295 ++++++++++++++++++++---------------- lib/modifiedAPIFunctions.js | 32 ++++ lib/modifiedAudioAPI.js | 98 +++++++----- manifest.json | 1 + releaseNotes.txt | 2 +- 6 files changed, 375 insertions(+), 281 deletions(-) create mode 100644 lib/modifiedAPIFunctions.js diff --git a/lib/intercept.js b/lib/intercept.js index f334e4a..64d26c3 100644 --- a/lib/intercept.js +++ b/lib/intercept.js @@ -158,9 +158,107 @@ }); } }; - + let extensionID = browser.extension.getURL(""); scope.intercept = function intercept({subject: window}, {check, checkStack, ask, notify, prefs}){ + function getDataURL(object, prefs){ + return ( + this && + prefs("storeImageForInspection") && + prefs("showNotifications")? + ( + this instanceof HTMLCanvasElement? + this.toDataURL(): + ( + this.canvas instanceof HTMLCanvasElement? + this.canvas.toDataURL(): + false + ) + ): + false + ); + } + function generateChecker(name, changedFunction, siteStatus, original){ + return function checker(callingDepth = 2){ + var url = getURL(window); + if (!url){ + return undef; + } + var error = new Error(); + try { + // return original if the extension itself requested the function + if ( + error.stack + .split("\n", callingDepth + 2)[callingDepth + 1] + .split("@", callingDepth + 1)[1] + .startsWith(extensionID) + ){ + return {allow: true, original, window}; + } + } + catch (e) { + // stack had an unknown form + } + if (checkStack(error.stack)){ + return {allow: true, original, window}; + } + var funcStatus = changedFunction.getStatus(this, siteStatus); + + function notifyCallback(messageId){ + notify({ + url, + errorStack: error.stack, + messageId, + timestamp: new Date(), + functionName: name, + api: changedFunction.api, + dataURL: getDataURL(this, prefs) + }); + } + + if (funcStatus.active && !prefs("apiWhiteList")[name]){ + if (funcStatus.mode === "ask"){ + funcStatus.mode = ask({ + window: window, + type: changedFunction.type, + api: changedFunction.api, + canvas: this instanceof HTMLCanvasElement? + this: + ( + this && + (this.canvas instanceof HTMLCanvasElement)? + this.canvas: + false + ), + errorStack: error.stack + }); + } + switch (funcStatus.mode){ + case "allow": + return {allow: true, original, window}; + case "fake": + setRandomSupplyByType(prefs("rng")); + return { + allow: "fake", + prefs, + notify: notifyCallback, + window, + original + }; + //case "block": + default: + return { + allow: false, + notify: notifyCallback + }; + } + } + else { + return {allow: true, original, window}; + } + }; + } + var siteStatus = check({url: getURL(window)}); logging.verbose("status for page", window, siteStatus); if (siteStatus.mode !== "allow"){ @@ -177,117 +275,25 @@ var constructor = getWrapped(window)[object]; if (constructor){ var original = constructor.prototype[name]; - - Object.defineProperty( - constructor.prototype, - name, - { - enumerable: true, - configureable: true, - get: exportFunction(function(){ - var url = getURL(window); - if (!url){ - return undef; - } - var error = new Error(); - try { - // return original if the extension itself requested the function - if (error.stack.split("\n", 3)[1].split("@", 2)[1].startsWith(extensionID)){ - return original; - } - } - catch (e) { - // stack had an unknown form - } - if (checkStack(error.stack)){ - return original; - } - var funcStatus = changedFunction.getStatus(this, siteStatus); - - function notifyCallback(messageId){ - notify({ - url, - errorStack: error.stack, - messageId, - timestamp: new Date(), - functionName: name, - api: changedFunction.api, - dataURL: - this && - prefs("storeImageForInspection") && - prefs("showNotifications")? - ( - this instanceof HTMLCanvasElement? - this.toDataURL(): - ( - this.canvas instanceof HTMLCanvasElement? - this.canvas.toDataURL(): - false - ) - ): - false - }); - } - - if (funcStatus.active && !prefs("apiWhiteList")[name]){ - if (funcStatus.mode === "ask"){ - funcStatus.mode = ask({ - window: window, - type: changedFunction.type, - api: changedFunction.api, - canvas: this instanceof HTMLCanvasElement? - this: - ( - this && - (this.canvas instanceof HTMLCanvasElement)? - this.canvas: - false - ), - errorStack: error.stack - }); - } - switch (funcStatus.mode){ - case "allow": - return original; - case "fake": - setRandomSupplyByType(prefs("rng")); - var fake = changedFunction.fakeGenerator( - prefs, - notifyCallback, - window, - original - ); - switch (fake){ - case true: - return original; - case false: - return undef; - default: - return exportFunction(fake, getWrapped(window)); - } - //case "block": - default: - return undef; - } - } - else { - return original; - } - }, window), - set: exportFunction(function(value){ - Object.defineProperty( - constructor.prototype, - name, - { - value, - writable: true, - configurable: true, - enumerable: true - } - ); - }, window) + const checker = generateChecker(name, changedFunction, siteStatus, original); + var descriptor = Object.getOwnPropertyDescriptor(constructor.prototype, name); + if (descriptor.hasOwnProperty("value")){ + if (changedFunction.fakeGenerator){ + descriptor.value = exportFunction( + changedFunction.fakeGenerator(checker), + window + ); } - ); + else { + descriptor.value = null; + } + } + else { + descriptor.get = exportFunction(function(){ + return changedFunction.fakeGenerator(checker); + }, window); + } + Object.defineProperty(constructor.prototype, name, descriptor); } }); } diff --git a/lib/modifiedAPI.js b/lib/modifiedAPI.js index e7aba6f..c7c4a22 100644 --- a/lib/modifiedAPI.js +++ b/lib/modifiedAPI.js @@ -17,6 +17,7 @@ const logging = require("./logging"); const {copyCanvasToWebgl} = require("./webgl"); const getWrapped = require("sdk/getWrapped"); + const {hasType, checkerWrapper} = require("./modifiedAPIFunctions"); const modifiedAudioAPI = require("./modifiedAudioAPI"); var randomSupply = null; @@ -151,10 +152,6 @@ } } - function hasType(status, type){ - return status.type.indexOf(type) !== -1; - } - scope.setRandomSupply = function(supply){ randomSupply = supply; modifiedAudioAPI.setRandomSupply(supply); @@ -186,10 +183,13 @@ } }, object: "HTMLCanvasElement", - fakeGenerator: function(prefs, notify, window, original){ + fakeGenerator: function(checker){ return function(context, contextAttributes){ - canvasContextType.set(this, context); - return original.apply(this, window.Array.from(arguments)); + return checkerWrapper(checker, this, arguments, function(args, check){ + var {prefs, notify, window, original} = check; + canvasContextType.set(this, context); + return original.apply(this, window.Array.from(args)); + }); }; } }, @@ -205,18 +205,21 @@ return status; }, object: "HTMLCanvasElement", - fakeGenerator: function(prefs, notify, window, original){ + fakeGenerator: function(checker){ return function toDataURL(){ - if (canvasSizeShouldBeFaked(this, prefs)){ - var fakeCanvas = getFakeCanvas(window, this, prefs); - if (fakeCanvas !== this){ - notify.call(this, "fakedReadout"); + return checkerWrapper(checker, this, arguments, function(args, check){ + var {prefs, notify, window, original} = check; + if (canvasSizeShouldBeFaked(this, prefs)){ + var fakeCanvas = getFakeCanvas(window, this, prefs); + if (fakeCanvas !== this){ + notify.call(this, "fakedReadout"); + } + return original.apply(fakeCanvas, window.Array.from(args)); } - return original.apply(fakeCanvas, window.Array.from(arguments)); - } - else { - return original.apply(this, window.Array.from(arguments)); - } + else { + return original.apply(this, window.Array.from(args)); + } + }); }; } }, @@ -232,18 +235,21 @@ return status; }, object: "HTMLCanvasElement", - fakeGenerator: function(prefs, notify, window, original){ + fakeGenerator: function(checker){ return function toBlob(callback){ - if (canvasSizeShouldBeFaked(this, prefs)){ - var fakeCanvas = getFakeCanvas(window, this, prefs); - if (fakeCanvas !== this){ - notify.call(this, "fakedReadout"); + return checkerWrapper(checker, this, arguments, function(args, check){ + var {prefs, notify, window, original} = check; + if (canvasSizeShouldBeFaked(this, prefs)){ + var fakeCanvas = getFakeCanvas(window, this, prefs); + if (fakeCanvas !== this){ + notify.call(this, "fakedReadout"); + } + return original.apply(fakeCanvas, window.Array.from(args)); } - return original.apply(fakeCanvas, window.Array.from(arguments)); - } - else { - return original.apply(this, window.Array.from(arguments)); - } + else { + return original.apply(this, window.Array.from(args)); + } + }); }; }, exportOptions: {allowCallbacks: true} @@ -260,18 +266,21 @@ return status; }, object: "HTMLCanvasElement", - fakeGenerator: function(prefs, notify, window, original){ + fakeGenerator: function(checker){ return function mozGetAsFile(callback){ - if (canvasSizeShouldBeFaked(this, prefs)){ - var fakeCanvas = getFakeCanvas(window, this, prefs); - if (fakeCanvas !== this){ - notify.call(this, "fakedReadout"); + return checkerWrapper(checker, this, arguments, function(args, check){ + var {prefs, notify, window, original} = check; + if (canvasSizeShouldBeFaked(this, prefs)){ + var fakeCanvas = getFakeCanvas(window, this, prefs); + if (fakeCanvas !== this){ + notify.call(this, "fakedReadout"); + } + return original.apply(fakeCanvas, window.Array.from(args)); } - return original.apply(fakeCanvas, window.Array.from(arguments)); - } - else { - return original.apply(this, window.Array.from(arguments)); - } + else { + return original.apply(this, window.Array.from(args)); + } + }); }; } }, @@ -283,26 +292,29 @@ return status; }, object: "CanvasRenderingContext2D", - fakeGenerator: function(prefs, notify, window, original){ + fakeGenerator: function(checker){ return function getImageData(sx, sy, sw, sh){ - if (!this || canvasSizeShouldBeFaked(this.canvas, prefs)){ - var fakeCanvas; - var context = this; - if (this && this.canvas) { - fakeCanvas = getFakeCanvas(window, this.canvas, prefs); + return checkerWrapper(checker, this, arguments, function(args, check){ + var {prefs, notify, window, original} = check; + if (!this || canvasSizeShouldBeFaked(this.canvas, prefs)){ + var fakeCanvas; + var context = this; + if (this && this.canvas) { + fakeCanvas = getFakeCanvas(window, this.canvas, prefs); + } + if (fakeCanvas && fakeCanvas !== this.canvas){ + notify.call(this, "fakedReadout"); + context = window.HTMLCanvasElement.prototype.getContext.call( + fakeCanvas, + "2d" + ); + } + return original.apply(context, window.Array.from(args)); } - if (fakeCanvas && fakeCanvas !== this.canvas){ - notify.call(this, "fakedReadout"); - context = window.HTMLCanvasElement.prototype.getContext.call( - fakeCanvas, - "2d" - ); + else { + return original.apply(this, window.Array.from(args)); } - return original.apply(context, window.Array.from(arguments)); - } - else { - return original.apply(this, window.Array.from(arguments)); - } + }); }; } }, @@ -314,18 +326,21 @@ return status; }, object: "CanvasRenderingContext2D", - fakeGenerator: function(prefs, notify, window, original){ + fakeGenerator: function(checker){ return function isPointInPath(x, y){ - var rng = randomSupply.getValueRng(1, window); - var originalValue = original.apply(this, window.Array.from(arguments)); - if ((typeof originalValue) === "boolean"){ - notify.call(this, "fakedReadout"); - var index = x + this.width * y; - return original.call(this, rng(x, index), rng(y, index)); - } - else { - return originalValue; - } + return checkerWrapper(checker, this, arguments, function(args, check){ + var {prefs, notify, window, original} = check; + var rng = randomSupply.getValueRng(1, window); + var originalValue = original.apply(this, window.Array.from(args)); + if ((typeof originalValue) === "boolean"){ + notify.call(this, "fakedReadout"); + var index = x + this.width * y; + return original.call(this, rng(x, index), rng(y, index)); + } + else { + return originalValue; + } + }); }; } }, @@ -337,18 +352,21 @@ return status; }, object: "CanvasRenderingContext2D", - fakeGenerator: function(prefs, notify, window, original){ + fakeGenerator: function(checker){ return function isPointInStroke(x, y){ - var rng = randomSupply.getValueRng(1, window); - var originalValue = original.apply(this, window.Array.from(arguments)); - if ((typeof originalValue) === "boolean"){ - notify.call(this, "fakedReadout"); - var index = x + this.width * y; - return original.call(this, rng(x, index), rng(y, index)); - } - else { - return originalValue; - } + return checkerWrapper(checker, this, arguments, function(args, check){ + var {prefs, notify, window, original} = check; + var rng = randomSupply.getValueRng(1, window); + var originalValue = original.apply(this, window.Array.from(args)); + if ((typeof originalValue) === "boolean"){ + notify.call(this, "fakedReadout"); + var index = x + this.width * y; + return original.call(this, rng(x, index), rng(y, index)); + } + else { + return originalValue; + } + }); }; } }, @@ -360,27 +378,30 @@ return status; }, object: "CanvasRenderingContext2D", - fakeGenerator: function(prefs, notify, window, original){ + fakeGenerator: function(checker){ return function fillText(str, x, y){ - if (!this || canvasSizeShouldBeFaked(this.canvas, prefs)){ - notify.call(this, "fakedInput"); - var oldImageData; - try { - // "this" is not trustable - it may be not a context - oldImageData = getImageData(window, this).imageData; + return checkerWrapper(checker, this, arguments, function(args, check){ + var {prefs, notify, window, original} = check; + if (!this || canvasSizeShouldBeFaked(this.canvas, prefs)){ + notify.call(this, "fakedInput"); + var oldImageData; + try { + // "this" is not trustable - it may be not a context + oldImageData = getImageData(window, this).imageData; + } + catch (e){ + // nothing to do here + } + // if "this" is not a correct context the next line will throw an error + var ret = original.apply(this, window.Array.from(args)); + var newImageData = getImageData(window, this).imageData; + this.putImageData(randomMixImageData(window, oldImageData, newImageData), 0, 0); + return ret; } - catch (e){ - // nothing to do here + else { + return original.apply(this, window.Array.from(args)); } - // if "this" is not a correct context the next line will throw an error - var ret = original.apply(this, window.Array.from(arguments)); - var newImageData = getImageData(window, this).imageData; - this.putImageData(randomMixImageData(window, oldImageData, newImageData), 0, 0); - return ret; - } - else { - return original.apply(this, window.Array.from(arguments)); - } + }); }; } }, @@ -392,27 +413,30 @@ return status; }, object: "CanvasRenderingContext2D", - fakeGenerator: function(prefs, notify, window, original){ + fakeGenerator: function(checker){ return function strokeText(str, x, y){ - if (!this || canvasSizeShouldBeFaked(this.canvas, prefs)){ - notify.call(this, "fakedInput"); - var oldImageData; - try { - // "this" is not trustable - it may be not a context - oldImageData = getImageData(window, this).imageData; + return checkerWrapper(checker, this, arguments, function(args, check){ + var {prefs, notify, window, original} = check; + if (!this || canvasSizeShouldBeFaked(this.canvas, prefs)){ + notify.call(this, "fakedInput"); + var oldImageData; + try { + // "this" is not trustable - it may be not a context + oldImageData = getImageData(window, this).imageData; + } + catch (e){ + // nothing to do here + } + // if "this" is not a correct context the next line will throw an error + var ret = original.apply(this, window.Array.from(args)); + var newImageData = getImageData(window, this).imageData; + this.putImageData(randomMixImageData(window, oldImageData, newImageData), 0, 0); + return ret; } - catch (e){ - // nothing to do here + else { + return original.apply(this, window.Array.from(args)); } - // if "this" is not a correct context the next line will throw an error - var ret = original.apply(this, window.Array.from(arguments)); - var newImageData = getImageData(window, this).imageData; - this.putImageData(randomMixImageData(window, oldImageData, newImageData), 0, 0); - return ret; - } - else { - return original.apply(this, window.Array.from(arguments)); - } + }); }; } }, @@ -424,21 +448,24 @@ return status; }, object: ["WebGLRenderingContext", "WebGL2RenderingContext"], - fakeGenerator: function(prefs, notify, window, original){ + fakeGenerator: function(checker){ return function readPixels(x, y, width, height, format, type, pixels){ // eslint-disable-line max-params - if (!this || canvasSizeShouldBeFaked(this.canvas, prefs)){ - notify.call(this, "fakedReadout"); - var fakeCanvas = getFakeCanvas(window, this.canvas, prefs); - var {context} = copyCanvasToWebgl( - window, - fakeCanvas, - this instanceof window.WebGLRenderingContext? "webgl": "webgl2" - ); - return original.apply(context, window.Array.from(arguments)); - } - else { - return original.apply(this, window.Array.from(arguments)); - } + return checkerWrapper(checker, this, arguments, function(args, check){ + var {prefs, notify, window, original} = check; + if (!this || canvasSizeShouldBeFaked(this.canvas, prefs)){ + notify.call(this, "fakedReadout"); + var fakeCanvas = getFakeCanvas(window, this.canvas, prefs); + var {context} = copyCanvasToWebgl( + window, + fakeCanvas, + this instanceof window.WebGLRenderingContext? "webgl": "webgl2" + ); + return original.apply(context, window.Array.from(args)); + } + else { + return original.apply(this, window.Array.from(args)); + } + }); }; } } @@ -446,7 +473,17 @@ Object.keys(scope.changedFunctions).forEach(function(key){ scope.changedFunctions[key].api = "canvas"; }); - Object.keys(modifiedAudioAPI.changedFunctions).forEach(function(key){ - scope.changedFunctions[key] = modifiedAudioAPI.changedFunctions[key]; - }); + + scope.changedGetters = {}; + + function appendModified(collection){ + Object.keys(collection.changedFunctions || {}).forEach(function(key){ + scope.changedFunctions[key] = collection.changedFunctions[key]; + }); + + Object.keys(collection.changedGetters || {}).forEach(function(key){ + scope.changedGetters[key] = collection.changedGetters[key]; + }); + } + appendModified(modifiedAudioAPI); }()); \ No newline at end of file diff --git a/lib/modifiedAPIFunctions.js b/lib/modifiedAPIFunctions.js new file mode 100644 index 0000000..c3ddedd --- /dev/null +++ b/lib/modifiedAPIFunctions.js @@ -0,0 +1,32 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +(function(){ + "use strict"; + + var scope; + if ((typeof exports) !== "undefined"){ + scope = exports; + } + else { + window.scope.modifiedAPIFunctions = {}; + scope = window.scope.modifiedAPIFunctions; + } + + + + scope.hasType = function hasType(status, type){ + return status.type.indexOf(type) !== -1; + }; + + scope.checkerWrapper = function checkerWrapper(checker, object, args, callback){ + const check = checker(); + if (check.allow){ + if (check.allow === true){ + return check.original.apply(object, check.window.Array.from(args)); + } + return callback.call(object, args, check); + } + return undefined; + }; +}()); \ No newline at end of file diff --git a/lib/modifiedAudioAPI.js b/lib/modifiedAudioAPI.js index 2df72c5..9cb51a9 100644 --- a/lib/modifiedAudioAPI.js +++ b/lib/modifiedAudioAPI.js @@ -16,6 +16,7 @@ const logging = require("./logging"); const {sha256String: hashing} = require("./hash"); const getWrapped = require("sdk/getWrapped"); + const {hasType, checkerWrapper} = require("./modifiedAPIFunctions"); var randomSupply = null; @@ -143,10 +144,6 @@ } } - function hasType(status, type){ - return status.type.indexOf(type) !== -1; - } - scope.setRandomSupply = function(supply){ randomSupply = supply; }; @@ -168,79 +165,100 @@ scope.changedFunctions = { getFloatFrequencyData: { object: ["AnalyserNode"], - fakeGenerator: function(prefs, notify, window, original){ + fakeGenerator: function(checker){ return function getFloatFrequencyData(array){ - notifyOnce("getFloatFrequencyData", notify); - var ret = original.apply(this, window.Array.from(arguments)); - fakeFloat32Array(array, window, prefs); - return ret; + return checkerWrapper(checker, this, arguments, function(args, check){ + var {prefs, notify, window, original} = check; + notifyOnce("getFloatFrequencyData", notify); + var ret = original.apply(this, window.Array.from(args)); + fakeFloat32Array(array, window, prefs); + return ret; + }); }; } }, getByteFrequencyData: { object: ["AnalyserNode"], - fakeGenerator: function(prefs, notify, window, original){ + fakeGenerator: function(checker){ return function getByteFrequencyData(array){ - notifyOnce("getByteFrequencyData", notify); - var ret = original.apply(this, window.Array.from(arguments)); - fakeUint8Array(array, window, prefs); - return ret; + return checkerWrapper(checker, this, arguments, function(args, check){ + var {prefs, notify, window, original} = check; + notifyOnce("getByteFrequencyData", notify); + var ret = original.apply(this, window.Array.from(args)); + fakeUint8Array(array, window, prefs); + return ret; + }); }; } }, getFloatTimeDomainData: { object: ["AnalyserNode"], - fakeGenerator: function(prefs, notify, window, original){ + fakeGenerator: function(checker){ return function getFloatTimeDomainData(array){ - notifyOnce("getFloatTimeDomainData", notify); - var ret = original.apply(this, window.Array.from(arguments)); - fakeFloat32Array(array, window, prefs); - return ret; + return checkerWrapper(checker, this, arguments, function(args, check){ + var {prefs, notify, window, original} = check; + notifyOnce("getFloatTimeDomainData", notify); + var ret = original.apply(this, window.Array.from(args)); + fakeFloat32Array(array, window, prefs); + return ret; + }); }; } }, getByteTimeDomainData: { object: ["AnalyserNode"], - fakeGenerator: function(prefs, notify, window, original){ + fakeGenerator: function(checker){ return function getByteTimeDomainData(array){ - notifyOnce("getByteTimeDomainData", notify); - var ret = original.apply(this, window.Array.from(arguments)); - fakeUint8Array(array, window, prefs); - return ret; + return checkerWrapper(checker, this, arguments, function(args, check){ + var {prefs, notify, window, original} = check; + notifyOnce("getByteTimeDomainData", notify); + var ret = original.apply(this, window.Array.from(args)); + fakeUint8Array(array, window, prefs); + return ret; + }); }; } }, getChannelData: { object: ["AudioBuffer"], - fakeGenerator: function(prefs, notify, window, original){ + fakeGenerator: function(checker){ return function getChannelData(channel){ - notifyOnce("getChannelData", notify); - var ret = original.apply(this, window.Array.from(arguments)); - fakeFloat32Array(ret, window, prefs); - return ret; + return checkerWrapper(checker, this, arguments, function(args, check){ + var {prefs, notify, window, original} = check; + notifyOnce("getChannelData", notify); + var ret = original.apply(this, window.Array.from(args)); + fakeFloat32Array(ret, window, prefs); + return ret; + }); }; } }, copyFromChannel: { object: ["AudioBuffer"], - fakeGenerator: function(prefs, notify, window, original){ + fakeGenerator: function(checker){ return function copyFromChannel(destination, channelNumber, startInChannel){ - notifyOnce("copyFromChannel", notify); - var ret = original.apply(this, window.Array.from(arguments)); - fakeFloat32Array(destination, window, prefs); - return ret; + return checkerWrapper(checker, this, arguments, function(args, check){ + var {prefs, notify, window, original} = check; + notifyOnce("copyFromChannel", notify); + var ret = original.apply(this, window.Array.from(args)); + fakeFloat32Array(destination, window, prefs); + return ret; + }); }; } }, getFrequencyResponse: { object: ["BiquadFilterNode", "IIRFilterNode"], - fakeGenerator: function(prefs, notify, window, original){ + fakeGenerator: function(checker){ return function getFrequencyResponse(frequencyArray, magResponseOutput, phaseResponseOutput){ - notifyOnce("getFrequencyResponse", notify); - var ret = original.apply(this, window.Array.from(arguments)); - fakeFloat32Array(magResponseOutput, window, prefs); - fakeFloat32Array(phaseResponseOutput, window, prefs); - return ret; + return checkerWrapper(checker, this, arguments, function(args, check){ + var {prefs, notify, window, original} = check; + notifyOnce("getFrequencyResponse", notify); + var ret = original.apply(this, window.Array.from(args)); + fakeFloat32Array(magResponseOutput, window, prefs); + fakeFloat32Array(phaseResponseOutput, window, prefs); + return ret; + }); }; } }, diff --git a/manifest.json b/manifest.json index 8be512e..56c8467 100644 --- a/manifest.json +++ b/manifest.json @@ -32,6 +32,7 @@ "lib/colorStatistics.js", "lib/webgl.js", "lib/hash.js", + "lib/modifiedAPIFunctions.js", "lib/modifiedAudioAPI.js", "lib/modifiedAPI.js", "lib/randomSupplies.js", diff --git a/releaseNotes.txt b/releaseNotes.txt index 0448569..c333aed 100644 --- a/releaseNotes.txt +++ b/releaseNotes.txt @@ -8,7 +8,7 @@ Version 0.4.6: - Settings can be hidden fixes: - - + - make function replacements not detectable Version 0.4.5c: new features: