gh_2215_make_GM_xhr_more_parallel_again

Go to https://github.com/Tampermonkey/tampermonkey/issues/2215 for more.

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.gf.qytechs.cn/scripts/515994/1478507/gh_2215_make_GM_xhr_more_parallel_again.js

  1. // ==UserScript==
  2. // @name gh_2215_make_GM_xhr_more_parallel_again
  3. // @namespace https://github.com/Tampermonkey/tampermonkey/issues/2215
  4. // @version 1.0
  5. // @description Go to https://github.com/Tampermonkey/tampermonkey/issues/2215 for more.
  6. // @author Tampermonkey
  7. // ==/UserScript==
  8.  
  9. /*
  10. * https://github.com/Tampermonkey/tampermonkey/issues/2215
  11. *
  12. * This script provides a workaround for a Chrome MV3 issue (https://github.com/w3c/webextensions/issues/694)
  13. * where extensions can't set/delete headers that are preserved over redirects.
  14. *
  15. * By setting `redirect: 'manual'` and following redirects manually, this script ensures request redirects work
  16. * as intended and requests to different URLs are made in parallel (again).
  17. *
  18. * Userscript authors should include this as a `@require` when they need to make parallel requests with GM_xmlhttpRequest,
  19. * especially if requests might take a long time to complete.
  20. *
  21. * Including this script will modify the behavior of GM_xmlhttpRequest and GM.xmlHttpRequest in Tampermonkey only.
  22. *
  23. * Usage:
  24. *
  25. * Add this to the metadata block of your userscript:
  26. *
  27. * // @grant GM_xmlhttpRequest
  28. * // @require https://raw.githubusercontent.com/Tampermonkey/utils/refs/heads/main/requires/gh_2215_make_GM_xhr_more_parallel_again.js
  29. *
  30. **/
  31.  
  32. /* global GM_info, GM_xmlhttpRequest, GM */
  33.  
  34. const HAS_GM = typeof GM !== 'undefined';
  35. const NEW_GM = ((scope, GM) => {
  36. // Check if running in Tampermonkey and if version supports redirect control
  37. if (GM_info.scriptHandler !== "Tampermonkey" || compareVersions(GM_info.version, "5.3.2") < 0) return;
  38.  
  39. // Backup original functions
  40. const GM_xmlhttpRequestOrig = GM_xmlhttpRequest;
  41. const GM_xmlHttpRequestOrig = GM.xmlHttpRequest;
  42.  
  43. function compareVersions(v1, v2) {
  44. const parts1 = v1.split('.').map(Number);
  45. const parts2 = v2.split('.').map(Number);
  46. const length = Math.max(parts1.length, parts2.length);
  47.  
  48. for (let i = 0; i < length; i++) {
  49. const num1 = parts1[i] || 0;
  50. const num2 = parts2[i] || 0;
  51.  
  52. if (num1 > num2) return 1;
  53. if (num1 < num2) return -1;
  54. }
  55. return 0;
  56. }
  57.  
  58. // Wrapper for GM_xmlhttpRequest
  59. function GM_xmlhttpRequestWrapper(odetails) {
  60. // If redirect is manually set, simply pass odetails to the original function
  61. if (odetails.redirect !== undefined) {
  62. return GM_xmlhttpRequestOrig(odetails);
  63. }
  64.  
  65. // Warn if onprogress is used with settings incompatible with fetch mode used in background
  66. if (odetails.onprogress || odetails.fetch === false) {
  67. console.warn("Fetch mode does not support onprogress in the background.");
  68. }
  69.  
  70. const {
  71. onload,
  72. onloadend,
  73. onerror,
  74. onabort,
  75. ontimeout,
  76. ...details
  77. } = odetails;
  78.  
  79. // Set redirect to manual and handle redirects
  80. const handleRedirects = (initialDetails) => {
  81. const request = GM_xmlhttpRequestOrig({
  82. ...initialDetails,
  83. redirect: 'manual',
  84. onload: function(response) {
  85. if (response.status >= 300 && response.status < 400) {
  86. const m = response.responseHeaders.match(/Location:\s*(\S+)/i);
  87. // Follow redirect manually
  88. const redirectUrl = m && m[1];
  89. if (redirectUrl) {
  90. const absoluteUrl = new URL(redirectUrl, initialDetails.url).href;
  91. handleRedirects({ ...initialDetails, url: absoluteUrl });
  92. return;
  93. }
  94. }
  95.  
  96. if (onload) onload.call(this, response);
  97. if (onloadend) onloadend.call(this, response);
  98. },
  99. onerror: function(response) {
  100. if (onerror) onerror.call(this, response);
  101. if (onloadend) onloadend.call(this, response);
  102. },
  103. onabort: function(response) {
  104. if (onabort) onabort.call(this, response);
  105. if (onloadend) onloadend.call(this, response);
  106. },
  107. ontimeout: function(response) {
  108. if (ontimeout) ontimeout.call(this, response);
  109. if (onloadend) onloadend.call(this, response);
  110. }
  111. });
  112. return request;
  113. };
  114.  
  115. return handleRedirects(details);
  116. }
  117.  
  118. // Wrapper for GM.xmlHttpRequest
  119. function GM_xmlHttpRequestWrapper(odetails) {
  120. let abort;
  121.  
  122. const p = new Promise((resolve, reject) => {
  123. const { onload, ontimeout, onerror, ...send } = odetails;
  124.  
  125. send.onerror = function(r) {
  126. if (onerror) {
  127. resolve(r);
  128. onerror.call(this, r);
  129. } else {
  130. reject(r);
  131. }
  132. };
  133. send.ontimeout = function(r) {
  134. if (ontimeout) {
  135. // See comment above
  136. resolve(r);
  137. ontimeout.call(this, r);
  138. } else {
  139. reject(r);
  140. }
  141. };
  142. send.onload = function(r) {
  143. resolve(r);
  144. if (onload) onload.call(this, r);
  145. };
  146.  
  147. const a = GM_xmlhttpRequestWrapper(send).abort;
  148. if (abort === true) {
  149. a();
  150. } else {
  151. abort = a;
  152. }
  153. });
  154.  
  155. p.abort = () => {
  156. if (typeof abort === 'function') {
  157. abort();
  158. } else {
  159. abort = true;
  160. }
  161. };
  162.  
  163. return p;
  164. }
  165.  
  166. // Export wrappers
  167. GM_xmlhttpRequest = GM_xmlhttpRequestWrapper;
  168. scope.GM_xmlhttpRequestOrig = GM_xmlhttpRequestOrig;
  169.  
  170. const gopd = Object.getOwnPropertyDescriptor(GM, 'xmlHttpRequest');
  171. if (gopd && gopd.configurable === false) {
  172. return {
  173. __proto__: GM,
  174. xmlHttpRequest: GM_xmlHttpRequestWrapper,
  175. xmlHttpRequestOrig: GM_xmlHttpRequestOrig
  176. };
  177. } else {
  178. GM.xmlHttpRequest = GM_xmlHttpRequestWrapper;
  179. GM.xmlHttpRequestOrig = GM_xmlHttpRequestOrig;
  180. }
  181. })(this, HAS_GM ? GM : {});
  182.  
  183. if (HAS_GM && NEW_GM) GM = NEW_GM;

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址