1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346
|
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
//
// Licensed 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.
// The original file lives here: http://go/cross_domain_channel.js
/**
* @fileoverview Implements a cross-domain communication channel. A
* typical web page is prevented by browser security from sending
* request, such as a XMLHttpRequest, to other servers than the ones
* from which it came. The Jsonp class provides a workaround by
* using dynamically generated script tags. Typical usage:.
*
* var trustedUri = goog.html.TrustedResourceUrl.fromConstant(
* goog.string.Const.from('https://example.com/servlet'));
* var jsonp = new goog.net.Jsonp(trustedUri);
* var payload = {'foo': 1, 'bar': true};
* jsonp.send(payload, function(reply) { alert(reply) });
*
* This script works in all browsers that are currently supported by
* the Google Maps API, which is IE 6.0+, Firefox 0.8+, Safari 1.2.4+,
* Netscape 7.1+, Mozilla 1.4+, Opera 8.02+.
*
*/
goog.provide('goog.net.Jsonp');
goog.require('goog.html.TrustedResourceUrl');
goog.require('goog.net.jsloader');
goog.require('goog.object');
// WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
//
// This class allows us (Google) to send data from non-Google and thus
// UNTRUSTED pages to our servers. Under NO CIRCUMSTANCES return
// anything sensitive, such as session or cookie specific data. Return
// only data that you want parties external to Google to have. Also
// NEVER use this method to send data from web pages to untrusted
// servers, or redirects to unknown servers (www.google.com/cache,
// /q=xx&btnl, /url, www.googlepages.com, etc.)
//
// WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
/**
* Creates a new cross domain channel that sends data to the specified
* host URL. By default, if no reply arrives within 5s, the channel
* assumes the call failed to complete successfully.
*
* @param {!goog.html.TrustedResourceUrl} uri The Uri of the server side code
* that receives data posted through this channel (e.g.,
* "http://maps.google.com/maps/geo").
*
* @param {string=} opt_callbackParamName The parameter name that is used to
* specify the callback. Defaults to "callback".
*
* @constructor
* @final
*/
goog.net.Jsonp = function(uri, opt_callbackParamName) {
/**
* The uri_ object will be used to encode the payload that is sent to the
* server.
* @type {!goog.html.TrustedResourceUrl}
* @private
*/
this.uri_ = uri;
/**
* This is the callback parameter name that is added to the uri.
* @type {string}
* @private
*/
this.callbackParamName_ =
opt_callbackParamName ? opt_callbackParamName : 'callback';
/**
* The length of time, in milliseconds, this channel is prepared
* to wait for for a request to complete. The default value is 5 seconds.
* @type {number}
* @private
*/
this.timeout_ = 5000;
/**
* The nonce to use in the dynamically generated script tags. This is used for
* allowing the script callbacks to execute when the page has an enforced
* Content Security Policy.
* @type {string}
* @private
*/
this.nonce_ = '';
};
/**
* The prefix for the callback name which will be stored on goog.global.
*/
goog.net.Jsonp.CALLBACKS = '_callbacks_';
/**
* Used to generate unique callback IDs. The counter must be global because
* all channels share a common callback object.
* @private
*/
goog.net.Jsonp.scriptCounter_ = 0;
/**
* Static private method which returns the global unique callback id.
*
* @param {string} id The id of the script node.
* @return {string} A global unique id used to store callback on goog.global
* object.
* @private
*/
goog.net.Jsonp.getCallbackId_ = function(id) {
return goog.net.Jsonp.CALLBACKS + '__' + id;
};
/**
* Sets the length of time, in milliseconds, this channel is prepared
* to wait for for a request to complete. If the call is not competed
* within the set time span, it is assumed to have failed. To wait
* indefinitely for a request to complete set the timout to a negative
* number.
*
* @param {number} timeout The length of time before calls are
* interrupted.
*/
goog.net.Jsonp.prototype.setRequestTimeout = function(timeout) {
this.timeout_ = timeout;
};
/**
* Returns the current timeout value, in milliseconds.
*
* @return {number} The timeout value.
*/
goog.net.Jsonp.prototype.getRequestTimeout = function() {
return this.timeout_;
};
/**
* Sets the nonce value for CSP. This nonce value will be added to any created
* script elements and must match the nonce provided in the
* Content-Security-Policy header sent by the server for the callback to pass
* CSP enforcement.
*
* @param {string} nonce The CSP nonce value.
*/
goog.net.Jsonp.prototype.setNonce = function(nonce) {
this.nonce_ = nonce;
};
/**
* Sends the given payload to the URL specified at the construction
* time. The reply is delivered to the given replyCallback. If the
* errorCallback is specified and the reply does not arrive within the
* timeout period set on this channel, the errorCallback is invoked
* with the original payload.
*
* If no reply callback is specified, then the response is expected to
* consist of calls to globally registered functions. No &callback=
* URL parameter will be sent in the request, and the script element
* will be cleaned up after the timeout.
*
* @param {Object=} opt_payload Name-value pairs. If given, these will be
* added as parameters to the supplied URI as GET parameters to the
* given server URI.
*
* @param {Function=} opt_replyCallback A function expecting one
* argument, called when the reply arrives, with the response data.
*
* @param {Function=} opt_errorCallback A function expecting one
* argument, called on timeout, with the payload (if given), otherwise
* null.
*
* @param {string=} opt_callbackParamValue Value to be used as the
* parameter value for the callback parameter (callbackParamName).
* To be used when the value needs to be fixed by the client for a
* particular request, to make use of the cached responses for the request.
* NOTE: If multiple requests are made with the same
* opt_callbackParamValue, only the last call will work whenever the
* response comes back.
*
* @return {!Object} A request descriptor that may be used to cancel this
* transmission, or null, if the message may not be cancelled.
*/
goog.net.Jsonp.prototype.send = function(
opt_payload, opt_replyCallback, opt_errorCallback, opt_callbackParamValue) {
var payload = opt_payload ? goog.object.clone(opt_payload) : {};
var id = opt_callbackParamValue ||
'_' + (goog.net.Jsonp.scriptCounter_++).toString(36) +
goog.now().toString(36);
var callbackId = goog.net.Jsonp.getCallbackId_(id);
if (opt_replyCallback) {
var reply = goog.net.Jsonp.newReplyHandler_(id, opt_replyCallback);
// Register the callback on goog.global to make it discoverable
// by jsonp response.
goog.global[callbackId] = reply;
payload[this.callbackParamName_] = callbackId;
}
var options = {timeout: this.timeout_, cleanupWhenDone: true};
if (this.nonce_) {
options.attributes = {'nonce': this.nonce_};
}
var uri = this.uri_.cloneWithParams(payload);
var deferred = goog.net.jsloader.safeLoad(uri, options);
var error = goog.net.Jsonp.newErrorHandler_(id, payload, opt_errorCallback);
deferred.addErrback(error);
return {id_: id, deferred_: deferred};
};
/**
* Cancels a given request. The request must be exactly the object returned by
* the send method.
*
* @param {Object} request The request object returned by the send method.
*/
goog.net.Jsonp.prototype.cancel = function(request) {
if (request) {
if (request.deferred_) {
request.deferred_.cancel();
}
if (request.id_) {
goog.net.Jsonp.cleanup_(request.id_, false);
}
}
};
/**
* Creates a timeout callback that calls the given timeoutCallback with the
* original payload.
*
* @param {string} id The id of the script node.
* @param {Object} payload The payload that was sent to the server.
* @param {Function=} opt_errorCallback The function called on timeout.
* @return {!Function} A zero argument function that handles callback duties.
* @private
*/
goog.net.Jsonp.newErrorHandler_ = function(id, payload, opt_errorCallback) {
/**
* When we call across domains with a request, this function is the
* timeout handler. Once it's done executing the user-specified
* error-handler, it removes the script node and original function.
*/
return function() {
goog.net.Jsonp.cleanup_(id, false);
if (opt_errorCallback) {
opt_errorCallback(payload);
}
};
};
/**
* Creates a reply callback that calls the given replyCallback with data
* returned by the server.
*
* @param {string} id The id of the script node.
* @param {Function} replyCallback The function called on reply.
* @return {!Function} A reply callback function.
* @private
*/
goog.net.Jsonp.newReplyHandler_ = function(id, replyCallback) {
/**
* This function is the handler for the all-is-well response. It
* clears the error timeout handler, calls the user's handler, then
* removes the script node and itself.
*
* @param {...Object} var_args The response data sent from the server.
*/
var handler = function(var_args) {
goog.net.Jsonp.cleanup_(id, true);
replyCallback.apply(undefined, arguments);
};
return handler;
};
/**
* Removes the reply handler registered on goog.global object.
*
* @param {string} id The id of the script node to be removed.
* @param {boolean} deleteReplyHandler If true, delete the reply handler
* instead of setting it to nullFunction (if we know the callback could
* never be called again).
* @private
*/
goog.net.Jsonp.cleanup_ = function(id, deleteReplyHandler) {
var callbackId = goog.net.Jsonp.getCallbackId_(id);
if (goog.global[callbackId]) {
if (deleteReplyHandler) {
try {
delete goog.global[callbackId];
} catch (e) {
// NOTE: Workaround to delete property on 'window' in IE <= 8, see:
// http://stackoverflow.com/questions/1073414/deleting-a-window-property-in-ie
goog.global[callbackId] = undefined;
}
} else {
// Removing the script tag doesn't necessarily prevent the script
// from firing, so we make the callback a noop.
goog.global[callbackId] = goog.nullFunction;
}
}
};
// WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
//
// This class allows us (Google) to send data from non-Google and thus
// UNTRUSTED pages to our servers. Under NO CIRCUMSTANCES return
// anything sensitive, such as session or cookie specific data. Return
// only data that you want parties external to Google to have. Also
// NEVER use this method to send data from web pages to untrusted
// servers, or redirects to unknown servers (www.google.com/cache,
// /q=xx&btnl, /url, www.googlepages.com, etc.)
//
// WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
|