Modified: shindig/branches/2.0.x/features/src/main/javascript/features/rpc/nix.transport.js URL: http://svn.apache.org/viewvc/shindig/branches/2.0.x/features/src/main/javascript/features/rpc/nix.transport.js?rev=1005506&r1=1005505&r2=1005506&view=diff ============================================================================== --- shindig/branches/2.0.x/features/src/main/javascript/features/rpc/nix.transport.js (original) +++ shindig/branches/2.0.x/features/src/main/javascript/features/rpc/nix.transport.js Thu Oct 7 15:54:09 2010 @@ -64,222 +64,222 @@ gadgets.rpctx = gadgets.rpctx || {}; */ if (!gadgets.rpctx.nix) { // make lib resilient to double-inclusion -gadgets.rpctx.nix = function() { - // 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 MAX_NIX_SEARCHES = 10; - var NIX_SEARCH_PERIOD = 500; - - // 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 isForceSecure = {}; - - // Store the ready signal method for use on handshake complete. - var ready; - var numHandlerSearches = 0; - - // Search for NIX handler to parent. Tries MAX_NIX_SEARCHES times every - // NIX_SEARCH_PERIOD milliseconds. - function conductHandlerSearch() { - // Call from gadget to the container. - var handler = nix_channels['..']; - if (handler) { - return; - } - - if (++numHandlerSearches > MAX_NIX_SEARCHES) { - // Handshake failed. Will fall back. - gadgets.warn('Nix transport setup failed, falling back...'); - ready('..', false); - return; - } - - // 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.GetAuthToken) 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. - // This is just a sanity check, not a validity check. - if (!handler && window.opener && "GetAuthToken" in window.opener) { - handler = window.opener; - - // Create the channel to the parent/container. - // First verify that it knows our auth token to ensure it's not - // an impostor. - if (handler.GetAuthToken() == gadgets.rpc.getAuthToken('..')) { - // Auth match - pass it back along with our wrapper to finish. - // own wrapper and our authentication token for co-verification. - var token = gadgets.rpc.getAuthToken('..'); - handler.CreateChannel(window[NIX_GET_WRAPPER]('..', token), - token); - // Set channel handler - nix_channels['..'] = handler; - window.opener = null; - - // Signal success and readiness to send to parent. - // Container-to-gadget bit flipped in CreateChannel. - ready('..', true); + gadgets.rpctx.nix = function() { + // 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 MAX_NIX_SEARCHES = 10; + var NIX_SEARCH_PERIOD = 500; + + // 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 isForceSecure = {}; + + // Store the ready signal method for use on handshake complete. + var ready; + var numHandlerSearches = 0; + + // Search for NIX handler to parent. Tries MAX_NIX_SEARCHES times every + // NIX_SEARCH_PERIOD milliseconds. + function conductHandlerSearch() { + // Call from gadget to the container. + var handler = nix_channels['..']; + if (handler) { return; } - } - // Try again. - window.setTimeout(function() { conductHandlerSearch(); }, - NIX_SEARCH_PERIOD); - } - - // Returns current window location, without hash values - function getLocationNoHash() { - var loc = window.location.href; - var idx = loc.indexOf('#'); - if (idx == -1) { - return loc; - } - return loc.substring(0, idx); - } + if (++numHandlerSearches > MAX_NIX_SEARCHES) { + // Handshake failed. Will fall back. + gadgets.warn('Nix transport setup failed, falling back...'); + ready('..', false); + return; + } - // When "forcesecure" is set to true, use the relay file and a simple variant of IFPC to first - // authenticate the container and gadget with each other. Once that is done, then initialize - // the NIX protocol. - function setupSecureRelayToParent(rpctoken) { - // To the parent, transmit the child's URL, the passed in auth - // token, and another token generated by the child. - var childToken = (0x7FFFFFFF * Math.random()) | 0; // TODO expose way to have child set this value - var data = [ - getLocationNoHash(), - childToken - ]; - gadgets.rpc._createRelayIframe(rpctoken, data); - - // listen for response from parent - var hash = window.location.href.split('#')[1] || ''; - - function relayTimer() { - var newHash = window.location.href.split('#')[1] || ''; - if (newHash !== hash) { - clearInterval(relayTimerId); - var params = gadgets.util.getUrlParameters(window.location.href); - if (params.childtoken == childToken) { - // parent has been authenticated; now init NIX - conductHandlerSearch(); + // 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.GetAuthToken) 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. + // This is just a sanity check, not a validity check. + if (!handler && window.opener && 'GetAuthToken' in window.opener) { + handler = window.opener; + + // Create the channel to the parent/container. + // First verify that it knows our auth token to ensure it's not + // an impostor. + if (handler.GetAuthToken() == gadgets.rpc.getAuthToken('..')) { + // Auth match - pass it back along with our wrapper to finish. + // own wrapper and our authentication token for co-verification. + var token = gadgets.rpc.getAuthToken('..'); + handler.CreateChannel(window[NIX_GET_WRAPPER]('..', token), + token); + // Set channel handler + nix_channels['..'] = handler; + window.opener = null; + + // Signal success and readiness to send to parent. + // Container-to-gadget bit flipped in CreateChannel. + ready('..', true); return; } - // security error -- token didn't match - ready('..', false); } + + // Try again. + window.setTimeout(function() { conductHandlerSearch(); }, + NIX_SEARCH_PERIOD); } - var relayTimerId = setInterval( relayTimer, 100 ); - } - return { - getCode: function() { - return 'nix'; - }, - - isParentVerifiable: function(opt_receiverId) { - // NIX is only parent verifiable if a receiver was setup with "forcesecure" set to TRUE. - if (opt_receiverId) { - return isForceSecure[opt_receiverId]; + // Returns current window location, without hash values + function getLocationNoHash() { + var loc = window.location.href; + var idx = loc.indexOf('#'); + if (idx == -1) { + return loc; } - return false; - }, - - init: function(processFn, readyFn) { - ready = readyFn; + return loc.substring(0, idx); + } - // Ensure VBScript wrapper code is in the page and that the - // global Javascript handlers have been set. - // 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) { - window.setTimeout( - function() { processFn(gadgets.json.parse(data)); }, 0); - }; - - window[NIX_CREATE_CHANNEL] = function(name, channel, token) { - // Verify the authentication token of the gadget trying - // to create a channel for us. - if (gadgets.rpc.getAuthToken(name) === token) { - nix_channels[name] = channel; - ready(name, true); + // When "forcesecure" is set to true, use the relay file and a simple variant of IFPC to first + // authenticate the container and gadget with each other. Once that is done, then initialize + // the NIX protocol. + function setupSecureRelayToParent(rpctoken) { + // To the parent, transmit the child's URL, the passed in auth + // token, and another token generated by the child. + var childToken = (0x7FFFFFFF * Math.random()) | 0; // TODO expose way to have child set this value + var data = [ + getLocationNoHash(), + childToken + ]; + gadgets.rpc._createRelayIframe(rpctoken, data); + + // listen for response from parent + var hash = window.location.href.split('#')[1] || ''; + + function relayTimer() { + var newHash = window.location.href.split('#')[1] || ''; + if (newHash !== hash) { + clearInterval(relayTimerId); + var params = gadgets.util.getUrlParameters(window.location.href); + if (params.childtoken == childToken) { + // parent has been authenticated; now init NIX + conductHandlerSearch(); + return; } - }; + // security error -- token didn't match + ready('..', false); + } + } + var relayTimerId = setInterval(relayTimer, 100); + } + + return { + getCode: function() { + return 'nix'; + }, + + isParentVerifiable: function(opt_receiverId) { + // NIX is only parent verifiable if a receiver was setup with "forcesecure" set to TRUE. + if (opt_receiverId) { + return isForceSecure[opt_receiverId]; + } + return false; + }, - // Inject the VBScript code needed. - var 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 "..") + init: function(processFn, readyFn) { + ready = readyFn; + + // Ensure VBScript wrapper code is in the page and that the + // global Javascript handlers have been set. + // 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) { + window.setTimeout( + function() { processFn(gadgets.json.parse(data)); }, 0); + }; + + window[NIX_CREATE_CHANNEL] = function(name, channel, token) { + // Verify the authentication token of the gadget trying + // to create a channel for us. + if (gadgets.rpc.getAuthToken(name) === token) { + nix_channels[name] = channel; + ready(name, true); + } + }; + + // Inject the VBScript code needed. + var 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' - // Stores the auth token used to communicate with - // the gadget. The GetChannelCreator method returns - // an object that returns this auth token. Upon matching - // that with its own, the gadget uses the object - // to actually establish the communication channel. + // Stores the auth token used to communicate with + // the gadget. The GetChannelCreator method returns + // an object that returns this auth token. Upon matching + // that with its own, the gadget uses the object + // to actually establish the communication channel. + 'Private m_Auth\n' - // Method for internally setting the value - // of the m_Intended property. + // Method for internally setting the value + // of the m_Intended property. + 'Public Sub SetIntendedName(name)\n ' + 'If isEmpty(m_Intended) Then\n' + 'm_Intended = name\n' + 'End If\n' + 'End Sub\n' - // Method for internally setting the value of the m_Auth property. + // Method for internally setting the value of the m_Auth property. + 'Public Sub SetAuth(auth)\n ' + 'If isEmpty(m_Auth) Then\n' + 'm_Auth = auth\n' + 'End If\n' + 'End Sub\n' - // A wrapper method which actually causes a - // message to be sent to the other context. + // 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' - // Returns the auth token to the gadget, so it can - // confirm a match before initiating the connection + // Returns the auth token to the gadget, so it can + // confirm a match before initiating the connection + 'Public Function GetAuthToken()\n ' + 'GetAuthToken = m_Auth\n' + 'End Function\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. + // 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' + 'End Class\n' - // Function to get a reference to the wrapper. + // Function to get a reference to the wrapper. + 'Function ' + NIX_GET_WRAPPER + '(name, auth)\n' + 'Dim wrap\n' + 'Set wrap = New ' + NIX_WRAPPER + '\n' @@ -288,55 +288,55 @@ gadgets.rpctx.nix = function() { + 'Set ' + NIX_GET_WRAPPER + ' = wrap\n' + 'End Function'; + try { + window.execScript(vbscript, 'vbscript'); + } catch (e) { + return false; + } + } + return true; + }, + + setup: function(receiverId, token, forcesecure) { + isForceSecure[receiverId] = !!forcesecure; + if (receiverId === '..') { + if (forcesecure) { + setupSecureRelayToParent(token); + } else { + conductHandlerSearch(); + } + return true; + } try { - window.execScript(vbscript, 'vbscript'); + var frame = document.getElementById(receiverId); + var wrapper = window[NIX_GET_WRAPPER](receiverId, token); + frame.contentWindow.opener = wrapper; } catch (e) { return false; } - } - return true; - }, + return true; + }, - setup: function(receiverId, token, forcesecure) { - isForceSecure[receiverId] = !!forcesecure; - if (receiverId === '..') { - if (forcesecure) { - setupSecureRelayToParent(token); - } else { - conductHandlerSearch(); + call: function(targetId, from, rpc) { + try { + // If we have a handler, call it. + if (nix_channels[targetId]) { + nix_channels[targetId].SendMessage(gadgets.json.stringify(rpc)); + } + } catch (e) { + return false; } return true; - } - try { - var frame = document.getElementById(receiverId); - var wrapper = window[NIX_GET_WRAPPER](receiverId, token); - frame.contentWindow.opener = wrapper; - } catch (e) { - return false; - } - return true; - }, + }, - call: function(targetId, from, rpc) { - try { - // If we have a handler, call it. - if (nix_channels[targetId]) { - nix_channels[targetId].SendMessage(gadgets.json.stringify(rpc)); - } - } catch (e) { - return false; + // data = [child URL, child auth token] + relayOnload: function(receiverId, data) { + // transmit childtoken back to child to complete authentication + var src = data[0] + '#childtoken=' + data[1]; + var childIframe = document.getElementById(receiverId); + childIframe.src = src; } - return true; - }, - - // data = [child URL, child auth token] - relayOnload: function(receiverId, data) { - // transmit childtoken back to child to complete authentication - var src = data[0] + '#childtoken=' + data[1]; - var childIframe = document.getElementById(receiverId); - childIframe.src = src; - } - }; -}(); + }; + }(); } // !end of double-inclusion guard
Modified: shindig/branches/2.0.x/features/src/main/javascript/features/rpc/rmr.transport.js URL: http://svn.apache.org/viewvc/shindig/branches/2.0.x/features/src/main/javascript/features/rpc/rmr.transport.js?rev=1005506&r1=1005505&r2=1005506&view=diff ============================================================================== --- shindig/branches/2.0.x/features/src/main/javascript/features/rpc/rmr.transport.js (original) +++ shindig/branches/2.0.x/features/src/main/javascript/features/rpc/rmr.transport.js Thu Oct 7 15:54:09 2010 @@ -42,22 +42,22 @@ gadgets.rpctx = gadgets.rpctx || {}; */ if (!gadgets.rpctx.rmr) { // make lib resilient to double-inclusion -gadgets.rpctx.rmr = function() { - // Consts for RMR, including time in ms RMR uses to poll for - // its relay frame to be created, and the max # of polls it does. - var RMR_SEARCH_TIMEOUT = 500; - var RMR_MAX_POLLS = 10; - - // JavaScript references to the channel objects used by RMR. - // Gadgets will have but a single channel under - // rmr_channels['..'] while containers will have a channel - // per gadget stored under the gadget's ID. - var rmr_channels = {}; - - var process; - var ready; + gadgets.rpctx.rmr = function() { + // Consts for RMR, including time in ms RMR uses to poll for + // its relay frame to be created, and the max # of polls it does. + var RMR_SEARCH_TIMEOUT = 500; + var RMR_MAX_POLLS = 10; + + // JavaScript references to the channel objects used by RMR. + // Gadgets will have but a single channel under + // rmr_channels['..'] while containers will have a channel + // per gadget stored under the gadget's ID. + var rmr_channels = {}; - /** + var process; + var ready; + + /** * Append an RMR relay frame to the document. This allows the receiver * to start receiving messages. * @@ -66,122 +66,122 @@ gadgets.rpctx.rmr = function() { * @param {string} data to pass along to the frame. * @param {string=} opt_frameId ID of frame for which relay is being appended (optional). */ - function appendRmrFrame(channelFrame, relayUri, data, opt_frameId) { - var appendFn = function() { - // Append the iframe. - document.body.appendChild(channelFrame); - - // Set the src of the iframe to 'about:blank' first and then set it - // to the relay URI. This prevents the iframe from maintaining a src - // to the 'old' relay URI if the page is returned to from another. - // In other words, this fixes the bfcache issue that causes the iframe's - // src property to not be updated despite us assigning it a new value here. - channelFrame.src = 'about:blank'; - if (opt_frameId) { - // Process the initial sent payload (typically sent by container to - // child/gadget) only when the relay frame has finished loading. We - // do this to ensure that, in processRmrData(...), the ACK sent due - // to processing can actually be sent. Before this time, the frame's - // contentWindow is null, making it impossible to do so. - channelFrame.onload = function() { - processRmrData(opt_frameId); - }; - } - channelFrame.src = relayUri + '#' + data; - }; + function appendRmrFrame(channelFrame, relayUri, data, opt_frameId) { + var appendFn = function() { + // Append the iframe. + document.body.appendChild(channelFrame); + + // Set the src of the iframe to 'about:blank' first and then set it + // to the relay URI. This prevents the iframe from maintaining a src + // to the 'old' relay URI if the page is returned to from another. + // In other words, this fixes the bfcache issue that causes the iframe's + // src property to not be updated despite us assigning it a new value here. + channelFrame.src = 'about:blank'; + if (opt_frameId) { + // Process the initial sent payload (typically sent by container to + // child/gadget) only when the relay frame has finished loading. We + // do this to ensure that, in processRmrData(...), the ACK sent due + // to processing can actually be sent. Before this time, the frame's + // contentWindow is null, making it impossible to do so. + channelFrame.onload = function() { + processRmrData(opt_frameId); + }; + } + channelFrame.src = relayUri + '#' + data; + }; - if (document.body) { - appendFn(); - } else { - // Common gadget case: attaching header during in-gadget handshake, - // when we may still be in script in head. Attach onload. - gadgets.util.registerOnLoadHandler(function() { appendFn(); }); + if (document.body) { + appendFn(); + } else { + // Common gadget case: attaching header during in-gadget handshake, + // when we may still be in script in head. Attach onload. + gadgets.util.registerOnLoadHandler(function() { appendFn(); }); + } } - } - /** + /** * Sets up the RMR transport frame for the given frameId. For gadgets * calling containers, the frameId should be '..'. * * @param {string} frameId The ID of the frame. */ - function setupRmr(frameId) { - if (typeof rmr_channels[frameId] === "object") { - // Sanity check. Already done. - return; - } - - var channelFrame = document.createElement('iframe'); - var frameStyle = channelFrame.style; - frameStyle.position = 'absolute'; - frameStyle.top = '0px'; - frameStyle.border = '0'; - frameStyle.opacity = '0'; - - // The width here is important as RMR - // makes use of the resize handler for the frame. - // Do not modify unless you test thoroughly! - frameStyle.width = '10px'; - frameStyle.height = '1px'; - channelFrame.id = 'rmrtransport-' + frameId; - channelFrame.name = channelFrame.id; - - // Use the explicitly set relay, if one exists. Otherwise, - // Construct one using the parent parameter plus robots.txt - // as a synthetic relay. This works since browsers using RMR - // treat 404s as legitimate for the purposes of cross domain - // communication. - var relayUri = gadgets.rpc.getRelayUrl(frameId); - if (!relayUri) { - relayUri = - gadgets.rpc.getOrigin(gadgets.util.getUrlParameters()["parent"]) + - '/robots.txt'; - } + function setupRmr(frameId) { + if (typeof rmr_channels[frameId] === 'object') { + // Sanity check. Already done. + return; + } - rmr_channels[frameId] = { - frame: channelFrame, - receiveWindow: null, - relayUri: relayUri, - searchCounter : 0, - width: 10, - - // Waiting means "waiting for acknowledgement to be received." - // Acknowledgement always comes as a special ACK - // message having been received. This message is received - // during handshake in different ways by the container and - // gadget, and by normal RMR message passing once the handshake - // is complete. - waiting: true, - queue: [], - - // Number of non-ACK messages that have been sent to the recipient - // and have been acknowledged. - sendId: 0, - - // Number of messages received and processed from the sender. - // This is the number that accompanies every ACK to tell the - // sender to clear its queue. - recvId: 0 - }; + var channelFrame = document.createElement('iframe'); + var frameStyle = channelFrame.style; + frameStyle.position = 'absolute'; + frameStyle.top = '0px'; + frameStyle.border = '0'; + frameStyle.opacity = '0'; + + // The width here is important as RMR + // makes use of the resize handler for the frame. + // Do not modify unless you test thoroughly! + frameStyle.width = '10px'; + frameStyle.height = '1px'; + channelFrame.id = 'rmrtransport-' + frameId; + channelFrame.name = channelFrame.id; + + // Use the explicitly set relay, if one exists. Otherwise, + // Construct one using the parent parameter plus robots.txt + // as a synthetic relay. This works since browsers using RMR + // treat 404s as legitimate for the purposes of cross domain + // communication. + var relayUri = gadgets.rpc.getRelayUrl(frameId); + if (!relayUri) { + relayUri = + gadgets.rpc.getOrigin(gadgets.util.getUrlParameters()['parent']) + + '/robots.txt'; + } + + rmr_channels[frameId] = { + frame: channelFrame, + receiveWindow: null, + relayUri: relayUri, + searchCounter: 0, + width: 10, + + // Waiting means "waiting for acknowledgement to be received." + // Acknowledgement always comes as a special ACK + // message having been received. This message is received + // during handshake in different ways by the container and + // gadget, and by normal RMR message passing once the handshake + // is complete. + waiting: true, + queue: [], + + // Number of non-ACK messages that have been sent to the recipient + // and have been acknowledged. + sendId: 0, + + // Number of messages received and processed from the sender. + // This is the number that accompanies every ACK to tell the + // sender to clear its queue. + recvId: 0 + }; + + if (frameId !== '..') { + // Container always appends a relay to the gadget, before + // the gadget appends its own relay back to container. The + // gadget, in the meantime, refuses to attach the container + // relay until it finds this one. Thus, the container knows + // for certain that gadget to container communication is set + // up by the time it finds its own relay. In addition to + // establishing a reliable handshake protocol, this also + // makes it possible for the gadget to send an initial batch + // of messages to the container ASAP. + appendRmrFrame(channelFrame, relayUri, getRmrData(frameId)); + } - if (frameId !== '..') { - // Container always appends a relay to the gadget, before - // the gadget appends its own relay back to container. The - // gadget, in the meantime, refuses to attach the container - // relay until it finds this one. Thus, the container knows - // for certain that gadget to container communication is set - // up by the time it finds its own relay. In addition to - // establishing a reliable handshake protocol, this also - // makes it possible for the gadget to send an initial batch - // of messages to the container ASAP. - appendRmrFrame(channelFrame, relayUri, getRmrData(frameId)); + // Start searching for our own frame on the other page. + conductRmrSearch(frameId); } - - // Start searching for our own frame on the other page. - conductRmrSearch(frameId); - } - /** + /** * Searches for a relay frame, created by the sender referenced by * frameId, with which this context receives messages. Once * found with proper permissions, attaches a resize handler which @@ -189,51 +189,51 @@ gadgets.rpctx.rmr = function() { * * @param {string} frameId Frame ID of the prospective sender. */ - function conductRmrSearch(frameId) { - var channelWindow = null; - - // Increment the search counter. - rmr_channels[frameId].searchCounter++; - - try { - var targetWin = gadgets.rpc._getTargetWin(frameId); - if (frameId === '..') { - // We are a gadget. - channelWindow = targetWin.frames['rmrtransport-' + gadgets.rpc.RPC_ID]; - } else { - // We are a container. - channelWindow = targetWin.frames['rmrtransport-..']; - } - } catch (e) { - // Just in case; may happen when relay is set to about:blank or unset. - // Catching exceptions here ensures that the timeout to continue the - // search below continues to work. - } + function conductRmrSearch(frameId) { + var channelWindow = null; - var status = false; + // Increment the search counter. + rmr_channels[frameId].searchCounter++; - if (channelWindow) { - // We have a valid reference to "our" RMR transport frame. - // Register the proper event handlers. - status = registerRmrChannel(frameId, channelWindow); - } + try { + var targetWin = gadgets.rpc._getTargetWin(frameId); + if (frameId === '..') { + // We are a gadget. + channelWindow = targetWin.frames['rmrtransport-' + gadgets.rpc.RPC_ID]; + } else { + // We are a container. + channelWindow = targetWin.frames['rmrtransport-..']; + } + } catch (e) { + // Just in case; may happen when relay is set to about:blank or unset. + // Catching exceptions here ensures that the timeout to continue the + // search below continues to work. + } + + var status = false; + + if (channelWindow) { + // We have a valid reference to "our" RMR transport frame. + // Register the proper event handlers. + status = registerRmrChannel(frameId, channelWindow); + } + + if (!status) { + // Not found yet. Continue searching, but only if the counter + // has not reached the threshold. + if (rmr_channels[frameId].searchCounter > RMR_MAX_POLLS) { + // If we reach this point, then RMR has failed and we + // fall back to IFPC. + return; + } - if (!status) { - // Not found yet. Continue searching, but only if the counter - // has not reached the threshold. - if (rmr_channels[frameId].searchCounter > RMR_MAX_POLLS) { - // If we reach this point, then RMR has failed and we - // fall back to IFPC. - return; + window.setTimeout(function() { + conductRmrSearch(frameId); + }, RMR_SEARCH_TIMEOUT); } - - window.setTimeout(function() { - conductRmrSearch(frameId); - }, RMR_SEARCH_TIMEOUT); } - } - /** + /** * Attempts to conduct an RPC call to the specified * target with the specified data via the RMR * method. If this method fails, the system attempts again @@ -244,147 +244,147 @@ gadgets.rpctx.rmr = function() { * @param {string} from Module Id of the calling provider. * @param {Object} rpc The RPC data for this call. */ - function callRmr(targetId, serviceName, from, rpc) { - var handler = null; + function callRmr(targetId, serviceName, from, rpc) { + var handler = null; - if (from !== '..') { - // Call from gadget to the container. - handler = rmr_channels['..']; - } else { - // Call from container to the gadget. - handler = rmr_channels[targetId]; - } - - if (handler) { - // Queue the current message if not ACK. - // ACK is always sent through getRmrData(...). - if (serviceName !== gadgets.rpc.ACK) { - handler.queue.push(rpc); - } - - if (handler.waiting || - (handler.queue.length === 0 && - !(serviceName === gadgets.rpc.ACK && rpc && rpc.ackAlone === true))) { - // If we are awaiting a response from any previously-sent messages, - // or if we don't have anything new to send, just return. - // Note that we don't short-return if we're ACKing just-received - // messages. - return true; + if (from !== '..') { + // Call from gadget to the container. + handler = rmr_channels['..']; + } else { + // Call from container to the gadget. + handler = rmr_channels[targetId]; } - if (handler.queue.length > 0) { - handler.waiting = true; - } + if (handler) { + // Queue the current message if not ACK. + // ACK is always sent through getRmrData(...). + if (serviceName !== gadgets.rpc.ACK) { + handler.queue.push(rpc); + } - var url = handler.relayUri + "#" + getRmrData(targetId); + if (handler.waiting || + (handler.queue.length === 0 && + !(serviceName === gadgets.rpc.ACK && rpc && rpc.ackAlone === true))) { + // If we are awaiting a response from any previously-sent messages, + // or if we don't have anything new to send, just return. + // Note that we don't short-return if we're ACKing just-received + // messages. + return true; + } - try { - // Update the URL with the message. - handler.frame.contentWindow.location = url; + if (handler.queue.length > 0) { + handler.waiting = true; + } - // Resize the frame. - var newWidth = handler.width == 10 ? 20 : 10; - handler.frame.style.width = newWidth + 'px'; - handler.width = newWidth; + var url = handler.relayUri + '#' + getRmrData(targetId); - // Done! - } catch (e) { - // Something about location-setting or resizing failed. - // This should never happen, but if it does, fall back to - // the default transport. - return false; + try { + // Update the URL with the message. + handler.frame.contentWindow.location = url; + + // Resize the frame. + var newWidth = handler.width == 10 ? 20 : 10; + handler.frame.style.width = newWidth + 'px'; + handler.width = newWidth; + + // Done! + } catch (e) { + // Something about location-setting or resizing failed. + // This should never happen, but if it does, fall back to + // the default transport. + return false; + } } - } - return true; - } + return true; + } - /** + /** * Returns as a string the data to be appended to an RMR relay frame, * constructed from the current request queue plus an ACK message indicating * the currently latest-processed message ID. * * @param {string} toFrameId Frame whose sendable queued data to retrieve. */ - function getRmrData(toFrameId) { - var channel = rmr_channels[toFrameId]; - var rmrData = {id: channel.sendId}; - if (channel) { - rmrData.d = Array.prototype.slice.call(channel.queue, 0); - rmrData.d.push({s:gadgets.rpc.ACK, id:channel.recvId}); + function getRmrData(toFrameId) { + var channel = rmr_channels[toFrameId]; + var rmrData = {id: channel.sendId}; + if (channel) { + rmrData.d = Array.prototype.slice.call(channel.queue, 0); + rmrData.d.push({s: gadgets.rpc.ACK, id: channel.recvId}); + } + return gadgets.json.stringify(rmrData); } - return gadgets.json.stringify(rmrData); - } - /** + /** * Retrieve data from the channel keyed by the given frameId, * processing it as a batch. All processed data is assumed to have been * generated by getRmrData(...), pairing that method with this. * * @param {string} fromFrameId Frame from which data is being retrieved. */ - function processRmrData(fromFrameId) { - var channel = rmr_channels[fromFrameId]; - var data = channel.receiveWindow.location.hash.substring(1); - - // Decode the RPC object array. - var rpcObj = gadgets.json.parse(decodeURIComponent(data)) || {}; - var rpcArray = rpcObj.d || []; - - var nonAckReceived = false; - var noLongerWaiting = false; - - var numBypassed = 0; - var numToBypass = (channel.recvId - rpcObj.id); - for (var i = 0; i < rpcArray.length; ++i) { - var rpc = rpcArray[i]; - - // If we receive an ACK message, then mark the current - // handler as no longer waiting and send out the next - // queued message. - if (rpc.s === gadgets.rpc.ACK) { - // ACK received - whether this came from a handshake or - // an active call, in either case it indicates readiness to - // send messages to the from frame. - ready(fromFrameId, true); + function processRmrData(fromFrameId) { + var channel = rmr_channels[fromFrameId]; + var data = channel.receiveWindow.location.hash.substring(1); + + // Decode the RPC object array. + var rpcObj = gadgets.json.parse(decodeURIComponent(data)) || {}; + var rpcArray = rpcObj.d || []; + + var nonAckReceived = false; + var noLongerWaiting = false; + + var numBypassed = 0; + var numToBypass = (channel.recvId - rpcObj.id); + for (var i = 0; i < rpcArray.length; ++i) { + var rpc = rpcArray[i]; + + // If we receive an ACK message, then mark the current + // handler as no longer waiting and send out the next + // queued message. + if (rpc.s === gadgets.rpc.ACK) { + // ACK received - whether this came from a handshake or + // an active call, in either case it indicates readiness to + // send messages to the from frame. + ready(fromFrameId, true); + + if (channel.waiting) { + noLongerWaiting = true; + } + + channel.waiting = false; + var newlyAcked = Math.max(0, rpc.id - channel.sendId); + channel.queue.splice(0, newlyAcked); + channel.sendId = Math.max(channel.sendId, rpc.id || 0); + continue; + } - if (channel.waiting) { - noLongerWaiting = true; + // If we get here, we've received > 0 non-ACK messages to + // process. Indicate this bit for later. + nonAckReceived = true; + + // Bypass any messages already received. + if (++numBypassed <= numToBypass) { + continue; } - channel.waiting = false; - var newlyAcked = Math.max(0, rpc.id - channel.sendId); - channel.queue.splice(0, newlyAcked); - channel.sendId = Math.max(channel.sendId, rpc.id || 0); - continue; + ++channel.recvId; + process(rpc); // actually dispatch the message } - // If we get here, we've received > 0 non-ACK messages to - // process. Indicate this bit for later. - nonAckReceived = true; - - // Bypass any messages already received. - if (++numBypassed <= numToBypass) { - continue; + // Send an ACK indicating that we got/processed the message(s). + // Do so if we've received a message to process or if we were waiting + // before but a received ACK has cleared our waiting bit, and we have + // more messages to send. Performing this operation causes additional + // messages to be sent. + if (nonAckReceived || + (noLongerWaiting && channel.queue.length > 0)) { + var from = (fromFrameId === '..') ? gadgets.rpc.RPC_ID : '..'; + callRmr(fromFrameId, gadgets.rpc.ACK, from, {ackAlone: nonAckReceived}); } - - ++channel.recvId; - process(rpc); // actually dispatch the message - } - - // Send an ACK indicating that we got/processed the message(s). - // Do so if we've received a message to process or if we were waiting - // before but a received ACK has cleared our waiting bit, and we have - // more messages to send. Performing this operation causes additional - // messages to be sent. - if (nonAckReceived || - (noLongerWaiting && channel.queue.length > 0)) { - var from = (fromFrameId === '..') ? gadgets.rpc.RPC_ID : '..'; - callRmr(fromFrameId, gadgets.rpc.ACK, from, {ackAlone: nonAckReceived}); } - } - /** + /** * Registers the RMR channel handler for the given frameId and associated * channel window. * @@ -396,121 +396,121 @@ gadgets.rpctx.rmr = function() { * @return {boolean} True if the frame was setup successfully, false * otherwise. */ - function registerRmrChannel(frameId, channelWindow) { - var channel = rmr_channels[frameId]; + function registerRmrChannel(frameId, channelWindow) { + var channel = rmr_channels[frameId]; - // Verify that the channel is ready for receiving. - try { - var canAccess = false; - - // Check to see if the document is in the window. For Chrome, this - // will return 'false' if the channelWindow is inaccessible by this - // piece of JavaScript code, meaning that the URL of the channelWindow's - // parent iframe has not yet changed from 'about:blank'. We do this - // check this way because any true *access* on the channelWindow object - // will raise a security exception, which, despite the try-catch, still - // gets reported to the debugger (it does not break execution, the try - // handles that problem, but it is still reported, which is bad form). - // This check always succeeds in Safari 3.1 regardless of the state of - // the window. - canAccess = 'document' in channelWindow; + // Verify that the channel is ready for receiving. + try { + var canAccess = false; - if (!canAccess) { - return false; - } + // Check to see if the document is in the window. For Chrome, this + // will return 'false' if the channelWindow is inaccessible by this + // piece of JavaScript code, meaning that the URL of the channelWindow's + // parent iframe has not yet changed from 'about:blank'. We do this + // check this way because any true *access* on the channelWindow object + // will raise a security exception, which, despite the try-catch, still + // gets reported to the debugger (it does not break execution, the try + // handles that problem, but it is still reported, which is bad form). + // This check always succeeds in Safari 3.1 regardless of the state of + // the window. + canAccess = 'document' in channelWindow; - // Check to see if the document is an object. For Safari 3.1, this will - // return undefined if the page is still inaccessible. Unfortunately, this - // *will* raise a security issue in the debugger. - // TODO Find a way around this problem. - canAccess = typeof channelWindow['document'] == 'object'; + if (!canAccess) { + return false; + } - if (!canAccess) { - return false; - } + // Check to see if the document is an object. For Safari 3.1, this will + // return undefined if the page is still inaccessible. Unfortunately, this + // *will* raise a security issue in the debugger. + // TODO Find a way around this problem. + canAccess = typeof channelWindow['document'] == 'object'; - // Once we get here, we know we can access the document (and anything else) - // on the window object. Therefore, we check to see if the location is - // still about:blank (this takes care of the Safari 3.2 case). - var loc = channelWindow.location.href; + if (!canAccess) { + return false; + } - // Check if this is about:blank for Safari. - if (loc === 'about:blank') { + // Once we get here, we know we can access the document (and anything else) + // on the window object. Therefore, we check to see if the location is + // still about:blank (this takes care of the Safari 3.2 case). + var loc = channelWindow.location.href; + + // Check if this is about:blank for Safari. + if (loc === 'about:blank') { + return false; + } + } catch (ex) { + // For some reason, the iframe still points to about:blank. We try + // again in a bit. return false; } - } catch (ex) { - // For some reason, the iframe still points to about:blank. We try - // again in a bit. - return false; - } - // Save a reference to the receive window. - channel.receiveWindow = channelWindow; + // Save a reference to the receive window. + channel.receiveWindow = channelWindow; - // Register the onresize handler. - function onresize() { - processRmrData(frameId); - }; + // Register the onresize handler. + function onresize() { + processRmrData(frameId); + }; - if (typeof channelWindow.attachEvent === "undefined") { - channelWindow.onresize = onresize; - } else { - channelWindow.attachEvent("onresize", onresize); - } + if (typeof channelWindow.attachEvent === 'undefined') { + channelWindow.onresize = onresize; + } else { + channelWindow.attachEvent('onresize', onresize); + } - if (frameId === '..') { - // Gadget to container. Signal to the container that the gadget - // is ready to receive messages by attaching the g -> c relay. - // As a nice optimization, pass along any gadget to container - // queued messages that have backed up since then. ACK is enqueued in - // getRmrData to ensure that the container's waiting flag is set to false - // (this happens in the below code run on the container side). - appendRmrFrame(channel.frame, channel.relayUri, getRmrData(frameId), frameId); - } else { - // Process messages that the gadget sent in its initial relay payload. - // We can do this immediately because the container has already appended - // and loaded a relay frame that can be used to ACK the messages the gadget - // sent. In the preceding if-block, however, the processRmrData(...) call - // must wait. That's because appendRmrFrame may not actually append the - // frame - in the context of a gadget, this code may be running in the - // head element, so it cannot be appended to body. As a result, the - // gadget cannot ACK the container for messages it received. - processRmrData(frameId); + if (frameId === '..') { + // Gadget to container. Signal to the container that the gadget + // is ready to receive messages by attaching the g -> c relay. + // As a nice optimization, pass along any gadget to container + // queued messages that have backed up since then. ACK is enqueued in + // getRmrData to ensure that the container's waiting flag is set to false + // (this happens in the below code run on the container side). + appendRmrFrame(channel.frame, channel.relayUri, getRmrData(frameId), frameId); + } else { + // Process messages that the gadget sent in its initial relay payload. + // We can do this immediately because the container has already appended + // and loaded a relay frame that can be used to ACK the messages the gadget + // sent. In the preceding if-block, however, the processRmrData(...) call + // must wait. That's because appendRmrFrame may not actually append the + // frame - in the context of a gadget, this code may be running in the + // head element, so it cannot be appended to body. As a result, the + // gadget cannot ACK the container for messages it received. + processRmrData(frameId); + } + + return true; } - return true; - } + return { + getCode: function() { + return 'rmr'; + }, - return { - getCode: function() { - return 'rmr'; - }, + isParentVerifiable: function() { + return true; + }, - isParentVerifiable: function() { - return true; - }, + init: function(processFn, readyFn) { + // No global setup. + process = processFn; + ready = readyFn; + return true; + }, - init: function(processFn, readyFn) { - // No global setup. - process = processFn; - ready = readyFn; - return true; - }, + setup: function(receiverId, token) { + try { + setupRmr(receiverId); + } catch (e) { + gadgets.warn('Caught exception setting up RMR: ' + e); + return false; + } + return true; + }, - setup: function(receiverId, token) { - try { - setupRmr(receiverId); - } catch (e) { - gadgets.warn('Caught exception setting up RMR: ' + e); - return false; + call: function(targetId, from, rpc) { + return callRmr(targetId, rpc.s, from, rpc); } - return true; - }, - - call: function(targetId, from, rpc) { - return callRmr(targetId, rpc.s, from, rpc); - } - }; -}(); + }; + }(); } // !end of double-inclusion guard
