Browse Source

Prevent outside access to internal callback data but still allow Sync to access that data.

Edward Lee 15 years ago
parent
commit
36812061e3
2 changed files with 42 additions and 8 deletions
  1. 18 8
      Sync.js
  2. 24 0
      test/unit/test_Sync.js

+ 18 - 8
Sync.js

@@ -47,18 +47,27 @@ const Cu = Components.utils;
 const CB_READY = {};
 const CB_COMPLETE = {};
 
+// Share a secret only for functions in this file to prevent outside access
+const SECRET = {};
+
 /**
  * Create a callback that remembers state like whether it's been called
  */
 function makeCallback() {
+  // Initialize private callback data to prepare to be called
+  let _ = {
+    state: CB_READY,
+    value: null
+  };
+
   // The main callback remembers the value it's passed and that it got data
-  let onComplete = function Sync_onComplete(data) {
-    onComplete.value = data;
-    onComplete.state = CB_COMPLETE;
+  let onComplete = function makeCallback_onComplete(data) {
+    _.state = CB_COMPLETE;
+    _.value = data;
   };
 
-  // Initialize the callback to wait to be called
-  onComplete.state = CB_READY;
+  // Only allow access to the private data if the secret matches
+  onComplete._ = function onComplete__(secret) secret == SECRET ? _ : {};
 
   return onComplete;
 }
@@ -97,14 +106,15 @@ function Sync(func, thisArg, callback) {
     func.apply(thisArg, args);
 
     // Keep waiting until our callback is triggered
-    while (instanceCallback.state == CB_READY)
+    let callbackData = instanceCallback._(SECRET);
+    while (callbackData.state == CB_READY)
       thread.processNextEvent(true);
 
     // Reset the state of the callback to prepare for another call
-    instanceCallback.state = CB_READY;
+    callbackData.state = CB_READY;
 
     // Return the value passed to the callback
-    return instanceCallback.value;
+    return callbackData.value;
   };
 }
 

+ 24 - 0
test/unit/test_Sync.js

@@ -124,3 +124,27 @@ function test_function_Sync() {
   do_check_eq(Sync.call.toSource(), Function.prototype.call.toSource());
   do_check_eq(Sync.apply.toSource(), Function.prototype.apply.toSource());
 }
+
+// Make sure private callback data isn't accessible
+function test_callback_privates() {
+  // Make sure we can't read any values out of the callback
+  let checkAccess = function(cb) {
+    do_check_eq(cb.state, null);
+    do_check_eq(cb.value, null);
+    do_check_eq(cb._().state, null);
+    do_check_eq(cb._().value, null);
+  };
+
+  // Save the callback to use it after the sync. call
+  let theCb;
+  checkTime(Sync(function(cb) {
+    // Make sure there's no access when the function is called
+    checkAccess(cb);
+
+    // Save the callback and continue execution after waiting a little
+    setTimeout(theCb = cb, 100);
+  }), 100);
+
+  // Make sure there's no access after the sync. call
+  checkAccess(theCb);
+}