You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by dd...@apache.org on 2012/09/04 16:01:03 UTC

svn commit: r1380656 - in /shindig/trunk: ./ features/ features/src/main/javascript/features/container/ features/src/main/javascript/features/core.util.base/ features/src/main/javascript/features/oauthpopup/ features/src/test/javascript/features/oauthp...

Author: ddumont
Date: Tue Sep  4 14:01:02 2012
New Revision: 1380656

URL: http://svn.apache.org/viewvc?rev=1380656&view=rev
Log:
SHINDIG-1864 - Refactor oauthpopup feature to use rpc to the container

Added:
    shindig/trunk/features/src/main/javascript/features/oauthpopup/container_oauthpopup.js   (with props)
Modified:
    shindig/trunk/UPGRADING
    shindig/trunk/features/pom.xml
    shindig/trunk/features/src/main/javascript/features/container/container.js
    shindig/trunk/features/src/main/javascript/features/container/feature.xml
    shindig/trunk/features/src/main/javascript/features/core.util.base/base.js
    shindig/trunk/features/src/main/javascript/features/oauthpopup/feature.xml
    shindig/trunk/features/src/main/javascript/features/oauthpopup/oauthpopup.js
    shindig/trunk/features/src/test/javascript/features/oauthpopup/oauthpopup-test.js

Modified: shindig/trunk/UPGRADING
URL: http://svn.apache.org/viewvc/shindig/trunk/UPGRADING?rev=1380656&r1=1380655&r2=1380656&view=diff
==============================================================================
--- shindig/trunk/UPGRADING (original)
+++ shindig/trunk/UPGRADING Tue Sep  4 14:01:02 2012
@@ -7,6 +7,9 @@ FROM 2.0.x TO 2.5.x
 The new property allows for embedding the key directly or referencing a classpath or filesystem
 resource.  Please see the comments at the top of container.js and around the new property for more
 details.
+* The CommonContainer class now depends on the oauthpopup feature for some reworked logic in the
+feature.  If you roll your own container and do not use the CommonContainer, update your container
+dependencies to include the oauthpopup feature.
 
 == Java Dependency Changes ==
 

Modified: shindig/trunk/features/pom.xml
URL: http://svn.apache.org/viewvc/shindig/trunk/features/pom.xml?rev=1380656&r1=1380655&r2=1380656&view=diff
==============================================================================
--- shindig/trunk/features/pom.xml (original)
+++ shindig/trunk/features/pom.xml Tue Sep  4 14:01:02 2012
@@ -195,6 +195,7 @@
                 <source>osapi/jsonrpctransport.js</source>
                 <source>osapi/peoplehelpers.js</source>
                 <source>../../../../src/test/javascript/lib/testutils.js</source>
+                <source>oauthpopup/container_oauthpopup.js</source>
                 <source>oauthpopup/oauthpopup.js</source>
                 <source>selection/selection_container.js</source>
                 <source>selection/selection.js</source>

Modified: shindig/trunk/features/src/main/javascript/features/container/container.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/container/container.js?rev=1380656&r1=1380655&r2=1380656&view=diff
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/container/container.js (original)
+++ shindig/trunk/features/src/main/javascript/features/container/container.js Tue Sep  4 14:01:02 2012
@@ -399,8 +399,8 @@ osapi.container.Container.addMixin = fun
   if (mixins[namespace]) {
     var orig = mixins[namespace];
     mixins[namespace] = function(container) {
-      orig.call(this, container);
-      return func.call(this, container);
+      var base = orig.call(this, container);
+      return func.call(this, container, base); // pass overriding mixins the original.
     };
   } else {
     osapi.container.Container.prototype.mixinsOrder_.push(namespace);

Modified: shindig/trunk/features/src/main/javascript/features/container/feature.xml
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/container/feature.xml?rev=1380656&r1=1380655&r2=1380656&view=diff
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/container/feature.xml (original)
+++ shindig/trunk/features/src/main/javascript/features/container/feature.xml Tue Sep  4 14:01:02 2012
@@ -28,6 +28,7 @@ under the License.
   <dependency>container.util</dependency>
   <dependency>container.site.gadget</dependency>
   <dependency>container.site.url</dependency>
+  <dependency>oauthpopup</dependency>
   <container>
     <script src="service.js"/>
     <script src="container.js"/>

Modified: shindig/trunk/features/src/main/javascript/features/core.util.base/base.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/core.util.base/base.js?rev=1380656&r1=1380655&r2=1380656&view=diff
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/core.util.base/base.js (original)
+++ shindig/trunk/features/src/main/javascript/features/core.util.base/base.js Tue Sep  4 14:01:02 2012
@@ -44,18 +44,10 @@ gadgets.util = gadgets.util || {};
  * @return {function()} a callback function.
  */
 gadgets.util.makeClosure = function(scope, callback, var_args) {
-  // arguments isn't a real array, so we copy it into one.
-  var baseArgs = [];
-  for (var i = 2, j = arguments.length; i < j; ++i) {
-    baseArgs.push(arguments[i]);
-  }
+  var baseArgs = Array.prototype.slice.call(arguments, 2);
   return function() {
-    // append new arguments.
-    var tmpArgs = baseArgs.slice();
-    for (var i = 0, j = arguments.length; i < j; ++i) {
-      tmpArgs.push(arguments[i]);
-    }
-    return callback.apply(scope, tmpArgs);
+    var passedArgs = Array.prototype.slice.call(arguments);
+    return callback.apply(scope, baseArgs.concat(passedArgs));
   };
 };
 

Added: shindig/trunk/features/src/main/javascript/features/oauthpopup/container_oauthpopup.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/oauthpopup/container_oauthpopup.js?rev=1380656&view=auto
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/oauthpopup/container_oauthpopup.js (added)
+++ shindig/trunk/features/src/main/javascript/features/oauthpopup/container_oauthpopup.js Tue Sep  4 14:01:02 2012
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @fileoverview API to assist with management of the OAuth popup window.
+ */
+
+(function() {
+  var timers = {},
+      cbid = 1;  // start at 1 so they are always truthy
+
+  function checkClosed(win, callback, key) {
+    if (this.isClosed(win)) {
+      // setInterval, when missed, can run multiple times.
+      if (typeof(timers[key]) != 'undefined') {
+        window.clearInterval(timers[key]);
+        delete timers[key];
+        callback();
+      }
+    }
+  }
+
+  // scope
+  var oauth = {
+    /**
+     * Handles the rpc endpoint for gadgets wanting to open an oauth popup.
+     *
+     * @param {string} location Location to open
+     * @param {string} options Window open options
+     * @param {string} from Unique identifier of gadget requesting oauth
+     * @param {function(stirng)} onOpen Callback that takes the id of the close callback
+     */
+    open: function(location, options, from, onOpen) {
+      // If a popup blocker blocks the window, we do nothing.  The user will
+      // need to approve the popup, then click again to open the window.
+      // Note that because we don't call window.open until the user has clicked
+      // something the popup blockers *should* let us through.
+      var key = location + ':' + from + ":" + options,
+          win = this.getWindow(location, options);
+
+      if (win) {
+        var id = cbid++,
+            callback = gadgets.util.makeClosure(this, function(id) {
+              this.closeWindow(win); // make sure it's closed
+              gadgets.rpc.call(from, 'oauth.close', null, id);
+            }, id);
+
+        // Poll every 100ms to check if the window has been closed
+        timers[key] = window.setInterval(gadgets.util.makeClosure(this, checkClosed, win, callback, key), 100);
+
+        onOpen(id);
+      } else {
+        // handle error that might be able to resume.
+        this.error(Array.prototype.slice.call(arguments));
+      }
+    },
+
+    /**
+     * Opens a window at a specific location.
+     *
+     * @param {string} location Location to open
+     * @param {string} options Window open options
+     * @param {string} from Unique identifier of gadget requesting oauth
+     * @return {Object} The opened window
+     */
+    getWindow: function(location, options, from) {
+      return window.open(location, '_blank', options);
+    },
+
+    /**
+     * Closes a window opened by
+     *
+     * @param {Object} win A window opened by getWindow()
+     */
+    closeWindow: function(win) {
+      win && win.close && win.close();
+    },
+
+    /**
+     * Check to see if window opened by getWindow() is closed.
+     *
+     * @param {Object} win A window opened by getWindow()
+     * @return {boolean} If win is closed.
+     */
+    isClosed: function(win) {
+      return !win || win.closed;
+    },
+
+    error: function(openargs) {
+      gadgets.warn('OAuth popup window was not opened.');
+      // Try again?
+      // this.open.apply(this, openargs);
+    }
+  };
+
+  // Support mixing into the container which makes js rpc tests super easy.
+  if (osapi && osapi.container && osapi.container.Container && osapi.container.Container.addMixin) {
+    osapi.container.Container.addMixin('oauth', function(container) {
+      gadgets.rpc.register('oauth.open', function(location, options) {
+        container.oauth.open(location, options, this.f, this.callback);
+      });
+
+      return oauth;
+    });
+  } else {
+    gadgets.rpc.register('oauth.open', function(location, options) {
+      oauth.open(location, options, this.f, this.callback);
+    });
+  }
+})();
\ No newline at end of file

Propchange: shindig/trunk/features/src/main/javascript/features/oauthpopup/container_oauthpopup.js
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: shindig/trunk/features/src/main/javascript/features/oauthpopup/container_oauthpopup.js
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: shindig/trunk/features/src/main/javascript/features/oauthpopup/feature.xml
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/oauthpopup/feature.xml?rev=1380656&r1=1380655&r2=1380656&view=diff
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/oauthpopup/feature.xml (original)
+++ shindig/trunk/features/src/main/javascript/features/oauthpopup/feature.xml Tue Sep  4 14:01:02 2012
@@ -18,13 +18,22 @@ specific language governing permissions 
 -->
 <feature>
   <name>oauthpopup</name>
-  <dependency>globals</dependency>
+  <dependency>rpc</dependency>
+  <container>
+    <script src="container_oauthpopup.js"/>
+    <api>
+      <exports type="rpc">oauth.open</exports>
+      <uses type="rpc">oauth.close</uses>
+    </api>
+  </container>
   <gadget>
     <script src="oauthpopup.js"/>
     <api>
       <exports type="js">gadgets.oauth.Popup</exports>
       <exports type="js">gadgets.oauth.Popup.prototype.createOpenerOnClick</exports>
       <exports type="js">gadgets.oauth.Popup.prototype.createApprovedOnClick</exports>
+      <exports type="rpc">oauth.close</exports>
+      <uses type="rpc">oauth.open</uses>
     </api>
   </gadget>
 </feature>

Modified: shindig/trunk/features/src/main/javascript/features/oauthpopup/oauthpopup.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/oauthpopup/oauthpopup.js?rev=1380656&r1=1380655&r2=1380656&view=diff
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/oauthpopup/oauthpopup.js (original)
+++ shindig/trunk/features/src/main/javascript/features/oauthpopup/oauthpopup.js Tue Sep  4 14:01:02 2012
@@ -105,89 +105,64 @@ gadgets.oauth = gadgets.oauth || {};
  * @param {string} destination Target URL for the popup window.
  * @param {string} windowOptions Options for window.open, used to specify
  *     look and feel of the window.
- * @param {function()} openCallback Function to call when the window is opened.
- * @param {function()} closeCallback Function to call when the window is closed.
+ * @param {function()} onOpen Function to call when the window is opened.
+ * @param {function()} onClose Function to call when the window is closed.
  */
-gadgets.oauth.Popup = function(destination, windowOptions, openCallback,
-    closeCallback) {
+gadgets.oauth.Popup = function(destination, windowOptions, onOpen, onClose) {
   this.destination_ = destination;
   this.windowOptions_ = windowOptions;
-  this.openCallback_ = openCallback;
-  this.closeCallback_ = closeCallback;
-  this.win_ = null;
+  this.openCallback_ = onOpen;
+  this.closeCallback_ = onClose;
 };
 
 /**
  * @return {function()} an onclick handler for the "open the approval window" link.
  */
-gadgets.oauth.Popup.prototype.createOpenerOnClick = function() {
-  var self = this;
-  return function() {
-    self.onClick_();
-  };
-};
 
-/**
- * Called when the user clicks to open the popup window.
- *
- * @return {boolean} false to prevent the default action for the click.
- * @private
- */
-gadgets.oauth.Popup.prototype.onClick_ = function() {
-  // If a popup blocker blocks the window, we do nothing.  The user will
-  // need to approve the popup, then click again to open the window.
-  // Note that because we don't call window.open until the user has clicked
-  // something the popup blockers *should* let us through.
-  this.win_ = window.open(this.destination_, '_blank', this.windowOptions_);
-  if (this.win_) {
-    // Poll every 100ms to check if the window has been closed
-    var self = this;
-    var closure = function() {
-      self.checkClosed_();
-    };
-    this.timer_ = window.setInterval(closure, 100);
-    this.openCallback_();
-  }
-  return false;
-};
 
-/**
- * Called at intervals to check whether the window has closed.
- * @private
- */
-gadgets.oauth.Popup.prototype.checkClosed_ = function() {
-  if ((!this.win_) || this.win_.closed) {
-    this.win_ = null;
-    this.handleApproval_();
-  }
-};
+(function() {
+  var callbacks = {};
 
-/**
- * Called when we recieve an indication the user has approved access, either
- * because they closed the popup window or clicked an "I've approved" button.
- * @private
- */
-gadgets.oauth.Popup.prototype.handleApproval_ = function() {
-  if (this.timer_) {
-    window.clearInterval(this.timer_);
-    this.timer_ = null;
-  }
-  if (this.win_) {
-    this.win_.close();
-    this.win_ = null;
+  gadgets.util.registerOnLoadHandler(function() {
+    gadgets.rpc.register('oauth.close', function(cbid) {
+      if (this.f == '..') {
+        callbacks[cbid] && callbacks[cbid]();
+      }
+    });
+  });
+
+  function onOpen(cbid) {
+    this.cbid = cbid;
+    callbacks[cbid] = this.createApprovedOnClick();
+    this.openCallback_();
   }
-  this.closeCallback_();
-  return false;
-};
 
-/**
- * @return {function()} an onclick handler for the "I've approved" link.  This may not
- * ever be called.  If we successfully detect that the window was closed,
- * this link is unnecessary.
- */
-gadgets.oauth.Popup.prototype.createApprovedOnClick = function() {
-  var self = this;
-  return function() {
-    self.handleApproval_();
+  /**
+   * Called when the user clicks to open the popup window.
+   *
+   * @return {boolean} false to prevent the default action for the click.
+   * @private
+   */
+  gadgets.oauth.Popup.prototype.createOpenerOnClick = function() {
+    return gadgets.util.makeClosure(this, function() {
+      gadgets.rpc.call('..', 'oauth.open', gadgets.util.makeClosure(this, onOpen),
+              this.destination_, this.windowOptions_
+      );
+      return false;
+    });
   };
-};
+
+  /**
+   * @return {function()} an onclick handler for the "I've approved" link.  This may not
+   * ever be called.  If we successfully detect that the window was closed,
+   * this link is unnecessary.
+   */
+  gadgets.oauth.Popup.prototype.createApprovedOnClick = function() {
+    return gadgets.util.makeClosure(this, function() {
+      if (this.cbid) {
+        delete callbacks[this.cbid];
+      }
+      this.closeCallback_();
+    });
+  };
+})();
\ No newline at end of file

Modified: shindig/trunk/features/src/test/javascript/features/oauthpopup/oauthpopup-test.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/test/javascript/features/oauthpopup/oauthpopup-test.js?rev=1380656&r1=1380655&r2=1380656&view=diff
==============================================================================
--- shindig/trunk/features/src/test/javascript/features/oauthpopup/oauthpopup-test.js (original)
+++ shindig/trunk/features/src/test/javascript/features/oauthpopup/oauthpopup-test.js Tue Sep  4 14:01:02 2012
@@ -26,58 +26,76 @@ PopupTest.inherits(TestCase);
 PopupTest.prototype.setUp = function() {
   this.oldWindow = window;
   window = new mocks.FakeWindow();
+  window.__API_URI = shindig.uri('http://shindig.com');
+  window.__CONTAINER_URI = shindig.uri('http://container.com');
+
+  this.gadgetsRpc = gadgets.rpc;
+  gadgets.rpc = {};
+  var self = this;
+  gadgets.rpc.register = function(service, callback) {
+    if (self.captures && self.captures.hasOwnProperty(service)) {
+      self.captures[service] = callback;
+    }
+  };
+  gadgets.rpc.call = function() {
+    self.rpcArguments = Array.prototype.slice.call(arguments);
+  };
 };
 
 PopupTest.prototype.tearDown = function() {
   window = this.oldWindow;
+  gadgets.rpc = this.gadgetsRpc;
+  delete this.rpcArguments;
+  delete this.captures;
 };
 
 PopupTest.prototype.testPopup = function() {
-  var opened = false;
-  var open = function() {
-    opened = true;
+  var undef, captures = this.captures = {
+    'oauth.open': undef
   };
-  var closed = false;
-  var close = function() {
-    closed = true;
+  var container = new osapi.container.Container();
+  this.assertNotUndefined('RPC endpoint "oauth.open" was not registered.', captures['oauth.open']);
+
+  delete this.rpcArguments;
+
+  var cbid, popup, opened = false;
+  window.open = function(url, target, options) {
+    return popup = mocks.FakeWindow.prototype.open.call(this, url, target, options);
   };
-  // Create the popup
-  var popup = new gadgets.oauth.Popup('destination', 'options', open, close);
-  var openerOnClick = popup.createOpenerOnClick();
-  var closerOnClick = popup.createApprovedOnClick();
-  this.assertNull('Window opened prematurely', popup.win_);
-  this.assertFalse('Opener callback was called', opened);
+  this.assertUndefined('Window opened prematurely.', popup);
 
-  // Open the window
-  var ranDefaultAction = openerOnClick();
-  this.assertTrue('Window not opened', opened);
-  this.assertFalse('Ran browser default action on open', ranDefaultAction);
-  this.assertNotNull('Window was null', popup.win_);
-  this.assertEquals('Url incorrect', 'destination', popup.win_.url_);
-  this.assertEquals('Target incorrect', '_blank', popup.win_.target_);
-  this.assertEquals('Options incorrect', 'options', popup.win_.options_);
+  captures['oauth.open'].call({f: 'from', callback: function(id) {
+    opened = true;
+    cbid = id;
+  }}, 'destination', 'options');
+  this.assertNotUndefined('Window not opened.', popup);
+  this.assertTrue('Opened callback not fired.', opened);
+  this.assertEquals('Url incorrect.', 'destination', popup.url_);
+  this.assertEquals('Target incorrect.', '_blank', popup.target_);
+  this.assertEquals('Options incorrect.', 'options', popup.options_);
 
   // Wait a bit for our events to run
   window.incrementTime(1000);
-  this.assertFalse('closer callback called early', closed);
+  this.assertUndefined('close callback called early.', this.rpcArguments);
 
   // User or site closes window
-  popup.win_.close();
+  popup.close();
   window.incrementTime(100);
-  this.assertTrue('Closer callback not called', closed);
+  this.assertEquals('Closer callback not called.', ['from', 'oauth.close', null, cbid], this.rpcArguments);
+
+  delete this.rpcArguments;
+  window.incrementTime(1000);
+  this.assertUndefined('Timer not cancelled.', this.rpcArguments);
 };
 
 PopupTest.prototype.testPopup_userClick = function() {
-  var opened = false;
-  var open = function() {
+  var opened = false, closed = false;
+  // Create the popup
+  var popup = new gadgets.oauth.Popup('destination', 'options', function() {
     opened = true;
-  };
-  var closed = false;
-  var close = function() {
+  }, function() {
     closed = true;
-  };
-  // Create the popup
-  var popup = new gadgets.oauth.Popup('destination', 'options', open, close);
+  });
   var openerOnClick = popup.createOpenerOnClick();
   var closerOnClick = popup.createApprovedOnClick();
 
@@ -93,28 +111,3 @@ PopupTest.prototype.testPopup_userClick 
   this.assertFalse(ranDefaultAction);
   this.assertTrue('Closer callback not called', closed);
 };
-
-PopupTest.prototype.testTimerCancelled = function() {
-  var open = function() {};
-  var closeCount = 0;
-  var close = function() {
-    ++closeCount;
-  };
-
-  // Create the popup
-  var popup = new gadgets.oauth.Popup('destination', 'options', open, close);
-  var openerOnClick = popup.createOpenerOnClick();
-  var closerOnClick = popup.createApprovedOnClick();
-
-  // Open the window
-  openerOnClick();
-
-  // Close the window
-  popup.win_.close();
-
-  // Wait a bit for our events to run
-  window.incrementTime(1000);
-  this.assertEquals('Wrong number of calls to close', 1, closeCount);
-  window.incrementTime(1000);
-  this.assertEquals('timer not cancelled', 1, closeCount);
-};