blob: 7fe8a914d8cd0f68ff0ffe1b70db4352acc45659 [file] [log] [blame]
Adrià Vilanova Martínezf19ea432024-01-23 20:20:52 +01001// Copyright 2016 The Chromium Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
Copybara854996b2021-09-07 19:36:02 +00004
5
6/**
7 * @fileoverview AJAX-related helper functions.
8 */
9
10
11var DEBOUNCE_THRESH_MS = 2000;
12
13
14/**
15 * Simple debouncer to handle text input. Don't try to hit the server
16 * until the user has stopped typing for a few seconds. E.g.,
17 * var debouncedKeyHandler = debounce(keyHandler);
18 * el.addEventListener('keyup', debouncedKeyHandler);
19 */
20function debounce(func, opt_threshold_ms) {
21 let timeout;
22 return function() {
23 let context = this, args = arguments;
24 let later = function() {
25 timeout = null;
26 func.apply(context, args);
27 };
28 clearTimeout(timeout);
29 timeout = setTimeout(later, opt_threshold_ms || DEBOUNCE_THRESH_MS);
30 };
31}
32
33
34/**
35 * Builds a POST string from a parameter dictionary.
36 * @param {Array|Object} args: parameters to encode. Either an object
37 * mapping names to values or an Array of doubles containing [key, value].
38 * @return {string} encoded POST data.
39 */
40function CS_postData(args) {
41 let params = [];
42
43 if (args instanceof Array) {
44 for (var key in args) {
45 let inputValue = args[key];
46 let name = inputValue[0];
47 let value = inputValue[1];
48 if (value !== undefined) {
49 params.push(name + '=' + encodeURIComponent(String(value)));
50 }
51 }
52 } else {
53 for (var key in args) {
54 params.push(key + '=' + encodeURIComponent(String(args[key])));
55 }
56 }
57
58 params.push('token=' + encodeURIComponent(window.prpcClient.token));
59
60 return params.join('&');
61}
62
63/**
64 * Helper for an extremely common kind of XHR: a POST with an XHRF token
65 * where we silently ignore server or connectivity errors. If the token
66 * has expired, get a new one and retry the original request with the new
67 * token.
68 * @param {string} url request destination.
69 * @param {function(event)} callback function to be called
70 * upon successful completion of the request.
71 * @param {Object} args parameters to encode as POST data.
72 */
73function CS_doPost(url, callback, args) {
74 window.prpcClient.ensureTokenIsValid().then(() => {
75 let xh = XH_XmlHttpCreate();
76 XH_XmlHttpPOST(xh, url, CS_postData(args), callback);
77 });
78}
79
80
81/**
82 * Helper function to strip leading junk characters from a JSON response
83 * and then parse it into a JS constant.
84 *
85 * The reason that "}])'\n" is prepended to the response text is that
86 * it makes it impossible for a hacker to hit one of our JSON servlets
87 * via a <script src="..."> tag and do anything with the result. Even
88 * though a JSON response is just a constant, it could be passed into
89 * hacker code by tricks such as overriding the array constructor.
90 */
91function CS_parseJSON(xhr) {
92 return JSON.parse(xhr.responseText.substr(5));
93}
94
95
96/**
97 * Promise-based version of CS_parseJSON using the fetch API.
98 *
99 * Sends a GET request to a JSON endpoint then strips the XSSI prefix off
100 * of the response before resolving the promise.
101 *
102 * Args:
103 * url (string): The URL to fetch.
104 * Returns:
105 * A promise, resolved when the request returns. Also be sure to call
106 * .catch() on the promise (or wrap in a try/catch if using async/await)
107 * if you don't want errors to halt script execution.
108 */
109function CS_fetch(url) {
110 return fetch(url, {credentials: 'same-origin'})
111 .then((res) => res.text())
112 .then((rawResponse) => JSON.parse(rawResponse.substr(5)));
113}
114
115
116/**
117 * After we refresh the form token, we need to actually submit the form.
118 * formToSubmit keeps track of which form the user was trying to submit.
119 */
120var formToSubmit = null;
121
122/**
123 * If the form token that was generated when the page was served has
124 * now expired, then request a refreshed token from the server, and
125 * don't submit the form until after it arrives.
126 */
127function refreshTokens(event, formToken, formTokenPath, tokenExpiresSec) {
128 if (!window.prpcClient.constructor.isTokenExpired(tokenExpiresSec)) {
129 return;
130 }
131
132 formToSubmit = event.target;
133 event.preventDefault();
134 const message = {
135 token: formToken,
136 tokenPath: formTokenPath,
137 };
138 const refreshTokenPromise = window.prpcClient.call(
139 'monorail.Sitewide', 'RefreshToken', message);
140
141 refreshTokenPromise.then((freshToken) => {
142 let tokenFields = document.querySelectorAll('input[name=token]');
143 for (let i = 0; i < tokenFields.length; ++i) {
144 tokenFields[i].value = freshToken.token;
145 }
146 if (formToSubmit) {
147 formToSubmit.submit();
148 }
149 });
150}