You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by jo...@apache.org on 2010/04/01 05:26:30 UTC
svn commit: r929794 - in /shindig/trunk: features/
features/src/main/javascript/features/
features/src/main/javascript/features/core.io/
features/src/main/javascript/features/opensocial-templates/
features/src/main/javascript/features/xhrwrapper/ featu...
Author: johnh
Date: Thu Apr 1 03:26:28 2010
New Revision: 929794
URL: http://svn.apache.org/viewvc?rev=929794&view=rev
Log:
This feature allows to turn a type=url gadget which uses XMLHttpRequest into a
type=html/href= gadget, without having to modify it to use
gadgets.io.makeRequest.
Patch supplied by Jacobo Tarrio.
Taming to be done in a follow-up.
Added:
shindig/trunk/features/src/main/javascript/features/xhrwrapper/
shindig/trunk/features/src/main/javascript/features/xhrwrapper/feature.xml
shindig/trunk/features/src/main/javascript/features/xhrwrapper/xhrwrapper.js
shindig/trunk/features/src/test/javascript/features/xhrwrapper/
shindig/trunk/features/src/test/javascript/features/xhrwrapper/xhrwrappertest.js
Modified:
shindig/trunk/features/pom.xml
shindig/trunk/features/src/main/javascript/features/core.io/io.js
shindig/trunk/features/src/main/javascript/features/features.txt
shindig/trunk/features/src/main/javascript/features/opensocial-templates/loader.js
shindig/trunk/features/src/test/javascript/features/alltests.js
shindig/trunk/features/src/test/javascript/features/core.io/iotest.js
shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderingGadgetRewriter.java
shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/RenderingGadgetRewriterTest.java
Modified: shindig/trunk/features/pom.xml
URL: http://svn.apache.org/viewvc/shindig/trunk/features/pom.xml?rev=929794&r1=929793&r2=929794&view=diff
==============================================================================
--- shindig/trunk/features/pom.xml (original)
+++ shindig/trunk/features/pom.xml Thu Apr 1 03:26:28 2010
@@ -121,6 +121,7 @@
<source>i18n/numberformat.js</source>
<source>setprefs/setprefs.js</source>
<source>views/views.js</source>
+ <source>xhrwrapper/xhrwrapper.js</source>
<source>xmlutil/xmlutil.js</source>
<source>opensocial-data-context/datacontext.js</source>
<source>opensocial-data/data.js</source>
Modified: shindig/trunk/features/src/main/javascript/features/core.io/io.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/core.io/io.js?rev=929794&r1=929793&r2=929794&view=diff
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/core.io/io.js (original)
+++ shindig/trunk/features/src/main/javascript/features/core.io/io.js Thu Apr 1 03:26:28 2010
@@ -47,7 +47,11 @@ gadgets.io = function() {
*/
function makeXhr() {
var x;
- if (window.ActiveXObject) {
+ if (typeof shindig != 'undefined' &&
+ shindig.xhrwrapper &&
+ shindig.xhrwrapper.createXHR) {
+ return shindig.xhrwrapper.createXHR();
+ } else if (window.ActiveXObject) {
x = new ActiveXObject("Msxml2.XMLHTTP");
if (!x) {
x = new ActiveXObject("Microsoft.XMLHTTP");
Modified: shindig/trunk/features/src/main/javascript/features/features.txt
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/features.txt?rev=929794&r1=929793&r2=929794&view=diff
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/features.txt (original)
+++ shindig/trunk/features/src/main/javascript/features/features.txt Thu Apr 1 03:26:28 2010
@@ -63,4 +63,5 @@ features/skins/feature.xml
features/swfobject/feature.xml
features/tabs/feature.xml
features/views/feature.xml
+features/xhrwrapper/feature.xml
features/xmlutil/feature.xml
Modified: shindig/trunk/features/src/main/javascript/features/opensocial-templates/loader.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/opensocial-templates/loader.js?rev=929794&r1=929793&r2=929794&view=diff
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/opensocial-templates/loader.js (original)
+++ shindig/trunk/features/src/main/javascript/features/opensocial-templates/loader.js Thu Apr 1 03:26:28 2010
@@ -83,7 +83,11 @@ os.Loader.requestUrlXHR_ = function(url,
return;
}
var req = null;
- if (typeof(XMLHttpRequest) != "undefined") {
+ if (typeof shindig != 'undefined' &&
+ shindig.xhrwrapper &&
+ shindig.xhrwrapper.createXHR) {
+ req = shindig.xhrwrapper.createXHR();
+ } else if (typeof XMLHttpRequest != "undefined") {
req = new XMLHttpRequest();
} else {
req = new ActiveXObject("MSXML2.XMLHTTP");
Added: shindig/trunk/features/src/main/javascript/features/xhrwrapper/feature.xml
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/xhrwrapper/feature.xml?rev=929794&view=auto
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/xhrwrapper/feature.xml (added)
+++ shindig/trunk/features/src/main/javascript/features/xhrwrapper/feature.xml Thu Apr 1 03:26:28 2010
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations under the License.
+-->
+<feature>
+ <name>xhrwrapper</name>
+ <dependency>core.io</dependency>
+ <dependency>xmlutil</dependency>
+ <gadget>
+ <script src="xhrwrapper.js"/>
+ </gadget>
+</feature>
Added: shindig/trunk/features/src/main/javascript/features/xhrwrapper/xhrwrapper.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/main/javascript/features/xhrwrapper/xhrwrapper.js?rev=929794&view=auto
==============================================================================
--- shindig/trunk/features/src/main/javascript/features/xhrwrapper/xhrwrapper.js (added)
+++ shindig/trunk/features/src/main/javascript/features/xhrwrapper/xhrwrapper.js Thu Apr 1 03:26:28 2010
@@ -0,0 +1,391 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @fileoverview Emulate XMLHttpRequest using gadgets.io.makeRequest.
+ *
+ * This is not a complete implementation of XMLHttpRequest:
+ * - synchronous send() is unsupported;
+ * - the callback function will not get full header information, as makeRequest
+ * only provides the Set-Cookie and Location headers.
+ */
+
+shindig.xhrwrapper = shindig.xhrwrapper || {};
+
+(function () {
+
+ // Save the browser's XMLHttpRequest and ActiveXObject constructors.
+ var RealXMLHttpRequest = window.XMLHttpRequest;
+ var RealActiveXObject = window.ActiveXObject;
+
+ /**
+ * Creates a real XMLHttpRequest object.
+ *
+ * This function is to be used by code that needs access to the browser's
+ * XMLHttpRequest functionality, such as the code that implements
+ * gadgets.io.makeRequest itself.
+ *
+ * @return {Object|undefined} A XMLHttpRequest object, if one could
+ * be created.
+ */
+ shindig.xhrwrapper.createXHR = function() {
+ var activeXIdents =
+ ['MSXML2.XMLHTTP.6.0', 'MSXML2.XMLHTTP.3.0', 'MSXML2.XMLHTTP'];
+ if (typeof RealActiveXObject != 'undefined') {
+ for (var i = 0; i < activeXIdents.length; i++) {
+ try {
+ return new RealActiveXObject(activeXIdents[i]);
+ } catch (x) {
+ // do nothing, if none exists we'll do something later
+ }
+ }
+ }
+ if (typeof RealXMLHttpRequest != 'undefined') {
+ return new RealXMLHttpRequest();
+ }
+ return undefined;
+ };
+
+ /**
+ * @class XhrWrapper class.
+ *
+ * @constructor
+ * @description Implements the XMLHttpRequest interface, using
+ * gadgets.io.makeRequest to make the actual network accesses.
+ */
+ shindig.xhrwrapper.XhrWrapper = function() {
+ this.config_ = gadgets.config.get('shindig.xhrwrapper');
+
+ // XMLHttpRequest event listeners
+ this.onreadystatechange = null;
+
+ // XMLHttpRequest properties
+ this.readyState = 0;
+ };
+
+ /**
+ * Aborts the request if it has already been sent.
+ */
+ shindig.xhrwrapper.XhrWrapper.prototype.abort = function() {
+ this.aborted_ = true;
+ };
+
+ /**
+ * Returns all response headers as a string.
+ *
+ * @return {string?} The text of all response headers, or null if no response
+ * has been received.
+ */
+ shindig.xhrwrapper.XhrWrapper.prototype.getAllResponseHeaders = function() {
+ if (!this.responseHeaders_) {
+ return null;
+ }
+
+ var allHeaders = '';
+ for (var header in this.responseHeaders_) {
+ allHeaders += header + ': ' + this.responseHeaders_[header] + '\n';
+ }
+ return allHeaders;
+ };
+
+ /**
+ * Returns the value of a particular response header.
+ *
+ * @param {string} The name of the header to return.
+ * @return {string?} The value of the header, or null if no response has
+ * been received or the header doesn't exist in the response.
+ */
+ shindig.xhrwrapper.XhrWrapper.prototype.getResponseHeader = function(header) {
+ if (!this.responseHeaders_) {
+ return null;
+ }
+
+ var value = this.responseHeaders_[header.toLowerCase()];
+ return value ? value : null;
+ };
+
+ /**
+ * Initializes a request.
+ *
+ * @param {string} method The HTTP method to use ('POST' or 'GET').
+ * @param {string} url The URL to which to send the request.
+ * @param {boolean=} opt_async Whether to perform the operation
+ * asynchronously (defaults to true). Synchronous operations are not
+ * supported, so it must presently be omitted or true.
+ */
+ shindig.xhrwrapper.XhrWrapper.prototype.open =
+ function(method, url, opt_async) {
+ this.method_ = method;
+ this.url_ = new Url(url);
+ this.aborted_ = false;
+ this.requestHeaders_ = {};
+ this.responseHeaders_ = {};
+
+ this.baseUrl_ = new Url(this.config_.contentUrl);
+
+ this.fixRequestUrl_();
+
+ if (!this.baseUrl_.hasSameOrigin(this.url_)) {
+ throw new Error('A gadget at ' + this.config_.contentUrl +
+ ' tried to access ' + url + ' via XMLHttpRequest.');
+ }
+
+ if (opt_async === false) {
+ throw new Error('xhrwrapper does not support synchronous XHR.');
+ }
+
+ // XMLHttpRequest properties
+ this.multipart = false;
+ this.readyState = 1;
+ this.responseText = null;
+ this.responseXML = null;
+ this.status = 0;
+ this.statusText = null;
+ };
+
+ /**
+ * Sends the request.
+ *
+ * @param {string=} opt_data The data used to populate the body of a POST
+ * request.
+ */
+ shindig.xhrwrapper.XhrWrapper.prototype.send = function(opt_data) {
+ this.aborted_ = false;
+ var that = this;
+ var params = {};
+ params[gadgets.io.RequestParameters.METHOD] = this.method_;
+ params[gadgets.io.RequestParameters.HEADERS] = this.requestHeaders_;
+ params[gadgets.io.RequestParameters.POST_DATA] = opt_data;
+ gadgets.io.makeRequest(this.url_.toString(),
+ function(response) { that.callback_(response); },
+ params);
+ };
+
+ /**
+ * Sets the value of an HTTP request header.
+ *
+ * @param {string} header The name of the header to set.
+ * @param {string} value The value for the header.
+ */
+ shindig.xhrwrapper.XhrWrapper.prototype.setRequestHeader =
+ function(header, value) {
+ this.requestHeaders_[header] = value;
+ };
+
+ /**
+ * Processes the results from makeRequest and calls onreadystatechange.
+ *
+ * @param {Object} response The response from makeRequest.
+ * @private
+ */
+ shindig.xhrwrapper.XhrWrapper.prototype.callback_ = function(response) {
+ if (this.aborted_) {
+ return;
+ }
+ this.readyState = 4;
+ this.responseHeaders_ = response.headers;
+ this.responseText = response.text;
+ try {
+ this.responseXML = opensocial.xmlutil.parseXML(response.text);
+ } catch (x) {
+ this.responseXML = null;
+ }
+ this.status = response.rc;
+ if (response.errors) {
+ this.statusText = response.errors[0];
+ }
+ if (this.onreadystatechange) {
+ var event = {};
+ event.type = 'readystatechange';
+ event.srcElement = this;
+ event.target = this;
+ this.onreadystatechange(event);
+ }
+ };
+
+ /**
+ * Points the request URL to the correct server.
+ *
+ * If the URL is pointing to the gadget server, this function assumes the
+ * gadget's author wanted to point to the gadget contents location and
+ * changes it so that it points to the right place.
+ *
+ * For example, if the gadget is rendered in https://shindig/gadgets/ifr
+ * and the gadget's contents are at http://foo.com/bar/baz.html:
+ *
+ * - foo.xml gets turned into http://foo.com/bar/foo.xml
+ * - /foo/bar.xml gets turned into http://foo.com/foo/bar.xml
+ * - //foo.com/bar.xml gets turned into http://foo.com/bar.xml
+ * - http://foo.com/bar.xml is untouched
+ * - https://shindig/bar.xml is turned into http://foo.com/bar.xml
+ * - https://shindig/gadgets/bar.xml is turned into
+ * http://foo.com/bar/bar.xml
+ */
+ shindig.xhrwrapper.XhrWrapper.prototype.fixRequestUrl_ = function() {
+ this.url_.fullyQualify(this.baseUrl_);
+ var loc = new Url(window.location.href);
+ if (this.url_.hasSameOrigin(loc)) {
+ this.url_.schema = this.baseUrl_.schema;
+ this.url_.authority = this.baseUrl_.authority;
+ var pathLen = loc.path.length;
+ if (this.url_.path.substr(0, pathLen) == loc.path) {
+ this.url_.path = this.baseUrl_.path + this.url_.path.substr(pathLen);
+ }
+ }
+ };
+
+ /**
+ * @class A class for processing URLs.
+ *
+ * @constructor
+ * @description Pries apart the components of a URL, so it can be sliced
+ * and diced and combined with other URLs as needed.
+ */
+ function Url(url) {
+ this.schema = "";
+ this.authority = "";
+ this.path = "";
+ this.filename = "";
+ this.query = "";
+ this.fragment = "";
+
+ var parse = url;
+ var sharp = parse.indexOf('#');
+ if (sharp != -1) {
+ this.fragment = parse.substr(sharp);
+ parse = parse.substr(0, sharp);
+ }
+ var question = parse.indexOf('?');
+ if (question != -1) {
+ this.query = parse.substr(question);
+ parse = parse.substr(0, question);
+ }
+ var doubleSlash = parse.indexOf('//');
+ if (doubleSlash != -1) {
+ this.schema = parse.substr(0, doubleSlash);
+ parse = parse.substr(doubleSlash + 2);
+ var firstSlash = parse.indexOf('/');
+ if (firstSlash != -1) {
+ this.authority = parse.substr(0, firstSlash);
+ parse = parse.substr(firstSlash);
+ } else {
+ this.authority = parse;
+ parse = '';
+ }
+ }
+ var lastSlash = parse.lastIndexOf('/');
+ if (lastSlash != -1) {
+ this.path = parse.substr(0, lastSlash + 1);
+ parse = parse.substr(lastSlash + 1);
+ }
+ this.filename = parse;
+ };
+
+ /**
+ * Checks that a URL has the same origin as this URL.
+ *
+ * Two URLs have the same origin if they point to the same schema, server
+ * and port.
+ *
+ * @param {Url} other The URL to compare to this URL.
+ * @return {boolean} Whether the URLs have the same origin.
+ */
+ Url.prototype.hasSameOrigin = function(other) {
+ return this.schema == other.schema && this.authority == other.authority;
+ };
+
+ /**
+ * Fully qualifies this URL if it is relative, using a given base URL.
+ *
+ * @param {Url} base The base URL.
+ */
+ Url.prototype.fullyQualify = function(base) {
+ if (this.schema == '') {
+ this.schema = base.schema;
+ }
+ if (this.authority == '') {
+ this.authority = base.authority;
+ if (this.path == '' || this.path[0] != '/') {
+ this.path = base.path + this.path;
+ }
+ }
+ };
+
+ /**
+ * Returns a readable representation of the URL.
+ *
+ * @return {string} A readable URL.
+ */
+ Url.prototype.toString = function() {
+ var url = "";
+ if (this.schema) {
+ url += this.schema;
+ }
+ if (this.authority) {
+ url += '//' + this.authority;
+ }
+ if (this.path) {
+ url += this.path;
+ }
+ if (this.filename) {
+ url += this.filename;
+ }
+ if (this.query) {
+ url += this.query;
+ }
+ if (this.fragment) {
+ url += this.fragment;
+ }
+ return url;
+ };
+
+ /**
+ * Acts as a drop-in replacement for IE's ActiveXObject.
+ * @param {string} className The name of the class to create.
+ */
+ function ActiveXObjectReplacement(className) {
+ var obj;
+ if (typeof className == 'string' &&
+ (className.substr(0, 14).toLowerCase() == 'msxml2.xmlhttp' ||
+ className.toLowerCase() == 'microsoft.xmlhttp')) {
+ obj = new shindig.xhrwrapper.XhrWrapper();
+ } else {
+ obj = new RealActiveXObject(className);
+ }
+ for (var f in obj) {
+ this[f] = obj[f];
+ }
+ };
+
+ // Replace the browser's XMLHttpRequest and ActiveXObject constructors with
+ // xhrwrapper's.
+ if (window.XMLHttpRequest) {
+ window.XMLHttpRequest = shindig.xhrwrapper.XhrWrapper;
+ }
+ if (window.ActiveXObject) {
+ window.ActiveXObject = ActiveXObjectReplacement;
+ }
+
+ var config = {
+ contentUrl: gadgets.config.NonEmptyStringValidator
+ };
+ gadgets.config.register('shindig.xhrwrapper', config);
+
+})();
+
Modified: shindig/trunk/features/src/test/javascript/features/alltests.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/test/javascript/features/alltests.js?rev=929794&r1=929793&r2=929794&view=diff
==============================================================================
--- shindig/trunk/features/src/test/javascript/features/alltests.js (original)
+++ shindig/trunk/features/src/test/javascript/features/alltests.js Thu Apr 1 03:26:28 2010
@@ -43,6 +43,7 @@ if (!this.JsUtil) {
eval(JsUtil.prototype.include(srcDir + '/core/log.js'));
eval(JsUtil.prototype.include(srcDir + '/core.io/io.js'));
eval(JsUtil.prototype.include(srcDir + '/views/views.js'));
+ eval(JsUtil.prototype.include(srcDir + '/xhrwrapper/xhrwrapper.js'));
eval(JsUtil.prototype.include(srcDir + '/opensocial-reference/opensocial.js'));
eval(JsUtil.prototype.include(srcDir + '/opensocial-reference/activity.js'));
eval(JsUtil.prototype.include(srcDir + '/opensocial-reference/address.js'));
@@ -91,6 +92,7 @@ if (!this.JsUtil) {
eval(JsUtil.prototype.include(testSrcDir + "/osapi/batchtest.js"));
eval(JsUtil.prototype.include(testSrcDir + "/osapi/jsonrpctransporttest.js"));
eval(JsUtil.prototype.include(testSrcDir + "/views/urltemplatetest.js"));
+ eval(JsUtil.prototype.include(testSrcDir + "/xhrwrapper/xhrwrappertest.js"));
}
function AllTests() {
Modified: shindig/trunk/features/src/test/javascript/features/core.io/iotest.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/test/javascript/features/core.io/iotest.js?rev=929794&r1=929793&r2=929794&view=diff
==============================================================================
--- shindig/trunk/features/src/test/javascript/features/core.io/iotest.js (original)
+++ shindig/trunk/features/src/test/javascript/features/core.io/iotest.js Thu Apr 1 03:26:28 2010
@@ -34,7 +34,9 @@ IoTest.prototype.setUp = function() {
this.fakeXhrs = new fakeXhr.Factory(this);
this.oldXMLHttpRequest = window.XMLHTTPRequest;
+ this.oldXhrWrapper = shindig.xhrwrapper;
window.XMLHttpRequest = this.fakeXhrs.getXhrConstructor();
+ shindig.xhrwrapper = undefined;
gadgets.config.init({ "core.io" : {
"proxyUrl" : "http://example.com/proxy?url=%url%&refresh=%refresh%&g=%gadget%&c=%container%",
@@ -52,6 +54,7 @@ IoTest.prototype.setSchemaless = functio
IoTest.prototype.tearDown = function() {
gadgets.util.getUrlParameters = this.oldGetUrlParameters;
window.XMLHttpRequest = this.oldXMLHTTPRequest;
+ shindig.xhrwrapper = this.oldXhrWrapper;
};
IoTest.prototype.testGetProxyUrl = function() {
Added: shindig/trunk/features/src/test/javascript/features/xhrwrapper/xhrwrappertest.js
URL: http://svn.apache.org/viewvc/shindig/trunk/features/src/test/javascript/features/xhrwrapper/xhrwrappertest.js?rev=929794&view=auto
==============================================================================
--- shindig/trunk/features/src/test/javascript/features/xhrwrapper/xhrwrappertest.js (added)
+++ shindig/trunk/features/src/test/javascript/features/xhrwrapper/xhrwrappertest.js Thu Apr 1 03:26:28 2010
@@ -0,0 +1,191 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @fileoverview
+ *
+ * Unittests for the xhrwrapper feature.
+ */
+
+var shindig = shindig || {};
+
+function XhrWrapperTest(name) {
+ TestCase.call(this, name);
+}
+XhrWrapperTest.inherits(TestCase);
+
+XhrWrapperTest.prototype.setUp = function() {
+ // prepare mocks
+ gadgets.io = gadgets.io || {};
+ window.location = window.location || {};
+ opensocial.xmlutil = opensocial.xmlutil || {};
+ this.oldMakeRequest = gadgets.io.makeRequest;
+ this.oldWindowLocation = window.location;
+ this.oldParseXML = opensocial.xmlutil.parseXML;
+ this.madeRequest = {};
+ gadgets.io.makeRequest = this.mockMakeRequest(this.madeRequest);
+ window.location = { 'href': 'http://shindig/gadgets/ifr?url=blah' };
+ opensocial.xmlutil.parseXML = XhrWrapperTest.mockParseXML;
+ gadgets.config.init(
+ {"shindig.xhrwrapper": {"contentUrl": "http://foo.bar/baz/bax.html"}});
+};
+
+XhrWrapperTest.prototype.tearDown = function() {
+ // remove mocks
+ gadgets.io.makeRequest = this.oldMakeRequest;
+ window.location = this.oldWindowLocation;
+ opensocial.xmlutil.parseXML = this.oldParseXML;
+};
+
+XhrWrapperTest.prototype.testBasicWorking = function() {
+ var that = this;
+ var calledCallback = false;
+ var xhr = new shindig.xhrwrapper.XhrWrapper();
+ xhr.open('GET', 'http://foo.bar');
+ xhr.onreadystatechange = function(e) {
+ that.assertEquals('readystatechange', e.type);
+ that.assertEquals(xhr, e.target);
+ calledCallback = true;
+ };
+ xhr.send();
+
+ this.madeRequest.doCallback();
+
+ this.checkRequest('GET', 'http://foo.bar');
+ this.assertTrue(calledCallback);
+ this.assertEquals(4, xhr.readyState);
+ this.assertEquals('some text', xhr.responseText);
+ this.assertEquals('this would normally be XML', xhr.responseXML);
+ this.assertEquals(200, xhr.status);
+ this.assertEquals('no error', xhr.statusText);
+ this.assertEquals('v1', xhr.getResponseHeader('h1'));
+ this.assertEquals('v2', xhr.getResponseHeader('h2'));
+ this.assertEquals('h1: v1\nh2: v2\n', xhr.getAllResponseHeaders());
+};
+
+XhrWrapperTest.prototype.testAddRequestHeaders = function() {
+ var xhr = new shindig.xhrwrapper.XhrWrapper();
+ xhr.open('GET', 'http://foo.bar');
+ xhr.setRequestHeader('header', 'value');
+ xhr.send();
+
+ this.assertEquals('value', this.madeRequest.params.HEADERS['header']);
+};
+
+XhrWrapperTest.prototype.testSameOriginViolation = function() {
+ var thrown;
+ var xhr;
+
+ // Different schema
+ gadgets.config.init(
+ {"shindig.xhrwrapper": {"contentUrl": "https://foo.bar/baz/bax.html"}});
+ xhr = new shindig.xhrwrapper.XhrWrapper();
+ try {
+ xhr.open('GET', 'http://foo.bar/thing');
+ thrown = false;
+ } catch (x) {
+ thrown = true;
+ }
+ this.assertTrue('Should have thrown an error.', thrown);
+
+ // Different authority
+ gadgets.config.init(
+ {"shindig.xhrwrapper": {"contentUrl": "http://baw.net/bax.html"}});
+ xhr = new shindig.xhrwrapper.XhrWrapper();
+ try {
+ xhr.open('GET', 'http://foo.bar/thing');
+ thrown = false;
+ } catch (x) {
+ thrown = true;
+ }
+ this.assertTrue('Should have thrown an error.', thrown);
+
+ // Same schema and authority
+ gadgets.config.init(
+ {"shindig.xhrwrapper": {"contentUrl": "http://foo.bar/some/bax.html"}});
+ xhr = new shindig.xhrwrapper.XhrWrapper();
+ try {
+ xhr.open('GET', 'http://foo.bar/thing');
+ thrown = false;
+ } catch (x) {
+ thrown = true;
+ }
+ this.assertFalse('Should not have thrown an error.', thrown);
+};
+
+XhrWrapperTest.prototype.testResolveRelativeUrl = function() {
+ var xhr;
+
+ // Only path provided
+ xhr = new shindig.xhrwrapper.XhrWrapper();
+ xhr.open('GET', '/foo/bar/baz.xml');
+ xhr.send();
+ this.checkRequest('GET', 'http://foo.bar/foo/bar/baz.xml');
+
+ // Schema missing
+ xhr = new shindig.xhrwrapper.XhrWrapper();
+ xhr.open('GET', '//foo.bar/baz.xml');
+ xhr.send();
+ this.checkRequest('GET', 'http://foo.bar/baz.xml');
+};
+
+XhrWrapperTest.prototype.testRepointWrongUrls = function() {
+ var xhr;
+
+ // Only schema and hostname match
+ xhr = new shindig.xhrwrapper.XhrWrapper();
+ xhr.open('GET', 'http://shindig/foo/bar/baz.xml');
+ xhr.send();
+ this.checkRequest('GET', 'http://foo.bar/foo/bar/baz.xml');
+
+ // Schema, hostname and first part of path match
+ xhr = new shindig.xhrwrapper.XhrWrapper();
+ xhr.open('GET', 'http://shindig/gadgets/foo/bar/baz.xml');
+ xhr.send();
+ this.checkRequest('GET', 'http://foo.bar/baz/foo/bar/baz.xml');
+};
+
+XhrWrapperTest.prototype.mockMakeRequest = function(info) {
+ var that = this;
+ return function(url, callback, opt_params) {
+ info.url = url;
+ info.callback = callback;
+ info.params = opt_params;
+ info.doCallback = function() {
+ var response = {
+ data: 'some data',
+ errors: [ 'no error' ],
+ headers: { h1: 'v1', h2: 'v2' },
+ rc: 200,
+ text: 'some text'
+ };
+ info.callback.call(null, response);
+ };
+ };
+};
+
+XhrWrapperTest.mockParseXML = function(t) {
+ return 'this would normally be XML';
+};
+
+XhrWrapperTest.prototype.checkRequest = function(method, url) {
+ this.assertEquals(method, this.madeRequest.params['METHOD']);
+ this.assertEquals(url, this.madeRequest.url);
+};
+
Modified: shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderingGadgetRewriter.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderingGadgetRewriter.java?rev=929794&r1=929793&r2=929794&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderingGadgetRewriter.java (original)
+++ shindig/trunk/java/gadgets/src/main/java/org/apache/shindig/gadgets/render/RenderingGadgetRewriter.java Thu Apr 1 03:26:28 2010
@@ -379,9 +379,20 @@ public class RenderingGadgetRewriter imp
addHasFeatureConfig(gadget, config);
addOsapiSystemListMethodsConfig(context.getContainer(), config, context.getHost());
addSecurityTokenConfig(context, config);
+ addXhrWrapperConfig(gadget, config);
return "gadgets.config.init(" + JsonSerializer.serialize(config) + ");\n";
}
+ private void addXhrWrapperConfig(Gadget gadget, Map<String, Object> config) {
+ boolean isUsingXhrWrapper = gadget.getAllFeatures().contains("xhrwrapper");
+ if (isUsingXhrWrapper) {
+ Map<String, String> xhrWrapperConfig = Maps.newHashMapWithExpectedSize(2);
+ Uri contentsUri = gadget.getCurrentView().getHref();
+ xhrWrapperConfig.put("contentUrl", contentsUri == null ? "" : contentsUri.toString());
+ config.put("shindig.xhrwrapper", xhrWrapperConfig);
+ }
+ }
+
private void addSecurityTokenConfig(GadgetContext context, Map<String, Object> config) {
SecurityToken authToken = context.getToken();
if (authToken != null) {
Modified: shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/RenderingGadgetRewriterTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/RenderingGadgetRewriterTest.java?rev=929794&r1=929793&r2=929794&view=diff
==============================================================================
--- shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/RenderingGadgetRewriterTest.java (original)
+++ shindig/trunk/java/gadgets/src/test/java/org/apache/shindig/gadgets/render/RenderingGadgetRewriterTest.java Thu Apr 1 03:26:28 2010
@@ -117,7 +117,8 @@ public class RenderingGadgetRewriterTest
Gadget gadget = new Gadget()
.setContext(context)
.setPreloads(ImmutableList.<PreloadedData>of())
- .setSpec(spec);
+ .setSpec(spec)
+ .setGadgetFeatureRegistry(featureRegistry);
// Convenience: by default expect no features requested, by gadget or extern.
// expectFeatureCalls(...) resets featureRegistry if called again.
expectFeatureCalls(gadget,
@@ -617,6 +618,41 @@ public class RenderingGadgetRewriterTest
assertEquals("", defaultsJson.get("pref_two"));
}
+ @Test
+ public void xhrWrapperConfigurationInjected() throws Exception {
+ String gadgetXml =
+ "<Module><ModulePrefs title=''>" +
+ " <Require feature='xhrwrapper' />" +
+ "</ModulePrefs>" +
+ "<Content type='html' href='http://foo.com/bar/baz.html' />" +
+ "</Module>";
+
+ Gadget gadget = makeGadgetWithSpec(gadgetXml);
+ gadget.setCurrentView(gadget.getSpec().getView("default"));
+
+ String rewritten = rewrite(gadget, BODY_CONTENT);
+
+ boolean containsConfig =
+ rewritten.contains("\"shindig.xhrwrapper\":{\"contentUrl\":\"http://foo.com/bar/baz.html\"}");
+ assertTrue("No shindig.xhrwrapper configuration present in rewritten HTML.", containsConfig);
+ }
+
+ @Test
+ public void xhrWrapperConfigurationNotInjectedIfUnnecessary() throws Exception {
+ String gadgetXml =
+ "<Module><ModulePrefs title='' />" +
+ "<Content type='html' href='http://foo.com/bar/baz.html' />" +
+ "</Module>";
+
+ Gadget gadget = makeGadgetWithSpec(gadgetXml);
+ gadget.setCurrentView(gadget.getSpec().getView("default"));
+
+ String rewritten = rewrite(gadget, BODY_CONTENT);
+
+ boolean containsConfig = rewritten.contains("\"shindig.xhrwrapper\"");
+ assertFalse("shindig.xhrwrapper configuration present in rewritten HTML.", containsConfig);
+ }
+
@Test(expected = RewritingException.class)
public void unsupportedFeatureThrows() throws Exception {
String gadgetXml =
@@ -683,6 +719,8 @@ public class RenderingGadgetRewriterTest
.andReturn(ImmutableList.<FeatureResource>of());
expect(featureRegistry.getFeatures(eq(ImmutableList.of("core", "bar"))))
.andReturn(ImmutableList.of("core"));
+ expect(featureRegistry.getFeatures(eq(ImmutableList.of("core"))))
+ .andReturn(ImmutableList.of("core"));
replay(featureRegistry);
rewrite(gadget, "");
@@ -843,7 +881,8 @@ public class RenderingGadgetRewriterTest
GadgetContext gadgetContext = gadget.getContext();
List<String> gadgetFeatures = Lists.newArrayList(gadget.getDirectFeatureDeps());
List<String> allFeatures = Lists.newArrayList(gadgetFeatures);
- allFeatures.addAll(externLibs);
+ List<String> allFeaturesAndLibs = Lists.newArrayList(gadgetFeatures);
+ allFeaturesAndLibs.addAll(externLibs);
List<String> emptyList = Lists.newArrayList();
expect(featureRegistry.getFeatureResources(same(gadgetContext), eq(externLibs), eq(emptyList)))
.andReturn(externResources);
@@ -851,6 +890,8 @@ public class RenderingGadgetRewriterTest
.andReturn(gadgetResources);
expect(featureRegistry.getFeatures(eq(allFeatures)))
.andReturn(allFeatures);
+ expect(featureRegistry.getFeatures(eq(allFeaturesAndLibs)))
+ .andReturn(allFeaturesAndLibs);
replay(featureRegistry);
}