[Pkg-mozext-commits] [requestpolicy] 15/257: [add] utils: add wrapFunction() and unwrapFunction
David Prévot
taffit at moszumanska.debian.org
Thu Jan 28 03:19:52 UTC 2016
This is an automated email from the git hooks/post-receive script.
taffit pushed a commit to branch master
in repository requestpolicy.
commit 164d4e54e825ecdc169ed2dc7bc8c5a4c819bf07
Author: Martin Kimmerle <dev at 256k.de>
Date: Mon Aug 3 12:43:57 2015 +0200
[add] utils: add wrapFunction() and unwrapFunction
---
src/content/lib/utils.jsm | 99 +++++++++++++++++++++++-
tests/xpcshell/test_wrap_function.js | 142 +++++++++++++++++++++++++++++++++++
tests/xpcshell/xpcshell.ini | 1 +
3 files changed, 241 insertions(+), 1 deletion(-)
diff --git a/src/content/lib/utils.jsm b/src/content/lib/utils.jsm
index 57a2ceb..557788c 100644
--- a/src/content/lib/utils.jsm
+++ b/src/content/lib/utils.jsm
@@ -35,7 +35,8 @@ Cu.import("chrome://rpcontinued/content/lib/script-loader.jsm");
ScriptLoader.importModules([
"lib/prefs",
"lib/utils/constants",
- "lib/environment"
+ "lib/environment",
+ "lib/logger"
], this);
if (ProcessEnvironment.isMainProcess) {
@@ -146,5 +147,101 @@ let Utils = (function() {
return aModuleScope.internal;
};
+
+ /**
+ * Wrap a function. Allow 'before' and 'after' functions.
+ * If the function was wrapped already earlier in time, the old
+ * wrapper function will be re-used.
+ *
+ * @param {Object} aOwnerObject The object which contains (a reference to)
+ * the function which should be wrapped.
+ * @param {string} aFunctionName The function's name in the object.
+ * @param {Function=} aBeforeFunction The function to be called before the
+ * original function.
+ * @param {Function=} aAfterFunction The function to be called after the
+ * original function.
+ */
+ self.wrapFunction = function(aOwnerObject, aFunctionName,
+ aBeforeFunction = null, aAfterFunction = null) {
+ initWrapperFunction(aOwnerObject, aFunctionName);
+
+ var fnMetadata = aOwnerObject.rpcontinuedWrappedFunctions[aFunctionName];
+ fnMetadata.before = aBeforeFunction;
+ fnMetadata.after = aAfterFunction;
+ };
+
+ /**
+ * Unwrap a function which has been wrapped before. The function won't
+ * be removed though, because another addon could have wrapped the same
+ * function as well. Instead, the 'before' and 'after' functions are
+ * set to `null`.
+ *
+ * @param {Object} aOwnerObject The object which contains (a reference to)
+ * the function which should be wrapped.
+ * @param {string} aFunctionName The function's name in the object.
+ */
+ self.unwrapFunction = function(aOwnerObject, aFunctionName) {
+ self.wrapFunction(aOwnerObject, aFunctionName, null, null);
+ };
+
+ /**
+ * @param {Object} aOwnerObject The object which contains (a reference to)
+ * the function which should be wrapped.
+ * @param {string} aFunctionName The function's name in the object.
+ */
+ function initWrapperFunction(aOwnerObject, aFunctionName) {
+ // create metadata object
+ if (!aOwnerObject.hasOwnProperty("rpcontinuedWrappedFunctions")) {
+ aOwnerObject.rpcontinuedWrappedFunctions = {};
+ }
+
+ var metadata = aOwnerObject.rpcontinuedWrappedFunctions;
+
+ if (metadata.hasOwnProperty(aFunctionName)) {
+ // the function is already wrapped by RequestPolicy
+ return;
+ }
+
+ // create metadata
+ metadata[aFunctionName] = {
+ main: aOwnerObject[aFunctionName], // the original function
+ before: null,
+ after: null
+ };
+
+ // actually wrap the object
+ aOwnerObject[aFunctionName] = function() {
+ var {main, before, after} = metadata[aFunctionName];
+
+ // Execute some action before the original function call.
+ try {
+ if (before) {
+ before.apply(aOwnerObject, arguments);
+ }
+ } catch (e) {
+ Logger.warning(Logger.TYPE_ERROR, "The 'before' function of the " +
+ "`" + aFunctionName + "()` wrapper has thrown an " +
+ "error.", e);
+ }
+
+ // Execute original function.
+ var rv = main.apply(aOwnerObject, arguments);
+
+ // Execute some action afterwards.
+ try {
+ if (after) {
+ after.apply(aOwnerObject, arguments);
+ }
+ } catch (e) {
+ Logger.warning(Logger.TYPE_ERROR, "The 'after' function of the " +
+ "`" + aFunctionName + "()` wrapper has thrown an " +
+ "error.", e);
+ }
+
+ // return the original result
+ return rv;
+ };
+ }
+
return self;
}());
diff --git a/tests/xpcshell/test_wrap_function.js b/tests/xpcshell/test_wrap_function.js
new file mode 100644
index 0000000..e7b93f2
--- /dev/null
+++ b/tests/xpcshell/test_wrap_function.js
@@ -0,0 +1,142 @@
+const MODULE_URI = "chrome://rpcontinued/content/lib/utils.jsm";
+
+var functionCalls = "";
+
+var testObj = {
+ testProp: "foo",
+ testFunction: wrappedFunction
+};
+
+var mod = {};
+
+Cu.import(MODULE_URI, mod);
+
+
+function run_test() {
+
+ // 1: Basic check.
+ uut_wrap(wrapperFunction1, wrapperFunction2);
+ callAndCheck("_1_0_2");
+
+ // 2: Wrap "manually" so that RequestPolicy's wrapper function isn't the
+ // outermost anymore.
+ manualWrap();
+ callAndCheck("{_1_0_2}");
+
+ // 3: Check that a second call to the uut_wrap function overwrites the
+ // old 'before' and 'after' functions. It should not wrap again.
+ uut_wrap(wrapperFunction2, wrapperFunction1);
+ callAndCheck("{_2_0_1}");
+
+ // 4: Check that thrown errors are catched.
+ // At first check that function 3 throws.
+ Assert.throws(() => wrapperFunction3("foo", "bar"));
+ uut_wrap(wrapperFunction3, wrapperFunction3);
+ callAndCheck("{_3_0_3}");
+
+ // 5: Check that 'before' and 'after' function can be `null` and `undefined`.
+ uut_wrap(null, null);
+ callAndCheck("{_0}");
+ uut_wrap(undefined, undefined);
+ callAndCheck("{_0}");
+
+ // 6: Test the `unwrap` function.
+ uut_unwrap();
+ callAndCheck("{_0}");
+
+ // 7: Check that the wrapped function works after unloading. Unloading
+ // is what is done when the addon gets shutdown, e.g. when disabled
+ // or being updated.
+ Cu.unload(MODULE_URI);
+ mod = {};
+ callAndCheck("{_0}");
+
+ // 8: Again importing the Module should work without again wrapping the
+ // outermost function. Reason: disable-enabling and update actions
+ // should not increase the number of wrapper functions.
+ Cu.import(MODULE_URI, mod);
+ // Note: Using `wrapperFunction3()` did not work. Since wrapperFunction3
+ // throws an error, the `Logger` is invoked. The error was:
+ // "Logger is undefined at […]/utils.jsm"
+ // I suppose the problem is that `Logger` and the other
+ // modules required by `utils.jsm` haven't been unloaded.
+ //uut_wrap(wrapperFunction1, wrapperFunction3);
+ //callAndCheck("{_1_0_3}");
+ uut_wrap(wrapperFunction1, wrapperFunction2);
+ callAndCheck("{_1_0_2}");
+}
+
+function uut_wrap(f1, f2) {
+ mod.Utils.wrapFunction(testObj, "testFunction", f1, f2);
+}
+
+function uut_unwrap() {
+ mod.Utils.unwrapFunction(testObj, "testFunction");
+}
+
+/**
+ * Wraps `testObj.testFunction()` in order to test scenarios where
+ * multiple addons are wrapping the same function.
+ */
+function manualWrap() {
+ var _orig = testObj.testFunction;
+
+ testObj.testFunction = function () {
+ functionCalls += "{";
+ var rv = _orig.apply(testObj, arguments);
+ functionCalls += "}";
+ return rv;
+ };
+}
+
+function callAndCheck(expectedFunctionCalls) {
+ // reset the function calls
+ functionCalls = "";
+
+ // call the function, remember the return value
+ var rv = testObj.testFunction("foo", "bar");
+
+ // do checks
+ do_check_eq(rv, "baz");
+ do_check_eq(functionCalls, expectedFunctionCalls);
+}
+
+function wrappedFunction(param1, param2) {
+ functionCalls += "_0";
+
+ // check that "this" is correctly set
+ do_check_true(this.testProp === "foo");
+
+ // check that the parameters have been passed correctly
+ do_check_true(param1 === "foo");
+ do_check_true(param2 === "bar");
+
+ return "baz";
+}
+
+function wrapperFunction1(param1, param2) {
+ functionCalls += "_1";
+
+ // check that the parameters have been passed correctly
+ do_check_true(param1 === "foo");
+ do_check_true(param2 === "bar");
+}
+
+function wrapperFunction2(param1, param2) {
+ functionCalls += "_2";
+
+ // check that the parameters have been passed correctly
+ do_check_true(param1 === "foo");
+ do_check_true(param2 === "bar");
+}
+
+function wrapperFunction3(param1, param2) {
+ functionCalls += "_3";
+
+ // check that the parameters have been passed correctly
+ do_check_true(param1 === "foo");
+ do_check_true(param2 === "bar");
+
+ // test that errors are catched
+ throw "test error";
+}
diff --git a/tests/xpcshell/xpcshell.ini b/tests/xpcshell/xpcshell.ini
index 1ff4c48..eb96eb4 100644
--- a/tests/xpcshell/xpcshell.ini
+++ b/tests/xpcshell/xpcshell.ini
@@ -6,3 +6,4 @@ tail =
;[test_policymanager.js]
[test_policystorage.js]
;[test_subscription.js]
+[test_wrap_function.js]
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-mozext/requestpolicy.git
More information about the Pkg-mozext-commits
mailing list