[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