You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by John Hjelmstad <fa...@google.com> on 2008/06/27 23:06:15 UTC

Re: svn commit: r672393 - in /incubator/shindig/trunk: features/rpc/ java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ javascript/container/

+jschorr in case there are comments.

On Fri, Jun 27, 2008 at 2:01 PM, <jo...@apache.org> wrote:

> Author: johnh
> Date: Fri Jun 27 14:01:20 2008
> New Revision: 672393
>
> URL: http://svn.apache.org/viewvc?rev=672393&view=rev
> Log:
> Cleaned up gadgets.rpc library implementation, and implemented fast IE
> transport layer.
>
> Credit:
>        Joseph Schorr (jschorr@google.com) and I implemented this together,
> but he
>        really did all the hard work, as well as developing the NIX
> technique's
>        fundamentals. Huge thanks to Joseph!
>
> Details:
>        * Created a new relayChannel for Firefox frameElement technique,
>          making its implementation more linear to read.
>        * Consolidated all transport-specific code in setupChannel(),
> setupFrame(),
>          and callX(), where X = relay type. This refactoring makes setup
> and use
>          of each transport more clear, and makes it possible in later
> revisions
>          to separate out each method as its own class. In theory, if we can
> trust
>          things like User-Agent headers, we can use this to reduce rpc.js
> code
>          size significantly by only including the needed transport(s).
>        * Implemented "NIX" - Native IE XDC. This method exploits the fact
>          that in IE6 and IE7 the window.opener property can be both set and
>          read across domain boundaries for an IFRAME, and that any object
>          can be passed through it. In particular, functions implementing
>          message passing can be passed. These can't be JS functions,
> however,
>          since those can leak sender context. So, VBScript (COM) objects
>          wrapping these are passed to maintain context isolation.
>          - Requires for IE6/7 that rpc.js be included in the container
>            at load time, not dynamically. TODO added to consider detecting
>            whether dynamic loading is happening (at which point we should
>            fall back to IFPC).
>          - Message channel handshake *and* message passing validated
>            using authToken, as with other channels.
>          - Impl requires that gadget call container first - else IFPC
>            is used. This is the same as the frameElement technique. We
>            could add a setInterval()-based initialization routine later
>            if needed. To date, the only gadgets.rpc calls made from
>            container to gadget without gadget to container first are
>            in theory pubsub messages.
>          - Extensive documentation on this technique and the others
>            added to the comments, since they're stripped out in the
>            majority case.
>        * Implemented quick-and-dirty performance testing page verifying
>          the library works and giving basic performance metrics.
>          This needs to be improved! But does the trick for now...
>
> Testing/Data:
>
> Library verified on the following browsers with the RPC test page. For
> each,
> a "bandwidth" measure was taken by averaging the results of 6 G2C and C2G
> runs
> with 100 messages being passed of 1K apiece. Units are 1K messages/sec.
> The "latency" value is the average amount of time taken, over 6 G2C and C2G
> runs,
> to send 1 1K message. Happily, for all browsers save Safari (soon due to
> get
> window.postMessage), latency is around or below time required to achieve
> a cross-domain call without user-perceived delay.
>
>        * Firefox 3 (WPM) - bandwidth: 88 kB/sec; latency: 18ms.
>        * Firefox 2 (FE)  - bandwidth: 80 kB/sec; latency: 15ms.
>        * Opera 9   (WPM) - bandwidth: 85 kB/sec; latency: 7ms.
>        * IE6       (NIX) - bandwidth: 64 kB/sec; latency: 18ms.
>        * IE7       (NIX) - bandwidth: 64 kB/sec; latency: 22ms.
>        * Safari 3 (IFPC) - bandwidth: ?6-8kB/sec; latency: ?100-200ms.
>          - Safari is somewhat flaky with the RPC test page, before
>            and after the rpc.js change. Multiple messages seem to
>            confuse it from time to time.
>
>
> Added:
>    incubator/shindig/trunk/javascript/container/rpctest_container.html
>    incubator/shindig/trunk/javascript/container/rpctest_gadget.html
>    incubator/shindig/trunk/javascript/container/rpctest_perf.js
> Modified:
>    incubator/shindig/trunk/features/rpc/rpc.js
>
>  incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
>
> Modified: incubator/shindig/trunk/features/rpc/rpc.js
> URL:
> http://svn.apache.org/viewvc/incubator/shindig/trunk/features/rpc/rpc.js?rev=672393&r1=672392&r2=672393&view=diff
>
> ==============================================================================
> --- incubator/shindig/trunk/features/rpc/rpc.js (original)
> +++ incubator/shindig/trunk/features/rpc/rpc.js Fri Jun 27 14:01:20 2008
> @@ -18,7 +18,7 @@
>
>  /**
>  * @fileoverview Remote procedure call library for gadget-to-container,
> - * container-to-gadget, and gadget-to-gadget communication.
> + * container-to-gadget, and gadget-to-gadget (thru container)
> communication.
>  */
>
>  var gadgets = gadgets || {};
> @@ -29,6 +29,30 @@
>  * @name gadgets.rpc
>  */
>  gadgets.rpc = function() {
> +  // General constants.
> +  var CALLBACK_NAME = '__cb';
> +  var DEFAULT_NAME = '';
> +
> +  // Consts for FrameElement.
> +  var FE_G2C_CHANNEL = '__g2c_rpc';
> +  var FE_C2G_CHANNEL = '__c2g_rpc';
> +
> +  // Consts for NIX. VBScript doesn't
> +  // allow items to start with _ for some reason,
> +  // so we need to make these names quite unique, as
> +  // they will go into the global namespace.
> +  var NIX_WRAPPER = 'GRPC____NIXVBS_wrapper';
> +  var NIX_GET_WRAPPER = 'GRPC____NIXVBS_get_wrapper';
> +  var NIX_HANDLE_MESSAGE = 'GRPC____NIXVBS_handle_message';
> +  var NIX_CREATE_CHANNEL = 'GRPC____NIXVBS_create_channel';
> +  var NIX_ID_FIELD = 'GRPC____NIXVBS_container';
> +
> +  // JavaScript reference to the NIX VBScript wrappers.
> +  // Gadgets will have but a single channel under
> +  // nix_channels['..'] while containers will have a channel
> +  // per gadget stored under the gadget's ID.
> +  var nix_channels = {};
> +
>   var services = {};
>   var iframePool = [];
>   var relayUrl = {};
> @@ -36,28 +60,135 @@
>   var authToken = {};
>   var callId = 0;
>   var callbacks = {};
> +  var setup = {};
> +
> +  var params = {};
> +
> +  // Load the authentication token for speaking to the container
> +  // from the gadget's parameters, or default to '0' if not found.
> +  if (gadgets.util) {
> +    params = gadgets.util.getUrlParameters();
> +  }
>
> -  var params = gadgets.util.getUrlParameters();
>   authToken['..'] = params.rpctoken || params.ifpctok || 0;
>
> -  // Pick the most efficient RPC relay mechanism
> -  var relayChannel = typeof document.postMessage === 'function' ? 'dpm' :
> -                     typeof window.postMessage === 'function' ? 'wpm' :
> +  // Pick the most efficient RPC relay mechanism:
> +  //
> +  // * For those browsers that support native messaging (various
> implementations
> +  //   of the HTML5 postMessage method), use that. Officially defined at
> +  //
> http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html.
> +  //
> +  //   postMessage is a native implementation of XDC. A page registers
> that
> +  //   it would like to receive messages by listening the the "message"
> event
> +  //   on the window (document in DPM) object. In turn, another page can
> +  //   raise that event by calling window.postMessage
> (document.postMessage
> +  //   in DPM) with a string representing the message and a string
> +  //   indicating on which domain the receiving page must be to receive
> +  //   the message. The target page will then have its "message" event
> raised
> +  //   if the domain matches and can, in turn, check the origin of the
> message
> +  //   and process the data contained within.
> +  //
> +  //     wpm: postMessage on the window object.
> +  //        - Internet Explorer 8+
> +  //        - Safari (latest nightlies as of 26/6/2008)
> +  //        - Firefox 3+
> +  //        - Opera 9+
> +  //
> +  //     dpm: postMessage on the document object.
> +  //        - Opera 8+
> +  //
> +  // * For Internet Explorer before version 8, the security model allows a
> +  //   child to set the value of the "opener" property on the parent
> window
> +  //   and vice versa. This method is dubbed "Native IE XDC" (NIX).
> +  //
> +  //   This method works by placing a handler object in the "opener"
> property
> +  //   of a gadget when the container sets up the authentication
> information
> +  //   for that gadget (by calling setAuthToken(...)). At that point, a
> NIX
> +  //   wrapper is created and placed into the gadget by calling
> +  //   theframe.contentWindow.opener = wrapper. Note that as a result, NIX
> can
> +  //   only be used by a container to call a particular gadget *after*
> that
> +  //   gadget has called the container at least once via NIX.
> +  //
> +  //   The NIX wrappers in this RPC implementation are instances of a
> VBScript
> +  //   class that is created when this implementation loads. The reason
> for
> +  //   using a VBScript class stems from the fact that any object can be
> passed
> +  //   into the opener property.
> +  //   While this is a good thing, as it lets us pass functions and setup
> a true
> +  //   bidirectional channel via callbacks, it opens a potential security
> hole
> +  //   by which the other page can get ahold of the "window" or "document"
> +  //   objects in the parent page and in turn wreak havok. This is due to
> the
> +  //   fact that any JS object useful for establishing such a
> bidirectional
> +  //   channel (such as a function) can be used to access a function
> +  //   (eg. obj.toString, or a function itself) created in a specific
> context,
> +  //   in particular the global context of the sender. Suppose container
> +  //   domain C passes object obj to gadget on domain G. Then the gadget
> can
> +  //   access C's global context using:
> +  //   var parentWindow = (new obj.toString.constructor("return
> window;"))();
> +  //   Nulling out all of obj's properties doesn't fix this, since IE
> helpfully
> +  //   restores them to their original values if you do something like:
> +  //   delete obj.toString; delete obj.toString;
> +  //   Thus, we wrap the necessary functions and information inside a
> VBScript
> +  //   object. VBScript objects in IE, like DOM objects, are in fact COM
> +  //   wrappers when used in JavaScript, so we can safely pass them around
> +  //   without worrying about a breach of context while at the same time
> +  //   allowing them to act as a pass-through mechanism for information
> +  //   and function calls. The implementation details of this VBScript
> wrapper
> +  //   can be found in the setupChannel() method below.
> +  //
> +  //     nix: Internet Explorer-specific window.opener trick.
> +  //       - Internet Explorer 6
> +  //       - Internet Explorer 7
> +  //
> +  // * For Gecko-based browsers, the security model allows a child to call
> a
> +  //   function on the frameElement of the iframe, even if the child is in
> +  //   a different domain. This method is dubbed "frameElement" (fe).
> +  //
> +  //   The ability to add and call such functions on the frameElement
> allows
> +  //   a bidirectional channel to be setup via the adding of simple
> function
> +  //   references on the frameElement object itself. In this
> implementation,
> +  //   when the container sets up the authentication information for that
> gadget
> +  //   (by calling setAuth(...)) it as well adds a special function on the
> +  //   gadget's iframe. This function can then be used by the gadget to
> send
> +  //   messages to the container. In turn, when the gadget tries to send a
> +  //   message, it checks to see if this function has its own function
> stored
> +  //   that can be used by the container to call the gadget. If not, the
> +  //   function is created and subsequently used by the container.
> +  //   Note that as a result, FE can only be used by a container to call a
> +  //   particular gadget *after* that gadget has called the container at
> +  //   least once via FE.
> +  //
> +  //     fe: Gecko-specific frameElement trick.
> +  //        - Firefox 1+
> +  //
> +  // * For all others, we have a fallback mechanism known as "ifpc". IFPC
> +  //   exploits the fact that while same-origin policy prohibits a frame
> from
> +  //   accessing members on a window not in the same domain, that frame
> can,
> +  //   however, navigate the window heirarchy (via parent). This is
> exploited by
> +  //   having a page on domain A that wants to talk to domain B create an
> iframe
> +  //   on domain B pointing to a special relay file and with a message
> encoded
> +  //   after the hash (#). This relay, in turn, finds the page on domain
> B, and
> +  //   can call a receipt function with the message given to it. The relay
> URL
> +  //   used by each caller is set via the gadgets.rpc.setRelayUrl(..) and
> +  //   *must* be called before the call method is used.
> +  //
> +  //     ifpc: Iframe-based method, utilizing a relay page, to send a
> message.
> +  //
> +  var relayChannel = typeof window.postMessage === 'function' ? 'wpm' :
> +                    typeof document.postMessage === 'function' ? 'dpm' :
> +                    window.ActiveXObject ? 'nix' :
> +                     navigator.product === 'Gecko' ? 'fe' :
>                      'ifpc';
> -  if (relayChannel === 'dpm' || relayChannel === 'wpm') {
> -    window.addEventListener('message', function(packet) {
> -      // TODO validate packet.domain for security reasons
> -      process(gadgets.json.parse(packet.data));
> -    }, false);
> -  }
>
> -  // Default RPC handler
> -  services[''] = function() {
> +  // Conduct any setup necessary for the chosen channel.
> +  setupChannel();
> +
> +  // Create the Default RPC handler.
> +  services[DEFAULT_NAME] = function() {
>     throw new Error('Unknown RPC service: ' + this.s);
>   };
>
> -  // Special RPC handler for callbacks
> -  services['__cb'] = function(callbackId, result) {
> +  // Create a Special RPC handler for callbacks.
> +  services[CALLBACK_NAME] = function(callbackId, result) {
>     var callback = callbacks[callbackId];
>     if (callback) {
>       delete callbacks[callbackId];
> @@ -66,6 +197,147 @@
>   };
>
>   /**
> +   * Conducts any initial global work necessary to setup the
> +   * channel type chosen.
> +   */
> +  function setupChannel() {
> +    // If the channel type is one of the native
> +    // postMessage based ones, setup the handler to receive
> +    // messages.
> +    if (relayChannel === 'dpm' || relayChannel === 'wpm') {
> +      window.addEventListener('message', function(packet) {
> +        // TODO validate packet.domain for security reasons
> +        process(gadgets.json.parse(packet.data));
> +      }, false);
> +    }
> +
> +    // If the channel type is NIX, we need to ensure the
> +    // VBScript wrapper code is in the page and that the
> +    // global Javascript handlers have been set.
> +    if (relayChannel === 'nix') {
> +      // VBScript methods return a type of 'unknown' when
> +      // checked via the typeof operator in IE. Fortunately
> +      // for us, this only applies to COM objects, so we
> +      // won't see this for a real Javascript object.
> +      if (typeof window[NIX_GET_WRAPPER] !== 'unknown') {
> +        window[NIX_HANDLE_MESSAGE] = function(data) {
> +          process(gadgets.json.parse(data));
> +        };
> +
> +        window[NIX_CREATE_CHANNEL] = function(name, channel, token) {
> +          // Verify the authentication token of the gadget trying
> +          // to create a channel for us.
> +          if (authToken[name] == token) {
> +            nix_channels[name] = channel;
> +          }
> +        };
> +
> +        // Inject the VBScript code needed.
> +        var vbscript =
> +          '<scr' + 'ipt language="VBScript">'
> +          // We create a class to act as a wrapper for
> +          // a Javascript call, to prevent a break in of
> +          // the context.
> +          + 'Class ' + NIX_WRAPPER + '\n '
> +
> +          // An internal member for keeping track of the
> +          // name of the document (container or gadget)
> +          // for which this wrapper is intended. For
> +          // those wrappers created by gadgets, this is not
> +          // used (although it is set to "..")
> +          + 'Private m_Intended\n'
> +
> +          // Method for internally setting the value
> +          // of the m_Intended property.
> +          + 'Public Sub SetIntendedName(name)\n '
> +          + 'm_Intended = name\n'
> +          + 'End Sub\n'
> +
> +          // A wrapper method which actually causes a
> +          // message to be sent to the other context.
> +          + 'Public Sub SendMessage(data)\n '
> +          + NIX_HANDLE_MESSAGE + '(data)\n'
> +          + 'End Sub\n'
> +
> +          // Method for setting up the container->gadget
> +          // channel. Not strictly needed in the gadget's
> +          // wrapper, but no reason to get rid of it. Note here
> +          // that we pass the intended name to the NIX_CREATE_CHANNEL
> +          // method so that it can save the channel in the proper place
> +          // *and* verify the channel via the authentication token passed
> +          // here.
> +          + 'Public Sub CreateChannel(channel, auth)\n '
> +          + 'Call ' + NIX_CREATE_CHANNEL + '(m_Intended, channel, auth)\n'
> +          + 'End Sub\n'
> +
> +          // An empty field with a unique identifier to
> +          // prevent the code from confusing this wrapper
> +          // with a run-of-the-mill value found in window.opener.
> +          + 'Public Sub ' + NIX_ID_FIELD + '()\n '
> +          + 'End Sub\n'
> +          + 'End Class\n '
> +
> +          // Function to get a reference to the wrapper.
> +          + 'Function ' + NIX_GET_WRAPPER + '(name)\n'
> +          + 'Dim wrap\n'
> +          + 'Set wrap = New ' + NIX_WRAPPER + '\n'
> +          + 'wrap.SetIntendedName name\n'
> +          + 'Set ' + NIX_GET_WRAPPER + ' = wrap\n'
> +          + 'End Function'
> +          + '</scr' + 'ipt>'
> +
> +          // Resets the "default" scripting language in IE back
> +          // to Javascript. This is needed or else any script
> +          // tags without a proper Language="..." will be treated as
> +          // VBScript once this code is added to the document.
> +          + '<scri' + 'pt language="JScript"></scr' + 'ipt>';
> +
> +        // Note that this code can only be run once the document has
> +        // fully loaded.
> +        // TODO: Perhaps add some sort of check here for this?
> +        document.write(vbscript);
> +      }
> +    }
> +  }
> +
> +  /**
> +   * Conducts any frame-specific work necessary to setup
> +   * the channel type chosen. This method is called when
> +   * the container page first registers the gadget in the
> +   * RPC mechanism. Gadgets, in turn, will complete the setup
> +   * of the channel once they send their first messages.
> +   */
> +  function setupFrame(frameId) {
> +    if (setup[frameId]) {
> +      return;
> +    }
> +
> +    if (relayChannel === 'fe') {
> +      try {
> +        var frame = document.getElementById(frameId);
> +        frame[FE_G2C_CHANNEL] = function(args) {
> +          process(gadgets.json.parse(args));
> +        };
> +      } catch (e) {
> +        // Something went wrong. System will fallback to
> +        // IFPC.
> +      }
> +    }
> +
> +    if (relayChannel === 'nix') {
> +      try {
> +        var frame = document.getElementById(frameId);
> +        frame.contentWindow.opener = window[NIX_GET_WRAPPER](frameId);
> +      } catch (e) {
> +        // Something went wrong. System will fallback to
> +        // IFPC.
> +      }
> +    }
> +
> +    setup[frameId] = true;
> +  }
> +
> +  /**
>    * Encodes arguments for the legacy IFPC wire format.
>    *
>    * @param {Object} args
> @@ -86,8 +358,17 @@
>    * @private
>    */
>   function process(rpc) {
> +    //
> +    // RPC object contents:
> +    //   s: Service Name
> +    //   f: From
> +    //   c: The callback ID or 0 if none.
> +    //   a: The arguments for this RPC call.
> +    //   t: The authentication token.
> +    //
>     if (rpc && typeof rpc.s === 'string' && typeof rpc.f === 'string' &&
>         rpc.a instanceof Array) {
> +
>       // Validate auth token.
>       if (authToken[rpc.f]) {
>         // We allow type coercion here because all the url params are
> strings.
> @@ -96,43 +377,158 @@
>         }
>       }
>
> -      // The Gecko engine used by FireFox etc. allows an IFrame to
> directly call
> -      // methods on the frameElement property added by the container page
> even
> -      // if their domains don't match.
> -      // Here we try to set up a relay channel using the frameElement
> technique
> -      // to greatly reduce the latency of cross-domain calls if the
> postMessage
> -      // method is not supported.
> -      if (relayChannel === 'ifpc') {
> -        if (rpc.f === '..') {
> -          // Container-to-gadget call
> -          try {
> -            var fel = window.frameElement;
> -            if (typeof fel.__g2c_rpc === 'function' &&
> -                typeof fel.__g2c_rpc.__c2g_rpc != 'function') {
> -              fel.__g2c_rpc.__c2g_rpc = function(args) {
> -                process(gadgets.json.parse(args));
> -              };
> -            }
> -          } catch (e) {
> -          }
> -        } else {
> -          // Gadget-to-container call
> -          var iframe = document.getElementById(rpc.f);
> -          if (iframe && typeof iframe.__g2c_rpc != 'function') {
> -            iframe.__g2c_rpc = function(args) {
> +      // Call the requested RPC service.
> +      var result = (services[rpc.s] ||
> +                    services[DEFAULT_NAME]).apply(rpc, rpc.a);
> +
> +      // If there is a callback for this service, initiate it as well.
> +      if (rpc.c) {
> +        gadgets.rpc.call(rpc.f, CALLBACK_NAME, null, rpc.c, result);
> +      }
> +    }
> +  }
> +
> +  /**
> +   * Attempts to conduct an RPC call to the specified
> +   * target with the specified data via the NIX
> +   * method. If this method fails, the system attempts again
> +   * using the known default of IFPC.
> +   *
> +   * @param {String} targetId Module Id of the RPC service provider.
> +   * @param {String} from Module Id of the calling provider.
> +   * @param {Object} rpcData The RPC data for this call.
> +   */
> +  function callNIX(targetId, from, rpcData) {
> +    try {
> +      if (from != '..') {
> +        // Call from gadget to the container.
> +        var handler = nix_channels['..'];
> +
> +        // If the gadget has yet to retrieve a reference to
> +        // the NIX handler, try to do so now. We don't do a
> +        // typeof(window.opener[NIX_ID_FIELD]) check here
> +        // because it means accessing that field on the COM object, which,
> +        // being an internal function reference, is not allowed.
> +        // "in" works because it merely checks for the prescence of
> +        // the key, rather than actually accessing the object's property.
> +        if (!handler && window.opener && NIX_ID_FIELD in window.opener) {
> +          handler = nix_channels['..'] = window.opener;
> +
> +          // Create the channel to the parent/container. We pass both our
> +          // own wrapper and our authentication token for verification.
> +          handler.CreateChannel(window[NIX_GET_WRAPPER]('..'),
> authToken['..']);
> +        }
> +
> +        // If we have a handler, call it.
> +        if (handler) {
> +          handler.SendMessage(rpcData);
> +          return;
> +        }
> +      } else {
> +        // Call from container to a gadget[targetId].
> +
> +        // If we have a handler, call it.
> +        if (nix_channels[targetId]) {
> +          nix_channels[targetId].SendMessage(rpcData);
> +          return;
> +        }
> +      }
> +    } catch (e) {
> +    }
> +
> +    // If we have reached this point, something has failed
> +    // with the NIX method, so we default to using
> +    // IFPC for this call.
> +    callIFPC(targetId, from, rpcData);
> +  }
> +
> +  /**
> +   * Attempts to conduct an RPC call to the specified
> +   * target with the specified data via the FrameElement
> +   * method. If this method fails, the system attempts again
> +   * using the known default of IFPC.
> +   *
> +   * @param {String} targetId Module Id of the RPC service provider.
> +   * @param {String} from Module Id of the calling provider.
> +   * @param {Object} rpcData The RPC data for this call.
> +   */
> +  function callFrameElement(targetId, from, rpcData) {
> +    try {
> +      if (from != '..') {
> +        // Call from gadget to the container.
> +        var fe = window.frameElement;
> +
> +        if (typeof fe[FE_G2C_CHANNEL] === 'function') {
> +          // Complete the setup of the FE channel if need be.
> +          if (typeof fe[FE_G2C_CHANNEL][FE_C2G_CHANNEL] !== 'function') {
> +            fe[FE_G2C_CHANNEL][FE_C2G_CHANNEL] = function(args) {
>               process(gadgets.json.parse(args));
>             };
>           }
> +
> +          // Conduct the RPC call.
> +          fe[FE_G2C_CHANNEL](rpcData);
> +          return;
>         }
> -      }
> +      } else {
> +        // Call from container to gadget[targetId].
> +        var frame = document.getElementById(targetId);
>
> -      var result = (services[rpc.s] || services['']).apply(rpc, rpc.a);
> -      if (rpc.c) {
> -        gadgets.rpc.call(rpc.f, '__cb', null, rpc.c, result);
> +        if (typeof frame[FE_G2C_CHANNEL] === 'function' &&
> +            typeof frame[FE_G2C_CHANNEL][FE_C2G_CHANNEL] === 'function') {
> +
> +          // Conduct the RPC call.
> +          frame[FE_G2C_CHANNEL][FE_C2G_CHANNEL](rpcData);
> +          return;
> +        }
>       }
> +    } catch (e) {
> +    }
> +
> +    // If we have reached this point, something has failed
> +    // with the FrameElement method, so we default to using
> +    // IFPC for this call.
> +    callIFPC(targetId, from, rpcData);
> +  }
> +
> +  /**
> +   * Conducts an RPC call to the specified
> +   * target with the specified data via the IFPC
> +   * method.
> +   *
> +   * @param {String} targetId Module Id of the RPC service provider.
> +   * @param {String} from Module Id of the calling provider.
> +   * @param {Object} rpcData The RPC data for this call.
> +   */
> +  function callIFPC(targetId, from, rpcData) {
> +    // Retrieve the relay file used by IFPC. Note that
> +    // this must be set before the call, and so we conduct
> +    // an extra check to ensure it is not blank.
> +    var relay = gadgets.rpc.getRelayUrl(targetId);
> +
> +    if (!relay) {
> +      throw new Error('No relay file assigned for IFPC');
>     }
> +
> +    // The RPC mechanism supports two formats for IFPC (legacy and
> current).
> +    var src = null;
> +    if (useLegacyProtocol[targetId]) {
> +      // Format: #iframe_id&callId&num_packets&packet_num&block_of_data
> +      src = [relay, '#', encodeLegacyData([from, callId, 1, 0,
> +             encodeLegacyData([from, serviceName, '', '', from].concat(
> +               Array.prototype.slice.call(arguments, 3)))])].join('');
> +    } else {
> +      // Format: #targetId & sourceId@callId & packetNum & packetId &
> packetData
> +      src = [relay, '#', targetId, '&', from, '@', callId,
> +             '&1&0&', encodeURIComponent(rpcData)].join('');
> +    }
> +
> +    // Conduct the IFPC call by creating the Iframe with
> +    // the relay URL and appended message.
> +    emitInvisibleIframe(src);
>   }
>
> +
>   /**
>    * Helper function to emit an invisible IFrame.
>    * @param {String} src SRC attribute of the IFrame to emit.
> @@ -221,6 +617,15 @@
>      * @member gadgets.rpc
>      */
>     register: function(serviceName, handler) {
> +      if (serviceName == CALLBACK_NAME) {
> +        throw new Error("Cannot overwrite callback service");
> +      }
> +
> +      if (serviceName == DEFAULT_NAME) {
> +        throw new Error("Cannot overwrite default service:"
> +                        + " use registerDefault");
> +      }
> +
>       services[serviceName] = handler;
>     },
>
> @@ -231,6 +636,15 @@
>      * @member gadgets.rpc
>      */
>     unregister: function(serviceName) {
> +      if (serviceName == CALLBACK_NAME) {
> +        throw new Error("Cannot delete callback service");
> +      }
> +
> +      if (serviceName == DEFAULT_NAME) {
> +        throw new Error("Cannot delete default service:"
> +                        + " use unregisterDefault");
> +      }
> +
>       delete services[serviceName];
>     },
>
> @@ -272,12 +686,14 @@
>       if (callback) {
>         callbacks[callId] = callback;
>       }
> -      var from;
> +
> +      // Default to the container calling.
> +      var from = '..';
> +
>       if (targetId === '..') {
>         from = window.name;
> -      } else {
> -        from = '..';
>       }
> +
>       // Not used by legacy, create it anyway...
>       var rpcData = gadgets.json.stringify({
>         s: serviceName,
> @@ -287,53 +703,37 @@
>         t: authToken[targetId]
>       });
>
> +      var channelType = relayChannel;
> +
> +      // If we are told to use the legacy format, then we must
> +      // default to IFPC.
>       if (useLegacyProtocol[targetId]) {
> -        relayChannel = 'ifpc';
> +        channelType = 'ifpc';
>       }
>
> -      switch (relayChannel) {
> -      case 'dpm': // use document.postMessage
> -        var targetDoc = targetId === '..' ? parent.document :
> -                                            frames[targetId].document;
> -        targetDoc.postMessage(rpcData);
> -        break;
> -      case 'wpm': // use window.postMessage
> -        var targetWin = targetId === '..' ? parent : frames[targetId];
> -        targetWin.postMessage(rpcData, "*");
> -        break;
> -      default: // use 'ifpc' as a fallback mechanism
> -        var relay = gadgets.rpc.getRelayUrl(targetId);
> -        // TODO split message if too long
> -        var src;
> -        if (useLegacyProtocol[targetId]) {
> -          // #iframe_id&callId&num_packets&packet_num&block_of_data
> -          src = [relay, '#', encodeLegacyData([from, callId, 1, 0,
> -                 encodeLegacyData([from, serviceName, '', '',
> from].concat(
> -                 Array.prototype.slice.call(arguments, 3)))])].join('');
> -        } else {
> -          // Try the frameElement channel if available
> -          try {
> -            if (from === '..') {
> -              // Container-to-gadget
> -              var iframe = document.getElementById(targetId);
> -              if (typeof iframe.__g2c_rpc.__c2g_rpc === 'function') {
> -                iframe.__g2c_rpc.__c2g_rpc(rpcData);
> -                return;
> -              }
> -            } else {
> -              // Gadget-to-container
> -              if (typeof window.frameElement.__g2c_rpc === 'function') {
> -                window.frameElement.__g2c_rpc(rpcData);
> -                return;
> -              }
> -            }
> -          } catch (e) {
> -          }
> -          // # targetId & sourceId@callId & packetNum & packetId &
> packetData
> -          src = [relay, '#', targetId, '&', from, '@', callId,
> -                 '&1&0&', encodeURIComponent(rpcData)].join('');
> -        }
> -        emitInvisibleIframe(src);
> +      switch (channelType) {
> +        case 'dpm': // use document.postMessage.
> +          var targetDoc = targetId === '..' ? parent.document :
> +                                              frames[targetId].document;
> +          targetDoc.postMessage(rpcData);
> +          break;
> +
> +        case 'wpm': // use window.postMessage.
> +          var targetWin = targetId === '..' ? parent : frames[targetId];
> +          targetWin.postMessage(rpcData, "*");
> +          break;
> +
> +        case 'nix': // use NIX.
> +          callNIX(targetId, from, rpcData);
> +          break;
> +
> +        case 'fe': // use FrameElement.
> +          callFrameElement(targetId, from, rpcData);
> +          break;
> +
> +        default: // use 'ifpc' as a fallback mechanism.
> +          callIFPC(targetId, from, rpcData);
> +          break;
>       }
>     },
>
> @@ -372,15 +772,13 @@
>      */
>     setAuthToken: function(targetId, token) {
>       authToken[targetId] = token;
> +      setupFrame(targetId);
>     },
>
>     /**
>      * Gets the RPC relay mechanism.
> -     * @return {String} RPC relay mechanism. Supported types:
> -     *                  'wpm' - Use window.postMessage (defined by HTML5)
> -     *                  'dpm' - Use document.postMessage (defined by an
> early
> -     *                          draft of HTML5 and implemented by Opera)
> -     *                  'ifpc' - Use invisible IFrames
> +     * @return {String} RPC relay mechanism. See above for
> +     *   a list of supported types.
>      *
>      * @member gadgets.rpc
>      */
>
> Modified:
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> URL:
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java?rev=672393&r1=672392&r2=672393&view=diff
>
> ==============================================================================
> ---
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> (original)
> +++
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> Fri Jun 27 14:01:20 2008
> @@ -221,7 +221,8 @@
>           "a {color:#0000cc;}a:visited {color:#551a8b;}" +
>           "a:active {color:#ff0000;}" +
>           "body{margin: 0px;padding: 0px;background-color:white;}" +
> -          "</style></head>";
> +          "</style><base>" + gadget.getSpec().getUrl().toString() +
> +          "</base></head>";
>     markup.append(boilerPlate);
>     LocaleSpec localeSpec = gadget.getSpec().getModulePrefs().getLocale(
>         gadget.getContext().getLocale());
>
> Added: incubator/shindig/trunk/javascript/container/rpctest_container.html
> URL:
> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_container.html?rev=672393&view=auto
>
> ==============================================================================
> --- incubator/shindig/trunk/javascript/container/rpctest_container.html
> (added)
> +++ incubator/shindig/trunk/javascript/container/rpctest_container.html Fri
> Jun 27 14:01:20 2008
> @@ -0,0 +1,90 @@
> +<!--
> +  Simple page for testing gadgets.rpc performance.
> +  Allows you to add a simulated "gadget" (in this case just a static
> +  HTML page which loads gadgets.rpc also), and pass some
> +  specified number of random messages of specified size to
> +  and from it.
> +
> +  A simple recommended setup is to start up two instances of
> +  the Shindig Gadgets Server on two separate ports to test
> +  "real" cross-domain communication, since port is factored
> +  into the same-domain policy enforced by browsers.
> +
> +  If your servers are on localhost:8080 and localhost:8081, then hit:
> +  http://localhost:8080/gadgets/files/container/rpctest_container.html? \
> +  http://localhost:8081/gadgets/files/container/rpctest_gadget.html& \
> +
> http://localhost:8081/gadgets/files/container/rpc_relay.uncompressed.html
> +
> +  (Note the backslashes should be removed, as they exist for formatting
> only.)
> +
> +  There are two arguments, separated by an ampersand:
> +  1. URL of the "gadget" test page.
> +  2. URL of the "gadget" test page's RPC relay (for browsers requiring
> it).
> +-->
> +<html>
> +  <head>
> +    <title>gadgets.rpc Performance Tests: Container</title>
> +    <script src="../../js/rpc.js?c=1&debug=1"></script>
> +    <script src="rpctest_perf.js"></script>
> +    <script>
> +      function initTest() {
> +        var container = document.getElementById("container");
> +
> +        // query string is assumed to be the "gadget" URL as arg 1,
> +        // and optionally the relay URL as arg 2
> +        var pageArgs = window.location.search.substring(1).split('&');
> +        var gadgetUrl = pageArgs[0];
> +        var secret = Math.round(Math.random()*10000000);
> +        if (pageArgs[1]) {
> +          gadgets.rpc.setRelayUrl('gadget', pageArgs[1]);
> +        }
> +        var containerRelay = pageArgs[2] || '';
> +        container.innerHTML = "<iframe id='gadget' name='gadget'
> height=300 width=300 src='" + gadgetUrl + "?parent=" + containerRelay +
> "#rpctoken=" + secret + "'></iframe>";
> +        gadgets.rpc.setAuthToken('gadget', secret);
> +
> +        initPerfTest();
> +      };
> +    </script>
> +  </head>
> +  <body onload="initTest();">
> +    <div>gadgets.rpc Performance: Container Page</div><hr/>
> +    <div>Test<br/>
> +      <ul>
> +        <li>Number of messages to send:
> +          <select name="num_msgs" id="num_msgs">
> +            <option value="1">1</option>
> +            <option value="10">10</option>
> +            <option value="100" selected>100</option>
> +            <option value="1000">1000</option>
> +          </select>
> +        </li>
> +        <li>Message size:
> +          <select name="msg_size" id="msg_size">
> +            <option value="10">10 B</option>
> +            <option value="100">100 B</option>
> +            <option value="1024" selected>1 kB</option>
> +            <option value="10240">10 kB</option>
> +            <option value="102400">100 kB</option>
> +            <option value="1048576">1 MB</option>
> +          </select>
> +        </li>
> +        <li>
> +          <input type="button" value="Start The Test!"
> onclick="runPerfTest('gadget');" />
> +        </li>
> +      </ul>
> +    </div>
> +    <div id="test_running" style="display:none;">
> +      Running test...
> +    </div>
> +    <div id="results" style="display:none;">
> +      Results: Gadget-to-Container<br/>
> +      Messages: <span id="results_num_received"></span>, Bytes: <span
> id="results_bytes_received"></span> <span id="in_or_out"></span><br/>
> +      Time elapsed for test run: <span id="results_time_used"></span><br/>
> +      Messages/second: <span id="results_msgs_per_sec"></span><br/>
> +      Bytes/second: <span id="results_bytes_per_sec"></span>
> +    </div>
> +    <hr/>
> +    <div>Gadget:</div>
> +    <div id="container"></div>
> +  </body>
> +</html>
>
> Added: incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> URL:
> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_gadget.html?rev=672393&view=auto
>
> ==============================================================================
> --- incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> (added)
> +++ incubator/shindig/trunk/javascript/container/rpctest_gadget.html Fri
> Jun 27 14:01:20 2008
> @@ -0,0 +1,49 @@
> +<html>
> +  <head>
> +    <title>gadgets.rpc Performance Tests: Gadget</title>
> +    <script src="../../js/rpc.js?c=1&debug=1"></script>
> +    <script src="rpctest_perf.js"></script>
> +    <script>
> +      var containerRelay = window.location.search.substring(1);
> +      gadgets.rpc.setRelayUrl(null, containerRelay);
> +    </script>
> +  </head>
> +  <body onload="initPerfTest();">
> +    <div>gadgets.rpc Performance: "Gadget" page</div><hr/>
> +    <div>Test<br/>
> +      <ul>
> +        <li>Number of messages to send:
> +          <select name="num_msgs" id="num_msgs">
> +            <option value="1">1</option>
> +            <option value="10">10</option>
> +            <option value="100" selected>100</option>
> +            <option value="1000">1000</option>
> +          </select>
> +        </li>
> +        <li>Message size:
> +          <select name="msg_size" id="msg_size">
> +            <option value="10">10 B</option>
> +            <option value="100">100 B</option>
> +            <option value="1024" selected>1 kB</option>
> +            <option value="10240">10 kB</option>
> +            <option value="102400">100 kB</option>
> +            <option value="1048576">1 MB</option>
> +          </select>
> +        </li>
> +        <li>
> +          <input type="button" value="Start The Test!"
> onclick="runPerfTest();" />
> +        </li>
> +      </ul>
> +    </div>
> +    <div id="test_running" style="display:none;">
> +      Running test...
> +    </div>
> +    <div id="results" style="display:none;">
> +      Results: Gadget-to-Container<br/>
> +      Messages: <span id="results_num_received"></span>, Bytes: <span
> id="results_bytes_received"></span> <span id="in_or_out"></span><br/>
> +      Time elapsed for test run: <span id="results_time_used"></span><br/>
> +      Messages/second: <span id="results_msgs_per_sec"></span><br/>
> +      Bytes/second: <span id="results_bytes_per_sec"></span>
> +    </div>
> +  </body>
> +</html>
>
> Added: incubator/shindig/trunk/javascript/container/rpctest_perf.js
> URL:
> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_perf.js?rev=672393&view=auto
>
> ==============================================================================
> --- incubator/shindig/trunk/javascript/container/rpctest_perf.js (added)
> +++ incubator/shindig/trunk/javascript/container/rpctest_perf.js Fri Jun 27
> 14:01:20 2008
> @@ -0,0 +1,93 @@
> +var perfStats = null;
> +var currentRun = {};
> +
> +function perfService(message) {
> +  if (perfStats.numResults++ === 0) {
> +    perfStats.firstMsg = message; // stored since it has "real" start time
> +  }
> +  perfStats.bytesReceived += message.length;
> +};
> +
> +function clearPerfStats(inOrOut) {
> +  perfStats = {
> +    numResults: 0,
> +    bytesReceived: 0,
> +    firstMsg: null
> +  };
> +
> +  document.getElementById("in_or_out").innerHTML = inOrOut;
> +
> +  // hide results fields
> +  document.getElementById("results").style.display = "none";
> +};
> +
> +function completePerfStats() {
> +  perfStats.timeEnded = new Date().getTime();
> +
> +  // get time started from the first sent message
> +  perfStats.timeStarted = perfStats.firstMsg.substr(0,
> perfStats.firstMsg.indexOf(':'));
> +
> +  var timeUsedMs = perfStats.timeEnded - perfStats.timeStarted;
> +
> +  // fill in fields
> +  document.getElementById("results_num_received").innerHTML =
> perfStats.numResults;
> +  document.getElementById("results_bytes_received").innerHTML =
> perfStats.bytesReceived;
> +  document.getElementById("results_time_used").innerHTML = timeUsedMs +
> "ms";
> +  document.getElementById("results_msgs_per_sec").innerHTML =
> (perfStats.numResults / (timeUsedMs / 1000));
> +  document.getElementById("results_bytes_per_sec").innerHTML =
> (perfStats.bytesReceived / (timeUsedMs / 1000));
> +  document.getElementById("test_running").style.display = "none";
> +  document.getElementById("results").style.display = "";
> +};
> +
> +function initPerfTest() {
> +  clearPerfStats();
> +  gadgets.rpc.register("perf_service", perfService);
> +  gadgets.rpc.register("clear_perf_stats", clearPerfStats);
> +  gadgets.rpc.register("complete_perf_stats", completePerfStats);
> +};
> +
> +var alphabet =
> "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 _-*&(){}'";
> +
> +function sendPerfMessage() {
> +  var msgToSend = currentRun.msg;
> +  if (currentRun.curMsgId++ <= 1) {
> +    var nowString = new Date().getTime() + ':';
> +    msgToSend = nowString + currentRun.msg.substring(nowString.length);
> +  }
> +
> +  gadgets.rpc.call(currentRun.targetId, "perf_service", null, msgToSend);
> +  if (currentRun.curMsgId < currentRun.endMsgId) {
> +    // loop, giving up execution in case rpc technique demands it
> +    window.setTimeout(sendPerfMessage, 0);
> +  } else {
> +    // send finisher
> +    window.setTimeout(function() { gadgets.rpc.call(currentRun.targetId,
> "complete_perf_stats", null); }, 0);
> +  }
> +};
> +
> +function runPerfTest(targetId) {
> +  document.getElementById("test_running").style.display = "";
> +
> +  // initialize the current run
> +  var num_msgs = document.getElementById("num_msgs").value;
> +  var msg_size = document.getElementById("msg_size").value;
> +
> +  currentRun.targetId = targetId;
> +  currentRun.curMsgId = 0;
> +  currentRun.endMsgId = num_msgs;
> +
> +  var msg = [];
> +  for (var i = 0; i < msg_size; ++i) {
> +    msg[i] = alphabet.charAt(Math.round(Math.random(alphabet.length)));
> +  }
> +  currentRun.msg = msg.join('');
> +
> +  // clear local perf stats
> +  clearPerfStats("(outbound)");
> +
> +  // clear target perf stats
> +  gadgets.rpc.call(targetId, "clear_perf_stats", null, "(inbound)");
> +
> +  // kick off the send loop
> +  sendPerfMessage();
> +};
>
>
>

Re: svn commit: r672393 - in /incubator/shindig/trunk: features/rpc/ java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ javascript/container/

Posted by John Hjelmstad <fa...@google.com>.
Too giddy for my own good... sigh.

I've reverted rpc.js for the time being, and created:
http://issues.apache.org/jira/browse/SHINDIG-410

For further discussion, with the equivalent patch. I didn't revert the
rpctest page, which could use some work... but only tests the API anyway.

John

On Fri, Jun 27, 2008 at 5:12 PM, Kevin Brown <et...@google.com> wrote:

> And the performance improvements look good, to be sure -- you just can't
> drop something that big on people's lap though.
>
> On Fri, Jun 27, 2008 at 4:57 PM, John Hjelmstad <fa...@google.com> wrote:
>
> > That sounds fair. To be honest, if you want I can revert the whole thing
> > and
> > attach it to a JIRA issue. Sorta let my excitement about the performance
> > improvement put the cart in front of the horse ;)
> >
> > John
> >
> > On Fri, Jun 27, 2008 at 4:55 PM, Chris Chabot <ch...@xs4all.nl> wrote:
> >
> > > I'm kind of on board with Kevin that this probably should have been
> done
> > > the apache way, there's a lot of very big containers that use shindig,
> > > and hundreds of smaller ones too .. We're an apache project to make
> sure
> > > everything happens democratically, and without that in place people
> > > might not we're ready to graduate out of incubation you know :-)
> > >
> > > As a fellow committer i for one would've liked to at least take a quick
> > > look at, or at least hear about it on the mailing list it before seeing
> > > it show up 'out of nowhere', at least with other big changes we know
> > > it's being worked on in advance, and we then have the chance to see the
> > > patch cook in jira so people could respond to it if they wanted (like,
> > > positively community like even! :P)
> > >
> > > Anyhow, for now the compromise works for me, but let's do it
> differently
> > > next time ok :-)
> > >
> > >    -- Chris
> > >
> > > On Fri, 2008-06-27 at 16:39 -0700, John Hjelmstad wrote:
> > >
> > > > How's this for a compromise: I'll add a param that disables the new
> > > > technique by default, and allows it to be used if preferred. The rest
> > is
> > > > refactoring and documentation. Given that's the bulk of the code
> change
> > > in
> > > > terms of volume, I didn't create a JIRA issue for it. I'm happy to do
> > so
> > > to
> > > > enable it longer-term, however. Thoughts?
> > > >
> > > > John
> > > >
> > > > On Fri, Jun 27, 2008 at 4:27 PM, Kevin Brown <et...@google.com>
> wrote:
> > > >
> > > > > On Fri, Jun 27, 2008 at 3:45 PM, John Hjelmstad <fa...@google.com>
> > > wrote:
> > > > >
> > > > > > As indicated in the CL, I tested this on half a dozen browsers.
> We
> > > don't
> > > > > > have unit tests for it. I've also sent the code and inquiries
> about
> > > the
> > > > > > technique to several poeple, some of whom expressed concerns but
> no
> > > > > ability
> > > > > > to actively exploit the code.
> > > > >
> > > > >
> > > > > Anyone on this mailing list? Either of the people I mentioned?
> > > > >
> > > > >
> > > > > > The main concern was around VBScript
> > > > > > knowledge, which few people I know extensively have. That was
> true
> > of
> > > > > IFPC
> > > > > > when it was originally released as well. If any attacks are
> found,
> > > they
> > > > > > will
> > > > > > be actively closed or the technique will be removed. Would you
> > prefer
> > > > > that
> > > > > > no code, irrespective of its value, ever be submitted? It's
> > possible
> > > for
> > > > > > any
> > > > > > code to have bugs, and a lot of code to have security holes. If
> > > found,
> > > > > > they're fixed.
> > > > >
> > > > >
> > > > > Yes, but this is code that uses a security technique that is highly
> > > > > questionable and has been flagged as potentially problematic by
> > several
> > > > > people. Therefore, it needs more scrutiny. I won't deploy this on
> any
> > > of
> > > > > our
> > > > > production systems until I get an OK from people who are actually
> > > familiar
> > > > > with the problems in question have chimed in.
> > > > >
> > > > > This is also a very large code change, and, per policy, large code
> > > changes
> > > > > need JIRA tickets and feedback from other people. There are
> hundreds
> > > (if
> > > > > not
> > > > > thousands) of people using Shindig today and the code is deployed
> to
> > > > > hundreds of millions of users.
> > > > >
> > > > >
> > > > > >
> > > > > > John
> > > > > >
> > > > > > On Fri, Jun 27, 2008 at 3:38 PM, Kevin Brown <et...@google.com>
> > > wrote:
> > > > > >
> > > > > > > Please actually test your code or send it to other shindig
> > > committers
> > > > > > > before
> > > > > > > you commit it. Your change to GadgetRenderingTask breaks
> proxying
> > > and
> > > > > > > makeRequest in several cases. Fix it.
> > > > > > >
> > > > > > > Have you run this by Brian and/or Mike Samuel? They both
> > expressed
> > > > > doubts
> > > > > > > about whether this mechanism was actually secure.
> > > > > > >
> > > > > > > On Fri, Jun 27, 2008 at 2:06 PM, John Hjelmstad <
> > fargo@google.com>
> > > > > > wrote:
> > > > > > >
> > > > > > > > +jschorr in case there are comments.
> > > > > > > >
> > > > > > > > On Fri, Jun 27, 2008 at 2:01 PM, <jo...@apache.org> wrote:
> > > > > > > >
> > > > > > > > > Author: johnh
> > > > > > > > > Date: Fri Jun 27 14:01:20 2008
> > > > > > > > > New Revision: 672393
> > > > > > > > >
> > > > > > > > > URL: http://svn.apache.org/viewvc?rev=672393&view=rev
> > > > > > > > > Log:
> > > > > > > > > Cleaned up gadgets.rpc library implementation, and
> > implemented
> > > fast
> > > > > > IE
> > > > > > > > > transport layer.
> > > > > > > > >
> > > > > > > > > Credit:
> > > > > > > > >        Joseph Schorr (jschorr@google.com) and I
> implemented
> > > this
> > > > > > > > together,
> > > > > > > > > but he
> > > > > > > > >        really did all the hard work, as well as developing
> > the
> > > NIX
> > > > > > > > > technique's
> > > > > > > > >        fundamentals. Huge thanks to Joseph!
> > > > > > > > >
> > > > > > > > > Details:
> > > > > > > > >        * Created a new relayChannel for Firefox
> frameElement
> > > > > > technique,
> > > > > > > > >          making its implementation more linear to read.
> > > > > > > > >        * Consolidated all transport-specific code in
> > > > > setupChannel(),
> > > > > > > > > setupFrame(),
> > > > > > > > >          and callX(), where X = relay type. This
> refactoring
> > > makes
> > > > > > > setup
> > > > > > > > > and use
> > > > > > > > >          of each transport more clear, and makes it
> possible
> > in
> > > > > later
> > > > > > > > > revisions
> > > > > > > > >          to separate out each method as its own class. In
> > > theory,
> > > > > if
> > > > > > we
> > > > > > > > can
> > > > > > > > > trust
> > > > > > > > >          things like User-Agent headers, we can use this to
> > > reduce
> > > > > > > rpc.js
> > > > > > > > > code
> > > > > > > > >          size significantly by only including the needed
> > > > > > transport(s).
> > > > > > > > >        * Implemented "NIX" - Native IE XDC. This method
> > > exploits
> > > > > the
> > > > > > > fact
> > > > > > > > >          that in IE6 and IE7 the window.opener property can
> > be
> > > both
> > > > > > set
> > > > > > > > and
> > > > > > > > >          read across domain boundaries for an IFRAME, and
> > that
> > > any
> > > > > > > object
> > > > > > > > >          can be passed through it. In particular, functions
> > > > > > > implementing
> > > > > > > > >          message passing can be passed. These can't be JS
> > > > > functions,
> > > > > > > > > however,
> > > > > > > > >          since those can leak sender context. So, VBScript
> > > (COM)
> > > > > > > objects
> > > > > > > > >          wrapping these are passed to maintain context
> > > isolation.
> > > > > > > > >          - Requires for IE6/7 that rpc.js be included in
> the
> > > > > > container
> > > > > > > > >            at load time, not dynamically. TODO added to
> > > consider
> > > > > > > > detecting
> > > > > > > > >            whether dynamic loading is happening (at which
> > point
> > > we
> > > > > > > should
> > > > > > > > >            fall back to IFPC).
> > > > > > > > >          - Message channel handshake *and* message passing
> > > > > validated
> > > > > > > > >            using authToken, as with other channels.
> > > > > > > > >          - Impl requires that gadget call container first -
> > > else
> > > > > IFPC
> > > > > > > > >            is used. This is the same as the frameElement
> > > technique.
> > > > > > We
> > > > > > > > >            could add a setInterval()-based initialization
> > > routine
> > > > > > later
> > > > > > > > >            if needed. To date, the only gadgets.rpc calls
> > made
> > > from
> > > > > > > > >            container to gadget without gadget to container
> > > first
> > > > > are
> > > > > > > > >            in theory pubsub messages.
> > > > > > > > >          - Extensive documentation on this technique and
> the
> > > others
> > > > > > > > >            added to the comments, since they're stripped
> out
> > in
> > > the
> > > > > > > > >            majority case.
> > > > > > > > >        * Implemented quick-and-dirty performance testing
> page
> > > > > > verifying
> > > > > > > > >          the library works and giving basic performance
> > > metrics.
> > > > > > > > >          This needs to be improved! But does the trick for
> > > now...
> > > > > > > > >
> > > > > > > > > Testing/Data:
> > > > > > > > >
> > > > > > > > > Library verified on the following browsers with the RPC
> test
> > > page.
> > > > > > For
> > > > > > > > > each,
> > > > > > > > > a "bandwidth" measure was taken by averaging the results of
> 6
> > > G2C
> > > > > and
> > > > > > > C2G
> > > > > > > > > runs
> > > > > > > > > with 100 messages being passed of 1K apiece. Units are 1K
> > > > > > messages/sec.
> > > > > > > > > The "latency" value is the average amount of time taken,
> over
> > 6
> > > G2C
> > > > > > and
> > > > > > > > C2G
> > > > > > > > > runs,
> > > > > > > > > to send 1 1K message. Happily, for all browsers save Safari
> > > (soon
> > > > > due
> > > > > > > to
> > > > > > > > > get
> > > > > > > > > window.postMessage), latency is around or below time
> required
> > > to
> > > > > > > achieve
> > > > > > > > > a cross-domain call without user-perceived delay.
> > > > > > > > >
> > > > > > > > >        * Firefox 3 (WPM) - bandwidth: 88 kB/sec; latency:
> > 18ms.
> > > > > > > > >        * Firefox 2 (FE)  - bandwidth: 80 kB/sec; latency:
> > 15ms.
> > > > > > > > >        * Opera 9   (WPM) - bandwidth: 85 kB/sec; latency:
> > 7ms.
> > > > > > > > >        * IE6       (NIX) - bandwidth: 64 kB/sec; latency:
> > 18ms.
> > > > > > > > >        * IE7       (NIX) - bandwidth: 64 kB/sec; latency:
> > 22ms.
> > > > > > > > >        * Safari 3 (IFPC) - bandwidth: ?6-8kB/sec; latency:
> > > > > > ?100-200ms.
> > > > > > > > >          - Safari is somewhat flaky with the RPC test page,
> > > before
> > > > > > > > >            and after the rpc.js change. Multiple messages
> > seem
> > > to
> > > > > > > > >            confuse it from time to time.
> > > > > > > > >
> > > > > > > > >
> > > > > > > > > Added:
> > > > > > > > >
> > > > > >
> >  incubator/shindig/trunk/javascript/container/rpctest_container.html
> > > > > > > > >
> > >  incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > > > > > > > >
> >  incubator/shindig/trunk/javascript/container/rpctest_perf.js
> > > > > > > > > Modified:
> > > > > > > > >    incubator/shindig/trunk/features/rpc/rpc.js
> > > > > > > > >
> > > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > >
> >
>  incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > > > > > > >
> > > > > > > > > Modified: incubator/shindig/trunk/features/rpc/rpc.js
> > > > > > > > > URL:
> > > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > >
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/features/rpc/rpc.js?rev=672393&r1=672392&r2=672393&view=diff
> > > > > > > > >
> > > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > >
> >
> ==============================================================================
> > > > > > > > > --- incubator/shindig/trunk/features/rpc/rpc.js (original)
> > > > > > > > > +++ incubator/shindig/trunk/features/rpc/rpc.js Fri Jun 27
> > > 14:01:20
> > > > > > > 2008
> > > > > > > > > @@ -18,7 +18,7 @@
> > > > > > > > >
> > > > > > > > >  /**
> > > > > > > > >  * @fileoverview Remote procedure call library for
> > > > > > gadget-to-container,
> > > > > > > > > - * container-to-gadget, and gadget-to-gadget
> communication.
> > > > > > > > > + * container-to-gadget, and gadget-to-gadget (thru
> > container)
> > > > > > > > > communication.
> > > > > > > > >  */
> > > > > > > > >
> > > > > > > > >  var gadgets = gadgets || {};
> > > > > > > > > @@ -29,6 +29,30 @@
> > > > > > > > >  * @name gadgets.rpc
> > > > > > > > >  */
> > > > > > > > >  gadgets.rpc = function() {
> > > > > > > > > +  // General constants.
> > > > > > > > > +  var CALLBACK_NAME = '__cb';
> > > > > > > > > +  var DEFAULT_NAME = '';
> > > > > > > > > +
> > > > > > > > > +  // Consts for FrameElement.
> > > > > > > > > +  var FE_G2C_CHANNEL = '__g2c_rpc';
> > > > > > > > > +  var FE_C2G_CHANNEL = '__c2g_rpc';
> > > > > > > > > +
> > > > > > > > > +  // Consts for NIX. VBScript doesn't
> > > > > > > > > +  // allow items to start with _ for some reason,
> > > > > > > > > +  // so we need to make these names quite unique, as
> > > > > > > > > +  // they will go into the global namespace.
> > > > > > > > > +  var NIX_WRAPPER = 'GRPC____NIXVBS_wrapper';
> > > > > > > > > +  var NIX_GET_WRAPPER = 'GRPC____NIXVBS_get_wrapper';
> > > > > > > > > +  var NIX_HANDLE_MESSAGE =
> 'GRPC____NIXVBS_handle_message';
> > > > > > > > > +  var NIX_CREATE_CHANNEL =
> 'GRPC____NIXVBS_create_channel';
> > > > > > > > > +  var NIX_ID_FIELD = 'GRPC____NIXVBS_container';
> > > > > > > > > +
> > > > > > > > > +  // JavaScript reference to the NIX VBScript wrappers.
> > > > > > > > > +  // Gadgets will have but a single channel under
> > > > > > > > > +  // nix_channels['..'] while containers will have a
> channel
> > > > > > > > > +  // per gadget stored under the gadget's ID.
> > > > > > > > > +  var nix_channels = {};
> > > > > > > > > +
> > > > > > > > >   var services = {};
> > > > > > > > >   var iframePool = [];
> > > > > > > > >   var relayUrl = {};
> > > > > > > > > @@ -36,28 +60,135 @@
> > > > > > > > >   var authToken = {};
> > > > > > > > >   var callId = 0;
> > > > > > > > >   var callbacks = {};
> > > > > > > > > +  var setup = {};
> > > > > > > > > +
> > > > > > > > > +  var params = {};
> > > > > > > > > +
> > > > > > > > > +  // Load the authentication token for speaking to the
> > > container
> > > > > > > > > +  // from the gadget's parameters, or default to '0' if
> not
> > > found.
> > > > > > > > > +  if (gadgets.util) {
> > > > > > > > > +    params = gadgets.util.getUrlParameters();
> > > > > > > > > +  }
> > > > > > > > >
> > > > > > > > > -  var params = gadgets.util.getUrlParameters();
> > > > > > > > >   authToken['..'] = params.rpctoken || params.ifpctok || 0;
> > > > > > > > >
> > > > > > > > > -  // Pick the most efficient RPC relay mechanism
> > > > > > > > > -  var relayChannel = typeof document.postMessage ===
> > > 'function' ?
> > > > > > > 'dpm'
> > > > > > > > :
> > > > > > > > > -                     typeof window.postMessage ===
> > 'function'
> > > ?
> > > > > > 'wpm'
> > > > > > > :
> > > > > > > > > +  // Pick the most efficient RPC relay mechanism:
> > > > > > > > > +  //
> > > > > > > > > +  // * For those browsers that support native messaging
> > > (various
> > > > > > > > > implementations
> > > > > > > > > +  //   of the HTML5 postMessage method), use that.
> > Officially
> > > > > > defined
> > > > > > > at
> > > > > > > > > +  //
> > > > > > > > >
> > > > > >
> > > http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html
> > > > > > > .
> > > > > > > > > +  //
> > > > > > > > > +  //   postMessage is a native implementation of XDC. A
> page
> > > > > > registers
> > > > > > > > > that
> > > > > > > > > +  //   it would like to receive messages by listening the
> > the
> > > > > > > "message"
> > > > > > > > > event
> > > > > > > > > +  //   on the window (document in DPM) object. In turn,
> > > another
> > > > > page
> > > > > > > can
> > > > > > > > > +  //   raise that event by calling window.postMessage
> > > > > > > > > (document.postMessage
> > > > > > > > > +  //   in DPM) with a string representing the message and
> a
> > > string
> > > > > > > > > +  //   indicating on which domain the receiving page must
> be
> > > to
> > > > > > > receive
> > > > > > > > > +  //   the message. The target page will then have its
> > > "message"
> > > > > > event
> > > > > > > > > raised
> > > > > > > > > +  //   if the domain matches and can, in turn, check the
> > > origin of
> > > > > > the
> > > > > > > > > message
> > > > > > > > > +  //   and process the data contained within.
> > > > > > > > > +  //
> > > > > > > > > +  //     wpm: postMessage on the window object.
> > > > > > > > > +  //        - Internet Explorer 8+
> > > > > > > > > +  //        - Safari (latest nightlies as of 26/6/2008)
> > > > > > > > > +  //        - Firefox 3+
> > > > > > > > > +  //        - Opera 9+
> > > > > > > > > +  //
> > > > > > > > > +  //     dpm: postMessage on the document object.
> > > > > > > > > +  //        - Opera 8+
> > > > > > > > > +  //
> > > > > > > > > +  // * For Internet Explorer before version 8, the
> security
> > > model
> > > > > > > allows
> > > > > > > > a
> > > > > > > > > +  //   child to set the value of the "opener" property on
> > the
> > > > > parent
> > > > > > > > > window
> > > > > > > > > +  //   and vice versa. This method is dubbed "Native IE
> XDC"
> > > > > (NIX).
> > > > > > > > > +  //
> > > > > > > > > +  //   This method works by placing a handler object in
> the
> > > > > "opener"
> > > > > > > > > property
> > > > > > > > > +  //   of a gadget when the container sets up the
> > > authentication
> > > > > > > > > information
> > > > > > > > > +  //   for that gadget (by calling setAuthToken(...)). At
> > that
> > > > > > point,
> > > > > > > a
> > > > > > > > > NIX
> > > > > > > > > +  //   wrapper is created and placed into the gadget by
> > > calling
> > > > > > > > > +  //   theframe.contentWindow.opener = wrapper. Note that
> as
> > a
> > > > > > result,
> > > > > > > > NIX
> > > > > > > > > can
> > > > > > > > > +  //   only be used by a container to call a particular
> > gadget
> > > > > > *after*
> > > > > > > > > that
> > > > > > > > > +  //   gadget has called the container at least once via
> > NIX.
> > > > > > > > > +  //
> > > > > > > > > +  //   The NIX wrappers in this RPC implementation are
> > > instances
> > > > > of
> > > > > > a
> > > > > > > > > VBScript
> > > > > > > > > +  //   class that is created when this implementation
> loads.
> > > The
> > > > > > > reason
> > > > > > > > > for
> > > > > > > > > +  //   using a VBScript class stems from the fact that any
> > > object
> > > > > > can
> > > > > > > be
> > > > > > > > > passed
> > > > > > > > > +  //   into the opener property.
> > > > > > > > > +  //   While this is a good thing, as it lets us pass
> > > functions
> > > > > and
> > > > > > > > setup
> > > > > > > > > a true
> > > > > > > > > +  //   bidirectional channel via callbacks, it opens a
> > > potential
> > > > > > > > security
> > > > > > > > > hole
> > > > > > > > > +  //   by which the other page can get ahold of the
> "window"
> > > or
> > > > > > > > "document"
> > > > > > > > > +  //   objects in the parent page and in turn wreak havok.
> > > This is
> > > > > > due
> > > > > > > > to
> > > > > > > > > the
> > > > > > > > > +  //   fact that any JS object useful for establishing
> such
> > a
> > > > > > > > > bidirectional
> > > > > > > > > +  //   channel (such as a function) can be used to access
> a
> > > > > function
> > > > > > > > > +  //   (eg. obj.toString, or a function itself) created in
> a
> > > > > > specific
> > > > > > > > > context,
> > > > > > > > > +  //   in particular the global context of the sender.
> > Suppose
> > > > > > > container
> > > > > > > > > +  //   domain C passes object obj to gadget on domain G.
> > Then
> > > the
> > > > > > > gadget
> > > > > > > > > can
> > > > > > > > > +  //   access C's global context using:
> > > > > > > > > +  //   var parentWindow = (new
> > > obj.toString.constructor("return
> > > > > > > > > window;"))();
> > > > > > > > > +  //   Nulling out all of obj's properties doesn't fix
> this,
> > > since
> > > > > > IE
> > > > > > > > > helpfully
> > > > > > > > > +  //   restores them to their original values if you do
> > > something
> > > > > > > like:
> > > > > > > > > +  //   delete obj.toString; delete obj.toString;
> > > > > > > > > +  //   Thus, we wrap the necessary functions and
> information
> > > > > inside
> > > > > > a
> > > > > > > > > VBScript
> > > > > > > > > +  //   object. VBScript objects in IE, like DOM objects,
> are
> > > in
> > > > > fact
> > > > > > > COM
> > > > > > > > > +  //   wrappers when used in JavaScript, so we can safely
> > pass
> > > > > them
> > > > > > > > around
> > > > > > > > > +  //   without worrying about a breach of context while at
> > the
> > > > > same
> > > > > > > time
> > > > > > > > > +  //   allowing them to act as a pass-through mechanism
> for
> > > > > > > information
> > > > > > > > > +  //   and function calls. The implementation details of
> > this
> > > > > > VBScript
> > > > > > > > > wrapper
> > > > > > > > > +  //   can be found in the setupChannel() method below.
> > > > > > > > > +  //
> > > > > > > > > +  //     nix: Internet Explorer-specific window.opener
> > trick.
> > > > > > > > > +  //       - Internet Explorer 6
> > > > > > > > > +  //       - Internet Explorer 7
> > > > > > > > > +  //
> > > > > > > > > +  // * For Gecko-based browsers, the security model allows
> a
> > > child
> > > > > > to
> > > > > > > > call
> > > > > > > > > a
> > > > > > > > > +  //   function on the frameElement of the iframe, even if
> > the
> > > > > child
> > > > > > > is
> > > > > > > > in
> > > > > > > > > +  //   a different domain. This method is dubbed
> > > "frameElement"
> > > > > > (fe).
> > > > > > > > > +  //
> > > > > > > > > +  //   The ability to add and call such functions on the
> > > > > > frameElement
> > > > > > > > > allows
> > > > > > > > > +  //   a bidirectional channel to be setup via the adding
> of
> > > > > simple
> > > > > > > > > function
> > > > > > > > > +  //   references on the frameElement object itself. In
> this
> > > > > > > > > implementation,
> > > > > > > > > +  //   when the container sets up the authentication
> > > information
> > > > > for
> > > > > > > > that
> > > > > > > > > gadget
> > > > > > > > > +  //   (by calling setAuth(...)) it as well adds a special
> > > > > function
> > > > > > on
> > > > > > > > the
> > > > > > > > > +  //   gadget's iframe. This function can then be used by
> > the
> > > > > gadget
> > > > > > > to
> > > > > > > > > send
> > > > > > > > > +  //   messages to the container. In turn, when the gadget
> > > tries
> > > > > to
> > > > > > > send
> > > > > > > > a
> > > > > > > > > +  //   message, it checks to see if this function has its
> > own
> > > > > > function
> > > > > > > > > stored
> > > > > > > > > +  //   that can be used by the container to call the
> gadget.
> > > If
> > > > > not,
> > > > > > > the
> > > > > > > > > +  //   function is created and subsequently used by the
> > > container.
> > > > > > > > > +  //   Note that as a result, FE can only be used by a
> > > container
> > > > > to
> > > > > > > call
> > > > > > > > a
> > > > > > > > > +  //   particular gadget *after* that gadget has called
> the
> > > > > > container
> > > > > > > at
> > > > > > > > > +  //   least once via FE.
> > > > > > > > > +  //
> > > > > > > > > +  //     fe: Gecko-specific frameElement trick.
> > > > > > > > > +  //        - Firefox 1+
> > > > > > > > > +  //
> > > > > > > > > +  // * For all others, we have a fallback mechanism known
> as
> > > > > "ifpc".
> > > > > > > > IFPC
> > > > > > > > > +  //   exploits the fact that while same-origin policy
> > > prohibits a
> > > > > > > frame
> > > > > > > > > from
> > > > > > > > > +  //   accessing members on a window not in the same
> domain,
> > > that
> > > > > > > frame
> > > > > > > > > can,
> > > > > > > > > +  //   however, navigate the window heirarchy (via
> parent).
> > > This
> > > > > is
> > > > > > > > > exploited by
> > > > > > > > > +  //   having a page on domain A that wants to talk to
> > domain
> > > B
> > > > > > create
> > > > > > > > an
> > > > > > > > > iframe
> > > > > > > > > +  //   on domain B pointing to a special relay file and
> with
> > a
> > > > > > message
> > > > > > > > > encoded
> > > > > > > > > +  //   after the hash (#). This relay, in turn, finds the
> > page
> > > on
> > > > > > > domain
> > > > > > > > > B, and
> > > > > > > > > +  //   can call a receipt function with the message given
> to
> > > it.
> > > > > The
> > > > > > > > relay
> > > > > > > > > URL
> > > > > > > > > +  //   used by each caller is set via the
> > > > > > gadgets.rpc.setRelayUrl(..)
> > > > > > > > and
> > > > > > > > > +  //   *must* be called before the call method is used.
> > > > > > > > > +  //
> > > > > > > > > +  //     ifpc: Iframe-based method, utilizing a relay
> page,
> > to
> > > > > send
> > > > > > a
> > > > > > > > > message.
> > > > > > > > > +  //
> > > > > > > > > +  var relayChannel = typeof window.postMessage ===
> > 'function'
> > > ?
> > > > > > 'wpm'
> > > > > > > :
> > > > > > > > > +                    typeof document.postMessage ===
> > 'function'
> > > ?
> > > > > > 'dpm'
> > > > > > > :
> > > > > > > > > +                    window.ActiveXObject ? 'nix' :
> > > > > > > > > +                     navigator.product === 'Gecko' ? 'fe'
> :
> > > > > > > > >                      'ifpc';
> > > > > > > > > -  if (relayChannel === 'dpm' || relayChannel === 'wpm') {
> > > > > > > > > -    window.addEventListener('message', function(packet) {
> > > > > > > > > -      // TODO validate packet.domain for security reasons
> > > > > > > > > -      process(gadgets.json.parse(packet.data));
> > > > > > > > > -    }, false);
> > > > > > > > > -  }
> > > > > > > > >
> > > > > > > > > -  // Default RPC handler
> > > > > > > > > -  services[''] = function() {
> > > > > > > > > +  // Conduct any setup necessary for the chosen channel.
> > > > > > > > > +  setupChannel();
> > > > > > > > > +
> > > > > > > > > +  // Create the Default RPC handler.
> > > > > > > > > +  services[DEFAULT_NAME] = function() {
> > > > > > > > >     throw new Error('Unknown RPC service: ' + this.s);
> > > > > > > > >   };
> > > > > > > > >
> > > > > > > > > -  // Special RPC handler for callbacks
> > > > > > > > > -  services['__cb'] = function(callbackId, result) {
> > > > > > > > > +  // Create a Special RPC handler for callbacks.
> > > > > > > > > +  services[CALLBACK_NAME] = function(callbackId, result) {
> > > > > > > > >     var callback = callbacks[callbackId];
> > > > > > > > >     if (callback) {
> > > > > > > > >       delete callbacks[callbackId];
> > > > > > > > > @@ -66,6 +197,147 @@
> > > > > > > > >   };
> > > > > > > > >
> > > > > > > > >   /**
> > > > > > > > > +   * Conducts any initial global work necessary to setup
> the
> > > > > > > > > +   * channel type chosen.
> > > > > > > > > +   */
> > > > > > > > > +  function setupChannel() {
> > > > > > > > > +    // If the channel type is one of the native
> > > > > > > > > +    // postMessage based ones, setup the handler to
> receive
> > > > > > > > > +    // messages.
> > > > > > > > > +    if (relayChannel === 'dpm' || relayChannel === 'wpm')
> {
> > > > > > > > > +      window.addEventListener('message', function(packet)
> {
> > > > > > > > > +        // TODO validate packet.domain for security
> reasons
> > > > > > > > > +        process(gadgets.json.parse(packet.data));
> > > > > > > > > +      }, false);
> > > > > > > > > +    }
> > > > > > > > > +
> > > > > > > > > +    // If the channel type is NIX, we need to ensure the
> > > > > > > > > +    // VBScript wrapper code is in the page and that the
> > > > > > > > > +    // global Javascript handlers have been set.
> > > > > > > > > +    if (relayChannel === 'nix') {
> > > > > > > > > +      // VBScript methods return a type of 'unknown' when
> > > > > > > > > +      // checked via the typeof operator in IE.
> Fortunately
> > > > > > > > > +      // for us, this only applies to COM objects, so we
> > > > > > > > > +      // won't see this for a real Javascript object.
> > > > > > > > > +      if (typeof window[NIX_GET_WRAPPER] !== 'unknown') {
> > > > > > > > > +        window[NIX_HANDLE_MESSAGE] = function(data) {
> > > > > > > > > +          process(gadgets.json.parse(data));
> > > > > > > > > +        };
> > > > > > > > > +
> > > > > > > > > +        window[NIX_CREATE_CHANNEL] = function(name,
> channel,
> > > > > token)
> > > > > > {
> > > > > > > > > +          // Verify the authentication token of the gadget
> > > trying
> > > > > > > > > +          // to create a channel for us.
> > > > > > > > > +          if (authToken[name] == token) {
> > > > > > > > > +            nix_channels[name] = channel;
> > > > > > > > > +          }
> > > > > > > > > +        };
> > > > > > > > > +
> > > > > > > > > +        // Inject the VBScript code needed.
> > > > > > > > > +        var vbscript =
> > > > > > > > > +          '<scr' + 'ipt language="VBScript">'
> > > > > > > > > +          // We create a class to act as a wrapper for
> > > > > > > > > +          // a Javascript call, to prevent a break in of
> > > > > > > > > +          // the context.
> > > > > > > > > +          + 'Class ' + NIX_WRAPPER + '\n '
> > > > > > > > > +
> > > > > > > > > +          // An internal member for keeping track of the
> > > > > > > > > +          // name of the document (container or gadget)
> > > > > > > > > +          // for which this wrapper is intended. For
> > > > > > > > > +          // those wrappers created by gadgets, this is
> not
> > > > > > > > > +          // used (although it is set to "..")
> > > > > > > > > +          + 'Private m_Intended\n'
> > > > > > > > > +
> > > > > > > > > +          // Method for internally setting the value
> > > > > > > > > +          // of the m_Intended property.
> > > > > > > > > +          + 'Public Sub SetIntendedName(name)\n '
> > > > > > > > > +          + 'm_Intended = name\n'
> > > > > > > > > +          + 'End Sub\n'
> > > > > > > > > +
> > > > > > > > > +          // A wrapper method which actually causes a
> > > > > > > > > +          // message to be sent to the other context.
> > > > > > > > > +          + 'Public Sub SendMessage(data)\n '
> > > > > > > > > +          + NIX_HANDLE_MESSAGE + '(data)\n'
> > > > > > > > > +          + 'End Sub\n'
> > > > > > > > > +
> > > > > > > > > +          // Method for setting up the container->gadget
> > > > > > > > > +          // channel. Not strictly needed in the gadget's
> > > > > > > > > +          // wrapper, but no reason to get rid of it. Note
> > > here
> > > > > > > > > +          // that we pass the intended name to the
> > > > > > NIX_CREATE_CHANNEL
> > > > > > > > > +          // method so that it can save the channel in the
> > > proper
> > > > > > > place
> > > > > > > > > +          // *and* verify the channel via the
> authentication
> > > token
> > > > > > > > passed
> > > > > > > > > +          // here.
> > > > > > > > > +          + 'Public Sub CreateChannel(channel, auth)\n '
> > > > > > > > > +          + 'Call ' + NIX_CREATE_CHANNEL + '(m_Intended,
> > > channel,
> > > > > > > > auth)\n'
> > > > > > > > > +          + 'End Sub\n'
> > > > > > > > > +
> > > > > > > > > +          // An empty field with a unique identifier to
> > > > > > > > > +          // prevent the code from confusing this wrapper
> > > > > > > > > +          // with a run-of-the-mill value found in
> > > window.opener.
> > > > > > > > > +          + 'Public Sub ' + NIX_ID_FIELD + '()\n '
> > > > > > > > > +          + 'End Sub\n'
> > > > > > > > > +          + 'End Class\n '
> > > > > > > > > +
> > > > > > > > > +          // Function to get a reference to the wrapper.
> > > > > > > > > +          + 'Function ' + NIX_GET_WRAPPER + '(name)\n'
> > > > > > > > > +          + 'Dim wrap\n'
> > > > > > > > > +          + 'Set wrap = New ' + NIX_WRAPPER + '\n'
> > > > > > > > > +          + 'wrap.SetIntendedName name\n'
> > > > > > > > > +          + 'Set ' + NIX_GET_WRAPPER + ' = wrap\n'
> > > > > > > > > +          + 'End Function'
> > > > > > > > > +          + '</scr' + 'ipt>'
> > > > > > > > > +
> > > > > > > > > +          // Resets the "default" scripting language in IE
> > > back
> > > > > > > > > +          // to Javascript. This is needed or else any
> > script
> > > > > > > > > +          // tags without a proper Language="..." will be
> > > treated
> > > > > as
> > > > > > > > > +          // VBScript once this code is added to the
> > document.
> > > > > > > > > +          + '<scri' + 'pt language="JScript"></scr' +
> > 'ipt>';
> > > > > > > > > +
> > > > > > > > > +        // Note that this code can only be run once the
> > > document
> > > > > has
> > > > > > > > > +        // fully loaded.
> > > > > > > > > +        // TODO: Perhaps add some sort of check here for
> > this?
> > > > > > > > > +        document.write(vbscript);
> > > > > > > > > +      }
> > > > > > > > > +    }
> > > > > > > > > +  }
> > > > > > > > > +
> > > > > > > > > +  /**
> > > > > > > > > +   * Conducts any frame-specific work necessary to setup
> > > > > > > > > +   * the channel type chosen. This method is called when
> > > > > > > > > +   * the container page first registers the gadget in the
> > > > > > > > > +   * RPC mechanism. Gadgets, in turn, will complete the
> > setup
> > > > > > > > > +   * of the channel once they send their first messages.
> > > > > > > > > +   */
> > > > > > > > > +  function setupFrame(frameId) {
> > > > > > > > > +    if (setup[frameId]) {
> > > > > > > > > +      return;
> > > > > > > > > +    }
> > > > > > > > > +
> > > > > > > > > +    if (relayChannel === 'fe') {
> > > > > > > > > +      try {
> > > > > > > > > +        var frame = document.getElementById(frameId);
> > > > > > > > > +        frame[FE_G2C_CHANNEL] = function(args) {
> > > > > > > > > +          process(gadgets.json.parse(args));
> > > > > > > > > +        };
> > > > > > > > > +      } catch (e) {
> > > > > > > > > +        // Something went wrong. System will fallback to
> > > > > > > > > +        // IFPC.
> > > > > > > > > +      }
> > > > > > > > > +    }
> > > > > > > > > +
> > > > > > > > > +    if (relayChannel === 'nix') {
> > > > > > > > > +      try {
> > > > > > > > > +        var frame = document.getElementById(frameId);
> > > > > > > > > +        frame.contentWindow.opener =
> > > > > > window[NIX_GET_WRAPPER](frameId);
> > > > > > > > > +      } catch (e) {
> > > > > > > > > +        // Something went wrong. System will fallback to
> > > > > > > > > +        // IFPC.
> > > > > > > > > +      }
> > > > > > > > > +    }
> > > > > > > > > +
> > > > > > > > > +    setup[frameId] = true;
> > > > > > > > > +  }
> > > > > > > > > +
> > > > > > > > > +  /**
> > > > > > > > >    * Encodes arguments for the legacy IFPC wire format.
> > > > > > > > >    *
> > > > > > > > >    * @param {Object} args
> > > > > > > > > @@ -86,8 +358,17 @@
> > > > > > > > >    * @private
> > > > > > > > >    */
> > > > > > > > >   function process(rpc) {
> > > > > > > > > +    //
> > > > > > > > > +    // RPC object contents:
> > > > > > > > > +    //   s: Service Name
> > > > > > > > > +    //   f: From
> > > > > > > > > +    //   c: The callback ID or 0 if none.
> > > > > > > > > +    //   a: The arguments for this RPC call.
> > > > > > > > > +    //   t: The authentication token.
> > > > > > > > > +    //
> > > > > > > > >     if (rpc && typeof rpc.s === 'string' && typeof rpc.f
> ===
> > > > > 'string'
> > > > > > > &&
> > > > > > > > >         rpc.a instanceof Array) {
> > > > > > > > > +
> > > > > > > > >       // Validate auth token.
> > > > > > > > >       if (authToken[rpc.f]) {
> > > > > > > > >         // We allow type coercion here because all the url
> > > params
> > > > > are
> > > > > > > > > strings.
> > > > > > > > > @@ -96,43 +377,158 @@
> > > > > > > > >         }
> > > > > > > > >       }
> > > > > > > > >
> > > > > > > > > -      // The Gecko engine used by FireFox etc. allows an
> > > IFrame to
> > > > > > > > > directly call
> > > > > > > > > -      // methods on the frameElement property added by the
> > > > > container
> > > > > > > > page
> > > > > > > > > even
> > > > > > > > > -      // if their domains don't match.
> > > > > > > > > -      // Here we try to set up a relay channel using the
> > > > > > frameElement
> > > > > > > > > technique
> > > > > > > > > -      // to greatly reduce the latency of cross-domain
> calls
> > > if
> > > > > the
> > > > > > > > > postMessage
> > > > > > > > > -      // method is not supported.
> > > > > > > > > -      if (relayChannel === 'ifpc') {
> > > > > > > > > -        if (rpc.f === '..') {
> > > > > > > > > -          // Container-to-gadget call
> > > > > > > > > -          try {
> > > > > > > > > -            var fel = window.frameElement;
> > > > > > > > > -            if (typeof fel.__g2c_rpc === 'function' &&
> > > > > > > > > -                typeof fel.__g2c_rpc.__c2g_rpc !=
> > 'function')
> > > {
> > > > > > > > > -              fel.__g2c_rpc.__c2g_rpc = function(args) {
> > > > > > > > > -                process(gadgets.json.parse(args));
> > > > > > > > > -              };
> > > > > > > > > -            }
> > > > > > > > > -          } catch (e) {
> > > > > > > > > -          }
> > > > > > > > > -        } else {
> > > > > > > > > -          // Gadget-to-container call
> > > > > > > > > -          var iframe = document.getElementById(rpc.f);
> > > > > > > > > -          if (iframe && typeof iframe.__g2c_rpc !=
> > 'function')
> > > {
> > > > > > > > > -            iframe.__g2c_rpc = function(args) {
> > > > > > > > > +      // Call the requested RPC service.
> > > > > > > > > +      var result = (services[rpc.s] ||
> > > > > > > > > +                    services[DEFAULT_NAME]).apply(rpc,
> > rpc.a);
> > > > > > > > > +
> > > > > > > > > +      // If there is a callback for this service, initiate
> > it
> > > as
> > > > > > well.
> > > > > > > > > +      if (rpc.c) {
> > > > > > > > > +        gadgets.rpc.call(rpc.f, CALLBACK_NAME, null,
> rpc.c,
> > > > > result);
> > > > > > > > > +      }
> > > > > > > > > +    }
> > > > > > > > > +  }
> > > > > > > > > +
> > > > > > > > > +  /**
> > > > > > > > > +   * Attempts to conduct an RPC call to the specified
> > > > > > > > > +   * target with the specified data via the NIX
> > > > > > > > > +   * method. If this method fails, the system attempts
> again
> > > > > > > > > +   * using the known default of IFPC.
> > > > > > > > > +   *
> > > > > > > > > +   * @param {String} targetId Module Id of the RPC service
> > > > > provider.
> > > > > > > > > +   * @param {String} from Module Id of the calling
> provider.
> > > > > > > > > +   * @param {Object} rpcData The RPC data for this call.
> > > > > > > > > +   */
> > > > > > > > > +  function callNIX(targetId, from, rpcData) {
> > > > > > > > > +    try {
> > > > > > > > > +      if (from != '..') {
> > > > > > > > > +        // Call from gadget to the container.
> > > > > > > > > +        var handler = nix_channels['..'];
> > > > > > > > > +
> > > > > > > > > +        // If the gadget has yet to retrieve a reference
> to
> > > > > > > > > +        // the NIX handler, try to do so now. We don't do
> a
> > > > > > > > > +        // typeof(window.opener[NIX_ID_FIELD]) check here
> > > > > > > > > +        // because it means accessing that field on the
> COM
> > > > > object,
> > > > > > > > which,
> > > > > > > > > +        // being an internal function reference, is not
> > > allowed.
> > > > > > > > > +        // "in" works because it merely checks for the
> > > prescence
> > > > > of
> > > > > > > > > +        // the key, rather than actually accessing the
> > > object's
> > > > > > > > property.
> > > > > > > > > +        if (!handler && window.opener && NIX_ID_FIELD in
> > > > > > > window.opener)
> > > > > > > > {
> > > > > > > > > +          handler = nix_channels['..'] = window.opener;
> > > > > > > > > +
> > > > > > > > > +          // Create the channel to the parent/container.
> We
> > > pass
> > > > > > both
> > > > > > > > our
> > > > > > > > > +          // own wrapper and our authentication token for
> > > > > > > verification.
> > > > > > > > > +
> >  handler.CreateChannel(window[NIX_GET_WRAPPER]('..'),
> > > > > > > > > authToken['..']);
> > > > > > > > > +        }
> > > > > > > > > +
> > > > > > > > > +        // If we have a handler, call it.
> > > > > > > > > +        if (handler) {
> > > > > > > > > +          handler.SendMessage(rpcData);
> > > > > > > > > +          return;
> > > > > > > > > +        }
> > > > > > > > > +      } else {
> > > > > > > > > +        // Call from container to a gadget[targetId].
> > > > > > > > > +
> > > > > > > > > +        // If we have a handler, call it.
> > > > > > > > > +        if (nix_channels[targetId]) {
> > > > > > > > > +          nix_channels[targetId].SendMessage(rpcData);
> > > > > > > > > +          return;
> > > > > > > > > +        }
> > > > > > > > > +      }
> > > > > > > > > +    } catch (e) {
> > > > > > > > > +    }
> > > > > > > > > +
> > > > > > > > > +    // If we have reached this point, something has failed
> > > > > > > > > +    // with the NIX method, so we default to using
> > > > > > > > > +    // IFPC for this call.
> > > > > > > > > +    callIFPC(targetId, from, rpcData);
> > > > > > > > > +  }
> > > > > > > > > +
> > > > > > > > > +  /**
> > > > > > > > > +   * Attempts to conduct an RPC call to the specified
> > > > > > > > > +   * target with the specified data via the FrameElement
> > > > > > > > > +   * method. If this method fails, the system attempts
> again
> > > > > > > > > +   * using the known default of IFPC.
> > > > > > > > > +   *
> > > > > > > > > +   * @param {String} targetId Module Id of the RPC service
> > > > > provider.
> > > > > > > > > +   * @param {String} from Module Id of the calling
> provider.
> > > > > > > > > +   * @param {Object} rpcData The RPC data for this call.
> > > > > > > > > +   */
> > > > > > > > > +  function callFrameElement(targetId, from, rpcData) {
> > > > > > > > > +    try {
> > > > > > > > > +      if (from != '..') {
> > > > > > > > > +        // Call from gadget to the container.
> > > > > > > > > +        var fe = window.frameElement;
> > > > > > > > > +
> > > > > > > > > +        if (typeof fe[FE_G2C_CHANNEL] === 'function') {
> > > > > > > > > +          // Complete the setup of the FE channel if need
> > be.
> > > > > > > > > +          if (typeof fe[FE_G2C_CHANNEL][FE_C2G_CHANNEL]
> !==
> > > > > > > 'function')
> > > > > > > > {
> > > > > > > > > +            fe[FE_G2C_CHANNEL][FE_C2G_CHANNEL] =
> > > function(args) {
> > > > > > > > >               process(gadgets.json.parse(args));
> > > > > > > > >             };
> > > > > > > > >           }
> > > > > > > > > +
> > > > > > > > > +          // Conduct the RPC call.
> > > > > > > > > +          fe[FE_G2C_CHANNEL](rpcData);
> > > > > > > > > +          return;
> > > > > > > > >         }
> > > > > > > > > -      }
> > > > > > > > > +      } else {
> > > > > > > > > +        // Call from container to gadget[targetId].
> > > > > > > > > +        var frame = document.getElementById(targetId);
> > > > > > > > >
> > > > > > > > > -      var result = (services[rpc.s] ||
> > > services['']).apply(rpc,
> > > > > > > rpc.a);
> > > > > > > > > -      if (rpc.c) {
> > > > > > > > > -        gadgets.rpc.call(rpc.f, '__cb', null, rpc.c,
> > result);
> > > > > > > > > +        if (typeof frame[FE_G2C_CHANNEL] === 'function' &&
> > > > > > > > > +            typeof frame[FE_G2C_CHANNEL][FE_C2G_CHANNEL]
> ===
> > > > > > > 'function')
> > > > > > > > {
> > > > > > > > > +
> > > > > > > > > +          // Conduct the RPC call.
> > > > > > > > > +          frame[FE_G2C_CHANNEL][FE_C2G_CHANNEL](rpcData);
> > > > > > > > > +          return;
> > > > > > > > > +        }
> > > > > > > > >       }
> > > > > > > > > +    } catch (e) {
> > > > > > > > > +    }
> > > > > > > > > +
> > > > > > > > > +    // If we have reached this point, something has failed
> > > > > > > > > +    // with the FrameElement method, so we default to
> using
> > > > > > > > > +    // IFPC for this call.
> > > > > > > > > +    callIFPC(targetId, from, rpcData);
> > > > > > > > > +  }
> > > > > > > > > +
> > > > > > > > > +  /**
> > > > > > > > > +   * Conducts an RPC call to the specified
> > > > > > > > > +   * target with the specified data via the IFPC
> > > > > > > > > +   * method.
> > > > > > > > > +   *
> > > > > > > > > +   * @param {String} targetId Module Id of the RPC service
> > > > > provider.
> > > > > > > > > +   * @param {String} from Module Id of the calling
> provider.
> > > > > > > > > +   * @param {Object} rpcData The RPC data for this call.
> > > > > > > > > +   */
> > > > > > > > > +  function callIFPC(targetId, from, rpcData) {
> > > > > > > > > +    // Retrieve the relay file used by IFPC. Note that
> > > > > > > > > +    // this must be set before the call, and so we conduct
> > > > > > > > > +    // an extra check to ensure it is not blank.
> > > > > > > > > +    var relay = gadgets.rpc.getRelayUrl(targetId);
> > > > > > > > > +
> > > > > > > > > +    if (!relay) {
> > > > > > > > > +      throw new Error('No relay file assigned for IFPC');
> > > > > > > > >     }
> > > > > > > > > +
> > > > > > > > > +    // The RPC mechanism supports two formats for IFPC
> > (legacy
> > > and
> > > > > > > > > current).
> > > > > > > > > +    var src = null;
> > > > > > > > > +    if (useLegacyProtocol[targetId]) {
> > > > > > > > > +      // Format:
> > > > > > > #iframe_id&callId&num_packets&packet_num&block_of_data
> > > > > > > > > +      src = [relay, '#', encodeLegacyData([from, callId,
> 1,
> > 0,
> > > > > > > > > +             encodeLegacyData([from, serviceName, '', '',
> > > > > > > from].concat(
> > > > > > > > > +               Array.prototype.slice.call(arguments,
> > > > > > 3)))])].join('');
> > > > > > > > > +    } else {
> > > > > > > > > +      // Format: #targetId & sourceId@callId & packetNum
> &
> > > > > packetId
> > > > > > &
> > > > > > > > > packetData
> > > > > > > > > +      src = [relay, '#', targetId, '&', from, '@', callId,
> > > > > > > > > +             '&1&0&',
> encodeURIComponent(rpcData)].join('');
> > > > > > > > > +    }
> > > > > > > > > +
> > > > > > > > > +    // Conduct the IFPC call by creating the Iframe with
> > > > > > > > > +    // the relay URL and appended message.
> > > > > > > > > +    emitInvisibleIframe(src);
> > > > > > > > >   }
> > > > > > > > >
> > > > > > > > > +
> > > > > > > > >   /**
> > > > > > > > >    * Helper function to emit an invisible IFrame.
> > > > > > > > >    * @param {String} src SRC attribute of the IFrame to
> emit.
> > > > > > > > > @@ -221,6 +617,15 @@
> > > > > > > > >      * @member gadgets.rpc
> > > > > > > > >      */
> > > > > > > > >     register: function(serviceName, handler) {
> > > > > > > > > +      if (serviceName == CALLBACK_NAME) {
> > > > > > > > > +        throw new Error("Cannot overwrite callback
> > service");
> > > > > > > > > +      }
> > > > > > > > > +
> > > > > > > > > +      if (serviceName == DEFAULT_NAME) {
> > > > > > > > > +        throw new Error("Cannot overwrite default
> service:"
> > > > > > > > > +                        + " use registerDefault");
> > > > > > > > > +      }
> > > > > > > > > +
> > > > > > > > >       services[serviceName] = handler;
> > > > > > > > >     },
> > > > > > > > >
> > > > > > > > > @@ -231,6 +636,15 @@
> > > > > > > > >      * @member gadgets.rpc
> > > > > > > > >      */
> > > > > > > > >     unregister: function(serviceName) {
> > > > > > > > > +      if (serviceName == CALLBACK_NAME) {
> > > > > > > > > +        throw new Error("Cannot delete callback service");
> > > > > > > > > +      }
> > > > > > > > > +
> > > > > > > > > +      if (serviceName == DEFAULT_NAME) {
> > > > > > > > > +        throw new Error("Cannot delete default service:"
> > > > > > > > > +                        + " use unregisterDefault");
> > > > > > > > > +      }
> > > > > > > > > +
> > > > > > > > >       delete services[serviceName];
> > > > > > > > >     },
> > > > > > > > >
> > > > > > > > > @@ -272,12 +686,14 @@
> > > > > > > > >       if (callback) {
> > > > > > > > >         callbacks[callId] = callback;
> > > > > > > > >       }
> > > > > > > > > -      var from;
> > > > > > > > > +
> > > > > > > > > +      // Default to the container calling.
> > > > > > > > > +      var from = '..';
> > > > > > > > > +
> > > > > > > > >       if (targetId === '..') {
> > > > > > > > >         from = window.name;
> > > > > > > > > -      } else {
> > > > > > > > > -        from = '..';
> > > > > > > > >       }
> > > > > > > > > +
> > > > > > > > >       // Not used by legacy, create it anyway...
> > > > > > > > >       var rpcData = gadgets.json.stringify({
> > > > > > > > >         s: serviceName,
> > > > > > > > > @@ -287,53 +703,37 @@
> > > > > > > > >         t: authToken[targetId]
> > > > > > > > >       });
> > > > > > > > >
> > > > > > > > > +      var channelType = relayChannel;
> > > > > > > > > +
> > > > > > > > > +      // If we are told to use the legacy format, then we
> > must
> > > > > > > > > +      // default to IFPC.
> > > > > > > > >       if (useLegacyProtocol[targetId]) {
> > > > > > > > > -        relayChannel = 'ifpc';
> > > > > > > > > +        channelType = 'ifpc';
> > > > > > > > >       }
> > > > > > > > >
> > > > > > > > > -      switch (relayChannel) {
> > > > > > > > > -      case 'dpm': // use document.postMessage
> > > > > > > > > -        var targetDoc = targetId === '..' ?
> parent.document
> > :
> > > > > > > > > -
> > > > > >  frames[targetId].document;
> > > > > > > > > -        targetDoc.postMessage(rpcData);
> > > > > > > > > -        break;
> > > > > > > > > -      case 'wpm': // use window.postMessage
> > > > > > > > > -        var targetWin = targetId === '..' ? parent :
> > > > > > frames[targetId];
> > > > > > > > > -        targetWin.postMessage(rpcData, "*");
> > > > > > > > > -        break;
> > > > > > > > > -      default: // use 'ifpc' as a fallback mechanism
> > > > > > > > > -        var relay = gadgets.rpc.getRelayUrl(targetId);
> > > > > > > > > -        // TODO split message if too long
> > > > > > > > > -        var src;
> > > > > > > > > -        if (useLegacyProtocol[targetId]) {
> > > > > > > > > -          //
> > > > > #iframe_id&callId&num_packets&packet_num&block_of_data
> > > > > > > > > -          src = [relay, '#', encodeLegacyData([from,
> callId,
> > > 1, 0,
> > > > > > > > > -                 encodeLegacyData([from, serviceName, '',
> > '',
> > > > > > > > > from].concat(
> > > > > > > > > -                 Array.prototype.slice.call(arguments,
> > > > > > > 3)))])].join('');
> > > > > > > > > -        } else {
> > > > > > > > > -          // Try the frameElement channel if available
> > > > > > > > > -          try {
> > > > > > > > > -            if (from === '..') {
> > > > > > > > > -              // Container-to-gadget
> > > > > > > > > -              var iframe =
> > document.getElementById(targetId);
> > > > > > > > > -              if (typeof iframe.__g2c_rpc.__c2g_rpc ===
> > > > > 'function')
> > > > > > {
> > > > > > > > > -                iframe.__g2c_rpc.__c2g_rpc(rpcData);
> > > > > > > > > -                return;
> > > > > > > > > -              }
> > > > > > > > > -            } else {
> > > > > > > > > -              // Gadget-to-container
> > > > > > > > > -              if (typeof window.frameElement.__g2c_rpc ===
> > > > > > 'function')
> > > > > > > {
> > > > > > > > > -                window.frameElement.__g2c_rpc(rpcData);
> > > > > > > > > -                return;
> > > > > > > > > -              }
> > > > > > > > > -            }
> > > > > > > > > -          } catch (e) {
> > > > > > > > > -          }
> > > > > > > > > -          // # targetId & sourceId@callId & packetNum &
> > > packetId
> > > > > &
> > > > > > > > > packetData
> > > > > > > > > -          src = [relay, '#', targetId, '&', from, '@',
> > callId,
> > > > > > > > > -                 '&1&0&',
> > > encodeURIComponent(rpcData)].join('');
> > > > > > > > > -        }
> > > > > > > > > -        emitInvisibleIframe(src);
> > > > > > > > > +      switch (channelType) {
> > > > > > > > > +        case 'dpm': // use document.postMessage.
> > > > > > > > > +          var targetDoc = targetId === '..' ?
> > parent.document
> > > :
> > > > > > > > > +
> > > > > > >  frames[targetId].document;
> > > > > > > > > +          targetDoc.postMessage(rpcData);
> > > > > > > > > +          break;
> > > > > > > > > +
> > > > > > > > > +        case 'wpm': // use window.postMessage.
> > > > > > > > > +          var targetWin = targetId === '..' ? parent :
> > > > > > > frames[targetId];
> > > > > > > > > +          targetWin.postMessage(rpcData, "*");
> > > > > > > > > +          break;
> > > > > > > > > +
> > > > > > > > > +        case 'nix': // use NIX.
> > > > > > > > > +          callNIX(targetId, from, rpcData);
> > > > > > > > > +          break;
> > > > > > > > > +
> > > > > > > > > +        case 'fe': // use FrameElement.
> > > > > > > > > +          callFrameElement(targetId, from, rpcData);
> > > > > > > > > +          break;
> > > > > > > > > +
> > > > > > > > > +        default: // use 'ifpc' as a fallback mechanism.
> > > > > > > > > +          callIFPC(targetId, from, rpcData);
> > > > > > > > > +          break;
> > > > > > > > >       }
> > > > > > > > >     },
> > > > > > > > >
> > > > > > > > > @@ -372,15 +772,13 @@
> > > > > > > > >      */
> > > > > > > > >     setAuthToken: function(targetId, token) {
> > > > > > > > >       authToken[targetId] = token;
> > > > > > > > > +      setupFrame(targetId);
> > > > > > > > >     },
> > > > > > > > >
> > > > > > > > >     /**
> > > > > > > > >      * Gets the RPC relay mechanism.
> > > > > > > > > -     * @return {String} RPC relay mechanism. Supported
> > types:
> > > > > > > > > -     *                  'wpm' - Use window.postMessage
> > > (defined by
> > > > > > > > HTML5)
> > > > > > > > > -     *                  'dpm' - Use document.postMessage
> > > (defined
> > > > > by
> > > > > > > an
> > > > > > > > > early
> > > > > > > > > -     *                          draft of HTML5 and
> > implemented
> > > by
> > > > > > > Opera)
> > > > > > > > > -     *                  'ifpc' - Use invisible IFrames
> > > > > > > > > +     * @return {String} RPC relay mechanism. See above for
> > > > > > > > > +     *   a list of supported types.
> > > > > > > > >      *
> > > > > > > > >      * @member gadgets.rpc
> > > > > > > > >      */
> > > > > > > > >
> > > > > > > > > Modified:
> > > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > >
> >
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > > > > > > > URL:
> > > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > >
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java?rev=672393&r1=672392&r2=672393&view=diff
> > > > > > > > >
> > > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > >
> >
> ==============================================================================
> > > > > > > > > ---
> > > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > >
> >
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > > > > > > > (original)
> > > > > > > > > +++
> > > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > >
> >
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > > > > > > > Fri Jun 27 14:01:20 2008
> > > > > > > > > @@ -221,7 +221,8 @@
> > > > > > > > >           "a {color:#0000cc;}a:visited {color:#551a8b;}" +
> > > > > > > > >           "a:active {color:#ff0000;}" +
> > > > > > > > >           "body{margin: 0px;padding:
> > > 0px;background-color:white;}"
> > > > > +
> > > > > > > > > -          "</style></head>";
> > > > > > > > > +          "</style><base>" +
> > > gadget.getSpec().getUrl().toString()
> > > > > +
> > > > > > > > > +          "</base></head>";
> > > > > > > > >     markup.append(boilerPlate);
> > > > > > > > >     LocaleSpec localeSpec =
> > > > > > > gadget.getSpec().getModulePrefs().getLocale(
> > > > > > > > >         gadget.getContext().getLocale());
> > > > > > > > >
> > > > > > > > > Added:
> > > > > > > >
> > > incubator/shindig/trunk/javascript/container/rpctest_container.html
> > > > > > > > > URL:
> > > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > >
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_container.html?rev=672393&view=auto
> > > > > > > > >
> > > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > >
> >
> ==============================================================================
> > > > > > > > > ---
> > > > > >
> incubator/shindig/trunk/javascript/container/rpctest_container.html
> > > > > > > > > (added)
> > > > > > > > > +++
> > > > > >
> incubator/shindig/trunk/javascript/container/rpctest_container.html
> > > > > > > > Fri
> > > > > > > > > Jun 27 14:01:20 2008
> > > > > > > > > @@ -0,0 +1,90 @@
> > > > > > > > > +<!--
> > > > > > > > > +  Simple page for testing gadgets.rpc performance.
> > > > > > > > > +  Allows you to add a simulated "gadget" (in this case
> just
> > a
> > > > > static
> > > > > > > > > +  HTML page which loads gadgets.rpc also), and pass some
> > > > > > > > > +  specified number of random messages of specified size to
> > > > > > > > > +  and from it.
> > > > > > > > > +
> > > > > > > > > +  A simple recommended setup is to start up two instances
> of
> > > > > > > > > +  the Shindig Gadgets Server on two separate ports to test
> > > > > > > > > +  "real" cross-domain communication, since port is
> factored
> > > > > > > > > +  into the same-domain policy enforced by browsers.
> > > > > > > > > +
> > > > > > > > > +  If your servers are on localhost:8080 and
> localhost:8081,
> > > then
> > > > > > hit:
> > > > > > > > > +
> > > > > > >
> > > http://localhost:8080/gadgets/files/container/rpctest_container.html?
> > > > > > > > \
> > > > > > > > > +
> > > > > >
> > http://localhost:8081/gadgets/files/container/rpctest_gadget.html&\<http://localhost:8081/gadgets/files/container/rpctest_gadget.html&%5C>
> <http://localhost:8081/gadgets/files/container/rpctest_gadget.html&%5C>
> > <http://localhost:8081/gadgets/files/container/rpctest_gadget.html&%5C>
> > > <http://localhost:8081/gadgets/files/container/rpctest_gadget.html&%5C
> >
> > > > > <
> > http://localhost:8081/gadgets/files/container/rpctest_gadget.html&%5C
> > > >
> > > > > > > > > +
> > > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > >
> >
> http://localhost:8081/gadgets/files/container/rpc_relay.uncompressed.html
> > > > > > > > > +
> > > > > > > > > +  (Note the backslashes should be removed, as they exist
> for
> > > > > > > formatting
> > > > > > > > > only.)
> > > > > > > > > +
> > > > > > > > > +  There are two arguments, separated by an ampersand:
> > > > > > > > > +  1. URL of the "gadget" test page.
> > > > > > > > > +  2. URL of the "gadget" test page's RPC relay (for
> browsers
> > > > > > requiring
> > > > > > > > > it).
> > > > > > > > > +-->
> > > > > > > > > +<html>
> > > > > > > > > +  <head>
> > > > > > > > > +    <title>gadgets.rpc Performance Tests:
> Container</title>
> > > > > > > > > +    <script src="../../js/rpc.js?c=1&debug=1"></script>
> > > > > > > > > +    <script src="rpctest_perf.js"></script>
> > > > > > > > > +    <script>
> > > > > > > > > +      function initTest() {
> > > > > > > > > +        var container =
> > document.getElementById("container");
> > > > > > > > > +
> > > > > > > > > +        // query string is assumed to be the "gadget" URL
> as
> > > arg
> > > > > 1,
> > > > > > > > > +        // and optionally the relay URL as arg 2
> > > > > > > > > +        var pageArgs =
> > > > > > window.location.search.substring(1).split('&');
> > > > > > > > > +        var gadgetUrl = pageArgs[0];
> > > > > > > > > +        var secret = Math.round(Math.random()*10000000);
> > > > > > > > > +        if (pageArgs[1]) {
> > > > > > > > > +          gadgets.rpc.setRelayUrl('gadget', pageArgs[1]);
> > > > > > > > > +        }
> > > > > > > > > +        var containerRelay = pageArgs[2] || '';
> > > > > > > > > +        container.innerHTML = "<iframe id='gadget'
> > > name='gadget'
> > > > > > > > > height=300 width=300 src='" + gadgetUrl + "?parent=" +
> > > > > containerRelay
> > > > > > +
> > > > > > > > > "#rpctoken=" + secret + "'></iframe>";
> > > > > > > > > +        gadgets.rpc.setAuthToken('gadget', secret);
> > > > > > > > > +
> > > > > > > > > +        initPerfTest();
> > > > > > > > > +      };
> > > > > > > > > +    </script>
> > > > > > > > > +  </head>
> > > > > > > > > +  <body onload="initTest();">
> > > > > > > > > +    <div>gadgets.rpc Performance: Container
> Page</div><hr/>
> > > > > > > > > +    <div>Test<br/>
> > > > > > > > > +      <ul>
> > > > > > > > > +        <li>Number of messages to send:
> > > > > > > > > +          <select name="num_msgs" id="num_msgs">
> > > > > > > > > +            <option value="1">1</option>
> > > > > > > > > +            <option value="10">10</option>
> > > > > > > > > +            <option value="100" selected>100</option>
> > > > > > > > > +            <option value="1000">1000</option>
> > > > > > > > > +          </select>
> > > > > > > > > +        </li>
> > > > > > > > > +        <li>Message size:
> > > > > > > > > +          <select name="msg_size" id="msg_size">
> > > > > > > > > +            <option value="10">10 B</option>
> > > > > > > > > +            <option value="100">100 B</option>
> > > > > > > > > +            <option value="1024" selected>1 kB</option>
> > > > > > > > > +            <option value="10240">10 kB</option>
> > > > > > > > > +            <option value="102400">100 kB</option>
> > > > > > > > > +            <option value="1048576">1 MB</option>
> > > > > > > > > +          </select>
> > > > > > > > > +        </li>
> > > > > > > > > +        <li>
> > > > > > > > > +          <input type="button" value="Start The Test!"
> > > > > > > > > onclick="runPerfTest('gadget');" />
> > > > > > > > > +        </li>
> > > > > > > > > +      </ul>
> > > > > > > > > +    </div>
> > > > > > > > > +    <div id="test_running" style="display:none;">
> > > > > > > > > +      Running test...
> > > > > > > > > +    </div>
> > > > > > > > > +    <div id="results" style="display:none;">
> > > > > > > > > +      Results: Gadget-to-Container<br/>
> > > > > > > > > +      Messages: <span id="results_num_received"></span>,
> > > Bytes:
> > > > > > <span
> > > > > > > > > id="results_bytes_received"></span> <span
> > > > > id="in_or_out"></span><br/>
> > > > > > > > > +      Time elapsed for test run: <span
> > > > > > > > id="results_time_used"></span><br/>
> > > > > > > > > +      Messages/second: <span
> > > > > id="results_msgs_per_sec"></span><br/>
> > > > > > > > > +      Bytes/second: <span
> id="results_bytes_per_sec"></span>
> > > > > > > > > +    </div>
> > > > > > > > > +    <hr/>
> > > > > > > > > +    <div>Gadget:</div>
> > > > > > > > > +    <div id="container"></div>
> > > > > > > > > +  </body>
> > > > > > > > > +</html>
> > > > > > > > >
> > > > > > > > > Added:
> > > > > > incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > > > > > > > > URL:
> > > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > >
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_gadget.html?rev=672393&view=auto
> > > > > > > > >
> > > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > >
> >
> ==============================================================================
> > > > > > > > > ---
> > > > > incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > > > > > > > > (added)
> > > > > > > > > +++
> > > > > incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > > > > > > Fri
> > > > > > > > > Jun 27 14:01:20 2008
> > > > > > > > > @@ -0,0 +1,49 @@
> > > > > > > > > +<html>
> > > > > > > > > +  <head>
> > > > > > > > > +    <title>gadgets.rpc Performance Tests: Gadget</title>
> > > > > > > > > +    <script src="../../js/rpc.js?c=1&debug=1"></script>
> > > > > > > > > +    <script src="rpctest_perf.js"></script>
> > > > > > > > > +    <script>
> > > > > > > > > +      var containerRelay =
> > > window.location.search.substring(1);
> > > > > > > > > +      gadgets.rpc.setRelayUrl(null, containerRelay);
> > > > > > > > > +    </script>
> > > > > > > > > +  </head>
> > > > > > > > > +  <body onload="initPerfTest();">
> > > > > > > > > +    <div>gadgets.rpc Performance: "Gadget" page</div><hr/>
> > > > > > > > > +    <div>Test<br/>
> > > > > > > > > +      <ul>
> > > > > > > > > +        <li>Number of messages to send:
> > > > > > > > > +          <select name="num_msgs" id="num_msgs">
> > > > > > > > > +            <option value="1">1</option>
> > > > > > > > > +            <option value="10">10</option>
> > > > > > > > > +            <option value="100" selected>100</option>
> > > > > > > > > +            <option value="1000">1000</option>
> > > > > > > > > +          </select>
> > > > > > > > > +        </li>
> > > > > > > > > +        <li>Message size:
> > > > > > > > > +          <select name="msg_size" id="msg_size">
> > > > > > > > > +            <option value="10">10 B</option>
> > > > > > > > > +            <option value="100">100 B</option>
> > > > > > > > > +            <option value="1024" selected>1 kB</option>
> > > > > > > > > +            <option value="10240">10 kB</option>
> > > > > > > > > +            <option value="102400">100 kB</option>
> > > > > > > > > +            <option value="1048576">1 MB</option>
> > > > > > > > > +          </select>
> > > > > > > > > +        </li>
> > > > > > > > > +        <li>
> > > > > > > > > +          <input type="button" value="Start The Test!"
> > > > > > > > > onclick="runPerfTest();" />
> > > > > > > > > +        </li>
> > > > > > > > > +      </ul>
> > > > > > > > > +    </div>
> > > > > > > > > +    <div id="test_running" style="display:none;">
> > > > > > > > > +      Running test...
> > > > > > > > > +    </div>
> > > > > > > > > +    <div id="results" style="display:none;">
> > > > > > > > > +      Results: Gadget-to-Container<br/>
> > > > > > > > > +      Messages: <span id="results_num_received"></span>,
> > > Bytes:
> > > > > > <span
> > > > > > > > > id="results_bytes_received"></span> <span
> > > > > id="in_or_out"></span><br/>
> > > > > > > > > +      Time elapsed for test run: <span
> > > > > > > > id="results_time_used"></span><br/>
> > > > > > > > > +      Messages/second: <span
> > > > > id="results_msgs_per_sec"></span><br/>
> > > > > > > > > +      Bytes/second: <span
> id="results_bytes_per_sec"></span>
> > > > > > > > > +    </div>
> > > > > > > > > +  </body>
> > > > > > > > > +</html>
> > > > > > > > >
> > > > > > > > > Added:
> > > incubator/shindig/trunk/javascript/container/rpctest_perf.js
> > > > > > > > > URL:
> > > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > >
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_perf.js?rev=672393&view=auto
> > > > > > > > >
> > > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > >
> >
> ==============================================================================
> > > > > > > > > ---
> > > incubator/shindig/trunk/javascript/container/rpctest_perf.js
> > > > > > > (added)
> > > > > > > > > +++
> > > incubator/shindig/trunk/javascript/container/rpctest_perf.js
> > > > > Fri
> > > > > > > Jun
> > > > > > > > 27
> > > > > > > > > 14:01:20 2008
> > > > > > > > > @@ -0,0 +1,93 @@
> > > > > > > > > +var perfStats = null;
> > > > > > > > > +var currentRun = {};
> > > > > > > > > +
> > > > > > > > > +function perfService(message) {
> > > > > > > > > +  if (perfStats.numResults++ === 0) {
> > > > > > > > > +    perfStats.firstMsg = message; // stored since it has
> > > "real"
> > > > > > start
> > > > > > > > time
> > > > > > > > > +  }
> > > > > > > > > +  perfStats.bytesReceived += message.length;
> > > > > > > > > +};
> > > > > > > > > +
> > > > > > > > > +function clearPerfStats(inOrOut) {
> > > > > > > > > +  perfStats = {
> > > > > > > > > +    numResults: 0,
> > > > > > > > > +    bytesReceived: 0,
> > > > > > > > > +    firstMsg: null
> > > > > > > > > +  };
> > > > > > > > > +
> > > > > > > > > +  document.getElementById("in_or_out").innerHTML =
> inOrOut;
> > > > > > > > > +
> > > > > > > > > +  // hide results fields
> > > > > > > > > +  document.getElementById("results").style.display =
> "none";
> > > > > > > > > +};
> > > > > > > > > +
> > > > > > > > > +function completePerfStats() {
> > > > > > > > > +  perfStats.timeEnded = new Date().getTime();
> > > > > > > > > +
> > > > > > > > > +  // get time started from the first sent message
> > > > > > > > > +  perfStats.timeStarted = perfStats.firstMsg.substr(0,
> > > > > > > > > perfStats.firstMsg.indexOf(':'));
> > > > > > > > > +
> > > > > > > > > +  var timeUsedMs = perfStats.timeEnded -
> > > perfStats.timeStarted;
> > > > > > > > > +
> > > > > > > > > +  // fill in fields
> > > > > > > > > +
>  document.getElementById("results_num_received").innerHTML
> > =
> > > > > > > > > perfStats.numResults;
> > > > > > > > > +
> >  document.getElementById("results_bytes_received").innerHTML
> > > =
> > > > > > > > > perfStats.bytesReceived;
> > > > > > > > > +  document.getElementById("results_time_used").innerHTML =
> > > > > > timeUsedMs
> > > > > > > +
> > > > > > > > > "ms";
> > > > > > > > > +
>  document.getElementById("results_msgs_per_sec").innerHTML
> > =
> > > > > > > > > (perfStats.numResults / (timeUsedMs / 1000));
> > > > > > > > > +
>  document.getElementById("results_bytes_per_sec").innerHTML
> > =
> > > > > > > > > (perfStats.bytesReceived / (timeUsedMs / 1000));
> > > > > > > > > +  document.getElementById("test_running").style.display =
> > > "none";
> > > > > > > > > +  document.getElementById("results").style.display = "";
> > > > > > > > > +};
> > > > > > > > > +
> > > > > > > > > +function initPerfTest() {
> > > > > > > > > +  clearPerfStats();
> > > > > > > > > +  gadgets.rpc.register("perf_service", perfService);
> > > > > > > > > +  gadgets.rpc.register("clear_perf_stats",
> clearPerfStats);
> > > > > > > > > +  gadgets.rpc.register("complete_perf_stats",
> > > completePerfStats);
> > > > > > > > > +};
> > > > > > > > > +
> > > > > > > > > +var alphabet =
> > > > > > > > >
> > "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
> > > > > > > > _-*&(){}'";
> > > > > > > > > +
> > > > > > > > > +function sendPerfMessage() {
> > > > > > > > > +  var msgToSend = currentRun.msg;
> > > > > > > > > +  if (currentRun.curMsgId++ <= 1) {
> > > > > > > > > +    var nowString = new Date().getTime() + ':';
> > > > > > > > > +    msgToSend = nowString +
> > > > > > > currentRun.msg.substring(nowString.length);
> > > > > > > > > +  }
> > > > > > > > > +
> > > > > > > > > +  gadgets.rpc.call(currentRun.targetId, "perf_service",
> > null,
> > > > > > > > msgToSend);
> > > > > > > > > +  if (currentRun.curMsgId < currentRun.endMsgId) {
> > > > > > > > > +    // loop, giving up execution in case rpc technique
> > demands
> > > it
> > > > > > > > > +    window.setTimeout(sendPerfMessage, 0);
> > > > > > > > > +  } else {
> > > > > > > > > +    // send finisher
> > > > > > > > > +    window.setTimeout(function() {
> > > > > > > gadgets.rpc.call(currentRun.targetId,
> > > > > > > > > "complete_perf_stats", null); }, 0);
> > > > > > > > > +  }
> > > > > > > > > +};
> > > > > > > > > +
> > > > > > > > > +function runPerfTest(targetId) {
> > > > > > > > > +  document.getElementById("test_running").style.display =
> > "";
> > > > > > > > > +
> > > > > > > > > +  // initialize the current run
> > > > > > > > > +  var num_msgs =
> document.getElementById("num_msgs").value;
> > > > > > > > > +  var msg_size =
> document.getElementById("msg_size").value;
> > > > > > > > > +
> > > > > > > > > +  currentRun.targetId = targetId;
> > > > > > > > > +  currentRun.curMsgId = 0;
> > > > > > > > > +  currentRun.endMsgId = num_msgs;
> > > > > > > > > +
> > > > > > > > > +  var msg = [];
> > > > > > > > > +  for (var i = 0; i < msg_size; ++i) {
> > > > > > > > > +    msg[i] =
> > > > > > > alphabet.charAt(Math.round(Math.random(alphabet.length)));
> > > > > > > > > +  }
> > > > > > > > > +  currentRun.msg = msg.join('');
> > > > > > > > > +
> > > > > > > > > +  // clear local perf stats
> > > > > > > > > +  clearPerfStats("(outbound)");
> > > > > > > > > +
> > > > > > > > > +  // clear target perf stats
> > > > > > > > > +  gadgets.rpc.call(targetId, "clear_perf_stats", null,
> > > > > "(inbound)");
> > > > > > > > > +
> > > > > > > > > +  // kick off the send loop
> > > > > > > > > +  sendPerfMessage();
> > > > > > > > > +};
> > > > > > > > >
> > > > > > > > >
> > > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > >
> >
>

Re: svn commit: r672393 - in /incubator/shindig/trunk: features/rpc/ java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ javascript/container/

Posted by Kevin Brown <et...@google.com>.
And the performance improvements look good, to be sure -- you just can't
drop something that big on people's lap though.

On Fri, Jun 27, 2008 at 4:57 PM, John Hjelmstad <fa...@google.com> wrote:

> That sounds fair. To be honest, if you want I can revert the whole thing
> and
> attach it to a JIRA issue. Sorta let my excitement about the performance
> improvement put the cart in front of the horse ;)
>
> John
>
> On Fri, Jun 27, 2008 at 4:55 PM, Chris Chabot <ch...@xs4all.nl> wrote:
>
> > I'm kind of on board with Kevin that this probably should have been done
> > the apache way, there's a lot of very big containers that use shindig,
> > and hundreds of smaller ones too .. We're an apache project to make sure
> > everything happens democratically, and without that in place people
> > might not we're ready to graduate out of incubation you know :-)
> >
> > As a fellow committer i for one would've liked to at least take a quick
> > look at, or at least hear about it on the mailing list it before seeing
> > it show up 'out of nowhere', at least with other big changes we know
> > it's being worked on in advance, and we then have the chance to see the
> > patch cook in jira so people could respond to it if they wanted (like,
> > positively community like even! :P)
> >
> > Anyhow, for now the compromise works for me, but let's do it differently
> > next time ok :-)
> >
> >    -- Chris
> >
> > On Fri, 2008-06-27 at 16:39 -0700, John Hjelmstad wrote:
> >
> > > How's this for a compromise: I'll add a param that disables the new
> > > technique by default, and allows it to be used if preferred. The rest
> is
> > > refactoring and documentation. Given that's the bulk of the code change
> > in
> > > terms of volume, I didn't create a JIRA issue for it. I'm happy to do
> so
> > to
> > > enable it longer-term, however. Thoughts?
> > >
> > > John
> > >
> > > On Fri, Jun 27, 2008 at 4:27 PM, Kevin Brown <et...@google.com> wrote:
> > >
> > > > On Fri, Jun 27, 2008 at 3:45 PM, John Hjelmstad <fa...@google.com>
> > wrote:
> > > >
> > > > > As indicated in the CL, I tested this on half a dozen browsers. We
> > don't
> > > > > have unit tests for it. I've also sent the code and inquiries about
> > the
> > > > > technique to several poeple, some of whom expressed concerns but no
> > > > ability
> > > > > to actively exploit the code.
> > > >
> > > >
> > > > Anyone on this mailing list? Either of the people I mentioned?
> > > >
> > > >
> > > > > The main concern was around VBScript
> > > > > knowledge, which few people I know extensively have. That was true
> of
> > > > IFPC
> > > > > when it was originally released as well. If any attacks are found,
> > they
> > > > > will
> > > > > be actively closed or the technique will be removed. Would you
> prefer
> > > > that
> > > > > no code, irrespective of its value, ever be submitted? It's
> possible
> > for
> > > > > any
> > > > > code to have bugs, and a lot of code to have security holes. If
> > found,
> > > > > they're fixed.
> > > >
> > > >
> > > > Yes, but this is code that uses a security technique that is highly
> > > > questionable and has been flagged as potentially problematic by
> several
> > > > people. Therefore, it needs more scrutiny. I won't deploy this on any
> > of
> > > > our
> > > > production systems until I get an OK from people who are actually
> > familiar
> > > > with the problems in question have chimed in.
> > > >
> > > > This is also a very large code change, and, per policy, large code
> > changes
> > > > need JIRA tickets and feedback from other people. There are hundreds
> > (if
> > > > not
> > > > thousands) of people using Shindig today and the code is deployed to
> > > > hundreds of millions of users.
> > > >
> > > >
> > > > >
> > > > > John
> > > > >
> > > > > On Fri, Jun 27, 2008 at 3:38 PM, Kevin Brown <et...@google.com>
> > wrote:
> > > > >
> > > > > > Please actually test your code or send it to other shindig
> > committers
> > > > > > before
> > > > > > you commit it. Your change to GadgetRenderingTask breaks proxying
> > and
> > > > > > makeRequest in several cases. Fix it.
> > > > > >
> > > > > > Have you run this by Brian and/or Mike Samuel? They both
> expressed
> > > > doubts
> > > > > > about whether this mechanism was actually secure.
> > > > > >
> > > > > > On Fri, Jun 27, 2008 at 2:06 PM, John Hjelmstad <
> fargo@google.com>
> > > > > wrote:
> > > > > >
> > > > > > > +jschorr in case there are comments.
> > > > > > >
> > > > > > > On Fri, Jun 27, 2008 at 2:01 PM, <jo...@apache.org> wrote:
> > > > > > >
> > > > > > > > Author: johnh
> > > > > > > > Date: Fri Jun 27 14:01:20 2008
> > > > > > > > New Revision: 672393
> > > > > > > >
> > > > > > > > URL: http://svn.apache.org/viewvc?rev=672393&view=rev
> > > > > > > > Log:
> > > > > > > > Cleaned up gadgets.rpc library implementation, and
> implemented
> > fast
> > > > > IE
> > > > > > > > transport layer.
> > > > > > > >
> > > > > > > > Credit:
> > > > > > > >        Joseph Schorr (jschorr@google.com) and I implemented
> > this
> > > > > > > together,
> > > > > > > > but he
> > > > > > > >        really did all the hard work, as well as developing
> the
> > NIX
> > > > > > > > technique's
> > > > > > > >        fundamentals. Huge thanks to Joseph!
> > > > > > > >
> > > > > > > > Details:
> > > > > > > >        * Created a new relayChannel for Firefox frameElement
> > > > > technique,
> > > > > > > >          making its implementation more linear to read.
> > > > > > > >        * Consolidated all transport-specific code in
> > > > setupChannel(),
> > > > > > > > setupFrame(),
> > > > > > > >          and callX(), where X = relay type. This refactoring
> > makes
> > > > > > setup
> > > > > > > > and use
> > > > > > > >          of each transport more clear, and makes it possible
> in
> > > > later
> > > > > > > > revisions
> > > > > > > >          to separate out each method as its own class. In
> > theory,
> > > > if
> > > > > we
> > > > > > > can
> > > > > > > > trust
> > > > > > > >          things like User-Agent headers, we can use this to
> > reduce
> > > > > > rpc.js
> > > > > > > > code
> > > > > > > >          size significantly by only including the needed
> > > > > transport(s).
> > > > > > > >        * Implemented "NIX" - Native IE XDC. This method
> > exploits
> > > > the
> > > > > > fact
> > > > > > > >          that in IE6 and IE7 the window.opener property can
> be
> > both
> > > > > set
> > > > > > > and
> > > > > > > >          read across domain boundaries for an IFRAME, and
> that
> > any
> > > > > > object
> > > > > > > >          can be passed through it. In particular, functions
> > > > > > implementing
> > > > > > > >          message passing can be passed. These can't be JS
> > > > functions,
> > > > > > > > however,
> > > > > > > >          since those can leak sender context. So, VBScript
> > (COM)
> > > > > > objects
> > > > > > > >          wrapping these are passed to maintain context
> > isolation.
> > > > > > > >          - Requires for IE6/7 that rpc.js be included in the
> > > > > container
> > > > > > > >            at load time, not dynamically. TODO added to
> > consider
> > > > > > > detecting
> > > > > > > >            whether dynamic loading is happening (at which
> point
> > we
> > > > > > should
> > > > > > > >            fall back to IFPC).
> > > > > > > >          - Message channel handshake *and* message passing
> > > > validated
> > > > > > > >            using authToken, as with other channels.
> > > > > > > >          - Impl requires that gadget call container first -
> > else
> > > > IFPC
> > > > > > > >            is used. This is the same as the frameElement
> > technique.
> > > > > We
> > > > > > > >            could add a setInterval()-based initialization
> > routine
> > > > > later
> > > > > > > >            if needed. To date, the only gadgets.rpc calls
> made
> > from
> > > > > > > >            container to gadget without gadget to container
> > first
> > > > are
> > > > > > > >            in theory pubsub messages.
> > > > > > > >          - Extensive documentation on this technique and the
> > others
> > > > > > > >            added to the comments, since they're stripped out
> in
> > the
> > > > > > > >            majority case.
> > > > > > > >        * Implemented quick-and-dirty performance testing page
> > > > > verifying
> > > > > > > >          the library works and giving basic performance
> > metrics.
> > > > > > > >          This needs to be improved! But does the trick for
> > now...
> > > > > > > >
> > > > > > > > Testing/Data:
> > > > > > > >
> > > > > > > > Library verified on the following browsers with the RPC test
> > page.
> > > > > For
> > > > > > > > each,
> > > > > > > > a "bandwidth" measure was taken by averaging the results of 6
> > G2C
> > > > and
> > > > > > C2G
> > > > > > > > runs
> > > > > > > > with 100 messages being passed of 1K apiece. Units are 1K
> > > > > messages/sec.
> > > > > > > > The "latency" value is the average amount of time taken, over
> 6
> > G2C
> > > > > and
> > > > > > > C2G
> > > > > > > > runs,
> > > > > > > > to send 1 1K message. Happily, for all browsers save Safari
> > (soon
> > > > due
> > > > > > to
> > > > > > > > get
> > > > > > > > window.postMessage), latency is around or below time required
> > to
> > > > > > achieve
> > > > > > > > a cross-domain call without user-perceived delay.
> > > > > > > >
> > > > > > > >        * Firefox 3 (WPM) - bandwidth: 88 kB/sec; latency:
> 18ms.
> > > > > > > >        * Firefox 2 (FE)  - bandwidth: 80 kB/sec; latency:
> 15ms.
> > > > > > > >        * Opera 9   (WPM) - bandwidth: 85 kB/sec; latency:
> 7ms.
> > > > > > > >        * IE6       (NIX) - bandwidth: 64 kB/sec; latency:
> 18ms.
> > > > > > > >        * IE7       (NIX) - bandwidth: 64 kB/sec; latency:
> 22ms.
> > > > > > > >        * Safari 3 (IFPC) - bandwidth: ?6-8kB/sec; latency:
> > > > > ?100-200ms.
> > > > > > > >          - Safari is somewhat flaky with the RPC test page,
> > before
> > > > > > > >            and after the rpc.js change. Multiple messages
> seem
> > to
> > > > > > > >            confuse it from time to time.
> > > > > > > >
> > > > > > > >
> > > > > > > > Added:
> > > > > > > >
> > > > >
>  incubator/shindig/trunk/javascript/container/rpctest_container.html
> > > > > > > >
> >  incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > > > > > > >
>  incubator/shindig/trunk/javascript/container/rpctest_perf.js
> > > > > > > > Modified:
> > > > > > > >    incubator/shindig/trunk/features/rpc/rpc.js
> > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> >
>  incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > > > > > >
> > > > > > > > Modified: incubator/shindig/trunk/features/rpc/rpc.js
> > > > > > > > URL:
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/features/rpc/rpc.js?rev=672393&r1=672392&r2=672393&view=diff
> > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> >
> ==============================================================================
> > > > > > > > --- incubator/shindig/trunk/features/rpc/rpc.js (original)
> > > > > > > > +++ incubator/shindig/trunk/features/rpc/rpc.js Fri Jun 27
> > 14:01:20
> > > > > > 2008
> > > > > > > > @@ -18,7 +18,7 @@
> > > > > > > >
> > > > > > > >  /**
> > > > > > > >  * @fileoverview Remote procedure call library for
> > > > > gadget-to-container,
> > > > > > > > - * container-to-gadget, and gadget-to-gadget communication.
> > > > > > > > + * container-to-gadget, and gadget-to-gadget (thru
> container)
> > > > > > > > communication.
> > > > > > > >  */
> > > > > > > >
> > > > > > > >  var gadgets = gadgets || {};
> > > > > > > > @@ -29,6 +29,30 @@
> > > > > > > >  * @name gadgets.rpc
> > > > > > > >  */
> > > > > > > >  gadgets.rpc = function() {
> > > > > > > > +  // General constants.
> > > > > > > > +  var CALLBACK_NAME = '__cb';
> > > > > > > > +  var DEFAULT_NAME = '';
> > > > > > > > +
> > > > > > > > +  // Consts for FrameElement.
> > > > > > > > +  var FE_G2C_CHANNEL = '__g2c_rpc';
> > > > > > > > +  var FE_C2G_CHANNEL = '__c2g_rpc';
> > > > > > > > +
> > > > > > > > +  // Consts for NIX. VBScript doesn't
> > > > > > > > +  // allow items to start with _ for some reason,
> > > > > > > > +  // so we need to make these names quite unique, as
> > > > > > > > +  // they will go into the global namespace.
> > > > > > > > +  var NIX_WRAPPER = 'GRPC____NIXVBS_wrapper';
> > > > > > > > +  var NIX_GET_WRAPPER = 'GRPC____NIXVBS_get_wrapper';
> > > > > > > > +  var NIX_HANDLE_MESSAGE = 'GRPC____NIXVBS_handle_message';
> > > > > > > > +  var NIX_CREATE_CHANNEL = 'GRPC____NIXVBS_create_channel';
> > > > > > > > +  var NIX_ID_FIELD = 'GRPC____NIXVBS_container';
> > > > > > > > +
> > > > > > > > +  // JavaScript reference to the NIX VBScript wrappers.
> > > > > > > > +  // Gadgets will have but a single channel under
> > > > > > > > +  // nix_channels['..'] while containers will have a channel
> > > > > > > > +  // per gadget stored under the gadget's ID.
> > > > > > > > +  var nix_channels = {};
> > > > > > > > +
> > > > > > > >   var services = {};
> > > > > > > >   var iframePool = [];
> > > > > > > >   var relayUrl = {};
> > > > > > > > @@ -36,28 +60,135 @@
> > > > > > > >   var authToken = {};
> > > > > > > >   var callId = 0;
> > > > > > > >   var callbacks = {};
> > > > > > > > +  var setup = {};
> > > > > > > > +
> > > > > > > > +  var params = {};
> > > > > > > > +
> > > > > > > > +  // Load the authentication token for speaking to the
> > container
> > > > > > > > +  // from the gadget's parameters, or default to '0' if not
> > found.
> > > > > > > > +  if (gadgets.util) {
> > > > > > > > +    params = gadgets.util.getUrlParameters();
> > > > > > > > +  }
> > > > > > > >
> > > > > > > > -  var params = gadgets.util.getUrlParameters();
> > > > > > > >   authToken['..'] = params.rpctoken || params.ifpctok || 0;
> > > > > > > >
> > > > > > > > -  // Pick the most efficient RPC relay mechanism
> > > > > > > > -  var relayChannel = typeof document.postMessage ===
> > 'function' ?
> > > > > > 'dpm'
> > > > > > > :
> > > > > > > > -                     typeof window.postMessage ===
> 'function'
> > ?
> > > > > 'wpm'
> > > > > > :
> > > > > > > > +  // Pick the most efficient RPC relay mechanism:
> > > > > > > > +  //
> > > > > > > > +  // * For those browsers that support native messaging
> > (various
> > > > > > > > implementations
> > > > > > > > +  //   of the HTML5 postMessage method), use that.
> Officially
> > > > > defined
> > > > > > at
> > > > > > > > +  //
> > > > > > > >
> > > > >
> > http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html
> > > > > > .
> > > > > > > > +  //
> > > > > > > > +  //   postMessage is a native implementation of XDC. A page
> > > > > registers
> > > > > > > > that
> > > > > > > > +  //   it would like to receive messages by listening the
> the
> > > > > > "message"
> > > > > > > > event
> > > > > > > > +  //   on the window (document in DPM) object. In turn,
> > another
> > > > page
> > > > > > can
> > > > > > > > +  //   raise that event by calling window.postMessage
> > > > > > > > (document.postMessage
> > > > > > > > +  //   in DPM) with a string representing the message and a
> > string
> > > > > > > > +  //   indicating on which domain the receiving page must be
> > to
> > > > > > receive
> > > > > > > > +  //   the message. The target page will then have its
> > "message"
> > > > > event
> > > > > > > > raised
> > > > > > > > +  //   if the domain matches and can, in turn, check the
> > origin of
> > > > > the
> > > > > > > > message
> > > > > > > > +  //   and process the data contained within.
> > > > > > > > +  //
> > > > > > > > +  //     wpm: postMessage on the window object.
> > > > > > > > +  //        - Internet Explorer 8+
> > > > > > > > +  //        - Safari (latest nightlies as of 26/6/2008)
> > > > > > > > +  //        - Firefox 3+
> > > > > > > > +  //        - Opera 9+
> > > > > > > > +  //
> > > > > > > > +  //     dpm: postMessage on the document object.
> > > > > > > > +  //        - Opera 8+
> > > > > > > > +  //
> > > > > > > > +  // * For Internet Explorer before version 8, the security
> > model
> > > > > > allows
> > > > > > > a
> > > > > > > > +  //   child to set the value of the "opener" property on
> the
> > > > parent
> > > > > > > > window
> > > > > > > > +  //   and vice versa. This method is dubbed "Native IE XDC"
> > > > (NIX).
> > > > > > > > +  //
> > > > > > > > +  //   This method works by placing a handler object in the
> > > > "opener"
> > > > > > > > property
> > > > > > > > +  //   of a gadget when the container sets up the
> > authentication
> > > > > > > > information
> > > > > > > > +  //   for that gadget (by calling setAuthToken(...)). At
> that
> > > > > point,
> > > > > > a
> > > > > > > > NIX
> > > > > > > > +  //   wrapper is created and placed into the gadget by
> > calling
> > > > > > > > +  //   theframe.contentWindow.opener = wrapper. Note that as
> a
> > > > > result,
> > > > > > > NIX
> > > > > > > > can
> > > > > > > > +  //   only be used by a container to call a particular
> gadget
> > > > > *after*
> > > > > > > > that
> > > > > > > > +  //   gadget has called the container at least once via
> NIX.
> > > > > > > > +  //
> > > > > > > > +  //   The NIX wrappers in this RPC implementation are
> > instances
> > > > of
> > > > > a
> > > > > > > > VBScript
> > > > > > > > +  //   class that is created when this implementation loads.
> > The
> > > > > > reason
> > > > > > > > for
> > > > > > > > +  //   using a VBScript class stems from the fact that any
> > object
> > > > > can
> > > > > > be
> > > > > > > > passed
> > > > > > > > +  //   into the opener property.
> > > > > > > > +  //   While this is a good thing, as it lets us pass
> > functions
> > > > and
> > > > > > > setup
> > > > > > > > a true
> > > > > > > > +  //   bidirectional channel via callbacks, it opens a
> > potential
> > > > > > > security
> > > > > > > > hole
> > > > > > > > +  //   by which the other page can get ahold of the "window"
> > or
> > > > > > > "document"
> > > > > > > > +  //   objects in the parent page and in turn wreak havok.
> > This is
> > > > > due
> > > > > > > to
> > > > > > > > the
> > > > > > > > +  //   fact that any JS object useful for establishing such
> a
> > > > > > > > bidirectional
> > > > > > > > +  //   channel (such as a function) can be used to access a
> > > > function
> > > > > > > > +  //   (eg. obj.toString, or a function itself) created in a
> > > > > specific
> > > > > > > > context,
> > > > > > > > +  //   in particular the global context of the sender.
> Suppose
> > > > > > container
> > > > > > > > +  //   domain C passes object obj to gadget on domain G.
> Then
> > the
> > > > > > gadget
> > > > > > > > can
> > > > > > > > +  //   access C's global context using:
> > > > > > > > +  //   var parentWindow = (new
> > obj.toString.constructor("return
> > > > > > > > window;"))();
> > > > > > > > +  //   Nulling out all of obj's properties doesn't fix this,
> > since
> > > > > IE
> > > > > > > > helpfully
> > > > > > > > +  //   restores them to their original values if you do
> > something
> > > > > > like:
> > > > > > > > +  //   delete obj.toString; delete obj.toString;
> > > > > > > > +  //   Thus, we wrap the necessary functions and information
> > > > inside
> > > > > a
> > > > > > > > VBScript
> > > > > > > > +  //   object. VBScript objects in IE, like DOM objects, are
> > in
> > > > fact
> > > > > > COM
> > > > > > > > +  //   wrappers when used in JavaScript, so we can safely
> pass
> > > > them
> > > > > > > around
> > > > > > > > +  //   without worrying about a breach of context while at
> the
> > > > same
> > > > > > time
> > > > > > > > +  //   allowing them to act as a pass-through mechanism for
> > > > > > information
> > > > > > > > +  //   and function calls. The implementation details of
> this
> > > > > VBScript
> > > > > > > > wrapper
> > > > > > > > +  //   can be found in the setupChannel() method below.
> > > > > > > > +  //
> > > > > > > > +  //     nix: Internet Explorer-specific window.opener
> trick.
> > > > > > > > +  //       - Internet Explorer 6
> > > > > > > > +  //       - Internet Explorer 7
> > > > > > > > +  //
> > > > > > > > +  // * For Gecko-based browsers, the security model allows a
> > child
> > > > > to
> > > > > > > call
> > > > > > > > a
> > > > > > > > +  //   function on the frameElement of the iframe, even if
> the
> > > > child
> > > > > > is
> > > > > > > in
> > > > > > > > +  //   a different domain. This method is dubbed
> > "frameElement"
> > > > > (fe).
> > > > > > > > +  //
> > > > > > > > +  //   The ability to add and call such functions on the
> > > > > frameElement
> > > > > > > > allows
> > > > > > > > +  //   a bidirectional channel to be setup via the adding of
> > > > simple
> > > > > > > > function
> > > > > > > > +  //   references on the frameElement object itself. In this
> > > > > > > > implementation,
> > > > > > > > +  //   when the container sets up the authentication
> > information
> > > > for
> > > > > > > that
> > > > > > > > gadget
> > > > > > > > +  //   (by calling setAuth(...)) it as well adds a special
> > > > function
> > > > > on
> > > > > > > the
> > > > > > > > +  //   gadget's iframe. This function can then be used by
> the
> > > > gadget
> > > > > > to
> > > > > > > > send
> > > > > > > > +  //   messages to the container. In turn, when the gadget
> > tries
> > > > to
> > > > > > send
> > > > > > > a
> > > > > > > > +  //   message, it checks to see if this function has its
> own
> > > > > function
> > > > > > > > stored
> > > > > > > > +  //   that can be used by the container to call the gadget.
> > If
> > > > not,
> > > > > > the
> > > > > > > > +  //   function is created and subsequently used by the
> > container.
> > > > > > > > +  //   Note that as a result, FE can only be used by a
> > container
> > > > to
> > > > > > call
> > > > > > > a
> > > > > > > > +  //   particular gadget *after* that gadget has called the
> > > > > container
> > > > > > at
> > > > > > > > +  //   least once via FE.
> > > > > > > > +  //
> > > > > > > > +  //     fe: Gecko-specific frameElement trick.
> > > > > > > > +  //        - Firefox 1+
> > > > > > > > +  //
> > > > > > > > +  // * For all others, we have a fallback mechanism known as
> > > > "ifpc".
> > > > > > > IFPC
> > > > > > > > +  //   exploits the fact that while same-origin policy
> > prohibits a
> > > > > > frame
> > > > > > > > from
> > > > > > > > +  //   accessing members on a window not in the same domain,
> > that
> > > > > > frame
> > > > > > > > can,
> > > > > > > > +  //   however, navigate the window heirarchy (via parent).
> > This
> > > > is
> > > > > > > > exploited by
> > > > > > > > +  //   having a page on domain A that wants to talk to
> domain
> > B
> > > > > create
> > > > > > > an
> > > > > > > > iframe
> > > > > > > > +  //   on domain B pointing to a special relay file and with
> a
> > > > > message
> > > > > > > > encoded
> > > > > > > > +  //   after the hash (#). This relay, in turn, finds the
> page
> > on
> > > > > > domain
> > > > > > > > B, and
> > > > > > > > +  //   can call a receipt function with the message given to
> > it.
> > > > The
> > > > > > > relay
> > > > > > > > URL
> > > > > > > > +  //   used by each caller is set via the
> > > > > gadgets.rpc.setRelayUrl(..)
> > > > > > > and
> > > > > > > > +  //   *must* be called before the call method is used.
> > > > > > > > +  //
> > > > > > > > +  //     ifpc: Iframe-based method, utilizing a relay page,
> to
> > > > send
> > > > > a
> > > > > > > > message.
> > > > > > > > +  //
> > > > > > > > +  var relayChannel = typeof window.postMessage ===
> 'function'
> > ?
> > > > > 'wpm'
> > > > > > :
> > > > > > > > +                    typeof document.postMessage ===
> 'function'
> > ?
> > > > > 'dpm'
> > > > > > :
> > > > > > > > +                    window.ActiveXObject ? 'nix' :
> > > > > > > > +                     navigator.product === 'Gecko' ? 'fe' :
> > > > > > > >                      'ifpc';
> > > > > > > > -  if (relayChannel === 'dpm' || relayChannel === 'wpm') {
> > > > > > > > -    window.addEventListener('message', function(packet) {
> > > > > > > > -      // TODO validate packet.domain for security reasons
> > > > > > > > -      process(gadgets.json.parse(packet.data));
> > > > > > > > -    }, false);
> > > > > > > > -  }
> > > > > > > >
> > > > > > > > -  // Default RPC handler
> > > > > > > > -  services[''] = function() {
> > > > > > > > +  // Conduct any setup necessary for the chosen channel.
> > > > > > > > +  setupChannel();
> > > > > > > > +
> > > > > > > > +  // Create the Default RPC handler.
> > > > > > > > +  services[DEFAULT_NAME] = function() {
> > > > > > > >     throw new Error('Unknown RPC service: ' + this.s);
> > > > > > > >   };
> > > > > > > >
> > > > > > > > -  // Special RPC handler for callbacks
> > > > > > > > -  services['__cb'] = function(callbackId, result) {
> > > > > > > > +  // Create a Special RPC handler for callbacks.
> > > > > > > > +  services[CALLBACK_NAME] = function(callbackId, result) {
> > > > > > > >     var callback = callbacks[callbackId];
> > > > > > > >     if (callback) {
> > > > > > > >       delete callbacks[callbackId];
> > > > > > > > @@ -66,6 +197,147 @@
> > > > > > > >   };
> > > > > > > >
> > > > > > > >   /**
> > > > > > > > +   * Conducts any initial global work necessary to setup the
> > > > > > > > +   * channel type chosen.
> > > > > > > > +   */
> > > > > > > > +  function setupChannel() {
> > > > > > > > +    // If the channel type is one of the native
> > > > > > > > +    // postMessage based ones, setup the handler to receive
> > > > > > > > +    // messages.
> > > > > > > > +    if (relayChannel === 'dpm' || relayChannel === 'wpm') {
> > > > > > > > +      window.addEventListener('message', function(packet) {
> > > > > > > > +        // TODO validate packet.domain for security reasons
> > > > > > > > +        process(gadgets.json.parse(packet.data));
> > > > > > > > +      }, false);
> > > > > > > > +    }
> > > > > > > > +
> > > > > > > > +    // If the channel type is NIX, we need to ensure the
> > > > > > > > +    // VBScript wrapper code is in the page and that the
> > > > > > > > +    // global Javascript handlers have been set.
> > > > > > > > +    if (relayChannel === 'nix') {
> > > > > > > > +      // VBScript methods return a type of 'unknown' when
> > > > > > > > +      // checked via the typeof operator in IE. Fortunately
> > > > > > > > +      // for us, this only applies to COM objects, so we
> > > > > > > > +      // won't see this for a real Javascript object.
> > > > > > > > +      if (typeof window[NIX_GET_WRAPPER] !== 'unknown') {
> > > > > > > > +        window[NIX_HANDLE_MESSAGE] = function(data) {
> > > > > > > > +          process(gadgets.json.parse(data));
> > > > > > > > +        };
> > > > > > > > +
> > > > > > > > +        window[NIX_CREATE_CHANNEL] = function(name, channel,
> > > > token)
> > > > > {
> > > > > > > > +          // Verify the authentication token of the gadget
> > trying
> > > > > > > > +          // to create a channel for us.
> > > > > > > > +          if (authToken[name] == token) {
> > > > > > > > +            nix_channels[name] = channel;
> > > > > > > > +          }
> > > > > > > > +        };
> > > > > > > > +
> > > > > > > > +        // Inject the VBScript code needed.
> > > > > > > > +        var vbscript =
> > > > > > > > +          '<scr' + 'ipt language="VBScript">'
> > > > > > > > +          // We create a class to act as a wrapper for
> > > > > > > > +          // a Javascript call, to prevent a break in of
> > > > > > > > +          // the context.
> > > > > > > > +          + 'Class ' + NIX_WRAPPER + '\n '
> > > > > > > > +
> > > > > > > > +          // An internal member for keeping track of the
> > > > > > > > +          // name of the document (container or gadget)
> > > > > > > > +          // for which this wrapper is intended. For
> > > > > > > > +          // those wrappers created by gadgets, this is not
> > > > > > > > +          // used (although it is set to "..")
> > > > > > > > +          + 'Private m_Intended\n'
> > > > > > > > +
> > > > > > > > +          // Method for internally setting the value
> > > > > > > > +          // of the m_Intended property.
> > > > > > > > +          + 'Public Sub SetIntendedName(name)\n '
> > > > > > > > +          + 'm_Intended = name\n'
> > > > > > > > +          + 'End Sub\n'
> > > > > > > > +
> > > > > > > > +          // A wrapper method which actually causes a
> > > > > > > > +          // message to be sent to the other context.
> > > > > > > > +          + 'Public Sub SendMessage(data)\n '
> > > > > > > > +          + NIX_HANDLE_MESSAGE + '(data)\n'
> > > > > > > > +          + 'End Sub\n'
> > > > > > > > +
> > > > > > > > +          // Method for setting up the container->gadget
> > > > > > > > +          // channel. Not strictly needed in the gadget's
> > > > > > > > +          // wrapper, but no reason to get rid of it. Note
> > here
> > > > > > > > +          // that we pass the intended name to the
> > > > > NIX_CREATE_CHANNEL
> > > > > > > > +          // method so that it can save the channel in the
> > proper
> > > > > > place
> > > > > > > > +          // *and* verify the channel via the authentication
> > token
> > > > > > > passed
> > > > > > > > +          // here.
> > > > > > > > +          + 'Public Sub CreateChannel(channel, auth)\n '
> > > > > > > > +          + 'Call ' + NIX_CREATE_CHANNEL + '(m_Intended,
> > channel,
> > > > > > > auth)\n'
> > > > > > > > +          + 'End Sub\n'
> > > > > > > > +
> > > > > > > > +          // An empty field with a unique identifier to
> > > > > > > > +          // prevent the code from confusing this wrapper
> > > > > > > > +          // with a run-of-the-mill value found in
> > window.opener.
> > > > > > > > +          + 'Public Sub ' + NIX_ID_FIELD + '()\n '
> > > > > > > > +          + 'End Sub\n'
> > > > > > > > +          + 'End Class\n '
> > > > > > > > +
> > > > > > > > +          // Function to get a reference to the wrapper.
> > > > > > > > +          + 'Function ' + NIX_GET_WRAPPER + '(name)\n'
> > > > > > > > +          + 'Dim wrap\n'
> > > > > > > > +          + 'Set wrap = New ' + NIX_WRAPPER + '\n'
> > > > > > > > +          + 'wrap.SetIntendedName name\n'
> > > > > > > > +          + 'Set ' + NIX_GET_WRAPPER + ' = wrap\n'
> > > > > > > > +          + 'End Function'
> > > > > > > > +          + '</scr' + 'ipt>'
> > > > > > > > +
> > > > > > > > +          // Resets the "default" scripting language in IE
> > back
> > > > > > > > +          // to Javascript. This is needed or else any
> script
> > > > > > > > +          // tags without a proper Language="..." will be
> > treated
> > > > as
> > > > > > > > +          // VBScript once this code is added to the
> document.
> > > > > > > > +          + '<scri' + 'pt language="JScript"></scr' +
> 'ipt>';
> > > > > > > > +
> > > > > > > > +        // Note that this code can only be run once the
> > document
> > > > has
> > > > > > > > +        // fully loaded.
> > > > > > > > +        // TODO: Perhaps add some sort of check here for
> this?
> > > > > > > > +        document.write(vbscript);
> > > > > > > > +      }
> > > > > > > > +    }
> > > > > > > > +  }
> > > > > > > > +
> > > > > > > > +  /**
> > > > > > > > +   * Conducts any frame-specific work necessary to setup
> > > > > > > > +   * the channel type chosen. This method is called when
> > > > > > > > +   * the container page first registers the gadget in the
> > > > > > > > +   * RPC mechanism. Gadgets, in turn, will complete the
> setup
> > > > > > > > +   * of the channel once they send their first messages.
> > > > > > > > +   */
> > > > > > > > +  function setupFrame(frameId) {
> > > > > > > > +    if (setup[frameId]) {
> > > > > > > > +      return;
> > > > > > > > +    }
> > > > > > > > +
> > > > > > > > +    if (relayChannel === 'fe') {
> > > > > > > > +      try {
> > > > > > > > +        var frame = document.getElementById(frameId);
> > > > > > > > +        frame[FE_G2C_CHANNEL] = function(args) {
> > > > > > > > +          process(gadgets.json.parse(args));
> > > > > > > > +        };
> > > > > > > > +      } catch (e) {
> > > > > > > > +        // Something went wrong. System will fallback to
> > > > > > > > +        // IFPC.
> > > > > > > > +      }
> > > > > > > > +    }
> > > > > > > > +
> > > > > > > > +    if (relayChannel === 'nix') {
> > > > > > > > +      try {
> > > > > > > > +        var frame = document.getElementById(frameId);
> > > > > > > > +        frame.contentWindow.opener =
> > > > > window[NIX_GET_WRAPPER](frameId);
> > > > > > > > +      } catch (e) {
> > > > > > > > +        // Something went wrong. System will fallback to
> > > > > > > > +        // IFPC.
> > > > > > > > +      }
> > > > > > > > +    }
> > > > > > > > +
> > > > > > > > +    setup[frameId] = true;
> > > > > > > > +  }
> > > > > > > > +
> > > > > > > > +  /**
> > > > > > > >    * Encodes arguments for the legacy IFPC wire format.
> > > > > > > >    *
> > > > > > > >    * @param {Object} args
> > > > > > > > @@ -86,8 +358,17 @@
> > > > > > > >    * @private
> > > > > > > >    */
> > > > > > > >   function process(rpc) {
> > > > > > > > +    //
> > > > > > > > +    // RPC object contents:
> > > > > > > > +    //   s: Service Name
> > > > > > > > +    //   f: From
> > > > > > > > +    //   c: The callback ID or 0 if none.
> > > > > > > > +    //   a: The arguments for this RPC call.
> > > > > > > > +    //   t: The authentication token.
> > > > > > > > +    //
> > > > > > > >     if (rpc && typeof rpc.s === 'string' && typeof rpc.f ===
> > > > 'string'
> > > > > > &&
> > > > > > > >         rpc.a instanceof Array) {
> > > > > > > > +
> > > > > > > >       // Validate auth token.
> > > > > > > >       if (authToken[rpc.f]) {
> > > > > > > >         // We allow type coercion here because all the url
> > params
> > > > are
> > > > > > > > strings.
> > > > > > > > @@ -96,43 +377,158 @@
> > > > > > > >         }
> > > > > > > >       }
> > > > > > > >
> > > > > > > > -      // The Gecko engine used by FireFox etc. allows an
> > IFrame to
> > > > > > > > directly call
> > > > > > > > -      // methods on the frameElement property added by the
> > > > container
> > > > > > > page
> > > > > > > > even
> > > > > > > > -      // if their domains don't match.
> > > > > > > > -      // Here we try to set up a relay channel using the
> > > > > frameElement
> > > > > > > > technique
> > > > > > > > -      // to greatly reduce the latency of cross-domain calls
> > if
> > > > the
> > > > > > > > postMessage
> > > > > > > > -      // method is not supported.
> > > > > > > > -      if (relayChannel === 'ifpc') {
> > > > > > > > -        if (rpc.f === '..') {
> > > > > > > > -          // Container-to-gadget call
> > > > > > > > -          try {
> > > > > > > > -            var fel = window.frameElement;
> > > > > > > > -            if (typeof fel.__g2c_rpc === 'function' &&
> > > > > > > > -                typeof fel.__g2c_rpc.__c2g_rpc !=
> 'function')
> > {
> > > > > > > > -              fel.__g2c_rpc.__c2g_rpc = function(args) {
> > > > > > > > -                process(gadgets.json.parse(args));
> > > > > > > > -              };
> > > > > > > > -            }
> > > > > > > > -          } catch (e) {
> > > > > > > > -          }
> > > > > > > > -        } else {
> > > > > > > > -          // Gadget-to-container call
> > > > > > > > -          var iframe = document.getElementById(rpc.f);
> > > > > > > > -          if (iframe && typeof iframe.__g2c_rpc !=
> 'function')
> > {
> > > > > > > > -            iframe.__g2c_rpc = function(args) {
> > > > > > > > +      // Call the requested RPC service.
> > > > > > > > +      var result = (services[rpc.s] ||
> > > > > > > > +                    services[DEFAULT_NAME]).apply(rpc,
> rpc.a);
> > > > > > > > +
> > > > > > > > +      // If there is a callback for this service, initiate
> it
> > as
> > > > > well.
> > > > > > > > +      if (rpc.c) {
> > > > > > > > +        gadgets.rpc.call(rpc.f, CALLBACK_NAME, null, rpc.c,
> > > > result);
> > > > > > > > +      }
> > > > > > > > +    }
> > > > > > > > +  }
> > > > > > > > +
> > > > > > > > +  /**
> > > > > > > > +   * Attempts to conduct an RPC call to the specified
> > > > > > > > +   * target with the specified data via the NIX
> > > > > > > > +   * method. If this method fails, the system attempts again
> > > > > > > > +   * using the known default of IFPC.
> > > > > > > > +   *
> > > > > > > > +   * @param {String} targetId Module Id of the RPC service
> > > > provider.
> > > > > > > > +   * @param {String} from Module Id of the calling provider.
> > > > > > > > +   * @param {Object} rpcData The RPC data for this call.
> > > > > > > > +   */
> > > > > > > > +  function callNIX(targetId, from, rpcData) {
> > > > > > > > +    try {
> > > > > > > > +      if (from != '..') {
> > > > > > > > +        // Call from gadget to the container.
> > > > > > > > +        var handler = nix_channels['..'];
> > > > > > > > +
> > > > > > > > +        // If the gadget has yet to retrieve a reference to
> > > > > > > > +        // the NIX handler, try to do so now. We don't do a
> > > > > > > > +        // typeof(window.opener[NIX_ID_FIELD]) check here
> > > > > > > > +        // because it means accessing that field on the COM
> > > > object,
> > > > > > > which,
> > > > > > > > +        // being an internal function reference, is not
> > allowed.
> > > > > > > > +        // "in" works because it merely checks for the
> > prescence
> > > > of
> > > > > > > > +        // the key, rather than actually accessing the
> > object's
> > > > > > > property.
> > > > > > > > +        if (!handler && window.opener && NIX_ID_FIELD in
> > > > > > window.opener)
> > > > > > > {
> > > > > > > > +          handler = nix_channels['..'] = window.opener;
> > > > > > > > +
> > > > > > > > +          // Create the channel to the parent/container. We
> > pass
> > > > > both
> > > > > > > our
> > > > > > > > +          // own wrapper and our authentication token for
> > > > > > verification.
> > > > > > > > +
>  handler.CreateChannel(window[NIX_GET_WRAPPER]('..'),
> > > > > > > > authToken['..']);
> > > > > > > > +        }
> > > > > > > > +
> > > > > > > > +        // If we have a handler, call it.
> > > > > > > > +        if (handler) {
> > > > > > > > +          handler.SendMessage(rpcData);
> > > > > > > > +          return;
> > > > > > > > +        }
> > > > > > > > +      } else {
> > > > > > > > +        // Call from container to a gadget[targetId].
> > > > > > > > +
> > > > > > > > +        // If we have a handler, call it.
> > > > > > > > +        if (nix_channels[targetId]) {
> > > > > > > > +          nix_channels[targetId].SendMessage(rpcData);
> > > > > > > > +          return;
> > > > > > > > +        }
> > > > > > > > +      }
> > > > > > > > +    } catch (e) {
> > > > > > > > +    }
> > > > > > > > +
> > > > > > > > +    // If we have reached this point, something has failed
> > > > > > > > +    // with the NIX method, so we default to using
> > > > > > > > +    // IFPC for this call.
> > > > > > > > +    callIFPC(targetId, from, rpcData);
> > > > > > > > +  }
> > > > > > > > +
> > > > > > > > +  /**
> > > > > > > > +   * Attempts to conduct an RPC call to the specified
> > > > > > > > +   * target with the specified data via the FrameElement
> > > > > > > > +   * method. If this method fails, the system attempts again
> > > > > > > > +   * using the known default of IFPC.
> > > > > > > > +   *
> > > > > > > > +   * @param {String} targetId Module Id of the RPC service
> > > > provider.
> > > > > > > > +   * @param {String} from Module Id of the calling provider.
> > > > > > > > +   * @param {Object} rpcData The RPC data for this call.
> > > > > > > > +   */
> > > > > > > > +  function callFrameElement(targetId, from, rpcData) {
> > > > > > > > +    try {
> > > > > > > > +      if (from != '..') {
> > > > > > > > +        // Call from gadget to the container.
> > > > > > > > +        var fe = window.frameElement;
> > > > > > > > +
> > > > > > > > +        if (typeof fe[FE_G2C_CHANNEL] === 'function') {
> > > > > > > > +          // Complete the setup of the FE channel if need
> be.
> > > > > > > > +          if (typeof fe[FE_G2C_CHANNEL][FE_C2G_CHANNEL] !==
> > > > > > 'function')
> > > > > > > {
> > > > > > > > +            fe[FE_G2C_CHANNEL][FE_C2G_CHANNEL] =
> > function(args) {
> > > > > > > >               process(gadgets.json.parse(args));
> > > > > > > >             };
> > > > > > > >           }
> > > > > > > > +
> > > > > > > > +          // Conduct the RPC call.
> > > > > > > > +          fe[FE_G2C_CHANNEL](rpcData);
> > > > > > > > +          return;
> > > > > > > >         }
> > > > > > > > -      }
> > > > > > > > +      } else {
> > > > > > > > +        // Call from container to gadget[targetId].
> > > > > > > > +        var frame = document.getElementById(targetId);
> > > > > > > >
> > > > > > > > -      var result = (services[rpc.s] ||
> > services['']).apply(rpc,
> > > > > > rpc.a);
> > > > > > > > -      if (rpc.c) {
> > > > > > > > -        gadgets.rpc.call(rpc.f, '__cb', null, rpc.c,
> result);
> > > > > > > > +        if (typeof frame[FE_G2C_CHANNEL] === 'function' &&
> > > > > > > > +            typeof frame[FE_G2C_CHANNEL][FE_C2G_CHANNEL] ===
> > > > > > 'function')
> > > > > > > {
> > > > > > > > +
> > > > > > > > +          // Conduct the RPC call.
> > > > > > > > +          frame[FE_G2C_CHANNEL][FE_C2G_CHANNEL](rpcData);
> > > > > > > > +          return;
> > > > > > > > +        }
> > > > > > > >       }
> > > > > > > > +    } catch (e) {
> > > > > > > > +    }
> > > > > > > > +
> > > > > > > > +    // If we have reached this point, something has failed
> > > > > > > > +    // with the FrameElement method, so we default to using
> > > > > > > > +    // IFPC for this call.
> > > > > > > > +    callIFPC(targetId, from, rpcData);
> > > > > > > > +  }
> > > > > > > > +
> > > > > > > > +  /**
> > > > > > > > +   * Conducts an RPC call to the specified
> > > > > > > > +   * target with the specified data via the IFPC
> > > > > > > > +   * method.
> > > > > > > > +   *
> > > > > > > > +   * @param {String} targetId Module Id of the RPC service
> > > > provider.
> > > > > > > > +   * @param {String} from Module Id of the calling provider.
> > > > > > > > +   * @param {Object} rpcData The RPC data for this call.
> > > > > > > > +   */
> > > > > > > > +  function callIFPC(targetId, from, rpcData) {
> > > > > > > > +    // Retrieve the relay file used by IFPC. Note that
> > > > > > > > +    // this must be set before the call, and so we conduct
> > > > > > > > +    // an extra check to ensure it is not blank.
> > > > > > > > +    var relay = gadgets.rpc.getRelayUrl(targetId);
> > > > > > > > +
> > > > > > > > +    if (!relay) {
> > > > > > > > +      throw new Error('No relay file assigned for IFPC');
> > > > > > > >     }
> > > > > > > > +
> > > > > > > > +    // The RPC mechanism supports two formats for IFPC
> (legacy
> > and
> > > > > > > > current).
> > > > > > > > +    var src = null;
> > > > > > > > +    if (useLegacyProtocol[targetId]) {
> > > > > > > > +      // Format:
> > > > > > #iframe_id&callId&num_packets&packet_num&block_of_data
> > > > > > > > +      src = [relay, '#', encodeLegacyData([from, callId, 1,
> 0,
> > > > > > > > +             encodeLegacyData([from, serviceName, '', '',
> > > > > > from].concat(
> > > > > > > > +               Array.prototype.slice.call(arguments,
> > > > > 3)))])].join('');
> > > > > > > > +    } else {
> > > > > > > > +      // Format: #targetId & sourceId@callId & packetNum &
> > > > packetId
> > > > > &
> > > > > > > > packetData
> > > > > > > > +      src = [relay, '#', targetId, '&', from, '@', callId,
> > > > > > > > +             '&1&0&', encodeURIComponent(rpcData)].join('');
> > > > > > > > +    }
> > > > > > > > +
> > > > > > > > +    // Conduct the IFPC call by creating the Iframe with
> > > > > > > > +    // the relay URL and appended message.
> > > > > > > > +    emitInvisibleIframe(src);
> > > > > > > >   }
> > > > > > > >
> > > > > > > > +
> > > > > > > >   /**
> > > > > > > >    * Helper function to emit an invisible IFrame.
> > > > > > > >    * @param {String} src SRC attribute of the IFrame to emit.
> > > > > > > > @@ -221,6 +617,15 @@
> > > > > > > >      * @member gadgets.rpc
> > > > > > > >      */
> > > > > > > >     register: function(serviceName, handler) {
> > > > > > > > +      if (serviceName == CALLBACK_NAME) {
> > > > > > > > +        throw new Error("Cannot overwrite callback
> service");
> > > > > > > > +      }
> > > > > > > > +
> > > > > > > > +      if (serviceName == DEFAULT_NAME) {
> > > > > > > > +        throw new Error("Cannot overwrite default service:"
> > > > > > > > +                        + " use registerDefault");
> > > > > > > > +      }
> > > > > > > > +
> > > > > > > >       services[serviceName] = handler;
> > > > > > > >     },
> > > > > > > >
> > > > > > > > @@ -231,6 +636,15 @@
> > > > > > > >      * @member gadgets.rpc
> > > > > > > >      */
> > > > > > > >     unregister: function(serviceName) {
> > > > > > > > +      if (serviceName == CALLBACK_NAME) {
> > > > > > > > +        throw new Error("Cannot delete callback service");
> > > > > > > > +      }
> > > > > > > > +
> > > > > > > > +      if (serviceName == DEFAULT_NAME) {
> > > > > > > > +        throw new Error("Cannot delete default service:"
> > > > > > > > +                        + " use unregisterDefault");
> > > > > > > > +      }
> > > > > > > > +
> > > > > > > >       delete services[serviceName];
> > > > > > > >     },
> > > > > > > >
> > > > > > > > @@ -272,12 +686,14 @@
> > > > > > > >       if (callback) {
> > > > > > > >         callbacks[callId] = callback;
> > > > > > > >       }
> > > > > > > > -      var from;
> > > > > > > > +
> > > > > > > > +      // Default to the container calling.
> > > > > > > > +      var from = '..';
> > > > > > > > +
> > > > > > > >       if (targetId === '..') {
> > > > > > > >         from = window.name;
> > > > > > > > -      } else {
> > > > > > > > -        from = '..';
> > > > > > > >       }
> > > > > > > > +
> > > > > > > >       // Not used by legacy, create it anyway...
> > > > > > > >       var rpcData = gadgets.json.stringify({
> > > > > > > >         s: serviceName,
> > > > > > > > @@ -287,53 +703,37 @@
> > > > > > > >         t: authToken[targetId]
> > > > > > > >       });
> > > > > > > >
> > > > > > > > +      var channelType = relayChannel;
> > > > > > > > +
> > > > > > > > +      // If we are told to use the legacy format, then we
> must
> > > > > > > > +      // default to IFPC.
> > > > > > > >       if (useLegacyProtocol[targetId]) {
> > > > > > > > -        relayChannel = 'ifpc';
> > > > > > > > +        channelType = 'ifpc';
> > > > > > > >       }
> > > > > > > >
> > > > > > > > -      switch (relayChannel) {
> > > > > > > > -      case 'dpm': // use document.postMessage
> > > > > > > > -        var targetDoc = targetId === '..' ? parent.document
> :
> > > > > > > > -
> > > > >  frames[targetId].document;
> > > > > > > > -        targetDoc.postMessage(rpcData);
> > > > > > > > -        break;
> > > > > > > > -      case 'wpm': // use window.postMessage
> > > > > > > > -        var targetWin = targetId === '..' ? parent :
> > > > > frames[targetId];
> > > > > > > > -        targetWin.postMessage(rpcData, "*");
> > > > > > > > -        break;
> > > > > > > > -      default: // use 'ifpc' as a fallback mechanism
> > > > > > > > -        var relay = gadgets.rpc.getRelayUrl(targetId);
> > > > > > > > -        // TODO split message if too long
> > > > > > > > -        var src;
> > > > > > > > -        if (useLegacyProtocol[targetId]) {
> > > > > > > > -          //
> > > > #iframe_id&callId&num_packets&packet_num&block_of_data
> > > > > > > > -          src = [relay, '#', encodeLegacyData([from, callId,
> > 1, 0,
> > > > > > > > -                 encodeLegacyData([from, serviceName, '',
> '',
> > > > > > > > from].concat(
> > > > > > > > -                 Array.prototype.slice.call(arguments,
> > > > > > 3)))])].join('');
> > > > > > > > -        } else {
> > > > > > > > -          // Try the frameElement channel if available
> > > > > > > > -          try {
> > > > > > > > -            if (from === '..') {
> > > > > > > > -              // Container-to-gadget
> > > > > > > > -              var iframe =
> document.getElementById(targetId);
> > > > > > > > -              if (typeof iframe.__g2c_rpc.__c2g_rpc ===
> > > > 'function')
> > > > > {
> > > > > > > > -                iframe.__g2c_rpc.__c2g_rpc(rpcData);
> > > > > > > > -                return;
> > > > > > > > -              }
> > > > > > > > -            } else {
> > > > > > > > -              // Gadget-to-container
> > > > > > > > -              if (typeof window.frameElement.__g2c_rpc ===
> > > > > 'function')
> > > > > > {
> > > > > > > > -                window.frameElement.__g2c_rpc(rpcData);
> > > > > > > > -                return;
> > > > > > > > -              }
> > > > > > > > -            }
> > > > > > > > -          } catch (e) {
> > > > > > > > -          }
> > > > > > > > -          // # targetId & sourceId@callId & packetNum &
> > packetId
> > > > &
> > > > > > > > packetData
> > > > > > > > -          src = [relay, '#', targetId, '&', from, '@',
> callId,
> > > > > > > > -                 '&1&0&',
> > encodeURIComponent(rpcData)].join('');
> > > > > > > > -        }
> > > > > > > > -        emitInvisibleIframe(src);
> > > > > > > > +      switch (channelType) {
> > > > > > > > +        case 'dpm': // use document.postMessage.
> > > > > > > > +          var targetDoc = targetId === '..' ?
> parent.document
> > :
> > > > > > > > +
> > > > > >  frames[targetId].document;
> > > > > > > > +          targetDoc.postMessage(rpcData);
> > > > > > > > +          break;
> > > > > > > > +
> > > > > > > > +        case 'wpm': // use window.postMessage.
> > > > > > > > +          var targetWin = targetId === '..' ? parent :
> > > > > > frames[targetId];
> > > > > > > > +          targetWin.postMessage(rpcData, "*");
> > > > > > > > +          break;
> > > > > > > > +
> > > > > > > > +        case 'nix': // use NIX.
> > > > > > > > +          callNIX(targetId, from, rpcData);
> > > > > > > > +          break;
> > > > > > > > +
> > > > > > > > +        case 'fe': // use FrameElement.
> > > > > > > > +          callFrameElement(targetId, from, rpcData);
> > > > > > > > +          break;
> > > > > > > > +
> > > > > > > > +        default: // use 'ifpc' as a fallback mechanism.
> > > > > > > > +          callIFPC(targetId, from, rpcData);
> > > > > > > > +          break;
> > > > > > > >       }
> > > > > > > >     },
> > > > > > > >
> > > > > > > > @@ -372,15 +772,13 @@
> > > > > > > >      */
> > > > > > > >     setAuthToken: function(targetId, token) {
> > > > > > > >       authToken[targetId] = token;
> > > > > > > > +      setupFrame(targetId);
> > > > > > > >     },
> > > > > > > >
> > > > > > > >     /**
> > > > > > > >      * Gets the RPC relay mechanism.
> > > > > > > > -     * @return {String} RPC relay mechanism. Supported
> types:
> > > > > > > > -     *                  'wpm' - Use window.postMessage
> > (defined by
> > > > > > > HTML5)
> > > > > > > > -     *                  'dpm' - Use document.postMessage
> > (defined
> > > > by
> > > > > > an
> > > > > > > > early
> > > > > > > > -     *                          draft of HTML5 and
> implemented
> > by
> > > > > > Opera)
> > > > > > > > -     *                  'ifpc' - Use invisible IFrames
> > > > > > > > +     * @return {String} RPC relay mechanism. See above for
> > > > > > > > +     *   a list of supported types.
> > > > > > > >      *
> > > > > > > >      * @member gadgets.rpc
> > > > > > > >      */
> > > > > > > >
> > > > > > > > Modified:
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> >
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > > > > > > URL:
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java?rev=672393&r1=672392&r2=672393&view=diff
> > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> >
> ==============================================================================
> > > > > > > > ---
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> >
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > > > > > > (original)
> > > > > > > > +++
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> >
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > > > > > > Fri Jun 27 14:01:20 2008
> > > > > > > > @@ -221,7 +221,8 @@
> > > > > > > >           "a {color:#0000cc;}a:visited {color:#551a8b;}" +
> > > > > > > >           "a:active {color:#ff0000;}" +
> > > > > > > >           "body{margin: 0px;padding:
> > 0px;background-color:white;}"
> > > > +
> > > > > > > > -          "</style></head>";
> > > > > > > > +          "</style><base>" +
> > gadget.getSpec().getUrl().toString()
> > > > +
> > > > > > > > +          "</base></head>";
> > > > > > > >     markup.append(boilerPlate);
> > > > > > > >     LocaleSpec localeSpec =
> > > > > > gadget.getSpec().getModulePrefs().getLocale(
> > > > > > > >         gadget.getContext().getLocale());
> > > > > > > >
> > > > > > > > Added:
> > > > > > >
> > incubator/shindig/trunk/javascript/container/rpctest_container.html
> > > > > > > > URL:
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_container.html?rev=672393&view=auto
> > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> >
> ==============================================================================
> > > > > > > > ---
> > > > > incubator/shindig/trunk/javascript/container/rpctest_container.html
> > > > > > > > (added)
> > > > > > > > +++
> > > > > incubator/shindig/trunk/javascript/container/rpctest_container.html
> > > > > > > Fri
> > > > > > > > Jun 27 14:01:20 2008
> > > > > > > > @@ -0,0 +1,90 @@
> > > > > > > > +<!--
> > > > > > > > +  Simple page for testing gadgets.rpc performance.
> > > > > > > > +  Allows you to add a simulated "gadget" (in this case just
> a
> > > > static
> > > > > > > > +  HTML page which loads gadgets.rpc also), and pass some
> > > > > > > > +  specified number of random messages of specified size to
> > > > > > > > +  and from it.
> > > > > > > > +
> > > > > > > > +  A simple recommended setup is to start up two instances of
> > > > > > > > +  the Shindig Gadgets Server on two separate ports to test
> > > > > > > > +  "real" cross-domain communication, since port is factored
> > > > > > > > +  into the same-domain policy enforced by browsers.
> > > > > > > > +
> > > > > > > > +  If your servers are on localhost:8080 and localhost:8081,
> > then
> > > > > hit:
> > > > > > > > +
> > > > > >
> > http://localhost:8080/gadgets/files/container/rpctest_container.html?
> > > > > > > \
> > > > > > > > +
> > > > >
> http://localhost:8081/gadgets/files/container/rpctest_gadget.html&\<http://localhost:8081/gadgets/files/container/rpctest_gadget.html&%5C>
> <http://localhost:8081/gadgets/files/container/rpctest_gadget.html&%5C>
> > <http://localhost:8081/gadgets/files/container/rpctest_gadget.html&%5C>
> > > > <
> http://localhost:8081/gadgets/files/container/rpctest_gadget.html&%5C
> > >
> > > > > > > > +
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> >
> http://localhost:8081/gadgets/files/container/rpc_relay.uncompressed.html
> > > > > > > > +
> > > > > > > > +  (Note the backslashes should be removed, as they exist for
> > > > > > formatting
> > > > > > > > only.)
> > > > > > > > +
> > > > > > > > +  There are two arguments, separated by an ampersand:
> > > > > > > > +  1. URL of the "gadget" test page.
> > > > > > > > +  2. URL of the "gadget" test page's RPC relay (for browsers
> > > > > requiring
> > > > > > > > it).
> > > > > > > > +-->
> > > > > > > > +<html>
> > > > > > > > +  <head>
> > > > > > > > +    <title>gadgets.rpc Performance Tests: Container</title>
> > > > > > > > +    <script src="../../js/rpc.js?c=1&debug=1"></script>
> > > > > > > > +    <script src="rpctest_perf.js"></script>
> > > > > > > > +    <script>
> > > > > > > > +      function initTest() {
> > > > > > > > +        var container =
> document.getElementById("container");
> > > > > > > > +
> > > > > > > > +        // query string is assumed to be the "gadget" URL as
> > arg
> > > > 1,
> > > > > > > > +        // and optionally the relay URL as arg 2
> > > > > > > > +        var pageArgs =
> > > > > window.location.search.substring(1).split('&');
> > > > > > > > +        var gadgetUrl = pageArgs[0];
> > > > > > > > +        var secret = Math.round(Math.random()*10000000);
> > > > > > > > +        if (pageArgs[1]) {
> > > > > > > > +          gadgets.rpc.setRelayUrl('gadget', pageArgs[1]);
> > > > > > > > +        }
> > > > > > > > +        var containerRelay = pageArgs[2] || '';
> > > > > > > > +        container.innerHTML = "<iframe id='gadget'
> > name='gadget'
> > > > > > > > height=300 width=300 src='" + gadgetUrl + "?parent=" +
> > > > containerRelay
> > > > > +
> > > > > > > > "#rpctoken=" + secret + "'></iframe>";
> > > > > > > > +        gadgets.rpc.setAuthToken('gadget', secret);
> > > > > > > > +
> > > > > > > > +        initPerfTest();
> > > > > > > > +      };
> > > > > > > > +    </script>
> > > > > > > > +  </head>
> > > > > > > > +  <body onload="initTest();">
> > > > > > > > +    <div>gadgets.rpc Performance: Container Page</div><hr/>
> > > > > > > > +    <div>Test<br/>
> > > > > > > > +      <ul>
> > > > > > > > +        <li>Number of messages to send:
> > > > > > > > +          <select name="num_msgs" id="num_msgs">
> > > > > > > > +            <option value="1">1</option>
> > > > > > > > +            <option value="10">10</option>
> > > > > > > > +            <option value="100" selected>100</option>
> > > > > > > > +            <option value="1000">1000</option>
> > > > > > > > +          </select>
> > > > > > > > +        </li>
> > > > > > > > +        <li>Message size:
> > > > > > > > +          <select name="msg_size" id="msg_size">
> > > > > > > > +            <option value="10">10 B</option>
> > > > > > > > +            <option value="100">100 B</option>
> > > > > > > > +            <option value="1024" selected>1 kB</option>
> > > > > > > > +            <option value="10240">10 kB</option>
> > > > > > > > +            <option value="102400">100 kB</option>
> > > > > > > > +            <option value="1048576">1 MB</option>
> > > > > > > > +          </select>
> > > > > > > > +        </li>
> > > > > > > > +        <li>
> > > > > > > > +          <input type="button" value="Start The Test!"
> > > > > > > > onclick="runPerfTest('gadget');" />
> > > > > > > > +        </li>
> > > > > > > > +      </ul>
> > > > > > > > +    </div>
> > > > > > > > +    <div id="test_running" style="display:none;">
> > > > > > > > +      Running test...
> > > > > > > > +    </div>
> > > > > > > > +    <div id="results" style="display:none;">
> > > > > > > > +      Results: Gadget-to-Container<br/>
> > > > > > > > +      Messages: <span id="results_num_received"></span>,
> > Bytes:
> > > > > <span
> > > > > > > > id="results_bytes_received"></span> <span
> > > > id="in_or_out"></span><br/>
> > > > > > > > +      Time elapsed for test run: <span
> > > > > > > id="results_time_used"></span><br/>
> > > > > > > > +      Messages/second: <span
> > > > id="results_msgs_per_sec"></span><br/>
> > > > > > > > +      Bytes/second: <span id="results_bytes_per_sec"></span>
> > > > > > > > +    </div>
> > > > > > > > +    <hr/>
> > > > > > > > +    <div>Gadget:</div>
> > > > > > > > +    <div id="container"></div>
> > > > > > > > +  </body>
> > > > > > > > +</html>
> > > > > > > >
> > > > > > > > Added:
> > > > > incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > > > > > > > URL:
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_gadget.html?rev=672393&view=auto
> > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> >
> ==============================================================================
> > > > > > > > ---
> > > > incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > > > > > > > (added)
> > > > > > > > +++
> > > > incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > > > > > Fri
> > > > > > > > Jun 27 14:01:20 2008
> > > > > > > > @@ -0,0 +1,49 @@
> > > > > > > > +<html>
> > > > > > > > +  <head>
> > > > > > > > +    <title>gadgets.rpc Performance Tests: Gadget</title>
> > > > > > > > +    <script src="../../js/rpc.js?c=1&debug=1"></script>
> > > > > > > > +    <script src="rpctest_perf.js"></script>
> > > > > > > > +    <script>
> > > > > > > > +      var containerRelay =
> > window.location.search.substring(1);
> > > > > > > > +      gadgets.rpc.setRelayUrl(null, containerRelay);
> > > > > > > > +    </script>
> > > > > > > > +  </head>
> > > > > > > > +  <body onload="initPerfTest();">
> > > > > > > > +    <div>gadgets.rpc Performance: "Gadget" page</div><hr/>
> > > > > > > > +    <div>Test<br/>
> > > > > > > > +      <ul>
> > > > > > > > +        <li>Number of messages to send:
> > > > > > > > +          <select name="num_msgs" id="num_msgs">
> > > > > > > > +            <option value="1">1</option>
> > > > > > > > +            <option value="10">10</option>
> > > > > > > > +            <option value="100" selected>100</option>
> > > > > > > > +            <option value="1000">1000</option>
> > > > > > > > +          </select>
> > > > > > > > +        </li>
> > > > > > > > +        <li>Message size:
> > > > > > > > +          <select name="msg_size" id="msg_size">
> > > > > > > > +            <option value="10">10 B</option>
> > > > > > > > +            <option value="100">100 B</option>
> > > > > > > > +            <option value="1024" selected>1 kB</option>
> > > > > > > > +            <option value="10240">10 kB</option>
> > > > > > > > +            <option value="102400">100 kB</option>
> > > > > > > > +            <option value="1048576">1 MB</option>
> > > > > > > > +          </select>
> > > > > > > > +        </li>
> > > > > > > > +        <li>
> > > > > > > > +          <input type="button" value="Start The Test!"
> > > > > > > > onclick="runPerfTest();" />
> > > > > > > > +        </li>
> > > > > > > > +      </ul>
> > > > > > > > +    </div>
> > > > > > > > +    <div id="test_running" style="display:none;">
> > > > > > > > +      Running test...
> > > > > > > > +    </div>
> > > > > > > > +    <div id="results" style="display:none;">
> > > > > > > > +      Results: Gadget-to-Container<br/>
> > > > > > > > +      Messages: <span id="results_num_received"></span>,
> > Bytes:
> > > > > <span
> > > > > > > > id="results_bytes_received"></span> <span
> > > > id="in_or_out"></span><br/>
> > > > > > > > +      Time elapsed for test run: <span
> > > > > > > id="results_time_used"></span><br/>
> > > > > > > > +      Messages/second: <span
> > > > id="results_msgs_per_sec"></span><br/>
> > > > > > > > +      Bytes/second: <span id="results_bytes_per_sec"></span>
> > > > > > > > +    </div>
> > > > > > > > +  </body>
> > > > > > > > +</html>
> > > > > > > >
> > > > > > > > Added:
> > incubator/shindig/trunk/javascript/container/rpctest_perf.js
> > > > > > > > URL:
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_perf.js?rev=672393&view=auto
> > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> >
> ==============================================================================
> > > > > > > > ---
> > incubator/shindig/trunk/javascript/container/rpctest_perf.js
> > > > > > (added)
> > > > > > > > +++
> > incubator/shindig/trunk/javascript/container/rpctest_perf.js
> > > > Fri
> > > > > > Jun
> > > > > > > 27
> > > > > > > > 14:01:20 2008
> > > > > > > > @@ -0,0 +1,93 @@
> > > > > > > > +var perfStats = null;
> > > > > > > > +var currentRun = {};
> > > > > > > > +
> > > > > > > > +function perfService(message) {
> > > > > > > > +  if (perfStats.numResults++ === 0) {
> > > > > > > > +    perfStats.firstMsg = message; // stored since it has
> > "real"
> > > > > start
> > > > > > > time
> > > > > > > > +  }
> > > > > > > > +  perfStats.bytesReceived += message.length;
> > > > > > > > +};
> > > > > > > > +
> > > > > > > > +function clearPerfStats(inOrOut) {
> > > > > > > > +  perfStats = {
> > > > > > > > +    numResults: 0,
> > > > > > > > +    bytesReceived: 0,
> > > > > > > > +    firstMsg: null
> > > > > > > > +  };
> > > > > > > > +
> > > > > > > > +  document.getElementById("in_or_out").innerHTML = inOrOut;
> > > > > > > > +
> > > > > > > > +  // hide results fields
> > > > > > > > +  document.getElementById("results").style.display = "none";
> > > > > > > > +};
> > > > > > > > +
> > > > > > > > +function completePerfStats() {
> > > > > > > > +  perfStats.timeEnded = new Date().getTime();
> > > > > > > > +
> > > > > > > > +  // get time started from the first sent message
> > > > > > > > +  perfStats.timeStarted = perfStats.firstMsg.substr(0,
> > > > > > > > perfStats.firstMsg.indexOf(':'));
> > > > > > > > +
> > > > > > > > +  var timeUsedMs = perfStats.timeEnded -
> > perfStats.timeStarted;
> > > > > > > > +
> > > > > > > > +  // fill in fields
> > > > > > > > +  document.getElementById("results_num_received").innerHTML
> =
> > > > > > > > perfStats.numResults;
> > > > > > > > +
>  document.getElementById("results_bytes_received").innerHTML
> > =
> > > > > > > > perfStats.bytesReceived;
> > > > > > > > +  document.getElementById("results_time_used").innerHTML =
> > > > > timeUsedMs
> > > > > > +
> > > > > > > > "ms";
> > > > > > > > +  document.getElementById("results_msgs_per_sec").innerHTML
> =
> > > > > > > > (perfStats.numResults / (timeUsedMs / 1000));
> > > > > > > > +  document.getElementById("results_bytes_per_sec").innerHTML
> =
> > > > > > > > (perfStats.bytesReceived / (timeUsedMs / 1000));
> > > > > > > > +  document.getElementById("test_running").style.display =
> > "none";
> > > > > > > > +  document.getElementById("results").style.display = "";
> > > > > > > > +};
> > > > > > > > +
> > > > > > > > +function initPerfTest() {
> > > > > > > > +  clearPerfStats();
> > > > > > > > +  gadgets.rpc.register("perf_service", perfService);
> > > > > > > > +  gadgets.rpc.register("clear_perf_stats", clearPerfStats);
> > > > > > > > +  gadgets.rpc.register("complete_perf_stats",
> > completePerfStats);
> > > > > > > > +};
> > > > > > > > +
> > > > > > > > +var alphabet =
> > > > > > > >
> "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
> > > > > > > _-*&(){}'";
> > > > > > > > +
> > > > > > > > +function sendPerfMessage() {
> > > > > > > > +  var msgToSend = currentRun.msg;
> > > > > > > > +  if (currentRun.curMsgId++ <= 1) {
> > > > > > > > +    var nowString = new Date().getTime() + ':';
> > > > > > > > +    msgToSend = nowString +
> > > > > > currentRun.msg.substring(nowString.length);
> > > > > > > > +  }
> > > > > > > > +
> > > > > > > > +  gadgets.rpc.call(currentRun.targetId, "perf_service",
> null,
> > > > > > > msgToSend);
> > > > > > > > +  if (currentRun.curMsgId < currentRun.endMsgId) {
> > > > > > > > +    // loop, giving up execution in case rpc technique
> demands
> > it
> > > > > > > > +    window.setTimeout(sendPerfMessage, 0);
> > > > > > > > +  } else {
> > > > > > > > +    // send finisher
> > > > > > > > +    window.setTimeout(function() {
> > > > > > gadgets.rpc.call(currentRun.targetId,
> > > > > > > > "complete_perf_stats", null); }, 0);
> > > > > > > > +  }
> > > > > > > > +};
> > > > > > > > +
> > > > > > > > +function runPerfTest(targetId) {
> > > > > > > > +  document.getElementById("test_running").style.display =
> "";
> > > > > > > > +
> > > > > > > > +  // initialize the current run
> > > > > > > > +  var num_msgs = document.getElementById("num_msgs").value;
> > > > > > > > +  var msg_size = document.getElementById("msg_size").value;
> > > > > > > > +
> > > > > > > > +  currentRun.targetId = targetId;
> > > > > > > > +  currentRun.curMsgId = 0;
> > > > > > > > +  currentRun.endMsgId = num_msgs;
> > > > > > > > +
> > > > > > > > +  var msg = [];
> > > > > > > > +  for (var i = 0; i < msg_size; ++i) {
> > > > > > > > +    msg[i] =
> > > > > > alphabet.charAt(Math.round(Math.random(alphabet.length)));
> > > > > > > > +  }
> > > > > > > > +  currentRun.msg = msg.join('');
> > > > > > > > +
> > > > > > > > +  // clear local perf stats
> > > > > > > > +  clearPerfStats("(outbound)");
> > > > > > > > +
> > > > > > > > +  // clear target perf stats
> > > > > > > > +  gadgets.rpc.call(targetId, "clear_perf_stats", null,
> > > > "(inbound)");
> > > > > > > > +
> > > > > > > > +  // kick off the send loop
> > > > > > > > +  sendPerfMessage();
> > > > > > > > +};
> > > > > > > >
> > > > > > > >
> > > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> >
>

Re: svn commit: r672393 - in /incubator/shindig/trunk: features/rpc/ java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ javascript/container/

Posted by John Hjelmstad <fa...@google.com>.
That sounds fair. To be honest, if you want I can revert the whole thing and
attach it to a JIRA issue. Sorta let my excitement about the performance
improvement put the cart in front of the horse ;)

John

On Fri, Jun 27, 2008 at 4:55 PM, Chris Chabot <ch...@xs4all.nl> wrote:

> I'm kind of on board with Kevin that this probably should have been done
> the apache way, there's a lot of very big containers that use shindig,
> and hundreds of smaller ones too .. We're an apache project to make sure
> everything happens democratically, and without that in place people
> might not we're ready to graduate out of incubation you know :-)
>
> As a fellow committer i for one would've liked to at least take a quick
> look at, or at least hear about it on the mailing list it before seeing
> it show up 'out of nowhere', at least with other big changes we know
> it's being worked on in advance, and we then have the chance to see the
> patch cook in jira so people could respond to it if they wanted (like,
> positively community like even! :P)
>
> Anyhow, for now the compromise works for me, but let's do it differently
> next time ok :-)
>
>    -- Chris
>
> On Fri, 2008-06-27 at 16:39 -0700, John Hjelmstad wrote:
>
> > How's this for a compromise: I'll add a param that disables the new
> > technique by default, and allows it to be used if preferred. The rest is
> > refactoring and documentation. Given that's the bulk of the code change
> in
> > terms of volume, I didn't create a JIRA issue for it. I'm happy to do so
> to
> > enable it longer-term, however. Thoughts?
> >
> > John
> >
> > On Fri, Jun 27, 2008 at 4:27 PM, Kevin Brown <et...@google.com> wrote:
> >
> > > On Fri, Jun 27, 2008 at 3:45 PM, John Hjelmstad <fa...@google.com>
> wrote:
> > >
> > > > As indicated in the CL, I tested this on half a dozen browsers. We
> don't
> > > > have unit tests for it. I've also sent the code and inquiries about
> the
> > > > technique to several poeple, some of whom expressed concerns but no
> > > ability
> > > > to actively exploit the code.
> > >
> > >
> > > Anyone on this mailing list? Either of the people I mentioned?
> > >
> > >
> > > > The main concern was around VBScript
> > > > knowledge, which few people I know extensively have. That was true of
> > > IFPC
> > > > when it was originally released as well. If any attacks are found,
> they
> > > > will
> > > > be actively closed or the technique will be removed. Would you prefer
> > > that
> > > > no code, irrespective of its value, ever be submitted? It's possible
> for
> > > > any
> > > > code to have bugs, and a lot of code to have security holes. If
> found,
> > > > they're fixed.
> > >
> > >
> > > Yes, but this is code that uses a security technique that is highly
> > > questionable and has been flagged as potentially problematic by several
> > > people. Therefore, it needs more scrutiny. I won't deploy this on any
> of
> > > our
> > > production systems until I get an OK from people who are actually
> familiar
> > > with the problems in question have chimed in.
> > >
> > > This is also a very large code change, and, per policy, large code
> changes
> > > need JIRA tickets and feedback from other people. There are hundreds
> (if
> > > not
> > > thousands) of people using Shindig today and the code is deployed to
> > > hundreds of millions of users.
> > >
> > >
> > > >
> > > > John
> > > >
> > > > On Fri, Jun 27, 2008 at 3:38 PM, Kevin Brown <et...@google.com>
> wrote:
> > > >
> > > > > Please actually test your code or send it to other shindig
> committers
> > > > > before
> > > > > you commit it. Your change to GadgetRenderingTask breaks proxying
> and
> > > > > makeRequest in several cases. Fix it.
> > > > >
> > > > > Have you run this by Brian and/or Mike Samuel? They both expressed
> > > doubts
> > > > > about whether this mechanism was actually secure.
> > > > >
> > > > > On Fri, Jun 27, 2008 at 2:06 PM, John Hjelmstad <fa...@google.com>
> > > > wrote:
> > > > >
> > > > > > +jschorr in case there are comments.
> > > > > >
> > > > > > On Fri, Jun 27, 2008 at 2:01 PM, <jo...@apache.org> wrote:
> > > > > >
> > > > > > > Author: johnh
> > > > > > > Date: Fri Jun 27 14:01:20 2008
> > > > > > > New Revision: 672393
> > > > > > >
> > > > > > > URL: http://svn.apache.org/viewvc?rev=672393&view=rev
> > > > > > > Log:
> > > > > > > Cleaned up gadgets.rpc library implementation, and implemented
> fast
> > > > IE
> > > > > > > transport layer.
> > > > > > >
> > > > > > > Credit:
> > > > > > >        Joseph Schorr (jschorr@google.com) and I implemented
> this
> > > > > > together,
> > > > > > > but he
> > > > > > >        really did all the hard work, as well as developing the
> NIX
> > > > > > > technique's
> > > > > > >        fundamentals. Huge thanks to Joseph!
> > > > > > >
> > > > > > > Details:
> > > > > > >        * Created a new relayChannel for Firefox frameElement
> > > > technique,
> > > > > > >          making its implementation more linear to read.
> > > > > > >        * Consolidated all transport-specific code in
> > > setupChannel(),
> > > > > > > setupFrame(),
> > > > > > >          and callX(), where X = relay type. This refactoring
> makes
> > > > > setup
> > > > > > > and use
> > > > > > >          of each transport more clear, and makes it possible in
> > > later
> > > > > > > revisions
> > > > > > >          to separate out each method as its own class. In
> theory,
> > > if
> > > > we
> > > > > > can
> > > > > > > trust
> > > > > > >          things like User-Agent headers, we can use this to
> reduce
> > > > > rpc.js
> > > > > > > code
> > > > > > >          size significantly by only including the needed
> > > > transport(s).
> > > > > > >        * Implemented "NIX" - Native IE XDC. This method
> exploits
> > > the
> > > > > fact
> > > > > > >          that in IE6 and IE7 the window.opener property can be
> both
> > > > set
> > > > > > and
> > > > > > >          read across domain boundaries for an IFRAME, and that
> any
> > > > > object
> > > > > > >          can be passed through it. In particular, functions
> > > > > implementing
> > > > > > >          message passing can be passed. These can't be JS
> > > functions,
> > > > > > > however,
> > > > > > >          since those can leak sender context. So, VBScript
> (COM)
> > > > > objects
> > > > > > >          wrapping these are passed to maintain context
> isolation.
> > > > > > >          - Requires for IE6/7 that rpc.js be included in the
> > > > container
> > > > > > >            at load time, not dynamically. TODO added to
> consider
> > > > > > detecting
> > > > > > >            whether dynamic loading is happening (at which point
> we
> > > > > should
> > > > > > >            fall back to IFPC).
> > > > > > >          - Message channel handshake *and* message passing
> > > validated
> > > > > > >            using authToken, as with other channels.
> > > > > > >          - Impl requires that gadget call container first -
> else
> > > IFPC
> > > > > > >            is used. This is the same as the frameElement
> technique.
> > > > We
> > > > > > >            could add a setInterval()-based initialization
> routine
> > > > later
> > > > > > >            if needed. To date, the only gadgets.rpc calls made
> from
> > > > > > >            container to gadget without gadget to container
> first
> > > are
> > > > > > >            in theory pubsub messages.
> > > > > > >          - Extensive documentation on this technique and the
> others
> > > > > > >            added to the comments, since they're stripped out in
> the
> > > > > > >            majority case.
> > > > > > >        * Implemented quick-and-dirty performance testing page
> > > > verifying
> > > > > > >          the library works and giving basic performance
> metrics.
> > > > > > >          This needs to be improved! But does the trick for
> now...
> > > > > > >
> > > > > > > Testing/Data:
> > > > > > >
> > > > > > > Library verified on the following browsers with the RPC test
> page.
> > > > For
> > > > > > > each,
> > > > > > > a "bandwidth" measure was taken by averaging the results of 6
> G2C
> > > and
> > > > > C2G
> > > > > > > runs
> > > > > > > with 100 messages being passed of 1K apiece. Units are 1K
> > > > messages/sec.
> > > > > > > The "latency" value is the average amount of time taken, over 6
> G2C
> > > > and
> > > > > > C2G
> > > > > > > runs,
> > > > > > > to send 1 1K message. Happily, for all browsers save Safari
> (soon
> > > due
> > > > > to
> > > > > > > get
> > > > > > > window.postMessage), latency is around or below time required
> to
> > > > > achieve
> > > > > > > a cross-domain call without user-perceived delay.
> > > > > > >
> > > > > > >        * Firefox 3 (WPM) - bandwidth: 88 kB/sec; latency: 18ms.
> > > > > > >        * Firefox 2 (FE)  - bandwidth: 80 kB/sec; latency: 15ms.
> > > > > > >        * Opera 9   (WPM) - bandwidth: 85 kB/sec; latency: 7ms.
> > > > > > >        * IE6       (NIX) - bandwidth: 64 kB/sec; latency: 18ms.
> > > > > > >        * IE7       (NIX) - bandwidth: 64 kB/sec; latency: 22ms.
> > > > > > >        * Safari 3 (IFPC) - bandwidth: ?6-8kB/sec; latency:
> > > > ?100-200ms.
> > > > > > >          - Safari is somewhat flaky with the RPC test page,
> before
> > > > > > >            and after the rpc.js change. Multiple messages seem
> to
> > > > > > >            confuse it from time to time.
> > > > > > >
> > > > > > >
> > > > > > > Added:
> > > > > > >
> > > >  incubator/shindig/trunk/javascript/container/rpctest_container.html
> > > > > > >
>  incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > > > > > >    incubator/shindig/trunk/javascript/container/rpctest_perf.js
> > > > > > > Modified:
> > > > > > >    incubator/shindig/trunk/features/rpc/rpc.js
> > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
>  incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > > > > >
> > > > > > > Modified: incubator/shindig/trunk/features/rpc/rpc.js
> > > > > > > URL:
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/features/rpc/rpc.js?rev=672393&r1=672392&r2=672393&view=diff
> > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
> ==============================================================================
> > > > > > > --- incubator/shindig/trunk/features/rpc/rpc.js (original)
> > > > > > > +++ incubator/shindig/trunk/features/rpc/rpc.js Fri Jun 27
> 14:01:20
> > > > > 2008
> > > > > > > @@ -18,7 +18,7 @@
> > > > > > >
> > > > > > >  /**
> > > > > > >  * @fileoverview Remote procedure call library for
> > > > gadget-to-container,
> > > > > > > - * container-to-gadget, and gadget-to-gadget communication.
> > > > > > > + * container-to-gadget, and gadget-to-gadget (thru container)
> > > > > > > communication.
> > > > > > >  */
> > > > > > >
> > > > > > >  var gadgets = gadgets || {};
> > > > > > > @@ -29,6 +29,30 @@
> > > > > > >  * @name gadgets.rpc
> > > > > > >  */
> > > > > > >  gadgets.rpc = function() {
> > > > > > > +  // General constants.
> > > > > > > +  var CALLBACK_NAME = '__cb';
> > > > > > > +  var DEFAULT_NAME = '';
> > > > > > > +
> > > > > > > +  // Consts for FrameElement.
> > > > > > > +  var FE_G2C_CHANNEL = '__g2c_rpc';
> > > > > > > +  var FE_C2G_CHANNEL = '__c2g_rpc';
> > > > > > > +
> > > > > > > +  // Consts for NIX. VBScript doesn't
> > > > > > > +  // allow items to start with _ for some reason,
> > > > > > > +  // so we need to make these names quite unique, as
> > > > > > > +  // they will go into the global namespace.
> > > > > > > +  var NIX_WRAPPER = 'GRPC____NIXVBS_wrapper';
> > > > > > > +  var NIX_GET_WRAPPER = 'GRPC____NIXVBS_get_wrapper';
> > > > > > > +  var NIX_HANDLE_MESSAGE = 'GRPC____NIXVBS_handle_message';
> > > > > > > +  var NIX_CREATE_CHANNEL = 'GRPC____NIXVBS_create_channel';
> > > > > > > +  var NIX_ID_FIELD = 'GRPC____NIXVBS_container';
> > > > > > > +
> > > > > > > +  // JavaScript reference to the NIX VBScript wrappers.
> > > > > > > +  // Gadgets will have but a single channel under
> > > > > > > +  // nix_channels['..'] while containers will have a channel
> > > > > > > +  // per gadget stored under the gadget's ID.
> > > > > > > +  var nix_channels = {};
> > > > > > > +
> > > > > > >   var services = {};
> > > > > > >   var iframePool = [];
> > > > > > >   var relayUrl = {};
> > > > > > > @@ -36,28 +60,135 @@
> > > > > > >   var authToken = {};
> > > > > > >   var callId = 0;
> > > > > > >   var callbacks = {};
> > > > > > > +  var setup = {};
> > > > > > > +
> > > > > > > +  var params = {};
> > > > > > > +
> > > > > > > +  // Load the authentication token for speaking to the
> container
> > > > > > > +  // from the gadget's parameters, or default to '0' if not
> found.
> > > > > > > +  if (gadgets.util) {
> > > > > > > +    params = gadgets.util.getUrlParameters();
> > > > > > > +  }
> > > > > > >
> > > > > > > -  var params = gadgets.util.getUrlParameters();
> > > > > > >   authToken['..'] = params.rpctoken || params.ifpctok || 0;
> > > > > > >
> > > > > > > -  // Pick the most efficient RPC relay mechanism
> > > > > > > -  var relayChannel = typeof document.postMessage ===
> 'function' ?
> > > > > 'dpm'
> > > > > > :
> > > > > > > -                     typeof window.postMessage === 'function'
> ?
> > > > 'wpm'
> > > > > :
> > > > > > > +  // Pick the most efficient RPC relay mechanism:
> > > > > > > +  //
> > > > > > > +  // * For those browsers that support native messaging
> (various
> > > > > > > implementations
> > > > > > > +  //   of the HTML5 postMessage method), use that. Officially
> > > > defined
> > > > > at
> > > > > > > +  //
> > > > > > >
> > > >
> http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html
> > > > > .
> > > > > > > +  //
> > > > > > > +  //   postMessage is a native implementation of XDC. A page
> > > > registers
> > > > > > > that
> > > > > > > +  //   it would like to receive messages by listening the the
> > > > > "message"
> > > > > > > event
> > > > > > > +  //   on the window (document in DPM) object. In turn,
> another
> > > page
> > > > > can
> > > > > > > +  //   raise that event by calling window.postMessage
> > > > > > > (document.postMessage
> > > > > > > +  //   in DPM) with a string representing the message and a
> string
> > > > > > > +  //   indicating on which domain the receiving page must be
> to
> > > > > receive
> > > > > > > +  //   the message. The target page will then have its
> "message"
> > > > event
> > > > > > > raised
> > > > > > > +  //   if the domain matches and can, in turn, check the
> origin of
> > > > the
> > > > > > > message
> > > > > > > +  //   and process the data contained within.
> > > > > > > +  //
> > > > > > > +  //     wpm: postMessage on the window object.
> > > > > > > +  //        - Internet Explorer 8+
> > > > > > > +  //        - Safari (latest nightlies as of 26/6/2008)
> > > > > > > +  //        - Firefox 3+
> > > > > > > +  //        - Opera 9+
> > > > > > > +  //
> > > > > > > +  //     dpm: postMessage on the document object.
> > > > > > > +  //        - Opera 8+
> > > > > > > +  //
> > > > > > > +  // * For Internet Explorer before version 8, the security
> model
> > > > > allows
> > > > > > a
> > > > > > > +  //   child to set the value of the "opener" property on the
> > > parent
> > > > > > > window
> > > > > > > +  //   and vice versa. This method is dubbed "Native IE XDC"
> > > (NIX).
> > > > > > > +  //
> > > > > > > +  //   This method works by placing a handler object in the
> > > "opener"
> > > > > > > property
> > > > > > > +  //   of a gadget when the container sets up the
> authentication
> > > > > > > information
> > > > > > > +  //   for that gadget (by calling setAuthToken(...)). At that
> > > > point,
> > > > > a
> > > > > > > NIX
> > > > > > > +  //   wrapper is created and placed into the gadget by
> calling
> > > > > > > +  //   theframe.contentWindow.opener = wrapper. Note that as a
> > > > result,
> > > > > > NIX
> > > > > > > can
> > > > > > > +  //   only be used by a container to call a particular gadget
> > > > *after*
> > > > > > > that
> > > > > > > +  //   gadget has called the container at least once via NIX.
> > > > > > > +  //
> > > > > > > +  //   The NIX wrappers in this RPC implementation are
> instances
> > > of
> > > > a
> > > > > > > VBScript
> > > > > > > +  //   class that is created when this implementation loads.
> The
> > > > > reason
> > > > > > > for
> > > > > > > +  //   using a VBScript class stems from the fact that any
> object
> > > > can
> > > > > be
> > > > > > > passed
> > > > > > > +  //   into the opener property.
> > > > > > > +  //   While this is a good thing, as it lets us pass
> functions
> > > and
> > > > > > setup
> > > > > > > a true
> > > > > > > +  //   bidirectional channel via callbacks, it opens a
> potential
> > > > > > security
> > > > > > > hole
> > > > > > > +  //   by which the other page can get ahold of the "window"
> or
> > > > > > "document"
> > > > > > > +  //   objects in the parent page and in turn wreak havok.
> This is
> > > > due
> > > > > > to
> > > > > > > the
> > > > > > > +  //   fact that any JS object useful for establishing such a
> > > > > > > bidirectional
> > > > > > > +  //   channel (such as a function) can be used to access a
> > > function
> > > > > > > +  //   (eg. obj.toString, or a function itself) created in a
> > > > specific
> > > > > > > context,
> > > > > > > +  //   in particular the global context of the sender. Suppose
> > > > > container
> > > > > > > +  //   domain C passes object obj to gadget on domain G. Then
> the
> > > > > gadget
> > > > > > > can
> > > > > > > +  //   access C's global context using:
> > > > > > > +  //   var parentWindow = (new
> obj.toString.constructor("return
> > > > > > > window;"))();
> > > > > > > +  //   Nulling out all of obj's properties doesn't fix this,
> since
> > > > IE
> > > > > > > helpfully
> > > > > > > +  //   restores them to their original values if you do
> something
> > > > > like:
> > > > > > > +  //   delete obj.toString; delete obj.toString;
> > > > > > > +  //   Thus, we wrap the necessary functions and information
> > > inside
> > > > a
> > > > > > > VBScript
> > > > > > > +  //   object. VBScript objects in IE, like DOM objects, are
> in
> > > fact
> > > > > COM
> > > > > > > +  //   wrappers when used in JavaScript, so we can safely pass
> > > them
> > > > > > around
> > > > > > > +  //   without worrying about a breach of context while at the
> > > same
> > > > > time
> > > > > > > +  //   allowing them to act as a pass-through mechanism for
> > > > > information
> > > > > > > +  //   and function calls. The implementation details of this
> > > > VBScript
> > > > > > > wrapper
> > > > > > > +  //   can be found in the setupChannel() method below.
> > > > > > > +  //
> > > > > > > +  //     nix: Internet Explorer-specific window.opener trick.
> > > > > > > +  //       - Internet Explorer 6
> > > > > > > +  //       - Internet Explorer 7
> > > > > > > +  //
> > > > > > > +  // * For Gecko-based browsers, the security model allows a
> child
> > > > to
> > > > > > call
> > > > > > > a
> > > > > > > +  //   function on the frameElement of the iframe, even if the
> > > child
> > > > > is
> > > > > > in
> > > > > > > +  //   a different domain. This method is dubbed
> "frameElement"
> > > > (fe).
> > > > > > > +  //
> > > > > > > +  //   The ability to add and call such functions on the
> > > > frameElement
> > > > > > > allows
> > > > > > > +  //   a bidirectional channel to be setup via the adding of
> > > simple
> > > > > > > function
> > > > > > > +  //   references on the frameElement object itself. In this
> > > > > > > implementation,
> > > > > > > +  //   when the container sets up the authentication
> information
> > > for
> > > > > > that
> > > > > > > gadget
> > > > > > > +  //   (by calling setAuth(...)) it as well adds a special
> > > function
> > > > on
> > > > > > the
> > > > > > > +  //   gadget's iframe. This function can then be used by the
> > > gadget
> > > > > to
> > > > > > > send
> > > > > > > +  //   messages to the container. In turn, when the gadget
> tries
> > > to
> > > > > send
> > > > > > a
> > > > > > > +  //   message, it checks to see if this function has its own
> > > > function
> > > > > > > stored
> > > > > > > +  //   that can be used by the container to call the gadget.
> If
> > > not,
> > > > > the
> > > > > > > +  //   function is created and subsequently used by the
> container.
> > > > > > > +  //   Note that as a result, FE can only be used by a
> container
> > > to
> > > > > call
> > > > > > a
> > > > > > > +  //   particular gadget *after* that gadget has called the
> > > > container
> > > > > at
> > > > > > > +  //   least once via FE.
> > > > > > > +  //
> > > > > > > +  //     fe: Gecko-specific frameElement trick.
> > > > > > > +  //        - Firefox 1+
> > > > > > > +  //
> > > > > > > +  // * For all others, we have a fallback mechanism known as
> > > "ifpc".
> > > > > > IFPC
> > > > > > > +  //   exploits the fact that while same-origin policy
> prohibits a
> > > > > frame
> > > > > > > from
> > > > > > > +  //   accessing members on a window not in the same domain,
> that
> > > > > frame
> > > > > > > can,
> > > > > > > +  //   however, navigate the window heirarchy (via parent).
> This
> > > is
> > > > > > > exploited by
> > > > > > > +  //   having a page on domain A that wants to talk to domain
> B
> > > > create
> > > > > > an
> > > > > > > iframe
> > > > > > > +  //   on domain B pointing to a special relay file and with a
> > > > message
> > > > > > > encoded
> > > > > > > +  //   after the hash (#). This relay, in turn, finds the page
> on
> > > > > domain
> > > > > > > B, and
> > > > > > > +  //   can call a receipt function with the message given to
> it.
> > > The
> > > > > > relay
> > > > > > > URL
> > > > > > > +  //   used by each caller is set via the
> > > > gadgets.rpc.setRelayUrl(..)
> > > > > > and
> > > > > > > +  //   *must* be called before the call method is used.
> > > > > > > +  //
> > > > > > > +  //     ifpc: Iframe-based method, utilizing a relay page, to
> > > send
> > > > a
> > > > > > > message.
> > > > > > > +  //
> > > > > > > +  var relayChannel = typeof window.postMessage === 'function'
> ?
> > > > 'wpm'
> > > > > :
> > > > > > > +                    typeof document.postMessage === 'function'
> ?
> > > > 'dpm'
> > > > > :
> > > > > > > +                    window.ActiveXObject ? 'nix' :
> > > > > > > +                     navigator.product === 'Gecko' ? 'fe' :
> > > > > > >                      'ifpc';
> > > > > > > -  if (relayChannel === 'dpm' || relayChannel === 'wpm') {
> > > > > > > -    window.addEventListener('message', function(packet) {
> > > > > > > -      // TODO validate packet.domain for security reasons
> > > > > > > -      process(gadgets.json.parse(packet.data));
> > > > > > > -    }, false);
> > > > > > > -  }
> > > > > > >
> > > > > > > -  // Default RPC handler
> > > > > > > -  services[''] = function() {
> > > > > > > +  // Conduct any setup necessary for the chosen channel.
> > > > > > > +  setupChannel();
> > > > > > > +
> > > > > > > +  // Create the Default RPC handler.
> > > > > > > +  services[DEFAULT_NAME] = function() {
> > > > > > >     throw new Error('Unknown RPC service: ' + this.s);
> > > > > > >   };
> > > > > > >
> > > > > > > -  // Special RPC handler for callbacks
> > > > > > > -  services['__cb'] = function(callbackId, result) {
> > > > > > > +  // Create a Special RPC handler for callbacks.
> > > > > > > +  services[CALLBACK_NAME] = function(callbackId, result) {
> > > > > > >     var callback = callbacks[callbackId];
> > > > > > >     if (callback) {
> > > > > > >       delete callbacks[callbackId];
> > > > > > > @@ -66,6 +197,147 @@
> > > > > > >   };
> > > > > > >
> > > > > > >   /**
> > > > > > > +   * Conducts any initial global work necessary to setup the
> > > > > > > +   * channel type chosen.
> > > > > > > +   */
> > > > > > > +  function setupChannel() {
> > > > > > > +    // If the channel type is one of the native
> > > > > > > +    // postMessage based ones, setup the handler to receive
> > > > > > > +    // messages.
> > > > > > > +    if (relayChannel === 'dpm' || relayChannel === 'wpm') {
> > > > > > > +      window.addEventListener('message', function(packet) {
> > > > > > > +        // TODO validate packet.domain for security reasons
> > > > > > > +        process(gadgets.json.parse(packet.data));
> > > > > > > +      }, false);
> > > > > > > +    }
> > > > > > > +
> > > > > > > +    // If the channel type is NIX, we need to ensure the
> > > > > > > +    // VBScript wrapper code is in the page and that the
> > > > > > > +    // global Javascript handlers have been set.
> > > > > > > +    if (relayChannel === 'nix') {
> > > > > > > +      // VBScript methods return a type of 'unknown' when
> > > > > > > +      // checked via the typeof operator in IE. Fortunately
> > > > > > > +      // for us, this only applies to COM objects, so we
> > > > > > > +      // won't see this for a real Javascript object.
> > > > > > > +      if (typeof window[NIX_GET_WRAPPER] !== 'unknown') {
> > > > > > > +        window[NIX_HANDLE_MESSAGE] = function(data) {
> > > > > > > +          process(gadgets.json.parse(data));
> > > > > > > +        };
> > > > > > > +
> > > > > > > +        window[NIX_CREATE_CHANNEL] = function(name, channel,
> > > token)
> > > > {
> > > > > > > +          // Verify the authentication token of the gadget
> trying
> > > > > > > +          // to create a channel for us.
> > > > > > > +          if (authToken[name] == token) {
> > > > > > > +            nix_channels[name] = channel;
> > > > > > > +          }
> > > > > > > +        };
> > > > > > > +
> > > > > > > +        // Inject the VBScript code needed.
> > > > > > > +        var vbscript =
> > > > > > > +          '<scr' + 'ipt language="VBScript">'
> > > > > > > +          // We create a class to act as a wrapper for
> > > > > > > +          // a Javascript call, to prevent a break in of
> > > > > > > +          // the context.
> > > > > > > +          + 'Class ' + NIX_WRAPPER + '\n '
> > > > > > > +
> > > > > > > +          // An internal member for keeping track of the
> > > > > > > +          // name of the document (container or gadget)
> > > > > > > +          // for which this wrapper is intended. For
> > > > > > > +          // those wrappers created by gadgets, this is not
> > > > > > > +          // used (although it is set to "..")
> > > > > > > +          + 'Private m_Intended\n'
> > > > > > > +
> > > > > > > +          // Method for internally setting the value
> > > > > > > +          // of the m_Intended property.
> > > > > > > +          + 'Public Sub SetIntendedName(name)\n '
> > > > > > > +          + 'm_Intended = name\n'
> > > > > > > +          + 'End Sub\n'
> > > > > > > +
> > > > > > > +          // A wrapper method which actually causes a
> > > > > > > +          // message to be sent to the other context.
> > > > > > > +          + 'Public Sub SendMessage(data)\n '
> > > > > > > +          + NIX_HANDLE_MESSAGE + '(data)\n'
> > > > > > > +          + 'End Sub\n'
> > > > > > > +
> > > > > > > +          // Method for setting up the container->gadget
> > > > > > > +          // channel. Not strictly needed in the gadget's
> > > > > > > +          // wrapper, but no reason to get rid of it. Note
> here
> > > > > > > +          // that we pass the intended name to the
> > > > NIX_CREATE_CHANNEL
> > > > > > > +          // method so that it can save the channel in the
> proper
> > > > > place
> > > > > > > +          // *and* verify the channel via the authentication
> token
> > > > > > passed
> > > > > > > +          // here.
> > > > > > > +          + 'Public Sub CreateChannel(channel, auth)\n '
> > > > > > > +          + 'Call ' + NIX_CREATE_CHANNEL + '(m_Intended,
> channel,
> > > > > > auth)\n'
> > > > > > > +          + 'End Sub\n'
> > > > > > > +
> > > > > > > +          // An empty field with a unique identifier to
> > > > > > > +          // prevent the code from confusing this wrapper
> > > > > > > +          // with a run-of-the-mill value found in
> window.opener.
> > > > > > > +          + 'Public Sub ' + NIX_ID_FIELD + '()\n '
> > > > > > > +          + 'End Sub\n'
> > > > > > > +          + 'End Class\n '
> > > > > > > +
> > > > > > > +          // Function to get a reference to the wrapper.
> > > > > > > +          + 'Function ' + NIX_GET_WRAPPER + '(name)\n'
> > > > > > > +          + 'Dim wrap\n'
> > > > > > > +          + 'Set wrap = New ' + NIX_WRAPPER + '\n'
> > > > > > > +          + 'wrap.SetIntendedName name\n'
> > > > > > > +          + 'Set ' + NIX_GET_WRAPPER + ' = wrap\n'
> > > > > > > +          + 'End Function'
> > > > > > > +          + '</scr' + 'ipt>'
> > > > > > > +
> > > > > > > +          // Resets the "default" scripting language in IE
> back
> > > > > > > +          // to Javascript. This is needed or else any script
> > > > > > > +          // tags without a proper Language="..." will be
> treated
> > > as
> > > > > > > +          // VBScript once this code is added to the document.
> > > > > > > +          + '<scri' + 'pt language="JScript"></scr' + 'ipt>';
> > > > > > > +
> > > > > > > +        // Note that this code can only be run once the
> document
> > > has
> > > > > > > +        // fully loaded.
> > > > > > > +        // TODO: Perhaps add some sort of check here for this?
> > > > > > > +        document.write(vbscript);
> > > > > > > +      }
> > > > > > > +    }
> > > > > > > +  }
> > > > > > > +
> > > > > > > +  /**
> > > > > > > +   * Conducts any frame-specific work necessary to setup
> > > > > > > +   * the channel type chosen. This method is called when
> > > > > > > +   * the container page first registers the gadget in the
> > > > > > > +   * RPC mechanism. Gadgets, in turn, will complete the setup
> > > > > > > +   * of the channel once they send their first messages.
> > > > > > > +   */
> > > > > > > +  function setupFrame(frameId) {
> > > > > > > +    if (setup[frameId]) {
> > > > > > > +      return;
> > > > > > > +    }
> > > > > > > +
> > > > > > > +    if (relayChannel === 'fe') {
> > > > > > > +      try {
> > > > > > > +        var frame = document.getElementById(frameId);
> > > > > > > +        frame[FE_G2C_CHANNEL] = function(args) {
> > > > > > > +          process(gadgets.json.parse(args));
> > > > > > > +        };
> > > > > > > +      } catch (e) {
> > > > > > > +        // Something went wrong. System will fallback to
> > > > > > > +        // IFPC.
> > > > > > > +      }
> > > > > > > +    }
> > > > > > > +
> > > > > > > +    if (relayChannel === 'nix') {
> > > > > > > +      try {
> > > > > > > +        var frame = document.getElementById(frameId);
> > > > > > > +        frame.contentWindow.opener =
> > > > window[NIX_GET_WRAPPER](frameId);
> > > > > > > +      } catch (e) {
> > > > > > > +        // Something went wrong. System will fallback to
> > > > > > > +        // IFPC.
> > > > > > > +      }
> > > > > > > +    }
> > > > > > > +
> > > > > > > +    setup[frameId] = true;
> > > > > > > +  }
> > > > > > > +
> > > > > > > +  /**
> > > > > > >    * Encodes arguments for the legacy IFPC wire format.
> > > > > > >    *
> > > > > > >    * @param {Object} args
> > > > > > > @@ -86,8 +358,17 @@
> > > > > > >    * @private
> > > > > > >    */
> > > > > > >   function process(rpc) {
> > > > > > > +    //
> > > > > > > +    // RPC object contents:
> > > > > > > +    //   s: Service Name
> > > > > > > +    //   f: From
> > > > > > > +    //   c: The callback ID or 0 if none.
> > > > > > > +    //   a: The arguments for this RPC call.
> > > > > > > +    //   t: The authentication token.
> > > > > > > +    //
> > > > > > >     if (rpc && typeof rpc.s === 'string' && typeof rpc.f ===
> > > 'string'
> > > > > &&
> > > > > > >         rpc.a instanceof Array) {
> > > > > > > +
> > > > > > >       // Validate auth token.
> > > > > > >       if (authToken[rpc.f]) {
> > > > > > >         // We allow type coercion here because all the url
> params
> > > are
> > > > > > > strings.
> > > > > > > @@ -96,43 +377,158 @@
> > > > > > >         }
> > > > > > >       }
> > > > > > >
> > > > > > > -      // The Gecko engine used by FireFox etc. allows an
> IFrame to
> > > > > > > directly call
> > > > > > > -      // methods on the frameElement property added by the
> > > container
> > > > > > page
> > > > > > > even
> > > > > > > -      // if their domains don't match.
> > > > > > > -      // Here we try to set up a relay channel using the
> > > > frameElement
> > > > > > > technique
> > > > > > > -      // to greatly reduce the latency of cross-domain calls
> if
> > > the
> > > > > > > postMessage
> > > > > > > -      // method is not supported.
> > > > > > > -      if (relayChannel === 'ifpc') {
> > > > > > > -        if (rpc.f === '..') {
> > > > > > > -          // Container-to-gadget call
> > > > > > > -          try {
> > > > > > > -            var fel = window.frameElement;
> > > > > > > -            if (typeof fel.__g2c_rpc === 'function' &&
> > > > > > > -                typeof fel.__g2c_rpc.__c2g_rpc != 'function')
> {
> > > > > > > -              fel.__g2c_rpc.__c2g_rpc = function(args) {
> > > > > > > -                process(gadgets.json.parse(args));
> > > > > > > -              };
> > > > > > > -            }
> > > > > > > -          } catch (e) {
> > > > > > > -          }
> > > > > > > -        } else {
> > > > > > > -          // Gadget-to-container call
> > > > > > > -          var iframe = document.getElementById(rpc.f);
> > > > > > > -          if (iframe && typeof iframe.__g2c_rpc != 'function')
> {
> > > > > > > -            iframe.__g2c_rpc = function(args) {
> > > > > > > +      // Call the requested RPC service.
> > > > > > > +      var result = (services[rpc.s] ||
> > > > > > > +                    services[DEFAULT_NAME]).apply(rpc, rpc.a);
> > > > > > > +
> > > > > > > +      // If there is a callback for this service, initiate it
> as
> > > > well.
> > > > > > > +      if (rpc.c) {
> > > > > > > +        gadgets.rpc.call(rpc.f, CALLBACK_NAME, null, rpc.c,
> > > result);
> > > > > > > +      }
> > > > > > > +    }
> > > > > > > +  }
> > > > > > > +
> > > > > > > +  /**
> > > > > > > +   * Attempts to conduct an RPC call to the specified
> > > > > > > +   * target with the specified data via the NIX
> > > > > > > +   * method. If this method fails, the system attempts again
> > > > > > > +   * using the known default of IFPC.
> > > > > > > +   *
> > > > > > > +   * @param {String} targetId Module Id of the RPC service
> > > provider.
> > > > > > > +   * @param {String} from Module Id of the calling provider.
> > > > > > > +   * @param {Object} rpcData The RPC data for this call.
> > > > > > > +   */
> > > > > > > +  function callNIX(targetId, from, rpcData) {
> > > > > > > +    try {
> > > > > > > +      if (from != '..') {
> > > > > > > +        // Call from gadget to the container.
> > > > > > > +        var handler = nix_channels['..'];
> > > > > > > +
> > > > > > > +        // If the gadget has yet to retrieve a reference to
> > > > > > > +        // the NIX handler, try to do so now. We don't do a
> > > > > > > +        // typeof(window.opener[NIX_ID_FIELD]) check here
> > > > > > > +        // because it means accessing that field on the COM
> > > object,
> > > > > > which,
> > > > > > > +        // being an internal function reference, is not
> allowed.
> > > > > > > +        // "in" works because it merely checks for the
> prescence
> > > of
> > > > > > > +        // the key, rather than actually accessing the
> object's
> > > > > > property.
> > > > > > > +        if (!handler && window.opener && NIX_ID_FIELD in
> > > > > window.opener)
> > > > > > {
> > > > > > > +          handler = nix_channels['..'] = window.opener;
> > > > > > > +
> > > > > > > +          // Create the channel to the parent/container. We
> pass
> > > > both
> > > > > > our
> > > > > > > +          // own wrapper and our authentication token for
> > > > > verification.
> > > > > > > +          handler.CreateChannel(window[NIX_GET_WRAPPER]('..'),
> > > > > > > authToken['..']);
> > > > > > > +        }
> > > > > > > +
> > > > > > > +        // If we have a handler, call it.
> > > > > > > +        if (handler) {
> > > > > > > +          handler.SendMessage(rpcData);
> > > > > > > +          return;
> > > > > > > +        }
> > > > > > > +      } else {
> > > > > > > +        // Call from container to a gadget[targetId].
> > > > > > > +
> > > > > > > +        // If we have a handler, call it.
> > > > > > > +        if (nix_channels[targetId]) {
> > > > > > > +          nix_channels[targetId].SendMessage(rpcData);
> > > > > > > +          return;
> > > > > > > +        }
> > > > > > > +      }
> > > > > > > +    } catch (e) {
> > > > > > > +    }
> > > > > > > +
> > > > > > > +    // If we have reached this point, something has failed
> > > > > > > +    // with the NIX method, so we default to using
> > > > > > > +    // IFPC for this call.
> > > > > > > +    callIFPC(targetId, from, rpcData);
> > > > > > > +  }
> > > > > > > +
> > > > > > > +  /**
> > > > > > > +   * Attempts to conduct an RPC call to the specified
> > > > > > > +   * target with the specified data via the FrameElement
> > > > > > > +   * method. If this method fails, the system attempts again
> > > > > > > +   * using the known default of IFPC.
> > > > > > > +   *
> > > > > > > +   * @param {String} targetId Module Id of the RPC service
> > > provider.
> > > > > > > +   * @param {String} from Module Id of the calling provider.
> > > > > > > +   * @param {Object} rpcData The RPC data for this call.
> > > > > > > +   */
> > > > > > > +  function callFrameElement(targetId, from, rpcData) {
> > > > > > > +    try {
> > > > > > > +      if (from != '..') {
> > > > > > > +        // Call from gadget to the container.
> > > > > > > +        var fe = window.frameElement;
> > > > > > > +
> > > > > > > +        if (typeof fe[FE_G2C_CHANNEL] === 'function') {
> > > > > > > +          // Complete the setup of the FE channel if need be.
> > > > > > > +          if (typeof fe[FE_G2C_CHANNEL][FE_C2G_CHANNEL] !==
> > > > > 'function')
> > > > > > {
> > > > > > > +            fe[FE_G2C_CHANNEL][FE_C2G_CHANNEL] =
> function(args) {
> > > > > > >               process(gadgets.json.parse(args));
> > > > > > >             };
> > > > > > >           }
> > > > > > > +
> > > > > > > +          // Conduct the RPC call.
> > > > > > > +          fe[FE_G2C_CHANNEL](rpcData);
> > > > > > > +          return;
> > > > > > >         }
> > > > > > > -      }
> > > > > > > +      } else {
> > > > > > > +        // Call from container to gadget[targetId].
> > > > > > > +        var frame = document.getElementById(targetId);
> > > > > > >
> > > > > > > -      var result = (services[rpc.s] ||
> services['']).apply(rpc,
> > > > > rpc.a);
> > > > > > > -      if (rpc.c) {
> > > > > > > -        gadgets.rpc.call(rpc.f, '__cb', null, rpc.c, result);
> > > > > > > +        if (typeof frame[FE_G2C_CHANNEL] === 'function' &&
> > > > > > > +            typeof frame[FE_G2C_CHANNEL][FE_C2G_CHANNEL] ===
> > > > > 'function')
> > > > > > {
> > > > > > > +
> > > > > > > +          // Conduct the RPC call.
> > > > > > > +          frame[FE_G2C_CHANNEL][FE_C2G_CHANNEL](rpcData);
> > > > > > > +          return;
> > > > > > > +        }
> > > > > > >       }
> > > > > > > +    } catch (e) {
> > > > > > > +    }
> > > > > > > +
> > > > > > > +    // If we have reached this point, something has failed
> > > > > > > +    // with the FrameElement method, so we default to using
> > > > > > > +    // IFPC for this call.
> > > > > > > +    callIFPC(targetId, from, rpcData);
> > > > > > > +  }
> > > > > > > +
> > > > > > > +  /**
> > > > > > > +   * Conducts an RPC call to the specified
> > > > > > > +   * target with the specified data via the IFPC
> > > > > > > +   * method.
> > > > > > > +   *
> > > > > > > +   * @param {String} targetId Module Id of the RPC service
> > > provider.
> > > > > > > +   * @param {String} from Module Id of the calling provider.
> > > > > > > +   * @param {Object} rpcData The RPC data for this call.
> > > > > > > +   */
> > > > > > > +  function callIFPC(targetId, from, rpcData) {
> > > > > > > +    // Retrieve the relay file used by IFPC. Note that
> > > > > > > +    // this must be set before the call, and so we conduct
> > > > > > > +    // an extra check to ensure it is not blank.
> > > > > > > +    var relay = gadgets.rpc.getRelayUrl(targetId);
> > > > > > > +
> > > > > > > +    if (!relay) {
> > > > > > > +      throw new Error('No relay file assigned for IFPC');
> > > > > > >     }
> > > > > > > +
> > > > > > > +    // The RPC mechanism supports two formats for IFPC (legacy
> and
> > > > > > > current).
> > > > > > > +    var src = null;
> > > > > > > +    if (useLegacyProtocol[targetId]) {
> > > > > > > +      // Format:
> > > > > #iframe_id&callId&num_packets&packet_num&block_of_data
> > > > > > > +      src = [relay, '#', encodeLegacyData([from, callId, 1, 0,
> > > > > > > +             encodeLegacyData([from, serviceName, '', '',
> > > > > from].concat(
> > > > > > > +               Array.prototype.slice.call(arguments,
> > > > 3)))])].join('');
> > > > > > > +    } else {
> > > > > > > +      // Format: #targetId & sourceId@callId & packetNum &
> > > packetId
> > > > &
> > > > > > > packetData
> > > > > > > +      src = [relay, '#', targetId, '&', from, '@', callId,
> > > > > > > +             '&1&0&', encodeURIComponent(rpcData)].join('');
> > > > > > > +    }
> > > > > > > +
> > > > > > > +    // Conduct the IFPC call by creating the Iframe with
> > > > > > > +    // the relay URL and appended message.
> > > > > > > +    emitInvisibleIframe(src);
> > > > > > >   }
> > > > > > >
> > > > > > > +
> > > > > > >   /**
> > > > > > >    * Helper function to emit an invisible IFrame.
> > > > > > >    * @param {String} src SRC attribute of the IFrame to emit.
> > > > > > > @@ -221,6 +617,15 @@
> > > > > > >      * @member gadgets.rpc
> > > > > > >      */
> > > > > > >     register: function(serviceName, handler) {
> > > > > > > +      if (serviceName == CALLBACK_NAME) {
> > > > > > > +        throw new Error("Cannot overwrite callback service");
> > > > > > > +      }
> > > > > > > +
> > > > > > > +      if (serviceName == DEFAULT_NAME) {
> > > > > > > +        throw new Error("Cannot overwrite default service:"
> > > > > > > +                        + " use registerDefault");
> > > > > > > +      }
> > > > > > > +
> > > > > > >       services[serviceName] = handler;
> > > > > > >     },
> > > > > > >
> > > > > > > @@ -231,6 +636,15 @@
> > > > > > >      * @member gadgets.rpc
> > > > > > >      */
> > > > > > >     unregister: function(serviceName) {
> > > > > > > +      if (serviceName == CALLBACK_NAME) {
> > > > > > > +        throw new Error("Cannot delete callback service");
> > > > > > > +      }
> > > > > > > +
> > > > > > > +      if (serviceName == DEFAULT_NAME) {
> > > > > > > +        throw new Error("Cannot delete default service:"
> > > > > > > +                        + " use unregisterDefault");
> > > > > > > +      }
> > > > > > > +
> > > > > > >       delete services[serviceName];
> > > > > > >     },
> > > > > > >
> > > > > > > @@ -272,12 +686,14 @@
> > > > > > >       if (callback) {
> > > > > > >         callbacks[callId] = callback;
> > > > > > >       }
> > > > > > > -      var from;
> > > > > > > +
> > > > > > > +      // Default to the container calling.
> > > > > > > +      var from = '..';
> > > > > > > +
> > > > > > >       if (targetId === '..') {
> > > > > > >         from = window.name;
> > > > > > > -      } else {
> > > > > > > -        from = '..';
> > > > > > >       }
> > > > > > > +
> > > > > > >       // Not used by legacy, create it anyway...
> > > > > > >       var rpcData = gadgets.json.stringify({
> > > > > > >         s: serviceName,
> > > > > > > @@ -287,53 +703,37 @@
> > > > > > >         t: authToken[targetId]
> > > > > > >       });
> > > > > > >
> > > > > > > +      var channelType = relayChannel;
> > > > > > > +
> > > > > > > +      // If we are told to use the legacy format, then we must
> > > > > > > +      // default to IFPC.
> > > > > > >       if (useLegacyProtocol[targetId]) {
> > > > > > > -        relayChannel = 'ifpc';
> > > > > > > +        channelType = 'ifpc';
> > > > > > >       }
> > > > > > >
> > > > > > > -      switch (relayChannel) {
> > > > > > > -      case 'dpm': // use document.postMessage
> > > > > > > -        var targetDoc = targetId === '..' ? parent.document :
> > > > > > > -
> > > >  frames[targetId].document;
> > > > > > > -        targetDoc.postMessage(rpcData);
> > > > > > > -        break;
> > > > > > > -      case 'wpm': // use window.postMessage
> > > > > > > -        var targetWin = targetId === '..' ? parent :
> > > > frames[targetId];
> > > > > > > -        targetWin.postMessage(rpcData, "*");
> > > > > > > -        break;
> > > > > > > -      default: // use 'ifpc' as a fallback mechanism
> > > > > > > -        var relay = gadgets.rpc.getRelayUrl(targetId);
> > > > > > > -        // TODO split message if too long
> > > > > > > -        var src;
> > > > > > > -        if (useLegacyProtocol[targetId]) {
> > > > > > > -          //
> > > #iframe_id&callId&num_packets&packet_num&block_of_data
> > > > > > > -          src = [relay, '#', encodeLegacyData([from, callId,
> 1, 0,
> > > > > > > -                 encodeLegacyData([from, serviceName, '', '',
> > > > > > > from].concat(
> > > > > > > -                 Array.prototype.slice.call(arguments,
> > > > > 3)))])].join('');
> > > > > > > -        } else {
> > > > > > > -          // Try the frameElement channel if available
> > > > > > > -          try {
> > > > > > > -            if (from === '..') {
> > > > > > > -              // Container-to-gadget
> > > > > > > -              var iframe = document.getElementById(targetId);
> > > > > > > -              if (typeof iframe.__g2c_rpc.__c2g_rpc ===
> > > 'function')
> > > > {
> > > > > > > -                iframe.__g2c_rpc.__c2g_rpc(rpcData);
> > > > > > > -                return;
> > > > > > > -              }
> > > > > > > -            } else {
> > > > > > > -              // Gadget-to-container
> > > > > > > -              if (typeof window.frameElement.__g2c_rpc ===
> > > > 'function')
> > > > > {
> > > > > > > -                window.frameElement.__g2c_rpc(rpcData);
> > > > > > > -                return;
> > > > > > > -              }
> > > > > > > -            }
> > > > > > > -          } catch (e) {
> > > > > > > -          }
> > > > > > > -          // # targetId & sourceId@callId & packetNum &
> packetId
> > > &
> > > > > > > packetData
> > > > > > > -          src = [relay, '#', targetId, '&', from, '@', callId,
> > > > > > > -                 '&1&0&',
> encodeURIComponent(rpcData)].join('');
> > > > > > > -        }
> > > > > > > -        emitInvisibleIframe(src);
> > > > > > > +      switch (channelType) {
> > > > > > > +        case 'dpm': // use document.postMessage.
> > > > > > > +          var targetDoc = targetId === '..' ? parent.document
> :
> > > > > > > +
> > > > >  frames[targetId].document;
> > > > > > > +          targetDoc.postMessage(rpcData);
> > > > > > > +          break;
> > > > > > > +
> > > > > > > +        case 'wpm': // use window.postMessage.
> > > > > > > +          var targetWin = targetId === '..' ? parent :
> > > > > frames[targetId];
> > > > > > > +          targetWin.postMessage(rpcData, "*");
> > > > > > > +          break;
> > > > > > > +
> > > > > > > +        case 'nix': // use NIX.
> > > > > > > +          callNIX(targetId, from, rpcData);
> > > > > > > +          break;
> > > > > > > +
> > > > > > > +        case 'fe': // use FrameElement.
> > > > > > > +          callFrameElement(targetId, from, rpcData);
> > > > > > > +          break;
> > > > > > > +
> > > > > > > +        default: // use 'ifpc' as a fallback mechanism.
> > > > > > > +          callIFPC(targetId, from, rpcData);
> > > > > > > +          break;
> > > > > > >       }
> > > > > > >     },
> > > > > > >
> > > > > > > @@ -372,15 +772,13 @@
> > > > > > >      */
> > > > > > >     setAuthToken: function(targetId, token) {
> > > > > > >       authToken[targetId] = token;
> > > > > > > +      setupFrame(targetId);
> > > > > > >     },
> > > > > > >
> > > > > > >     /**
> > > > > > >      * Gets the RPC relay mechanism.
> > > > > > > -     * @return {String} RPC relay mechanism. Supported types:
> > > > > > > -     *                  'wpm' - Use window.postMessage
> (defined by
> > > > > > HTML5)
> > > > > > > -     *                  'dpm' - Use document.postMessage
> (defined
> > > by
> > > > > an
> > > > > > > early
> > > > > > > -     *                          draft of HTML5 and implemented
> by
> > > > > Opera)
> > > > > > > -     *                  'ifpc' - Use invisible IFrames
> > > > > > > +     * @return {String} RPC relay mechanism. See above for
> > > > > > > +     *   a list of supported types.
> > > > > > >      *
> > > > > > >      * @member gadgets.rpc
> > > > > > >      */
> > > > > > >
> > > > > > > Modified:
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > > > > > URL:
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java?rev=672393&r1=672392&r2=672393&view=diff
> > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
> ==============================================================================
> > > > > > > ---
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > > > > > (original)
> > > > > > > +++
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > > > > > Fri Jun 27 14:01:20 2008
> > > > > > > @@ -221,7 +221,8 @@
> > > > > > >           "a {color:#0000cc;}a:visited {color:#551a8b;}" +
> > > > > > >           "a:active {color:#ff0000;}" +
> > > > > > >           "body{margin: 0px;padding:
> 0px;background-color:white;}"
> > > +
> > > > > > > -          "</style></head>";
> > > > > > > +          "</style><base>" +
> gadget.getSpec().getUrl().toString()
> > > +
> > > > > > > +          "</base></head>";
> > > > > > >     markup.append(boilerPlate);
> > > > > > >     LocaleSpec localeSpec =
> > > > > gadget.getSpec().getModulePrefs().getLocale(
> > > > > > >         gadget.getContext().getLocale());
> > > > > > >
> > > > > > > Added:
> > > > > >
> incubator/shindig/trunk/javascript/container/rpctest_container.html
> > > > > > > URL:
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_container.html?rev=672393&view=auto
> > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
> ==============================================================================
> > > > > > > ---
> > > > incubator/shindig/trunk/javascript/container/rpctest_container.html
> > > > > > > (added)
> > > > > > > +++
> > > > incubator/shindig/trunk/javascript/container/rpctest_container.html
> > > > > > Fri
> > > > > > > Jun 27 14:01:20 2008
> > > > > > > @@ -0,0 +1,90 @@
> > > > > > > +<!--
> > > > > > > +  Simple page for testing gadgets.rpc performance.
> > > > > > > +  Allows you to add a simulated "gadget" (in this case just a
> > > static
> > > > > > > +  HTML page which loads gadgets.rpc also), and pass some
> > > > > > > +  specified number of random messages of specified size to
> > > > > > > +  and from it.
> > > > > > > +
> > > > > > > +  A simple recommended setup is to start up two instances of
> > > > > > > +  the Shindig Gadgets Server on two separate ports to test
> > > > > > > +  "real" cross-domain communication, since port is factored
> > > > > > > +  into the same-domain policy enforced by browsers.
> > > > > > > +
> > > > > > > +  If your servers are on localhost:8080 and localhost:8081,
> then
> > > > hit:
> > > > > > > +
> > > > >
> http://localhost:8080/gadgets/files/container/rpctest_container.html?
> > > > > > \
> > > > > > > +
> > > > http://localhost:8081/gadgets/files/container/rpctest_gadget.html&\<http://localhost:8081/gadgets/files/container/rpctest_gadget.html&%5C>
> <http://localhost:8081/gadgets/files/container/rpctest_gadget.html&%5C>
> > > <http://localhost:8081/gadgets/files/container/rpctest_gadget.html&%5C
> >
> > > > > > > +
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
> http://localhost:8081/gadgets/files/container/rpc_relay.uncompressed.html
> > > > > > > +
> > > > > > > +  (Note the backslashes should be removed, as they exist for
> > > > > formatting
> > > > > > > only.)
> > > > > > > +
> > > > > > > +  There are two arguments, separated by an ampersand:
> > > > > > > +  1. URL of the "gadget" test page.
> > > > > > > +  2. URL of the "gadget" test page's RPC relay (for browsers
> > > > requiring
> > > > > > > it).
> > > > > > > +-->
> > > > > > > +<html>
> > > > > > > +  <head>
> > > > > > > +    <title>gadgets.rpc Performance Tests: Container</title>
> > > > > > > +    <script src="../../js/rpc.js?c=1&debug=1"></script>
> > > > > > > +    <script src="rpctest_perf.js"></script>
> > > > > > > +    <script>
> > > > > > > +      function initTest() {
> > > > > > > +        var container = document.getElementById("container");
> > > > > > > +
> > > > > > > +        // query string is assumed to be the "gadget" URL as
> arg
> > > 1,
> > > > > > > +        // and optionally the relay URL as arg 2
> > > > > > > +        var pageArgs =
> > > > window.location.search.substring(1).split('&');
> > > > > > > +        var gadgetUrl = pageArgs[0];
> > > > > > > +        var secret = Math.round(Math.random()*10000000);
> > > > > > > +        if (pageArgs[1]) {
> > > > > > > +          gadgets.rpc.setRelayUrl('gadget', pageArgs[1]);
> > > > > > > +        }
> > > > > > > +        var containerRelay = pageArgs[2] || '';
> > > > > > > +        container.innerHTML = "<iframe id='gadget'
> name='gadget'
> > > > > > > height=300 width=300 src='" + gadgetUrl + "?parent=" +
> > > containerRelay
> > > > +
> > > > > > > "#rpctoken=" + secret + "'></iframe>";
> > > > > > > +        gadgets.rpc.setAuthToken('gadget', secret);
> > > > > > > +
> > > > > > > +        initPerfTest();
> > > > > > > +      };
> > > > > > > +    </script>
> > > > > > > +  </head>
> > > > > > > +  <body onload="initTest();">
> > > > > > > +    <div>gadgets.rpc Performance: Container Page</div><hr/>
> > > > > > > +    <div>Test<br/>
> > > > > > > +      <ul>
> > > > > > > +        <li>Number of messages to send:
> > > > > > > +          <select name="num_msgs" id="num_msgs">
> > > > > > > +            <option value="1">1</option>
> > > > > > > +            <option value="10">10</option>
> > > > > > > +            <option value="100" selected>100</option>
> > > > > > > +            <option value="1000">1000</option>
> > > > > > > +          </select>
> > > > > > > +        </li>
> > > > > > > +        <li>Message size:
> > > > > > > +          <select name="msg_size" id="msg_size">
> > > > > > > +            <option value="10">10 B</option>
> > > > > > > +            <option value="100">100 B</option>
> > > > > > > +            <option value="1024" selected>1 kB</option>
> > > > > > > +            <option value="10240">10 kB</option>
> > > > > > > +            <option value="102400">100 kB</option>
> > > > > > > +            <option value="1048576">1 MB</option>
> > > > > > > +          </select>
> > > > > > > +        </li>
> > > > > > > +        <li>
> > > > > > > +          <input type="button" value="Start The Test!"
> > > > > > > onclick="runPerfTest('gadget');" />
> > > > > > > +        </li>
> > > > > > > +      </ul>
> > > > > > > +    </div>
> > > > > > > +    <div id="test_running" style="display:none;">
> > > > > > > +      Running test...
> > > > > > > +    </div>
> > > > > > > +    <div id="results" style="display:none;">
> > > > > > > +      Results: Gadget-to-Container<br/>
> > > > > > > +      Messages: <span id="results_num_received"></span>,
> Bytes:
> > > > <span
> > > > > > > id="results_bytes_received"></span> <span
> > > id="in_or_out"></span><br/>
> > > > > > > +      Time elapsed for test run: <span
> > > > > > id="results_time_used"></span><br/>
> > > > > > > +      Messages/second: <span
> > > id="results_msgs_per_sec"></span><br/>
> > > > > > > +      Bytes/second: <span id="results_bytes_per_sec"></span>
> > > > > > > +    </div>
> > > > > > > +    <hr/>
> > > > > > > +    <div>Gadget:</div>
> > > > > > > +    <div id="container"></div>
> > > > > > > +  </body>
> > > > > > > +</html>
> > > > > > >
> > > > > > > Added:
> > > > incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > > > > > > URL:
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_gadget.html?rev=672393&view=auto
> > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
> ==============================================================================
> > > > > > > ---
> > > incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > > > > > > (added)
> > > > > > > +++
> > > incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > > > > Fri
> > > > > > > Jun 27 14:01:20 2008
> > > > > > > @@ -0,0 +1,49 @@
> > > > > > > +<html>
> > > > > > > +  <head>
> > > > > > > +    <title>gadgets.rpc Performance Tests: Gadget</title>
> > > > > > > +    <script src="../../js/rpc.js?c=1&debug=1"></script>
> > > > > > > +    <script src="rpctest_perf.js"></script>
> > > > > > > +    <script>
> > > > > > > +      var containerRelay =
> window.location.search.substring(1);
> > > > > > > +      gadgets.rpc.setRelayUrl(null, containerRelay);
> > > > > > > +    </script>
> > > > > > > +  </head>
> > > > > > > +  <body onload="initPerfTest();">
> > > > > > > +    <div>gadgets.rpc Performance: "Gadget" page</div><hr/>
> > > > > > > +    <div>Test<br/>
> > > > > > > +      <ul>
> > > > > > > +        <li>Number of messages to send:
> > > > > > > +          <select name="num_msgs" id="num_msgs">
> > > > > > > +            <option value="1">1</option>
> > > > > > > +            <option value="10">10</option>
> > > > > > > +            <option value="100" selected>100</option>
> > > > > > > +            <option value="1000">1000</option>
> > > > > > > +          </select>
> > > > > > > +        </li>
> > > > > > > +        <li>Message size:
> > > > > > > +          <select name="msg_size" id="msg_size">
> > > > > > > +            <option value="10">10 B</option>
> > > > > > > +            <option value="100">100 B</option>
> > > > > > > +            <option value="1024" selected>1 kB</option>
> > > > > > > +            <option value="10240">10 kB</option>
> > > > > > > +            <option value="102400">100 kB</option>
> > > > > > > +            <option value="1048576">1 MB</option>
> > > > > > > +          </select>
> > > > > > > +        </li>
> > > > > > > +        <li>
> > > > > > > +          <input type="button" value="Start The Test!"
> > > > > > > onclick="runPerfTest();" />
> > > > > > > +        </li>
> > > > > > > +      </ul>
> > > > > > > +    </div>
> > > > > > > +    <div id="test_running" style="display:none;">
> > > > > > > +      Running test...
> > > > > > > +    </div>
> > > > > > > +    <div id="results" style="display:none;">
> > > > > > > +      Results: Gadget-to-Container<br/>
> > > > > > > +      Messages: <span id="results_num_received"></span>,
> Bytes:
> > > > <span
> > > > > > > id="results_bytes_received"></span> <span
> > > id="in_or_out"></span><br/>
> > > > > > > +      Time elapsed for test run: <span
> > > > > > id="results_time_used"></span><br/>
> > > > > > > +      Messages/second: <span
> > > id="results_msgs_per_sec"></span><br/>
> > > > > > > +      Bytes/second: <span id="results_bytes_per_sec"></span>
> > > > > > > +    </div>
> > > > > > > +  </body>
> > > > > > > +</html>
> > > > > > >
> > > > > > > Added:
> incubator/shindig/trunk/javascript/container/rpctest_perf.js
> > > > > > > URL:
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_perf.js?rev=672393&view=auto
> > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
> ==============================================================================
> > > > > > > ---
> incubator/shindig/trunk/javascript/container/rpctest_perf.js
> > > > > (added)
> > > > > > > +++
> incubator/shindig/trunk/javascript/container/rpctest_perf.js
> > > Fri
> > > > > Jun
> > > > > > 27
> > > > > > > 14:01:20 2008
> > > > > > > @@ -0,0 +1,93 @@
> > > > > > > +var perfStats = null;
> > > > > > > +var currentRun = {};
> > > > > > > +
> > > > > > > +function perfService(message) {
> > > > > > > +  if (perfStats.numResults++ === 0) {
> > > > > > > +    perfStats.firstMsg = message; // stored since it has
> "real"
> > > > start
> > > > > > time
> > > > > > > +  }
> > > > > > > +  perfStats.bytesReceived += message.length;
> > > > > > > +};
> > > > > > > +
> > > > > > > +function clearPerfStats(inOrOut) {
> > > > > > > +  perfStats = {
> > > > > > > +    numResults: 0,
> > > > > > > +    bytesReceived: 0,
> > > > > > > +    firstMsg: null
> > > > > > > +  };
> > > > > > > +
> > > > > > > +  document.getElementById("in_or_out").innerHTML = inOrOut;
> > > > > > > +
> > > > > > > +  // hide results fields
> > > > > > > +  document.getElementById("results").style.display = "none";
> > > > > > > +};
> > > > > > > +
> > > > > > > +function completePerfStats() {
> > > > > > > +  perfStats.timeEnded = new Date().getTime();
> > > > > > > +
> > > > > > > +  // get time started from the first sent message
> > > > > > > +  perfStats.timeStarted = perfStats.firstMsg.substr(0,
> > > > > > > perfStats.firstMsg.indexOf(':'));
> > > > > > > +
> > > > > > > +  var timeUsedMs = perfStats.timeEnded -
> perfStats.timeStarted;
> > > > > > > +
> > > > > > > +  // fill in fields
> > > > > > > +  document.getElementById("results_num_received").innerHTML =
> > > > > > > perfStats.numResults;
> > > > > > > +  document.getElementById("results_bytes_received").innerHTML
> =
> > > > > > > perfStats.bytesReceived;
> > > > > > > +  document.getElementById("results_time_used").innerHTML =
> > > > timeUsedMs
> > > > > +
> > > > > > > "ms";
> > > > > > > +  document.getElementById("results_msgs_per_sec").innerHTML =
> > > > > > > (perfStats.numResults / (timeUsedMs / 1000));
> > > > > > > +  document.getElementById("results_bytes_per_sec").innerHTML =
> > > > > > > (perfStats.bytesReceived / (timeUsedMs / 1000));
> > > > > > > +  document.getElementById("test_running").style.display =
> "none";
> > > > > > > +  document.getElementById("results").style.display = "";
> > > > > > > +};
> > > > > > > +
> > > > > > > +function initPerfTest() {
> > > > > > > +  clearPerfStats();
> > > > > > > +  gadgets.rpc.register("perf_service", perfService);
> > > > > > > +  gadgets.rpc.register("clear_perf_stats", clearPerfStats);
> > > > > > > +  gadgets.rpc.register("complete_perf_stats",
> completePerfStats);
> > > > > > > +};
> > > > > > > +
> > > > > > > +var alphabet =
> > > > > > > "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
> > > > > > _-*&(){}'";
> > > > > > > +
> > > > > > > +function sendPerfMessage() {
> > > > > > > +  var msgToSend = currentRun.msg;
> > > > > > > +  if (currentRun.curMsgId++ <= 1) {
> > > > > > > +    var nowString = new Date().getTime() + ':';
> > > > > > > +    msgToSend = nowString +
> > > > > currentRun.msg.substring(nowString.length);
> > > > > > > +  }
> > > > > > > +
> > > > > > > +  gadgets.rpc.call(currentRun.targetId, "perf_service", null,
> > > > > > msgToSend);
> > > > > > > +  if (currentRun.curMsgId < currentRun.endMsgId) {
> > > > > > > +    // loop, giving up execution in case rpc technique demands
> it
> > > > > > > +    window.setTimeout(sendPerfMessage, 0);
> > > > > > > +  } else {
> > > > > > > +    // send finisher
> > > > > > > +    window.setTimeout(function() {
> > > > > gadgets.rpc.call(currentRun.targetId,
> > > > > > > "complete_perf_stats", null); }, 0);
> > > > > > > +  }
> > > > > > > +};
> > > > > > > +
> > > > > > > +function runPerfTest(targetId) {
> > > > > > > +  document.getElementById("test_running").style.display = "";
> > > > > > > +
> > > > > > > +  // initialize the current run
> > > > > > > +  var num_msgs = document.getElementById("num_msgs").value;
> > > > > > > +  var msg_size = document.getElementById("msg_size").value;
> > > > > > > +
> > > > > > > +  currentRun.targetId = targetId;
> > > > > > > +  currentRun.curMsgId = 0;
> > > > > > > +  currentRun.endMsgId = num_msgs;
> > > > > > > +
> > > > > > > +  var msg = [];
> > > > > > > +  for (var i = 0; i < msg_size; ++i) {
> > > > > > > +    msg[i] =
> > > > > alphabet.charAt(Math.round(Math.random(alphabet.length)));
> > > > > > > +  }
> > > > > > > +  currentRun.msg = msg.join('');
> > > > > > > +
> > > > > > > +  // clear local perf stats
> > > > > > > +  clearPerfStats("(outbound)");
> > > > > > > +
> > > > > > > +  // clear target perf stats
> > > > > > > +  gadgets.rpc.call(targetId, "clear_perf_stats", null,
> > > "(inbound)");
> > > > > > > +
> > > > > > > +  // kick off the send loop
> > > > > > > +  sendPerfMessage();
> > > > > > > +};
> > > > > > >
> > > > > > >
> > > > > > >
> > > > > >
> > > > >
> > > >
> > >
>

Re: svn commit: r672393 - in /incubator/shindig/trunk: features/rpc/ java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ javascript/container/

Posted by Chris Chabot <ch...@xs4all.nl>.
I'm kind of on board with Kevin that this probably should have been done
the apache way, there's a lot of very big containers that use shindig,
and hundreds of smaller ones too .. We're an apache project to make sure
everything happens democratically, and without that in place people
might not we're ready to graduate out of incubation you know :-)

As a fellow committer i for one would've liked to at least take a quick
look at, or at least hear about it on the mailing list it before seeing
it show up 'out of nowhere', at least with other big changes we know
it's being worked on in advance, and we then have the chance to see the
patch cook in jira so people could respond to it if they wanted (like,
positively community like even! :P)

Anyhow, for now the compromise works for me, but let's do it differently
next time ok :-)

    -- Chris

On Fri, 2008-06-27 at 16:39 -0700, John Hjelmstad wrote:

> How's this for a compromise: I'll add a param that disables the new
> technique by default, and allows it to be used if preferred. The rest is
> refactoring and documentation. Given that's the bulk of the code change in
> terms of volume, I didn't create a JIRA issue for it. I'm happy to do so to
> enable it longer-term, however. Thoughts?
> 
> John
> 
> On Fri, Jun 27, 2008 at 4:27 PM, Kevin Brown <et...@google.com> wrote:
> 
> > On Fri, Jun 27, 2008 at 3:45 PM, John Hjelmstad <fa...@google.com> wrote:
> >
> > > As indicated in the CL, I tested this on half a dozen browsers. We don't
> > > have unit tests for it. I've also sent the code and inquiries about the
> > > technique to several poeple, some of whom expressed concerns but no
> > ability
> > > to actively exploit the code.
> >
> >
> > Anyone on this mailing list? Either of the people I mentioned?
> >
> >
> > > The main concern was around VBScript
> > > knowledge, which few people I know extensively have. That was true of
> > IFPC
> > > when it was originally released as well. If any attacks are found, they
> > > will
> > > be actively closed or the technique will be removed. Would you prefer
> > that
> > > no code, irrespective of its value, ever be submitted? It's possible for
> > > any
> > > code to have bugs, and a lot of code to have security holes. If found,
> > > they're fixed.
> >
> >
> > Yes, but this is code that uses a security technique that is highly
> > questionable and has been flagged as potentially problematic by several
> > people. Therefore, it needs more scrutiny. I won't deploy this on any of
> > our
> > production systems until I get an OK from people who are actually familiar
> > with the problems in question have chimed in.
> >
> > This is also a very large code change, and, per policy, large code changes
> > need JIRA tickets and feedback from other people. There are hundreds (if
> > not
> > thousands) of people using Shindig today and the code is deployed to
> > hundreds of millions of users.
> >
> >
> > >
> > > John
> > >
> > > On Fri, Jun 27, 2008 at 3:38 PM, Kevin Brown <et...@google.com> wrote:
> > >
> > > > Please actually test your code or send it to other shindig committers
> > > > before
> > > > you commit it. Your change to GadgetRenderingTask breaks proxying and
> > > > makeRequest in several cases. Fix it.
> > > >
> > > > Have you run this by Brian and/or Mike Samuel? They both expressed
> > doubts
> > > > about whether this mechanism was actually secure.
> > > >
> > > > On Fri, Jun 27, 2008 at 2:06 PM, John Hjelmstad <fa...@google.com>
> > > wrote:
> > > >
> > > > > +jschorr in case there are comments.
> > > > >
> > > > > On Fri, Jun 27, 2008 at 2:01 PM, <jo...@apache.org> wrote:
> > > > >
> > > > > > Author: johnh
> > > > > > Date: Fri Jun 27 14:01:20 2008
> > > > > > New Revision: 672393
> > > > > >
> > > > > > URL: http://svn.apache.org/viewvc?rev=672393&view=rev
> > > > > > Log:
> > > > > > Cleaned up gadgets.rpc library implementation, and implemented fast
> > > IE
> > > > > > transport layer.
> > > > > >
> > > > > > Credit:
> > > > > >        Joseph Schorr (jschorr@google.com) and I implemented this
> > > > > together,
> > > > > > but he
> > > > > >        really did all the hard work, as well as developing the NIX
> > > > > > technique's
> > > > > >        fundamentals. Huge thanks to Joseph!
> > > > > >
> > > > > > Details:
> > > > > >        * Created a new relayChannel for Firefox frameElement
> > > technique,
> > > > > >          making its implementation more linear to read.
> > > > > >        * Consolidated all transport-specific code in
> > setupChannel(),
> > > > > > setupFrame(),
> > > > > >          and callX(), where X = relay type. This refactoring makes
> > > > setup
> > > > > > and use
> > > > > >          of each transport more clear, and makes it possible in
> > later
> > > > > > revisions
> > > > > >          to separate out each method as its own class. In theory,
> > if
> > > we
> > > > > can
> > > > > > trust
> > > > > >          things like User-Agent headers, we can use this to reduce
> > > > rpc.js
> > > > > > code
> > > > > >          size significantly by only including the needed
> > > transport(s).
> > > > > >        * Implemented "NIX" - Native IE XDC. This method exploits
> > the
> > > > fact
> > > > > >          that in IE6 and IE7 the window.opener property can be both
> > > set
> > > > > and
> > > > > >          read across domain boundaries for an IFRAME, and that any
> > > > object
> > > > > >          can be passed through it. In particular, functions
> > > > implementing
> > > > > >          message passing can be passed. These can't be JS
> > functions,
> > > > > > however,
> > > > > >          since those can leak sender context. So, VBScript (COM)
> > > > objects
> > > > > >          wrapping these are passed to maintain context isolation.
> > > > > >          - Requires for IE6/7 that rpc.js be included in the
> > > container
> > > > > >            at load time, not dynamically. TODO added to consider
> > > > > detecting
> > > > > >            whether dynamic loading is happening (at which point we
> > > > should
> > > > > >            fall back to IFPC).
> > > > > >          - Message channel handshake *and* message passing
> > validated
> > > > > >            using authToken, as with other channels.
> > > > > >          - Impl requires that gadget call container first - else
> > IFPC
> > > > > >            is used. This is the same as the frameElement technique.
> > > We
> > > > > >            could add a setInterval()-based initialization routine
> > > later
> > > > > >            if needed. To date, the only gadgets.rpc calls made from
> > > > > >            container to gadget without gadget to container first
> > are
> > > > > >            in theory pubsub messages.
> > > > > >          - Extensive documentation on this technique and the others
> > > > > >            added to the comments, since they're stripped out in the
> > > > > >            majority case.
> > > > > >        * Implemented quick-and-dirty performance testing page
> > > verifying
> > > > > >          the library works and giving basic performance metrics.
> > > > > >          This needs to be improved! But does the trick for now...
> > > > > >
> > > > > > Testing/Data:
> > > > > >
> > > > > > Library verified on the following browsers with the RPC test page.
> > > For
> > > > > > each,
> > > > > > a "bandwidth" measure was taken by averaging the results of 6 G2C
> > and
> > > > C2G
> > > > > > runs
> > > > > > with 100 messages being passed of 1K apiece. Units are 1K
> > > messages/sec.
> > > > > > The "latency" value is the average amount of time taken, over 6 G2C
> > > and
> > > > > C2G
> > > > > > runs,
> > > > > > to send 1 1K message. Happily, for all browsers save Safari (soon
> > due
> > > > to
> > > > > > get
> > > > > > window.postMessage), latency is around or below time required to
> > > > achieve
> > > > > > a cross-domain call without user-perceived delay.
> > > > > >
> > > > > >        * Firefox 3 (WPM) - bandwidth: 88 kB/sec; latency: 18ms.
> > > > > >        * Firefox 2 (FE)  - bandwidth: 80 kB/sec; latency: 15ms.
> > > > > >        * Opera 9   (WPM) - bandwidth: 85 kB/sec; latency: 7ms.
> > > > > >        * IE6       (NIX) - bandwidth: 64 kB/sec; latency: 18ms.
> > > > > >        * IE7       (NIX) - bandwidth: 64 kB/sec; latency: 22ms.
> > > > > >        * Safari 3 (IFPC) - bandwidth: ?6-8kB/sec; latency:
> > > ?100-200ms.
> > > > > >          - Safari is somewhat flaky with the RPC test page, before
> > > > > >            and after the rpc.js change. Multiple messages seem to
> > > > > >            confuse it from time to time.
> > > > > >
> > > > > >
> > > > > > Added:
> > > > > >
> > >  incubator/shindig/trunk/javascript/container/rpctest_container.html
> > > > > >    incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > > > > >    incubator/shindig/trunk/javascript/container/rpctest_perf.js
> > > > > > Modified:
> > > > > >    incubator/shindig/trunk/features/rpc/rpc.js
> > > > > >
> > > > > >
> > > > >
> > > >
> > >
> >  incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > > > >
> > > > > > Modified: incubator/shindig/trunk/features/rpc/rpc.js
> > > > > > URL:
> > > > > >
> > > > >
> > > >
> > >
> > http://svn.apache.org/viewvc/incubator/shindig/trunk/features/rpc/rpc.js?rev=672393&r1=672392&r2=672393&view=diff
> > > > > >
> > > > > >
> > > > >
> > > >
> > >
> > ==============================================================================
> > > > > > --- incubator/shindig/trunk/features/rpc/rpc.js (original)
> > > > > > +++ incubator/shindig/trunk/features/rpc/rpc.js Fri Jun 27 14:01:20
> > > > 2008
> > > > > > @@ -18,7 +18,7 @@
> > > > > >
> > > > > >  /**
> > > > > >  * @fileoverview Remote procedure call library for
> > > gadget-to-container,
> > > > > > - * container-to-gadget, and gadget-to-gadget communication.
> > > > > > + * container-to-gadget, and gadget-to-gadget (thru container)
> > > > > > communication.
> > > > > >  */
> > > > > >
> > > > > >  var gadgets = gadgets || {};
> > > > > > @@ -29,6 +29,30 @@
> > > > > >  * @name gadgets.rpc
> > > > > >  */
> > > > > >  gadgets.rpc = function() {
> > > > > > +  // General constants.
> > > > > > +  var CALLBACK_NAME = '__cb';
> > > > > > +  var DEFAULT_NAME = '';
> > > > > > +
> > > > > > +  // Consts for FrameElement.
> > > > > > +  var FE_G2C_CHANNEL = '__g2c_rpc';
> > > > > > +  var FE_C2G_CHANNEL = '__c2g_rpc';
> > > > > > +
> > > > > > +  // Consts for NIX. VBScript doesn't
> > > > > > +  // allow items to start with _ for some reason,
> > > > > > +  // so we need to make these names quite unique, as
> > > > > > +  // they will go into the global namespace.
> > > > > > +  var NIX_WRAPPER = 'GRPC____NIXVBS_wrapper';
> > > > > > +  var NIX_GET_WRAPPER = 'GRPC____NIXVBS_get_wrapper';
> > > > > > +  var NIX_HANDLE_MESSAGE = 'GRPC____NIXVBS_handle_message';
> > > > > > +  var NIX_CREATE_CHANNEL = 'GRPC____NIXVBS_create_channel';
> > > > > > +  var NIX_ID_FIELD = 'GRPC____NIXVBS_container';
> > > > > > +
> > > > > > +  // JavaScript reference to the NIX VBScript wrappers.
> > > > > > +  // Gadgets will have but a single channel under
> > > > > > +  // nix_channels['..'] while containers will have a channel
> > > > > > +  // per gadget stored under the gadget's ID.
> > > > > > +  var nix_channels = {};
> > > > > > +
> > > > > >   var services = {};
> > > > > >   var iframePool = [];
> > > > > >   var relayUrl = {};
> > > > > > @@ -36,28 +60,135 @@
> > > > > >   var authToken = {};
> > > > > >   var callId = 0;
> > > > > >   var callbacks = {};
> > > > > > +  var setup = {};
> > > > > > +
> > > > > > +  var params = {};
> > > > > > +
> > > > > > +  // Load the authentication token for speaking to the container
> > > > > > +  // from the gadget's parameters, or default to '0' if not found.
> > > > > > +  if (gadgets.util) {
> > > > > > +    params = gadgets.util.getUrlParameters();
> > > > > > +  }
> > > > > >
> > > > > > -  var params = gadgets.util.getUrlParameters();
> > > > > >   authToken['..'] = params.rpctoken || params.ifpctok || 0;
> > > > > >
> > > > > > -  // Pick the most efficient RPC relay mechanism
> > > > > > -  var relayChannel = typeof document.postMessage === 'function' ?
> > > > 'dpm'
> > > > > :
> > > > > > -                     typeof window.postMessage === 'function' ?
> > > 'wpm'
> > > > :
> > > > > > +  // Pick the most efficient RPC relay mechanism:
> > > > > > +  //
> > > > > > +  // * For those browsers that support native messaging (various
> > > > > > implementations
> > > > > > +  //   of the HTML5 postMessage method), use that. Officially
> > > defined
> > > > at
> > > > > > +  //
> > > > > >
> > > http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html
> > > > .
> > > > > > +  //
> > > > > > +  //   postMessage is a native implementation of XDC. A page
> > > registers
> > > > > > that
> > > > > > +  //   it would like to receive messages by listening the the
> > > > "message"
> > > > > > event
> > > > > > +  //   on the window (document in DPM) object. In turn, another
> > page
> > > > can
> > > > > > +  //   raise that event by calling window.postMessage
> > > > > > (document.postMessage
> > > > > > +  //   in DPM) with a string representing the message and a string
> > > > > > +  //   indicating on which domain the receiving page must be to
> > > > receive
> > > > > > +  //   the message. The target page will then have its "message"
> > > event
> > > > > > raised
> > > > > > +  //   if the domain matches and can, in turn, check the origin of
> > > the
> > > > > > message
> > > > > > +  //   and process the data contained within.
> > > > > > +  //
> > > > > > +  //     wpm: postMessage on the window object.
> > > > > > +  //        - Internet Explorer 8+
> > > > > > +  //        - Safari (latest nightlies as of 26/6/2008)
> > > > > > +  //        - Firefox 3+
> > > > > > +  //        - Opera 9+
> > > > > > +  //
> > > > > > +  //     dpm: postMessage on the document object.
> > > > > > +  //        - Opera 8+
> > > > > > +  //
> > > > > > +  // * For Internet Explorer before version 8, the security model
> > > > allows
> > > > > a
> > > > > > +  //   child to set the value of the "opener" property on the
> > parent
> > > > > > window
> > > > > > +  //   and vice versa. This method is dubbed "Native IE XDC"
> > (NIX).
> > > > > > +  //
> > > > > > +  //   This method works by placing a handler object in the
> > "opener"
> > > > > > property
> > > > > > +  //   of a gadget when the container sets up the authentication
> > > > > > information
> > > > > > +  //   for that gadget (by calling setAuthToken(...)). At that
> > > point,
> > > > a
> > > > > > NIX
> > > > > > +  //   wrapper is created and placed into the gadget by calling
> > > > > > +  //   theframe.contentWindow.opener = wrapper. Note that as a
> > > result,
> > > > > NIX
> > > > > > can
> > > > > > +  //   only be used by a container to call a particular gadget
> > > *after*
> > > > > > that
> > > > > > +  //   gadget has called the container at least once via NIX.
> > > > > > +  //
> > > > > > +  //   The NIX wrappers in this RPC implementation are instances
> > of
> > > a
> > > > > > VBScript
> > > > > > +  //   class that is created when this implementation loads. The
> > > > reason
> > > > > > for
> > > > > > +  //   using a VBScript class stems from the fact that any object
> > > can
> > > > be
> > > > > > passed
> > > > > > +  //   into the opener property.
> > > > > > +  //   While this is a good thing, as it lets us pass functions
> > and
> > > > > setup
> > > > > > a true
> > > > > > +  //   bidirectional channel via callbacks, it opens a potential
> > > > > security
> > > > > > hole
> > > > > > +  //   by which the other page can get ahold of the "window" or
> > > > > "document"
> > > > > > +  //   objects in the parent page and in turn wreak havok. This is
> > > due
> > > > > to
> > > > > > the
> > > > > > +  //   fact that any JS object useful for establishing such a
> > > > > > bidirectional
> > > > > > +  //   channel (such as a function) can be used to access a
> > function
> > > > > > +  //   (eg. obj.toString, or a function itself) created in a
> > > specific
> > > > > > context,
> > > > > > +  //   in particular the global context of the sender. Suppose
> > > > container
> > > > > > +  //   domain C passes object obj to gadget on domain G. Then the
> > > > gadget
> > > > > > can
> > > > > > +  //   access C's global context using:
> > > > > > +  //   var parentWindow = (new obj.toString.constructor("return
> > > > > > window;"))();
> > > > > > +  //   Nulling out all of obj's properties doesn't fix this, since
> > > IE
> > > > > > helpfully
> > > > > > +  //   restores them to their original values if you do something
> > > > like:
> > > > > > +  //   delete obj.toString; delete obj.toString;
> > > > > > +  //   Thus, we wrap the necessary functions and information
> > inside
> > > a
> > > > > > VBScript
> > > > > > +  //   object. VBScript objects in IE, like DOM objects, are in
> > fact
> > > > COM
> > > > > > +  //   wrappers when used in JavaScript, so we can safely pass
> > them
> > > > > around
> > > > > > +  //   without worrying about a breach of context while at the
> > same
> > > > time
> > > > > > +  //   allowing them to act as a pass-through mechanism for
> > > > information
> > > > > > +  //   and function calls. The implementation details of this
> > > VBScript
> > > > > > wrapper
> > > > > > +  //   can be found in the setupChannel() method below.
> > > > > > +  //
> > > > > > +  //     nix: Internet Explorer-specific window.opener trick.
> > > > > > +  //       - Internet Explorer 6
> > > > > > +  //       - Internet Explorer 7
> > > > > > +  //
> > > > > > +  // * For Gecko-based browsers, the security model allows a child
> > > to
> > > > > call
> > > > > > a
> > > > > > +  //   function on the frameElement of the iframe, even if the
> > child
> > > > is
> > > > > in
> > > > > > +  //   a different domain. This method is dubbed "frameElement"
> > > (fe).
> > > > > > +  //
> > > > > > +  //   The ability to add and call such functions on the
> > > frameElement
> > > > > > allows
> > > > > > +  //   a bidirectional channel to be setup via the adding of
> > simple
> > > > > > function
> > > > > > +  //   references on the frameElement object itself. In this
> > > > > > implementation,
> > > > > > +  //   when the container sets up the authentication information
> > for
> > > > > that
> > > > > > gadget
> > > > > > +  //   (by calling setAuth(...)) it as well adds a special
> > function
> > > on
> > > > > the
> > > > > > +  //   gadget's iframe. This function can then be used by the
> > gadget
> > > > to
> > > > > > send
> > > > > > +  //   messages to the container. In turn, when the gadget tries
> > to
> > > > send
> > > > > a
> > > > > > +  //   message, it checks to see if this function has its own
> > > function
> > > > > > stored
> > > > > > +  //   that can be used by the container to call the gadget. If
> > not,
> > > > the
> > > > > > +  //   function is created and subsequently used by the container.
> > > > > > +  //   Note that as a result, FE can only be used by a container
> > to
> > > > call
> > > > > a
> > > > > > +  //   particular gadget *after* that gadget has called the
> > > container
> > > > at
> > > > > > +  //   least once via FE.
> > > > > > +  //
> > > > > > +  //     fe: Gecko-specific frameElement trick.
> > > > > > +  //        - Firefox 1+
> > > > > > +  //
> > > > > > +  // * For all others, we have a fallback mechanism known as
> > "ifpc".
> > > > > IFPC
> > > > > > +  //   exploits the fact that while same-origin policy prohibits a
> > > > frame
> > > > > > from
> > > > > > +  //   accessing members on a window not in the same domain, that
> > > > frame
> > > > > > can,
> > > > > > +  //   however, navigate the window heirarchy (via parent). This
> > is
> > > > > > exploited by
> > > > > > +  //   having a page on domain A that wants to talk to domain B
> > > create
> > > > > an
> > > > > > iframe
> > > > > > +  //   on domain B pointing to a special relay file and with a
> > > message
> > > > > > encoded
> > > > > > +  //   after the hash (#). This relay, in turn, finds the page on
> > > > domain
> > > > > > B, and
> > > > > > +  //   can call a receipt function with the message given to it.
> > The
> > > > > relay
> > > > > > URL
> > > > > > +  //   used by each caller is set via the
> > > gadgets.rpc.setRelayUrl(..)
> > > > > and
> > > > > > +  //   *must* be called before the call method is used.
> > > > > > +  //
> > > > > > +  //     ifpc: Iframe-based method, utilizing a relay page, to
> > send
> > > a
> > > > > > message.
> > > > > > +  //
> > > > > > +  var relayChannel = typeof window.postMessage === 'function' ?
> > > 'wpm'
> > > > :
> > > > > > +                    typeof document.postMessage === 'function' ?
> > > 'dpm'
> > > > :
> > > > > > +                    window.ActiveXObject ? 'nix' :
> > > > > > +                     navigator.product === 'Gecko' ? 'fe' :
> > > > > >                      'ifpc';
> > > > > > -  if (relayChannel === 'dpm' || relayChannel === 'wpm') {
> > > > > > -    window.addEventListener('message', function(packet) {
> > > > > > -      // TODO validate packet.domain for security reasons
> > > > > > -      process(gadgets.json.parse(packet.data));
> > > > > > -    }, false);
> > > > > > -  }
> > > > > >
> > > > > > -  // Default RPC handler
> > > > > > -  services[''] = function() {
> > > > > > +  // Conduct any setup necessary for the chosen channel.
> > > > > > +  setupChannel();
> > > > > > +
> > > > > > +  // Create the Default RPC handler.
> > > > > > +  services[DEFAULT_NAME] = function() {
> > > > > >     throw new Error('Unknown RPC service: ' + this.s);
> > > > > >   };
> > > > > >
> > > > > > -  // Special RPC handler for callbacks
> > > > > > -  services['__cb'] = function(callbackId, result) {
> > > > > > +  // Create a Special RPC handler for callbacks.
> > > > > > +  services[CALLBACK_NAME] = function(callbackId, result) {
> > > > > >     var callback = callbacks[callbackId];
> > > > > >     if (callback) {
> > > > > >       delete callbacks[callbackId];
> > > > > > @@ -66,6 +197,147 @@
> > > > > >   };
> > > > > >
> > > > > >   /**
> > > > > > +   * Conducts any initial global work necessary to setup the
> > > > > > +   * channel type chosen.
> > > > > > +   */
> > > > > > +  function setupChannel() {
> > > > > > +    // If the channel type is one of the native
> > > > > > +    // postMessage based ones, setup the handler to receive
> > > > > > +    // messages.
> > > > > > +    if (relayChannel === 'dpm' || relayChannel === 'wpm') {
> > > > > > +      window.addEventListener('message', function(packet) {
> > > > > > +        // TODO validate packet.domain for security reasons
> > > > > > +        process(gadgets.json.parse(packet.data));
> > > > > > +      }, false);
> > > > > > +    }
> > > > > > +
> > > > > > +    // If the channel type is NIX, we need to ensure the
> > > > > > +    // VBScript wrapper code is in the page and that the
> > > > > > +    // global Javascript handlers have been set.
> > > > > > +    if (relayChannel === 'nix') {
> > > > > > +      // VBScript methods return a type of 'unknown' when
> > > > > > +      // checked via the typeof operator in IE. Fortunately
> > > > > > +      // for us, this only applies to COM objects, so we
> > > > > > +      // won't see this for a real Javascript object.
> > > > > > +      if (typeof window[NIX_GET_WRAPPER] !== 'unknown') {
> > > > > > +        window[NIX_HANDLE_MESSAGE] = function(data) {
> > > > > > +          process(gadgets.json.parse(data));
> > > > > > +        };
> > > > > > +
> > > > > > +        window[NIX_CREATE_CHANNEL] = function(name, channel,
> > token)
> > > {
> > > > > > +          // Verify the authentication token of the gadget trying
> > > > > > +          // to create a channel for us.
> > > > > > +          if (authToken[name] == token) {
> > > > > > +            nix_channels[name] = channel;
> > > > > > +          }
> > > > > > +        };
> > > > > > +
> > > > > > +        // Inject the VBScript code needed.
> > > > > > +        var vbscript =
> > > > > > +          '<scr' + 'ipt language="VBScript">'
> > > > > > +          // We create a class to act as a wrapper for
> > > > > > +          // a Javascript call, to prevent a break in of
> > > > > > +          // the context.
> > > > > > +          + 'Class ' + NIX_WRAPPER + '\n '
> > > > > > +
> > > > > > +          // An internal member for keeping track of the
> > > > > > +          // name of the document (container or gadget)
> > > > > > +          // for which this wrapper is intended. For
> > > > > > +          // those wrappers created by gadgets, this is not
> > > > > > +          // used (although it is set to "..")
> > > > > > +          + 'Private m_Intended\n'
> > > > > > +
> > > > > > +          // Method for internally setting the value
> > > > > > +          // of the m_Intended property.
> > > > > > +          + 'Public Sub SetIntendedName(name)\n '
> > > > > > +          + 'm_Intended = name\n'
> > > > > > +          + 'End Sub\n'
> > > > > > +
> > > > > > +          // A wrapper method which actually causes a
> > > > > > +          // message to be sent to the other context.
> > > > > > +          + 'Public Sub SendMessage(data)\n '
> > > > > > +          + NIX_HANDLE_MESSAGE + '(data)\n'
> > > > > > +          + 'End Sub\n'
> > > > > > +
> > > > > > +          // Method for setting up the container->gadget
> > > > > > +          // channel. Not strictly needed in the gadget's
> > > > > > +          // wrapper, but no reason to get rid of it. Note here
> > > > > > +          // that we pass the intended name to the
> > > NIX_CREATE_CHANNEL
> > > > > > +          // method so that it can save the channel in the proper
> > > > place
> > > > > > +          // *and* verify the channel via the authentication token
> > > > > passed
> > > > > > +          // here.
> > > > > > +          + 'Public Sub CreateChannel(channel, auth)\n '
> > > > > > +          + 'Call ' + NIX_CREATE_CHANNEL + '(m_Intended, channel,
> > > > > auth)\n'
> > > > > > +          + 'End Sub\n'
> > > > > > +
> > > > > > +          // An empty field with a unique identifier to
> > > > > > +          // prevent the code from confusing this wrapper
> > > > > > +          // with a run-of-the-mill value found in window.opener.
> > > > > > +          + 'Public Sub ' + NIX_ID_FIELD + '()\n '
> > > > > > +          + 'End Sub\n'
> > > > > > +          + 'End Class\n '
> > > > > > +
> > > > > > +          // Function to get a reference to the wrapper.
> > > > > > +          + 'Function ' + NIX_GET_WRAPPER + '(name)\n'
> > > > > > +          + 'Dim wrap\n'
> > > > > > +          + 'Set wrap = New ' + NIX_WRAPPER + '\n'
> > > > > > +          + 'wrap.SetIntendedName name\n'
> > > > > > +          + 'Set ' + NIX_GET_WRAPPER + ' = wrap\n'
> > > > > > +          + 'End Function'
> > > > > > +          + '</scr' + 'ipt>'
> > > > > > +
> > > > > > +          // Resets the "default" scripting language in IE back
> > > > > > +          // to Javascript. This is needed or else any script
> > > > > > +          // tags without a proper Language="..." will be treated
> > as
> > > > > > +          // VBScript once this code is added to the document.
> > > > > > +          + '<scri' + 'pt language="JScript"></scr' + 'ipt>';
> > > > > > +
> > > > > > +        // Note that this code can only be run once the document
> > has
> > > > > > +        // fully loaded.
> > > > > > +        // TODO: Perhaps add some sort of check here for this?
> > > > > > +        document.write(vbscript);
> > > > > > +      }
> > > > > > +    }
> > > > > > +  }
> > > > > > +
> > > > > > +  /**
> > > > > > +   * Conducts any frame-specific work necessary to setup
> > > > > > +   * the channel type chosen. This method is called when
> > > > > > +   * the container page first registers the gadget in the
> > > > > > +   * RPC mechanism. Gadgets, in turn, will complete the setup
> > > > > > +   * of the channel once they send their first messages.
> > > > > > +   */
> > > > > > +  function setupFrame(frameId) {
> > > > > > +    if (setup[frameId]) {
> > > > > > +      return;
> > > > > > +    }
> > > > > > +
> > > > > > +    if (relayChannel === 'fe') {
> > > > > > +      try {
> > > > > > +        var frame = document.getElementById(frameId);
> > > > > > +        frame[FE_G2C_CHANNEL] = function(args) {
> > > > > > +          process(gadgets.json.parse(args));
> > > > > > +        };
> > > > > > +      } catch (e) {
> > > > > > +        // Something went wrong. System will fallback to
> > > > > > +        // IFPC.
> > > > > > +      }
> > > > > > +    }
> > > > > > +
> > > > > > +    if (relayChannel === 'nix') {
> > > > > > +      try {
> > > > > > +        var frame = document.getElementById(frameId);
> > > > > > +        frame.contentWindow.opener =
> > > window[NIX_GET_WRAPPER](frameId);
> > > > > > +      } catch (e) {
> > > > > > +        // Something went wrong. System will fallback to
> > > > > > +        // IFPC.
> > > > > > +      }
> > > > > > +    }
> > > > > > +
> > > > > > +    setup[frameId] = true;
> > > > > > +  }
> > > > > > +
> > > > > > +  /**
> > > > > >    * Encodes arguments for the legacy IFPC wire format.
> > > > > >    *
> > > > > >    * @param {Object} args
> > > > > > @@ -86,8 +358,17 @@
> > > > > >    * @private
> > > > > >    */
> > > > > >   function process(rpc) {
> > > > > > +    //
> > > > > > +    // RPC object contents:
> > > > > > +    //   s: Service Name
> > > > > > +    //   f: From
> > > > > > +    //   c: The callback ID or 0 if none.
> > > > > > +    //   a: The arguments for this RPC call.
> > > > > > +    //   t: The authentication token.
> > > > > > +    //
> > > > > >     if (rpc && typeof rpc.s === 'string' && typeof rpc.f ===
> > 'string'
> > > > &&
> > > > > >         rpc.a instanceof Array) {
> > > > > > +
> > > > > >       // Validate auth token.
> > > > > >       if (authToken[rpc.f]) {
> > > > > >         // We allow type coercion here because all the url params
> > are
> > > > > > strings.
> > > > > > @@ -96,43 +377,158 @@
> > > > > >         }
> > > > > >       }
> > > > > >
> > > > > > -      // The Gecko engine used by FireFox etc. allows an IFrame to
> > > > > > directly call
> > > > > > -      // methods on the frameElement property added by the
> > container
> > > > > page
> > > > > > even
> > > > > > -      // if their domains don't match.
> > > > > > -      // Here we try to set up a relay channel using the
> > > frameElement
> > > > > > technique
> > > > > > -      // to greatly reduce the latency of cross-domain calls if
> > the
> > > > > > postMessage
> > > > > > -      // method is not supported.
> > > > > > -      if (relayChannel === 'ifpc') {
> > > > > > -        if (rpc.f === '..') {
> > > > > > -          // Container-to-gadget call
> > > > > > -          try {
> > > > > > -            var fel = window.frameElement;
> > > > > > -            if (typeof fel.__g2c_rpc === 'function' &&
> > > > > > -                typeof fel.__g2c_rpc.__c2g_rpc != 'function') {
> > > > > > -              fel.__g2c_rpc.__c2g_rpc = function(args) {
> > > > > > -                process(gadgets.json.parse(args));
> > > > > > -              };
> > > > > > -            }
> > > > > > -          } catch (e) {
> > > > > > -          }
> > > > > > -        } else {
> > > > > > -          // Gadget-to-container call
> > > > > > -          var iframe = document.getElementById(rpc.f);
> > > > > > -          if (iframe && typeof iframe.__g2c_rpc != 'function') {
> > > > > > -            iframe.__g2c_rpc = function(args) {
> > > > > > +      // Call the requested RPC service.
> > > > > > +      var result = (services[rpc.s] ||
> > > > > > +                    services[DEFAULT_NAME]).apply(rpc, rpc.a);
> > > > > > +
> > > > > > +      // If there is a callback for this service, initiate it as
> > > well.
> > > > > > +      if (rpc.c) {
> > > > > > +        gadgets.rpc.call(rpc.f, CALLBACK_NAME, null, rpc.c,
> > result);
> > > > > > +      }
> > > > > > +    }
> > > > > > +  }
> > > > > > +
> > > > > > +  /**
> > > > > > +   * Attempts to conduct an RPC call to the specified
> > > > > > +   * target with the specified data via the NIX
> > > > > > +   * method. If this method fails, the system attempts again
> > > > > > +   * using the known default of IFPC.
> > > > > > +   *
> > > > > > +   * @param {String} targetId Module Id of the RPC service
> > provider.
> > > > > > +   * @param {String} from Module Id of the calling provider.
> > > > > > +   * @param {Object} rpcData The RPC data for this call.
> > > > > > +   */
> > > > > > +  function callNIX(targetId, from, rpcData) {
> > > > > > +    try {
> > > > > > +      if (from != '..') {
> > > > > > +        // Call from gadget to the container.
> > > > > > +        var handler = nix_channels['..'];
> > > > > > +
> > > > > > +        // If the gadget has yet to retrieve a reference to
> > > > > > +        // the NIX handler, try to do so now. We don't do a
> > > > > > +        // typeof(window.opener[NIX_ID_FIELD]) check here
> > > > > > +        // because it means accessing that field on the COM
> > object,
> > > > > which,
> > > > > > +        // being an internal function reference, is not allowed.
> > > > > > +        // "in" works because it merely checks for the prescence
> > of
> > > > > > +        // the key, rather than actually accessing the object's
> > > > > property.
> > > > > > +        if (!handler && window.opener && NIX_ID_FIELD in
> > > > window.opener)
> > > > > {
> > > > > > +          handler = nix_channels['..'] = window.opener;
> > > > > > +
> > > > > > +          // Create the channel to the parent/container. We pass
> > > both
> > > > > our
> > > > > > +          // own wrapper and our authentication token for
> > > > verification.
> > > > > > +          handler.CreateChannel(window[NIX_GET_WRAPPER]('..'),
> > > > > > authToken['..']);
> > > > > > +        }
> > > > > > +
> > > > > > +        // If we have a handler, call it.
> > > > > > +        if (handler) {
> > > > > > +          handler.SendMessage(rpcData);
> > > > > > +          return;
> > > > > > +        }
> > > > > > +      } else {
> > > > > > +        // Call from container to a gadget[targetId].
> > > > > > +
> > > > > > +        // If we have a handler, call it.
> > > > > > +        if (nix_channels[targetId]) {
> > > > > > +          nix_channels[targetId].SendMessage(rpcData);
> > > > > > +          return;
> > > > > > +        }
> > > > > > +      }
> > > > > > +    } catch (e) {
> > > > > > +    }
> > > > > > +
> > > > > > +    // If we have reached this point, something has failed
> > > > > > +    // with the NIX method, so we default to using
> > > > > > +    // IFPC for this call.
> > > > > > +    callIFPC(targetId, from, rpcData);
> > > > > > +  }
> > > > > > +
> > > > > > +  /**
> > > > > > +   * Attempts to conduct an RPC call to the specified
> > > > > > +   * target with the specified data via the FrameElement
> > > > > > +   * method. If this method fails, the system attempts again
> > > > > > +   * using the known default of IFPC.
> > > > > > +   *
> > > > > > +   * @param {String} targetId Module Id of the RPC service
> > provider.
> > > > > > +   * @param {String} from Module Id of the calling provider.
> > > > > > +   * @param {Object} rpcData The RPC data for this call.
> > > > > > +   */
> > > > > > +  function callFrameElement(targetId, from, rpcData) {
> > > > > > +    try {
> > > > > > +      if (from != '..') {
> > > > > > +        // Call from gadget to the container.
> > > > > > +        var fe = window.frameElement;
> > > > > > +
> > > > > > +        if (typeof fe[FE_G2C_CHANNEL] === 'function') {
> > > > > > +          // Complete the setup of the FE channel if need be.
> > > > > > +          if (typeof fe[FE_G2C_CHANNEL][FE_C2G_CHANNEL] !==
> > > > 'function')
> > > > > {
> > > > > > +            fe[FE_G2C_CHANNEL][FE_C2G_CHANNEL] = function(args) {
> > > > > >               process(gadgets.json.parse(args));
> > > > > >             };
> > > > > >           }
> > > > > > +
> > > > > > +          // Conduct the RPC call.
> > > > > > +          fe[FE_G2C_CHANNEL](rpcData);
> > > > > > +          return;
> > > > > >         }
> > > > > > -      }
> > > > > > +      } else {
> > > > > > +        // Call from container to gadget[targetId].
> > > > > > +        var frame = document.getElementById(targetId);
> > > > > >
> > > > > > -      var result = (services[rpc.s] || services['']).apply(rpc,
> > > > rpc.a);
> > > > > > -      if (rpc.c) {
> > > > > > -        gadgets.rpc.call(rpc.f, '__cb', null, rpc.c, result);
> > > > > > +        if (typeof frame[FE_G2C_CHANNEL] === 'function' &&
> > > > > > +            typeof frame[FE_G2C_CHANNEL][FE_C2G_CHANNEL] ===
> > > > 'function')
> > > > > {
> > > > > > +
> > > > > > +          // Conduct the RPC call.
> > > > > > +          frame[FE_G2C_CHANNEL][FE_C2G_CHANNEL](rpcData);
> > > > > > +          return;
> > > > > > +        }
> > > > > >       }
> > > > > > +    } catch (e) {
> > > > > > +    }
> > > > > > +
> > > > > > +    // If we have reached this point, something has failed
> > > > > > +    // with the FrameElement method, so we default to using
> > > > > > +    // IFPC for this call.
> > > > > > +    callIFPC(targetId, from, rpcData);
> > > > > > +  }
> > > > > > +
> > > > > > +  /**
> > > > > > +   * Conducts an RPC call to the specified
> > > > > > +   * target with the specified data via the IFPC
> > > > > > +   * method.
> > > > > > +   *
> > > > > > +   * @param {String} targetId Module Id of the RPC service
> > provider.
> > > > > > +   * @param {String} from Module Id of the calling provider.
> > > > > > +   * @param {Object} rpcData The RPC data for this call.
> > > > > > +   */
> > > > > > +  function callIFPC(targetId, from, rpcData) {
> > > > > > +    // Retrieve the relay file used by IFPC. Note that
> > > > > > +    // this must be set before the call, and so we conduct
> > > > > > +    // an extra check to ensure it is not blank.
> > > > > > +    var relay = gadgets.rpc.getRelayUrl(targetId);
> > > > > > +
> > > > > > +    if (!relay) {
> > > > > > +      throw new Error('No relay file assigned for IFPC');
> > > > > >     }
> > > > > > +
> > > > > > +    // The RPC mechanism supports two formats for IFPC (legacy and
> > > > > > current).
> > > > > > +    var src = null;
> > > > > > +    if (useLegacyProtocol[targetId]) {
> > > > > > +      // Format:
> > > > #iframe_id&callId&num_packets&packet_num&block_of_data
> > > > > > +      src = [relay, '#', encodeLegacyData([from, callId, 1, 0,
> > > > > > +             encodeLegacyData([from, serviceName, '', '',
> > > > from].concat(
> > > > > > +               Array.prototype.slice.call(arguments,
> > > 3)))])].join('');
> > > > > > +    } else {
> > > > > > +      // Format: #targetId & sourceId@callId & packetNum &
> > packetId
> > > &
> > > > > > packetData
> > > > > > +      src = [relay, '#', targetId, '&', from, '@', callId,
> > > > > > +             '&1&0&', encodeURIComponent(rpcData)].join('');
> > > > > > +    }
> > > > > > +
> > > > > > +    // Conduct the IFPC call by creating the Iframe with
> > > > > > +    // the relay URL and appended message.
> > > > > > +    emitInvisibleIframe(src);
> > > > > >   }
> > > > > >
> > > > > > +
> > > > > >   /**
> > > > > >    * Helper function to emit an invisible IFrame.
> > > > > >    * @param {String} src SRC attribute of the IFrame to emit.
> > > > > > @@ -221,6 +617,15 @@
> > > > > >      * @member gadgets.rpc
> > > > > >      */
> > > > > >     register: function(serviceName, handler) {
> > > > > > +      if (serviceName == CALLBACK_NAME) {
> > > > > > +        throw new Error("Cannot overwrite callback service");
> > > > > > +      }
> > > > > > +
> > > > > > +      if (serviceName == DEFAULT_NAME) {
> > > > > > +        throw new Error("Cannot overwrite default service:"
> > > > > > +                        + " use registerDefault");
> > > > > > +      }
> > > > > > +
> > > > > >       services[serviceName] = handler;
> > > > > >     },
> > > > > >
> > > > > > @@ -231,6 +636,15 @@
> > > > > >      * @member gadgets.rpc
> > > > > >      */
> > > > > >     unregister: function(serviceName) {
> > > > > > +      if (serviceName == CALLBACK_NAME) {
> > > > > > +        throw new Error("Cannot delete callback service");
> > > > > > +      }
> > > > > > +
> > > > > > +      if (serviceName == DEFAULT_NAME) {
> > > > > > +        throw new Error("Cannot delete default service:"
> > > > > > +                        + " use unregisterDefault");
> > > > > > +      }
> > > > > > +
> > > > > >       delete services[serviceName];
> > > > > >     },
> > > > > >
> > > > > > @@ -272,12 +686,14 @@
> > > > > >       if (callback) {
> > > > > >         callbacks[callId] = callback;
> > > > > >       }
> > > > > > -      var from;
> > > > > > +
> > > > > > +      // Default to the container calling.
> > > > > > +      var from = '..';
> > > > > > +
> > > > > >       if (targetId === '..') {
> > > > > >         from = window.name;
> > > > > > -      } else {
> > > > > > -        from = '..';
> > > > > >       }
> > > > > > +
> > > > > >       // Not used by legacy, create it anyway...
> > > > > >       var rpcData = gadgets.json.stringify({
> > > > > >         s: serviceName,
> > > > > > @@ -287,53 +703,37 @@
> > > > > >         t: authToken[targetId]
> > > > > >       });
> > > > > >
> > > > > > +      var channelType = relayChannel;
> > > > > > +
> > > > > > +      // If we are told to use the legacy format, then we must
> > > > > > +      // default to IFPC.
> > > > > >       if (useLegacyProtocol[targetId]) {
> > > > > > -        relayChannel = 'ifpc';
> > > > > > +        channelType = 'ifpc';
> > > > > >       }
> > > > > >
> > > > > > -      switch (relayChannel) {
> > > > > > -      case 'dpm': // use document.postMessage
> > > > > > -        var targetDoc = targetId === '..' ? parent.document :
> > > > > > -
> > >  frames[targetId].document;
> > > > > > -        targetDoc.postMessage(rpcData);
> > > > > > -        break;
> > > > > > -      case 'wpm': // use window.postMessage
> > > > > > -        var targetWin = targetId === '..' ? parent :
> > > frames[targetId];
> > > > > > -        targetWin.postMessage(rpcData, "*");
> > > > > > -        break;
> > > > > > -      default: // use 'ifpc' as a fallback mechanism
> > > > > > -        var relay = gadgets.rpc.getRelayUrl(targetId);
> > > > > > -        // TODO split message if too long
> > > > > > -        var src;
> > > > > > -        if (useLegacyProtocol[targetId]) {
> > > > > > -          //
> > #iframe_id&callId&num_packets&packet_num&block_of_data
> > > > > > -          src = [relay, '#', encodeLegacyData([from, callId, 1, 0,
> > > > > > -                 encodeLegacyData([from, serviceName, '', '',
> > > > > > from].concat(
> > > > > > -                 Array.prototype.slice.call(arguments,
> > > > 3)))])].join('');
> > > > > > -        } else {
> > > > > > -          // Try the frameElement channel if available
> > > > > > -          try {
> > > > > > -            if (from === '..') {
> > > > > > -              // Container-to-gadget
> > > > > > -              var iframe = document.getElementById(targetId);
> > > > > > -              if (typeof iframe.__g2c_rpc.__c2g_rpc ===
> > 'function')
> > > {
> > > > > > -                iframe.__g2c_rpc.__c2g_rpc(rpcData);
> > > > > > -                return;
> > > > > > -              }
> > > > > > -            } else {
> > > > > > -              // Gadget-to-container
> > > > > > -              if (typeof window.frameElement.__g2c_rpc ===
> > > 'function')
> > > > {
> > > > > > -                window.frameElement.__g2c_rpc(rpcData);
> > > > > > -                return;
> > > > > > -              }
> > > > > > -            }
> > > > > > -          } catch (e) {
> > > > > > -          }
> > > > > > -          // # targetId & sourceId@callId & packetNum & packetId
> > &
> > > > > > packetData
> > > > > > -          src = [relay, '#', targetId, '&', from, '@', callId,
> > > > > > -                 '&1&0&', encodeURIComponent(rpcData)].join('');
> > > > > > -        }
> > > > > > -        emitInvisibleIframe(src);
> > > > > > +      switch (channelType) {
> > > > > > +        case 'dpm': // use document.postMessage.
> > > > > > +          var targetDoc = targetId === '..' ? parent.document :
> > > > > > +
> > > >  frames[targetId].document;
> > > > > > +          targetDoc.postMessage(rpcData);
> > > > > > +          break;
> > > > > > +
> > > > > > +        case 'wpm': // use window.postMessage.
> > > > > > +          var targetWin = targetId === '..' ? parent :
> > > > frames[targetId];
> > > > > > +          targetWin.postMessage(rpcData, "*");
> > > > > > +          break;
> > > > > > +
> > > > > > +        case 'nix': // use NIX.
> > > > > > +          callNIX(targetId, from, rpcData);
> > > > > > +          break;
> > > > > > +
> > > > > > +        case 'fe': // use FrameElement.
> > > > > > +          callFrameElement(targetId, from, rpcData);
> > > > > > +          break;
> > > > > > +
> > > > > > +        default: // use 'ifpc' as a fallback mechanism.
> > > > > > +          callIFPC(targetId, from, rpcData);
> > > > > > +          break;
> > > > > >       }
> > > > > >     },
> > > > > >
> > > > > > @@ -372,15 +772,13 @@
> > > > > >      */
> > > > > >     setAuthToken: function(targetId, token) {
> > > > > >       authToken[targetId] = token;
> > > > > > +      setupFrame(targetId);
> > > > > >     },
> > > > > >
> > > > > >     /**
> > > > > >      * Gets the RPC relay mechanism.
> > > > > > -     * @return {String} RPC relay mechanism. Supported types:
> > > > > > -     *                  'wpm' - Use window.postMessage (defined by
> > > > > HTML5)
> > > > > > -     *                  'dpm' - Use document.postMessage (defined
> > by
> > > > an
> > > > > > early
> > > > > > -     *                          draft of HTML5 and implemented by
> > > > Opera)
> > > > > > -     *                  'ifpc' - Use invisible IFrames
> > > > > > +     * @return {String} RPC relay mechanism. See above for
> > > > > > +     *   a list of supported types.
> > > > > >      *
> > > > > >      * @member gadgets.rpc
> > > > > >      */
> > > > > >
> > > > > > Modified:
> > > > > >
> > > > >
> > > >
> > >
> > incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > > > > URL:
> > > > > >
> > > > >
> > > >
> > >
> > http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java?rev=672393&r1=672392&r2=672393&view=diff
> > > > > >
> > > > > >
> > > > >
> > > >
> > >
> > ==============================================================================
> > > > > > ---
> > > > > >
> > > > >
> > > >
> > >
> > incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > > > > (original)
> > > > > > +++
> > > > > >
> > > > >
> > > >
> > >
> > incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > > > > Fri Jun 27 14:01:20 2008
> > > > > > @@ -221,7 +221,8 @@
> > > > > >           "a {color:#0000cc;}a:visited {color:#551a8b;}" +
> > > > > >           "a:active {color:#ff0000;}" +
> > > > > >           "body{margin: 0px;padding: 0px;background-color:white;}"
> > +
> > > > > > -          "</style></head>";
> > > > > > +          "</style><base>" + gadget.getSpec().getUrl().toString()
> > +
> > > > > > +          "</base></head>";
> > > > > >     markup.append(boilerPlate);
> > > > > >     LocaleSpec localeSpec =
> > > > gadget.getSpec().getModulePrefs().getLocale(
> > > > > >         gadget.getContext().getLocale());
> > > > > >
> > > > > > Added:
> > > > > incubator/shindig/trunk/javascript/container/rpctest_container.html
> > > > > > URL:
> > > > > >
> > > > >
> > > >
> > >
> > http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_container.html?rev=672393&view=auto
> > > > > >
> > > > > >
> > > > >
> > > >
> > >
> > ==============================================================================
> > > > > > ---
> > > incubator/shindig/trunk/javascript/container/rpctest_container.html
> > > > > > (added)
> > > > > > +++
> > > incubator/shindig/trunk/javascript/container/rpctest_container.html
> > > > > Fri
> > > > > > Jun 27 14:01:20 2008
> > > > > > @@ -0,0 +1,90 @@
> > > > > > +<!--
> > > > > > +  Simple page for testing gadgets.rpc performance.
> > > > > > +  Allows you to add a simulated "gadget" (in this case just a
> > static
> > > > > > +  HTML page which loads gadgets.rpc also), and pass some
> > > > > > +  specified number of random messages of specified size to
> > > > > > +  and from it.
> > > > > > +
> > > > > > +  A simple recommended setup is to start up two instances of
> > > > > > +  the Shindig Gadgets Server on two separate ports to test
> > > > > > +  "real" cross-domain communication, since port is factored
> > > > > > +  into the same-domain policy enforced by browsers.
> > > > > > +
> > > > > > +  If your servers are on localhost:8080 and localhost:8081, then
> > > hit:
> > > > > > +
> > > > http://localhost:8080/gadgets/files/container/rpctest_container.html?
> > > > > \
> > > > > > +
> > > http://localhost:8081/gadgets/files/container/rpctest_gadget.html&\<http://localhost:8081/gadgets/files/container/rpctest_gadget.html&%5C>
> > <http://localhost:8081/gadgets/files/container/rpctest_gadget.html&%5C>
> > > > > > +
> > > > > >
> > > > >
> > > >
> > >
> > http://localhost:8081/gadgets/files/container/rpc_relay.uncompressed.html
> > > > > > +
> > > > > > +  (Note the backslashes should be removed, as they exist for
> > > > formatting
> > > > > > only.)
> > > > > > +
> > > > > > +  There are two arguments, separated by an ampersand:
> > > > > > +  1. URL of the "gadget" test page.
> > > > > > +  2. URL of the "gadget" test page's RPC relay (for browsers
> > > requiring
> > > > > > it).
> > > > > > +-->
> > > > > > +<html>
> > > > > > +  <head>
> > > > > > +    <title>gadgets.rpc Performance Tests: Container</title>
> > > > > > +    <script src="../../js/rpc.js?c=1&debug=1"></script>
> > > > > > +    <script src="rpctest_perf.js"></script>
> > > > > > +    <script>
> > > > > > +      function initTest() {
> > > > > > +        var container = document.getElementById("container");
> > > > > > +
> > > > > > +        // query string is assumed to be the "gadget" URL as arg
> > 1,
> > > > > > +        // and optionally the relay URL as arg 2
> > > > > > +        var pageArgs =
> > > window.location.search.substring(1).split('&');
> > > > > > +        var gadgetUrl = pageArgs[0];
> > > > > > +        var secret = Math.round(Math.random()*10000000);
> > > > > > +        if (pageArgs[1]) {
> > > > > > +          gadgets.rpc.setRelayUrl('gadget', pageArgs[1]);
> > > > > > +        }
> > > > > > +        var containerRelay = pageArgs[2] || '';
> > > > > > +        container.innerHTML = "<iframe id='gadget' name='gadget'
> > > > > > height=300 width=300 src='" + gadgetUrl + "?parent=" +
> > containerRelay
> > > +
> > > > > > "#rpctoken=" + secret + "'></iframe>";
> > > > > > +        gadgets.rpc.setAuthToken('gadget', secret);
> > > > > > +
> > > > > > +        initPerfTest();
> > > > > > +      };
> > > > > > +    </script>
> > > > > > +  </head>
> > > > > > +  <body onload="initTest();">
> > > > > > +    <div>gadgets.rpc Performance: Container Page</div><hr/>
> > > > > > +    <div>Test<br/>
> > > > > > +      <ul>
> > > > > > +        <li>Number of messages to send:
> > > > > > +          <select name="num_msgs" id="num_msgs">
> > > > > > +            <option value="1">1</option>
> > > > > > +            <option value="10">10</option>
> > > > > > +            <option value="100" selected>100</option>
> > > > > > +            <option value="1000">1000</option>
> > > > > > +          </select>
> > > > > > +        </li>
> > > > > > +        <li>Message size:
> > > > > > +          <select name="msg_size" id="msg_size">
> > > > > > +            <option value="10">10 B</option>
> > > > > > +            <option value="100">100 B</option>
> > > > > > +            <option value="1024" selected>1 kB</option>
> > > > > > +            <option value="10240">10 kB</option>
> > > > > > +            <option value="102400">100 kB</option>
> > > > > > +            <option value="1048576">1 MB</option>
> > > > > > +          </select>
> > > > > > +        </li>
> > > > > > +        <li>
> > > > > > +          <input type="button" value="Start The Test!"
> > > > > > onclick="runPerfTest('gadget');" />
> > > > > > +        </li>
> > > > > > +      </ul>
> > > > > > +    </div>
> > > > > > +    <div id="test_running" style="display:none;">
> > > > > > +      Running test...
> > > > > > +    </div>
> > > > > > +    <div id="results" style="display:none;">
> > > > > > +      Results: Gadget-to-Container<br/>
> > > > > > +      Messages: <span id="results_num_received"></span>, Bytes:
> > > <span
> > > > > > id="results_bytes_received"></span> <span
> > id="in_or_out"></span><br/>
> > > > > > +      Time elapsed for test run: <span
> > > > > id="results_time_used"></span><br/>
> > > > > > +      Messages/second: <span
> > id="results_msgs_per_sec"></span><br/>
> > > > > > +      Bytes/second: <span id="results_bytes_per_sec"></span>
> > > > > > +    </div>
> > > > > > +    <hr/>
> > > > > > +    <div>Gadget:</div>
> > > > > > +    <div id="container"></div>
> > > > > > +  </body>
> > > > > > +</html>
> > > > > >
> > > > > > Added:
> > > incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > > > > > URL:
> > > > > >
> > > > >
> > > >
> > >
> > http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_gadget.html?rev=672393&view=auto
> > > > > >
> > > > > >
> > > > >
> > > >
> > >
> > ==============================================================================
> > > > > > ---
> > incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > > > > > (added)
> > > > > > +++
> > incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > > > Fri
> > > > > > Jun 27 14:01:20 2008
> > > > > > @@ -0,0 +1,49 @@
> > > > > > +<html>
> > > > > > +  <head>
> > > > > > +    <title>gadgets.rpc Performance Tests: Gadget</title>
> > > > > > +    <script src="../../js/rpc.js?c=1&debug=1"></script>
> > > > > > +    <script src="rpctest_perf.js"></script>
> > > > > > +    <script>
> > > > > > +      var containerRelay = window.location.search.substring(1);
> > > > > > +      gadgets.rpc.setRelayUrl(null, containerRelay);
> > > > > > +    </script>
> > > > > > +  </head>
> > > > > > +  <body onload="initPerfTest();">
> > > > > > +    <div>gadgets.rpc Performance: "Gadget" page</div><hr/>
> > > > > > +    <div>Test<br/>
> > > > > > +      <ul>
> > > > > > +        <li>Number of messages to send:
> > > > > > +          <select name="num_msgs" id="num_msgs">
> > > > > > +            <option value="1">1</option>
> > > > > > +            <option value="10">10</option>
> > > > > > +            <option value="100" selected>100</option>
> > > > > > +            <option value="1000">1000</option>
> > > > > > +          </select>
> > > > > > +        </li>
> > > > > > +        <li>Message size:
> > > > > > +          <select name="msg_size" id="msg_size">
> > > > > > +            <option value="10">10 B</option>
> > > > > > +            <option value="100">100 B</option>
> > > > > > +            <option value="1024" selected>1 kB</option>
> > > > > > +            <option value="10240">10 kB</option>
> > > > > > +            <option value="102400">100 kB</option>
> > > > > > +            <option value="1048576">1 MB</option>
> > > > > > +          </select>
> > > > > > +        </li>
> > > > > > +        <li>
> > > > > > +          <input type="button" value="Start The Test!"
> > > > > > onclick="runPerfTest();" />
> > > > > > +        </li>
> > > > > > +      </ul>
> > > > > > +    </div>
> > > > > > +    <div id="test_running" style="display:none;">
> > > > > > +      Running test...
> > > > > > +    </div>
> > > > > > +    <div id="results" style="display:none;">
> > > > > > +      Results: Gadget-to-Container<br/>
> > > > > > +      Messages: <span id="results_num_received"></span>, Bytes:
> > > <span
> > > > > > id="results_bytes_received"></span> <span
> > id="in_or_out"></span><br/>
> > > > > > +      Time elapsed for test run: <span
> > > > > id="results_time_used"></span><br/>
> > > > > > +      Messages/second: <span
> > id="results_msgs_per_sec"></span><br/>
> > > > > > +      Bytes/second: <span id="results_bytes_per_sec"></span>
> > > > > > +    </div>
> > > > > > +  </body>
> > > > > > +</html>
> > > > > >
> > > > > > Added: incubator/shindig/trunk/javascript/container/rpctest_perf.js
> > > > > > URL:
> > > > > >
> > > > >
> > > >
> > >
> > http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_perf.js?rev=672393&view=auto
> > > > > >
> > > > > >
> > > > >
> > > >
> > >
> > ==============================================================================
> > > > > > --- incubator/shindig/trunk/javascript/container/rpctest_perf.js
> > > > (added)
> > > > > > +++ incubator/shindig/trunk/javascript/container/rpctest_perf.js
> > Fri
> > > > Jun
> > > > > 27
> > > > > > 14:01:20 2008
> > > > > > @@ -0,0 +1,93 @@
> > > > > > +var perfStats = null;
> > > > > > +var currentRun = {};
> > > > > > +
> > > > > > +function perfService(message) {
> > > > > > +  if (perfStats.numResults++ === 0) {
> > > > > > +    perfStats.firstMsg = message; // stored since it has "real"
> > > start
> > > > > time
> > > > > > +  }
> > > > > > +  perfStats.bytesReceived += message.length;
> > > > > > +};
> > > > > > +
> > > > > > +function clearPerfStats(inOrOut) {
> > > > > > +  perfStats = {
> > > > > > +    numResults: 0,
> > > > > > +    bytesReceived: 0,
> > > > > > +    firstMsg: null
> > > > > > +  };
> > > > > > +
> > > > > > +  document.getElementById("in_or_out").innerHTML = inOrOut;
> > > > > > +
> > > > > > +  // hide results fields
> > > > > > +  document.getElementById("results").style.display = "none";
> > > > > > +};
> > > > > > +
> > > > > > +function completePerfStats() {
> > > > > > +  perfStats.timeEnded = new Date().getTime();
> > > > > > +
> > > > > > +  // get time started from the first sent message
> > > > > > +  perfStats.timeStarted = perfStats.firstMsg.substr(0,
> > > > > > perfStats.firstMsg.indexOf(':'));
> > > > > > +
> > > > > > +  var timeUsedMs = perfStats.timeEnded - perfStats.timeStarted;
> > > > > > +
> > > > > > +  // fill in fields
> > > > > > +  document.getElementById("results_num_received").innerHTML =
> > > > > > perfStats.numResults;
> > > > > > +  document.getElementById("results_bytes_received").innerHTML =
> > > > > > perfStats.bytesReceived;
> > > > > > +  document.getElementById("results_time_used").innerHTML =
> > > timeUsedMs
> > > > +
> > > > > > "ms";
> > > > > > +  document.getElementById("results_msgs_per_sec").innerHTML =
> > > > > > (perfStats.numResults / (timeUsedMs / 1000));
> > > > > > +  document.getElementById("results_bytes_per_sec").innerHTML =
> > > > > > (perfStats.bytesReceived / (timeUsedMs / 1000));
> > > > > > +  document.getElementById("test_running").style.display = "none";
> > > > > > +  document.getElementById("results").style.display = "";
> > > > > > +};
> > > > > > +
> > > > > > +function initPerfTest() {
> > > > > > +  clearPerfStats();
> > > > > > +  gadgets.rpc.register("perf_service", perfService);
> > > > > > +  gadgets.rpc.register("clear_perf_stats", clearPerfStats);
> > > > > > +  gadgets.rpc.register("complete_perf_stats", completePerfStats);
> > > > > > +};
> > > > > > +
> > > > > > +var alphabet =
> > > > > > "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
> > > > > _-*&(){}'";
> > > > > > +
> > > > > > +function sendPerfMessage() {
> > > > > > +  var msgToSend = currentRun.msg;
> > > > > > +  if (currentRun.curMsgId++ <= 1) {
> > > > > > +    var nowString = new Date().getTime() + ':';
> > > > > > +    msgToSend = nowString +
> > > > currentRun.msg.substring(nowString.length);
> > > > > > +  }
> > > > > > +
> > > > > > +  gadgets.rpc.call(currentRun.targetId, "perf_service", null,
> > > > > msgToSend);
> > > > > > +  if (currentRun.curMsgId < currentRun.endMsgId) {
> > > > > > +    // loop, giving up execution in case rpc technique demands it
> > > > > > +    window.setTimeout(sendPerfMessage, 0);
> > > > > > +  } else {
> > > > > > +    // send finisher
> > > > > > +    window.setTimeout(function() {
> > > > gadgets.rpc.call(currentRun.targetId,
> > > > > > "complete_perf_stats", null); }, 0);
> > > > > > +  }
> > > > > > +};
> > > > > > +
> > > > > > +function runPerfTest(targetId) {
> > > > > > +  document.getElementById("test_running").style.display = "";
> > > > > > +
> > > > > > +  // initialize the current run
> > > > > > +  var num_msgs = document.getElementById("num_msgs").value;
> > > > > > +  var msg_size = document.getElementById("msg_size").value;
> > > > > > +
> > > > > > +  currentRun.targetId = targetId;
> > > > > > +  currentRun.curMsgId = 0;
> > > > > > +  currentRun.endMsgId = num_msgs;
> > > > > > +
> > > > > > +  var msg = [];
> > > > > > +  for (var i = 0; i < msg_size; ++i) {
> > > > > > +    msg[i] =
> > > > alphabet.charAt(Math.round(Math.random(alphabet.length)));
> > > > > > +  }
> > > > > > +  currentRun.msg = msg.join('');
> > > > > > +
> > > > > > +  // clear local perf stats
> > > > > > +  clearPerfStats("(outbound)");
> > > > > > +
> > > > > > +  // clear target perf stats
> > > > > > +  gadgets.rpc.call(targetId, "clear_perf_stats", null,
> > "(inbound)");
> > > > > > +
> > > > > > +  // kick off the send loop
> > > > > > +  sendPerfMessage();
> > > > > > +};
> > > > > >
> > > > > >
> > > > > >
> > > > >
> > > >
> > >
> >

Re: svn commit: r672393 - in /incubator/shindig/trunk: features/rpc/ java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ javascript/container/

Posted by John Hjelmstad <fa...@google.com>.
I'd attach this to a JIRA issue, but it seems time-sensitive. So, submitted
for comment:

Index: features/rpc/rpc.js
===================================================================
--- features/rpc/rpc.js    (revision 672393)
+++ features/rpc/rpc.js    (working copy)
@@ -139,6 +139,8 @@
   //       - Internet Explorer 6
   //       - Internet Explorer 7
   //
+  //   This technique is only applicable if gadgets.config.enableNIX ===
true
+  //
   // * For Gecko-based browsers, the security model allows a child to call
a
   //   function on the frameElement of the iframe, even if the child is in
   //   a different domain. This method is dubbed "frameElement" (fe).
@@ -175,7 +177,9 @@
   //
   var relayChannel = typeof window.postMessage === 'function' ? 'wpm' :
              typeof document.postMessage === 'function' ? 'dpm' :
-             window.ActiveXObject ? 'nix' :
+             (window.ActiveXObject &&
+                      gadgets.config &&
+                      gadgets.config.enableNIX) ? 'nix' :
                      navigator.product === 'Gecko' ? 'fe' :
                      'ifpc';


--John

On Fri, Jun 27, 2008 at 4:39 PM, John Hjelmstad <fa...@google.com> wrote:

> How's this for a compromise: I'll add a param that disables the new
> technique by default, and allows it to be used if preferred. The rest is
> refactoring and documentation. Given that's the bulk of the code change in
> terms of volume, I didn't create a JIRA issue for it. I'm happy to do so to
> enable it longer-term, however. Thoughts?
>
> John
>
> On Fri, Jun 27, 2008 at 4:27 PM, Kevin Brown <et...@google.com> wrote:
>
>> On Fri, Jun 27, 2008 at 3:45 PM, John Hjelmstad <fa...@google.com> wrote:
>>
>> > As indicated in the CL, I tested this on half a dozen browsers. We don't
>> > have unit tests for it. I've also sent the code and inquiries about the
>> > technique to several poeple, some of whom expressed concerns but no
>> ability
>> > to actively exploit the code.
>>
>>
>> Anyone on this mailing list? Either of the people I mentioned?
>>
>>
>> > The main concern was around VBScript
>> > knowledge, which few people I know extensively have. That was true of
>> IFPC
>> > when it was originally released as well. If any attacks are found, they
>> > will
>> > be actively closed or the technique will be removed. Would you prefer
>> that
>> > no code, irrespective of its value, ever be submitted? It's possible for
>> > any
>> > code to have bugs, and a lot of code to have security holes. If found,
>> > they're fixed.
>>
>>
>> Yes, but this is code that uses a security technique that is highly
>> questionable and has been flagged as potentially problematic by several
>> people. Therefore, it needs more scrutiny. I won't deploy this on any of
>> our
>> production systems until I get an OK from people who are actually familiar
>> with the problems in question have chimed in.
>>
>> This is also a very large code change, and, per policy, large code changes
>> need JIRA tickets and feedback from other people. There are hundreds (if
>> not
>> thousands) of people using Shindig today and the code is deployed to
>> hundreds of millions of users.
>>
>>
>> >
>> > John
>> >
>> > On Fri, Jun 27, 2008 at 3:38 PM, Kevin Brown <et...@google.com> wrote:
>> >
>> > > Please actually test your code or send it to other shindig committers
>> > > before
>> > > you commit it. Your change to GadgetRenderingTask breaks proxying and
>> > > makeRequest in several cases. Fix it.
>> > >
>> > > Have you run this by Brian and/or Mike Samuel? They both expressed
>> doubts
>> > > about whether this mechanism was actually secure.
>> > >
>> > > On Fri, Jun 27, 2008 at 2:06 PM, John Hjelmstad <fa...@google.com>
>> > wrote:
>> > >
>> > > > +jschorr in case there are comments.
>> > > >
>> > > > On Fri, Jun 27, 2008 at 2:01 PM, <jo...@apache.org> wrote:
>> > > >
>> > > > > Author: johnh
>> > > > > Date: Fri Jun 27 14:01:20 2008
>> > > > > New Revision: 672393
>> > > > >
>> > > > > URL: http://svn.apache.org/viewvc?rev=672393&view=rev
>> > > > > Log:
>> > > > > Cleaned up gadgets.rpc library implementation, and implemented
>> fast
>> > IE
>> > > > > transport layer.
>> > > > >
>> > > > > Credit:
>> > > > >        Joseph Schorr (jschorr@google.com) and I implemented this
>> > > > together,
>> > > > > but he
>> > > > >        really did all the hard work, as well as developing the NIX
>> > > > > technique's
>> > > > >        fundamentals. Huge thanks to Joseph!
>> > > > >
>> > > > > Details:
>> > > > >        * Created a new relayChannel for Firefox frameElement
>> > technique,
>> > > > >          making its implementation more linear to read.
>> > > > >        * Consolidated all transport-specific code in
>> setupChannel(),
>> > > > > setupFrame(),
>> > > > >          and callX(), where X = relay type. This refactoring makes
>> > > setup
>> > > > > and use
>> > > > >          of each transport more clear, and makes it possible in
>> later
>> > > > > revisions
>> > > > >          to separate out each method as its own class. In theory,
>> if
>> > we
>> > > > can
>> > > > > trust
>> > > > >          things like User-Agent headers, we can use this to reduce
>> > > rpc.js
>> > > > > code
>> > > > >          size significantly by only including the needed
>> > transport(s).
>> > > > >        * Implemented "NIX" - Native IE XDC. This method exploits
>> the
>> > > fact
>> > > > >          that in IE6 and IE7 the window.opener property can be
>> both
>> > set
>> > > > and
>> > > > >          read across domain boundaries for an IFRAME, and that any
>> > > object
>> > > > >          can be passed through it. In particular, functions
>> > > implementing
>> > > > >          message passing can be passed. These can't be JS
>> functions,
>> > > > > however,
>> > > > >          since those can leak sender context. So, VBScript (COM)
>> > > objects
>> > > > >          wrapping these are passed to maintain context isolation.
>> > > > >          - Requires for IE6/7 that rpc.js be included in the
>> > container
>> > > > >            at load time, not dynamically. TODO added to consider
>> > > > detecting
>> > > > >            whether dynamic loading is happening (at which point we
>> > > should
>> > > > >            fall back to IFPC).
>> > > > >          - Message channel handshake *and* message passing
>> validated
>> > > > >            using authToken, as with other channels.
>> > > > >          - Impl requires that gadget call container first - else
>> IFPC
>> > > > >            is used. This is the same as the frameElement
>> technique.
>> > We
>> > > > >            could add a setInterval()-based initialization routine
>> > later
>> > > > >            if needed. To date, the only gadgets.rpc calls made
>> from
>> > > > >            container to gadget without gadget to container first
>> are
>> > > > >            in theory pubsub messages.
>> > > > >          - Extensive documentation on this technique and the
>> others
>> > > > >            added to the comments, since they're stripped out in
>> the
>> > > > >            majority case.
>> > > > >        * Implemented quick-and-dirty performance testing page
>> > verifying
>> > > > >          the library works and giving basic performance metrics.
>> > > > >          This needs to be improved! But does the trick for now...
>> > > > >
>> > > > > Testing/Data:
>> > > > >
>> > > > > Library verified on the following browsers with the RPC test page.
>> > For
>> > > > > each,
>> > > > > a "bandwidth" measure was taken by averaging the results of 6 G2C
>> and
>> > > C2G
>> > > > > runs
>> > > > > with 100 messages being passed of 1K apiece. Units are 1K
>> > messages/sec.
>> > > > > The "latency" value is the average amount of time taken, over 6
>> G2C
>> > and
>> > > > C2G
>> > > > > runs,
>> > > > > to send 1 1K message. Happily, for all browsers save Safari (soon
>> due
>> > > to
>> > > > > get
>> > > > > window.postMessage), latency is around or below time required to
>> > > achieve
>> > > > > a cross-domain call without user-perceived delay.
>> > > > >
>> > > > >        * Firefox 3 (WPM) - bandwidth: 88 kB/sec; latency: 18ms.
>> > > > >        * Firefox 2 (FE)  - bandwidth: 80 kB/sec; latency: 15ms.
>> > > > >        * Opera 9   (WPM) - bandwidth: 85 kB/sec; latency: 7ms.
>> > > > >        * IE6       (NIX) - bandwidth: 64 kB/sec; latency: 18ms.
>> > > > >        * IE7       (NIX) - bandwidth: 64 kB/sec; latency: 22ms.
>> > > > >        * Safari 3 (IFPC) - bandwidth: ?6-8kB/sec; latency:
>> > ?100-200ms.
>> > > > >          - Safari is somewhat flaky with the RPC test page, before
>> > > > >            and after the rpc.js change. Multiple messages seem to
>> > > > >            confuse it from time to time.
>> > > > >
>> > > > >
>> > > > > Added:
>> > > > >
>> >  incubator/shindig/trunk/javascript/container/rpctest_container.html
>> > > > >
>>  incubator/shindig/trunk/javascript/container/rpctest_gadget.html
>> > > > >    incubator/shindig/trunk/javascript/container/rpctest_perf.js
>> > > > > Modified:
>> > > > >    incubator/shindig/trunk/features/rpc/rpc.js
>> > > > >
>> > > > >
>> > > >
>> > >
>> >
>>  incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
>> > > > >
>> > > > > Modified: incubator/shindig/trunk/features/rpc/rpc.js
>> > > > > URL:
>> > > > >
>> > > >
>> > >
>> >
>> http://svn.apache.org/viewvc/incubator/shindig/trunk/features/rpc/rpc.js?rev=672393&r1=672392&r2=672393&view=diff
>> > > > >
>> > > > >
>> > > >
>> > >
>> >
>> ==============================================================================
>> > > > > --- incubator/shindig/trunk/features/rpc/rpc.js (original)
>> > > > > +++ incubator/shindig/trunk/features/rpc/rpc.js Fri Jun 27
>> 14:01:20
>> > > 2008
>> > > > > @@ -18,7 +18,7 @@
>> > > > >
>> > > > >  /**
>> > > > >  * @fileoverview Remote procedure call library for
>> > gadget-to-container,
>> > > > > - * container-to-gadget, and gadget-to-gadget communication.
>> > > > > + * container-to-gadget, and gadget-to-gadget (thru container)
>> > > > > communication.
>> > > > >  */
>> > > > >
>> > > > >  var gadgets = gadgets || {};
>> > > > > @@ -29,6 +29,30 @@
>> > > > >  * @name gadgets.rpc
>> > > > >  */
>> > > > >  gadgets.rpc = function() {
>> > > > > +  // General constants.
>> > > > > +  var CALLBACK_NAME = '__cb';
>> > > > > +  var DEFAULT_NAME = '';
>> > > > > +
>> > > > > +  // Consts for FrameElement.
>> > > > > +  var FE_G2C_CHANNEL = '__g2c_rpc';
>> > > > > +  var FE_C2G_CHANNEL = '__c2g_rpc';
>> > > > > +
>> > > > > +  // Consts for NIX. VBScript doesn't
>> > > > > +  // allow items to start with _ for some reason,
>> > > > > +  // so we need to make these names quite unique, as
>> > > > > +  // they will go into the global namespace.
>> > > > > +  var NIX_WRAPPER = 'GRPC____NIXVBS_wrapper';
>> > > > > +  var NIX_GET_WRAPPER = 'GRPC____NIXVBS_get_wrapper';
>> > > > > +  var NIX_HANDLE_MESSAGE = 'GRPC____NIXVBS_handle_message';
>> > > > > +  var NIX_CREATE_CHANNEL = 'GRPC____NIXVBS_create_channel';
>> > > > > +  var NIX_ID_FIELD = 'GRPC____NIXVBS_container';
>> > > > > +
>> > > > > +  // JavaScript reference to the NIX VBScript wrappers.
>> > > > > +  // Gadgets will have but a single channel under
>> > > > > +  // nix_channels['..'] while containers will have a channel
>> > > > > +  // per gadget stored under the gadget's ID.
>> > > > > +  var nix_channels = {};
>> > > > > +
>> > > > >   var services = {};
>> > > > >   var iframePool = [];
>> > > > >   var relayUrl = {};
>> > > > > @@ -36,28 +60,135 @@
>> > > > >   var authToken = {};
>> > > > >   var callId = 0;
>> > > > >   var callbacks = {};
>> > > > > +  var setup = {};
>> > > > > +
>> > > > > +  var params = {};
>> > > > > +
>> > > > > +  // Load the authentication token for speaking to the container
>> > > > > +  // from the gadget's parameters, or default to '0' if not
>> found.
>> > > > > +  if (gadgets.util) {
>> > > > > +    params = gadgets.util.getUrlParameters();
>> > > > > +  }
>> > > > >
>> > > > > -  var params = gadgets.util.getUrlParameters();
>> > > > >   authToken['..'] = params.rpctoken || params.ifpctok || 0;
>> > > > >
>> > > > > -  // Pick the most efficient RPC relay mechanism
>> > > > > -  var relayChannel = typeof document.postMessage === 'function' ?
>> > > 'dpm'
>> > > > :
>> > > > > -                     typeof window.postMessage === 'function' ?
>> > 'wpm'
>> > > :
>> > > > > +  // Pick the most efficient RPC relay mechanism:
>> > > > > +  //
>> > > > > +  // * For those browsers that support native messaging (various
>> > > > > implementations
>> > > > > +  //   of the HTML5 postMessage method), use that. Officially
>> > defined
>> > > at
>> > > > > +  //
>> > > > >
>> > http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html
>> > > .
>> > > > > +  //
>> > > > > +  //   postMessage is a native implementation of XDC. A page
>> > registers
>> > > > > that
>> > > > > +  //   it would like to receive messages by listening the the
>> > > "message"
>> > > > > event
>> > > > > +  //   on the window (document in DPM) object. In turn, another
>> page
>> > > can
>> > > > > +  //   raise that event by calling window.postMessage
>> > > > > (document.postMessage
>> > > > > +  //   in DPM) with a string representing the message and a
>> string
>> > > > > +  //   indicating on which domain the receiving page must be to
>> > > receive
>> > > > > +  //   the message. The target page will then have its "message"
>> > event
>> > > > > raised
>> > > > > +  //   if the domain matches and can, in turn, check the origin
>> of
>> > the
>> > > > > message
>> > > > > +  //   and process the data contained within.
>> > > > > +  //
>> > > > > +  //     wpm: postMessage on the window object.
>> > > > > +  //        - Internet Explorer 8+
>> > > > > +  //        - Safari (latest nightlies as of 26/6/2008)
>> > > > > +  //        - Firefox 3+
>> > > > > +  //        - Opera 9+
>> > > > > +  //
>> > > > > +  //     dpm: postMessage on the document object.
>> > > > > +  //        - Opera 8+
>> > > > > +  //
>> > > > > +  // * For Internet Explorer before version 8, the security model
>> > > allows
>> > > > a
>> > > > > +  //   child to set the value of the "opener" property on the
>> parent
>> > > > > window
>> > > > > +  //   and vice versa. This method is dubbed "Native IE XDC"
>> (NIX).
>> > > > > +  //
>> > > > > +  //   This method works by placing a handler object in the
>> "opener"
>> > > > > property
>> > > > > +  //   of a gadget when the container sets up the authentication
>> > > > > information
>> > > > > +  //   for that gadget (by calling setAuthToken(...)). At that
>> > point,
>> > > a
>> > > > > NIX
>> > > > > +  //   wrapper is created and placed into the gadget by calling
>> > > > > +  //   theframe.contentWindow.opener = wrapper. Note that as a
>> > result,
>> > > > NIX
>> > > > > can
>> > > > > +  //   only be used by a container to call a particular gadget
>> > *after*
>> > > > > that
>> > > > > +  //   gadget has called the container at least once via NIX.
>> > > > > +  //
>> > > > > +  //   The NIX wrappers in this RPC implementation are instances
>> of
>> > a
>> > > > > VBScript
>> > > > > +  //   class that is created when this implementation loads. The
>> > > reason
>> > > > > for
>> > > > > +  //   using a VBScript class stems from the fact that any object
>> > can
>> > > be
>> > > > > passed
>> > > > > +  //   into the opener property.
>> > > > > +  //   While this is a good thing, as it lets us pass functions
>> and
>> > > > setup
>> > > > > a true
>> > > > > +  //   bidirectional channel via callbacks, it opens a potential
>> > > > security
>> > > > > hole
>> > > > > +  //   by which the other page can get ahold of the "window" or
>> > > > "document"
>> > > > > +  //   objects in the parent page and in turn wreak havok. This
>> is
>> > due
>> > > > to
>> > > > > the
>> > > > > +  //   fact that any JS object useful for establishing such a
>> > > > > bidirectional
>> > > > > +  //   channel (such as a function) can be used to access a
>> function
>> > > > > +  //   (eg. obj.toString, or a function itself) created in a
>> > specific
>> > > > > context,
>> > > > > +  //   in particular the global context of the sender. Suppose
>> > > container
>> > > > > +  //   domain C passes object obj to gadget on domain G. Then the
>> > > gadget
>> > > > > can
>> > > > > +  //   access C's global context using:
>> > > > > +  //   var parentWindow = (new obj.toString.constructor("return
>> > > > > window;"))();
>> > > > > +  //   Nulling out all of obj's properties doesn't fix this,
>> since
>> > IE
>> > > > > helpfully
>> > > > > +  //   restores them to their original values if you do something
>> > > like:
>> > > > > +  //   delete obj.toString; delete obj.toString;
>> > > > > +  //   Thus, we wrap the necessary functions and information
>> inside
>> > a
>> > > > > VBScript
>> > > > > +  //   object. VBScript objects in IE, like DOM objects, are in
>> fact
>> > > COM
>> > > > > +  //   wrappers when used in JavaScript, so we can safely pass
>> them
>> > > > around
>> > > > > +  //   without worrying about a breach of context while at the
>> same
>> > > time
>> > > > > +  //   allowing them to act as a pass-through mechanism for
>> > > information
>> > > > > +  //   and function calls. The implementation details of this
>> > VBScript
>> > > > > wrapper
>> > > > > +  //   can be found in the setupChannel() method below.
>> > > > > +  //
>> > > > > +  //     nix: Internet Explorer-specific window.opener trick.
>> > > > > +  //       - Internet Explorer 6
>> > > > > +  //       - Internet Explorer 7
>> > > > > +  //
>> > > > > +  // * For Gecko-based browsers, the security model allows a
>> child
>> > to
>> > > > call
>> > > > > a
>> > > > > +  //   function on the frameElement of the iframe, even if the
>> child
>> > > is
>> > > > in
>> > > > > +  //   a different domain. This method is dubbed "frameElement"
>> > (fe).
>> > > > > +  //
>> > > > > +  //   The ability to add and call such functions on the
>> > frameElement
>> > > > > allows
>> > > > > +  //   a bidirectional channel to be setup via the adding of
>> simple
>> > > > > function
>> > > > > +  //   references on the frameElement object itself. In this
>> > > > > implementation,
>> > > > > +  //   when the container sets up the authentication information
>> for
>> > > > that
>> > > > > gadget
>> > > > > +  //   (by calling setAuth(...)) it as well adds a special
>> function
>> > on
>> > > > the
>> > > > > +  //   gadget's iframe. This function can then be used by the
>> gadget
>> > > to
>> > > > > send
>> > > > > +  //   messages to the container. In turn, when the gadget tries
>> to
>> > > send
>> > > > a
>> > > > > +  //   message, it checks to see if this function has its own
>> > function
>> > > > > stored
>> > > > > +  //   that can be used by the container to call the gadget. If
>> not,
>> > > the
>> > > > > +  //   function is created and subsequently used by the
>> container.
>> > > > > +  //   Note that as a result, FE can only be used by a container
>> to
>> > > call
>> > > > a
>> > > > > +  //   particular gadget *after* that gadget has called the
>> > container
>> > > at
>> > > > > +  //   least once via FE.
>> > > > > +  //
>> > > > > +  //     fe: Gecko-specific frameElement trick.
>> > > > > +  //        - Firefox 1+
>> > > > > +  //
>> > > > > +  // * For all others, we have a fallback mechanism known as
>> "ifpc".
>> > > > IFPC
>> > > > > +  //   exploits the fact that while same-origin policy prohibits
>> a
>> > > frame
>> > > > > from
>> > > > > +  //   accessing members on a window not in the same domain, that
>> > > frame
>> > > > > can,
>> > > > > +  //   however, navigate the window heirarchy (via parent). This
>> is
>> > > > > exploited by
>> > > > > +  //   having a page on domain A that wants to talk to domain B
>> > create
>> > > > an
>> > > > > iframe
>> > > > > +  //   on domain B pointing to a special relay file and with a
>> > message
>> > > > > encoded
>> > > > > +  //   after the hash (#). This relay, in turn, finds the page on
>> > > domain
>> > > > > B, and
>> > > > > +  //   can call a receipt function with the message given to it.
>> The
>> > > > relay
>> > > > > URL
>> > > > > +  //   used by each caller is set via the
>> > gadgets.rpc.setRelayUrl(..)
>> > > > and
>> > > > > +  //   *must* be called before the call method is used.
>> > > > > +  //
>> > > > > +  //     ifpc: Iframe-based method, utilizing a relay page, to
>> send
>> > a
>> > > > > message.
>> > > > > +  //
>> > > > > +  var relayChannel = typeof window.postMessage === 'function' ?
>> > 'wpm'
>> > > :
>> > > > > +                    typeof document.postMessage === 'function' ?
>> > 'dpm'
>> > > :
>> > > > > +                    window.ActiveXObject ? 'nix' :
>> > > > > +                     navigator.product === 'Gecko' ? 'fe' :
>> > > > >                      'ifpc';
>> > > > > -  if (relayChannel === 'dpm' || relayChannel === 'wpm') {
>> > > > > -    window.addEventListener('message', function(packet) {
>> > > > > -      // TODO validate packet.domain for security reasons
>> > > > > -      process(gadgets.json.parse(packet.data));
>> > > > > -    }, false);
>> > > > > -  }
>> > > > >
>> > > > > -  // Default RPC handler
>> > > > > -  services[''] = function() {
>> > > > > +  // Conduct any setup necessary for the chosen channel.
>> > > > > +  setupChannel();
>> > > > > +
>> > > > > +  // Create the Default RPC handler.
>> > > > > +  services[DEFAULT_NAME] = function() {
>> > > > >     throw new Error('Unknown RPC service: ' + this.s);
>> > > > >   };
>> > > > >
>> > > > > -  // Special RPC handler for callbacks
>> > > > > -  services['__cb'] = function(callbackId, result) {
>> > > > > +  // Create a Special RPC handler for callbacks.
>> > > > > +  services[CALLBACK_NAME] = function(callbackId, result) {
>> > > > >     var callback = callbacks[callbackId];
>> > > > >     if (callback) {
>> > > > >       delete callbacks[callbackId];
>> > > > > @@ -66,6 +197,147 @@
>> > > > >   };
>> > > > >
>> > > > >   /**
>> > > > > +   * Conducts any initial global work necessary to setup the
>> > > > > +   * channel type chosen.
>> > > > > +   */
>> > > > > +  function setupChannel() {
>> > > > > +    // If the channel type is one of the native
>> > > > > +    // postMessage based ones, setup the handler to receive
>> > > > > +    // messages.
>> > > > > +    if (relayChannel === 'dpm' || relayChannel === 'wpm') {
>> > > > > +      window.addEventListener('message', function(packet) {
>> > > > > +        // TODO validate packet.domain for security reasons
>> > > > > +        process(gadgets.json.parse(packet.data));
>> > > > > +      }, false);
>> > > > > +    }
>> > > > > +
>> > > > > +    // If the channel type is NIX, we need to ensure the
>> > > > > +    // VBScript wrapper code is in the page and that the
>> > > > > +    // global Javascript handlers have been set.
>> > > > > +    if (relayChannel === 'nix') {
>> > > > > +      // VBScript methods return a type of 'unknown' when
>> > > > > +      // checked via the typeof operator in IE. Fortunately
>> > > > > +      // for us, this only applies to COM objects, so we
>> > > > > +      // won't see this for a real Javascript object.
>> > > > > +      if (typeof window[NIX_GET_WRAPPER] !== 'unknown') {
>> > > > > +        window[NIX_HANDLE_MESSAGE] = function(data) {
>> > > > > +          process(gadgets.json.parse(data));
>> > > > > +        };
>> > > > > +
>> > > > > +        window[NIX_CREATE_CHANNEL] = function(name, channel,
>> token)
>> > {
>> > > > > +          // Verify the authentication token of the gadget trying
>> > > > > +          // to create a channel for us.
>> > > > > +          if (authToken[name] == token) {
>> > > > > +            nix_channels[name] = channel;
>> > > > > +          }
>> > > > > +        };
>> > > > > +
>> > > > > +        // Inject the VBScript code needed.
>> > > > > +        var vbscript =
>> > > > > +          '<scr' + 'ipt language="VBScript">'
>> > > > > +          // We create a class to act as a wrapper for
>> > > > > +          // a Javascript call, to prevent a break in of
>> > > > > +          // the context.
>> > > > > +          + 'Class ' + NIX_WRAPPER + '\n '
>> > > > > +
>> > > > > +          // An internal member for keeping track of the
>> > > > > +          // name of the document (container or gadget)
>> > > > > +          // for which this wrapper is intended. For
>> > > > > +          // those wrappers created by gadgets, this is not
>> > > > > +          // used (although it is set to "..")
>> > > > > +          + 'Private m_Intended\n'
>> > > > > +
>> > > > > +          // Method for internally setting the value
>> > > > > +          // of the m_Intended property.
>> > > > > +          + 'Public Sub SetIntendedName(name)\n '
>> > > > > +          + 'm_Intended = name\n'
>> > > > > +          + 'End Sub\n'
>> > > > > +
>> > > > > +          // A wrapper method which actually causes a
>> > > > > +          // message to be sent to the other context.
>> > > > > +          + 'Public Sub SendMessage(data)\n '
>> > > > > +          + NIX_HANDLE_MESSAGE + '(data)\n'
>> > > > > +          + 'End Sub\n'
>> > > > > +
>> > > > > +          // Method for setting up the container->gadget
>> > > > > +          // channel. Not strictly needed in the gadget's
>> > > > > +          // wrapper, but no reason to get rid of it. Note here
>> > > > > +          // that we pass the intended name to the
>> > NIX_CREATE_CHANNEL
>> > > > > +          // method so that it can save the channel in the proper
>> > > place
>> > > > > +          // *and* verify the channel via the authentication
>> token
>> > > > passed
>> > > > > +          // here.
>> > > > > +          + 'Public Sub CreateChannel(channel, auth)\n '
>> > > > > +          + 'Call ' + NIX_CREATE_CHANNEL + '(m_Intended, channel,
>> > > > auth)\n'
>> > > > > +          + 'End Sub\n'
>> > > > > +
>> > > > > +          // An empty field with a unique identifier to
>> > > > > +          // prevent the code from confusing this wrapper
>> > > > > +          // with a run-of-the-mill value found in window.opener.
>> > > > > +          + 'Public Sub ' + NIX_ID_FIELD + '()\n '
>> > > > > +          + 'End Sub\n'
>> > > > > +          + 'End Class\n '
>> > > > > +
>> > > > > +          // Function to get a reference to the wrapper.
>> > > > > +          + 'Function ' + NIX_GET_WRAPPER + '(name)\n'
>> > > > > +          + 'Dim wrap\n'
>> > > > > +          + 'Set wrap = New ' + NIX_WRAPPER + '\n'
>> > > > > +          + 'wrap.SetIntendedName name\n'
>> > > > > +          + 'Set ' + NIX_GET_WRAPPER + ' = wrap\n'
>> > > > > +          + 'End Function'
>> > > > > +          + '</scr' + 'ipt>'
>> > > > > +
>> > > > > +          // Resets the "default" scripting language in IE back
>> > > > > +          // to Javascript. This is needed or else any script
>> > > > > +          // tags without a proper Language="..." will be treated
>> as
>> > > > > +          // VBScript once this code is added to the document.
>> > > > > +          + '<scri' + 'pt language="JScript"></scr' + 'ipt>';
>> > > > > +
>> > > > > +        // Note that this code can only be run once the document
>> has
>> > > > > +        // fully loaded.
>> > > > > +        // TODO: Perhaps add some sort of check here for this?
>> > > > > +        document.write(vbscript);
>> > > > > +      }
>> > > > > +    }
>> > > > > +  }
>> > > > > +
>> > > > > +  /**
>> > > > > +   * Conducts any frame-specific work necessary to setup
>> > > > > +   * the channel type chosen. This method is called when
>> > > > > +   * the container page first registers the gadget in the
>> > > > > +   * RPC mechanism. Gadgets, in turn, will complete the setup
>> > > > > +   * of the channel once they send their first messages.
>> > > > > +   */
>> > > > > +  function setupFrame(frameId) {
>> > > > > +    if (setup[frameId]) {
>> > > > > +      return;
>> > > > > +    }
>> > > > > +
>> > > > > +    if (relayChannel === 'fe') {
>> > > > > +      try {
>> > > > > +        var frame = document.getElementById(frameId);
>> > > > > +        frame[FE_G2C_CHANNEL] = function(args) {
>> > > > > +          process(gadgets.json.parse(args));
>> > > > > +        };
>> > > > > +      } catch (e) {
>> > > > > +        // Something went wrong. System will fallback to
>> > > > > +        // IFPC.
>> > > > > +      }
>> > > > > +    }
>> > > > > +
>> > > > > +    if (relayChannel === 'nix') {
>> > > > > +      try {
>> > > > > +        var frame = document.getElementById(frameId);
>> > > > > +        frame.contentWindow.opener =
>> > window[NIX_GET_WRAPPER](frameId);
>> > > > > +      } catch (e) {
>> > > > > +        // Something went wrong. System will fallback to
>> > > > > +        // IFPC.
>> > > > > +      }
>> > > > > +    }
>> > > > > +
>> > > > > +    setup[frameId] = true;
>> > > > > +  }
>> > > > > +
>> > > > > +  /**
>> > > > >    * Encodes arguments for the legacy IFPC wire format.
>> > > > >    *
>> > > > >    * @param {Object} args
>> > > > > @@ -86,8 +358,17 @@
>> > > > >    * @private
>> > > > >    */
>> > > > >   function process(rpc) {
>> > > > > +    //
>> > > > > +    // RPC object contents:
>> > > > > +    //   s: Service Name
>> > > > > +    //   f: From
>> > > > > +    //   c: The callback ID or 0 if none.
>> > > > > +    //   a: The arguments for this RPC call.
>> > > > > +    //   t: The authentication token.
>> > > > > +    //
>> > > > >     if (rpc && typeof rpc.s === 'string' && typeof rpc.f ===
>> 'string'
>> > > &&
>> > > > >         rpc.a instanceof Array) {
>> > > > > +
>> > > > >       // Validate auth token.
>> > > > >       if (authToken[rpc.f]) {
>> > > > >         // We allow type coercion here because all the url params
>> are
>> > > > > strings.
>> > > > > @@ -96,43 +377,158 @@
>> > > > >         }
>> > > > >       }
>> > > > >
>> > > > > -      // The Gecko engine used by FireFox etc. allows an IFrame
>> to
>> > > > > directly call
>> > > > > -      // methods on the frameElement property added by the
>> container
>> > > > page
>> > > > > even
>> > > > > -      // if their domains don't match.
>> > > > > -      // Here we try to set up a relay channel using the
>> > frameElement
>> > > > > technique
>> > > > > -      // to greatly reduce the latency of cross-domain calls if
>> the
>> > > > > postMessage
>> > > > > -      // method is not supported.
>> > > > > -      if (relayChannel === 'ifpc') {
>> > > > > -        if (rpc.f === '..') {
>> > > > > -          // Container-to-gadget call
>> > > > > -          try {
>> > > > > -            var fel = window.frameElement;
>> > > > > -            if (typeof fel.__g2c_rpc === 'function' &&
>> > > > > -                typeof fel.__g2c_rpc.__c2g_rpc != 'function') {
>> > > > > -              fel.__g2c_rpc.__c2g_rpc = function(args) {
>> > > > > -                process(gadgets.json.parse(args));
>> > > > > -              };
>> > > > > -            }
>> > > > > -          } catch (e) {
>> > > > > -          }
>> > > > > -        } else {
>> > > > > -          // Gadget-to-container call
>> > > > > -          var iframe = document.getElementById(rpc.f);
>> > > > > -          if (iframe && typeof iframe.__g2c_rpc != 'function') {
>> > > > > -            iframe.__g2c_rpc = function(args) {
>> > > > > +      // Call the requested RPC service.
>> > > > > +      var result = (services[rpc.s] ||
>> > > > > +                    services[DEFAULT_NAME]).apply(rpc, rpc.a);
>> > > > > +
>> > > > > +      // If there is a callback for this service, initiate it as
>> > well.
>> > > > > +      if (rpc.c) {
>> > > > > +        gadgets.rpc.call(rpc.f, CALLBACK_NAME, null, rpc.c,
>> result);
>> > > > > +      }
>> > > > > +    }
>> > > > > +  }
>> > > > > +
>> > > > > +  /**
>> > > > > +   * Attempts to conduct an RPC call to the specified
>> > > > > +   * target with the specified data via the NIX
>> > > > > +   * method. If this method fails, the system attempts again
>> > > > > +   * using the known default of IFPC.
>> > > > > +   *
>> > > > > +   * @param {String} targetId Module Id of the RPC service
>> provider.
>> > > > > +   * @param {String} from Module Id of the calling provider.
>> > > > > +   * @param {Object} rpcData The RPC data for this call.
>> > > > > +   */
>> > > > > +  function callNIX(targetId, from, rpcData) {
>> > > > > +    try {
>> > > > > +      if (from != '..') {
>> > > > > +        // Call from gadget to the container.
>> > > > > +        var handler = nix_channels['..'];
>> > > > > +
>> > > > > +        // If the gadget has yet to retrieve a reference to
>> > > > > +        // the NIX handler, try to do so now. We don't do a
>> > > > > +        // typeof(window.opener[NIX_ID_FIELD]) check here
>> > > > > +        // because it means accessing that field on the COM
>> object,
>> > > > which,
>> > > > > +        // being an internal function reference, is not allowed.
>> > > > > +        // "in" works because it merely checks for the prescence
>> of
>> > > > > +        // the key, rather than actually accessing the object's
>> > > > property.
>> > > > > +        if (!handler && window.opener && NIX_ID_FIELD in
>> > > window.opener)
>> > > > {
>> > > > > +          handler = nix_channels['..'] = window.opener;
>> > > > > +
>> > > > > +          // Create the channel to the parent/container. We pass
>> > both
>> > > > our
>> > > > > +          // own wrapper and our authentication token for
>> > > verification.
>> > > > > +          handler.CreateChannel(window[NIX_GET_WRAPPER]('..'),
>> > > > > authToken['..']);
>> > > > > +        }
>> > > > > +
>> > > > > +        // If we have a handler, call it.
>> > > > > +        if (handler) {
>> > > > > +          handler.SendMessage(rpcData);
>> > > > > +          return;
>> > > > > +        }
>> > > > > +      } else {
>> > > > > +        // Call from container to a gadget[targetId].
>> > > > > +
>> > > > > +        // If we have a handler, call it.
>> > > > > +        if (nix_channels[targetId]) {
>> > > > > +          nix_channels[targetId].SendMessage(rpcData);
>> > > > > +          return;
>> > > > > +        }
>> > > > > +      }
>> > > > > +    } catch (e) {
>> > > > > +    }
>> > > > > +
>> > > > > +    // If we have reached this point, something has failed
>> > > > > +    // with the NIX method, so we default to using
>> > > > > +    // IFPC for this call.
>> > > > > +    callIFPC(targetId, from, rpcData);
>> > > > > +  }
>> > > > > +
>> > > > > +  /**
>> > > > > +   * Attempts to conduct an RPC call to the specified
>> > > > > +   * target with the specified data via the FrameElement
>> > > > > +   * method. If this method fails, the system attempts again
>> > > > > +   * using the known default of IFPC.
>> > > > > +   *
>> > > > > +   * @param {String} targetId Module Id of the RPC service
>> provider.
>> > > > > +   * @param {String} from Module Id of the calling provider.
>> > > > > +   * @param {Object} rpcData The RPC data for this call.
>> > > > > +   */
>> > > > > +  function callFrameElement(targetId, from, rpcData) {
>> > > > > +    try {
>> > > > > +      if (from != '..') {
>> > > > > +        // Call from gadget to the container.
>> > > > > +        var fe = window.frameElement;
>> > > > > +
>> > > > > +        if (typeof fe[FE_G2C_CHANNEL] === 'function') {
>> > > > > +          // Complete the setup of the FE channel if need be.
>> > > > > +          if (typeof fe[FE_G2C_CHANNEL][FE_C2G_CHANNEL] !==
>> > > 'function')
>> > > > {
>> > > > > +            fe[FE_G2C_CHANNEL][FE_C2G_CHANNEL] = function(args) {
>> > > > >               process(gadgets.json.parse(args));
>> > > > >             };
>> > > > >           }
>> > > > > +
>> > > > > +          // Conduct the RPC call.
>> > > > > +          fe[FE_G2C_CHANNEL](rpcData);
>> > > > > +          return;
>> > > > >         }
>> > > > > -      }
>> > > > > +      } else {
>> > > > > +        // Call from container to gadget[targetId].
>> > > > > +        var frame = document.getElementById(targetId);
>> > > > >
>> > > > > -      var result = (services[rpc.s] || services['']).apply(rpc,
>> > > rpc.a);
>> > > > > -      if (rpc.c) {
>> > > > > -        gadgets.rpc.call(rpc.f, '__cb', null, rpc.c, result);
>> > > > > +        if (typeof frame[FE_G2C_CHANNEL] === 'function' &&
>> > > > > +            typeof frame[FE_G2C_CHANNEL][FE_C2G_CHANNEL] ===
>> > > 'function')
>> > > > {
>> > > > > +
>> > > > > +          // Conduct the RPC call.
>> > > > > +          frame[FE_G2C_CHANNEL][FE_C2G_CHANNEL](rpcData);
>> > > > > +          return;
>> > > > > +        }
>> > > > >       }
>> > > > > +    } catch (e) {
>> > > > > +    }
>> > > > > +
>> > > > > +    // If we have reached this point, something has failed
>> > > > > +    // with the FrameElement method, so we default to using
>> > > > > +    // IFPC for this call.
>> > > > > +    callIFPC(targetId, from, rpcData);
>> > > > > +  }
>> > > > > +
>> > > > > +  /**
>> > > > > +   * Conducts an RPC call to the specified
>> > > > > +   * target with the specified data via the IFPC
>> > > > > +   * method.
>> > > > > +   *
>> > > > > +   * @param {String} targetId Module Id of the RPC service
>> provider.
>> > > > > +   * @param {String} from Module Id of the calling provider.
>> > > > > +   * @param {Object} rpcData The RPC data for this call.
>> > > > > +   */
>> > > > > +  function callIFPC(targetId, from, rpcData) {
>> > > > > +    // Retrieve the relay file used by IFPC. Note that
>> > > > > +    // this must be set before the call, and so we conduct
>> > > > > +    // an extra check to ensure it is not blank.
>> > > > > +    var relay = gadgets.rpc.getRelayUrl(targetId);
>> > > > > +
>> > > > > +    if (!relay) {
>> > > > > +      throw new Error('No relay file assigned for IFPC');
>> > > > >     }
>> > > > > +
>> > > > > +    // The RPC mechanism supports two formats for IFPC (legacy
>> and
>> > > > > current).
>> > > > > +    var src = null;
>> > > > > +    if (useLegacyProtocol[targetId]) {
>> > > > > +      // Format:
>> > > #iframe_id&callId&num_packets&packet_num&block_of_data
>> > > > > +      src = [relay, '#', encodeLegacyData([from, callId, 1, 0,
>> > > > > +             encodeLegacyData([from, serviceName, '', '',
>> > > from].concat(
>> > > > > +               Array.prototype.slice.call(arguments,
>> > 3)))])].join('');
>> > > > > +    } else {
>> > > > > +      // Format: #targetId & sourceId@callId & packetNum &
>> packetId
>> > &
>> > > > > packetData
>> > > > > +      src = [relay, '#', targetId, '&', from, '@', callId,
>> > > > > +             '&1&0&', encodeURIComponent(rpcData)].join('');
>> > > > > +    }
>> > > > > +
>> > > > > +    // Conduct the IFPC call by creating the Iframe with
>> > > > > +    // the relay URL and appended message.
>> > > > > +    emitInvisibleIframe(src);
>> > > > >   }
>> > > > >
>> > > > > +
>> > > > >   /**
>> > > > >    * Helper function to emit an invisible IFrame.
>> > > > >    * @param {String} src SRC attribute of the IFrame to emit.
>> > > > > @@ -221,6 +617,15 @@
>> > > > >      * @member gadgets.rpc
>> > > > >      */
>> > > > >     register: function(serviceName, handler) {
>> > > > > +      if (serviceName == CALLBACK_NAME) {
>> > > > > +        throw new Error("Cannot overwrite callback service");
>> > > > > +      }
>> > > > > +
>> > > > > +      if (serviceName == DEFAULT_NAME) {
>> > > > > +        throw new Error("Cannot overwrite default service:"
>> > > > > +                        + " use registerDefault");
>> > > > > +      }
>> > > > > +
>> > > > >       services[serviceName] = handler;
>> > > > >     },
>> > > > >
>> > > > > @@ -231,6 +636,15 @@
>> > > > >      * @member gadgets.rpc
>> > > > >      */
>> > > > >     unregister: function(serviceName) {
>> > > > > +      if (serviceName == CALLBACK_NAME) {
>> > > > > +        throw new Error("Cannot delete callback service");
>> > > > > +      }
>> > > > > +
>> > > > > +      if (serviceName == DEFAULT_NAME) {
>> > > > > +        throw new Error("Cannot delete default service:"
>> > > > > +                        + " use unregisterDefault");
>> > > > > +      }
>> > > > > +
>> > > > >       delete services[serviceName];
>> > > > >     },
>> > > > >
>> > > > > @@ -272,12 +686,14 @@
>> > > > >       if (callback) {
>> > > > >         callbacks[callId] = callback;
>> > > > >       }
>> > > > > -      var from;
>> > > > > +
>> > > > > +      // Default to the container calling.
>> > > > > +      var from = '..';
>> > > > > +
>> > > > >       if (targetId === '..') {
>> > > > >         from = window.name;
>> > > > > -      } else {
>> > > > > -        from = '..';
>> > > > >       }
>> > > > > +
>> > > > >       // Not used by legacy, create it anyway...
>> > > > >       var rpcData = gadgets.json.stringify({
>> > > > >         s: serviceName,
>> > > > > @@ -287,53 +703,37 @@
>> > > > >         t: authToken[targetId]
>> > > > >       });
>> > > > >
>> > > > > +      var channelType = relayChannel;
>> > > > > +
>> > > > > +      // If we are told to use the legacy format, then we must
>> > > > > +      // default to IFPC.
>> > > > >       if (useLegacyProtocol[targetId]) {
>> > > > > -        relayChannel = 'ifpc';
>> > > > > +        channelType = 'ifpc';
>> > > > >       }
>> > > > >
>> > > > > -      switch (relayChannel) {
>> > > > > -      case 'dpm': // use document.postMessage
>> > > > > -        var targetDoc = targetId === '..' ? parent.document :
>> > > > > -
>> >  frames[targetId].document;
>> > > > > -        targetDoc.postMessage(rpcData);
>> > > > > -        break;
>> > > > > -      case 'wpm': // use window.postMessage
>> > > > > -        var targetWin = targetId === '..' ? parent :
>> > frames[targetId];
>> > > > > -        targetWin.postMessage(rpcData, "*");
>> > > > > -        break;
>> > > > > -      default: // use 'ifpc' as a fallback mechanism
>> > > > > -        var relay = gadgets.rpc.getRelayUrl(targetId);
>> > > > > -        // TODO split message if too long
>> > > > > -        var src;
>> > > > > -        if (useLegacyProtocol[targetId]) {
>> > > > > -          //
>> #iframe_id&callId&num_packets&packet_num&block_of_data
>> > > > > -          src = [relay, '#', encodeLegacyData([from, callId, 1,
>> 0,
>> > > > > -                 encodeLegacyData([from, serviceName, '', '',
>> > > > > from].concat(
>> > > > > -                 Array.prototype.slice.call(arguments,
>> > > 3)))])].join('');
>> > > > > -        } else {
>> > > > > -          // Try the frameElement channel if available
>> > > > > -          try {
>> > > > > -            if (from === '..') {
>> > > > > -              // Container-to-gadget
>> > > > > -              var iframe = document.getElementById(targetId);
>> > > > > -              if (typeof iframe.__g2c_rpc.__c2g_rpc ===
>> 'function')
>> > {
>> > > > > -                iframe.__g2c_rpc.__c2g_rpc(rpcData);
>> > > > > -                return;
>> > > > > -              }
>> > > > > -            } else {
>> > > > > -              // Gadget-to-container
>> > > > > -              if (typeof window.frameElement.__g2c_rpc ===
>> > 'function')
>> > > {
>> > > > > -                window.frameElement.__g2c_rpc(rpcData);
>> > > > > -                return;
>> > > > > -              }
>> > > > > -            }
>> > > > > -          } catch (e) {
>> > > > > -          }
>> > > > > -          // # targetId & sourceId@callId & packetNum & packetId
>> &
>> > > > > packetData
>> > > > > -          src = [relay, '#', targetId, '&', from, '@', callId,
>> > > > > -                 '&1&0&', encodeURIComponent(rpcData)].join('');
>> > > > > -        }
>> > > > > -        emitInvisibleIframe(src);
>> > > > > +      switch (channelType) {
>> > > > > +        case 'dpm': // use document.postMessage.
>> > > > > +          var targetDoc = targetId === '..' ? parent.document :
>> > > > > +
>> > >  frames[targetId].document;
>> > > > > +          targetDoc.postMessage(rpcData);
>> > > > > +          break;
>> > > > > +
>> > > > > +        case 'wpm': // use window.postMessage.
>> > > > > +          var targetWin = targetId === '..' ? parent :
>> > > frames[targetId];
>> > > > > +          targetWin.postMessage(rpcData, "*");
>> > > > > +          break;
>> > > > > +
>> > > > > +        case 'nix': // use NIX.
>> > > > > +          callNIX(targetId, from, rpcData);
>> > > > > +          break;
>> > > > > +
>> > > > > +        case 'fe': // use FrameElement.
>> > > > > +          callFrameElement(targetId, from, rpcData);
>> > > > > +          break;
>> > > > > +
>> > > > > +        default: // use 'ifpc' as a fallback mechanism.
>> > > > > +          callIFPC(targetId, from, rpcData);
>> > > > > +          break;
>> > > > >       }
>> > > > >     },
>> > > > >
>> > > > > @@ -372,15 +772,13 @@
>> > > > >      */
>> > > > >     setAuthToken: function(targetId, token) {
>> > > > >       authToken[targetId] = token;
>> > > > > +      setupFrame(targetId);
>> > > > >     },
>> > > > >
>> > > > >     /**
>> > > > >      * Gets the RPC relay mechanism.
>> > > > > -     * @return {String} RPC relay mechanism. Supported types:
>> > > > > -     *                  'wpm' - Use window.postMessage (defined
>> by
>> > > > HTML5)
>> > > > > -     *                  'dpm' - Use document.postMessage (defined
>> by
>> > > an
>> > > > > early
>> > > > > -     *                          draft of HTML5 and implemented by
>> > > Opera)
>> > > > > -     *                  'ifpc' - Use invisible IFrames
>> > > > > +     * @return {String} RPC relay mechanism. See above for
>> > > > > +     *   a list of supported types.
>> > > > >      *
>> > > > >      * @member gadgets.rpc
>> > > > >      */
>> > > > >
>> > > > > Modified:
>> > > > >
>> > > >
>> > >
>> >
>> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
>> > > > > URL:
>> > > > >
>> > > >
>> > >
>> >
>> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java?rev=672393&r1=672392&r2=672393&view=diff
>> > > > >
>> > > > >
>> > > >
>> > >
>> >
>> ==============================================================================
>> > > > > ---
>> > > > >
>> > > >
>> > >
>> >
>> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
>> > > > > (original)
>> > > > > +++
>> > > > >
>> > > >
>> > >
>> >
>> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
>> > > > > Fri Jun 27 14:01:20 2008
>> > > > > @@ -221,7 +221,8 @@
>> > > > >           "a {color:#0000cc;}a:visited {color:#551a8b;}" +
>> > > > >           "a:active {color:#ff0000;}" +
>> > > > >           "body{margin: 0px;padding: 0px;background-color:white;}"
>> +
>> > > > > -          "</style></head>";
>> > > > > +          "</style><base>" + gadget.getSpec().getUrl().toString()
>> +
>> > > > > +          "</base></head>";
>> > > > >     markup.append(boilerPlate);
>> > > > >     LocaleSpec localeSpec =
>> > > gadget.getSpec().getModulePrefs().getLocale(
>> > > > >         gadget.getContext().getLocale());
>> > > > >
>> > > > > Added:
>> > > > incubator/shindig/trunk/javascript/container/rpctest_container.html
>> > > > > URL:
>> > > > >
>> > > >
>> > >
>> >
>> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_container.html?rev=672393&view=auto
>> > > > >
>> > > > >
>> > > >
>> > >
>> >
>> ==============================================================================
>> > > > > ---
>> > incubator/shindig/trunk/javascript/container/rpctest_container.html
>> > > > > (added)
>> > > > > +++
>> > incubator/shindig/trunk/javascript/container/rpctest_container.html
>> > > > Fri
>> > > > > Jun 27 14:01:20 2008
>> > > > > @@ -0,0 +1,90 @@
>> > > > > +<!--
>> > > > > +  Simple page for testing gadgets.rpc performance.
>> > > > > +  Allows you to add a simulated "gadget" (in this case just a
>> static
>> > > > > +  HTML page which loads gadgets.rpc also), and pass some
>> > > > > +  specified number of random messages of specified size to
>> > > > > +  and from it.
>> > > > > +
>> > > > > +  A simple recommended setup is to start up two instances of
>> > > > > +  the Shindig Gadgets Server on two separate ports to test
>> > > > > +  "real" cross-domain communication, since port is factored
>> > > > > +  into the same-domain policy enforced by browsers.
>> > > > > +
>> > > > > +  If your servers are on localhost:8080 and localhost:8081, then
>> > hit:
>> > > > > +
>> > > http://localhost:8080/gadgets/files/container/rpctest_container.html?
>> > > > \
>> > > > > +
>> > http://localhost:8081/gadgets/files/container/rpctest_gadget.html&\<http://localhost:8081/gadgets/files/container/rpctest_gadget.html&%5C>
>> <http://localhost:8081/gadgets/files/container/rpctest_gadget.html&%5C>
>> > > > > +
>> > > > >
>> > > >
>> > >
>> >
>> http://localhost:8081/gadgets/files/container/rpc_relay.uncompressed.html
>> > > > > +
>> > > > > +  (Note the backslashes should be removed, as they exist for
>> > > formatting
>> > > > > only.)
>> > > > > +
>> > > > > +  There are two arguments, separated by an ampersand:
>> > > > > +  1. URL of the "gadget" test page.
>> > > > > +  2. URL of the "gadget" test page's RPC relay (for browsers
>> > requiring
>> > > > > it).
>> > > > > +-->
>> > > > > +<html>
>> > > > > +  <head>
>> > > > > +    <title>gadgets.rpc Performance Tests: Container</title>
>> > > > > +    <script src="../../js/rpc.js?c=1&debug=1"></script>
>> > > > > +    <script src="rpctest_perf.js"></script>
>> > > > > +    <script>
>> > > > > +      function initTest() {
>> > > > > +        var container = document.getElementById("container");
>> > > > > +
>> > > > > +        // query string is assumed to be the "gadget" URL as arg
>> 1,
>> > > > > +        // and optionally the relay URL as arg 2
>> > > > > +        var pageArgs =
>> > window.location.search.substring(1).split('&');
>> > > > > +        var gadgetUrl = pageArgs[0];
>> > > > > +        var secret = Math.round(Math.random()*10000000);
>> > > > > +        if (pageArgs[1]) {
>> > > > > +          gadgets.rpc.setRelayUrl('gadget', pageArgs[1]);
>> > > > > +        }
>> > > > > +        var containerRelay = pageArgs[2] || '';
>> > > > > +        container.innerHTML = "<iframe id='gadget' name='gadget'
>> > > > > height=300 width=300 src='" + gadgetUrl + "?parent=" +
>> containerRelay
>> > +
>> > > > > "#rpctoken=" + secret + "'></iframe>";
>> > > > > +        gadgets.rpc.setAuthToken('gadget', secret);
>> > > > > +
>> > > > > +        initPerfTest();
>> > > > > +      };
>> > > > > +    </script>
>> > > > > +  </head>
>> > > > > +  <body onload="initTest();">
>> > > > > +    <div>gadgets.rpc Performance: Container Page</div><hr/>
>> > > > > +    <div>Test<br/>
>> > > > > +      <ul>
>> > > > > +        <li>Number of messages to send:
>> > > > > +          <select name="num_msgs" id="num_msgs">
>> > > > > +            <option value="1">1</option>
>> > > > > +            <option value="10">10</option>
>> > > > > +            <option value="100" selected>100</option>
>> > > > > +            <option value="1000">1000</option>
>> > > > > +          </select>
>> > > > > +        </li>
>> > > > > +        <li>Message size:
>> > > > > +          <select name="msg_size" id="msg_size">
>> > > > > +            <option value="10">10 B</option>
>> > > > > +            <option value="100">100 B</option>
>> > > > > +            <option value="1024" selected>1 kB</option>
>> > > > > +            <option value="10240">10 kB</option>
>> > > > > +            <option value="102400">100 kB</option>
>> > > > > +            <option value="1048576">1 MB</option>
>> > > > > +          </select>
>> > > > > +        </li>
>> > > > > +        <li>
>> > > > > +          <input type="button" value="Start The Test!"
>> > > > > onclick="runPerfTest('gadget');" />
>> > > > > +        </li>
>> > > > > +      </ul>
>> > > > > +    </div>
>> > > > > +    <div id="test_running" style="display:none;">
>> > > > > +      Running test...
>> > > > > +    </div>
>> > > > > +    <div id="results" style="display:none;">
>> > > > > +      Results: Gadget-to-Container<br/>
>> > > > > +      Messages: <span id="results_num_received"></span>, Bytes:
>> > <span
>> > > > > id="results_bytes_received"></span> <span
>> id="in_or_out"></span><br/>
>> > > > > +      Time elapsed for test run: <span
>> > > > id="results_time_used"></span><br/>
>> > > > > +      Messages/second: <span
>> id="results_msgs_per_sec"></span><br/>
>> > > > > +      Bytes/second: <span id="results_bytes_per_sec"></span>
>> > > > > +    </div>
>> > > > > +    <hr/>
>> > > > > +    <div>Gadget:</div>
>> > > > > +    <div id="container"></div>
>> > > > > +  </body>
>> > > > > +</html>
>> > > > >
>> > > > > Added:
>> > incubator/shindig/trunk/javascript/container/rpctest_gadget.html
>> > > > > URL:
>> > > > >
>> > > >
>> > >
>> >
>> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_gadget.html?rev=672393&view=auto
>> > > > >
>> > > > >
>> > > >
>> > >
>> >
>> ==============================================================================
>> > > > > ---
>> incubator/shindig/trunk/javascript/container/rpctest_gadget.html
>> > > > > (added)
>> > > > > +++
>> incubator/shindig/trunk/javascript/container/rpctest_gadget.html
>> > > Fri
>> > > > > Jun 27 14:01:20 2008
>> > > > > @@ -0,0 +1,49 @@
>> > > > > +<html>
>> > > > > +  <head>
>> > > > > +    <title>gadgets.rpc Performance Tests: Gadget</title>
>> > > > > +    <script src="../../js/rpc.js?c=1&debug=1"></script>
>> > > > > +    <script src="rpctest_perf.js"></script>
>> > > > > +    <script>
>> > > > > +      var containerRelay = window.location.search.substring(1);
>> > > > > +      gadgets.rpc.setRelayUrl(null, containerRelay);
>> > > > > +    </script>
>> > > > > +  </head>
>> > > > > +  <body onload="initPerfTest();">
>> > > > > +    <div>gadgets.rpc Performance: "Gadget" page</div><hr/>
>> > > > > +    <div>Test<br/>
>> > > > > +      <ul>
>> > > > > +        <li>Number of messages to send:
>> > > > > +          <select name="num_msgs" id="num_msgs">
>> > > > > +            <option value="1">1</option>
>> > > > > +            <option value="10">10</option>
>> > > > > +            <option value="100" selected>100</option>
>> > > > > +            <option value="1000">1000</option>
>> > > > > +          </select>
>> > > > > +        </li>
>> > > > > +        <li>Message size:
>> > > > > +          <select name="msg_size" id="msg_size">
>> > > > > +            <option value="10">10 B</option>
>> > > > > +            <option value="100">100 B</option>
>> > > > > +            <option value="1024" selected>1 kB</option>
>> > > > > +            <option value="10240">10 kB</option>
>> > > > > +            <option value="102400">100 kB</option>
>> > > > > +            <option value="1048576">1 MB</option>
>> > > > > +          </select>
>> > > > > +        </li>
>> > > > > +        <li>
>> > > > > +          <input type="button" value="Start The Test!"
>> > > > > onclick="runPerfTest();" />
>> > > > > +        </li>
>> > > > > +      </ul>
>> > > > > +    </div>
>> > > > > +    <div id="test_running" style="display:none;">
>> > > > > +      Running test...
>> > > > > +    </div>
>> > > > > +    <div id="results" style="display:none;">
>> > > > > +      Results: Gadget-to-Container<br/>
>> > > > > +      Messages: <span id="results_num_received"></span>, Bytes:
>> > <span
>> > > > > id="results_bytes_received"></span> <span
>> id="in_or_out"></span><br/>
>> > > > > +      Time elapsed for test run: <span
>> > > > id="results_time_used"></span><br/>
>> > > > > +      Messages/second: <span
>> id="results_msgs_per_sec"></span><br/>
>> > > > > +      Bytes/second: <span id="results_bytes_per_sec"></span>
>> > > > > +    </div>
>> > > > > +  </body>
>> > > > > +</html>
>> > > > >
>> > > > > Added:
>> incubator/shindig/trunk/javascript/container/rpctest_perf.js
>> > > > > URL:
>> > > > >
>> > > >
>> > >
>> >
>> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_perf.js?rev=672393&view=auto
>> > > > >
>> > > > >
>> > > >
>> > >
>> >
>> ==============================================================================
>> > > > > --- incubator/shindig/trunk/javascript/container/rpctest_perf.js
>> > > (added)
>> > > > > +++ incubator/shindig/trunk/javascript/container/rpctest_perf.js
>> Fri
>> > > Jun
>> > > > 27
>> > > > > 14:01:20 2008
>> > > > > @@ -0,0 +1,93 @@
>> > > > > +var perfStats = null;
>> > > > > +var currentRun = {};
>> > > > > +
>> > > > > +function perfService(message) {
>> > > > > +  if (perfStats.numResults++ === 0) {
>> > > > > +    perfStats.firstMsg = message; // stored since it has "real"
>> > start
>> > > > time
>> > > > > +  }
>> > > > > +  perfStats.bytesReceived += message.length;
>> > > > > +};
>> > > > > +
>> > > > > +function clearPerfStats(inOrOut) {
>> > > > > +  perfStats = {
>> > > > > +    numResults: 0,
>> > > > > +    bytesReceived: 0,
>> > > > > +    firstMsg: null
>> > > > > +  };
>> > > > > +
>> > > > > +  document.getElementById("in_or_out").innerHTML = inOrOut;
>> > > > > +
>> > > > > +  // hide results fields
>> > > > > +  document.getElementById("results").style.display = "none";
>> > > > > +};
>> > > > > +
>> > > > > +function completePerfStats() {
>> > > > > +  perfStats.timeEnded = new Date().getTime();
>> > > > > +
>> > > > > +  // get time started from the first sent message
>> > > > > +  perfStats.timeStarted = perfStats.firstMsg.substr(0,
>> > > > > perfStats.firstMsg.indexOf(':'));
>> > > > > +
>> > > > > +  var timeUsedMs = perfStats.timeEnded - perfStats.timeStarted;
>> > > > > +
>> > > > > +  // fill in fields
>> > > > > +  document.getElementById("results_num_received").innerHTML =
>> > > > > perfStats.numResults;
>> > > > > +  document.getElementById("results_bytes_received").innerHTML =
>> > > > > perfStats.bytesReceived;
>> > > > > +  document.getElementById("results_time_used").innerHTML =
>> > timeUsedMs
>> > > +
>> > > > > "ms";
>> > > > > +  document.getElementById("results_msgs_per_sec").innerHTML =
>> > > > > (perfStats.numResults / (timeUsedMs / 1000));
>> > > > > +  document.getElementById("results_bytes_per_sec").innerHTML =
>> > > > > (perfStats.bytesReceived / (timeUsedMs / 1000));
>> > > > > +  document.getElementById("test_running").style.display = "none";
>> > > > > +  document.getElementById("results").style.display = "";
>> > > > > +};
>> > > > > +
>> > > > > +function initPerfTest() {
>> > > > > +  clearPerfStats();
>> > > > > +  gadgets.rpc.register("perf_service", perfService);
>> > > > > +  gadgets.rpc.register("clear_perf_stats", clearPerfStats);
>> > > > > +  gadgets.rpc.register("complete_perf_stats", completePerfStats);
>> > > > > +};
>> > > > > +
>> > > > > +var alphabet =
>> > > > > "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
>> > > > _-*&(){}'";
>> > > > > +
>> > > > > +function sendPerfMessage() {
>> > > > > +  var msgToSend = currentRun.msg;
>> > > > > +  if (currentRun.curMsgId++ <= 1) {
>> > > > > +    var nowString = new Date().getTime() + ':';
>> > > > > +    msgToSend = nowString +
>> > > currentRun.msg.substring(nowString.length);
>> > > > > +  }
>> > > > > +
>> > > > > +  gadgets.rpc.call(currentRun.targetId, "perf_service", null,
>> > > > msgToSend);
>> > > > > +  if (currentRun.curMsgId < currentRun.endMsgId) {
>> > > > > +    // loop, giving up execution in case rpc technique demands it
>> > > > > +    window.setTimeout(sendPerfMessage, 0);
>> > > > > +  } else {
>> > > > > +    // send finisher
>> > > > > +    window.setTimeout(function() {
>> > > gadgets.rpc.call(currentRun.targetId,
>> > > > > "complete_perf_stats", null); }, 0);
>> > > > > +  }
>> > > > > +};
>> > > > > +
>> > > > > +function runPerfTest(targetId) {
>> > > > > +  document.getElementById("test_running").style.display = "";
>> > > > > +
>> > > > > +  // initialize the current run
>> > > > > +  var num_msgs = document.getElementById("num_msgs").value;
>> > > > > +  var msg_size = document.getElementById("msg_size").value;
>> > > > > +
>> > > > > +  currentRun.targetId = targetId;
>> > > > > +  currentRun.curMsgId = 0;
>> > > > > +  currentRun.endMsgId = num_msgs;
>> > > > > +
>> > > > > +  var msg = [];
>> > > > > +  for (var i = 0; i < msg_size; ++i) {
>> > > > > +    msg[i] =
>> > > alphabet.charAt(Math.round(Math.random(alphabet.length)));
>> > > > > +  }
>> > > > > +  currentRun.msg = msg.join('');
>> > > > > +
>> > > > > +  // clear local perf stats
>> > > > > +  clearPerfStats("(outbound)");
>> > > > > +
>> > > > > +  // clear target perf stats
>> > > > > +  gadgets.rpc.call(targetId, "clear_perf_stats", null,
>> "(inbound)");
>> > > > > +
>> > > > > +  // kick off the send loop
>> > > > > +  sendPerfMessage();
>> > > > > +};
>> > > > >
>> > > > >
>> > > > >
>> > > >
>> > >
>> >
>>
>
>

Re: svn commit: r672393 - in /incubator/shindig/trunk: features/rpc/ java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ javascript/container/

Posted by John Hjelmstad <fa...@google.com>.
How's this for a compromise: I'll add a param that disables the new
technique by default, and allows it to be used if preferred. The rest is
refactoring and documentation. Given that's the bulk of the code change in
terms of volume, I didn't create a JIRA issue for it. I'm happy to do so to
enable it longer-term, however. Thoughts?

John

On Fri, Jun 27, 2008 at 4:27 PM, Kevin Brown <et...@google.com> wrote:

> On Fri, Jun 27, 2008 at 3:45 PM, John Hjelmstad <fa...@google.com> wrote:
>
> > As indicated in the CL, I tested this on half a dozen browsers. We don't
> > have unit tests for it. I've also sent the code and inquiries about the
> > technique to several poeple, some of whom expressed concerns but no
> ability
> > to actively exploit the code.
>
>
> Anyone on this mailing list? Either of the people I mentioned?
>
>
> > The main concern was around VBScript
> > knowledge, which few people I know extensively have. That was true of
> IFPC
> > when it was originally released as well. If any attacks are found, they
> > will
> > be actively closed or the technique will be removed. Would you prefer
> that
> > no code, irrespective of its value, ever be submitted? It's possible for
> > any
> > code to have bugs, and a lot of code to have security holes. If found,
> > they're fixed.
>
>
> Yes, but this is code that uses a security technique that is highly
> questionable and has been flagged as potentially problematic by several
> people. Therefore, it needs more scrutiny. I won't deploy this on any of
> our
> production systems until I get an OK from people who are actually familiar
> with the problems in question have chimed in.
>
> This is also a very large code change, and, per policy, large code changes
> need JIRA tickets and feedback from other people. There are hundreds (if
> not
> thousands) of people using Shindig today and the code is deployed to
> hundreds of millions of users.
>
>
> >
> > John
> >
> > On Fri, Jun 27, 2008 at 3:38 PM, Kevin Brown <et...@google.com> wrote:
> >
> > > Please actually test your code or send it to other shindig committers
> > > before
> > > you commit it. Your change to GadgetRenderingTask breaks proxying and
> > > makeRequest in several cases. Fix it.
> > >
> > > Have you run this by Brian and/or Mike Samuel? They both expressed
> doubts
> > > about whether this mechanism was actually secure.
> > >
> > > On Fri, Jun 27, 2008 at 2:06 PM, John Hjelmstad <fa...@google.com>
> > wrote:
> > >
> > > > +jschorr in case there are comments.
> > > >
> > > > On Fri, Jun 27, 2008 at 2:01 PM, <jo...@apache.org> wrote:
> > > >
> > > > > Author: johnh
> > > > > Date: Fri Jun 27 14:01:20 2008
> > > > > New Revision: 672393
> > > > >
> > > > > URL: http://svn.apache.org/viewvc?rev=672393&view=rev
> > > > > Log:
> > > > > Cleaned up gadgets.rpc library implementation, and implemented fast
> > IE
> > > > > transport layer.
> > > > >
> > > > > Credit:
> > > > >        Joseph Schorr (jschorr@google.com) and I implemented this
> > > > together,
> > > > > but he
> > > > >        really did all the hard work, as well as developing the NIX
> > > > > technique's
> > > > >        fundamentals. Huge thanks to Joseph!
> > > > >
> > > > > Details:
> > > > >        * Created a new relayChannel for Firefox frameElement
> > technique,
> > > > >          making its implementation more linear to read.
> > > > >        * Consolidated all transport-specific code in
> setupChannel(),
> > > > > setupFrame(),
> > > > >          and callX(), where X = relay type. This refactoring makes
> > > setup
> > > > > and use
> > > > >          of each transport more clear, and makes it possible in
> later
> > > > > revisions
> > > > >          to separate out each method as its own class. In theory,
> if
> > we
> > > > can
> > > > > trust
> > > > >          things like User-Agent headers, we can use this to reduce
> > > rpc.js
> > > > > code
> > > > >          size significantly by only including the needed
> > transport(s).
> > > > >        * Implemented "NIX" - Native IE XDC. This method exploits
> the
> > > fact
> > > > >          that in IE6 and IE7 the window.opener property can be both
> > set
> > > > and
> > > > >          read across domain boundaries for an IFRAME, and that any
> > > object
> > > > >          can be passed through it. In particular, functions
> > > implementing
> > > > >          message passing can be passed. These can't be JS
> functions,
> > > > > however,
> > > > >          since those can leak sender context. So, VBScript (COM)
> > > objects
> > > > >          wrapping these are passed to maintain context isolation.
> > > > >          - Requires for IE6/7 that rpc.js be included in the
> > container
> > > > >            at load time, not dynamically. TODO added to consider
> > > > detecting
> > > > >            whether dynamic loading is happening (at which point we
> > > should
> > > > >            fall back to IFPC).
> > > > >          - Message channel handshake *and* message passing
> validated
> > > > >            using authToken, as with other channels.
> > > > >          - Impl requires that gadget call container first - else
> IFPC
> > > > >            is used. This is the same as the frameElement technique.
> > We
> > > > >            could add a setInterval()-based initialization routine
> > later
> > > > >            if needed. To date, the only gadgets.rpc calls made from
> > > > >            container to gadget without gadget to container first
> are
> > > > >            in theory pubsub messages.
> > > > >          - Extensive documentation on this technique and the others
> > > > >            added to the comments, since they're stripped out in the
> > > > >            majority case.
> > > > >        * Implemented quick-and-dirty performance testing page
> > verifying
> > > > >          the library works and giving basic performance metrics.
> > > > >          This needs to be improved! But does the trick for now...
> > > > >
> > > > > Testing/Data:
> > > > >
> > > > > Library verified on the following browsers with the RPC test page.
> > For
> > > > > each,
> > > > > a "bandwidth" measure was taken by averaging the results of 6 G2C
> and
> > > C2G
> > > > > runs
> > > > > with 100 messages being passed of 1K apiece. Units are 1K
> > messages/sec.
> > > > > The "latency" value is the average amount of time taken, over 6 G2C
> > and
> > > > C2G
> > > > > runs,
> > > > > to send 1 1K message. Happily, for all browsers save Safari (soon
> due
> > > to
> > > > > get
> > > > > window.postMessage), latency is around or below time required to
> > > achieve
> > > > > a cross-domain call without user-perceived delay.
> > > > >
> > > > >        * Firefox 3 (WPM) - bandwidth: 88 kB/sec; latency: 18ms.
> > > > >        * Firefox 2 (FE)  - bandwidth: 80 kB/sec; latency: 15ms.
> > > > >        * Opera 9   (WPM) - bandwidth: 85 kB/sec; latency: 7ms.
> > > > >        * IE6       (NIX) - bandwidth: 64 kB/sec; latency: 18ms.
> > > > >        * IE7       (NIX) - bandwidth: 64 kB/sec; latency: 22ms.
> > > > >        * Safari 3 (IFPC) - bandwidth: ?6-8kB/sec; latency:
> > ?100-200ms.
> > > > >          - Safari is somewhat flaky with the RPC test page, before
> > > > >            and after the rpc.js change. Multiple messages seem to
> > > > >            confuse it from time to time.
> > > > >
> > > > >
> > > > > Added:
> > > > >
> >  incubator/shindig/trunk/javascript/container/rpctest_container.html
> > > > >    incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > > > >    incubator/shindig/trunk/javascript/container/rpctest_perf.js
> > > > > Modified:
> > > > >    incubator/shindig/trunk/features/rpc/rpc.js
> > > > >
> > > > >
> > > >
> > >
> >
>  incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > > >
> > > > > Modified: incubator/shindig/trunk/features/rpc/rpc.js
> > > > > URL:
> > > > >
> > > >
> > >
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/features/rpc/rpc.js?rev=672393&r1=672392&r2=672393&view=diff
> > > > >
> > > > >
> > > >
> > >
> >
> ==============================================================================
> > > > > --- incubator/shindig/trunk/features/rpc/rpc.js (original)
> > > > > +++ incubator/shindig/trunk/features/rpc/rpc.js Fri Jun 27 14:01:20
> > > 2008
> > > > > @@ -18,7 +18,7 @@
> > > > >
> > > > >  /**
> > > > >  * @fileoverview Remote procedure call library for
> > gadget-to-container,
> > > > > - * container-to-gadget, and gadget-to-gadget communication.
> > > > > + * container-to-gadget, and gadget-to-gadget (thru container)
> > > > > communication.
> > > > >  */
> > > > >
> > > > >  var gadgets = gadgets || {};
> > > > > @@ -29,6 +29,30 @@
> > > > >  * @name gadgets.rpc
> > > > >  */
> > > > >  gadgets.rpc = function() {
> > > > > +  // General constants.
> > > > > +  var CALLBACK_NAME = '__cb';
> > > > > +  var DEFAULT_NAME = '';
> > > > > +
> > > > > +  // Consts for FrameElement.
> > > > > +  var FE_G2C_CHANNEL = '__g2c_rpc';
> > > > > +  var FE_C2G_CHANNEL = '__c2g_rpc';
> > > > > +
> > > > > +  // Consts for NIX. VBScript doesn't
> > > > > +  // allow items to start with _ for some reason,
> > > > > +  // so we need to make these names quite unique, as
> > > > > +  // they will go into the global namespace.
> > > > > +  var NIX_WRAPPER = 'GRPC____NIXVBS_wrapper';
> > > > > +  var NIX_GET_WRAPPER = 'GRPC____NIXVBS_get_wrapper';
> > > > > +  var NIX_HANDLE_MESSAGE = 'GRPC____NIXVBS_handle_message';
> > > > > +  var NIX_CREATE_CHANNEL = 'GRPC____NIXVBS_create_channel';
> > > > > +  var NIX_ID_FIELD = 'GRPC____NIXVBS_container';
> > > > > +
> > > > > +  // JavaScript reference to the NIX VBScript wrappers.
> > > > > +  // Gadgets will have but a single channel under
> > > > > +  // nix_channels['..'] while containers will have a channel
> > > > > +  // per gadget stored under the gadget's ID.
> > > > > +  var nix_channels = {};
> > > > > +
> > > > >   var services = {};
> > > > >   var iframePool = [];
> > > > >   var relayUrl = {};
> > > > > @@ -36,28 +60,135 @@
> > > > >   var authToken = {};
> > > > >   var callId = 0;
> > > > >   var callbacks = {};
> > > > > +  var setup = {};
> > > > > +
> > > > > +  var params = {};
> > > > > +
> > > > > +  // Load the authentication token for speaking to the container
> > > > > +  // from the gadget's parameters, or default to '0' if not found.
> > > > > +  if (gadgets.util) {
> > > > > +    params = gadgets.util.getUrlParameters();
> > > > > +  }
> > > > >
> > > > > -  var params = gadgets.util.getUrlParameters();
> > > > >   authToken['..'] = params.rpctoken || params.ifpctok || 0;
> > > > >
> > > > > -  // Pick the most efficient RPC relay mechanism
> > > > > -  var relayChannel = typeof document.postMessage === 'function' ?
> > > 'dpm'
> > > > :
> > > > > -                     typeof window.postMessage === 'function' ?
> > 'wpm'
> > > :
> > > > > +  // Pick the most efficient RPC relay mechanism:
> > > > > +  //
> > > > > +  // * For those browsers that support native messaging (various
> > > > > implementations
> > > > > +  //   of the HTML5 postMessage method), use that. Officially
> > defined
> > > at
> > > > > +  //
> > > > >
> > http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html
> > > .
> > > > > +  //
> > > > > +  //   postMessage is a native implementation of XDC. A page
> > registers
> > > > > that
> > > > > +  //   it would like to receive messages by listening the the
> > > "message"
> > > > > event
> > > > > +  //   on the window (document in DPM) object. In turn, another
> page
> > > can
> > > > > +  //   raise that event by calling window.postMessage
> > > > > (document.postMessage
> > > > > +  //   in DPM) with a string representing the message and a string
> > > > > +  //   indicating on which domain the receiving page must be to
> > > receive
> > > > > +  //   the message. The target page will then have its "message"
> > event
> > > > > raised
> > > > > +  //   if the domain matches and can, in turn, check the origin of
> > the
> > > > > message
> > > > > +  //   and process the data contained within.
> > > > > +  //
> > > > > +  //     wpm: postMessage on the window object.
> > > > > +  //        - Internet Explorer 8+
> > > > > +  //        - Safari (latest nightlies as of 26/6/2008)
> > > > > +  //        - Firefox 3+
> > > > > +  //        - Opera 9+
> > > > > +  //
> > > > > +  //     dpm: postMessage on the document object.
> > > > > +  //        - Opera 8+
> > > > > +  //
> > > > > +  // * For Internet Explorer before version 8, the security model
> > > allows
> > > > a
> > > > > +  //   child to set the value of the "opener" property on the
> parent
> > > > > window
> > > > > +  //   and vice versa. This method is dubbed "Native IE XDC"
> (NIX).
> > > > > +  //
> > > > > +  //   This method works by placing a handler object in the
> "opener"
> > > > > property
> > > > > +  //   of a gadget when the container sets up the authentication
> > > > > information
> > > > > +  //   for that gadget (by calling setAuthToken(...)). At that
> > point,
> > > a
> > > > > NIX
> > > > > +  //   wrapper is created and placed into the gadget by calling
> > > > > +  //   theframe.contentWindow.opener = wrapper. Note that as a
> > result,
> > > > NIX
> > > > > can
> > > > > +  //   only be used by a container to call a particular gadget
> > *after*
> > > > > that
> > > > > +  //   gadget has called the container at least once via NIX.
> > > > > +  //
> > > > > +  //   The NIX wrappers in this RPC implementation are instances
> of
> > a
> > > > > VBScript
> > > > > +  //   class that is created when this implementation loads. The
> > > reason
> > > > > for
> > > > > +  //   using a VBScript class stems from the fact that any object
> > can
> > > be
> > > > > passed
> > > > > +  //   into the opener property.
> > > > > +  //   While this is a good thing, as it lets us pass functions
> and
> > > > setup
> > > > > a true
> > > > > +  //   bidirectional channel via callbacks, it opens a potential
> > > > security
> > > > > hole
> > > > > +  //   by which the other page can get ahold of the "window" or
> > > > "document"
> > > > > +  //   objects in the parent page and in turn wreak havok. This is
> > due
> > > > to
> > > > > the
> > > > > +  //   fact that any JS object useful for establishing such a
> > > > > bidirectional
> > > > > +  //   channel (such as a function) can be used to access a
> function
> > > > > +  //   (eg. obj.toString, or a function itself) created in a
> > specific
> > > > > context,
> > > > > +  //   in particular the global context of the sender. Suppose
> > > container
> > > > > +  //   domain C passes object obj to gadget on domain G. Then the
> > > gadget
> > > > > can
> > > > > +  //   access C's global context using:
> > > > > +  //   var parentWindow = (new obj.toString.constructor("return
> > > > > window;"))();
> > > > > +  //   Nulling out all of obj's properties doesn't fix this, since
> > IE
> > > > > helpfully
> > > > > +  //   restores them to their original values if you do something
> > > like:
> > > > > +  //   delete obj.toString; delete obj.toString;
> > > > > +  //   Thus, we wrap the necessary functions and information
> inside
> > a
> > > > > VBScript
> > > > > +  //   object. VBScript objects in IE, like DOM objects, are in
> fact
> > > COM
> > > > > +  //   wrappers when used in JavaScript, so we can safely pass
> them
> > > > around
> > > > > +  //   without worrying about a breach of context while at the
> same
> > > time
> > > > > +  //   allowing them to act as a pass-through mechanism for
> > > information
> > > > > +  //   and function calls. The implementation details of this
> > VBScript
> > > > > wrapper
> > > > > +  //   can be found in the setupChannel() method below.
> > > > > +  //
> > > > > +  //     nix: Internet Explorer-specific window.opener trick.
> > > > > +  //       - Internet Explorer 6
> > > > > +  //       - Internet Explorer 7
> > > > > +  //
> > > > > +  // * For Gecko-based browsers, the security model allows a child
> > to
> > > > call
> > > > > a
> > > > > +  //   function on the frameElement of the iframe, even if the
> child
> > > is
> > > > in
> > > > > +  //   a different domain. This method is dubbed "frameElement"
> > (fe).
> > > > > +  //
> > > > > +  //   The ability to add and call such functions on the
> > frameElement
> > > > > allows
> > > > > +  //   a bidirectional channel to be setup via the adding of
> simple
> > > > > function
> > > > > +  //   references on the frameElement object itself. In this
> > > > > implementation,
> > > > > +  //   when the container sets up the authentication information
> for
> > > > that
> > > > > gadget
> > > > > +  //   (by calling setAuth(...)) it as well adds a special
> function
> > on
> > > > the
> > > > > +  //   gadget's iframe. This function can then be used by the
> gadget
> > > to
> > > > > send
> > > > > +  //   messages to the container. In turn, when the gadget tries
> to
> > > send
> > > > a
> > > > > +  //   message, it checks to see if this function has its own
> > function
> > > > > stored
> > > > > +  //   that can be used by the container to call the gadget. If
> not,
> > > the
> > > > > +  //   function is created and subsequently used by the container.
> > > > > +  //   Note that as a result, FE can only be used by a container
> to
> > > call
> > > > a
> > > > > +  //   particular gadget *after* that gadget has called the
> > container
> > > at
> > > > > +  //   least once via FE.
> > > > > +  //
> > > > > +  //     fe: Gecko-specific frameElement trick.
> > > > > +  //        - Firefox 1+
> > > > > +  //
> > > > > +  // * For all others, we have a fallback mechanism known as
> "ifpc".
> > > > IFPC
> > > > > +  //   exploits the fact that while same-origin policy prohibits a
> > > frame
> > > > > from
> > > > > +  //   accessing members on a window not in the same domain, that
> > > frame
> > > > > can,
> > > > > +  //   however, navigate the window heirarchy (via parent). This
> is
> > > > > exploited by
> > > > > +  //   having a page on domain A that wants to talk to domain B
> > create
> > > > an
> > > > > iframe
> > > > > +  //   on domain B pointing to a special relay file and with a
> > message
> > > > > encoded
> > > > > +  //   after the hash (#). This relay, in turn, finds the page on
> > > domain
> > > > > B, and
> > > > > +  //   can call a receipt function with the message given to it.
> The
> > > > relay
> > > > > URL
> > > > > +  //   used by each caller is set via the
> > gadgets.rpc.setRelayUrl(..)
> > > > and
> > > > > +  //   *must* be called before the call method is used.
> > > > > +  //
> > > > > +  //     ifpc: Iframe-based method, utilizing a relay page, to
> send
> > a
> > > > > message.
> > > > > +  //
> > > > > +  var relayChannel = typeof window.postMessage === 'function' ?
> > 'wpm'
> > > :
> > > > > +                    typeof document.postMessage === 'function' ?
> > 'dpm'
> > > :
> > > > > +                    window.ActiveXObject ? 'nix' :
> > > > > +                     navigator.product === 'Gecko' ? 'fe' :
> > > > >                      'ifpc';
> > > > > -  if (relayChannel === 'dpm' || relayChannel === 'wpm') {
> > > > > -    window.addEventListener('message', function(packet) {
> > > > > -      // TODO validate packet.domain for security reasons
> > > > > -      process(gadgets.json.parse(packet.data));
> > > > > -    }, false);
> > > > > -  }
> > > > >
> > > > > -  // Default RPC handler
> > > > > -  services[''] = function() {
> > > > > +  // Conduct any setup necessary for the chosen channel.
> > > > > +  setupChannel();
> > > > > +
> > > > > +  // Create the Default RPC handler.
> > > > > +  services[DEFAULT_NAME] = function() {
> > > > >     throw new Error('Unknown RPC service: ' + this.s);
> > > > >   };
> > > > >
> > > > > -  // Special RPC handler for callbacks
> > > > > -  services['__cb'] = function(callbackId, result) {
> > > > > +  // Create a Special RPC handler for callbacks.
> > > > > +  services[CALLBACK_NAME] = function(callbackId, result) {
> > > > >     var callback = callbacks[callbackId];
> > > > >     if (callback) {
> > > > >       delete callbacks[callbackId];
> > > > > @@ -66,6 +197,147 @@
> > > > >   };
> > > > >
> > > > >   /**
> > > > > +   * Conducts any initial global work necessary to setup the
> > > > > +   * channel type chosen.
> > > > > +   */
> > > > > +  function setupChannel() {
> > > > > +    // If the channel type is one of the native
> > > > > +    // postMessage based ones, setup the handler to receive
> > > > > +    // messages.
> > > > > +    if (relayChannel === 'dpm' || relayChannel === 'wpm') {
> > > > > +      window.addEventListener('message', function(packet) {
> > > > > +        // TODO validate packet.domain for security reasons
> > > > > +        process(gadgets.json.parse(packet.data));
> > > > > +      }, false);
> > > > > +    }
> > > > > +
> > > > > +    // If the channel type is NIX, we need to ensure the
> > > > > +    // VBScript wrapper code is in the page and that the
> > > > > +    // global Javascript handlers have been set.
> > > > > +    if (relayChannel === 'nix') {
> > > > > +      // VBScript methods return a type of 'unknown' when
> > > > > +      // checked via the typeof operator in IE. Fortunately
> > > > > +      // for us, this only applies to COM objects, so we
> > > > > +      // won't see this for a real Javascript object.
> > > > > +      if (typeof window[NIX_GET_WRAPPER] !== 'unknown') {
> > > > > +        window[NIX_HANDLE_MESSAGE] = function(data) {
> > > > > +          process(gadgets.json.parse(data));
> > > > > +        };
> > > > > +
> > > > > +        window[NIX_CREATE_CHANNEL] = function(name, channel,
> token)
> > {
> > > > > +          // Verify the authentication token of the gadget trying
> > > > > +          // to create a channel for us.
> > > > > +          if (authToken[name] == token) {
> > > > > +            nix_channels[name] = channel;
> > > > > +          }
> > > > > +        };
> > > > > +
> > > > > +        // Inject the VBScript code needed.
> > > > > +        var vbscript =
> > > > > +          '<scr' + 'ipt language="VBScript">'
> > > > > +          // We create a class to act as a wrapper for
> > > > > +          // a Javascript call, to prevent a break in of
> > > > > +          // the context.
> > > > > +          + 'Class ' + NIX_WRAPPER + '\n '
> > > > > +
> > > > > +          // An internal member for keeping track of the
> > > > > +          // name of the document (container or gadget)
> > > > > +          // for which this wrapper is intended. For
> > > > > +          // those wrappers created by gadgets, this is not
> > > > > +          // used (although it is set to "..")
> > > > > +          + 'Private m_Intended\n'
> > > > > +
> > > > > +          // Method for internally setting the value
> > > > > +          // of the m_Intended property.
> > > > > +          + 'Public Sub SetIntendedName(name)\n '
> > > > > +          + 'm_Intended = name\n'
> > > > > +          + 'End Sub\n'
> > > > > +
> > > > > +          // A wrapper method which actually causes a
> > > > > +          // message to be sent to the other context.
> > > > > +          + 'Public Sub SendMessage(data)\n '
> > > > > +          + NIX_HANDLE_MESSAGE + '(data)\n'
> > > > > +          + 'End Sub\n'
> > > > > +
> > > > > +          // Method for setting up the container->gadget
> > > > > +          // channel. Not strictly needed in the gadget's
> > > > > +          // wrapper, but no reason to get rid of it. Note here
> > > > > +          // that we pass the intended name to the
> > NIX_CREATE_CHANNEL
> > > > > +          // method so that it can save the channel in the proper
> > > place
> > > > > +          // *and* verify the channel via the authentication token
> > > > passed
> > > > > +          // here.
> > > > > +          + 'Public Sub CreateChannel(channel, auth)\n '
> > > > > +          + 'Call ' + NIX_CREATE_CHANNEL + '(m_Intended, channel,
> > > > auth)\n'
> > > > > +          + 'End Sub\n'
> > > > > +
> > > > > +          // An empty field with a unique identifier to
> > > > > +          // prevent the code from confusing this wrapper
> > > > > +          // with a run-of-the-mill value found in window.opener.
> > > > > +          + 'Public Sub ' + NIX_ID_FIELD + '()\n '
> > > > > +          + 'End Sub\n'
> > > > > +          + 'End Class\n '
> > > > > +
> > > > > +          // Function to get a reference to the wrapper.
> > > > > +          + 'Function ' + NIX_GET_WRAPPER + '(name)\n'
> > > > > +          + 'Dim wrap\n'
> > > > > +          + 'Set wrap = New ' + NIX_WRAPPER + '\n'
> > > > > +          + 'wrap.SetIntendedName name\n'
> > > > > +          + 'Set ' + NIX_GET_WRAPPER + ' = wrap\n'
> > > > > +          + 'End Function'
> > > > > +          + '</scr' + 'ipt>'
> > > > > +
> > > > > +          // Resets the "default" scripting language in IE back
> > > > > +          // to Javascript. This is needed or else any script
> > > > > +          // tags without a proper Language="..." will be treated
> as
> > > > > +          // VBScript once this code is added to the document.
> > > > > +          + '<scri' + 'pt language="JScript"></scr' + 'ipt>';
> > > > > +
> > > > > +        // Note that this code can only be run once the document
> has
> > > > > +        // fully loaded.
> > > > > +        // TODO: Perhaps add some sort of check here for this?
> > > > > +        document.write(vbscript);
> > > > > +      }
> > > > > +    }
> > > > > +  }
> > > > > +
> > > > > +  /**
> > > > > +   * Conducts any frame-specific work necessary to setup
> > > > > +   * the channel type chosen. This method is called when
> > > > > +   * the container page first registers the gadget in the
> > > > > +   * RPC mechanism. Gadgets, in turn, will complete the setup
> > > > > +   * of the channel once they send their first messages.
> > > > > +   */
> > > > > +  function setupFrame(frameId) {
> > > > > +    if (setup[frameId]) {
> > > > > +      return;
> > > > > +    }
> > > > > +
> > > > > +    if (relayChannel === 'fe') {
> > > > > +      try {
> > > > > +        var frame = document.getElementById(frameId);
> > > > > +        frame[FE_G2C_CHANNEL] = function(args) {
> > > > > +          process(gadgets.json.parse(args));
> > > > > +        };
> > > > > +      } catch (e) {
> > > > > +        // Something went wrong. System will fallback to
> > > > > +        // IFPC.
> > > > > +      }
> > > > > +    }
> > > > > +
> > > > > +    if (relayChannel === 'nix') {
> > > > > +      try {
> > > > > +        var frame = document.getElementById(frameId);
> > > > > +        frame.contentWindow.opener =
> > window[NIX_GET_WRAPPER](frameId);
> > > > > +      } catch (e) {
> > > > > +        // Something went wrong. System will fallback to
> > > > > +        // IFPC.
> > > > > +      }
> > > > > +    }
> > > > > +
> > > > > +    setup[frameId] = true;
> > > > > +  }
> > > > > +
> > > > > +  /**
> > > > >    * Encodes arguments for the legacy IFPC wire format.
> > > > >    *
> > > > >    * @param {Object} args
> > > > > @@ -86,8 +358,17 @@
> > > > >    * @private
> > > > >    */
> > > > >   function process(rpc) {
> > > > > +    //
> > > > > +    // RPC object contents:
> > > > > +    //   s: Service Name
> > > > > +    //   f: From
> > > > > +    //   c: The callback ID or 0 if none.
> > > > > +    //   a: The arguments for this RPC call.
> > > > > +    //   t: The authentication token.
> > > > > +    //
> > > > >     if (rpc && typeof rpc.s === 'string' && typeof rpc.f ===
> 'string'
> > > &&
> > > > >         rpc.a instanceof Array) {
> > > > > +
> > > > >       // Validate auth token.
> > > > >       if (authToken[rpc.f]) {
> > > > >         // We allow type coercion here because all the url params
> are
> > > > > strings.
> > > > > @@ -96,43 +377,158 @@
> > > > >         }
> > > > >       }
> > > > >
> > > > > -      // The Gecko engine used by FireFox etc. allows an IFrame to
> > > > > directly call
> > > > > -      // methods on the frameElement property added by the
> container
> > > > page
> > > > > even
> > > > > -      // if their domains don't match.
> > > > > -      // Here we try to set up a relay channel using the
> > frameElement
> > > > > technique
> > > > > -      // to greatly reduce the latency of cross-domain calls if
> the
> > > > > postMessage
> > > > > -      // method is not supported.
> > > > > -      if (relayChannel === 'ifpc') {
> > > > > -        if (rpc.f === '..') {
> > > > > -          // Container-to-gadget call
> > > > > -          try {
> > > > > -            var fel = window.frameElement;
> > > > > -            if (typeof fel.__g2c_rpc === 'function' &&
> > > > > -                typeof fel.__g2c_rpc.__c2g_rpc != 'function') {
> > > > > -              fel.__g2c_rpc.__c2g_rpc = function(args) {
> > > > > -                process(gadgets.json.parse(args));
> > > > > -              };
> > > > > -            }
> > > > > -          } catch (e) {
> > > > > -          }
> > > > > -        } else {
> > > > > -          // Gadget-to-container call
> > > > > -          var iframe = document.getElementById(rpc.f);
> > > > > -          if (iframe && typeof iframe.__g2c_rpc != 'function') {
> > > > > -            iframe.__g2c_rpc = function(args) {
> > > > > +      // Call the requested RPC service.
> > > > > +      var result = (services[rpc.s] ||
> > > > > +                    services[DEFAULT_NAME]).apply(rpc, rpc.a);
> > > > > +
> > > > > +      // If there is a callback for this service, initiate it as
> > well.
> > > > > +      if (rpc.c) {
> > > > > +        gadgets.rpc.call(rpc.f, CALLBACK_NAME, null, rpc.c,
> result);
> > > > > +      }
> > > > > +    }
> > > > > +  }
> > > > > +
> > > > > +  /**
> > > > > +   * Attempts to conduct an RPC call to the specified
> > > > > +   * target with the specified data via the NIX
> > > > > +   * method. If this method fails, the system attempts again
> > > > > +   * using the known default of IFPC.
> > > > > +   *
> > > > > +   * @param {String} targetId Module Id of the RPC service
> provider.
> > > > > +   * @param {String} from Module Id of the calling provider.
> > > > > +   * @param {Object} rpcData The RPC data for this call.
> > > > > +   */
> > > > > +  function callNIX(targetId, from, rpcData) {
> > > > > +    try {
> > > > > +      if (from != '..') {
> > > > > +        // Call from gadget to the container.
> > > > > +        var handler = nix_channels['..'];
> > > > > +
> > > > > +        // If the gadget has yet to retrieve a reference to
> > > > > +        // the NIX handler, try to do so now. We don't do a
> > > > > +        // typeof(window.opener[NIX_ID_FIELD]) check here
> > > > > +        // because it means accessing that field on the COM
> object,
> > > > which,
> > > > > +        // being an internal function reference, is not allowed.
> > > > > +        // "in" works because it merely checks for the prescence
> of
> > > > > +        // the key, rather than actually accessing the object's
> > > > property.
> > > > > +        if (!handler && window.opener && NIX_ID_FIELD in
> > > window.opener)
> > > > {
> > > > > +          handler = nix_channels['..'] = window.opener;
> > > > > +
> > > > > +          // Create the channel to the parent/container. We pass
> > both
> > > > our
> > > > > +          // own wrapper and our authentication token for
> > > verification.
> > > > > +          handler.CreateChannel(window[NIX_GET_WRAPPER]('..'),
> > > > > authToken['..']);
> > > > > +        }
> > > > > +
> > > > > +        // If we have a handler, call it.
> > > > > +        if (handler) {
> > > > > +          handler.SendMessage(rpcData);
> > > > > +          return;
> > > > > +        }
> > > > > +      } else {
> > > > > +        // Call from container to a gadget[targetId].
> > > > > +
> > > > > +        // If we have a handler, call it.
> > > > > +        if (nix_channels[targetId]) {
> > > > > +          nix_channels[targetId].SendMessage(rpcData);
> > > > > +          return;
> > > > > +        }
> > > > > +      }
> > > > > +    } catch (e) {
> > > > > +    }
> > > > > +
> > > > > +    // If we have reached this point, something has failed
> > > > > +    // with the NIX method, so we default to using
> > > > > +    // IFPC for this call.
> > > > > +    callIFPC(targetId, from, rpcData);
> > > > > +  }
> > > > > +
> > > > > +  /**
> > > > > +   * Attempts to conduct an RPC call to the specified
> > > > > +   * target with the specified data via the FrameElement
> > > > > +   * method. If this method fails, the system attempts again
> > > > > +   * using the known default of IFPC.
> > > > > +   *
> > > > > +   * @param {String} targetId Module Id of the RPC service
> provider.
> > > > > +   * @param {String} from Module Id of the calling provider.
> > > > > +   * @param {Object} rpcData The RPC data for this call.
> > > > > +   */
> > > > > +  function callFrameElement(targetId, from, rpcData) {
> > > > > +    try {
> > > > > +      if (from != '..') {
> > > > > +        // Call from gadget to the container.
> > > > > +        var fe = window.frameElement;
> > > > > +
> > > > > +        if (typeof fe[FE_G2C_CHANNEL] === 'function') {
> > > > > +          // Complete the setup of the FE channel if need be.
> > > > > +          if (typeof fe[FE_G2C_CHANNEL][FE_C2G_CHANNEL] !==
> > > 'function')
> > > > {
> > > > > +            fe[FE_G2C_CHANNEL][FE_C2G_CHANNEL] = function(args) {
> > > > >               process(gadgets.json.parse(args));
> > > > >             };
> > > > >           }
> > > > > +
> > > > > +          // Conduct the RPC call.
> > > > > +          fe[FE_G2C_CHANNEL](rpcData);
> > > > > +          return;
> > > > >         }
> > > > > -      }
> > > > > +      } else {
> > > > > +        // Call from container to gadget[targetId].
> > > > > +        var frame = document.getElementById(targetId);
> > > > >
> > > > > -      var result = (services[rpc.s] || services['']).apply(rpc,
> > > rpc.a);
> > > > > -      if (rpc.c) {
> > > > > -        gadgets.rpc.call(rpc.f, '__cb', null, rpc.c, result);
> > > > > +        if (typeof frame[FE_G2C_CHANNEL] === 'function' &&
> > > > > +            typeof frame[FE_G2C_CHANNEL][FE_C2G_CHANNEL] ===
> > > 'function')
> > > > {
> > > > > +
> > > > > +          // Conduct the RPC call.
> > > > > +          frame[FE_G2C_CHANNEL][FE_C2G_CHANNEL](rpcData);
> > > > > +          return;
> > > > > +        }
> > > > >       }
> > > > > +    } catch (e) {
> > > > > +    }
> > > > > +
> > > > > +    // If we have reached this point, something has failed
> > > > > +    // with the FrameElement method, so we default to using
> > > > > +    // IFPC for this call.
> > > > > +    callIFPC(targetId, from, rpcData);
> > > > > +  }
> > > > > +
> > > > > +  /**
> > > > > +   * Conducts an RPC call to the specified
> > > > > +   * target with the specified data via the IFPC
> > > > > +   * method.
> > > > > +   *
> > > > > +   * @param {String} targetId Module Id of the RPC service
> provider.
> > > > > +   * @param {String} from Module Id of the calling provider.
> > > > > +   * @param {Object} rpcData The RPC data for this call.
> > > > > +   */
> > > > > +  function callIFPC(targetId, from, rpcData) {
> > > > > +    // Retrieve the relay file used by IFPC. Note that
> > > > > +    // this must be set before the call, and so we conduct
> > > > > +    // an extra check to ensure it is not blank.
> > > > > +    var relay = gadgets.rpc.getRelayUrl(targetId);
> > > > > +
> > > > > +    if (!relay) {
> > > > > +      throw new Error('No relay file assigned for IFPC');
> > > > >     }
> > > > > +
> > > > > +    // The RPC mechanism supports two formats for IFPC (legacy and
> > > > > current).
> > > > > +    var src = null;
> > > > > +    if (useLegacyProtocol[targetId]) {
> > > > > +      // Format:
> > > #iframe_id&callId&num_packets&packet_num&block_of_data
> > > > > +      src = [relay, '#', encodeLegacyData([from, callId, 1, 0,
> > > > > +             encodeLegacyData([from, serviceName, '', '',
> > > from].concat(
> > > > > +               Array.prototype.slice.call(arguments,
> > 3)))])].join('');
> > > > > +    } else {
> > > > > +      // Format: #targetId & sourceId@callId & packetNum &
> packetId
> > &
> > > > > packetData
> > > > > +      src = [relay, '#', targetId, '&', from, '@', callId,
> > > > > +             '&1&0&', encodeURIComponent(rpcData)].join('');
> > > > > +    }
> > > > > +
> > > > > +    // Conduct the IFPC call by creating the Iframe with
> > > > > +    // the relay URL and appended message.
> > > > > +    emitInvisibleIframe(src);
> > > > >   }
> > > > >
> > > > > +
> > > > >   /**
> > > > >    * Helper function to emit an invisible IFrame.
> > > > >    * @param {String} src SRC attribute of the IFrame to emit.
> > > > > @@ -221,6 +617,15 @@
> > > > >      * @member gadgets.rpc
> > > > >      */
> > > > >     register: function(serviceName, handler) {
> > > > > +      if (serviceName == CALLBACK_NAME) {
> > > > > +        throw new Error("Cannot overwrite callback service");
> > > > > +      }
> > > > > +
> > > > > +      if (serviceName == DEFAULT_NAME) {
> > > > > +        throw new Error("Cannot overwrite default service:"
> > > > > +                        + " use registerDefault");
> > > > > +      }
> > > > > +
> > > > >       services[serviceName] = handler;
> > > > >     },
> > > > >
> > > > > @@ -231,6 +636,15 @@
> > > > >      * @member gadgets.rpc
> > > > >      */
> > > > >     unregister: function(serviceName) {
> > > > > +      if (serviceName == CALLBACK_NAME) {
> > > > > +        throw new Error("Cannot delete callback service");
> > > > > +      }
> > > > > +
> > > > > +      if (serviceName == DEFAULT_NAME) {
> > > > > +        throw new Error("Cannot delete default service:"
> > > > > +                        + " use unregisterDefault");
> > > > > +      }
> > > > > +
> > > > >       delete services[serviceName];
> > > > >     },
> > > > >
> > > > > @@ -272,12 +686,14 @@
> > > > >       if (callback) {
> > > > >         callbacks[callId] = callback;
> > > > >       }
> > > > > -      var from;
> > > > > +
> > > > > +      // Default to the container calling.
> > > > > +      var from = '..';
> > > > > +
> > > > >       if (targetId === '..') {
> > > > >         from = window.name;
> > > > > -      } else {
> > > > > -        from = '..';
> > > > >       }
> > > > > +
> > > > >       // Not used by legacy, create it anyway...
> > > > >       var rpcData = gadgets.json.stringify({
> > > > >         s: serviceName,
> > > > > @@ -287,53 +703,37 @@
> > > > >         t: authToken[targetId]
> > > > >       });
> > > > >
> > > > > +      var channelType = relayChannel;
> > > > > +
> > > > > +      // If we are told to use the legacy format, then we must
> > > > > +      // default to IFPC.
> > > > >       if (useLegacyProtocol[targetId]) {
> > > > > -        relayChannel = 'ifpc';
> > > > > +        channelType = 'ifpc';
> > > > >       }
> > > > >
> > > > > -      switch (relayChannel) {
> > > > > -      case 'dpm': // use document.postMessage
> > > > > -        var targetDoc = targetId === '..' ? parent.document :
> > > > > -
> >  frames[targetId].document;
> > > > > -        targetDoc.postMessage(rpcData);
> > > > > -        break;
> > > > > -      case 'wpm': // use window.postMessage
> > > > > -        var targetWin = targetId === '..' ? parent :
> > frames[targetId];
> > > > > -        targetWin.postMessage(rpcData, "*");
> > > > > -        break;
> > > > > -      default: // use 'ifpc' as a fallback mechanism
> > > > > -        var relay = gadgets.rpc.getRelayUrl(targetId);
> > > > > -        // TODO split message if too long
> > > > > -        var src;
> > > > > -        if (useLegacyProtocol[targetId]) {
> > > > > -          //
> #iframe_id&callId&num_packets&packet_num&block_of_data
> > > > > -          src = [relay, '#', encodeLegacyData([from, callId, 1, 0,
> > > > > -                 encodeLegacyData([from, serviceName, '', '',
> > > > > from].concat(
> > > > > -                 Array.prototype.slice.call(arguments,
> > > 3)))])].join('');
> > > > > -        } else {
> > > > > -          // Try the frameElement channel if available
> > > > > -          try {
> > > > > -            if (from === '..') {
> > > > > -              // Container-to-gadget
> > > > > -              var iframe = document.getElementById(targetId);
> > > > > -              if (typeof iframe.__g2c_rpc.__c2g_rpc ===
> 'function')
> > {
> > > > > -                iframe.__g2c_rpc.__c2g_rpc(rpcData);
> > > > > -                return;
> > > > > -              }
> > > > > -            } else {
> > > > > -              // Gadget-to-container
> > > > > -              if (typeof window.frameElement.__g2c_rpc ===
> > 'function')
> > > {
> > > > > -                window.frameElement.__g2c_rpc(rpcData);
> > > > > -                return;
> > > > > -              }
> > > > > -            }
> > > > > -          } catch (e) {
> > > > > -          }
> > > > > -          // # targetId & sourceId@callId & packetNum & packetId
> &
> > > > > packetData
> > > > > -          src = [relay, '#', targetId, '&', from, '@', callId,
> > > > > -                 '&1&0&', encodeURIComponent(rpcData)].join('');
> > > > > -        }
> > > > > -        emitInvisibleIframe(src);
> > > > > +      switch (channelType) {
> > > > > +        case 'dpm': // use document.postMessage.
> > > > > +          var targetDoc = targetId === '..' ? parent.document :
> > > > > +
> > >  frames[targetId].document;
> > > > > +          targetDoc.postMessage(rpcData);
> > > > > +          break;
> > > > > +
> > > > > +        case 'wpm': // use window.postMessage.
> > > > > +          var targetWin = targetId === '..' ? parent :
> > > frames[targetId];
> > > > > +          targetWin.postMessage(rpcData, "*");
> > > > > +          break;
> > > > > +
> > > > > +        case 'nix': // use NIX.
> > > > > +          callNIX(targetId, from, rpcData);
> > > > > +          break;
> > > > > +
> > > > > +        case 'fe': // use FrameElement.
> > > > > +          callFrameElement(targetId, from, rpcData);
> > > > > +          break;
> > > > > +
> > > > > +        default: // use 'ifpc' as a fallback mechanism.
> > > > > +          callIFPC(targetId, from, rpcData);
> > > > > +          break;
> > > > >       }
> > > > >     },
> > > > >
> > > > > @@ -372,15 +772,13 @@
> > > > >      */
> > > > >     setAuthToken: function(targetId, token) {
> > > > >       authToken[targetId] = token;
> > > > > +      setupFrame(targetId);
> > > > >     },
> > > > >
> > > > >     /**
> > > > >      * Gets the RPC relay mechanism.
> > > > > -     * @return {String} RPC relay mechanism. Supported types:
> > > > > -     *                  'wpm' - Use window.postMessage (defined by
> > > > HTML5)
> > > > > -     *                  'dpm' - Use document.postMessage (defined
> by
> > > an
> > > > > early
> > > > > -     *                          draft of HTML5 and implemented by
> > > Opera)
> > > > > -     *                  'ifpc' - Use invisible IFrames
> > > > > +     * @return {String} RPC relay mechanism. See above for
> > > > > +     *   a list of supported types.
> > > > >      *
> > > > >      * @member gadgets.rpc
> > > > >      */
> > > > >
> > > > > Modified:
> > > > >
> > > >
> > >
> >
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > > > URL:
> > > > >
> > > >
> > >
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java?rev=672393&r1=672392&r2=672393&view=diff
> > > > >
> > > > >
> > > >
> > >
> >
> ==============================================================================
> > > > > ---
> > > > >
> > > >
> > >
> >
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > > > (original)
> > > > > +++
> > > > >
> > > >
> > >
> >
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > > > Fri Jun 27 14:01:20 2008
> > > > > @@ -221,7 +221,8 @@
> > > > >           "a {color:#0000cc;}a:visited {color:#551a8b;}" +
> > > > >           "a:active {color:#ff0000;}" +
> > > > >           "body{margin: 0px;padding: 0px;background-color:white;}"
> +
> > > > > -          "</style></head>";
> > > > > +          "</style><base>" + gadget.getSpec().getUrl().toString()
> +
> > > > > +          "</base></head>";
> > > > >     markup.append(boilerPlate);
> > > > >     LocaleSpec localeSpec =
> > > gadget.getSpec().getModulePrefs().getLocale(
> > > > >         gadget.getContext().getLocale());
> > > > >
> > > > > Added:
> > > > incubator/shindig/trunk/javascript/container/rpctest_container.html
> > > > > URL:
> > > > >
> > > >
> > >
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_container.html?rev=672393&view=auto
> > > > >
> > > > >
> > > >
> > >
> >
> ==============================================================================
> > > > > ---
> > incubator/shindig/trunk/javascript/container/rpctest_container.html
> > > > > (added)
> > > > > +++
> > incubator/shindig/trunk/javascript/container/rpctest_container.html
> > > > Fri
> > > > > Jun 27 14:01:20 2008
> > > > > @@ -0,0 +1,90 @@
> > > > > +<!--
> > > > > +  Simple page for testing gadgets.rpc performance.
> > > > > +  Allows you to add a simulated "gadget" (in this case just a
> static
> > > > > +  HTML page which loads gadgets.rpc also), and pass some
> > > > > +  specified number of random messages of specified size to
> > > > > +  and from it.
> > > > > +
> > > > > +  A simple recommended setup is to start up two instances of
> > > > > +  the Shindig Gadgets Server on two separate ports to test
> > > > > +  "real" cross-domain communication, since port is factored
> > > > > +  into the same-domain policy enforced by browsers.
> > > > > +
> > > > > +  If your servers are on localhost:8080 and localhost:8081, then
> > hit:
> > > > > +
> > > http://localhost:8080/gadgets/files/container/rpctest_container.html?
> > > > \
> > > > > +
> > http://localhost:8081/gadgets/files/container/rpctest_gadget.html&\<http://localhost:8081/gadgets/files/container/rpctest_gadget.html&%5C>
> <http://localhost:8081/gadgets/files/container/rpctest_gadget.html&%5C>
> > > > > +
> > > > >
> > > >
> > >
> >
> http://localhost:8081/gadgets/files/container/rpc_relay.uncompressed.html
> > > > > +
> > > > > +  (Note the backslashes should be removed, as they exist for
> > > formatting
> > > > > only.)
> > > > > +
> > > > > +  There are two arguments, separated by an ampersand:
> > > > > +  1. URL of the "gadget" test page.
> > > > > +  2. URL of the "gadget" test page's RPC relay (for browsers
> > requiring
> > > > > it).
> > > > > +-->
> > > > > +<html>
> > > > > +  <head>
> > > > > +    <title>gadgets.rpc Performance Tests: Container</title>
> > > > > +    <script src="../../js/rpc.js?c=1&debug=1"></script>
> > > > > +    <script src="rpctest_perf.js"></script>
> > > > > +    <script>
> > > > > +      function initTest() {
> > > > > +        var container = document.getElementById("container");
> > > > > +
> > > > > +        // query string is assumed to be the "gadget" URL as arg
> 1,
> > > > > +        // and optionally the relay URL as arg 2
> > > > > +        var pageArgs =
> > window.location.search.substring(1).split('&');
> > > > > +        var gadgetUrl = pageArgs[0];
> > > > > +        var secret = Math.round(Math.random()*10000000);
> > > > > +        if (pageArgs[1]) {
> > > > > +          gadgets.rpc.setRelayUrl('gadget', pageArgs[1]);
> > > > > +        }
> > > > > +        var containerRelay = pageArgs[2] || '';
> > > > > +        container.innerHTML = "<iframe id='gadget' name='gadget'
> > > > > height=300 width=300 src='" + gadgetUrl + "?parent=" +
> containerRelay
> > +
> > > > > "#rpctoken=" + secret + "'></iframe>";
> > > > > +        gadgets.rpc.setAuthToken('gadget', secret);
> > > > > +
> > > > > +        initPerfTest();
> > > > > +      };
> > > > > +    </script>
> > > > > +  </head>
> > > > > +  <body onload="initTest();">
> > > > > +    <div>gadgets.rpc Performance: Container Page</div><hr/>
> > > > > +    <div>Test<br/>
> > > > > +      <ul>
> > > > > +        <li>Number of messages to send:
> > > > > +          <select name="num_msgs" id="num_msgs">
> > > > > +            <option value="1">1</option>
> > > > > +            <option value="10">10</option>
> > > > > +            <option value="100" selected>100</option>
> > > > > +            <option value="1000">1000</option>
> > > > > +          </select>
> > > > > +        </li>
> > > > > +        <li>Message size:
> > > > > +          <select name="msg_size" id="msg_size">
> > > > > +            <option value="10">10 B</option>
> > > > > +            <option value="100">100 B</option>
> > > > > +            <option value="1024" selected>1 kB</option>
> > > > > +            <option value="10240">10 kB</option>
> > > > > +            <option value="102400">100 kB</option>
> > > > > +            <option value="1048576">1 MB</option>
> > > > > +          </select>
> > > > > +        </li>
> > > > > +        <li>
> > > > > +          <input type="button" value="Start The Test!"
> > > > > onclick="runPerfTest('gadget');" />
> > > > > +        </li>
> > > > > +      </ul>
> > > > > +    </div>
> > > > > +    <div id="test_running" style="display:none;">
> > > > > +      Running test...
> > > > > +    </div>
> > > > > +    <div id="results" style="display:none;">
> > > > > +      Results: Gadget-to-Container<br/>
> > > > > +      Messages: <span id="results_num_received"></span>, Bytes:
> > <span
> > > > > id="results_bytes_received"></span> <span
> id="in_or_out"></span><br/>
> > > > > +      Time elapsed for test run: <span
> > > > id="results_time_used"></span><br/>
> > > > > +      Messages/second: <span
> id="results_msgs_per_sec"></span><br/>
> > > > > +      Bytes/second: <span id="results_bytes_per_sec"></span>
> > > > > +    </div>
> > > > > +    <hr/>
> > > > > +    <div>Gadget:</div>
> > > > > +    <div id="container"></div>
> > > > > +  </body>
> > > > > +</html>
> > > > >
> > > > > Added:
> > incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > > > > URL:
> > > > >
> > > >
> > >
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_gadget.html?rev=672393&view=auto
> > > > >
> > > > >
> > > >
> > >
> >
> ==============================================================================
> > > > > ---
> incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > > > > (added)
> > > > > +++
> incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > > Fri
> > > > > Jun 27 14:01:20 2008
> > > > > @@ -0,0 +1,49 @@
> > > > > +<html>
> > > > > +  <head>
> > > > > +    <title>gadgets.rpc Performance Tests: Gadget</title>
> > > > > +    <script src="../../js/rpc.js?c=1&debug=1"></script>
> > > > > +    <script src="rpctest_perf.js"></script>
> > > > > +    <script>
> > > > > +      var containerRelay = window.location.search.substring(1);
> > > > > +      gadgets.rpc.setRelayUrl(null, containerRelay);
> > > > > +    </script>
> > > > > +  </head>
> > > > > +  <body onload="initPerfTest();">
> > > > > +    <div>gadgets.rpc Performance: "Gadget" page</div><hr/>
> > > > > +    <div>Test<br/>
> > > > > +      <ul>
> > > > > +        <li>Number of messages to send:
> > > > > +          <select name="num_msgs" id="num_msgs">
> > > > > +            <option value="1">1</option>
> > > > > +            <option value="10">10</option>
> > > > > +            <option value="100" selected>100</option>
> > > > > +            <option value="1000">1000</option>
> > > > > +          </select>
> > > > > +        </li>
> > > > > +        <li>Message size:
> > > > > +          <select name="msg_size" id="msg_size">
> > > > > +            <option value="10">10 B</option>
> > > > > +            <option value="100">100 B</option>
> > > > > +            <option value="1024" selected>1 kB</option>
> > > > > +            <option value="10240">10 kB</option>
> > > > > +            <option value="102400">100 kB</option>
> > > > > +            <option value="1048576">1 MB</option>
> > > > > +          </select>
> > > > > +        </li>
> > > > > +        <li>
> > > > > +          <input type="button" value="Start The Test!"
> > > > > onclick="runPerfTest();" />
> > > > > +        </li>
> > > > > +      </ul>
> > > > > +    </div>
> > > > > +    <div id="test_running" style="display:none;">
> > > > > +      Running test...
> > > > > +    </div>
> > > > > +    <div id="results" style="display:none;">
> > > > > +      Results: Gadget-to-Container<br/>
> > > > > +      Messages: <span id="results_num_received"></span>, Bytes:
> > <span
> > > > > id="results_bytes_received"></span> <span
> id="in_or_out"></span><br/>
> > > > > +      Time elapsed for test run: <span
> > > > id="results_time_used"></span><br/>
> > > > > +      Messages/second: <span
> id="results_msgs_per_sec"></span><br/>
> > > > > +      Bytes/second: <span id="results_bytes_per_sec"></span>
> > > > > +    </div>
> > > > > +  </body>
> > > > > +</html>
> > > > >
> > > > > Added: incubator/shindig/trunk/javascript/container/rpctest_perf.js
> > > > > URL:
> > > > >
> > > >
> > >
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_perf.js?rev=672393&view=auto
> > > > >
> > > > >
> > > >
> > >
> >
> ==============================================================================
> > > > > --- incubator/shindig/trunk/javascript/container/rpctest_perf.js
> > > (added)
> > > > > +++ incubator/shindig/trunk/javascript/container/rpctest_perf.js
> Fri
> > > Jun
> > > > 27
> > > > > 14:01:20 2008
> > > > > @@ -0,0 +1,93 @@
> > > > > +var perfStats = null;
> > > > > +var currentRun = {};
> > > > > +
> > > > > +function perfService(message) {
> > > > > +  if (perfStats.numResults++ === 0) {
> > > > > +    perfStats.firstMsg = message; // stored since it has "real"
> > start
> > > > time
> > > > > +  }
> > > > > +  perfStats.bytesReceived += message.length;
> > > > > +};
> > > > > +
> > > > > +function clearPerfStats(inOrOut) {
> > > > > +  perfStats = {
> > > > > +    numResults: 0,
> > > > > +    bytesReceived: 0,
> > > > > +    firstMsg: null
> > > > > +  };
> > > > > +
> > > > > +  document.getElementById("in_or_out").innerHTML = inOrOut;
> > > > > +
> > > > > +  // hide results fields
> > > > > +  document.getElementById("results").style.display = "none";
> > > > > +};
> > > > > +
> > > > > +function completePerfStats() {
> > > > > +  perfStats.timeEnded = new Date().getTime();
> > > > > +
> > > > > +  // get time started from the first sent message
> > > > > +  perfStats.timeStarted = perfStats.firstMsg.substr(0,
> > > > > perfStats.firstMsg.indexOf(':'));
> > > > > +
> > > > > +  var timeUsedMs = perfStats.timeEnded - perfStats.timeStarted;
> > > > > +
> > > > > +  // fill in fields
> > > > > +  document.getElementById("results_num_received").innerHTML =
> > > > > perfStats.numResults;
> > > > > +  document.getElementById("results_bytes_received").innerHTML =
> > > > > perfStats.bytesReceived;
> > > > > +  document.getElementById("results_time_used").innerHTML =
> > timeUsedMs
> > > +
> > > > > "ms";
> > > > > +  document.getElementById("results_msgs_per_sec").innerHTML =
> > > > > (perfStats.numResults / (timeUsedMs / 1000));
> > > > > +  document.getElementById("results_bytes_per_sec").innerHTML =
> > > > > (perfStats.bytesReceived / (timeUsedMs / 1000));
> > > > > +  document.getElementById("test_running").style.display = "none";
> > > > > +  document.getElementById("results").style.display = "";
> > > > > +};
> > > > > +
> > > > > +function initPerfTest() {
> > > > > +  clearPerfStats();
> > > > > +  gadgets.rpc.register("perf_service", perfService);
> > > > > +  gadgets.rpc.register("clear_perf_stats", clearPerfStats);
> > > > > +  gadgets.rpc.register("complete_perf_stats", completePerfStats);
> > > > > +};
> > > > > +
> > > > > +var alphabet =
> > > > > "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
> > > > _-*&(){}'";
> > > > > +
> > > > > +function sendPerfMessage() {
> > > > > +  var msgToSend = currentRun.msg;
> > > > > +  if (currentRun.curMsgId++ <= 1) {
> > > > > +    var nowString = new Date().getTime() + ':';
> > > > > +    msgToSend = nowString +
> > > currentRun.msg.substring(nowString.length);
> > > > > +  }
> > > > > +
> > > > > +  gadgets.rpc.call(currentRun.targetId, "perf_service", null,
> > > > msgToSend);
> > > > > +  if (currentRun.curMsgId < currentRun.endMsgId) {
> > > > > +    // loop, giving up execution in case rpc technique demands it
> > > > > +    window.setTimeout(sendPerfMessage, 0);
> > > > > +  } else {
> > > > > +    // send finisher
> > > > > +    window.setTimeout(function() {
> > > gadgets.rpc.call(currentRun.targetId,
> > > > > "complete_perf_stats", null); }, 0);
> > > > > +  }
> > > > > +};
> > > > > +
> > > > > +function runPerfTest(targetId) {
> > > > > +  document.getElementById("test_running").style.display = "";
> > > > > +
> > > > > +  // initialize the current run
> > > > > +  var num_msgs = document.getElementById("num_msgs").value;
> > > > > +  var msg_size = document.getElementById("msg_size").value;
> > > > > +
> > > > > +  currentRun.targetId = targetId;
> > > > > +  currentRun.curMsgId = 0;
> > > > > +  currentRun.endMsgId = num_msgs;
> > > > > +
> > > > > +  var msg = [];
> > > > > +  for (var i = 0; i < msg_size; ++i) {
> > > > > +    msg[i] =
> > > alphabet.charAt(Math.round(Math.random(alphabet.length)));
> > > > > +  }
> > > > > +  currentRun.msg = msg.join('');
> > > > > +
> > > > > +  // clear local perf stats
> > > > > +  clearPerfStats("(outbound)");
> > > > > +
> > > > > +  // clear target perf stats
> > > > > +  gadgets.rpc.call(targetId, "clear_perf_stats", null,
> "(inbound)");
> > > > > +
> > > > > +  // kick off the send loop
> > > > > +  sendPerfMessage();
> > > > > +};
> > > > >
> > > > >
> > > > >
> > > >
> > >
> >
>

Re: svn commit: r672393 - in /incubator/shindig/trunk: features/rpc/ java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ javascript/container/

Posted by Kevin Brown <et...@google.com>.
On Fri, Jun 27, 2008 at 3:45 PM, John Hjelmstad <fa...@google.com> wrote:

> As indicated in the CL, I tested this on half a dozen browsers. We don't
> have unit tests for it. I've also sent the code and inquiries about the
> technique to several poeple, some of whom expressed concerns but no ability
> to actively exploit the code.


Anyone on this mailing list? Either of the people I mentioned?


> The main concern was around VBScript
> knowledge, which few people I know extensively have. That was true of IFPC
> when it was originally released as well. If any attacks are found, they
> will
> be actively closed or the technique will be removed. Would you prefer that
> no code, irrespective of its value, ever be submitted? It's possible for
> any
> code to have bugs, and a lot of code to have security holes. If found,
> they're fixed.


Yes, but this is code that uses a security technique that is highly
questionable and has been flagged as potentially problematic by several
people. Therefore, it needs more scrutiny. I won't deploy this on any of our
production systems until I get an OK from people who are actually familiar
with the problems in question have chimed in.

This is also a very large code change, and, per policy, large code changes
need JIRA tickets and feedback from other people. There are hundreds (if not
thousands) of people using Shindig today and the code is deployed to
hundreds of millions of users.


>
> John
>
> On Fri, Jun 27, 2008 at 3:38 PM, Kevin Brown <et...@google.com> wrote:
>
> > Please actually test your code or send it to other shindig committers
> > before
> > you commit it. Your change to GadgetRenderingTask breaks proxying and
> > makeRequest in several cases. Fix it.
> >
> > Have you run this by Brian and/or Mike Samuel? They both expressed doubts
> > about whether this mechanism was actually secure.
> >
> > On Fri, Jun 27, 2008 at 2:06 PM, John Hjelmstad <fa...@google.com>
> wrote:
> >
> > > +jschorr in case there are comments.
> > >
> > > On Fri, Jun 27, 2008 at 2:01 PM, <jo...@apache.org> wrote:
> > >
> > > > Author: johnh
> > > > Date: Fri Jun 27 14:01:20 2008
> > > > New Revision: 672393
> > > >
> > > > URL: http://svn.apache.org/viewvc?rev=672393&view=rev
> > > > Log:
> > > > Cleaned up gadgets.rpc library implementation, and implemented fast
> IE
> > > > transport layer.
> > > >
> > > > Credit:
> > > >        Joseph Schorr (jschorr@google.com) and I implemented this
> > > together,
> > > > but he
> > > >        really did all the hard work, as well as developing the NIX
> > > > technique's
> > > >        fundamentals. Huge thanks to Joseph!
> > > >
> > > > Details:
> > > >        * Created a new relayChannel for Firefox frameElement
> technique,
> > > >          making its implementation more linear to read.
> > > >        * Consolidated all transport-specific code in setupChannel(),
> > > > setupFrame(),
> > > >          and callX(), where X = relay type. This refactoring makes
> > setup
> > > > and use
> > > >          of each transport more clear, and makes it possible in later
> > > > revisions
> > > >          to separate out each method as its own class. In theory, if
> we
> > > can
> > > > trust
> > > >          things like User-Agent headers, we can use this to reduce
> > rpc.js
> > > > code
> > > >          size significantly by only including the needed
> transport(s).
> > > >        * Implemented "NIX" - Native IE XDC. This method exploits the
> > fact
> > > >          that in IE6 and IE7 the window.opener property can be both
> set
> > > and
> > > >          read across domain boundaries for an IFRAME, and that any
> > object
> > > >          can be passed through it. In particular, functions
> > implementing
> > > >          message passing can be passed. These can't be JS functions,
> > > > however,
> > > >          since those can leak sender context. So, VBScript (COM)
> > objects
> > > >          wrapping these are passed to maintain context isolation.
> > > >          - Requires for IE6/7 that rpc.js be included in the
> container
> > > >            at load time, not dynamically. TODO added to consider
> > > detecting
> > > >            whether dynamic loading is happening (at which point we
> > should
> > > >            fall back to IFPC).
> > > >          - Message channel handshake *and* message passing validated
> > > >            using authToken, as with other channels.
> > > >          - Impl requires that gadget call container first - else IFPC
> > > >            is used. This is the same as the frameElement technique.
> We
> > > >            could add a setInterval()-based initialization routine
> later
> > > >            if needed. To date, the only gadgets.rpc calls made from
> > > >            container to gadget without gadget to container first are
> > > >            in theory pubsub messages.
> > > >          - Extensive documentation on this technique and the others
> > > >            added to the comments, since they're stripped out in the
> > > >            majority case.
> > > >        * Implemented quick-and-dirty performance testing page
> verifying
> > > >          the library works and giving basic performance metrics.
> > > >          This needs to be improved! But does the trick for now...
> > > >
> > > > Testing/Data:
> > > >
> > > > Library verified on the following browsers with the RPC test page.
> For
> > > > each,
> > > > a "bandwidth" measure was taken by averaging the results of 6 G2C and
> > C2G
> > > > runs
> > > > with 100 messages being passed of 1K apiece. Units are 1K
> messages/sec.
> > > > The "latency" value is the average amount of time taken, over 6 G2C
> and
> > > C2G
> > > > runs,
> > > > to send 1 1K message. Happily, for all browsers save Safari (soon due
> > to
> > > > get
> > > > window.postMessage), latency is around or below time required to
> > achieve
> > > > a cross-domain call without user-perceived delay.
> > > >
> > > >        * Firefox 3 (WPM) - bandwidth: 88 kB/sec; latency: 18ms.
> > > >        * Firefox 2 (FE)  - bandwidth: 80 kB/sec; latency: 15ms.
> > > >        * Opera 9   (WPM) - bandwidth: 85 kB/sec; latency: 7ms.
> > > >        * IE6       (NIX) - bandwidth: 64 kB/sec; latency: 18ms.
> > > >        * IE7       (NIX) - bandwidth: 64 kB/sec; latency: 22ms.
> > > >        * Safari 3 (IFPC) - bandwidth: ?6-8kB/sec; latency:
> ?100-200ms.
> > > >          - Safari is somewhat flaky with the RPC test page, before
> > > >            and after the rpc.js change. Multiple messages seem to
> > > >            confuse it from time to time.
> > > >
> > > >
> > > > Added:
> > > >
>  incubator/shindig/trunk/javascript/container/rpctest_container.html
> > > >    incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > > >    incubator/shindig/trunk/javascript/container/rpctest_perf.js
> > > > Modified:
> > > >    incubator/shindig/trunk/features/rpc/rpc.js
> > > >
> > > >
> > >
> >
>  incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > >
> > > > Modified: incubator/shindig/trunk/features/rpc/rpc.js
> > > > URL:
> > > >
> > >
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/features/rpc/rpc.js?rev=672393&r1=672392&r2=672393&view=diff
> > > >
> > > >
> > >
> >
> ==============================================================================
> > > > --- incubator/shindig/trunk/features/rpc/rpc.js (original)
> > > > +++ incubator/shindig/trunk/features/rpc/rpc.js Fri Jun 27 14:01:20
> > 2008
> > > > @@ -18,7 +18,7 @@
> > > >
> > > >  /**
> > > >  * @fileoverview Remote procedure call library for
> gadget-to-container,
> > > > - * container-to-gadget, and gadget-to-gadget communication.
> > > > + * container-to-gadget, and gadget-to-gadget (thru container)
> > > > communication.
> > > >  */
> > > >
> > > >  var gadgets = gadgets || {};
> > > > @@ -29,6 +29,30 @@
> > > >  * @name gadgets.rpc
> > > >  */
> > > >  gadgets.rpc = function() {
> > > > +  // General constants.
> > > > +  var CALLBACK_NAME = '__cb';
> > > > +  var DEFAULT_NAME = '';
> > > > +
> > > > +  // Consts for FrameElement.
> > > > +  var FE_G2C_CHANNEL = '__g2c_rpc';
> > > > +  var FE_C2G_CHANNEL = '__c2g_rpc';
> > > > +
> > > > +  // Consts for NIX. VBScript doesn't
> > > > +  // allow items to start with _ for some reason,
> > > > +  // so we need to make these names quite unique, as
> > > > +  // they will go into the global namespace.
> > > > +  var NIX_WRAPPER = 'GRPC____NIXVBS_wrapper';
> > > > +  var NIX_GET_WRAPPER = 'GRPC____NIXVBS_get_wrapper';
> > > > +  var NIX_HANDLE_MESSAGE = 'GRPC____NIXVBS_handle_message';
> > > > +  var NIX_CREATE_CHANNEL = 'GRPC____NIXVBS_create_channel';
> > > > +  var NIX_ID_FIELD = 'GRPC____NIXVBS_container';
> > > > +
> > > > +  // JavaScript reference to the NIX VBScript wrappers.
> > > > +  // Gadgets will have but a single channel under
> > > > +  // nix_channels['..'] while containers will have a channel
> > > > +  // per gadget stored under the gadget's ID.
> > > > +  var nix_channels = {};
> > > > +
> > > >   var services = {};
> > > >   var iframePool = [];
> > > >   var relayUrl = {};
> > > > @@ -36,28 +60,135 @@
> > > >   var authToken = {};
> > > >   var callId = 0;
> > > >   var callbacks = {};
> > > > +  var setup = {};
> > > > +
> > > > +  var params = {};
> > > > +
> > > > +  // Load the authentication token for speaking to the container
> > > > +  // from the gadget's parameters, or default to '0' if not found.
> > > > +  if (gadgets.util) {
> > > > +    params = gadgets.util.getUrlParameters();
> > > > +  }
> > > >
> > > > -  var params = gadgets.util.getUrlParameters();
> > > >   authToken['..'] = params.rpctoken || params.ifpctok || 0;
> > > >
> > > > -  // Pick the most efficient RPC relay mechanism
> > > > -  var relayChannel = typeof document.postMessage === 'function' ?
> > 'dpm'
> > > :
> > > > -                     typeof window.postMessage === 'function' ?
> 'wpm'
> > :
> > > > +  // Pick the most efficient RPC relay mechanism:
> > > > +  //
> > > > +  // * For those browsers that support native messaging (various
> > > > implementations
> > > > +  //   of the HTML5 postMessage method), use that. Officially
> defined
> > at
> > > > +  //
> > > >
> http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html
> > .
> > > > +  //
> > > > +  //   postMessage is a native implementation of XDC. A page
> registers
> > > > that
> > > > +  //   it would like to receive messages by listening the the
> > "message"
> > > > event
> > > > +  //   on the window (document in DPM) object. In turn, another page
> > can
> > > > +  //   raise that event by calling window.postMessage
> > > > (document.postMessage
> > > > +  //   in DPM) with a string representing the message and a string
> > > > +  //   indicating on which domain the receiving page must be to
> > receive
> > > > +  //   the message. The target page will then have its "message"
> event
> > > > raised
> > > > +  //   if the domain matches and can, in turn, check the origin of
> the
> > > > message
> > > > +  //   and process the data contained within.
> > > > +  //
> > > > +  //     wpm: postMessage on the window object.
> > > > +  //        - Internet Explorer 8+
> > > > +  //        - Safari (latest nightlies as of 26/6/2008)
> > > > +  //        - Firefox 3+
> > > > +  //        - Opera 9+
> > > > +  //
> > > > +  //     dpm: postMessage on the document object.
> > > > +  //        - Opera 8+
> > > > +  //
> > > > +  // * For Internet Explorer before version 8, the security model
> > allows
> > > a
> > > > +  //   child to set the value of the "opener" property on the parent
> > > > window
> > > > +  //   and vice versa. This method is dubbed "Native IE XDC" (NIX).
> > > > +  //
> > > > +  //   This method works by placing a handler object in the "opener"
> > > > property
> > > > +  //   of a gadget when the container sets up the authentication
> > > > information
> > > > +  //   for that gadget (by calling setAuthToken(...)). At that
> point,
> > a
> > > > NIX
> > > > +  //   wrapper is created and placed into the gadget by calling
> > > > +  //   theframe.contentWindow.opener = wrapper. Note that as a
> result,
> > > NIX
> > > > can
> > > > +  //   only be used by a container to call a particular gadget
> *after*
> > > > that
> > > > +  //   gadget has called the container at least once via NIX.
> > > > +  //
> > > > +  //   The NIX wrappers in this RPC implementation are instances of
> a
> > > > VBScript
> > > > +  //   class that is created when this implementation loads. The
> > reason
> > > > for
> > > > +  //   using a VBScript class stems from the fact that any object
> can
> > be
> > > > passed
> > > > +  //   into the opener property.
> > > > +  //   While this is a good thing, as it lets us pass functions and
> > > setup
> > > > a true
> > > > +  //   bidirectional channel via callbacks, it opens a potential
> > > security
> > > > hole
> > > > +  //   by which the other page can get ahold of the "window" or
> > > "document"
> > > > +  //   objects in the parent page and in turn wreak havok. This is
> due
> > > to
> > > > the
> > > > +  //   fact that any JS object useful for establishing such a
> > > > bidirectional
> > > > +  //   channel (such as a function) can be used to access a function
> > > > +  //   (eg. obj.toString, or a function itself) created in a
> specific
> > > > context,
> > > > +  //   in particular the global context of the sender. Suppose
> > container
> > > > +  //   domain C passes object obj to gadget on domain G. Then the
> > gadget
> > > > can
> > > > +  //   access C's global context using:
> > > > +  //   var parentWindow = (new obj.toString.constructor("return
> > > > window;"))();
> > > > +  //   Nulling out all of obj's properties doesn't fix this, since
> IE
> > > > helpfully
> > > > +  //   restores them to their original values if you do something
> > like:
> > > > +  //   delete obj.toString; delete obj.toString;
> > > > +  //   Thus, we wrap the necessary functions and information inside
> a
> > > > VBScript
> > > > +  //   object. VBScript objects in IE, like DOM objects, are in fact
> > COM
> > > > +  //   wrappers when used in JavaScript, so we can safely pass them
> > > around
> > > > +  //   without worrying about a breach of context while at the same
> > time
> > > > +  //   allowing them to act as a pass-through mechanism for
> > information
> > > > +  //   and function calls. The implementation details of this
> VBScript
> > > > wrapper
> > > > +  //   can be found in the setupChannel() method below.
> > > > +  //
> > > > +  //     nix: Internet Explorer-specific window.opener trick.
> > > > +  //       - Internet Explorer 6
> > > > +  //       - Internet Explorer 7
> > > > +  //
> > > > +  // * For Gecko-based browsers, the security model allows a child
> to
> > > call
> > > > a
> > > > +  //   function on the frameElement of the iframe, even if the child
> > is
> > > in
> > > > +  //   a different domain. This method is dubbed "frameElement"
> (fe).
> > > > +  //
> > > > +  //   The ability to add and call such functions on the
> frameElement
> > > > allows
> > > > +  //   a bidirectional channel to be setup via the adding of simple
> > > > function
> > > > +  //   references on the frameElement object itself. In this
> > > > implementation,
> > > > +  //   when the container sets up the authentication information for
> > > that
> > > > gadget
> > > > +  //   (by calling setAuth(...)) it as well adds a special function
> on
> > > the
> > > > +  //   gadget's iframe. This function can then be used by the gadget
> > to
> > > > send
> > > > +  //   messages to the container. In turn, when the gadget tries to
> > send
> > > a
> > > > +  //   message, it checks to see if this function has its own
> function
> > > > stored
> > > > +  //   that can be used by the container to call the gadget. If not,
> > the
> > > > +  //   function is created and subsequently used by the container.
> > > > +  //   Note that as a result, FE can only be used by a container to
> > call
> > > a
> > > > +  //   particular gadget *after* that gadget has called the
> container
> > at
> > > > +  //   least once via FE.
> > > > +  //
> > > > +  //     fe: Gecko-specific frameElement trick.
> > > > +  //        - Firefox 1+
> > > > +  //
> > > > +  // * For all others, we have a fallback mechanism known as "ifpc".
> > > IFPC
> > > > +  //   exploits the fact that while same-origin policy prohibits a
> > frame
> > > > from
> > > > +  //   accessing members on a window not in the same domain, that
> > frame
> > > > can,
> > > > +  //   however, navigate the window heirarchy (via parent). This is
> > > > exploited by
> > > > +  //   having a page on domain A that wants to talk to domain B
> create
> > > an
> > > > iframe
> > > > +  //   on domain B pointing to a special relay file and with a
> message
> > > > encoded
> > > > +  //   after the hash (#). This relay, in turn, finds the page on
> > domain
> > > > B, and
> > > > +  //   can call a receipt function with the message given to it. The
> > > relay
> > > > URL
> > > > +  //   used by each caller is set via the
> gadgets.rpc.setRelayUrl(..)
> > > and
> > > > +  //   *must* be called before the call method is used.
> > > > +  //
> > > > +  //     ifpc: Iframe-based method, utilizing a relay page, to send
> a
> > > > message.
> > > > +  //
> > > > +  var relayChannel = typeof window.postMessage === 'function' ?
> 'wpm'
> > :
> > > > +                    typeof document.postMessage === 'function' ?
> 'dpm'
> > :
> > > > +                    window.ActiveXObject ? 'nix' :
> > > > +                     navigator.product === 'Gecko' ? 'fe' :
> > > >                      'ifpc';
> > > > -  if (relayChannel === 'dpm' || relayChannel === 'wpm') {
> > > > -    window.addEventListener('message', function(packet) {
> > > > -      // TODO validate packet.domain for security reasons
> > > > -      process(gadgets.json.parse(packet.data));
> > > > -    }, false);
> > > > -  }
> > > >
> > > > -  // Default RPC handler
> > > > -  services[''] = function() {
> > > > +  // Conduct any setup necessary for the chosen channel.
> > > > +  setupChannel();
> > > > +
> > > > +  // Create the Default RPC handler.
> > > > +  services[DEFAULT_NAME] = function() {
> > > >     throw new Error('Unknown RPC service: ' + this.s);
> > > >   };
> > > >
> > > > -  // Special RPC handler for callbacks
> > > > -  services['__cb'] = function(callbackId, result) {
> > > > +  // Create a Special RPC handler for callbacks.
> > > > +  services[CALLBACK_NAME] = function(callbackId, result) {
> > > >     var callback = callbacks[callbackId];
> > > >     if (callback) {
> > > >       delete callbacks[callbackId];
> > > > @@ -66,6 +197,147 @@
> > > >   };
> > > >
> > > >   /**
> > > > +   * Conducts any initial global work necessary to setup the
> > > > +   * channel type chosen.
> > > > +   */
> > > > +  function setupChannel() {
> > > > +    // If the channel type is one of the native
> > > > +    // postMessage based ones, setup the handler to receive
> > > > +    // messages.
> > > > +    if (relayChannel === 'dpm' || relayChannel === 'wpm') {
> > > > +      window.addEventListener('message', function(packet) {
> > > > +        // TODO validate packet.domain for security reasons
> > > > +        process(gadgets.json.parse(packet.data));
> > > > +      }, false);
> > > > +    }
> > > > +
> > > > +    // If the channel type is NIX, we need to ensure the
> > > > +    // VBScript wrapper code is in the page and that the
> > > > +    // global Javascript handlers have been set.
> > > > +    if (relayChannel === 'nix') {
> > > > +      // VBScript methods return a type of 'unknown' when
> > > > +      // checked via the typeof operator in IE. Fortunately
> > > > +      // for us, this only applies to COM objects, so we
> > > > +      // won't see this for a real Javascript object.
> > > > +      if (typeof window[NIX_GET_WRAPPER] !== 'unknown') {
> > > > +        window[NIX_HANDLE_MESSAGE] = function(data) {
> > > > +          process(gadgets.json.parse(data));
> > > > +        };
> > > > +
> > > > +        window[NIX_CREATE_CHANNEL] = function(name, channel, token)
> {
> > > > +          // Verify the authentication token of the gadget trying
> > > > +          // to create a channel for us.
> > > > +          if (authToken[name] == token) {
> > > > +            nix_channels[name] = channel;
> > > > +          }
> > > > +        };
> > > > +
> > > > +        // Inject the VBScript code needed.
> > > > +        var vbscript =
> > > > +          '<scr' + 'ipt language="VBScript">'
> > > > +          // We create a class to act as a wrapper for
> > > > +          // a Javascript call, to prevent a break in of
> > > > +          // the context.
> > > > +          + 'Class ' + NIX_WRAPPER + '\n '
> > > > +
> > > > +          // An internal member for keeping track of the
> > > > +          // name of the document (container or gadget)
> > > > +          // for which this wrapper is intended. For
> > > > +          // those wrappers created by gadgets, this is not
> > > > +          // used (although it is set to "..")
> > > > +          + 'Private m_Intended\n'
> > > > +
> > > > +          // Method for internally setting the value
> > > > +          // of the m_Intended property.
> > > > +          + 'Public Sub SetIntendedName(name)\n '
> > > > +          + 'm_Intended = name\n'
> > > > +          + 'End Sub\n'
> > > > +
> > > > +          // A wrapper method which actually causes a
> > > > +          // message to be sent to the other context.
> > > > +          + 'Public Sub SendMessage(data)\n '
> > > > +          + NIX_HANDLE_MESSAGE + '(data)\n'
> > > > +          + 'End Sub\n'
> > > > +
> > > > +          // Method for setting up the container->gadget
> > > > +          // channel. Not strictly needed in the gadget's
> > > > +          // wrapper, but no reason to get rid of it. Note here
> > > > +          // that we pass the intended name to the
> NIX_CREATE_CHANNEL
> > > > +          // method so that it can save the channel in the proper
> > place
> > > > +          // *and* verify the channel via the authentication token
> > > passed
> > > > +          // here.
> > > > +          + 'Public Sub CreateChannel(channel, auth)\n '
> > > > +          + 'Call ' + NIX_CREATE_CHANNEL + '(m_Intended, channel,
> > > auth)\n'
> > > > +          + 'End Sub\n'
> > > > +
> > > > +          // An empty field with a unique identifier to
> > > > +          // prevent the code from confusing this wrapper
> > > > +          // with a run-of-the-mill value found in window.opener.
> > > > +          + 'Public Sub ' + NIX_ID_FIELD + '()\n '
> > > > +          + 'End Sub\n'
> > > > +          + 'End Class\n '
> > > > +
> > > > +          // Function to get a reference to the wrapper.
> > > > +          + 'Function ' + NIX_GET_WRAPPER + '(name)\n'
> > > > +          + 'Dim wrap\n'
> > > > +          + 'Set wrap = New ' + NIX_WRAPPER + '\n'
> > > > +          + 'wrap.SetIntendedName name\n'
> > > > +          + 'Set ' + NIX_GET_WRAPPER + ' = wrap\n'
> > > > +          + 'End Function'
> > > > +          + '</scr' + 'ipt>'
> > > > +
> > > > +          // Resets the "default" scripting language in IE back
> > > > +          // to Javascript. This is needed or else any script
> > > > +          // tags without a proper Language="..." will be treated as
> > > > +          // VBScript once this code is added to the document.
> > > > +          + '<scri' + 'pt language="JScript"></scr' + 'ipt>';
> > > > +
> > > > +        // Note that this code can only be run once the document has
> > > > +        // fully loaded.
> > > > +        // TODO: Perhaps add some sort of check here for this?
> > > > +        document.write(vbscript);
> > > > +      }
> > > > +    }
> > > > +  }
> > > > +
> > > > +  /**
> > > > +   * Conducts any frame-specific work necessary to setup
> > > > +   * the channel type chosen. This method is called when
> > > > +   * the container page first registers the gadget in the
> > > > +   * RPC mechanism. Gadgets, in turn, will complete the setup
> > > > +   * of the channel once they send their first messages.
> > > > +   */
> > > > +  function setupFrame(frameId) {
> > > > +    if (setup[frameId]) {
> > > > +      return;
> > > > +    }
> > > > +
> > > > +    if (relayChannel === 'fe') {
> > > > +      try {
> > > > +        var frame = document.getElementById(frameId);
> > > > +        frame[FE_G2C_CHANNEL] = function(args) {
> > > > +          process(gadgets.json.parse(args));
> > > > +        };
> > > > +      } catch (e) {
> > > > +        // Something went wrong. System will fallback to
> > > > +        // IFPC.
> > > > +      }
> > > > +    }
> > > > +
> > > > +    if (relayChannel === 'nix') {
> > > > +      try {
> > > > +        var frame = document.getElementById(frameId);
> > > > +        frame.contentWindow.opener =
> window[NIX_GET_WRAPPER](frameId);
> > > > +      } catch (e) {
> > > > +        // Something went wrong. System will fallback to
> > > > +        // IFPC.
> > > > +      }
> > > > +    }
> > > > +
> > > > +    setup[frameId] = true;
> > > > +  }
> > > > +
> > > > +  /**
> > > >    * Encodes arguments for the legacy IFPC wire format.
> > > >    *
> > > >    * @param {Object} args
> > > > @@ -86,8 +358,17 @@
> > > >    * @private
> > > >    */
> > > >   function process(rpc) {
> > > > +    //
> > > > +    // RPC object contents:
> > > > +    //   s: Service Name
> > > > +    //   f: From
> > > > +    //   c: The callback ID or 0 if none.
> > > > +    //   a: The arguments for this RPC call.
> > > > +    //   t: The authentication token.
> > > > +    //
> > > >     if (rpc && typeof rpc.s === 'string' && typeof rpc.f === 'string'
> > &&
> > > >         rpc.a instanceof Array) {
> > > > +
> > > >       // Validate auth token.
> > > >       if (authToken[rpc.f]) {
> > > >         // We allow type coercion here because all the url params are
> > > > strings.
> > > > @@ -96,43 +377,158 @@
> > > >         }
> > > >       }
> > > >
> > > > -      // The Gecko engine used by FireFox etc. allows an IFrame to
> > > > directly call
> > > > -      // methods on the frameElement property added by the container
> > > page
> > > > even
> > > > -      // if their domains don't match.
> > > > -      // Here we try to set up a relay channel using the
> frameElement
> > > > technique
> > > > -      // to greatly reduce the latency of cross-domain calls if the
> > > > postMessage
> > > > -      // method is not supported.
> > > > -      if (relayChannel === 'ifpc') {
> > > > -        if (rpc.f === '..') {
> > > > -          // Container-to-gadget call
> > > > -          try {
> > > > -            var fel = window.frameElement;
> > > > -            if (typeof fel.__g2c_rpc === 'function' &&
> > > > -                typeof fel.__g2c_rpc.__c2g_rpc != 'function') {
> > > > -              fel.__g2c_rpc.__c2g_rpc = function(args) {
> > > > -                process(gadgets.json.parse(args));
> > > > -              };
> > > > -            }
> > > > -          } catch (e) {
> > > > -          }
> > > > -        } else {
> > > > -          // Gadget-to-container call
> > > > -          var iframe = document.getElementById(rpc.f);
> > > > -          if (iframe && typeof iframe.__g2c_rpc != 'function') {
> > > > -            iframe.__g2c_rpc = function(args) {
> > > > +      // Call the requested RPC service.
> > > > +      var result = (services[rpc.s] ||
> > > > +                    services[DEFAULT_NAME]).apply(rpc, rpc.a);
> > > > +
> > > > +      // If there is a callback for this service, initiate it as
> well.
> > > > +      if (rpc.c) {
> > > > +        gadgets.rpc.call(rpc.f, CALLBACK_NAME, null, rpc.c, result);
> > > > +      }
> > > > +    }
> > > > +  }
> > > > +
> > > > +  /**
> > > > +   * Attempts to conduct an RPC call to the specified
> > > > +   * target with the specified data via the NIX
> > > > +   * method. If this method fails, the system attempts again
> > > > +   * using the known default of IFPC.
> > > > +   *
> > > > +   * @param {String} targetId Module Id of the RPC service provider.
> > > > +   * @param {String} from Module Id of the calling provider.
> > > > +   * @param {Object} rpcData The RPC data for this call.
> > > > +   */
> > > > +  function callNIX(targetId, from, rpcData) {
> > > > +    try {
> > > > +      if (from != '..') {
> > > > +        // Call from gadget to the container.
> > > > +        var handler = nix_channels['..'];
> > > > +
> > > > +        // If the gadget has yet to retrieve a reference to
> > > > +        // the NIX handler, try to do so now. We don't do a
> > > > +        // typeof(window.opener[NIX_ID_FIELD]) check here
> > > > +        // because it means accessing that field on the COM object,
> > > which,
> > > > +        // being an internal function reference, is not allowed.
> > > > +        // "in" works because it merely checks for the prescence of
> > > > +        // the key, rather than actually accessing the object's
> > > property.
> > > > +        if (!handler && window.opener && NIX_ID_FIELD in
> > window.opener)
> > > {
> > > > +          handler = nix_channels['..'] = window.opener;
> > > > +
> > > > +          // Create the channel to the parent/container. We pass
> both
> > > our
> > > > +          // own wrapper and our authentication token for
> > verification.
> > > > +          handler.CreateChannel(window[NIX_GET_WRAPPER]('..'),
> > > > authToken['..']);
> > > > +        }
> > > > +
> > > > +        // If we have a handler, call it.
> > > > +        if (handler) {
> > > > +          handler.SendMessage(rpcData);
> > > > +          return;
> > > > +        }
> > > > +      } else {
> > > > +        // Call from container to a gadget[targetId].
> > > > +
> > > > +        // If we have a handler, call it.
> > > > +        if (nix_channels[targetId]) {
> > > > +          nix_channels[targetId].SendMessage(rpcData);
> > > > +          return;
> > > > +        }
> > > > +      }
> > > > +    } catch (e) {
> > > > +    }
> > > > +
> > > > +    // If we have reached this point, something has failed
> > > > +    // with the NIX method, so we default to using
> > > > +    // IFPC for this call.
> > > > +    callIFPC(targetId, from, rpcData);
> > > > +  }
> > > > +
> > > > +  /**
> > > > +   * Attempts to conduct an RPC call to the specified
> > > > +   * target with the specified data via the FrameElement
> > > > +   * method. If this method fails, the system attempts again
> > > > +   * using the known default of IFPC.
> > > > +   *
> > > > +   * @param {String} targetId Module Id of the RPC service provider.
> > > > +   * @param {String} from Module Id of the calling provider.
> > > > +   * @param {Object} rpcData The RPC data for this call.
> > > > +   */
> > > > +  function callFrameElement(targetId, from, rpcData) {
> > > > +    try {
> > > > +      if (from != '..') {
> > > > +        // Call from gadget to the container.
> > > > +        var fe = window.frameElement;
> > > > +
> > > > +        if (typeof fe[FE_G2C_CHANNEL] === 'function') {
> > > > +          // Complete the setup of the FE channel if need be.
> > > > +          if (typeof fe[FE_G2C_CHANNEL][FE_C2G_CHANNEL] !==
> > 'function')
> > > {
> > > > +            fe[FE_G2C_CHANNEL][FE_C2G_CHANNEL] = function(args) {
> > > >               process(gadgets.json.parse(args));
> > > >             };
> > > >           }
> > > > +
> > > > +          // Conduct the RPC call.
> > > > +          fe[FE_G2C_CHANNEL](rpcData);
> > > > +          return;
> > > >         }
> > > > -      }
> > > > +      } else {
> > > > +        // Call from container to gadget[targetId].
> > > > +        var frame = document.getElementById(targetId);
> > > >
> > > > -      var result = (services[rpc.s] || services['']).apply(rpc,
> > rpc.a);
> > > > -      if (rpc.c) {
> > > > -        gadgets.rpc.call(rpc.f, '__cb', null, rpc.c, result);
> > > > +        if (typeof frame[FE_G2C_CHANNEL] === 'function' &&
> > > > +            typeof frame[FE_G2C_CHANNEL][FE_C2G_CHANNEL] ===
> > 'function')
> > > {
> > > > +
> > > > +          // Conduct the RPC call.
> > > > +          frame[FE_G2C_CHANNEL][FE_C2G_CHANNEL](rpcData);
> > > > +          return;
> > > > +        }
> > > >       }
> > > > +    } catch (e) {
> > > > +    }
> > > > +
> > > > +    // If we have reached this point, something has failed
> > > > +    // with the FrameElement method, so we default to using
> > > > +    // IFPC for this call.
> > > > +    callIFPC(targetId, from, rpcData);
> > > > +  }
> > > > +
> > > > +  /**
> > > > +   * Conducts an RPC call to the specified
> > > > +   * target with the specified data via the IFPC
> > > > +   * method.
> > > > +   *
> > > > +   * @param {String} targetId Module Id of the RPC service provider.
> > > > +   * @param {String} from Module Id of the calling provider.
> > > > +   * @param {Object} rpcData The RPC data for this call.
> > > > +   */
> > > > +  function callIFPC(targetId, from, rpcData) {
> > > > +    // Retrieve the relay file used by IFPC. Note that
> > > > +    // this must be set before the call, and so we conduct
> > > > +    // an extra check to ensure it is not blank.
> > > > +    var relay = gadgets.rpc.getRelayUrl(targetId);
> > > > +
> > > > +    if (!relay) {
> > > > +      throw new Error('No relay file assigned for IFPC');
> > > >     }
> > > > +
> > > > +    // The RPC mechanism supports two formats for IFPC (legacy and
> > > > current).
> > > > +    var src = null;
> > > > +    if (useLegacyProtocol[targetId]) {
> > > > +      // Format:
> > #iframe_id&callId&num_packets&packet_num&block_of_data
> > > > +      src = [relay, '#', encodeLegacyData([from, callId, 1, 0,
> > > > +             encodeLegacyData([from, serviceName, '', '',
> > from].concat(
> > > > +               Array.prototype.slice.call(arguments,
> 3)))])].join('');
> > > > +    } else {
> > > > +      // Format: #targetId & sourceId@callId & packetNum & packetId
> &
> > > > packetData
> > > > +      src = [relay, '#', targetId, '&', from, '@', callId,
> > > > +             '&1&0&', encodeURIComponent(rpcData)].join('');
> > > > +    }
> > > > +
> > > > +    // Conduct the IFPC call by creating the Iframe with
> > > > +    // the relay URL and appended message.
> > > > +    emitInvisibleIframe(src);
> > > >   }
> > > >
> > > > +
> > > >   /**
> > > >    * Helper function to emit an invisible IFrame.
> > > >    * @param {String} src SRC attribute of the IFrame to emit.
> > > > @@ -221,6 +617,15 @@
> > > >      * @member gadgets.rpc
> > > >      */
> > > >     register: function(serviceName, handler) {
> > > > +      if (serviceName == CALLBACK_NAME) {
> > > > +        throw new Error("Cannot overwrite callback service");
> > > > +      }
> > > > +
> > > > +      if (serviceName == DEFAULT_NAME) {
> > > > +        throw new Error("Cannot overwrite default service:"
> > > > +                        + " use registerDefault");
> > > > +      }
> > > > +
> > > >       services[serviceName] = handler;
> > > >     },
> > > >
> > > > @@ -231,6 +636,15 @@
> > > >      * @member gadgets.rpc
> > > >      */
> > > >     unregister: function(serviceName) {
> > > > +      if (serviceName == CALLBACK_NAME) {
> > > > +        throw new Error("Cannot delete callback service");
> > > > +      }
> > > > +
> > > > +      if (serviceName == DEFAULT_NAME) {
> > > > +        throw new Error("Cannot delete default service:"
> > > > +                        + " use unregisterDefault");
> > > > +      }
> > > > +
> > > >       delete services[serviceName];
> > > >     },
> > > >
> > > > @@ -272,12 +686,14 @@
> > > >       if (callback) {
> > > >         callbacks[callId] = callback;
> > > >       }
> > > > -      var from;
> > > > +
> > > > +      // Default to the container calling.
> > > > +      var from = '..';
> > > > +
> > > >       if (targetId === '..') {
> > > >         from = window.name;
> > > > -      } else {
> > > > -        from = '..';
> > > >       }
> > > > +
> > > >       // Not used by legacy, create it anyway...
> > > >       var rpcData = gadgets.json.stringify({
> > > >         s: serviceName,
> > > > @@ -287,53 +703,37 @@
> > > >         t: authToken[targetId]
> > > >       });
> > > >
> > > > +      var channelType = relayChannel;
> > > > +
> > > > +      // If we are told to use the legacy format, then we must
> > > > +      // default to IFPC.
> > > >       if (useLegacyProtocol[targetId]) {
> > > > -        relayChannel = 'ifpc';
> > > > +        channelType = 'ifpc';
> > > >       }
> > > >
> > > > -      switch (relayChannel) {
> > > > -      case 'dpm': // use document.postMessage
> > > > -        var targetDoc = targetId === '..' ? parent.document :
> > > > -
>  frames[targetId].document;
> > > > -        targetDoc.postMessage(rpcData);
> > > > -        break;
> > > > -      case 'wpm': // use window.postMessage
> > > > -        var targetWin = targetId === '..' ? parent :
> frames[targetId];
> > > > -        targetWin.postMessage(rpcData, "*");
> > > > -        break;
> > > > -      default: // use 'ifpc' as a fallback mechanism
> > > > -        var relay = gadgets.rpc.getRelayUrl(targetId);
> > > > -        // TODO split message if too long
> > > > -        var src;
> > > > -        if (useLegacyProtocol[targetId]) {
> > > > -          // #iframe_id&callId&num_packets&packet_num&block_of_data
> > > > -          src = [relay, '#', encodeLegacyData([from, callId, 1, 0,
> > > > -                 encodeLegacyData([from, serviceName, '', '',
> > > > from].concat(
> > > > -                 Array.prototype.slice.call(arguments,
> > 3)))])].join('');
> > > > -        } else {
> > > > -          // Try the frameElement channel if available
> > > > -          try {
> > > > -            if (from === '..') {
> > > > -              // Container-to-gadget
> > > > -              var iframe = document.getElementById(targetId);
> > > > -              if (typeof iframe.__g2c_rpc.__c2g_rpc === 'function')
> {
> > > > -                iframe.__g2c_rpc.__c2g_rpc(rpcData);
> > > > -                return;
> > > > -              }
> > > > -            } else {
> > > > -              // Gadget-to-container
> > > > -              if (typeof window.frameElement.__g2c_rpc ===
> 'function')
> > {
> > > > -                window.frameElement.__g2c_rpc(rpcData);
> > > > -                return;
> > > > -              }
> > > > -            }
> > > > -          } catch (e) {
> > > > -          }
> > > > -          // # targetId & sourceId@callId & packetNum & packetId &
> > > > packetData
> > > > -          src = [relay, '#', targetId, '&', from, '@', callId,
> > > > -                 '&1&0&', encodeURIComponent(rpcData)].join('');
> > > > -        }
> > > > -        emitInvisibleIframe(src);
> > > > +      switch (channelType) {
> > > > +        case 'dpm': // use document.postMessage.
> > > > +          var targetDoc = targetId === '..' ? parent.document :
> > > > +
> >  frames[targetId].document;
> > > > +          targetDoc.postMessage(rpcData);
> > > > +          break;
> > > > +
> > > > +        case 'wpm': // use window.postMessage.
> > > > +          var targetWin = targetId === '..' ? parent :
> > frames[targetId];
> > > > +          targetWin.postMessage(rpcData, "*");
> > > > +          break;
> > > > +
> > > > +        case 'nix': // use NIX.
> > > > +          callNIX(targetId, from, rpcData);
> > > > +          break;
> > > > +
> > > > +        case 'fe': // use FrameElement.
> > > > +          callFrameElement(targetId, from, rpcData);
> > > > +          break;
> > > > +
> > > > +        default: // use 'ifpc' as a fallback mechanism.
> > > > +          callIFPC(targetId, from, rpcData);
> > > > +          break;
> > > >       }
> > > >     },
> > > >
> > > > @@ -372,15 +772,13 @@
> > > >      */
> > > >     setAuthToken: function(targetId, token) {
> > > >       authToken[targetId] = token;
> > > > +      setupFrame(targetId);
> > > >     },
> > > >
> > > >     /**
> > > >      * Gets the RPC relay mechanism.
> > > > -     * @return {String} RPC relay mechanism. Supported types:
> > > > -     *                  'wpm' - Use window.postMessage (defined by
> > > HTML5)
> > > > -     *                  'dpm' - Use document.postMessage (defined by
> > an
> > > > early
> > > > -     *                          draft of HTML5 and implemented by
> > Opera)
> > > > -     *                  'ifpc' - Use invisible IFrames
> > > > +     * @return {String} RPC relay mechanism. See above for
> > > > +     *   a list of supported types.
> > > >      *
> > > >      * @member gadgets.rpc
> > > >      */
> > > >
> > > > Modified:
> > > >
> > >
> >
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > > URL:
> > > >
> > >
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java?rev=672393&r1=672392&r2=672393&view=diff
> > > >
> > > >
> > >
> >
> ==============================================================================
> > > > ---
> > > >
> > >
> >
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > > (original)
> > > > +++
> > > >
> > >
> >
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > > Fri Jun 27 14:01:20 2008
> > > > @@ -221,7 +221,8 @@
> > > >           "a {color:#0000cc;}a:visited {color:#551a8b;}" +
> > > >           "a:active {color:#ff0000;}" +
> > > >           "body{margin: 0px;padding: 0px;background-color:white;}" +
> > > > -          "</style></head>";
> > > > +          "</style><base>" + gadget.getSpec().getUrl().toString() +
> > > > +          "</base></head>";
> > > >     markup.append(boilerPlate);
> > > >     LocaleSpec localeSpec =
> > gadget.getSpec().getModulePrefs().getLocale(
> > > >         gadget.getContext().getLocale());
> > > >
> > > > Added:
> > > incubator/shindig/trunk/javascript/container/rpctest_container.html
> > > > URL:
> > > >
> > >
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_container.html?rev=672393&view=auto
> > > >
> > > >
> > >
> >
> ==============================================================================
> > > > ---
> incubator/shindig/trunk/javascript/container/rpctest_container.html
> > > > (added)
> > > > +++
> incubator/shindig/trunk/javascript/container/rpctest_container.html
> > > Fri
> > > > Jun 27 14:01:20 2008
> > > > @@ -0,0 +1,90 @@
> > > > +<!--
> > > > +  Simple page for testing gadgets.rpc performance.
> > > > +  Allows you to add a simulated "gadget" (in this case just a static
> > > > +  HTML page which loads gadgets.rpc also), and pass some
> > > > +  specified number of random messages of specified size to
> > > > +  and from it.
> > > > +
> > > > +  A simple recommended setup is to start up two instances of
> > > > +  the Shindig Gadgets Server on two separate ports to test
> > > > +  "real" cross-domain communication, since port is factored
> > > > +  into the same-domain policy enforced by browsers.
> > > > +
> > > > +  If your servers are on localhost:8080 and localhost:8081, then
> hit:
> > > > +
> > http://localhost:8080/gadgets/files/container/rpctest_container.html?
> > > \
> > > > +
> http://localhost:8081/gadgets/files/container/rpctest_gadget.html&\<http://localhost:8081/gadgets/files/container/rpctest_gadget.html&%5C>
> > > > +
> > > >
> > >
> >
> http://localhost:8081/gadgets/files/container/rpc_relay.uncompressed.html
> > > > +
> > > > +  (Note the backslashes should be removed, as they exist for
> > formatting
> > > > only.)
> > > > +
> > > > +  There are two arguments, separated by an ampersand:
> > > > +  1. URL of the "gadget" test page.
> > > > +  2. URL of the "gadget" test page's RPC relay (for browsers
> requiring
> > > > it).
> > > > +-->
> > > > +<html>
> > > > +  <head>
> > > > +    <title>gadgets.rpc Performance Tests: Container</title>
> > > > +    <script src="../../js/rpc.js?c=1&debug=1"></script>
> > > > +    <script src="rpctest_perf.js"></script>
> > > > +    <script>
> > > > +      function initTest() {
> > > > +        var container = document.getElementById("container");
> > > > +
> > > > +        // query string is assumed to be the "gadget" URL as arg 1,
> > > > +        // and optionally the relay URL as arg 2
> > > > +        var pageArgs =
> window.location.search.substring(1).split('&');
> > > > +        var gadgetUrl = pageArgs[0];
> > > > +        var secret = Math.round(Math.random()*10000000);
> > > > +        if (pageArgs[1]) {
> > > > +          gadgets.rpc.setRelayUrl('gadget', pageArgs[1]);
> > > > +        }
> > > > +        var containerRelay = pageArgs[2] || '';
> > > > +        container.innerHTML = "<iframe id='gadget' name='gadget'
> > > > height=300 width=300 src='" + gadgetUrl + "?parent=" + containerRelay
> +
> > > > "#rpctoken=" + secret + "'></iframe>";
> > > > +        gadgets.rpc.setAuthToken('gadget', secret);
> > > > +
> > > > +        initPerfTest();
> > > > +      };
> > > > +    </script>
> > > > +  </head>
> > > > +  <body onload="initTest();">
> > > > +    <div>gadgets.rpc Performance: Container Page</div><hr/>
> > > > +    <div>Test<br/>
> > > > +      <ul>
> > > > +        <li>Number of messages to send:
> > > > +          <select name="num_msgs" id="num_msgs">
> > > > +            <option value="1">1</option>
> > > > +            <option value="10">10</option>
> > > > +            <option value="100" selected>100</option>
> > > > +            <option value="1000">1000</option>
> > > > +          </select>
> > > > +        </li>
> > > > +        <li>Message size:
> > > > +          <select name="msg_size" id="msg_size">
> > > > +            <option value="10">10 B</option>
> > > > +            <option value="100">100 B</option>
> > > > +            <option value="1024" selected>1 kB</option>
> > > > +            <option value="10240">10 kB</option>
> > > > +            <option value="102400">100 kB</option>
> > > > +            <option value="1048576">1 MB</option>
> > > > +          </select>
> > > > +        </li>
> > > > +        <li>
> > > > +          <input type="button" value="Start The Test!"
> > > > onclick="runPerfTest('gadget');" />
> > > > +        </li>
> > > > +      </ul>
> > > > +    </div>
> > > > +    <div id="test_running" style="display:none;">
> > > > +      Running test...
> > > > +    </div>
> > > > +    <div id="results" style="display:none;">
> > > > +      Results: Gadget-to-Container<br/>
> > > > +      Messages: <span id="results_num_received"></span>, Bytes:
> <span
> > > > id="results_bytes_received"></span> <span id="in_or_out"></span><br/>
> > > > +      Time elapsed for test run: <span
> > > id="results_time_used"></span><br/>
> > > > +      Messages/second: <span id="results_msgs_per_sec"></span><br/>
> > > > +      Bytes/second: <span id="results_bytes_per_sec"></span>
> > > > +    </div>
> > > > +    <hr/>
> > > > +    <div>Gadget:</div>
> > > > +    <div id="container"></div>
> > > > +  </body>
> > > > +</html>
> > > >
> > > > Added:
> incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > > > URL:
> > > >
> > >
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_gadget.html?rev=672393&view=auto
> > > >
> > > >
> > >
> >
> ==============================================================================
> > > > --- incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > > > (added)
> > > > +++ incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > Fri
> > > > Jun 27 14:01:20 2008
> > > > @@ -0,0 +1,49 @@
> > > > +<html>
> > > > +  <head>
> > > > +    <title>gadgets.rpc Performance Tests: Gadget</title>
> > > > +    <script src="../../js/rpc.js?c=1&debug=1"></script>
> > > > +    <script src="rpctest_perf.js"></script>
> > > > +    <script>
> > > > +      var containerRelay = window.location.search.substring(1);
> > > > +      gadgets.rpc.setRelayUrl(null, containerRelay);
> > > > +    </script>
> > > > +  </head>
> > > > +  <body onload="initPerfTest();">
> > > > +    <div>gadgets.rpc Performance: "Gadget" page</div><hr/>
> > > > +    <div>Test<br/>
> > > > +      <ul>
> > > > +        <li>Number of messages to send:
> > > > +          <select name="num_msgs" id="num_msgs">
> > > > +            <option value="1">1</option>
> > > > +            <option value="10">10</option>
> > > > +            <option value="100" selected>100</option>
> > > > +            <option value="1000">1000</option>
> > > > +          </select>
> > > > +        </li>
> > > > +        <li>Message size:
> > > > +          <select name="msg_size" id="msg_size">
> > > > +            <option value="10">10 B</option>
> > > > +            <option value="100">100 B</option>
> > > > +            <option value="1024" selected>1 kB</option>
> > > > +            <option value="10240">10 kB</option>
> > > > +            <option value="102400">100 kB</option>
> > > > +            <option value="1048576">1 MB</option>
> > > > +          </select>
> > > > +        </li>
> > > > +        <li>
> > > > +          <input type="button" value="Start The Test!"
> > > > onclick="runPerfTest();" />
> > > > +        </li>
> > > > +      </ul>
> > > > +    </div>
> > > > +    <div id="test_running" style="display:none;">
> > > > +      Running test...
> > > > +    </div>
> > > > +    <div id="results" style="display:none;">
> > > > +      Results: Gadget-to-Container<br/>
> > > > +      Messages: <span id="results_num_received"></span>, Bytes:
> <span
> > > > id="results_bytes_received"></span> <span id="in_or_out"></span><br/>
> > > > +      Time elapsed for test run: <span
> > > id="results_time_used"></span><br/>
> > > > +      Messages/second: <span id="results_msgs_per_sec"></span><br/>
> > > > +      Bytes/second: <span id="results_bytes_per_sec"></span>
> > > > +    </div>
> > > > +  </body>
> > > > +</html>
> > > >
> > > > Added: incubator/shindig/trunk/javascript/container/rpctest_perf.js
> > > > URL:
> > > >
> > >
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_perf.js?rev=672393&view=auto
> > > >
> > > >
> > >
> >
> ==============================================================================
> > > > --- incubator/shindig/trunk/javascript/container/rpctest_perf.js
> > (added)
> > > > +++ incubator/shindig/trunk/javascript/container/rpctest_perf.js Fri
> > Jun
> > > 27
> > > > 14:01:20 2008
> > > > @@ -0,0 +1,93 @@
> > > > +var perfStats = null;
> > > > +var currentRun = {};
> > > > +
> > > > +function perfService(message) {
> > > > +  if (perfStats.numResults++ === 0) {
> > > > +    perfStats.firstMsg = message; // stored since it has "real"
> start
> > > time
> > > > +  }
> > > > +  perfStats.bytesReceived += message.length;
> > > > +};
> > > > +
> > > > +function clearPerfStats(inOrOut) {
> > > > +  perfStats = {
> > > > +    numResults: 0,
> > > > +    bytesReceived: 0,
> > > > +    firstMsg: null
> > > > +  };
> > > > +
> > > > +  document.getElementById("in_or_out").innerHTML = inOrOut;
> > > > +
> > > > +  // hide results fields
> > > > +  document.getElementById("results").style.display = "none";
> > > > +};
> > > > +
> > > > +function completePerfStats() {
> > > > +  perfStats.timeEnded = new Date().getTime();
> > > > +
> > > > +  // get time started from the first sent message
> > > > +  perfStats.timeStarted = perfStats.firstMsg.substr(0,
> > > > perfStats.firstMsg.indexOf(':'));
> > > > +
> > > > +  var timeUsedMs = perfStats.timeEnded - perfStats.timeStarted;
> > > > +
> > > > +  // fill in fields
> > > > +  document.getElementById("results_num_received").innerHTML =
> > > > perfStats.numResults;
> > > > +  document.getElementById("results_bytes_received").innerHTML =
> > > > perfStats.bytesReceived;
> > > > +  document.getElementById("results_time_used").innerHTML =
> timeUsedMs
> > +
> > > > "ms";
> > > > +  document.getElementById("results_msgs_per_sec").innerHTML =
> > > > (perfStats.numResults / (timeUsedMs / 1000));
> > > > +  document.getElementById("results_bytes_per_sec").innerHTML =
> > > > (perfStats.bytesReceived / (timeUsedMs / 1000));
> > > > +  document.getElementById("test_running").style.display = "none";
> > > > +  document.getElementById("results").style.display = "";
> > > > +};
> > > > +
> > > > +function initPerfTest() {
> > > > +  clearPerfStats();
> > > > +  gadgets.rpc.register("perf_service", perfService);
> > > > +  gadgets.rpc.register("clear_perf_stats", clearPerfStats);
> > > > +  gadgets.rpc.register("complete_perf_stats", completePerfStats);
> > > > +};
> > > > +
> > > > +var alphabet =
> > > > "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
> > > _-*&(){}'";
> > > > +
> > > > +function sendPerfMessage() {
> > > > +  var msgToSend = currentRun.msg;
> > > > +  if (currentRun.curMsgId++ <= 1) {
> > > > +    var nowString = new Date().getTime() + ':';
> > > > +    msgToSend = nowString +
> > currentRun.msg.substring(nowString.length);
> > > > +  }
> > > > +
> > > > +  gadgets.rpc.call(currentRun.targetId, "perf_service", null,
> > > msgToSend);
> > > > +  if (currentRun.curMsgId < currentRun.endMsgId) {
> > > > +    // loop, giving up execution in case rpc technique demands it
> > > > +    window.setTimeout(sendPerfMessage, 0);
> > > > +  } else {
> > > > +    // send finisher
> > > > +    window.setTimeout(function() {
> > gadgets.rpc.call(currentRun.targetId,
> > > > "complete_perf_stats", null); }, 0);
> > > > +  }
> > > > +};
> > > > +
> > > > +function runPerfTest(targetId) {
> > > > +  document.getElementById("test_running").style.display = "";
> > > > +
> > > > +  // initialize the current run
> > > > +  var num_msgs = document.getElementById("num_msgs").value;
> > > > +  var msg_size = document.getElementById("msg_size").value;
> > > > +
> > > > +  currentRun.targetId = targetId;
> > > > +  currentRun.curMsgId = 0;
> > > > +  currentRun.endMsgId = num_msgs;
> > > > +
> > > > +  var msg = [];
> > > > +  for (var i = 0; i < msg_size; ++i) {
> > > > +    msg[i] =
> > alphabet.charAt(Math.round(Math.random(alphabet.length)));
> > > > +  }
> > > > +  currentRun.msg = msg.join('');
> > > > +
> > > > +  // clear local perf stats
> > > > +  clearPerfStats("(outbound)");
> > > > +
> > > > +  // clear target perf stats
> > > > +  gadgets.rpc.call(targetId, "clear_perf_stats", null, "(inbound)");
> > > > +
> > > > +  // kick off the send loop
> > > > +  sendPerfMessage();
> > > > +};
> > > >
> > > >
> > > >
> > >
> >
>

Re: svn commit: r672393 - in /incubator/shindig/trunk: features/rpc/ java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ javascript/container/

Posted by John Hjelmstad <fa...@google.com>.
Re: GadgetRenderingTask.. that was a mistake. SVN committed it despite my
desire for that not to be so. Reverting.

On Fri, Jun 27, 2008 at 3:45 PM, John Hjelmstad <fa...@google.com> wrote:

> As indicated in the CL, I tested this on half a dozen browsers. We don't
> have unit tests for it. I've also sent the code and inquiries about the
> technique to several poeple, some of whom expressed concerns but no ability
> to actively exploit the code. The main concern was around VBScript
> knowledge, which few people I know extensively have. That was true of IFPC
> when it was originally released as well. If any attacks are found, they will
> be actively closed or the technique will be removed. Would you prefer that
> no code, irrespective of its value, ever be submitted? It's possible for any
> code to have bugs, and a lot of code to have security holes. If found,
> they're fixed.
>
> John
>
>
> On Fri, Jun 27, 2008 at 3:38 PM, Kevin Brown <et...@google.com> wrote:
>
>> Please actually test your code or send it to other shindig committers
>> before
>> you commit it. Your change to GadgetRenderingTask breaks proxying and
>> makeRequest in several cases. Fix it.
>>
>> Have you run this by Brian and/or Mike Samuel? They both expressed doubts
>> about whether this mechanism was actually secure.
>>
>> On Fri, Jun 27, 2008 at 2:06 PM, John Hjelmstad <fa...@google.com> wrote:
>>
>> > +jschorr in case there are comments.
>> >
>> > On Fri, Jun 27, 2008 at 2:01 PM, <jo...@apache.org> wrote:
>> >
>> > > Author: johnh
>> > > Date: Fri Jun 27 14:01:20 2008
>> > > New Revision: 672393
>> > >
>> > > URL: http://svn.apache.org/viewvc?rev=672393&view=rev
>> > > Log:
>> > > Cleaned up gadgets.rpc library implementation, and implemented fast IE
>> > > transport layer.
>> > >
>> > > Credit:
>> > >        Joseph Schorr (jschorr@google.com) and I implemented this
>> > together,
>> > > but he
>> > >        really did all the hard work, as well as developing the NIX
>> > > technique's
>> > >        fundamentals. Huge thanks to Joseph!
>> > >
>> > > Details:
>> > >        * Created a new relayChannel for Firefox frameElement
>> technique,
>> > >          making its implementation more linear to read.
>> > >        * Consolidated all transport-specific code in setupChannel(),
>> > > setupFrame(),
>> > >          and callX(), where X = relay type. This refactoring makes
>> setup
>> > > and use
>> > >          of each transport more clear, and makes it possible in later
>> > > revisions
>> > >          to separate out each method as its own class. In theory, if
>> we
>> > can
>> > > trust
>> > >          things like User-Agent headers, we can use this to reduce
>> rpc.js
>> > > code
>> > >          size significantly by only including the needed transport(s).
>> > >        * Implemented "NIX" - Native IE XDC. This method exploits the
>> fact
>> > >          that in IE6 and IE7 the window.opener property can be both
>> set
>> > and
>> > >          read across domain boundaries for an IFRAME, and that any
>> object
>> > >          can be passed through it. In particular, functions
>> implementing
>> > >          message passing can be passed. These can't be JS functions,
>> > > however,
>> > >          since those can leak sender context. So, VBScript (COM)
>> objects
>> > >          wrapping these are passed to maintain context isolation.
>> > >          - Requires for IE6/7 that rpc.js be included in the container
>> > >            at load time, not dynamically. TODO added to consider
>> > detecting
>> > >            whether dynamic loading is happening (at which point we
>> should
>> > >            fall back to IFPC).
>> > >          - Message channel handshake *and* message passing validated
>> > >            using authToken, as with other channels.
>> > >          - Impl requires that gadget call container first - else IFPC
>> > >            is used. This is the same as the frameElement technique. We
>> > >            could add a setInterval()-based initialization routine
>> later
>> > >            if needed. To date, the only gadgets.rpc calls made from
>> > >            container to gadget without gadget to container first are
>> > >            in theory pubsub messages.
>> > >          - Extensive documentation on this technique and the others
>> > >            added to the comments, since they're stripped out in the
>> > >            majority case.
>> > >        * Implemented quick-and-dirty performance testing page
>> verifying
>> > >          the library works and giving basic performance metrics.
>> > >          This needs to be improved! But does the trick for now...
>> > >
>> > > Testing/Data:
>> > >
>> > > Library verified on the following browsers with the RPC test page. For
>> > > each,
>> > > a "bandwidth" measure was taken by averaging the results of 6 G2C and
>> C2G
>> > > runs
>> > > with 100 messages being passed of 1K apiece. Units are 1K
>> messages/sec.
>> > > The "latency" value is the average amount of time taken, over 6 G2C
>> and
>> > C2G
>> > > runs,
>> > > to send 1 1K message. Happily, for all browsers save Safari (soon due
>> to
>> > > get
>> > > window.postMessage), latency is around or below time required to
>> achieve
>> > > a cross-domain call without user-perceived delay.
>> > >
>> > >        * Firefox 3 (WPM) - bandwidth: 88 kB/sec; latency: 18ms.
>> > >        * Firefox 2 (FE)  - bandwidth: 80 kB/sec; latency: 15ms.
>> > >        * Opera 9   (WPM) - bandwidth: 85 kB/sec; latency: 7ms.
>> > >        * IE6       (NIX) - bandwidth: 64 kB/sec; latency: 18ms.
>> > >        * IE7       (NIX) - bandwidth: 64 kB/sec; latency: 22ms.
>> > >        * Safari 3 (IFPC) - bandwidth: ?6-8kB/sec; latency: ?100-200ms.
>> > >          - Safari is somewhat flaky with the RPC test page, before
>> > >            and after the rpc.js change. Multiple messages seem to
>> > >            confuse it from time to time.
>> > >
>> > >
>> > > Added:
>> > >    incubator/shindig/trunk/javascript/container/rpctest_container.html
>> > >    incubator/shindig/trunk/javascript/container/rpctest_gadget.html
>> > >    incubator/shindig/trunk/javascript/container/rpctest_perf.js
>> > > Modified:
>> > >    incubator/shindig/trunk/features/rpc/rpc.js
>> > >
>> > >
>> >
>>  incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
>> > >
>> > > Modified: incubator/shindig/trunk/features/rpc/rpc.js
>> > > URL:
>> > >
>> >
>> http://svn.apache.org/viewvc/incubator/shindig/trunk/features/rpc/rpc.js?rev=672393&r1=672392&r2=672393&view=diff
>> > >
>> > >
>> >
>> ==============================================================================
>> > > --- incubator/shindig/trunk/features/rpc/rpc.js (original)
>> > > +++ incubator/shindig/trunk/features/rpc/rpc.js Fri Jun 27 14:01:20
>> 2008
>> > > @@ -18,7 +18,7 @@
>> > >
>> > >  /**
>> > >  * @fileoverview Remote procedure call library for
>> gadget-to-container,
>> > > - * container-to-gadget, and gadget-to-gadget communication.
>> > > + * container-to-gadget, and gadget-to-gadget (thru container)
>> > > communication.
>> > >  */
>> > >
>> > >  var gadgets = gadgets || {};
>> > > @@ -29,6 +29,30 @@
>> > >  * @name gadgets.rpc
>> > >  */
>> > >  gadgets.rpc = function() {
>> > > +  // General constants.
>> > > +  var CALLBACK_NAME = '__cb';
>> > > +  var DEFAULT_NAME = '';
>> > > +
>> > > +  // Consts for FrameElement.
>> > > +  var FE_G2C_CHANNEL = '__g2c_rpc';
>> > > +  var FE_C2G_CHANNEL = '__c2g_rpc';
>> > > +
>> > > +  // Consts for NIX. VBScript doesn't
>> > > +  // allow items to start with _ for some reason,
>> > > +  // so we need to make these names quite unique, as
>> > > +  // they will go into the global namespace.
>> > > +  var NIX_WRAPPER = 'GRPC____NIXVBS_wrapper';
>> > > +  var NIX_GET_WRAPPER = 'GRPC____NIXVBS_get_wrapper';
>> > > +  var NIX_HANDLE_MESSAGE = 'GRPC____NIXVBS_handle_message';
>> > > +  var NIX_CREATE_CHANNEL = 'GRPC____NIXVBS_create_channel';
>> > > +  var NIX_ID_FIELD = 'GRPC____NIXVBS_container';
>> > > +
>> > > +  // JavaScript reference to the NIX VBScript wrappers.
>> > > +  // Gadgets will have but a single channel under
>> > > +  // nix_channels['..'] while containers will have a channel
>> > > +  // per gadget stored under the gadget's ID.
>> > > +  var nix_channels = {};
>> > > +
>> > >   var services = {};
>> > >   var iframePool = [];
>> > >   var relayUrl = {};
>> > > @@ -36,28 +60,135 @@
>> > >   var authToken = {};
>> > >   var callId = 0;
>> > >   var callbacks = {};
>> > > +  var setup = {};
>> > > +
>> > > +  var params = {};
>> > > +
>> > > +  // Load the authentication token for speaking to the container
>> > > +  // from the gadget's parameters, or default to '0' if not found.
>> > > +  if (gadgets.util) {
>> > > +    params = gadgets.util.getUrlParameters();
>> > > +  }
>> > >
>> > > -  var params = gadgets.util.getUrlParameters();
>> > >   authToken['..'] = params.rpctoken || params.ifpctok || 0;
>> > >
>> > > -  // Pick the most efficient RPC relay mechanism
>> > > -  var relayChannel = typeof document.postMessage === 'function' ?
>> 'dpm'
>> > :
>> > > -                     typeof window.postMessage === 'function' ? 'wpm'
>> :
>> > > +  // Pick the most efficient RPC relay mechanism:
>> > > +  //
>> > > +  // * For those browsers that support native messaging (various
>> > > implementations
>> > > +  //   of the HTML5 postMessage method), use that. Officially defined
>> at
>> > > +  //
>> > >
>> http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html.
>> > > +  //
>> > > +  //   postMessage is a native implementation of XDC. A page
>> registers
>> > > that
>> > > +  //   it would like to receive messages by listening the the
>> "message"
>> > > event
>> > > +  //   on the window (document in DPM) object. In turn, another page
>> can
>> > > +  //   raise that event by calling window.postMessage
>> > > (document.postMessage
>> > > +  //   in DPM) with a string representing the message and a string
>> > > +  //   indicating on which domain the receiving page must be to
>> receive
>> > > +  //   the message. The target page will then have its "message"
>> event
>> > > raised
>> > > +  //   if the domain matches and can, in turn, check the origin of
>> the
>> > > message
>> > > +  //   and process the data contained within.
>> > > +  //
>> > > +  //     wpm: postMessage on the window object.
>> > > +  //        - Internet Explorer 8+
>> > > +  //        - Safari (latest nightlies as of 26/6/2008)
>> > > +  //        - Firefox 3+
>> > > +  //        - Opera 9+
>> > > +  //
>> > > +  //     dpm: postMessage on the document object.
>> > > +  //        - Opera 8+
>> > > +  //
>> > > +  // * For Internet Explorer before version 8, the security model
>> allows
>> > a
>> > > +  //   child to set the value of the "opener" property on the parent
>> > > window
>> > > +  //   and vice versa. This method is dubbed "Native IE XDC" (NIX).
>> > > +  //
>> > > +  //   This method works by placing a handler object in the "opener"
>> > > property
>> > > +  //   of a gadget when the container sets up the authentication
>> > > information
>> > > +  //   for that gadget (by calling setAuthToken(...)). At that point,
>> a
>> > > NIX
>> > > +  //   wrapper is created and placed into the gadget by calling
>> > > +  //   theframe.contentWindow.opener = wrapper. Note that as a
>> result,
>> > NIX
>> > > can
>> > > +  //   only be used by a container to call a particular gadget
>> *after*
>> > > that
>> > > +  //   gadget has called the container at least once via NIX.
>> > > +  //
>> > > +  //   The NIX wrappers in this RPC implementation are instances of a
>> > > VBScript
>> > > +  //   class that is created when this implementation loads. The
>> reason
>> > > for
>> > > +  //   using a VBScript class stems from the fact that any object can
>> be
>> > > passed
>> > > +  //   into the opener property.
>> > > +  //   While this is a good thing, as it lets us pass functions and
>> > setup
>> > > a true
>> > > +  //   bidirectional channel via callbacks, it opens a potential
>> > security
>> > > hole
>> > > +  //   by which the other page can get ahold of the "window" or
>> > "document"
>> > > +  //   objects in the parent page and in turn wreak havok. This is
>> due
>> > to
>> > > the
>> > > +  //   fact that any JS object useful for establishing such a
>> > > bidirectional
>> > > +  //   channel (such as a function) can be used to access a function
>> > > +  //   (eg. obj.toString, or a function itself) created in a specific
>> > > context,
>> > > +  //   in particular the global context of the sender. Suppose
>> container
>> > > +  //   domain C passes object obj to gadget on domain G. Then the
>> gadget
>> > > can
>> > > +  //   access C's global context using:
>> > > +  //   var parentWindow = (new obj.toString.constructor("return
>> > > window;"))();
>> > > +  //   Nulling out all of obj's properties doesn't fix this, since IE
>> > > helpfully
>> > > +  //   restores them to their original values if you do something
>> like:
>> > > +  //   delete obj.toString; delete obj.toString;
>> > > +  //   Thus, we wrap the necessary functions and information inside a
>> > > VBScript
>> > > +  //   object. VBScript objects in IE, like DOM objects, are in fact
>> COM
>> > > +  //   wrappers when used in JavaScript, so we can safely pass them
>> > around
>> > > +  //   without worrying about a breach of context while at the same
>> time
>> > > +  //   allowing them to act as a pass-through mechanism for
>> information
>> > > +  //   and function calls. The implementation details of this
>> VBScript
>> > > wrapper
>> > > +  //   can be found in the setupChannel() method below.
>> > > +  //
>> > > +  //     nix: Internet Explorer-specific window.opener trick.
>> > > +  //       - Internet Explorer 6
>> > > +  //       - Internet Explorer 7
>> > > +  //
>> > > +  // * For Gecko-based browsers, the security model allows a child to
>> > call
>> > > a
>> > > +  //   function on the frameElement of the iframe, even if the child
>> is
>> > in
>> > > +  //   a different domain. This method is dubbed "frameElement" (fe).
>> > > +  //
>> > > +  //   The ability to add and call such functions on the frameElement
>> > > allows
>> > > +  //   a bidirectional channel to be setup via the adding of simple
>> > > function
>> > > +  //   references on the frameElement object itself. In this
>> > > implementation,
>> > > +  //   when the container sets up the authentication information for
>> > that
>> > > gadget
>> > > +  //   (by calling setAuth(...)) it as well adds a special function
>> on
>> > the
>> > > +  //   gadget's iframe. This function can then be used by the gadget
>> to
>> > > send
>> > > +  //   messages to the container. In turn, when the gadget tries to
>> send
>> > a
>> > > +  //   message, it checks to see if this function has its own
>> function
>> > > stored
>> > > +  //   that can be used by the container to call the gadget. If not,
>> the
>> > > +  //   function is created and subsequently used by the container.
>> > > +  //   Note that as a result, FE can only be used by a container to
>> call
>> > a
>> > > +  //   particular gadget *after* that gadget has called the container
>> at
>> > > +  //   least once via FE.
>> > > +  //
>> > > +  //     fe: Gecko-specific frameElement trick.
>> > > +  //        - Firefox 1+
>> > > +  //
>> > > +  // * For all others, we have a fallback mechanism known as "ifpc".
>> > IFPC
>> > > +  //   exploits the fact that while same-origin policy prohibits a
>> frame
>> > > from
>> > > +  //   accessing members on a window not in the same domain, that
>> frame
>> > > can,
>> > > +  //   however, navigate the window heirarchy (via parent). This is
>> > > exploited by
>> > > +  //   having a page on domain A that wants to talk to domain B
>> create
>> > an
>> > > iframe
>> > > +  //   on domain B pointing to a special relay file and with a
>> message
>> > > encoded
>> > > +  //   after the hash (#). This relay, in turn, finds the page on
>> domain
>> > > B, and
>> > > +  //   can call a receipt function with the message given to it. The
>> > relay
>> > > URL
>> > > +  //   used by each caller is set via the gadgets.rpc.setRelayUrl(..)
>> > and
>> > > +  //   *must* be called before the call method is used.
>> > > +  //
>> > > +  //     ifpc: Iframe-based method, utilizing a relay page, to send a
>> > > message.
>> > > +  //
>> > > +  var relayChannel = typeof window.postMessage === 'function' ? 'wpm'
>> :
>> > > +                    typeof document.postMessage === 'function' ?
>> 'dpm' :
>> > > +                    window.ActiveXObject ? 'nix' :
>> > > +                     navigator.product === 'Gecko' ? 'fe' :
>> > >                      'ifpc';
>> > > -  if (relayChannel === 'dpm' || relayChannel === 'wpm') {
>> > > -    window.addEventListener('message', function(packet) {
>> > > -      // TODO validate packet.domain for security reasons
>> > > -      process(gadgets.json.parse(packet.data));
>> > > -    }, false);
>> > > -  }
>> > >
>> > > -  // Default RPC handler
>> > > -  services[''] = function() {
>> > > +  // Conduct any setup necessary for the chosen channel.
>> > > +  setupChannel();
>> > > +
>> > > +  // Create the Default RPC handler.
>> > > +  services[DEFAULT_NAME] = function() {
>> > >     throw new Error('Unknown RPC service: ' + this.s);
>> > >   };
>> > >
>> > > -  // Special RPC handler for callbacks
>> > > -  services['__cb'] = function(callbackId, result) {
>> > > +  // Create a Special RPC handler for callbacks.
>> > > +  services[CALLBACK_NAME] = function(callbackId, result) {
>> > >     var callback = callbacks[callbackId];
>> > >     if (callback) {
>> > >       delete callbacks[callbackId];
>> > > @@ -66,6 +197,147 @@
>> > >   };
>> > >
>> > >   /**
>> > > +   * Conducts any initial global work necessary to setup the
>> > > +   * channel type chosen.
>> > > +   */
>> > > +  function setupChannel() {
>> > > +    // If the channel type is one of the native
>> > > +    // postMessage based ones, setup the handler to receive
>> > > +    // messages.
>> > > +    if (relayChannel === 'dpm' || relayChannel === 'wpm') {
>> > > +      window.addEventListener('message', function(packet) {
>> > > +        // TODO validate packet.domain for security reasons
>> > > +        process(gadgets.json.parse(packet.data));
>> > > +      }, false);
>> > > +    }
>> > > +
>> > > +    // If the channel type is NIX, we need to ensure the
>> > > +    // VBScript wrapper code is in the page and that the
>> > > +    // global Javascript handlers have been set.
>> > > +    if (relayChannel === 'nix') {
>> > > +      // VBScript methods return a type of 'unknown' when
>> > > +      // checked via the typeof operator in IE. Fortunately
>> > > +      // for us, this only applies to COM objects, so we
>> > > +      // won't see this for a real Javascript object.
>> > > +      if (typeof window[NIX_GET_WRAPPER] !== 'unknown') {
>> > > +        window[NIX_HANDLE_MESSAGE] = function(data) {
>> > > +          process(gadgets.json.parse(data));
>> > > +        };
>> > > +
>> > > +        window[NIX_CREATE_CHANNEL] = function(name, channel, token) {
>> > > +          // Verify the authentication token of the gadget trying
>> > > +          // to create a channel for us.
>> > > +          if (authToken[name] == token) {
>> > > +            nix_channels[name] = channel;
>> > > +          }
>> > > +        };
>> > > +
>> > > +        // Inject the VBScript code needed.
>> > > +        var vbscript =
>> > > +          '<scr' + 'ipt language="VBScript">'
>> > > +          // We create a class to act as a wrapper for
>> > > +          // a Javascript call, to prevent a break in of
>> > > +          // the context.
>> > > +          + 'Class ' + NIX_WRAPPER + '\n '
>> > > +
>> > > +          // An internal member for keeping track of the
>> > > +          // name of the document (container or gadget)
>> > > +          // for which this wrapper is intended. For
>> > > +          // those wrappers created by gadgets, this is not
>> > > +          // used (although it is set to "..")
>> > > +          + 'Private m_Intended\n'
>> > > +
>> > > +          // Method for internally setting the value
>> > > +          // of the m_Intended property.
>> > > +          + 'Public Sub SetIntendedName(name)\n '
>> > > +          + 'm_Intended = name\n'
>> > > +          + 'End Sub\n'
>> > > +
>> > > +          // A wrapper method which actually causes a
>> > > +          // message to be sent to the other context.
>> > > +          + 'Public Sub SendMessage(data)\n '
>> > > +          + NIX_HANDLE_MESSAGE + '(data)\n'
>> > > +          + 'End Sub\n'
>> > > +
>> > > +          // Method for setting up the container->gadget
>> > > +          // channel. Not strictly needed in the gadget's
>> > > +          // wrapper, but no reason to get rid of it. Note here
>> > > +          // that we pass the intended name to the NIX_CREATE_CHANNEL
>> > > +          // method so that it can save the channel in the proper
>> place
>> > > +          // *and* verify the channel via the authentication token
>> > passed
>> > > +          // here.
>> > > +          + 'Public Sub CreateChannel(channel, auth)\n '
>> > > +          + 'Call ' + NIX_CREATE_CHANNEL + '(m_Intended, channel,
>> > auth)\n'
>> > > +          + 'End Sub\n'
>> > > +
>> > > +          // An empty field with a unique identifier to
>> > > +          // prevent the code from confusing this wrapper
>> > > +          // with a run-of-the-mill value found in window.opener.
>> > > +          + 'Public Sub ' + NIX_ID_FIELD + '()\n '
>> > > +          + 'End Sub\n'
>> > > +          + 'End Class\n '
>> > > +
>> > > +          // Function to get a reference to the wrapper.
>> > > +          + 'Function ' + NIX_GET_WRAPPER + '(name)\n'
>> > > +          + 'Dim wrap\n'
>> > > +          + 'Set wrap = New ' + NIX_WRAPPER + '\n'
>> > > +          + 'wrap.SetIntendedName name\n'
>> > > +          + 'Set ' + NIX_GET_WRAPPER + ' = wrap\n'
>> > > +          + 'End Function'
>> > > +          + '</scr' + 'ipt>'
>> > > +
>> > > +          // Resets the "default" scripting language in IE back
>> > > +          // to Javascript. This is needed or else any script
>> > > +          // tags without a proper Language="..." will be treated as
>> > > +          // VBScript once this code is added to the document.
>> > > +          + '<scri' + 'pt language="JScript"></scr' + 'ipt>';
>> > > +
>> > > +        // Note that this code can only be run once the document has
>> > > +        // fully loaded.
>> > > +        // TODO: Perhaps add some sort of check here for this?
>> > > +        document.write(vbscript);
>> > > +      }
>> > > +    }
>> > > +  }
>> > > +
>> > > +  /**
>> > > +   * Conducts any frame-specific work necessary to setup
>> > > +   * the channel type chosen. This method is called when
>> > > +   * the container page first registers the gadget in the
>> > > +   * RPC mechanism. Gadgets, in turn, will complete the setup
>> > > +   * of the channel once they send their first messages.
>> > > +   */
>> > > +  function setupFrame(frameId) {
>> > > +    if (setup[frameId]) {
>> > > +      return;
>> > > +    }
>> > > +
>> > > +    if (relayChannel === 'fe') {
>> > > +      try {
>> > > +        var frame = document.getElementById(frameId);
>> > > +        frame[FE_G2C_CHANNEL] = function(args) {
>> > > +          process(gadgets.json.parse(args));
>> > > +        };
>> > > +      } catch (e) {
>> > > +        // Something went wrong. System will fallback to
>> > > +        // IFPC.
>> > > +      }
>> > > +    }
>> > > +
>> > > +    if (relayChannel === 'nix') {
>> > > +      try {
>> > > +        var frame = document.getElementById(frameId);
>> > > +        frame.contentWindow.opener =
>> window[NIX_GET_WRAPPER](frameId);
>> > > +      } catch (e) {
>> > > +        // Something went wrong. System will fallback to
>> > > +        // IFPC.
>> > > +      }
>> > > +    }
>> > > +
>> > > +    setup[frameId] = true;
>> > > +  }
>> > > +
>> > > +  /**
>> > >    * Encodes arguments for the legacy IFPC wire format.
>> > >    *
>> > >    * @param {Object} args
>> > > @@ -86,8 +358,17 @@
>> > >    * @private
>> > >    */
>> > >   function process(rpc) {
>> > > +    //
>> > > +    // RPC object contents:
>> > > +    //   s: Service Name
>> > > +    //   f: From
>> > > +    //   c: The callback ID or 0 if none.
>> > > +    //   a: The arguments for this RPC call.
>> > > +    //   t: The authentication token.
>> > > +    //
>> > >     if (rpc && typeof rpc.s === 'string' && typeof rpc.f === 'string'
>> &&
>> > >         rpc.a instanceof Array) {
>> > > +
>> > >       // Validate auth token.
>> > >       if (authToken[rpc.f]) {
>> > >         // We allow type coercion here because all the url params are
>> > > strings.
>> > > @@ -96,43 +377,158 @@
>> > >         }
>> > >       }
>> > >
>> > > -      // The Gecko engine used by FireFox etc. allows an IFrame to
>> > > directly call
>> > > -      // methods on the frameElement property added by the container
>> > page
>> > > even
>> > > -      // if their domains don't match.
>> > > -      // Here we try to set up a relay channel using the frameElement
>> > > technique
>> > > -      // to greatly reduce the latency of cross-domain calls if the
>> > > postMessage
>> > > -      // method is not supported.
>> > > -      if (relayChannel === 'ifpc') {
>> > > -        if (rpc.f === '..') {
>> > > -          // Container-to-gadget call
>> > > -          try {
>> > > -            var fel = window.frameElement;
>> > > -            if (typeof fel.__g2c_rpc === 'function' &&
>> > > -                typeof fel.__g2c_rpc.__c2g_rpc != 'function') {
>> > > -              fel.__g2c_rpc.__c2g_rpc = function(args) {
>> > > -                process(gadgets.json.parse(args));
>> > > -              };
>> > > -            }
>> > > -          } catch (e) {
>> > > -          }
>> > > -        } else {
>> > > -          // Gadget-to-container call
>> > > -          var iframe = document.getElementById(rpc.f);
>> > > -          if (iframe && typeof iframe.__g2c_rpc != 'function') {
>> > > -            iframe.__g2c_rpc = function(args) {
>> > > +      // Call the requested RPC service.
>> > > +      var result = (services[rpc.s] ||
>> > > +                    services[DEFAULT_NAME]).apply(rpc, rpc.a);
>> > > +
>> > > +      // If there is a callback for this service, initiate it as
>> well.
>> > > +      if (rpc.c) {
>> > > +        gadgets.rpc.call(rpc.f, CALLBACK_NAME, null, rpc.c, result);
>> > > +      }
>> > > +    }
>> > > +  }
>> > > +
>> > > +  /**
>> > > +   * Attempts to conduct an RPC call to the specified
>> > > +   * target with the specified data via the NIX
>> > > +   * method. If this method fails, the system attempts again
>> > > +   * using the known default of IFPC.
>> > > +   *
>> > > +   * @param {String} targetId Module Id of the RPC service provider.
>> > > +   * @param {String} from Module Id of the calling provider.
>> > > +   * @param {Object} rpcData The RPC data for this call.
>> > > +   */
>> > > +  function callNIX(targetId, from, rpcData) {
>> > > +    try {
>> > > +      if (from != '..') {
>> > > +        // Call from gadget to the container.
>> > > +        var handler = nix_channels['..'];
>> > > +
>> > > +        // If the gadget has yet to retrieve a reference to
>> > > +        // the NIX handler, try to do so now. We don't do a
>> > > +        // typeof(window.opener[NIX_ID_FIELD]) check here
>> > > +        // because it means accessing that field on the COM object,
>> > which,
>> > > +        // being an internal function reference, is not allowed.
>> > > +        // "in" works because it merely checks for the prescence of
>> > > +        // the key, rather than actually accessing the object's
>> > property.
>> > > +        if (!handler && window.opener && NIX_ID_FIELD in
>> window.opener)
>> > {
>> > > +          handler = nix_channels['..'] = window.opener;
>> > > +
>> > > +          // Create the channel to the parent/container. We pass both
>> > our
>> > > +          // own wrapper and our authentication token for
>> verification.
>> > > +          handler.CreateChannel(window[NIX_GET_WRAPPER]('..'),
>> > > authToken['..']);
>> > > +        }
>> > > +
>> > > +        // If we have a handler, call it.
>> > > +        if (handler) {
>> > > +          handler.SendMessage(rpcData);
>> > > +          return;
>> > > +        }
>> > > +      } else {
>> > > +        // Call from container to a gadget[targetId].
>> > > +
>> > > +        // If we have a handler, call it.
>> > > +        if (nix_channels[targetId]) {
>> > > +          nix_channels[targetId].SendMessage(rpcData);
>> > > +          return;
>> > > +        }
>> > > +      }
>> > > +    } catch (e) {
>> > > +    }
>> > > +
>> > > +    // If we have reached this point, something has failed
>> > > +    // with the NIX method, so we default to using
>> > > +    // IFPC for this call.
>> > > +    callIFPC(targetId, from, rpcData);
>> > > +  }
>> > > +
>> > > +  /**
>> > > +   * Attempts to conduct an RPC call to the specified
>> > > +   * target with the specified data via the FrameElement
>> > > +   * method. If this method fails, the system attempts again
>> > > +   * using the known default of IFPC.
>> > > +   *
>> > > +   * @param {String} targetId Module Id of the RPC service provider.
>> > > +   * @param {String} from Module Id of the calling provider.
>> > > +   * @param {Object} rpcData The RPC data for this call.
>> > > +   */
>> > > +  function callFrameElement(targetId, from, rpcData) {
>> > > +    try {
>> > > +      if (from != '..') {
>> > > +        // Call from gadget to the container.
>> > > +        var fe = window.frameElement;
>> > > +
>> > > +        if (typeof fe[FE_G2C_CHANNEL] === 'function') {
>> > > +          // Complete the setup of the FE channel if need be.
>> > > +          if (typeof fe[FE_G2C_CHANNEL][FE_C2G_CHANNEL] !==
>> 'function')
>> > {
>> > > +            fe[FE_G2C_CHANNEL][FE_C2G_CHANNEL] = function(args) {
>> > >               process(gadgets.json.parse(args));
>> > >             };
>> > >           }
>> > > +
>> > > +          // Conduct the RPC call.
>> > > +          fe[FE_G2C_CHANNEL](rpcData);
>> > > +          return;
>> > >         }
>> > > -      }
>> > > +      } else {
>> > > +        // Call from container to gadget[targetId].
>> > > +        var frame = document.getElementById(targetId);
>> > >
>> > > -      var result = (services[rpc.s] || services['']).apply(rpc,
>> rpc.a);
>> > > -      if (rpc.c) {
>> > > -        gadgets.rpc.call(rpc.f, '__cb', null, rpc.c, result);
>> > > +        if (typeof frame[FE_G2C_CHANNEL] === 'function' &&
>> > > +            typeof frame[FE_G2C_CHANNEL][FE_C2G_CHANNEL] ===
>> 'function')
>> > {
>> > > +
>> > > +          // Conduct the RPC call.
>> > > +          frame[FE_G2C_CHANNEL][FE_C2G_CHANNEL](rpcData);
>> > > +          return;
>> > > +        }
>> > >       }
>> > > +    } catch (e) {
>> > > +    }
>> > > +
>> > > +    // If we have reached this point, something has failed
>> > > +    // with the FrameElement method, so we default to using
>> > > +    // IFPC for this call.
>> > > +    callIFPC(targetId, from, rpcData);
>> > > +  }
>> > > +
>> > > +  /**
>> > > +   * Conducts an RPC call to the specified
>> > > +   * target with the specified data via the IFPC
>> > > +   * method.
>> > > +   *
>> > > +   * @param {String} targetId Module Id of the RPC service provider.
>> > > +   * @param {String} from Module Id of the calling provider.
>> > > +   * @param {Object} rpcData The RPC data for this call.
>> > > +   */
>> > > +  function callIFPC(targetId, from, rpcData) {
>> > > +    // Retrieve the relay file used by IFPC. Note that
>> > > +    // this must be set before the call, and so we conduct
>> > > +    // an extra check to ensure it is not blank.
>> > > +    var relay = gadgets.rpc.getRelayUrl(targetId);
>> > > +
>> > > +    if (!relay) {
>> > > +      throw new Error('No relay file assigned for IFPC');
>> > >     }
>> > > +
>> > > +    // The RPC mechanism supports two formats for IFPC (legacy and
>> > > current).
>> > > +    var src = null;
>> > > +    if (useLegacyProtocol[targetId]) {
>> > > +      // Format:
>> #iframe_id&callId&num_packets&packet_num&block_of_data
>> > > +      src = [relay, '#', encodeLegacyData([from, callId, 1, 0,
>> > > +             encodeLegacyData([from, serviceName, '', '',
>> from].concat(
>> > > +               Array.prototype.slice.call(arguments,
>> 3)))])].join('');
>> > > +    } else {
>> > > +      // Format: #targetId & sourceId@callId & packetNum & packetId
>> &
>> > > packetData
>> > > +      src = [relay, '#', targetId, '&', from, '@', callId,
>> > > +             '&1&0&', encodeURIComponent(rpcData)].join('');
>> > > +    }
>> > > +
>> > > +    // Conduct the IFPC call by creating the Iframe with
>> > > +    // the relay URL and appended message.
>> > > +    emitInvisibleIframe(src);
>> > >   }
>> > >
>> > > +
>> > >   /**
>> > >    * Helper function to emit an invisible IFrame.
>> > >    * @param {String} src SRC attribute of the IFrame to emit.
>> > > @@ -221,6 +617,15 @@
>> > >      * @member gadgets.rpc
>> > >      */
>> > >     register: function(serviceName, handler) {
>> > > +      if (serviceName == CALLBACK_NAME) {
>> > > +        throw new Error("Cannot overwrite callback service");
>> > > +      }
>> > > +
>> > > +      if (serviceName == DEFAULT_NAME) {
>> > > +        throw new Error("Cannot overwrite default service:"
>> > > +                        + " use registerDefault");
>> > > +      }
>> > > +
>> > >       services[serviceName] = handler;
>> > >     },
>> > >
>> > > @@ -231,6 +636,15 @@
>> > >      * @member gadgets.rpc
>> > >      */
>> > >     unregister: function(serviceName) {
>> > > +      if (serviceName == CALLBACK_NAME) {
>> > > +        throw new Error("Cannot delete callback service");
>> > > +      }
>> > > +
>> > > +      if (serviceName == DEFAULT_NAME) {
>> > > +        throw new Error("Cannot delete default service:"
>> > > +                        + " use unregisterDefault");
>> > > +      }
>> > > +
>> > >       delete services[serviceName];
>> > >     },
>> > >
>> > > @@ -272,12 +686,14 @@
>> > >       if (callback) {
>> > >         callbacks[callId] = callback;
>> > >       }
>> > > -      var from;
>> > > +
>> > > +      // Default to the container calling.
>> > > +      var from = '..';
>> > > +
>> > >       if (targetId === '..') {
>> > >         from = window.name;
>> > > -      } else {
>> > > -        from = '..';
>> > >       }
>> > > +
>> > >       // Not used by legacy, create it anyway...
>> > >       var rpcData = gadgets.json.stringify({
>> > >         s: serviceName,
>> > > @@ -287,53 +703,37 @@
>> > >         t: authToken[targetId]
>> > >       });
>> > >
>> > > +      var channelType = relayChannel;
>> > > +
>> > > +      // If we are told to use the legacy format, then we must
>> > > +      // default to IFPC.
>> > >       if (useLegacyProtocol[targetId]) {
>> > > -        relayChannel = 'ifpc';
>> > > +        channelType = 'ifpc';
>> > >       }
>> > >
>> > > -      switch (relayChannel) {
>> > > -      case 'dpm': // use document.postMessage
>> > > -        var targetDoc = targetId === '..' ? parent.document :
>> > > -
>>  frames[targetId].document;
>> > > -        targetDoc.postMessage(rpcData);
>> > > -        break;
>> > > -      case 'wpm': // use window.postMessage
>> > > -        var targetWin = targetId === '..' ? parent :
>> frames[targetId];
>> > > -        targetWin.postMessage(rpcData, "*");
>> > > -        break;
>> > > -      default: // use 'ifpc' as a fallback mechanism
>> > > -        var relay = gadgets.rpc.getRelayUrl(targetId);
>> > > -        // TODO split message if too long
>> > > -        var src;
>> > > -        if (useLegacyProtocol[targetId]) {
>> > > -          // #iframe_id&callId&num_packets&packet_num&block_of_data
>> > > -          src = [relay, '#', encodeLegacyData([from, callId, 1, 0,
>> > > -                 encodeLegacyData([from, serviceName, '', '',
>> > > from].concat(
>> > > -                 Array.prototype.slice.call(arguments,
>> 3)))])].join('');
>> > > -        } else {
>> > > -          // Try the frameElement channel if available
>> > > -          try {
>> > > -            if (from === '..') {
>> > > -              // Container-to-gadget
>> > > -              var iframe = document.getElementById(targetId);
>> > > -              if (typeof iframe.__g2c_rpc.__c2g_rpc === 'function') {
>> > > -                iframe.__g2c_rpc.__c2g_rpc(rpcData);
>> > > -                return;
>> > > -              }
>> > > -            } else {
>> > > -              // Gadget-to-container
>> > > -              if (typeof window.frameElement.__g2c_rpc ===
>> 'function') {
>> > > -                window.frameElement.__g2c_rpc(rpcData);
>> > > -                return;
>> > > -              }
>> > > -            }
>> > > -          } catch (e) {
>> > > -          }
>> > > -          // # targetId & sourceId@callId & packetNum & packetId &
>> > > packetData
>> > > -          src = [relay, '#', targetId, '&', from, '@', callId,
>> > > -                 '&1&0&', encodeURIComponent(rpcData)].join('');
>> > > -        }
>> > > -        emitInvisibleIframe(src);
>> > > +      switch (channelType) {
>> > > +        case 'dpm': // use document.postMessage.
>> > > +          var targetDoc = targetId === '..' ? parent.document :
>> > > +
>>  frames[targetId].document;
>> > > +          targetDoc.postMessage(rpcData);
>> > > +          break;
>> > > +
>> > > +        case 'wpm': // use window.postMessage.
>> > > +          var targetWin = targetId === '..' ? parent :
>> frames[targetId];
>> > > +          targetWin.postMessage(rpcData, "*");
>> > > +          break;
>> > > +
>> > > +        case 'nix': // use NIX.
>> > > +          callNIX(targetId, from, rpcData);
>> > > +          break;
>> > > +
>> > > +        case 'fe': // use FrameElement.
>> > > +          callFrameElement(targetId, from, rpcData);
>> > > +          break;
>> > > +
>> > > +        default: // use 'ifpc' as a fallback mechanism.
>> > > +          callIFPC(targetId, from, rpcData);
>> > > +          break;
>> > >       }
>> > >     },
>> > >
>> > > @@ -372,15 +772,13 @@
>> > >      */
>> > >     setAuthToken: function(targetId, token) {
>> > >       authToken[targetId] = token;
>> > > +      setupFrame(targetId);
>> > >     },
>> > >
>> > >     /**
>> > >      * Gets the RPC relay mechanism.
>> > > -     * @return {String} RPC relay mechanism. Supported types:
>> > > -     *                  'wpm' - Use window.postMessage (defined by
>> > HTML5)
>> > > -     *                  'dpm' - Use document.postMessage (defined by
>> an
>> > > early
>> > > -     *                          draft of HTML5 and implemented by
>> Opera)
>> > > -     *                  'ifpc' - Use invisible IFrames
>> > > +     * @return {String} RPC relay mechanism. See above for
>> > > +     *   a list of supported types.
>> > >      *
>> > >      * @member gadgets.rpc
>> > >      */
>> > >
>> > > Modified:
>> > >
>> >
>> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
>> > > URL:
>> > >
>> >
>> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java?rev=672393&r1=672392&r2=672393&view=diff
>> > >
>> > >
>> >
>> ==============================================================================
>> > > ---
>> > >
>> >
>> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
>> > > (original)
>> > > +++
>> > >
>> >
>> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
>> > > Fri Jun 27 14:01:20 2008
>> > > @@ -221,7 +221,8 @@
>> > >           "a {color:#0000cc;}a:visited {color:#551a8b;}" +
>> > >           "a:active {color:#ff0000;}" +
>> > >           "body{margin: 0px;padding: 0px;background-color:white;}" +
>> > > -          "</style></head>";
>> > > +          "</style><base>" + gadget.getSpec().getUrl().toString() +
>> > > +          "</base></head>";
>> > >     markup.append(boilerPlate);
>> > >     LocaleSpec localeSpec =
>> gadget.getSpec().getModulePrefs().getLocale(
>> > >         gadget.getContext().getLocale());
>> > >
>> > > Added:
>> > incubator/shindig/trunk/javascript/container/rpctest_container.html
>> > > URL:
>> > >
>> >
>> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_container.html?rev=672393&view=auto
>> > >
>> > >
>> >
>> ==============================================================================
>> > > ---
>> incubator/shindig/trunk/javascript/container/rpctest_container.html
>> > > (added)
>> > > +++
>> incubator/shindig/trunk/javascript/container/rpctest_container.html
>> > Fri
>> > > Jun 27 14:01:20 2008
>> > > @@ -0,0 +1,90 @@
>> > > +<!--
>> > > +  Simple page for testing gadgets.rpc performance.
>> > > +  Allows you to add a simulated "gadget" (in this case just a static
>> > > +  HTML page which loads gadgets.rpc also), and pass some
>> > > +  specified number of random messages of specified size to
>> > > +  and from it.
>> > > +
>> > > +  A simple recommended setup is to start up two instances of
>> > > +  the Shindig Gadgets Server on two separate ports to test
>> > > +  "real" cross-domain communication, since port is factored
>> > > +  into the same-domain policy enforced by browsers.
>> > > +
>> > > +  If your servers are on localhost:8080 and localhost:8081, then hit:
>> > > +
>> http://localhost:8080/gadgets/files/container/rpctest_container.html?
>> > \
>> > > +  http://localhost:8081/gadgets/files/container/rpctest_gadget.html&\
>> > > +
>> > >
>> >
>> http://localhost:8081/gadgets/files/container/rpc_relay.uncompressed.html
>> > > +
>> > > +  (Note the backslashes should be removed, as they exist for
>> formatting
>> > > only.)
>> > > +
>> > > +  There are two arguments, separated by an ampersand:
>> > > +  1. URL of the "gadget" test page.
>> > > +  2. URL of the "gadget" test page's RPC relay (for browsers
>> requiring
>> > > it).
>> > > +-->
>> > > +<html>
>> > > +  <head>
>> > > +    <title>gadgets.rpc Performance Tests: Container</title>
>> > > +    <script src="../../js/rpc.js?c=1&debug=1"></script>
>> > > +    <script src="rpctest_perf.js"></script>
>> > > +    <script>
>> > > +      function initTest() {
>> > > +        var container = document.getElementById("container");
>> > > +
>> > > +        // query string is assumed to be the "gadget" URL as arg 1,
>> > > +        // and optionally the relay URL as arg 2
>> > > +        var pageArgs =
>> window.location.search.substring(1).split('&');
>> > > +        var gadgetUrl = pageArgs[0];
>> > > +        var secret = Math.round(Math.random()*10000000);
>> > > +        if (pageArgs[1]) {
>> > > +          gadgets.rpc.setRelayUrl('gadget', pageArgs[1]);
>> > > +        }
>> > > +        var containerRelay = pageArgs[2] || '';
>> > > +        container.innerHTML = "<iframe id='gadget' name='gadget'
>> > > height=300 width=300 src='" + gadgetUrl + "?parent=" + containerRelay
>> +
>> > > "#rpctoken=" + secret + "'></iframe>";
>> > > +        gadgets.rpc.setAuthToken('gadget', secret);
>> > > +
>> > > +        initPerfTest();
>> > > +      };
>> > > +    </script>
>> > > +  </head>
>> > > +  <body onload="initTest();">
>> > > +    <div>gadgets.rpc Performance: Container Page</div><hr/>
>> > > +    <div>Test<br/>
>> > > +      <ul>
>> > > +        <li>Number of messages to send:
>> > > +          <select name="num_msgs" id="num_msgs">
>> > > +            <option value="1">1</option>
>> > > +            <option value="10">10</option>
>> > > +            <option value="100" selected>100</option>
>> > > +            <option value="1000">1000</option>
>> > > +          </select>
>> > > +        </li>
>> > > +        <li>Message size:
>> > > +          <select name="msg_size" id="msg_size">
>> > > +            <option value="10">10 B</option>
>> > > +            <option value="100">100 B</option>
>> > > +            <option value="1024" selected>1 kB</option>
>> > > +            <option value="10240">10 kB</option>
>> > > +            <option value="102400">100 kB</option>
>> > > +            <option value="1048576">1 MB</option>
>> > > +          </select>
>> > > +        </li>
>> > > +        <li>
>> > > +          <input type="button" value="Start The Test!"
>> > > onclick="runPerfTest('gadget');" />
>> > > +        </li>
>> > > +      </ul>
>> > > +    </div>
>> > > +    <div id="test_running" style="display:none;">
>> > > +      Running test...
>> > > +    </div>
>> > > +    <div id="results" style="display:none;">
>> > > +      Results: Gadget-to-Container<br/>
>> > > +      Messages: <span id="results_num_received"></span>, Bytes: <span
>> > > id="results_bytes_received"></span> <span id="in_or_out"></span><br/>
>> > > +      Time elapsed for test run: <span
>> > id="results_time_used"></span><br/>
>> > > +      Messages/second: <span id="results_msgs_per_sec"></span><br/>
>> > > +      Bytes/second: <span id="results_bytes_per_sec"></span>
>> > > +    </div>
>> > > +    <hr/>
>> > > +    <div>Gadget:</div>
>> > > +    <div id="container"></div>
>> > > +  </body>
>> > > +</html>
>> > >
>> > > Added:
>> incubator/shindig/trunk/javascript/container/rpctest_gadget.html
>> > > URL:
>> > >
>> >
>> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_gadget.html?rev=672393&view=auto
>> > >
>> > >
>> >
>> ==============================================================================
>> > > --- incubator/shindig/trunk/javascript/container/rpctest_gadget.html
>> > > (added)
>> > > +++ incubator/shindig/trunk/javascript/container/rpctest_gadget.html
>> Fri
>> > > Jun 27 14:01:20 2008
>> > > @@ -0,0 +1,49 @@
>> > > +<html>
>> > > +  <head>
>> > > +    <title>gadgets.rpc Performance Tests: Gadget</title>
>> > > +    <script src="../../js/rpc.js?c=1&debug=1"></script>
>> > > +    <script src="rpctest_perf.js"></script>
>> > > +    <script>
>> > > +      var containerRelay = window.location.search.substring(1);
>> > > +      gadgets.rpc.setRelayUrl(null, containerRelay);
>> > > +    </script>
>> > > +  </head>
>> > > +  <body onload="initPerfTest();">
>> > > +    <div>gadgets.rpc Performance: "Gadget" page</div><hr/>
>> > > +    <div>Test<br/>
>> > > +      <ul>
>> > > +        <li>Number of messages to send:
>> > > +          <select name="num_msgs" id="num_msgs">
>> > > +            <option value="1">1</option>
>> > > +            <option value="10">10</option>
>> > > +            <option value="100" selected>100</option>
>> > > +            <option value="1000">1000</option>
>> > > +          </select>
>> > > +        </li>
>> > > +        <li>Message size:
>> > > +          <select name="msg_size" id="msg_size">
>> > > +            <option value="10">10 B</option>
>> > > +            <option value="100">100 B</option>
>> > > +            <option value="1024" selected>1 kB</option>
>> > > +            <option value="10240">10 kB</option>
>> > > +            <option value="102400">100 kB</option>
>> > > +            <option value="1048576">1 MB</option>
>> > > +          </select>
>> > > +        </li>
>> > > +        <li>
>> > > +          <input type="button" value="Start The Test!"
>> > > onclick="runPerfTest();" />
>> > > +        </li>
>> > > +      </ul>
>> > > +    </div>
>> > > +    <div id="test_running" style="display:none;">
>> > > +      Running test...
>> > > +    </div>
>> > > +    <div id="results" style="display:none;">
>> > > +      Results: Gadget-to-Container<br/>
>> > > +      Messages: <span id="results_num_received"></span>, Bytes: <span
>> > > id="results_bytes_received"></span> <span id="in_or_out"></span><br/>
>> > > +      Time elapsed for test run: <span
>> > id="results_time_used"></span><br/>
>> > > +      Messages/second: <span id="results_msgs_per_sec"></span><br/>
>> > > +      Bytes/second: <span id="results_bytes_per_sec"></span>
>> > > +    </div>
>> > > +  </body>
>> > > +</html>
>> > >
>> > > Added: incubator/shindig/trunk/javascript/container/rpctest_perf.js
>> > > URL:
>> > >
>> >
>> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_perf.js?rev=672393&view=auto
>> > >
>> > >
>> >
>> ==============================================================================
>> > > --- incubator/shindig/trunk/javascript/container/rpctest_perf.js
>> (added)
>> > > +++ incubator/shindig/trunk/javascript/container/rpctest_perf.js Fri
>> Jun
>> > 27
>> > > 14:01:20 2008
>> > > @@ -0,0 +1,93 @@
>> > > +var perfStats = null;
>> > > +var currentRun = {};
>> > > +
>> > > +function perfService(message) {
>> > > +  if (perfStats.numResults++ === 0) {
>> > > +    perfStats.firstMsg = message; // stored since it has "real" start
>> > time
>> > > +  }
>> > > +  perfStats.bytesReceived += message.length;
>> > > +};
>> > > +
>> > > +function clearPerfStats(inOrOut) {
>> > > +  perfStats = {
>> > > +    numResults: 0,
>> > > +    bytesReceived: 0,
>> > > +    firstMsg: null
>> > > +  };
>> > > +
>> > > +  document.getElementById("in_or_out").innerHTML = inOrOut;
>> > > +
>> > > +  // hide results fields
>> > > +  document.getElementById("results").style.display = "none";
>> > > +};
>> > > +
>> > > +function completePerfStats() {
>> > > +  perfStats.timeEnded = new Date().getTime();
>> > > +
>> > > +  // get time started from the first sent message
>> > > +  perfStats.timeStarted = perfStats.firstMsg.substr(0,
>> > > perfStats.firstMsg.indexOf(':'));
>> > > +
>> > > +  var timeUsedMs = perfStats.timeEnded - perfStats.timeStarted;
>> > > +
>> > > +  // fill in fields
>> > > +  document.getElementById("results_num_received").innerHTML =
>> > > perfStats.numResults;
>> > > +  document.getElementById("results_bytes_received").innerHTML =
>> > > perfStats.bytesReceived;
>> > > +  document.getElementById("results_time_used").innerHTML = timeUsedMs
>> +
>> > > "ms";
>> > > +  document.getElementById("results_msgs_per_sec").innerHTML =
>> > > (perfStats.numResults / (timeUsedMs / 1000));
>> > > +  document.getElementById("results_bytes_per_sec").innerHTML =
>> > > (perfStats.bytesReceived / (timeUsedMs / 1000));
>> > > +  document.getElementById("test_running").style.display = "none";
>> > > +  document.getElementById("results").style.display = "";
>> > > +};
>> > > +
>> > > +function initPerfTest() {
>> > > +  clearPerfStats();
>> > > +  gadgets.rpc.register("perf_service", perfService);
>> > > +  gadgets.rpc.register("clear_perf_stats", clearPerfStats);
>> > > +  gadgets.rpc.register("complete_perf_stats", completePerfStats);
>> > > +};
>> > > +
>> > > +var alphabet =
>> > > "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
>> > _-*&(){}'";
>> > > +
>> > > +function sendPerfMessage() {
>> > > +  var msgToSend = currentRun.msg;
>> > > +  if (currentRun.curMsgId++ <= 1) {
>> > > +    var nowString = new Date().getTime() + ':';
>> > > +    msgToSend = nowString +
>> currentRun.msg.substring(nowString.length);
>> > > +  }
>> > > +
>> > > +  gadgets.rpc.call(currentRun.targetId, "perf_service", null,
>> > msgToSend);
>> > > +  if (currentRun.curMsgId < currentRun.endMsgId) {
>> > > +    // loop, giving up execution in case rpc technique demands it
>> > > +    window.setTimeout(sendPerfMessage, 0);
>> > > +  } else {
>> > > +    // send finisher
>> > > +    window.setTimeout(function() {
>> gadgets.rpc.call(currentRun.targetId,
>> > > "complete_perf_stats", null); }, 0);
>> > > +  }
>> > > +};
>> > > +
>> > > +function runPerfTest(targetId) {
>> > > +  document.getElementById("test_running").style.display = "";
>> > > +
>> > > +  // initialize the current run
>> > > +  var num_msgs = document.getElementById("num_msgs").value;
>> > > +  var msg_size = document.getElementById("msg_size").value;
>> > > +
>> > > +  currentRun.targetId = targetId;
>> > > +  currentRun.curMsgId = 0;
>> > > +  currentRun.endMsgId = num_msgs;
>> > > +
>> > > +  var msg = [];
>> > > +  for (var i = 0; i < msg_size; ++i) {
>> > > +    msg[i] =
>> alphabet.charAt(Math.round(Math.random(alphabet.length)));
>> > > +  }
>> > > +  currentRun.msg = msg.join('');
>> > > +
>> > > +  // clear local perf stats
>> > > +  clearPerfStats("(outbound)");
>> > > +
>> > > +  // clear target perf stats
>> > > +  gadgets.rpc.call(targetId, "clear_perf_stats", null, "(inbound)");
>> > > +
>> > > +  // kick off the send loop
>> > > +  sendPerfMessage();
>> > > +};
>> > >
>> > >
>> > >
>> >
>>
>
>

Re: svn commit: r672393 - in /incubator/shindig/trunk: features/rpc/ java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ javascript/container/

Posted by John Hjelmstad <fa...@google.com>.
As indicated in the CL, I tested this on half a dozen browsers. We don't
have unit tests for it. I've also sent the code and inquiries about the
technique to several poeple, some of whom expressed concerns but no ability
to actively exploit the code. The main concern was around VBScript
knowledge, which few people I know extensively have. That was true of IFPC
when it was originally released as well. If any attacks are found, they will
be actively closed or the technique will be removed. Would you prefer that
no code, irrespective of its value, ever be submitted? It's possible for any
code to have bugs, and a lot of code to have security holes. If found,
they're fixed.

John

On Fri, Jun 27, 2008 at 3:38 PM, Kevin Brown <et...@google.com> wrote:

> Please actually test your code or send it to other shindig committers
> before
> you commit it. Your change to GadgetRenderingTask breaks proxying and
> makeRequest in several cases. Fix it.
>
> Have you run this by Brian and/or Mike Samuel? They both expressed doubts
> about whether this mechanism was actually secure.
>
> On Fri, Jun 27, 2008 at 2:06 PM, John Hjelmstad <fa...@google.com> wrote:
>
> > +jschorr in case there are comments.
> >
> > On Fri, Jun 27, 2008 at 2:01 PM, <jo...@apache.org> wrote:
> >
> > > Author: johnh
> > > Date: Fri Jun 27 14:01:20 2008
> > > New Revision: 672393
> > >
> > > URL: http://svn.apache.org/viewvc?rev=672393&view=rev
> > > Log:
> > > Cleaned up gadgets.rpc library implementation, and implemented fast IE
> > > transport layer.
> > >
> > > Credit:
> > >        Joseph Schorr (jschorr@google.com) and I implemented this
> > together,
> > > but he
> > >        really did all the hard work, as well as developing the NIX
> > > technique's
> > >        fundamentals. Huge thanks to Joseph!
> > >
> > > Details:
> > >        * Created a new relayChannel for Firefox frameElement technique,
> > >          making its implementation more linear to read.
> > >        * Consolidated all transport-specific code in setupChannel(),
> > > setupFrame(),
> > >          and callX(), where X = relay type. This refactoring makes
> setup
> > > and use
> > >          of each transport more clear, and makes it possible in later
> > > revisions
> > >          to separate out each method as its own class. In theory, if we
> > can
> > > trust
> > >          things like User-Agent headers, we can use this to reduce
> rpc.js
> > > code
> > >          size significantly by only including the needed transport(s).
> > >        * Implemented "NIX" - Native IE XDC. This method exploits the
> fact
> > >          that in IE6 and IE7 the window.opener property can be both set
> > and
> > >          read across domain boundaries for an IFRAME, and that any
> object
> > >          can be passed through it. In particular, functions
> implementing
> > >          message passing can be passed. These can't be JS functions,
> > > however,
> > >          since those can leak sender context. So, VBScript (COM)
> objects
> > >          wrapping these are passed to maintain context isolation.
> > >          - Requires for IE6/7 that rpc.js be included in the container
> > >            at load time, not dynamically. TODO added to consider
> > detecting
> > >            whether dynamic loading is happening (at which point we
> should
> > >            fall back to IFPC).
> > >          - Message channel handshake *and* message passing validated
> > >            using authToken, as with other channels.
> > >          - Impl requires that gadget call container first - else IFPC
> > >            is used. This is the same as the frameElement technique. We
> > >            could add a setInterval()-based initialization routine later
> > >            if needed. To date, the only gadgets.rpc calls made from
> > >            container to gadget without gadget to container first are
> > >            in theory pubsub messages.
> > >          - Extensive documentation on this technique and the others
> > >            added to the comments, since they're stripped out in the
> > >            majority case.
> > >        * Implemented quick-and-dirty performance testing page verifying
> > >          the library works and giving basic performance metrics.
> > >          This needs to be improved! But does the trick for now...
> > >
> > > Testing/Data:
> > >
> > > Library verified on the following browsers with the RPC test page. For
> > > each,
> > > a "bandwidth" measure was taken by averaging the results of 6 G2C and
> C2G
> > > runs
> > > with 100 messages being passed of 1K apiece. Units are 1K messages/sec.
> > > The "latency" value is the average amount of time taken, over 6 G2C and
> > C2G
> > > runs,
> > > to send 1 1K message. Happily, for all browsers save Safari (soon due
> to
> > > get
> > > window.postMessage), latency is around or below time required to
> achieve
> > > a cross-domain call without user-perceived delay.
> > >
> > >        * Firefox 3 (WPM) - bandwidth: 88 kB/sec; latency: 18ms.
> > >        * Firefox 2 (FE)  - bandwidth: 80 kB/sec; latency: 15ms.
> > >        * Opera 9   (WPM) - bandwidth: 85 kB/sec; latency: 7ms.
> > >        * IE6       (NIX) - bandwidth: 64 kB/sec; latency: 18ms.
> > >        * IE7       (NIX) - bandwidth: 64 kB/sec; latency: 22ms.
> > >        * Safari 3 (IFPC) - bandwidth: ?6-8kB/sec; latency: ?100-200ms.
> > >          - Safari is somewhat flaky with the RPC test page, before
> > >            and after the rpc.js change. Multiple messages seem to
> > >            confuse it from time to time.
> > >
> > >
> > > Added:
> > >    incubator/shindig/trunk/javascript/container/rpctest_container.html
> > >    incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > >    incubator/shindig/trunk/javascript/container/rpctest_perf.js
> > > Modified:
> > >    incubator/shindig/trunk/features/rpc/rpc.js
> > >
> > >
> >
>  incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > >
> > > Modified: incubator/shindig/trunk/features/rpc/rpc.js
> > > URL:
> > >
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/features/rpc/rpc.js?rev=672393&r1=672392&r2=672393&view=diff
> > >
> > >
> >
> ==============================================================================
> > > --- incubator/shindig/trunk/features/rpc/rpc.js (original)
> > > +++ incubator/shindig/trunk/features/rpc/rpc.js Fri Jun 27 14:01:20
> 2008
> > > @@ -18,7 +18,7 @@
> > >
> > >  /**
> > >  * @fileoverview Remote procedure call library for gadget-to-container,
> > > - * container-to-gadget, and gadget-to-gadget communication.
> > > + * container-to-gadget, and gadget-to-gadget (thru container)
> > > communication.
> > >  */
> > >
> > >  var gadgets = gadgets || {};
> > > @@ -29,6 +29,30 @@
> > >  * @name gadgets.rpc
> > >  */
> > >  gadgets.rpc = function() {
> > > +  // General constants.
> > > +  var CALLBACK_NAME = '__cb';
> > > +  var DEFAULT_NAME = '';
> > > +
> > > +  // Consts for FrameElement.
> > > +  var FE_G2C_CHANNEL = '__g2c_rpc';
> > > +  var FE_C2G_CHANNEL = '__c2g_rpc';
> > > +
> > > +  // Consts for NIX. VBScript doesn't
> > > +  // allow items to start with _ for some reason,
> > > +  // so we need to make these names quite unique, as
> > > +  // they will go into the global namespace.
> > > +  var NIX_WRAPPER = 'GRPC____NIXVBS_wrapper';
> > > +  var NIX_GET_WRAPPER = 'GRPC____NIXVBS_get_wrapper';
> > > +  var NIX_HANDLE_MESSAGE = 'GRPC____NIXVBS_handle_message';
> > > +  var NIX_CREATE_CHANNEL = 'GRPC____NIXVBS_create_channel';
> > > +  var NIX_ID_FIELD = 'GRPC____NIXVBS_container';
> > > +
> > > +  // JavaScript reference to the NIX VBScript wrappers.
> > > +  // Gadgets will have but a single channel under
> > > +  // nix_channels['..'] while containers will have a channel
> > > +  // per gadget stored under the gadget's ID.
> > > +  var nix_channels = {};
> > > +
> > >   var services = {};
> > >   var iframePool = [];
> > >   var relayUrl = {};
> > > @@ -36,28 +60,135 @@
> > >   var authToken = {};
> > >   var callId = 0;
> > >   var callbacks = {};
> > > +  var setup = {};
> > > +
> > > +  var params = {};
> > > +
> > > +  // Load the authentication token for speaking to the container
> > > +  // from the gadget's parameters, or default to '0' if not found.
> > > +  if (gadgets.util) {
> > > +    params = gadgets.util.getUrlParameters();
> > > +  }
> > >
> > > -  var params = gadgets.util.getUrlParameters();
> > >   authToken['..'] = params.rpctoken || params.ifpctok || 0;
> > >
> > > -  // Pick the most efficient RPC relay mechanism
> > > -  var relayChannel = typeof document.postMessage === 'function' ?
> 'dpm'
> > :
> > > -                     typeof window.postMessage === 'function' ? 'wpm'
> :
> > > +  // Pick the most efficient RPC relay mechanism:
> > > +  //
> > > +  // * For those browsers that support native messaging (various
> > > implementations
> > > +  //   of the HTML5 postMessage method), use that. Officially defined
> at
> > > +  //
> > > http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html
> .
> > > +  //
> > > +  //   postMessage is a native implementation of XDC. A page registers
> > > that
> > > +  //   it would like to receive messages by listening the the
> "message"
> > > event
> > > +  //   on the window (document in DPM) object. In turn, another page
> can
> > > +  //   raise that event by calling window.postMessage
> > > (document.postMessage
> > > +  //   in DPM) with a string representing the message and a string
> > > +  //   indicating on which domain the receiving page must be to
> receive
> > > +  //   the message. The target page will then have its "message" event
> > > raised
> > > +  //   if the domain matches and can, in turn, check the origin of the
> > > message
> > > +  //   and process the data contained within.
> > > +  //
> > > +  //     wpm: postMessage on the window object.
> > > +  //        - Internet Explorer 8+
> > > +  //        - Safari (latest nightlies as of 26/6/2008)
> > > +  //        - Firefox 3+
> > > +  //        - Opera 9+
> > > +  //
> > > +  //     dpm: postMessage on the document object.
> > > +  //        - Opera 8+
> > > +  //
> > > +  // * For Internet Explorer before version 8, the security model
> allows
> > a
> > > +  //   child to set the value of the "opener" property on the parent
> > > window
> > > +  //   and vice versa. This method is dubbed "Native IE XDC" (NIX).
> > > +  //
> > > +  //   This method works by placing a handler object in the "opener"
> > > property
> > > +  //   of a gadget when the container sets up the authentication
> > > information
> > > +  //   for that gadget (by calling setAuthToken(...)). At that point,
> a
> > > NIX
> > > +  //   wrapper is created and placed into the gadget by calling
> > > +  //   theframe.contentWindow.opener = wrapper. Note that as a result,
> > NIX
> > > can
> > > +  //   only be used by a container to call a particular gadget *after*
> > > that
> > > +  //   gadget has called the container at least once via NIX.
> > > +  //
> > > +  //   The NIX wrappers in this RPC implementation are instances of a
> > > VBScript
> > > +  //   class that is created when this implementation loads. The
> reason
> > > for
> > > +  //   using a VBScript class stems from the fact that any object can
> be
> > > passed
> > > +  //   into the opener property.
> > > +  //   While this is a good thing, as it lets us pass functions and
> > setup
> > > a true
> > > +  //   bidirectional channel via callbacks, it opens a potential
> > security
> > > hole
> > > +  //   by which the other page can get ahold of the "window" or
> > "document"
> > > +  //   objects in the parent page and in turn wreak havok. This is due
> > to
> > > the
> > > +  //   fact that any JS object useful for establishing such a
> > > bidirectional
> > > +  //   channel (such as a function) can be used to access a function
> > > +  //   (eg. obj.toString, or a function itself) created in a specific
> > > context,
> > > +  //   in particular the global context of the sender. Suppose
> container
> > > +  //   domain C passes object obj to gadget on domain G. Then the
> gadget
> > > can
> > > +  //   access C's global context using:
> > > +  //   var parentWindow = (new obj.toString.constructor("return
> > > window;"))();
> > > +  //   Nulling out all of obj's properties doesn't fix this, since IE
> > > helpfully
> > > +  //   restores them to their original values if you do something
> like:
> > > +  //   delete obj.toString; delete obj.toString;
> > > +  //   Thus, we wrap the necessary functions and information inside a
> > > VBScript
> > > +  //   object. VBScript objects in IE, like DOM objects, are in fact
> COM
> > > +  //   wrappers when used in JavaScript, so we can safely pass them
> > around
> > > +  //   without worrying about a breach of context while at the same
> time
> > > +  //   allowing them to act as a pass-through mechanism for
> information
> > > +  //   and function calls. The implementation details of this VBScript
> > > wrapper
> > > +  //   can be found in the setupChannel() method below.
> > > +  //
> > > +  //     nix: Internet Explorer-specific window.opener trick.
> > > +  //       - Internet Explorer 6
> > > +  //       - Internet Explorer 7
> > > +  //
> > > +  // * For Gecko-based browsers, the security model allows a child to
> > call
> > > a
> > > +  //   function on the frameElement of the iframe, even if the child
> is
> > in
> > > +  //   a different domain. This method is dubbed "frameElement" (fe).
> > > +  //
> > > +  //   The ability to add and call such functions on the frameElement
> > > allows
> > > +  //   a bidirectional channel to be setup via the adding of simple
> > > function
> > > +  //   references on the frameElement object itself. In this
> > > implementation,
> > > +  //   when the container sets up the authentication information for
> > that
> > > gadget
> > > +  //   (by calling setAuth(...)) it as well adds a special function on
> > the
> > > +  //   gadget's iframe. This function can then be used by the gadget
> to
> > > send
> > > +  //   messages to the container. In turn, when the gadget tries to
> send
> > a
> > > +  //   message, it checks to see if this function has its own function
> > > stored
> > > +  //   that can be used by the container to call the gadget. If not,
> the
> > > +  //   function is created and subsequently used by the container.
> > > +  //   Note that as a result, FE can only be used by a container to
> call
> > a
> > > +  //   particular gadget *after* that gadget has called the container
> at
> > > +  //   least once via FE.
> > > +  //
> > > +  //     fe: Gecko-specific frameElement trick.
> > > +  //        - Firefox 1+
> > > +  //
> > > +  // * For all others, we have a fallback mechanism known as "ifpc".
> > IFPC
> > > +  //   exploits the fact that while same-origin policy prohibits a
> frame
> > > from
> > > +  //   accessing members on a window not in the same domain, that
> frame
> > > can,
> > > +  //   however, navigate the window heirarchy (via parent). This is
> > > exploited by
> > > +  //   having a page on domain A that wants to talk to domain B create
> > an
> > > iframe
> > > +  //   on domain B pointing to a special relay file and with a message
> > > encoded
> > > +  //   after the hash (#). This relay, in turn, finds the page on
> domain
> > > B, and
> > > +  //   can call a receipt function with the message given to it. The
> > relay
> > > URL
> > > +  //   used by each caller is set via the gadgets.rpc.setRelayUrl(..)
> > and
> > > +  //   *must* be called before the call method is used.
> > > +  //
> > > +  //     ifpc: Iframe-based method, utilizing a relay page, to send a
> > > message.
> > > +  //
> > > +  var relayChannel = typeof window.postMessage === 'function' ? 'wpm'
> :
> > > +                    typeof document.postMessage === 'function' ? 'dpm'
> :
> > > +                    window.ActiveXObject ? 'nix' :
> > > +                     navigator.product === 'Gecko' ? 'fe' :
> > >                      'ifpc';
> > > -  if (relayChannel === 'dpm' || relayChannel === 'wpm') {
> > > -    window.addEventListener('message', function(packet) {
> > > -      // TODO validate packet.domain for security reasons
> > > -      process(gadgets.json.parse(packet.data));
> > > -    }, false);
> > > -  }
> > >
> > > -  // Default RPC handler
> > > -  services[''] = function() {
> > > +  // Conduct any setup necessary for the chosen channel.
> > > +  setupChannel();
> > > +
> > > +  // Create the Default RPC handler.
> > > +  services[DEFAULT_NAME] = function() {
> > >     throw new Error('Unknown RPC service: ' + this.s);
> > >   };
> > >
> > > -  // Special RPC handler for callbacks
> > > -  services['__cb'] = function(callbackId, result) {
> > > +  // Create a Special RPC handler for callbacks.
> > > +  services[CALLBACK_NAME] = function(callbackId, result) {
> > >     var callback = callbacks[callbackId];
> > >     if (callback) {
> > >       delete callbacks[callbackId];
> > > @@ -66,6 +197,147 @@
> > >   };
> > >
> > >   /**
> > > +   * Conducts any initial global work necessary to setup the
> > > +   * channel type chosen.
> > > +   */
> > > +  function setupChannel() {
> > > +    // If the channel type is one of the native
> > > +    // postMessage based ones, setup the handler to receive
> > > +    // messages.
> > > +    if (relayChannel === 'dpm' || relayChannel === 'wpm') {
> > > +      window.addEventListener('message', function(packet) {
> > > +        // TODO validate packet.domain for security reasons
> > > +        process(gadgets.json.parse(packet.data));
> > > +      }, false);
> > > +    }
> > > +
> > > +    // If the channel type is NIX, we need to ensure the
> > > +    // VBScript wrapper code is in the page and that the
> > > +    // global Javascript handlers have been set.
> > > +    if (relayChannel === 'nix') {
> > > +      // VBScript methods return a type of 'unknown' when
> > > +      // checked via the typeof operator in IE. Fortunately
> > > +      // for us, this only applies to COM objects, so we
> > > +      // won't see this for a real Javascript object.
> > > +      if (typeof window[NIX_GET_WRAPPER] !== 'unknown') {
> > > +        window[NIX_HANDLE_MESSAGE] = function(data) {
> > > +          process(gadgets.json.parse(data));
> > > +        };
> > > +
> > > +        window[NIX_CREATE_CHANNEL] = function(name, channel, token) {
> > > +          // Verify the authentication token of the gadget trying
> > > +          // to create a channel for us.
> > > +          if (authToken[name] == token) {
> > > +            nix_channels[name] = channel;
> > > +          }
> > > +        };
> > > +
> > > +        // Inject the VBScript code needed.
> > > +        var vbscript =
> > > +          '<scr' + 'ipt language="VBScript">'
> > > +          // We create a class to act as a wrapper for
> > > +          // a Javascript call, to prevent a break in of
> > > +          // the context.
> > > +          + 'Class ' + NIX_WRAPPER + '\n '
> > > +
> > > +          // An internal member for keeping track of the
> > > +          // name of the document (container or gadget)
> > > +          // for which this wrapper is intended. For
> > > +          // those wrappers created by gadgets, this is not
> > > +          // used (although it is set to "..")
> > > +          + 'Private m_Intended\n'
> > > +
> > > +          // Method for internally setting the value
> > > +          // of the m_Intended property.
> > > +          + 'Public Sub SetIntendedName(name)\n '
> > > +          + 'm_Intended = name\n'
> > > +          + 'End Sub\n'
> > > +
> > > +          // A wrapper method which actually causes a
> > > +          // message to be sent to the other context.
> > > +          + 'Public Sub SendMessage(data)\n '
> > > +          + NIX_HANDLE_MESSAGE + '(data)\n'
> > > +          + 'End Sub\n'
> > > +
> > > +          // Method for setting up the container->gadget
> > > +          // channel. Not strictly needed in the gadget's
> > > +          // wrapper, but no reason to get rid of it. Note here
> > > +          // that we pass the intended name to the NIX_CREATE_CHANNEL
> > > +          // method so that it can save the channel in the proper
> place
> > > +          // *and* verify the channel via the authentication token
> > passed
> > > +          // here.
> > > +          + 'Public Sub CreateChannel(channel, auth)\n '
> > > +          + 'Call ' + NIX_CREATE_CHANNEL + '(m_Intended, channel,
> > auth)\n'
> > > +          + 'End Sub\n'
> > > +
> > > +          // An empty field with a unique identifier to
> > > +          // prevent the code from confusing this wrapper
> > > +          // with a run-of-the-mill value found in window.opener.
> > > +          + 'Public Sub ' + NIX_ID_FIELD + '()\n '
> > > +          + 'End Sub\n'
> > > +          + 'End Class\n '
> > > +
> > > +          // Function to get a reference to the wrapper.
> > > +          + 'Function ' + NIX_GET_WRAPPER + '(name)\n'
> > > +          + 'Dim wrap\n'
> > > +          + 'Set wrap = New ' + NIX_WRAPPER + '\n'
> > > +          + 'wrap.SetIntendedName name\n'
> > > +          + 'Set ' + NIX_GET_WRAPPER + ' = wrap\n'
> > > +          + 'End Function'
> > > +          + '</scr' + 'ipt>'
> > > +
> > > +          // Resets the "default" scripting language in IE back
> > > +          // to Javascript. This is needed or else any script
> > > +          // tags without a proper Language="..." will be treated as
> > > +          // VBScript once this code is added to the document.
> > > +          + '<scri' + 'pt language="JScript"></scr' + 'ipt>';
> > > +
> > > +        // Note that this code can only be run once the document has
> > > +        // fully loaded.
> > > +        // TODO: Perhaps add some sort of check here for this?
> > > +        document.write(vbscript);
> > > +      }
> > > +    }
> > > +  }
> > > +
> > > +  /**
> > > +   * Conducts any frame-specific work necessary to setup
> > > +   * the channel type chosen. This method is called when
> > > +   * the container page first registers the gadget in the
> > > +   * RPC mechanism. Gadgets, in turn, will complete the setup
> > > +   * of the channel once they send their first messages.
> > > +   */
> > > +  function setupFrame(frameId) {
> > > +    if (setup[frameId]) {
> > > +      return;
> > > +    }
> > > +
> > > +    if (relayChannel === 'fe') {
> > > +      try {
> > > +        var frame = document.getElementById(frameId);
> > > +        frame[FE_G2C_CHANNEL] = function(args) {
> > > +          process(gadgets.json.parse(args));
> > > +        };
> > > +      } catch (e) {
> > > +        // Something went wrong. System will fallback to
> > > +        // IFPC.
> > > +      }
> > > +    }
> > > +
> > > +    if (relayChannel === 'nix') {
> > > +      try {
> > > +        var frame = document.getElementById(frameId);
> > > +        frame.contentWindow.opener = window[NIX_GET_WRAPPER](frameId);
> > > +      } catch (e) {
> > > +        // Something went wrong. System will fallback to
> > > +        // IFPC.
> > > +      }
> > > +    }
> > > +
> > > +    setup[frameId] = true;
> > > +  }
> > > +
> > > +  /**
> > >    * Encodes arguments for the legacy IFPC wire format.
> > >    *
> > >    * @param {Object} args
> > > @@ -86,8 +358,17 @@
> > >    * @private
> > >    */
> > >   function process(rpc) {
> > > +    //
> > > +    // RPC object contents:
> > > +    //   s: Service Name
> > > +    //   f: From
> > > +    //   c: The callback ID or 0 if none.
> > > +    //   a: The arguments for this RPC call.
> > > +    //   t: The authentication token.
> > > +    //
> > >     if (rpc && typeof rpc.s === 'string' && typeof rpc.f === 'string'
> &&
> > >         rpc.a instanceof Array) {
> > > +
> > >       // Validate auth token.
> > >       if (authToken[rpc.f]) {
> > >         // We allow type coercion here because all the url params are
> > > strings.
> > > @@ -96,43 +377,158 @@
> > >         }
> > >       }
> > >
> > > -      // The Gecko engine used by FireFox etc. allows an IFrame to
> > > directly call
> > > -      // methods on the frameElement property added by the container
> > page
> > > even
> > > -      // if their domains don't match.
> > > -      // Here we try to set up a relay channel using the frameElement
> > > technique
> > > -      // to greatly reduce the latency of cross-domain calls if the
> > > postMessage
> > > -      // method is not supported.
> > > -      if (relayChannel === 'ifpc') {
> > > -        if (rpc.f === '..') {
> > > -          // Container-to-gadget call
> > > -          try {
> > > -            var fel = window.frameElement;
> > > -            if (typeof fel.__g2c_rpc === 'function' &&
> > > -                typeof fel.__g2c_rpc.__c2g_rpc != 'function') {
> > > -              fel.__g2c_rpc.__c2g_rpc = function(args) {
> > > -                process(gadgets.json.parse(args));
> > > -              };
> > > -            }
> > > -          } catch (e) {
> > > -          }
> > > -        } else {
> > > -          // Gadget-to-container call
> > > -          var iframe = document.getElementById(rpc.f);
> > > -          if (iframe && typeof iframe.__g2c_rpc != 'function') {
> > > -            iframe.__g2c_rpc = function(args) {
> > > +      // Call the requested RPC service.
> > > +      var result = (services[rpc.s] ||
> > > +                    services[DEFAULT_NAME]).apply(rpc, rpc.a);
> > > +
> > > +      // If there is a callback for this service, initiate it as well.
> > > +      if (rpc.c) {
> > > +        gadgets.rpc.call(rpc.f, CALLBACK_NAME, null, rpc.c, result);
> > > +      }
> > > +    }
> > > +  }
> > > +
> > > +  /**
> > > +   * Attempts to conduct an RPC call to the specified
> > > +   * target with the specified data via the NIX
> > > +   * method. If this method fails, the system attempts again
> > > +   * using the known default of IFPC.
> > > +   *
> > > +   * @param {String} targetId Module Id of the RPC service provider.
> > > +   * @param {String} from Module Id of the calling provider.
> > > +   * @param {Object} rpcData The RPC data for this call.
> > > +   */
> > > +  function callNIX(targetId, from, rpcData) {
> > > +    try {
> > > +      if (from != '..') {
> > > +        // Call from gadget to the container.
> > > +        var handler = nix_channels['..'];
> > > +
> > > +        // If the gadget has yet to retrieve a reference to
> > > +        // the NIX handler, try to do so now. We don't do a
> > > +        // typeof(window.opener[NIX_ID_FIELD]) check here
> > > +        // because it means accessing that field on the COM object,
> > which,
> > > +        // being an internal function reference, is not allowed.
> > > +        // "in" works because it merely checks for the prescence of
> > > +        // the key, rather than actually accessing the object's
> > property.
> > > +        if (!handler && window.opener && NIX_ID_FIELD in
> window.opener)
> > {
> > > +          handler = nix_channels['..'] = window.opener;
> > > +
> > > +          // Create the channel to the parent/container. We pass both
> > our
> > > +          // own wrapper and our authentication token for
> verification.
> > > +          handler.CreateChannel(window[NIX_GET_WRAPPER]('..'),
> > > authToken['..']);
> > > +        }
> > > +
> > > +        // If we have a handler, call it.
> > > +        if (handler) {
> > > +          handler.SendMessage(rpcData);
> > > +          return;
> > > +        }
> > > +      } else {
> > > +        // Call from container to a gadget[targetId].
> > > +
> > > +        // If we have a handler, call it.
> > > +        if (nix_channels[targetId]) {
> > > +          nix_channels[targetId].SendMessage(rpcData);
> > > +          return;
> > > +        }
> > > +      }
> > > +    } catch (e) {
> > > +    }
> > > +
> > > +    // If we have reached this point, something has failed
> > > +    // with the NIX method, so we default to using
> > > +    // IFPC for this call.
> > > +    callIFPC(targetId, from, rpcData);
> > > +  }
> > > +
> > > +  /**
> > > +   * Attempts to conduct an RPC call to the specified
> > > +   * target with the specified data via the FrameElement
> > > +   * method. If this method fails, the system attempts again
> > > +   * using the known default of IFPC.
> > > +   *
> > > +   * @param {String} targetId Module Id of the RPC service provider.
> > > +   * @param {String} from Module Id of the calling provider.
> > > +   * @param {Object} rpcData The RPC data for this call.
> > > +   */
> > > +  function callFrameElement(targetId, from, rpcData) {
> > > +    try {
> > > +      if (from != '..') {
> > > +        // Call from gadget to the container.
> > > +        var fe = window.frameElement;
> > > +
> > > +        if (typeof fe[FE_G2C_CHANNEL] === 'function') {
> > > +          // Complete the setup of the FE channel if need be.
> > > +          if (typeof fe[FE_G2C_CHANNEL][FE_C2G_CHANNEL] !==
> 'function')
> > {
> > > +            fe[FE_G2C_CHANNEL][FE_C2G_CHANNEL] = function(args) {
> > >               process(gadgets.json.parse(args));
> > >             };
> > >           }
> > > +
> > > +          // Conduct the RPC call.
> > > +          fe[FE_G2C_CHANNEL](rpcData);
> > > +          return;
> > >         }
> > > -      }
> > > +      } else {
> > > +        // Call from container to gadget[targetId].
> > > +        var frame = document.getElementById(targetId);
> > >
> > > -      var result = (services[rpc.s] || services['']).apply(rpc,
> rpc.a);
> > > -      if (rpc.c) {
> > > -        gadgets.rpc.call(rpc.f, '__cb', null, rpc.c, result);
> > > +        if (typeof frame[FE_G2C_CHANNEL] === 'function' &&
> > > +            typeof frame[FE_G2C_CHANNEL][FE_C2G_CHANNEL] ===
> 'function')
> > {
> > > +
> > > +          // Conduct the RPC call.
> > > +          frame[FE_G2C_CHANNEL][FE_C2G_CHANNEL](rpcData);
> > > +          return;
> > > +        }
> > >       }
> > > +    } catch (e) {
> > > +    }
> > > +
> > > +    // If we have reached this point, something has failed
> > > +    // with the FrameElement method, so we default to using
> > > +    // IFPC for this call.
> > > +    callIFPC(targetId, from, rpcData);
> > > +  }
> > > +
> > > +  /**
> > > +   * Conducts an RPC call to the specified
> > > +   * target with the specified data via the IFPC
> > > +   * method.
> > > +   *
> > > +   * @param {String} targetId Module Id of the RPC service provider.
> > > +   * @param {String} from Module Id of the calling provider.
> > > +   * @param {Object} rpcData The RPC data for this call.
> > > +   */
> > > +  function callIFPC(targetId, from, rpcData) {
> > > +    // Retrieve the relay file used by IFPC. Note that
> > > +    // this must be set before the call, and so we conduct
> > > +    // an extra check to ensure it is not blank.
> > > +    var relay = gadgets.rpc.getRelayUrl(targetId);
> > > +
> > > +    if (!relay) {
> > > +      throw new Error('No relay file assigned for IFPC');
> > >     }
> > > +
> > > +    // The RPC mechanism supports two formats for IFPC (legacy and
> > > current).
> > > +    var src = null;
> > > +    if (useLegacyProtocol[targetId]) {
> > > +      // Format:
> #iframe_id&callId&num_packets&packet_num&block_of_data
> > > +      src = [relay, '#', encodeLegacyData([from, callId, 1, 0,
> > > +             encodeLegacyData([from, serviceName, '', '',
> from].concat(
> > > +               Array.prototype.slice.call(arguments, 3)))])].join('');
> > > +    } else {
> > > +      // Format: #targetId & sourceId@callId & packetNum & packetId &
> > > packetData
> > > +      src = [relay, '#', targetId, '&', from, '@', callId,
> > > +             '&1&0&', encodeURIComponent(rpcData)].join('');
> > > +    }
> > > +
> > > +    // Conduct the IFPC call by creating the Iframe with
> > > +    // the relay URL and appended message.
> > > +    emitInvisibleIframe(src);
> > >   }
> > >
> > > +
> > >   /**
> > >    * Helper function to emit an invisible IFrame.
> > >    * @param {String} src SRC attribute of the IFrame to emit.
> > > @@ -221,6 +617,15 @@
> > >      * @member gadgets.rpc
> > >      */
> > >     register: function(serviceName, handler) {
> > > +      if (serviceName == CALLBACK_NAME) {
> > > +        throw new Error("Cannot overwrite callback service");
> > > +      }
> > > +
> > > +      if (serviceName == DEFAULT_NAME) {
> > > +        throw new Error("Cannot overwrite default service:"
> > > +                        + " use registerDefault");
> > > +      }
> > > +
> > >       services[serviceName] = handler;
> > >     },
> > >
> > > @@ -231,6 +636,15 @@
> > >      * @member gadgets.rpc
> > >      */
> > >     unregister: function(serviceName) {
> > > +      if (serviceName == CALLBACK_NAME) {
> > > +        throw new Error("Cannot delete callback service");
> > > +      }
> > > +
> > > +      if (serviceName == DEFAULT_NAME) {
> > > +        throw new Error("Cannot delete default service:"
> > > +                        + " use unregisterDefault");
> > > +      }
> > > +
> > >       delete services[serviceName];
> > >     },
> > >
> > > @@ -272,12 +686,14 @@
> > >       if (callback) {
> > >         callbacks[callId] = callback;
> > >       }
> > > -      var from;
> > > +
> > > +      // Default to the container calling.
> > > +      var from = '..';
> > > +
> > >       if (targetId === '..') {
> > >         from = window.name;
> > > -      } else {
> > > -        from = '..';
> > >       }
> > > +
> > >       // Not used by legacy, create it anyway...
> > >       var rpcData = gadgets.json.stringify({
> > >         s: serviceName,
> > > @@ -287,53 +703,37 @@
> > >         t: authToken[targetId]
> > >       });
> > >
> > > +      var channelType = relayChannel;
> > > +
> > > +      // If we are told to use the legacy format, then we must
> > > +      // default to IFPC.
> > >       if (useLegacyProtocol[targetId]) {
> > > -        relayChannel = 'ifpc';
> > > +        channelType = 'ifpc';
> > >       }
> > >
> > > -      switch (relayChannel) {
> > > -      case 'dpm': // use document.postMessage
> > > -        var targetDoc = targetId === '..' ? parent.document :
> > > -                                            frames[targetId].document;
> > > -        targetDoc.postMessage(rpcData);
> > > -        break;
> > > -      case 'wpm': // use window.postMessage
> > > -        var targetWin = targetId === '..' ? parent : frames[targetId];
> > > -        targetWin.postMessage(rpcData, "*");
> > > -        break;
> > > -      default: // use 'ifpc' as a fallback mechanism
> > > -        var relay = gadgets.rpc.getRelayUrl(targetId);
> > > -        // TODO split message if too long
> > > -        var src;
> > > -        if (useLegacyProtocol[targetId]) {
> > > -          // #iframe_id&callId&num_packets&packet_num&block_of_data
> > > -          src = [relay, '#', encodeLegacyData([from, callId, 1, 0,
> > > -                 encodeLegacyData([from, serviceName, '', '',
> > > from].concat(
> > > -                 Array.prototype.slice.call(arguments,
> 3)))])].join('');
> > > -        } else {
> > > -          // Try the frameElement channel if available
> > > -          try {
> > > -            if (from === '..') {
> > > -              // Container-to-gadget
> > > -              var iframe = document.getElementById(targetId);
> > > -              if (typeof iframe.__g2c_rpc.__c2g_rpc === 'function') {
> > > -                iframe.__g2c_rpc.__c2g_rpc(rpcData);
> > > -                return;
> > > -              }
> > > -            } else {
> > > -              // Gadget-to-container
> > > -              if (typeof window.frameElement.__g2c_rpc === 'function')
> {
> > > -                window.frameElement.__g2c_rpc(rpcData);
> > > -                return;
> > > -              }
> > > -            }
> > > -          } catch (e) {
> > > -          }
> > > -          // # targetId & sourceId@callId & packetNum & packetId &
> > > packetData
> > > -          src = [relay, '#', targetId, '&', from, '@', callId,
> > > -                 '&1&0&', encodeURIComponent(rpcData)].join('');
> > > -        }
> > > -        emitInvisibleIframe(src);
> > > +      switch (channelType) {
> > > +        case 'dpm': // use document.postMessage.
> > > +          var targetDoc = targetId === '..' ? parent.document :
> > > +
>  frames[targetId].document;
> > > +          targetDoc.postMessage(rpcData);
> > > +          break;
> > > +
> > > +        case 'wpm': // use window.postMessage.
> > > +          var targetWin = targetId === '..' ? parent :
> frames[targetId];
> > > +          targetWin.postMessage(rpcData, "*");
> > > +          break;
> > > +
> > > +        case 'nix': // use NIX.
> > > +          callNIX(targetId, from, rpcData);
> > > +          break;
> > > +
> > > +        case 'fe': // use FrameElement.
> > > +          callFrameElement(targetId, from, rpcData);
> > > +          break;
> > > +
> > > +        default: // use 'ifpc' as a fallback mechanism.
> > > +          callIFPC(targetId, from, rpcData);
> > > +          break;
> > >       }
> > >     },
> > >
> > > @@ -372,15 +772,13 @@
> > >      */
> > >     setAuthToken: function(targetId, token) {
> > >       authToken[targetId] = token;
> > > +      setupFrame(targetId);
> > >     },
> > >
> > >     /**
> > >      * Gets the RPC relay mechanism.
> > > -     * @return {String} RPC relay mechanism. Supported types:
> > > -     *                  'wpm' - Use window.postMessage (defined by
> > HTML5)
> > > -     *                  'dpm' - Use document.postMessage (defined by
> an
> > > early
> > > -     *                          draft of HTML5 and implemented by
> Opera)
> > > -     *                  'ifpc' - Use invisible IFrames
> > > +     * @return {String} RPC relay mechanism. See above for
> > > +     *   a list of supported types.
> > >      *
> > >      * @member gadgets.rpc
> > >      */
> > >
> > > Modified:
> > >
> >
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > URL:
> > >
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java?rev=672393&r1=672392&r2=672393&view=diff
> > >
> > >
> >
> ==============================================================================
> > > ---
> > >
> >
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > (original)
> > > +++
> > >
> >
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > > Fri Jun 27 14:01:20 2008
> > > @@ -221,7 +221,8 @@
> > >           "a {color:#0000cc;}a:visited {color:#551a8b;}" +
> > >           "a:active {color:#ff0000;}" +
> > >           "body{margin: 0px;padding: 0px;background-color:white;}" +
> > > -          "</style></head>";
> > > +          "</style><base>" + gadget.getSpec().getUrl().toString() +
> > > +          "</base></head>";
> > >     markup.append(boilerPlate);
> > >     LocaleSpec localeSpec =
> gadget.getSpec().getModulePrefs().getLocale(
> > >         gadget.getContext().getLocale());
> > >
> > > Added:
> > incubator/shindig/trunk/javascript/container/rpctest_container.html
> > > URL:
> > >
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_container.html?rev=672393&view=auto
> > >
> > >
> >
> ==============================================================================
> > > --- incubator/shindig/trunk/javascript/container/rpctest_container.html
> > > (added)
> > > +++ incubator/shindig/trunk/javascript/container/rpctest_container.html
> > Fri
> > > Jun 27 14:01:20 2008
> > > @@ -0,0 +1,90 @@
> > > +<!--
> > > +  Simple page for testing gadgets.rpc performance.
> > > +  Allows you to add a simulated "gadget" (in this case just a static
> > > +  HTML page which loads gadgets.rpc also), and pass some
> > > +  specified number of random messages of specified size to
> > > +  and from it.
> > > +
> > > +  A simple recommended setup is to start up two instances of
> > > +  the Shindig Gadgets Server on two separate ports to test
> > > +  "real" cross-domain communication, since port is factored
> > > +  into the same-domain policy enforced by browsers.
> > > +
> > > +  If your servers are on localhost:8080 and localhost:8081, then hit:
> > > +
> http://localhost:8080/gadgets/files/container/rpctest_container.html?
> > \
> > > +  http://localhost:8081/gadgets/files/container/rpctest_gadget.html&\
> > > +
> > >
> >
> http://localhost:8081/gadgets/files/container/rpc_relay.uncompressed.html
> > > +
> > > +  (Note the backslashes should be removed, as they exist for
> formatting
> > > only.)
> > > +
> > > +  There are two arguments, separated by an ampersand:
> > > +  1. URL of the "gadget" test page.
> > > +  2. URL of the "gadget" test page's RPC relay (for browsers requiring
> > > it).
> > > +-->
> > > +<html>
> > > +  <head>
> > > +    <title>gadgets.rpc Performance Tests: Container</title>
> > > +    <script src="../../js/rpc.js?c=1&debug=1"></script>
> > > +    <script src="rpctest_perf.js"></script>
> > > +    <script>
> > > +      function initTest() {
> > > +        var container = document.getElementById("container");
> > > +
> > > +        // query string is assumed to be the "gadget" URL as arg 1,
> > > +        // and optionally the relay URL as arg 2
> > > +        var pageArgs = window.location.search.substring(1).split('&');
> > > +        var gadgetUrl = pageArgs[0];
> > > +        var secret = Math.round(Math.random()*10000000);
> > > +        if (pageArgs[1]) {
> > > +          gadgets.rpc.setRelayUrl('gadget', pageArgs[1]);
> > > +        }
> > > +        var containerRelay = pageArgs[2] || '';
> > > +        container.innerHTML = "<iframe id='gadget' name='gadget'
> > > height=300 width=300 src='" + gadgetUrl + "?parent=" + containerRelay +
> > > "#rpctoken=" + secret + "'></iframe>";
> > > +        gadgets.rpc.setAuthToken('gadget', secret);
> > > +
> > > +        initPerfTest();
> > > +      };
> > > +    </script>
> > > +  </head>
> > > +  <body onload="initTest();">
> > > +    <div>gadgets.rpc Performance: Container Page</div><hr/>
> > > +    <div>Test<br/>
> > > +      <ul>
> > > +        <li>Number of messages to send:
> > > +          <select name="num_msgs" id="num_msgs">
> > > +            <option value="1">1</option>
> > > +            <option value="10">10</option>
> > > +            <option value="100" selected>100</option>
> > > +            <option value="1000">1000</option>
> > > +          </select>
> > > +        </li>
> > > +        <li>Message size:
> > > +          <select name="msg_size" id="msg_size">
> > > +            <option value="10">10 B</option>
> > > +            <option value="100">100 B</option>
> > > +            <option value="1024" selected>1 kB</option>
> > > +            <option value="10240">10 kB</option>
> > > +            <option value="102400">100 kB</option>
> > > +            <option value="1048576">1 MB</option>
> > > +          </select>
> > > +        </li>
> > > +        <li>
> > > +          <input type="button" value="Start The Test!"
> > > onclick="runPerfTest('gadget');" />
> > > +        </li>
> > > +      </ul>
> > > +    </div>
> > > +    <div id="test_running" style="display:none;">
> > > +      Running test...
> > > +    </div>
> > > +    <div id="results" style="display:none;">
> > > +      Results: Gadget-to-Container<br/>
> > > +      Messages: <span id="results_num_received"></span>, Bytes: <span
> > > id="results_bytes_received"></span> <span id="in_or_out"></span><br/>
> > > +      Time elapsed for test run: <span
> > id="results_time_used"></span><br/>
> > > +      Messages/second: <span id="results_msgs_per_sec"></span><br/>
> > > +      Bytes/second: <span id="results_bytes_per_sec"></span>
> > > +    </div>
> > > +    <hr/>
> > > +    <div>Gadget:</div>
> > > +    <div id="container"></div>
> > > +  </body>
> > > +</html>
> > >
> > > Added: incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > > URL:
> > >
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_gadget.html?rev=672393&view=auto
> > >
> > >
> >
> ==============================================================================
> > > --- incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > > (added)
> > > +++ incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> Fri
> > > Jun 27 14:01:20 2008
> > > @@ -0,0 +1,49 @@
> > > +<html>
> > > +  <head>
> > > +    <title>gadgets.rpc Performance Tests: Gadget</title>
> > > +    <script src="../../js/rpc.js?c=1&debug=1"></script>
> > > +    <script src="rpctest_perf.js"></script>
> > > +    <script>
> > > +      var containerRelay = window.location.search.substring(1);
> > > +      gadgets.rpc.setRelayUrl(null, containerRelay);
> > > +    </script>
> > > +  </head>
> > > +  <body onload="initPerfTest();">
> > > +    <div>gadgets.rpc Performance: "Gadget" page</div><hr/>
> > > +    <div>Test<br/>
> > > +      <ul>
> > > +        <li>Number of messages to send:
> > > +          <select name="num_msgs" id="num_msgs">
> > > +            <option value="1">1</option>
> > > +            <option value="10">10</option>
> > > +            <option value="100" selected>100</option>
> > > +            <option value="1000">1000</option>
> > > +          </select>
> > > +        </li>
> > > +        <li>Message size:
> > > +          <select name="msg_size" id="msg_size">
> > > +            <option value="10">10 B</option>
> > > +            <option value="100">100 B</option>
> > > +            <option value="1024" selected>1 kB</option>
> > > +            <option value="10240">10 kB</option>
> > > +            <option value="102400">100 kB</option>
> > > +            <option value="1048576">1 MB</option>
> > > +          </select>
> > > +        </li>
> > > +        <li>
> > > +          <input type="button" value="Start The Test!"
> > > onclick="runPerfTest();" />
> > > +        </li>
> > > +      </ul>
> > > +    </div>
> > > +    <div id="test_running" style="display:none;">
> > > +      Running test...
> > > +    </div>
> > > +    <div id="results" style="display:none;">
> > > +      Results: Gadget-to-Container<br/>
> > > +      Messages: <span id="results_num_received"></span>, Bytes: <span
> > > id="results_bytes_received"></span> <span id="in_or_out"></span><br/>
> > > +      Time elapsed for test run: <span
> > id="results_time_used"></span><br/>
> > > +      Messages/second: <span id="results_msgs_per_sec"></span><br/>
> > > +      Bytes/second: <span id="results_bytes_per_sec"></span>
> > > +    </div>
> > > +  </body>
> > > +</html>
> > >
> > > Added: incubator/shindig/trunk/javascript/container/rpctest_perf.js
> > > URL:
> > >
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_perf.js?rev=672393&view=auto
> > >
> > >
> >
> ==============================================================================
> > > --- incubator/shindig/trunk/javascript/container/rpctest_perf.js
> (added)
> > > +++ incubator/shindig/trunk/javascript/container/rpctest_perf.js Fri
> Jun
> > 27
> > > 14:01:20 2008
> > > @@ -0,0 +1,93 @@
> > > +var perfStats = null;
> > > +var currentRun = {};
> > > +
> > > +function perfService(message) {
> > > +  if (perfStats.numResults++ === 0) {
> > > +    perfStats.firstMsg = message; // stored since it has "real" start
> > time
> > > +  }
> > > +  perfStats.bytesReceived += message.length;
> > > +};
> > > +
> > > +function clearPerfStats(inOrOut) {
> > > +  perfStats = {
> > > +    numResults: 0,
> > > +    bytesReceived: 0,
> > > +    firstMsg: null
> > > +  };
> > > +
> > > +  document.getElementById("in_or_out").innerHTML = inOrOut;
> > > +
> > > +  // hide results fields
> > > +  document.getElementById("results").style.display = "none";
> > > +};
> > > +
> > > +function completePerfStats() {
> > > +  perfStats.timeEnded = new Date().getTime();
> > > +
> > > +  // get time started from the first sent message
> > > +  perfStats.timeStarted = perfStats.firstMsg.substr(0,
> > > perfStats.firstMsg.indexOf(':'));
> > > +
> > > +  var timeUsedMs = perfStats.timeEnded - perfStats.timeStarted;
> > > +
> > > +  // fill in fields
> > > +  document.getElementById("results_num_received").innerHTML =
> > > perfStats.numResults;
> > > +  document.getElementById("results_bytes_received").innerHTML =
> > > perfStats.bytesReceived;
> > > +  document.getElementById("results_time_used").innerHTML = timeUsedMs
> +
> > > "ms";
> > > +  document.getElementById("results_msgs_per_sec").innerHTML =
> > > (perfStats.numResults / (timeUsedMs / 1000));
> > > +  document.getElementById("results_bytes_per_sec").innerHTML =
> > > (perfStats.bytesReceived / (timeUsedMs / 1000));
> > > +  document.getElementById("test_running").style.display = "none";
> > > +  document.getElementById("results").style.display = "";
> > > +};
> > > +
> > > +function initPerfTest() {
> > > +  clearPerfStats();
> > > +  gadgets.rpc.register("perf_service", perfService);
> > > +  gadgets.rpc.register("clear_perf_stats", clearPerfStats);
> > > +  gadgets.rpc.register("complete_perf_stats", completePerfStats);
> > > +};
> > > +
> > > +var alphabet =
> > > "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
> > _-*&(){}'";
> > > +
> > > +function sendPerfMessage() {
> > > +  var msgToSend = currentRun.msg;
> > > +  if (currentRun.curMsgId++ <= 1) {
> > > +    var nowString = new Date().getTime() + ':';
> > > +    msgToSend = nowString +
> currentRun.msg.substring(nowString.length);
> > > +  }
> > > +
> > > +  gadgets.rpc.call(currentRun.targetId, "perf_service", null,
> > msgToSend);
> > > +  if (currentRun.curMsgId < currentRun.endMsgId) {
> > > +    // loop, giving up execution in case rpc technique demands it
> > > +    window.setTimeout(sendPerfMessage, 0);
> > > +  } else {
> > > +    // send finisher
> > > +    window.setTimeout(function() {
> gadgets.rpc.call(currentRun.targetId,
> > > "complete_perf_stats", null); }, 0);
> > > +  }
> > > +};
> > > +
> > > +function runPerfTest(targetId) {
> > > +  document.getElementById("test_running").style.display = "";
> > > +
> > > +  // initialize the current run
> > > +  var num_msgs = document.getElementById("num_msgs").value;
> > > +  var msg_size = document.getElementById("msg_size").value;
> > > +
> > > +  currentRun.targetId = targetId;
> > > +  currentRun.curMsgId = 0;
> > > +  currentRun.endMsgId = num_msgs;
> > > +
> > > +  var msg = [];
> > > +  for (var i = 0; i < msg_size; ++i) {
> > > +    msg[i] =
> alphabet.charAt(Math.round(Math.random(alphabet.length)));
> > > +  }
> > > +  currentRun.msg = msg.join('');
> > > +
> > > +  // clear local perf stats
> > > +  clearPerfStats("(outbound)");
> > > +
> > > +  // clear target perf stats
> > > +  gadgets.rpc.call(targetId, "clear_perf_stats", null, "(inbound)");
> > > +
> > > +  // kick off the send loop
> > > +  sendPerfMessage();
> > > +};
> > >
> > >
> > >
> >
>

Re: svn commit: r672393 - in /incubator/shindig/trunk: features/rpc/ java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/ javascript/container/

Posted by Kevin Brown <et...@google.com>.
Please actually test your code or send it to other shindig committers before
you commit it. Your change to GadgetRenderingTask breaks proxying and
makeRequest in several cases. Fix it.

Have you run this by Brian and/or Mike Samuel? They both expressed doubts
about whether this mechanism was actually secure.

On Fri, Jun 27, 2008 at 2:06 PM, John Hjelmstad <fa...@google.com> wrote:

> +jschorr in case there are comments.
>
> On Fri, Jun 27, 2008 at 2:01 PM, <jo...@apache.org> wrote:
>
> > Author: johnh
> > Date: Fri Jun 27 14:01:20 2008
> > New Revision: 672393
> >
> > URL: http://svn.apache.org/viewvc?rev=672393&view=rev
> > Log:
> > Cleaned up gadgets.rpc library implementation, and implemented fast IE
> > transport layer.
> >
> > Credit:
> >        Joseph Schorr (jschorr@google.com) and I implemented this
> together,
> > but he
> >        really did all the hard work, as well as developing the NIX
> > technique's
> >        fundamentals. Huge thanks to Joseph!
> >
> > Details:
> >        * Created a new relayChannel for Firefox frameElement technique,
> >          making its implementation more linear to read.
> >        * Consolidated all transport-specific code in setupChannel(),
> > setupFrame(),
> >          and callX(), where X = relay type. This refactoring makes setup
> > and use
> >          of each transport more clear, and makes it possible in later
> > revisions
> >          to separate out each method as its own class. In theory, if we
> can
> > trust
> >          things like User-Agent headers, we can use this to reduce rpc.js
> > code
> >          size significantly by only including the needed transport(s).
> >        * Implemented "NIX" - Native IE XDC. This method exploits the fact
> >          that in IE6 and IE7 the window.opener property can be both set
> and
> >          read across domain boundaries for an IFRAME, and that any object
> >          can be passed through it. In particular, functions implementing
> >          message passing can be passed. These can't be JS functions,
> > however,
> >          since those can leak sender context. So, VBScript (COM) objects
> >          wrapping these are passed to maintain context isolation.
> >          - Requires for IE6/7 that rpc.js be included in the container
> >            at load time, not dynamically. TODO added to consider
> detecting
> >            whether dynamic loading is happening (at which point we should
> >            fall back to IFPC).
> >          - Message channel handshake *and* message passing validated
> >            using authToken, as with other channels.
> >          - Impl requires that gadget call container first - else IFPC
> >            is used. This is the same as the frameElement technique. We
> >            could add a setInterval()-based initialization routine later
> >            if needed. To date, the only gadgets.rpc calls made from
> >            container to gadget without gadget to container first are
> >            in theory pubsub messages.
> >          - Extensive documentation on this technique and the others
> >            added to the comments, since they're stripped out in the
> >            majority case.
> >        * Implemented quick-and-dirty performance testing page verifying
> >          the library works and giving basic performance metrics.
> >          This needs to be improved! But does the trick for now...
> >
> > Testing/Data:
> >
> > Library verified on the following browsers with the RPC test page. For
> > each,
> > a "bandwidth" measure was taken by averaging the results of 6 G2C and C2G
> > runs
> > with 100 messages being passed of 1K apiece. Units are 1K messages/sec.
> > The "latency" value is the average amount of time taken, over 6 G2C and
> C2G
> > runs,
> > to send 1 1K message. Happily, for all browsers save Safari (soon due to
> > get
> > window.postMessage), latency is around or below time required to achieve
> > a cross-domain call without user-perceived delay.
> >
> >        * Firefox 3 (WPM) - bandwidth: 88 kB/sec; latency: 18ms.
> >        * Firefox 2 (FE)  - bandwidth: 80 kB/sec; latency: 15ms.
> >        * Opera 9   (WPM) - bandwidth: 85 kB/sec; latency: 7ms.
> >        * IE6       (NIX) - bandwidth: 64 kB/sec; latency: 18ms.
> >        * IE7       (NIX) - bandwidth: 64 kB/sec; latency: 22ms.
> >        * Safari 3 (IFPC) - bandwidth: ?6-8kB/sec; latency: ?100-200ms.
> >          - Safari is somewhat flaky with the RPC test page, before
> >            and after the rpc.js change. Multiple messages seem to
> >            confuse it from time to time.
> >
> >
> > Added:
> >    incubator/shindig/trunk/javascript/container/rpctest_container.html
> >    incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> >    incubator/shindig/trunk/javascript/container/rpctest_perf.js
> > Modified:
> >    incubator/shindig/trunk/features/rpc/rpc.js
> >
> >
>  incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> >
> > Modified: incubator/shindig/trunk/features/rpc/rpc.js
> > URL:
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/features/rpc/rpc.js?rev=672393&r1=672392&r2=672393&view=diff
> >
> >
> ==============================================================================
> > --- incubator/shindig/trunk/features/rpc/rpc.js (original)
> > +++ incubator/shindig/trunk/features/rpc/rpc.js Fri Jun 27 14:01:20 2008
> > @@ -18,7 +18,7 @@
> >
> >  /**
> >  * @fileoverview Remote procedure call library for gadget-to-container,
> > - * container-to-gadget, and gadget-to-gadget communication.
> > + * container-to-gadget, and gadget-to-gadget (thru container)
> > communication.
> >  */
> >
> >  var gadgets = gadgets || {};
> > @@ -29,6 +29,30 @@
> >  * @name gadgets.rpc
> >  */
> >  gadgets.rpc = function() {
> > +  // General constants.
> > +  var CALLBACK_NAME = '__cb';
> > +  var DEFAULT_NAME = '';
> > +
> > +  // Consts for FrameElement.
> > +  var FE_G2C_CHANNEL = '__g2c_rpc';
> > +  var FE_C2G_CHANNEL = '__c2g_rpc';
> > +
> > +  // Consts for NIX. VBScript doesn't
> > +  // allow items to start with _ for some reason,
> > +  // so we need to make these names quite unique, as
> > +  // they will go into the global namespace.
> > +  var NIX_WRAPPER = 'GRPC____NIXVBS_wrapper';
> > +  var NIX_GET_WRAPPER = 'GRPC____NIXVBS_get_wrapper';
> > +  var NIX_HANDLE_MESSAGE = 'GRPC____NIXVBS_handle_message';
> > +  var NIX_CREATE_CHANNEL = 'GRPC____NIXVBS_create_channel';
> > +  var NIX_ID_FIELD = 'GRPC____NIXVBS_container';
> > +
> > +  // JavaScript reference to the NIX VBScript wrappers.
> > +  // Gadgets will have but a single channel under
> > +  // nix_channels['..'] while containers will have a channel
> > +  // per gadget stored under the gadget's ID.
> > +  var nix_channels = {};
> > +
> >   var services = {};
> >   var iframePool = [];
> >   var relayUrl = {};
> > @@ -36,28 +60,135 @@
> >   var authToken = {};
> >   var callId = 0;
> >   var callbacks = {};
> > +  var setup = {};
> > +
> > +  var params = {};
> > +
> > +  // Load the authentication token for speaking to the container
> > +  // from the gadget's parameters, or default to '0' if not found.
> > +  if (gadgets.util) {
> > +    params = gadgets.util.getUrlParameters();
> > +  }
> >
> > -  var params = gadgets.util.getUrlParameters();
> >   authToken['..'] = params.rpctoken || params.ifpctok || 0;
> >
> > -  // Pick the most efficient RPC relay mechanism
> > -  var relayChannel = typeof document.postMessage === 'function' ? 'dpm'
> :
> > -                     typeof window.postMessage === 'function' ? 'wpm' :
> > +  // Pick the most efficient RPC relay mechanism:
> > +  //
> > +  // * For those browsers that support native messaging (various
> > implementations
> > +  //   of the HTML5 postMessage method), use that. Officially defined at
> > +  //
> > http://www.whatwg.org/specs/web-apps/current-work/multipage/comms.html.
> > +  //
> > +  //   postMessage is a native implementation of XDC. A page registers
> > that
> > +  //   it would like to receive messages by listening the the "message"
> > event
> > +  //   on the window (document in DPM) object. In turn, another page can
> > +  //   raise that event by calling window.postMessage
> > (document.postMessage
> > +  //   in DPM) with a string representing the message and a string
> > +  //   indicating on which domain the receiving page must be to receive
> > +  //   the message. The target page will then have its "message" event
> > raised
> > +  //   if the domain matches and can, in turn, check the origin of the
> > message
> > +  //   and process the data contained within.
> > +  //
> > +  //     wpm: postMessage on the window object.
> > +  //        - Internet Explorer 8+
> > +  //        - Safari (latest nightlies as of 26/6/2008)
> > +  //        - Firefox 3+
> > +  //        - Opera 9+
> > +  //
> > +  //     dpm: postMessage on the document object.
> > +  //        - Opera 8+
> > +  //
> > +  // * For Internet Explorer before version 8, the security model allows
> a
> > +  //   child to set the value of the "opener" property on the parent
> > window
> > +  //   and vice versa. This method is dubbed "Native IE XDC" (NIX).
> > +  //
> > +  //   This method works by placing a handler object in the "opener"
> > property
> > +  //   of a gadget when the container sets up the authentication
> > information
> > +  //   for that gadget (by calling setAuthToken(...)). At that point, a
> > NIX
> > +  //   wrapper is created and placed into the gadget by calling
> > +  //   theframe.contentWindow.opener = wrapper. Note that as a result,
> NIX
> > can
> > +  //   only be used by a container to call a particular gadget *after*
> > that
> > +  //   gadget has called the container at least once via NIX.
> > +  //
> > +  //   The NIX wrappers in this RPC implementation are instances of a
> > VBScript
> > +  //   class that is created when this implementation loads. The reason
> > for
> > +  //   using a VBScript class stems from the fact that any object can be
> > passed
> > +  //   into the opener property.
> > +  //   While this is a good thing, as it lets us pass functions and
> setup
> > a true
> > +  //   bidirectional channel via callbacks, it opens a potential
> security
> > hole
> > +  //   by which the other page can get ahold of the "window" or
> "document"
> > +  //   objects in the parent page and in turn wreak havok. This is due
> to
> > the
> > +  //   fact that any JS object useful for establishing such a
> > bidirectional
> > +  //   channel (such as a function) can be used to access a function
> > +  //   (eg. obj.toString, or a function itself) created in a specific
> > context,
> > +  //   in particular the global context of the sender. Suppose container
> > +  //   domain C passes object obj to gadget on domain G. Then the gadget
> > can
> > +  //   access C's global context using:
> > +  //   var parentWindow = (new obj.toString.constructor("return
> > window;"))();
> > +  //   Nulling out all of obj's properties doesn't fix this, since IE
> > helpfully
> > +  //   restores them to their original values if you do something like:
> > +  //   delete obj.toString; delete obj.toString;
> > +  //   Thus, we wrap the necessary functions and information inside a
> > VBScript
> > +  //   object. VBScript objects in IE, like DOM objects, are in fact COM
> > +  //   wrappers when used in JavaScript, so we can safely pass them
> around
> > +  //   without worrying about a breach of context while at the same time
> > +  //   allowing them to act as a pass-through mechanism for information
> > +  //   and function calls. The implementation details of this VBScript
> > wrapper
> > +  //   can be found in the setupChannel() method below.
> > +  //
> > +  //     nix: Internet Explorer-specific window.opener trick.
> > +  //       - Internet Explorer 6
> > +  //       - Internet Explorer 7
> > +  //
> > +  // * For Gecko-based browsers, the security model allows a child to
> call
> > a
> > +  //   function on the frameElement of the iframe, even if the child is
> in
> > +  //   a different domain. This method is dubbed "frameElement" (fe).
> > +  //
> > +  //   The ability to add and call such functions on the frameElement
> > allows
> > +  //   a bidirectional channel to be setup via the adding of simple
> > function
> > +  //   references on the frameElement object itself. In this
> > implementation,
> > +  //   when the container sets up the authentication information for
> that
> > gadget
> > +  //   (by calling setAuth(...)) it as well adds a special function on
> the
> > +  //   gadget's iframe. This function can then be used by the gadget to
> > send
> > +  //   messages to the container. In turn, when the gadget tries to send
> a
> > +  //   message, it checks to see if this function has its own function
> > stored
> > +  //   that can be used by the container to call the gadget. If not, the
> > +  //   function is created and subsequently used by the container.
> > +  //   Note that as a result, FE can only be used by a container to call
> a
> > +  //   particular gadget *after* that gadget has called the container at
> > +  //   least once via FE.
> > +  //
> > +  //     fe: Gecko-specific frameElement trick.
> > +  //        - Firefox 1+
> > +  //
> > +  // * For all others, we have a fallback mechanism known as "ifpc".
> IFPC
> > +  //   exploits the fact that while same-origin policy prohibits a frame
> > from
> > +  //   accessing members on a window not in the same domain, that frame
> > can,
> > +  //   however, navigate the window heirarchy (via parent). This is
> > exploited by
> > +  //   having a page on domain A that wants to talk to domain B create
> an
> > iframe
> > +  //   on domain B pointing to a special relay file and with a message
> > encoded
> > +  //   after the hash (#). This relay, in turn, finds the page on domain
> > B, and
> > +  //   can call a receipt function with the message given to it. The
> relay
> > URL
> > +  //   used by each caller is set via the gadgets.rpc.setRelayUrl(..)
> and
> > +  //   *must* be called before the call method is used.
> > +  //
> > +  //     ifpc: Iframe-based method, utilizing a relay page, to send a
> > message.
> > +  //
> > +  var relayChannel = typeof window.postMessage === 'function' ? 'wpm' :
> > +                    typeof document.postMessage === 'function' ? 'dpm' :
> > +                    window.ActiveXObject ? 'nix' :
> > +                     navigator.product === 'Gecko' ? 'fe' :
> >                      'ifpc';
> > -  if (relayChannel === 'dpm' || relayChannel === 'wpm') {
> > -    window.addEventListener('message', function(packet) {
> > -      // TODO validate packet.domain for security reasons
> > -      process(gadgets.json.parse(packet.data));
> > -    }, false);
> > -  }
> >
> > -  // Default RPC handler
> > -  services[''] = function() {
> > +  // Conduct any setup necessary for the chosen channel.
> > +  setupChannel();
> > +
> > +  // Create the Default RPC handler.
> > +  services[DEFAULT_NAME] = function() {
> >     throw new Error('Unknown RPC service: ' + this.s);
> >   };
> >
> > -  // Special RPC handler for callbacks
> > -  services['__cb'] = function(callbackId, result) {
> > +  // Create a Special RPC handler for callbacks.
> > +  services[CALLBACK_NAME] = function(callbackId, result) {
> >     var callback = callbacks[callbackId];
> >     if (callback) {
> >       delete callbacks[callbackId];
> > @@ -66,6 +197,147 @@
> >   };
> >
> >   /**
> > +   * Conducts any initial global work necessary to setup the
> > +   * channel type chosen.
> > +   */
> > +  function setupChannel() {
> > +    // If the channel type is one of the native
> > +    // postMessage based ones, setup the handler to receive
> > +    // messages.
> > +    if (relayChannel === 'dpm' || relayChannel === 'wpm') {
> > +      window.addEventListener('message', function(packet) {
> > +        // TODO validate packet.domain for security reasons
> > +        process(gadgets.json.parse(packet.data));
> > +      }, false);
> > +    }
> > +
> > +    // If the channel type is NIX, we need to ensure the
> > +    // VBScript wrapper code is in the page and that the
> > +    // global Javascript handlers have been set.
> > +    if (relayChannel === 'nix') {
> > +      // VBScript methods return a type of 'unknown' when
> > +      // checked via the typeof operator in IE. Fortunately
> > +      // for us, this only applies to COM objects, so we
> > +      // won't see this for a real Javascript object.
> > +      if (typeof window[NIX_GET_WRAPPER] !== 'unknown') {
> > +        window[NIX_HANDLE_MESSAGE] = function(data) {
> > +          process(gadgets.json.parse(data));
> > +        };
> > +
> > +        window[NIX_CREATE_CHANNEL] = function(name, channel, token) {
> > +          // Verify the authentication token of the gadget trying
> > +          // to create a channel for us.
> > +          if (authToken[name] == token) {
> > +            nix_channels[name] = channel;
> > +          }
> > +        };
> > +
> > +        // Inject the VBScript code needed.
> > +        var vbscript =
> > +          '<scr' + 'ipt language="VBScript">'
> > +          // We create a class to act as a wrapper for
> > +          // a Javascript call, to prevent a break in of
> > +          // the context.
> > +          + 'Class ' + NIX_WRAPPER + '\n '
> > +
> > +          // An internal member for keeping track of the
> > +          // name of the document (container or gadget)
> > +          // for which this wrapper is intended. For
> > +          // those wrappers created by gadgets, this is not
> > +          // used (although it is set to "..")
> > +          + 'Private m_Intended\n'
> > +
> > +          // Method for internally setting the value
> > +          // of the m_Intended property.
> > +          + 'Public Sub SetIntendedName(name)\n '
> > +          + 'm_Intended = name\n'
> > +          + 'End Sub\n'
> > +
> > +          // A wrapper method which actually causes a
> > +          // message to be sent to the other context.
> > +          + 'Public Sub SendMessage(data)\n '
> > +          + NIX_HANDLE_MESSAGE + '(data)\n'
> > +          + 'End Sub\n'
> > +
> > +          // Method for setting up the container->gadget
> > +          // channel. Not strictly needed in the gadget's
> > +          // wrapper, but no reason to get rid of it. Note here
> > +          // that we pass the intended name to the NIX_CREATE_CHANNEL
> > +          // method so that it can save the channel in the proper place
> > +          // *and* verify the channel via the authentication token
> passed
> > +          // here.
> > +          + 'Public Sub CreateChannel(channel, auth)\n '
> > +          + 'Call ' + NIX_CREATE_CHANNEL + '(m_Intended, channel,
> auth)\n'
> > +          + 'End Sub\n'
> > +
> > +          // An empty field with a unique identifier to
> > +          // prevent the code from confusing this wrapper
> > +          // with a run-of-the-mill value found in window.opener.
> > +          + 'Public Sub ' + NIX_ID_FIELD + '()\n '
> > +          + 'End Sub\n'
> > +          + 'End Class\n '
> > +
> > +          // Function to get a reference to the wrapper.
> > +          + 'Function ' + NIX_GET_WRAPPER + '(name)\n'
> > +          + 'Dim wrap\n'
> > +          + 'Set wrap = New ' + NIX_WRAPPER + '\n'
> > +          + 'wrap.SetIntendedName name\n'
> > +          + 'Set ' + NIX_GET_WRAPPER + ' = wrap\n'
> > +          + 'End Function'
> > +          + '</scr' + 'ipt>'
> > +
> > +          // Resets the "default" scripting language in IE back
> > +          // to Javascript. This is needed or else any script
> > +          // tags without a proper Language="..." will be treated as
> > +          // VBScript once this code is added to the document.
> > +          + '<scri' + 'pt language="JScript"></scr' + 'ipt>';
> > +
> > +        // Note that this code can only be run once the document has
> > +        // fully loaded.
> > +        // TODO: Perhaps add some sort of check here for this?
> > +        document.write(vbscript);
> > +      }
> > +    }
> > +  }
> > +
> > +  /**
> > +   * Conducts any frame-specific work necessary to setup
> > +   * the channel type chosen. This method is called when
> > +   * the container page first registers the gadget in the
> > +   * RPC mechanism. Gadgets, in turn, will complete the setup
> > +   * of the channel once they send their first messages.
> > +   */
> > +  function setupFrame(frameId) {
> > +    if (setup[frameId]) {
> > +      return;
> > +    }
> > +
> > +    if (relayChannel === 'fe') {
> > +      try {
> > +        var frame = document.getElementById(frameId);
> > +        frame[FE_G2C_CHANNEL] = function(args) {
> > +          process(gadgets.json.parse(args));
> > +        };
> > +      } catch (e) {
> > +        // Something went wrong. System will fallback to
> > +        // IFPC.
> > +      }
> > +    }
> > +
> > +    if (relayChannel === 'nix') {
> > +      try {
> > +        var frame = document.getElementById(frameId);
> > +        frame.contentWindow.opener = window[NIX_GET_WRAPPER](frameId);
> > +      } catch (e) {
> > +        // Something went wrong. System will fallback to
> > +        // IFPC.
> > +      }
> > +    }
> > +
> > +    setup[frameId] = true;
> > +  }
> > +
> > +  /**
> >    * Encodes arguments for the legacy IFPC wire format.
> >    *
> >    * @param {Object} args
> > @@ -86,8 +358,17 @@
> >    * @private
> >    */
> >   function process(rpc) {
> > +    //
> > +    // RPC object contents:
> > +    //   s: Service Name
> > +    //   f: From
> > +    //   c: The callback ID or 0 if none.
> > +    //   a: The arguments for this RPC call.
> > +    //   t: The authentication token.
> > +    //
> >     if (rpc && typeof rpc.s === 'string' && typeof rpc.f === 'string' &&
> >         rpc.a instanceof Array) {
> > +
> >       // Validate auth token.
> >       if (authToken[rpc.f]) {
> >         // We allow type coercion here because all the url params are
> > strings.
> > @@ -96,43 +377,158 @@
> >         }
> >       }
> >
> > -      // The Gecko engine used by FireFox etc. allows an IFrame to
> > directly call
> > -      // methods on the frameElement property added by the container
> page
> > even
> > -      // if their domains don't match.
> > -      // Here we try to set up a relay channel using the frameElement
> > technique
> > -      // to greatly reduce the latency of cross-domain calls if the
> > postMessage
> > -      // method is not supported.
> > -      if (relayChannel === 'ifpc') {
> > -        if (rpc.f === '..') {
> > -          // Container-to-gadget call
> > -          try {
> > -            var fel = window.frameElement;
> > -            if (typeof fel.__g2c_rpc === 'function' &&
> > -                typeof fel.__g2c_rpc.__c2g_rpc != 'function') {
> > -              fel.__g2c_rpc.__c2g_rpc = function(args) {
> > -                process(gadgets.json.parse(args));
> > -              };
> > -            }
> > -          } catch (e) {
> > -          }
> > -        } else {
> > -          // Gadget-to-container call
> > -          var iframe = document.getElementById(rpc.f);
> > -          if (iframe && typeof iframe.__g2c_rpc != 'function') {
> > -            iframe.__g2c_rpc = function(args) {
> > +      // Call the requested RPC service.
> > +      var result = (services[rpc.s] ||
> > +                    services[DEFAULT_NAME]).apply(rpc, rpc.a);
> > +
> > +      // If there is a callback for this service, initiate it as well.
> > +      if (rpc.c) {
> > +        gadgets.rpc.call(rpc.f, CALLBACK_NAME, null, rpc.c, result);
> > +      }
> > +    }
> > +  }
> > +
> > +  /**
> > +   * Attempts to conduct an RPC call to the specified
> > +   * target with the specified data via the NIX
> > +   * method. If this method fails, the system attempts again
> > +   * using the known default of IFPC.
> > +   *
> > +   * @param {String} targetId Module Id of the RPC service provider.
> > +   * @param {String} from Module Id of the calling provider.
> > +   * @param {Object} rpcData The RPC data for this call.
> > +   */
> > +  function callNIX(targetId, from, rpcData) {
> > +    try {
> > +      if (from != '..') {
> > +        // Call from gadget to the container.
> > +        var handler = nix_channels['..'];
> > +
> > +        // If the gadget has yet to retrieve a reference to
> > +        // the NIX handler, try to do so now. We don't do a
> > +        // typeof(window.opener[NIX_ID_FIELD]) check here
> > +        // because it means accessing that field on the COM object,
> which,
> > +        // being an internal function reference, is not allowed.
> > +        // "in" works because it merely checks for the prescence of
> > +        // the key, rather than actually accessing the object's
> property.
> > +        if (!handler && window.opener && NIX_ID_FIELD in window.opener)
> {
> > +          handler = nix_channels['..'] = window.opener;
> > +
> > +          // Create the channel to the parent/container. We pass both
> our
> > +          // own wrapper and our authentication token for verification.
> > +          handler.CreateChannel(window[NIX_GET_WRAPPER]('..'),
> > authToken['..']);
> > +        }
> > +
> > +        // If we have a handler, call it.
> > +        if (handler) {
> > +          handler.SendMessage(rpcData);
> > +          return;
> > +        }
> > +      } else {
> > +        // Call from container to a gadget[targetId].
> > +
> > +        // If we have a handler, call it.
> > +        if (nix_channels[targetId]) {
> > +          nix_channels[targetId].SendMessage(rpcData);
> > +          return;
> > +        }
> > +      }
> > +    } catch (e) {
> > +    }
> > +
> > +    // If we have reached this point, something has failed
> > +    // with the NIX method, so we default to using
> > +    // IFPC for this call.
> > +    callIFPC(targetId, from, rpcData);
> > +  }
> > +
> > +  /**
> > +   * Attempts to conduct an RPC call to the specified
> > +   * target with the specified data via the FrameElement
> > +   * method. If this method fails, the system attempts again
> > +   * using the known default of IFPC.
> > +   *
> > +   * @param {String} targetId Module Id of the RPC service provider.
> > +   * @param {String} from Module Id of the calling provider.
> > +   * @param {Object} rpcData The RPC data for this call.
> > +   */
> > +  function callFrameElement(targetId, from, rpcData) {
> > +    try {
> > +      if (from != '..') {
> > +        // Call from gadget to the container.
> > +        var fe = window.frameElement;
> > +
> > +        if (typeof fe[FE_G2C_CHANNEL] === 'function') {
> > +          // Complete the setup of the FE channel if need be.
> > +          if (typeof fe[FE_G2C_CHANNEL][FE_C2G_CHANNEL] !== 'function')
> {
> > +            fe[FE_G2C_CHANNEL][FE_C2G_CHANNEL] = function(args) {
> >               process(gadgets.json.parse(args));
> >             };
> >           }
> > +
> > +          // Conduct the RPC call.
> > +          fe[FE_G2C_CHANNEL](rpcData);
> > +          return;
> >         }
> > -      }
> > +      } else {
> > +        // Call from container to gadget[targetId].
> > +        var frame = document.getElementById(targetId);
> >
> > -      var result = (services[rpc.s] || services['']).apply(rpc, rpc.a);
> > -      if (rpc.c) {
> > -        gadgets.rpc.call(rpc.f, '__cb', null, rpc.c, result);
> > +        if (typeof frame[FE_G2C_CHANNEL] === 'function' &&
> > +            typeof frame[FE_G2C_CHANNEL][FE_C2G_CHANNEL] === 'function')
> {
> > +
> > +          // Conduct the RPC call.
> > +          frame[FE_G2C_CHANNEL][FE_C2G_CHANNEL](rpcData);
> > +          return;
> > +        }
> >       }
> > +    } catch (e) {
> > +    }
> > +
> > +    // If we have reached this point, something has failed
> > +    // with the FrameElement method, so we default to using
> > +    // IFPC for this call.
> > +    callIFPC(targetId, from, rpcData);
> > +  }
> > +
> > +  /**
> > +   * Conducts an RPC call to the specified
> > +   * target with the specified data via the IFPC
> > +   * method.
> > +   *
> > +   * @param {String} targetId Module Id of the RPC service provider.
> > +   * @param {String} from Module Id of the calling provider.
> > +   * @param {Object} rpcData The RPC data for this call.
> > +   */
> > +  function callIFPC(targetId, from, rpcData) {
> > +    // Retrieve the relay file used by IFPC. Note that
> > +    // this must be set before the call, and so we conduct
> > +    // an extra check to ensure it is not blank.
> > +    var relay = gadgets.rpc.getRelayUrl(targetId);
> > +
> > +    if (!relay) {
> > +      throw new Error('No relay file assigned for IFPC');
> >     }
> > +
> > +    // The RPC mechanism supports two formats for IFPC (legacy and
> > current).
> > +    var src = null;
> > +    if (useLegacyProtocol[targetId]) {
> > +      // Format: #iframe_id&callId&num_packets&packet_num&block_of_data
> > +      src = [relay, '#', encodeLegacyData([from, callId, 1, 0,
> > +             encodeLegacyData([from, serviceName, '', '', from].concat(
> > +               Array.prototype.slice.call(arguments, 3)))])].join('');
> > +    } else {
> > +      // Format: #targetId & sourceId@callId & packetNum & packetId &
> > packetData
> > +      src = [relay, '#', targetId, '&', from, '@', callId,
> > +             '&1&0&', encodeURIComponent(rpcData)].join('');
> > +    }
> > +
> > +    // Conduct the IFPC call by creating the Iframe with
> > +    // the relay URL and appended message.
> > +    emitInvisibleIframe(src);
> >   }
> >
> > +
> >   /**
> >    * Helper function to emit an invisible IFrame.
> >    * @param {String} src SRC attribute of the IFrame to emit.
> > @@ -221,6 +617,15 @@
> >      * @member gadgets.rpc
> >      */
> >     register: function(serviceName, handler) {
> > +      if (serviceName == CALLBACK_NAME) {
> > +        throw new Error("Cannot overwrite callback service");
> > +      }
> > +
> > +      if (serviceName == DEFAULT_NAME) {
> > +        throw new Error("Cannot overwrite default service:"
> > +                        + " use registerDefault");
> > +      }
> > +
> >       services[serviceName] = handler;
> >     },
> >
> > @@ -231,6 +636,15 @@
> >      * @member gadgets.rpc
> >      */
> >     unregister: function(serviceName) {
> > +      if (serviceName == CALLBACK_NAME) {
> > +        throw new Error("Cannot delete callback service");
> > +      }
> > +
> > +      if (serviceName == DEFAULT_NAME) {
> > +        throw new Error("Cannot delete default service:"
> > +                        + " use unregisterDefault");
> > +      }
> > +
> >       delete services[serviceName];
> >     },
> >
> > @@ -272,12 +686,14 @@
> >       if (callback) {
> >         callbacks[callId] = callback;
> >       }
> > -      var from;
> > +
> > +      // Default to the container calling.
> > +      var from = '..';
> > +
> >       if (targetId === '..') {
> >         from = window.name;
> > -      } else {
> > -        from = '..';
> >       }
> > +
> >       // Not used by legacy, create it anyway...
> >       var rpcData = gadgets.json.stringify({
> >         s: serviceName,
> > @@ -287,53 +703,37 @@
> >         t: authToken[targetId]
> >       });
> >
> > +      var channelType = relayChannel;
> > +
> > +      // If we are told to use the legacy format, then we must
> > +      // default to IFPC.
> >       if (useLegacyProtocol[targetId]) {
> > -        relayChannel = 'ifpc';
> > +        channelType = 'ifpc';
> >       }
> >
> > -      switch (relayChannel) {
> > -      case 'dpm': // use document.postMessage
> > -        var targetDoc = targetId === '..' ? parent.document :
> > -                                            frames[targetId].document;
> > -        targetDoc.postMessage(rpcData);
> > -        break;
> > -      case 'wpm': // use window.postMessage
> > -        var targetWin = targetId === '..' ? parent : frames[targetId];
> > -        targetWin.postMessage(rpcData, "*");
> > -        break;
> > -      default: // use 'ifpc' as a fallback mechanism
> > -        var relay = gadgets.rpc.getRelayUrl(targetId);
> > -        // TODO split message if too long
> > -        var src;
> > -        if (useLegacyProtocol[targetId]) {
> > -          // #iframe_id&callId&num_packets&packet_num&block_of_data
> > -          src = [relay, '#', encodeLegacyData([from, callId, 1, 0,
> > -                 encodeLegacyData([from, serviceName, '', '',
> > from].concat(
> > -                 Array.prototype.slice.call(arguments, 3)))])].join('');
> > -        } else {
> > -          // Try the frameElement channel if available
> > -          try {
> > -            if (from === '..') {
> > -              // Container-to-gadget
> > -              var iframe = document.getElementById(targetId);
> > -              if (typeof iframe.__g2c_rpc.__c2g_rpc === 'function') {
> > -                iframe.__g2c_rpc.__c2g_rpc(rpcData);
> > -                return;
> > -              }
> > -            } else {
> > -              // Gadget-to-container
> > -              if (typeof window.frameElement.__g2c_rpc === 'function') {
> > -                window.frameElement.__g2c_rpc(rpcData);
> > -                return;
> > -              }
> > -            }
> > -          } catch (e) {
> > -          }
> > -          // # targetId & sourceId@callId & packetNum & packetId &
> > packetData
> > -          src = [relay, '#', targetId, '&', from, '@', callId,
> > -                 '&1&0&', encodeURIComponent(rpcData)].join('');
> > -        }
> > -        emitInvisibleIframe(src);
> > +      switch (channelType) {
> > +        case 'dpm': // use document.postMessage.
> > +          var targetDoc = targetId === '..' ? parent.document :
> > +                                              frames[targetId].document;
> > +          targetDoc.postMessage(rpcData);
> > +          break;
> > +
> > +        case 'wpm': // use window.postMessage.
> > +          var targetWin = targetId === '..' ? parent : frames[targetId];
> > +          targetWin.postMessage(rpcData, "*");
> > +          break;
> > +
> > +        case 'nix': // use NIX.
> > +          callNIX(targetId, from, rpcData);
> > +          break;
> > +
> > +        case 'fe': // use FrameElement.
> > +          callFrameElement(targetId, from, rpcData);
> > +          break;
> > +
> > +        default: // use 'ifpc' as a fallback mechanism.
> > +          callIFPC(targetId, from, rpcData);
> > +          break;
> >       }
> >     },
> >
> > @@ -372,15 +772,13 @@
> >      */
> >     setAuthToken: function(targetId, token) {
> >       authToken[targetId] = token;
> > +      setupFrame(targetId);
> >     },
> >
> >     /**
> >      * Gets the RPC relay mechanism.
> > -     * @return {String} RPC relay mechanism. Supported types:
> > -     *                  'wpm' - Use window.postMessage (defined by
> HTML5)
> > -     *                  'dpm' - Use document.postMessage (defined by an
> > early
> > -     *                          draft of HTML5 and implemented by Opera)
> > -     *                  'ifpc' - Use invisible IFrames
> > +     * @return {String} RPC relay mechanism. See above for
> > +     *   a list of supported types.
> >      *
> >      * @member gadgets.rpc
> >      */
> >
> > Modified:
> >
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > URL:
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java?rev=672393&r1=672392&r2=672393&view=diff
> >
> >
> ==============================================================================
> > ---
> >
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > (original)
> > +++
> >
> incubator/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/servlet/GadgetRenderingTask.java
> > Fri Jun 27 14:01:20 2008
> > @@ -221,7 +221,8 @@
> >           "a {color:#0000cc;}a:visited {color:#551a8b;}" +
> >           "a:active {color:#ff0000;}" +
> >           "body{margin: 0px;padding: 0px;background-color:white;}" +
> > -          "</style></head>";
> > +          "</style><base>" + gadget.getSpec().getUrl().toString() +
> > +          "</base></head>";
> >     markup.append(boilerPlate);
> >     LocaleSpec localeSpec = gadget.getSpec().getModulePrefs().getLocale(
> >         gadget.getContext().getLocale());
> >
> > Added:
> incubator/shindig/trunk/javascript/container/rpctest_container.html
> > URL:
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_container.html?rev=672393&view=auto
> >
> >
> ==============================================================================
> > --- incubator/shindig/trunk/javascript/container/rpctest_container.html
> > (added)
> > +++ incubator/shindig/trunk/javascript/container/rpctest_container.html
> Fri
> > Jun 27 14:01:20 2008
> > @@ -0,0 +1,90 @@
> > +<!--
> > +  Simple page for testing gadgets.rpc performance.
> > +  Allows you to add a simulated "gadget" (in this case just a static
> > +  HTML page which loads gadgets.rpc also), and pass some
> > +  specified number of random messages of specified size to
> > +  and from it.
> > +
> > +  A simple recommended setup is to start up two instances of
> > +  the Shindig Gadgets Server on two separate ports to test
> > +  "real" cross-domain communication, since port is factored
> > +  into the same-domain policy enforced by browsers.
> > +
> > +  If your servers are on localhost:8080 and localhost:8081, then hit:
> > +  http://localhost:8080/gadgets/files/container/rpctest_container.html?
> \
> > +  http://localhost:8081/gadgets/files/container/rpctest_gadget.html& \
> > +
> >
> http://localhost:8081/gadgets/files/container/rpc_relay.uncompressed.html
> > +
> > +  (Note the backslashes should be removed, as they exist for formatting
> > only.)
> > +
> > +  There are two arguments, separated by an ampersand:
> > +  1. URL of the "gadget" test page.
> > +  2. URL of the "gadget" test page's RPC relay (for browsers requiring
> > it).
> > +-->
> > +<html>
> > +  <head>
> > +    <title>gadgets.rpc Performance Tests: Container</title>
> > +    <script src="../../js/rpc.js?c=1&debug=1"></script>
> > +    <script src="rpctest_perf.js"></script>
> > +    <script>
> > +      function initTest() {
> > +        var container = document.getElementById("container");
> > +
> > +        // query string is assumed to be the "gadget" URL as arg 1,
> > +        // and optionally the relay URL as arg 2
> > +        var pageArgs = window.location.search.substring(1).split('&');
> > +        var gadgetUrl = pageArgs[0];
> > +        var secret = Math.round(Math.random()*10000000);
> > +        if (pageArgs[1]) {
> > +          gadgets.rpc.setRelayUrl('gadget', pageArgs[1]);
> > +        }
> > +        var containerRelay = pageArgs[2] || '';
> > +        container.innerHTML = "<iframe id='gadget' name='gadget'
> > height=300 width=300 src='" + gadgetUrl + "?parent=" + containerRelay +
> > "#rpctoken=" + secret + "'></iframe>";
> > +        gadgets.rpc.setAuthToken('gadget', secret);
> > +
> > +        initPerfTest();
> > +      };
> > +    </script>
> > +  </head>
> > +  <body onload="initTest();">
> > +    <div>gadgets.rpc Performance: Container Page</div><hr/>
> > +    <div>Test<br/>
> > +      <ul>
> > +        <li>Number of messages to send:
> > +          <select name="num_msgs" id="num_msgs">
> > +            <option value="1">1</option>
> > +            <option value="10">10</option>
> > +            <option value="100" selected>100</option>
> > +            <option value="1000">1000</option>
> > +          </select>
> > +        </li>
> > +        <li>Message size:
> > +          <select name="msg_size" id="msg_size">
> > +            <option value="10">10 B</option>
> > +            <option value="100">100 B</option>
> > +            <option value="1024" selected>1 kB</option>
> > +            <option value="10240">10 kB</option>
> > +            <option value="102400">100 kB</option>
> > +            <option value="1048576">1 MB</option>
> > +          </select>
> > +        </li>
> > +        <li>
> > +          <input type="button" value="Start The Test!"
> > onclick="runPerfTest('gadget');" />
> > +        </li>
> > +      </ul>
> > +    </div>
> > +    <div id="test_running" style="display:none;">
> > +      Running test...
> > +    </div>
> > +    <div id="results" style="display:none;">
> > +      Results: Gadget-to-Container<br/>
> > +      Messages: <span id="results_num_received"></span>, Bytes: <span
> > id="results_bytes_received"></span> <span id="in_or_out"></span><br/>
> > +      Time elapsed for test run: <span
> id="results_time_used"></span><br/>
> > +      Messages/second: <span id="results_msgs_per_sec"></span><br/>
> > +      Bytes/second: <span id="results_bytes_per_sec"></span>
> > +    </div>
> > +    <hr/>
> > +    <div>Gadget:</div>
> > +    <div id="container"></div>
> > +  </body>
> > +</html>
> >
> > Added: incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > URL:
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_gadget.html?rev=672393&view=auto
> >
> >
> ==============================================================================
> > --- incubator/shindig/trunk/javascript/container/rpctest_gadget.html
> > (added)
> > +++ incubator/shindig/trunk/javascript/container/rpctest_gadget.html Fri
> > Jun 27 14:01:20 2008
> > @@ -0,0 +1,49 @@
> > +<html>
> > +  <head>
> > +    <title>gadgets.rpc Performance Tests: Gadget</title>
> > +    <script src="../../js/rpc.js?c=1&debug=1"></script>
> > +    <script src="rpctest_perf.js"></script>
> > +    <script>
> > +      var containerRelay = window.location.search.substring(1);
> > +      gadgets.rpc.setRelayUrl(null, containerRelay);
> > +    </script>
> > +  </head>
> > +  <body onload="initPerfTest();">
> > +    <div>gadgets.rpc Performance: "Gadget" page</div><hr/>
> > +    <div>Test<br/>
> > +      <ul>
> > +        <li>Number of messages to send:
> > +          <select name="num_msgs" id="num_msgs">
> > +            <option value="1">1</option>
> > +            <option value="10">10</option>
> > +            <option value="100" selected>100</option>
> > +            <option value="1000">1000</option>
> > +          </select>
> > +        </li>
> > +        <li>Message size:
> > +          <select name="msg_size" id="msg_size">
> > +            <option value="10">10 B</option>
> > +            <option value="100">100 B</option>
> > +            <option value="1024" selected>1 kB</option>
> > +            <option value="10240">10 kB</option>
> > +            <option value="102400">100 kB</option>
> > +            <option value="1048576">1 MB</option>
> > +          </select>
> > +        </li>
> > +        <li>
> > +          <input type="button" value="Start The Test!"
> > onclick="runPerfTest();" />
> > +        </li>
> > +      </ul>
> > +    </div>
> > +    <div id="test_running" style="display:none;">
> > +      Running test...
> > +    </div>
> > +    <div id="results" style="display:none;">
> > +      Results: Gadget-to-Container<br/>
> > +      Messages: <span id="results_num_received"></span>, Bytes: <span
> > id="results_bytes_received"></span> <span id="in_or_out"></span><br/>
> > +      Time elapsed for test run: <span
> id="results_time_used"></span><br/>
> > +      Messages/second: <span id="results_msgs_per_sec"></span><br/>
> > +      Bytes/second: <span id="results_bytes_per_sec"></span>
> > +    </div>
> > +  </body>
> > +</html>
> >
> > Added: incubator/shindig/trunk/javascript/container/rpctest_perf.js
> > URL:
> >
> http://svn.apache.org/viewvc/incubator/shindig/trunk/javascript/container/rpctest_perf.js?rev=672393&view=auto
> >
> >
> ==============================================================================
> > --- incubator/shindig/trunk/javascript/container/rpctest_perf.js (added)
> > +++ incubator/shindig/trunk/javascript/container/rpctest_perf.js Fri Jun
> 27
> > 14:01:20 2008
> > @@ -0,0 +1,93 @@
> > +var perfStats = null;
> > +var currentRun = {};
> > +
> > +function perfService(message) {
> > +  if (perfStats.numResults++ === 0) {
> > +    perfStats.firstMsg = message; // stored since it has "real" start
> time
> > +  }
> > +  perfStats.bytesReceived += message.length;
> > +};
> > +
> > +function clearPerfStats(inOrOut) {
> > +  perfStats = {
> > +    numResults: 0,
> > +    bytesReceived: 0,
> > +    firstMsg: null
> > +  };
> > +
> > +  document.getElementById("in_or_out").innerHTML = inOrOut;
> > +
> > +  // hide results fields
> > +  document.getElementById("results").style.display = "none";
> > +};
> > +
> > +function completePerfStats() {
> > +  perfStats.timeEnded = new Date().getTime();
> > +
> > +  // get time started from the first sent message
> > +  perfStats.timeStarted = perfStats.firstMsg.substr(0,
> > perfStats.firstMsg.indexOf(':'));
> > +
> > +  var timeUsedMs = perfStats.timeEnded - perfStats.timeStarted;
> > +
> > +  // fill in fields
> > +  document.getElementById("results_num_received").innerHTML =
> > perfStats.numResults;
> > +  document.getElementById("results_bytes_received").innerHTML =
> > perfStats.bytesReceived;
> > +  document.getElementById("results_time_used").innerHTML = timeUsedMs +
> > "ms";
> > +  document.getElementById("results_msgs_per_sec").innerHTML =
> > (perfStats.numResults / (timeUsedMs / 1000));
> > +  document.getElementById("results_bytes_per_sec").innerHTML =
> > (perfStats.bytesReceived / (timeUsedMs / 1000));
> > +  document.getElementById("test_running").style.display = "none";
> > +  document.getElementById("results").style.display = "";
> > +};
> > +
> > +function initPerfTest() {
> > +  clearPerfStats();
> > +  gadgets.rpc.register("perf_service", perfService);
> > +  gadgets.rpc.register("clear_perf_stats", clearPerfStats);
> > +  gadgets.rpc.register("complete_perf_stats", completePerfStats);
> > +};
> > +
> > +var alphabet =
> > "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
> _-*&(){}'";
> > +
> > +function sendPerfMessage() {
> > +  var msgToSend = currentRun.msg;
> > +  if (currentRun.curMsgId++ <= 1) {
> > +    var nowString = new Date().getTime() + ':';
> > +    msgToSend = nowString + currentRun.msg.substring(nowString.length);
> > +  }
> > +
> > +  gadgets.rpc.call(currentRun.targetId, "perf_service", null,
> msgToSend);
> > +  if (currentRun.curMsgId < currentRun.endMsgId) {
> > +    // loop, giving up execution in case rpc technique demands it
> > +    window.setTimeout(sendPerfMessage, 0);
> > +  } else {
> > +    // send finisher
> > +    window.setTimeout(function() { gadgets.rpc.call(currentRun.targetId,
> > "complete_perf_stats", null); }, 0);
> > +  }
> > +};
> > +
> > +function runPerfTest(targetId) {
> > +  document.getElementById("test_running").style.display = "";
> > +
> > +  // initialize the current run
> > +  var num_msgs = document.getElementById("num_msgs").value;
> > +  var msg_size = document.getElementById("msg_size").value;
> > +
> > +  currentRun.targetId = targetId;
> > +  currentRun.curMsgId = 0;
> > +  currentRun.endMsgId = num_msgs;
> > +
> > +  var msg = [];
> > +  for (var i = 0; i < msg_size; ++i) {
> > +    msg[i] = alphabet.charAt(Math.round(Math.random(alphabet.length)));
> > +  }
> > +  currentRun.msg = msg.join('');
> > +
> > +  // clear local perf stats
> > +  clearPerfStats("(outbound)");
> > +
> > +  // clear target perf stats
> > +  gadgets.rpc.call(targetId, "clear_perf_stats", null, "(inbound)");
> > +
> > +  // kick off the send loop
> > +  sendPerfMessage();
> > +};
> >
> >
> >
>