OLD | NEW |
(Empty) | |
| 1 /* Copyright 2016 The Chromium Authors. All Rights Reserved. |
| 2 * |
| 3 * Use of this source code is governed by a BSD-style |
| 4 * license that can be found in the LICENSE file or at |
| 5 * https://developers.google.com/open-source/licenses/bsd |
| 6 */ |
| 7 |
| 8 |
| 9 /** |
| 10 * @fileoverview AJAX-related helper functions. |
| 11 */ |
| 12 |
| 13 |
| 14 /** |
| 15 * Builds a POST string from a parameter dictionary. |
| 16 * @param {Object} args parameters to encode. |
| 17 * @param {boolean} opt_includeToken whether to include an XSRF token. |
| 18 * If unspecified, defaults to true. Requires the user to be logged in. |
| 19 * @return {string} encoded POST data. |
| 20 */ |
| 21 function CS_postData(args, opt_token) { |
| 22 var params = []; |
| 23 for (var key in args) { |
| 24 params.push(key + "=" + encodeURIComponent(String(args[key]))); |
| 25 } |
| 26 if (opt_token) { |
| 27 params.push('token=' + encodeURIComponent(opt_token)); |
| 28 } else if (opt_token !== false) { |
| 29 params.push('token=' + encodeURIComponent(CS_env.token)); |
| 30 } |
| 31 return params.join('&'); |
| 32 } |
| 33 |
| 34 /** |
| 35 * Helper for an extremely common kind of XHR: a POST with an XHRF token |
| 36 * where we silently ignore server or connectivity errors. If the token |
| 37 * has expired, get a new one and retry the original request with the new |
| 38 * token. |
| 39 * @param {string} url request destination. |
| 40 * @param {function(event)} callback function to be called |
| 41 * upon successful completion of the request. |
| 42 * @param {Object} args parameters to encode as POST data. |
| 43 */ |
| 44 function CS_doPost(url, callback, args, opt_token, opt_tokenPath) { |
| 45 if (isTokenExpired()) { |
| 46 var refreshXHR = XH_XmlHttpCreate(); |
| 47 var refreshURL = '/hosting/tokenRefresh.do'; |
| 48 var refreshArgs = { |
| 49 form_token: opt_token || CS_env.token, |
| 50 form_token_path: opt_tokenPath || 'xhr' |
| 51 }; |
| 52 var refreshCallback = function(event) { |
| 53 var xhr = event.target; |
| 54 if (xhr.readyState != 4 || xhr.status != 200) |
| 55 return; |
| 56 var resp = CS_parseJSON(xhr); |
| 57 if (opt_tokenPath) |
| 58 CS_env[opt_tokenPath] = resp.form_token; |
| 59 CS_env.tokenExpiresSec = Number(resp.token_expires_sec); |
| 60 var retryXh = XH_XmlHttpCreate(); |
| 61 XH_XmlHttpPOST( |
| 62 retryXh, url, CS_postData(args, resp.form_token), callback); |
| 63 }; |
| 64 XH_XmlHttpPOST( |
| 65 refreshXHR, refreshURL, CS_postData(refreshArgs), refreshCallback); |
| 66 } else { |
| 67 var xh = XH_XmlHttpCreate(); |
| 68 XH_XmlHttpPOST( |
| 69 xh, url, |
| 70 CS_postData(args, CS_env[opt_tokenPath] || opt_token), |
| 71 callback); |
| 72 } |
| 73 } |
| 74 |
| 75 |
| 76 /** |
| 77 * Helper function to strip leading junk characters from a JSON response |
| 78 * and then parse it into a JS constant. |
| 79 * |
| 80 * The reason that "}])'\n" is prepended to the response text is that |
| 81 * it makes it impossible for a hacker to hit one of our JSON servlets |
| 82 * via a <script src="..."> tag and do anything with the result. Even |
| 83 * though a JSON response is just a constant, it could be passed into |
| 84 * hacker code by tricks such as overriding the array constructor. |
| 85 */ |
| 86 function CS_parseJSON(xhr) { |
| 87 return JSON.parse(xhr.responseText.substr(5)); |
| 88 } |
| 89 |
| 90 |
| 91 function isTokenExpired(opt_tokenExpiresSec) { |
| 92 var expiresSec = opt_tokenExpiresSec || CS_env.tokenExpiresSec; |
| 93 var tokenExpiresDate = new Date(expiresSec * 1000); |
| 94 return tokenExpiresDate <= new Date(); |
| 95 } |
| 96 |
| 97 /** |
| 98 * After we refresh the form token, we need to actually submit the form. |
| 99 * formToSubmit keeps track of which form the user was trying to submit. |
| 100 */ |
| 101 var formToSubmit = null; |
| 102 |
| 103 /** |
| 104 * If the form token that was generated when the page was served has |
| 105 * now expired, then request a refreshed token from the server, and |
| 106 * don't submit the form until after it arrives. |
| 107 */ |
| 108 function refreshTokens(event, formToken, formTokenPath, tokenExpiresSec) { |
| 109 if (!isTokenExpired(tokenExpiresSec)) |
| 110 return; |
| 111 |
| 112 formToSubmit = event.target; |
| 113 event.preventDefault(); |
| 114 CS_doPost("/hosting/tokenRefresh.do", gotXSRFToken, |
| 115 {form_token: formToken, |
| 116 form_token_path: formTokenPath}); |
| 117 } |
| 118 |
| 119 /** |
| 120 * If we got a new XSRF token from the server, use it to actually |
| 121 * submit the form that the user wanted to submit. |
| 122 */ |
| 123 function gotXSRFToken(event) { |
| 124 var xhr = event.target; |
| 125 if (xhr.readyState != 4 || xhr.status != 200) |
| 126 return; |
| 127 var resp = CS_parseJSON(xhr); |
| 128 var freshFormToken = resp["form_token"]; |
| 129 var tokenFields = document.querySelectorAll("input[name=token]"); |
| 130 for (var i = 0; i < tokenFields.length; ++i) { |
| 131 tokenFields[i].value = freshFormToken; |
| 132 } |
| 133 if (formToSubmit) { |
| 134 formToSubmit.submit(); |
| 135 } |
| 136 } |
OLD | NEW |