(opts, context, fileName)
| 173 | * @return {VMScriptXHRControl | Promise<VMScriptXHRControl>} |
| 174 | */ |
| 175 | export function onRequestCreate(opts, context, fileName) { |
| 176 | if (process.env.DEBUG) throwIfProtoPresent(opts); |
| 177 | let { data, url, [kResponseType]: type = '' } = opts; |
| 178 | let err, res; |
| 179 | // XHR spec requires `url` but allows ''/null/non-string |
| 180 | if (!url && !('url' in opts)) { |
| 181 | err = new SafeError('Required parameter "url" is missing.'); |
| 182 | } else if (!isString(url)) { |
| 183 | if (url === location) { url = url.href; } // safe window.location is unforgeable |
| 184 | else { |
| 185 | try { url = url::URLToString(); } // safe window.URL getter |
| 186 | catch (e) { |
| 187 | try { url = `${url}`; } // unsafe toString may throw e.g. for Symbol or if spoofed |
| 188 | catch (e2) { err = e2; } |
| 189 | } |
| 190 | } |
| 191 | opts.url = url; |
| 192 | } |
| 193 | if (err) { |
| 194 | onRequestInitError(opts, err); |
| 195 | return; // not returning the abort controller as there's no request to abort |
| 196 | } |
| 197 | if (!(type in XHR_TYPES)) { |
| 198 | logging.warn(`Unknown ${kResponseType} "${type}"`); |
| 199 | type = ''; |
| 200 | } |
| 201 | const scriptId = context.id; |
| 202 | const id = safeGetUniqId('VMxhr'); |
| 203 | const cb = [createNullObj(), createNullObj()]; |
| 204 | const events = [createNullObj(), createNullObj()]; |
| 205 | const req = safePickInto({ cb, id, scriptId }, opts, OPTS_TO_KEEP); |
| 206 | // withCredentials is for GM4 compatibility and used only if `anonymous` is not set, |
| 207 | // it's true by default per the standard/historical behavior of gmxhr |
| 208 | const { |
| 209 | withCredentials = true, |
| 210 | anonymous = !withCredentials, |
| 211 | [UPLOAD]: upload, |
| 212 | } = opts; |
| 213 | for (let i = 0, obj, key, val, passes = upload && isObject(upload) ? 2 : 1; i < passes; i++) { |
| 214 | obj = i ? nullObjFrom(upload) : opts; |
| 215 | for (key of EVENTS_TO_NOTIFY) { |
| 216 | if ((val = obj[`on${key}`]) && isFunction(val)) { |
| 217 | cb[i][key] = val; |
| 218 | events[i][key] = true; |
| 219 | } |
| 220 | } |
| 221 | } |
| 222 | if (context.async) res = new SafePromise((resolve, reject) => { |
| 223 | const { [LOAD]: onload, [ERROR]: onerror } = cb[0]; |
| 224 | cb[0][LOAD] = onload ? v => { resolve(v); onload(v); } : (events[0][LOAD] = true, resolve); |
| 225 | cb[0][ERROR] = onerror ? v => { reject(v); onerror(v); } : (events[0][ERROR] = true, reject); |
| 226 | }); |
| 227 | idMap[id] = req; |
| 228 | data = data == null && [] |
| 229 | // `binary` is for TM/GM-compatibility + non-objects = must use a string `data` |
| 230 | || (opts.binary || !isObject(data)) && [`${data}`] |
| 231 | // No browser can send FormData/URLSearchParams directly across worlds |
| 232 | || getFormData(data) |
no test coverage detected