confirm-oauth.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. (function attachBackgroundStep9(root, factory) {
  2. root.MultiPageBackgroundStep9 = factory();
  3. })(typeof self !== 'undefined' ? self : globalThis, function createBackgroundStep9Module() {
  4. function createStep9Executor(deps = {}) {
  5. const {
  6. addLog,
  7. chrome,
  8. cleanupStep8NavigationListeners,
  9. clickWithDebugger,
  10. completeStepFromBackground,
  11. ensureStep8SignupPageReady,
  12. getStep8CallbackUrlFromNavigation,
  13. getStep8CallbackUrlFromTabUpdate,
  14. getStep8EffectLabel,
  15. getTabId,
  16. isTabAlive,
  17. prepareStep8DebuggerClick,
  18. reloadStep8ConsentPage,
  19. reuseOrCreateTab,
  20. sleepWithStop,
  21. STEP8_CLICK_RETRY_DELAY_MS,
  22. STEP8_MAX_ROUNDS,
  23. STEP8_READY_WAIT_TIMEOUT_MS,
  24. STEP8_STRATEGIES,
  25. throwIfStep8SettledOrStopped,
  26. triggerStep8ContentStrategy,
  27. waitForStep8ClickEffect,
  28. waitForStep8Ready,
  29. setWebNavListener,
  30. setWebNavCommittedListener,
  31. setStep8PendingReject,
  32. setStep8TabUpdatedListener,
  33. } = deps;
  34. async function executeStep9(state) {
  35. if (!state.oauthUrl) {
  36. throw new Error('缺少登录用 OAuth 链接,请先完成步骤 7。');
  37. }
  38. await addLog('步骤 9:正在监听 localhost 回调地址...');
  39. return new Promise((resolve, reject) => {
  40. let resolved = false;
  41. let signupTabId = null;
  42. const cleanupListener = () => {
  43. cleanupStep8NavigationListeners();
  44. setStep8PendingReject(null);
  45. };
  46. const rejectStep9 = (error) => {
  47. if (resolved) return;
  48. resolved = true;
  49. clearTimeout(timeout);
  50. cleanupListener();
  51. reject(error);
  52. };
  53. const finalizeStep9Callback = (callbackUrl) => {
  54. if (resolved || !callbackUrl) return;
  55. resolved = true;
  56. cleanupListener();
  57. clearTimeout(timeout);
  58. addLog(`步骤 9:已捕获 localhost 地址:${callbackUrl}`, 'ok').then(() => {
  59. return completeStepFromBackground(9, { localhostUrl: callbackUrl });
  60. }).then(() => {
  61. resolve();
  62. }).catch((err) => {
  63. reject(err);
  64. });
  65. };
  66. const timeout = setTimeout(() => {
  67. rejectStep9(new Error('120 秒内未捕获到 localhost 回调跳转,步骤 9 的点击可能被拦截了。'));
  68. }, 120000);
  69. setStep8PendingReject((error) => {
  70. rejectStep9(error);
  71. });
  72. setWebNavListener((details) => {
  73. const callbackUrl = getStep8CallbackUrlFromNavigation(details, signupTabId);
  74. finalizeStep9Callback(callbackUrl);
  75. });
  76. setWebNavCommittedListener((details) => {
  77. const callbackUrl = getStep8CallbackUrlFromNavigation(details, signupTabId);
  78. finalizeStep9Callback(callbackUrl);
  79. });
  80. setStep8TabUpdatedListener((tabId, changeInfo, tab) => {
  81. const callbackUrl = getStep8CallbackUrlFromTabUpdate(tabId, changeInfo, tab, signupTabId);
  82. finalizeStep9Callback(callbackUrl);
  83. });
  84. (async () => {
  85. try {
  86. throwIfStep8SettledOrStopped(resolved);
  87. signupTabId = await getTabId('signup-page');
  88. throwIfStep8SettledOrStopped(resolved);
  89. if (signupTabId && await isTabAlive('signup-page')) {
  90. await chrome.tabs.update(signupTabId, { active: true });
  91. await addLog('步骤 9:已切回认证页,正在准备调试器点击...');
  92. } else {
  93. signupTabId = await reuseOrCreateTab('signup-page', state.oauthUrl);
  94. await addLog('步骤 9:已重新打开认证页,正在准备调试器点击...');
  95. }
  96. throwIfStep8SettledOrStopped(resolved);
  97. chrome.webNavigation.onBeforeNavigate.addListener(deps.getWebNavListener());
  98. chrome.webNavigation.onCommitted.addListener(deps.getWebNavCommittedListener());
  99. chrome.tabs.onUpdated.addListener(deps.getStep8TabUpdatedListener());
  100. await ensureStep8SignupPageReady(signupTabId, {
  101. timeoutMs: 15000,
  102. logMessage: '步骤 9:认证页内容脚本尚未就绪,正在等待页面恢复...',
  103. });
  104. for (let round = 1; round <= STEP8_MAX_ROUNDS && !resolved; round++) {
  105. throwIfStep8SettledOrStopped(resolved);
  106. const pageState = await waitForStep8Ready(signupTabId, STEP8_READY_WAIT_TIMEOUT_MS);
  107. if (!pageState?.consentReady) {
  108. await sleepWithStop(STEP8_CLICK_RETRY_DELAY_MS);
  109. continue;
  110. }
  111. const strategy = STEP8_STRATEGIES[Math.min(round - 1, STEP8_STRATEGIES.length - 1)];
  112. await addLog(`步骤 9:第 ${round}/${STEP8_MAX_ROUNDS} 轮尝试点击“继续”(${strategy.label})...`);
  113. if (strategy.mode === 'debugger') {
  114. const clickTarget = await prepareStep8DebuggerClick(signupTabId);
  115. throwIfStep8SettledOrStopped(resolved);
  116. await clickWithDebugger(signupTabId, clickTarget?.rect);
  117. } else {
  118. await triggerStep8ContentStrategy(signupTabId, strategy.strategy);
  119. }
  120. if (resolved) {
  121. return;
  122. }
  123. const effect = await waitForStep8ClickEffect(signupTabId, pageState.url);
  124. if (resolved) {
  125. return;
  126. }
  127. if (effect.progressed) {
  128. await addLog(`步骤 9:检测到本次点击已生效,${getStep8EffectLabel(effect)},继续等待 localhost 回调...`, 'info');
  129. break;
  130. }
  131. if (effect.restartCurrentStep) {
  132. await addLog(`步骤 9:${getStep8EffectLabel(effect)},准备重新定位“继续”按钮并重试...`, 'warn');
  133. await sleepWithStop(STEP8_CLICK_RETRY_DELAY_MS);
  134. continue;
  135. }
  136. if (round >= STEP8_MAX_ROUNDS) {
  137. throw new Error(`步骤 9:连续 ${STEP8_MAX_ROUNDS} 轮点击“继续”后页面仍无反应。`);
  138. }
  139. await addLog(`步骤 9:${strategy.label} 本轮点击后页面无反应,正在刷新认证页后重试(下一轮 ${round + 1}/${STEP8_MAX_ROUNDS})...`, 'warn');
  140. await reloadStep8ConsentPage(signupTabId);
  141. await sleepWithStop(STEP8_CLICK_RETRY_DELAY_MS);
  142. }
  143. } catch (err) {
  144. rejectStep9(err);
  145. }
  146. })();
  147. });
  148. }
  149. return { executeStep9 };
  150. }
  151. return { createStep9Executor };
  152. });