ソースを参照

feat: integrate iCloud hide my email generation into OAuth flow

- 合并 PR #72 的核心改动:新增 iCloud Hide My Email 生成、alias 管理与成功后自动标记/删除流程
- 本地补充修复:解决与 Cloudflare Temp Email 分支差异的冲突,并补齐合并后的测试常量
- 影响范围:background 邮箱生成与 Step 9 收尾、sidepanel alias 管理 UI、manifest/rules 与新增 iCloud 测试
Q3CC 4 ヶ月 前
コミット
14e3d6271d

+ 572 - 4
background.js

@@ -1,6 +1,6 @@
 // background.js — Service Worker: orchestration, state, tab management, message routing
 
-importScripts('data/names.js', 'hotmail-utils.js', 'cloudflare-temp-email-utils.js', 'content/activation-utils.js');
+importScripts('data/names.js', 'hotmail-utils.js', 'cloudflare-temp-email-utils.js', 'icloud-utils.js', 'content/activation-utils.js');
 
 const {
   buildHotmailMailApiLatestUrl,
@@ -28,12 +28,32 @@ const {
   normalizeCloudflareTempEmailDomains,
   normalizeCloudflareTempEmailMailApiMessages,
 } = self.CloudflareTempEmailUtils;
+const {
+  findIcloudAliasByEmail,
+  getConfiguredIcloudHostPreference,
+  getIcloudHostHintFromMessage,
+  getIcloudLoginUrlForHost,
+  getIcloudSetupUrlForHost,
+  normalizeBooleanMap,
+  normalizeIcloudAliasList,
+  normalizeIcloudHost,
+  pickReusableIcloudAlias,
+  toNormalizedEmailSet,
+} = self.IcloudUtils;
 const {
   isRecoverableStep9AuthFailure,
 } = self.MultiPageActivationUtils;
 
 const LOG_PREFIX = '[MultiPage:bg]';
 const DUCK_AUTOFILL_URL = 'https://duckduckgo.com/email/settings/autofill';
+const ICLOUD_SETUP_URLS = [
+  'https://setup.icloud.com.cn/setup/ws/1',
+  'https://setup.icloud.com/setup/ws/1',
+];
+const ICLOUD_LOGIN_URLS = [
+  'https://www.icloud.com.cn/',
+  'https://www.icloud.com/',
+];
 const HOTMAIL_PROVIDER = 'hotmail-api';
 const CLOUDFLARE_TEMP_EMAIL_PROVIDER = 'cloudflare-temp-email';
 const CLOUDFLARE_TEMP_EMAIL_GENERATOR = 'cloudflare-temp-email';
@@ -62,6 +82,7 @@ const DEFAULT_HOTMAIL_REMOTE_BASE_URL = '';
 const DEFAULT_HOTMAIL_LOCAL_BASE_URL = 'http://127.0.0.1:17373';
 const HOTMAIL_LOCAL_HELPER_TIMEOUT_MS = 45000;
 const DISPLAY_TIMEZONE = 'Asia/Shanghai';
+const PERSISTENT_ALIAS_STATE_KEYS = ['manualAliasUsage', 'preservedAliases'];
 
 initializeSessionStorageAccess();
 
@@ -86,6 +107,8 @@ const PERSISTED_SETTING_DEFAULTS = {
   autoStepDelaySeconds: null,
   mailProvider: '163',
   emailGenerator: 'duck',
+  autoDeleteUsedIcloudAlias: false,
+  icloudHostPreference: 'auto',
   emailPrefix: '',
   inbucketHost: '',
   inbucketMailbox: '',
@@ -116,6 +139,8 @@ const DEFAULT_STATE = {
   email: null, // 运行时邮箱,由程序自动获取并写入,不能手动预填。
   password: null, // 运行时实际密码,由 customPassword 或程序自动生成后写入。
   accounts: [], // 已生成账号记录:{ email, password, createdAt }。
+  manualAliasUsage: {},
+  preservedAliases: {},
   lastEmailTimestamp: null, // 最近一次获取到邮箱数据的运行时时间戳。
   lastSignupCode: null, // 注册验证码,运行时由程序自动读取并写入。
   lastLoginCode: null, // 登录验证码,运行时由程序自动读取并写入。
@@ -143,6 +168,7 @@ const DEFAULT_STATE = {
   signupVerificationRequestedAt: null,
   loginVerificationRequestedAt: null,
   currentHotmailAccountId: null,
+  preferredIcloudHost: '',
 };
 
 function normalizeAutoRunDelayMinutes(value) {
@@ -236,6 +262,9 @@ function normalizeEmailGenerator(value = '') {
   if (normalized === 'custom' || normalized === 'manual') {
     return 'custom';
   }
+  if (normalized === 'icloud') {
+    return 'icloud';
+  }
   if (normalized === 'cloudflare') return 'cloudflare';
   if (normalized === CLOUDFLARE_TEMP_EMAIL_GENERATOR) return CLOUDFLARE_TEMP_EMAIL_GENERATOR;
   return 'duck';
@@ -391,6 +420,10 @@ function normalizePersistentSettingValue(key, value) {
       return normalizeMailProvider(value);
     case 'emailGenerator':
       return normalizeEmailGenerator(value);
+    case 'autoDeleteUsedIcloudAlias':
+      return Boolean(value);
+    case 'icloudHostPreference':
+      return normalizeIcloudHost(value) || 'auto';
     case 'emailPrefix':
       return String(value || '').trim();
     case 'inbucketHost':
@@ -475,12 +508,29 @@ async function getPersistedSettings() {
   return buildPersistentSettingsPayload(stored, { fillDefaults: true });
 }
 
+async function getPersistedAliasState() {
+  try {
+    const stored = await chrome.storage.local.get(PERSISTENT_ALIAS_STATE_KEYS);
+    return {
+      manualAliasUsage: normalizeBooleanMap(stored.manualAliasUsage),
+      preservedAliases: normalizeBooleanMap(stored.preservedAliases),
+    };
+  } catch (err) {
+    console.warn(LOG_PREFIX, 'Failed to read persisted iCloud alias state:', err?.message || err);
+    return {
+      manualAliasUsage: {},
+      preservedAliases: {},
+    };
+  }
+}
+
 async function getState() {
-  const [state, persistedSettings] = await Promise.all([
+  const [state, persistedSettings, persistedAliasState] = await Promise.all([
     chrome.storage.session.get(null),
     getPersistedSettings(),
+    getPersistedAliasState(),
   ]);
-  return { ...DEFAULT_STATE, ...persistedSettings, ...state };
+  return { ...DEFAULT_STATE, ...persistedSettings, ...persistedAliasState, ...state };
 }
 
 async function initializeSessionStorageAccess() {
@@ -500,6 +550,16 @@ async function setState(updates) {
   console.log(LOG_PREFIX, 'storage.set:', JSON.stringify(updates).slice(0, 200));
   if (Object.keys(updates || {}).length > 0) {
     await chrome.storage.session.set(updates);
+    const persistentAliasUpdates = {};
+    if (Object.prototype.hasOwnProperty.call(updates, 'manualAliasUsage')) {
+      persistentAliasUpdates.manualAliasUsage = normalizeBooleanMap(updates.manualAliasUsage);
+    }
+    if (Object.prototype.hasOwnProperty.call(updates, 'preservedAliases')) {
+      persistentAliasUpdates.preservedAliases = normalizeBooleanMap(updates.preservedAliases);
+    }
+    if (Object.keys(persistentAliasUpdates).length > 0) {
+      await chrome.storage.local.set(persistentAliasUpdates);
+    }
   }
 }
 
@@ -578,6 +638,13 @@ function broadcastDataUpdate(payload) {
   }).catch(() => { });
 }
 
+function broadcastIcloudAliasesChanged(payload = {}) {
+  chrome.runtime.sendMessage({
+    type: 'ICLOUD_ALIASES_CHANGED',
+    payload,
+  }).catch(() => { });
+}
+
 async function setEmailStateSilently(email) {
   await setState({ email });
   broadcastDataUpdate({ email });
@@ -595,28 +662,84 @@ async function setPasswordState(password) {
   broadcastDataUpdate({ password });
 }
 
+function getManualAliasUsageMap(state) {
+  return normalizeBooleanMap(state?.manualAliasUsage);
+}
+
+function getPreservedAliasMap(state) {
+  return normalizeBooleanMap(state?.preservedAliases);
+}
+
+function isAliasPreserved(state, email) {
+  const normalizedEmail = String(email || '').trim().toLowerCase();
+  if (!normalizedEmail) return false;
+  return Boolean(getPreservedAliasMap(state)[normalizedEmail]);
+}
+
+function getEffectiveUsedEmails(state) {
+  return toNormalizedEmailSet(getManualAliasUsageMap(state));
+}
+
+async function setIcloudAliasUsedState(payload = {}, options = {}) {
+  const email = String(payload.email || '').trim().toLowerCase();
+  if (!email) {
+    throw new Error('未提供 iCloud 隐私邮箱地址。');
+  }
+
+  const used = Boolean(payload.used);
+  const state = await getState();
+  const manualAliasUsage = getManualAliasUsageMap(state);
+  manualAliasUsage[email] = used;
+  await setState({ manualAliasUsage });
+  if (!options.silentLog) {
+    await addLog(`iCloud:已将 ${email} 标记为${used ? '已用' : '未用'}`, 'ok');
+  }
+  broadcastIcloudAliasesChanged({ reason: 'used-updated', email, used });
+  return { email, used };
+}
+
+async function setIcloudAliasPreservedState(payload = {}) {
+  const email = String(payload.email || '').trim().toLowerCase();
+  if (!email) {
+    throw new Error('未提供 iCloud 隐私邮箱地址。');
+  }
+
+  const preserved = Boolean(payload.preserved);
+  const state = await getState();
+  const preservedAliases = getPreservedAliasMap(state);
+  preservedAliases[email] = preserved;
+  await setState({ preservedAliases });
+  await addLog(`iCloud:已将 ${email} ${preserved ? '设为保留' : '取消保留'}`, 'ok');
+  broadcastIcloudAliasesChanged({ reason: 'preserved-updated', email, preserved });
+  return { email, preserved };
+}
+
 async function resetState() {
   console.log(LOG_PREFIX, 'Resetting all state');
   // Preserve settings and persistent data across resets
-  const [prev, persistedSettings] = await Promise.all([
+  const [prev, persistedSettings, persistedAliasState] = await Promise.all([
     chrome.storage.session.get([
       'seenCodes',
       'seenInbucketMailIds',
       'accounts',
       'tabRegistry',
       'sourceLastUrls',
+      'preferredIcloudHost',
     ]),
     getPersistedSettings(),
+    getPersistedAliasState(),
   ]);
   await chrome.storage.session.clear();
   await chrome.storage.session.set({
     ...DEFAULT_STATE,
     ...persistedSettings,
+    ...persistedAliasState,
     seenCodes: prev.seenCodes || [],
     seenInbucketMailIds: prev.seenInbucketMailIds || [],
     accounts: prev.accounts || [],
     tabRegistry: prev.tabRegistry || {},
     sourceLastUrls: prev.sourceLastUrls || {},
+    preferredIcloudHost: prev.preferredIcloudHost || '',
   });
 }
 
@@ -1460,6 +1583,408 @@ async function pollCloudflareTempEmailVerificationCode(step, state, pollPayload
   throw lastError || new Error(`步骤 ${step}:未在 Cloudflare Temp Email 中找到新的匹配验证码。`);
 }
 
+async function getOpenIcloudHostPreference() {
+  try {
+    const tabs = await chrome.tabs.query({
+      url: [
+        'https://www.icloud.com/*',
+        'https://www.icloud.com.cn/*',
+      ],
+    });
+
+    const activeTab = tabs.find((tab) => tab.active);
+    const candidates = activeTab ? [activeTab, ...tabs.filter((tab) => tab.id !== activeTab.id)] : tabs;
+    for (const tab of candidates) {
+      try {
+        const host = normalizeIcloudHost(new URL(tab.url).host);
+        if (host) return host;
+      } catch {}
+    }
+  } catch {}
+
+  return '';
+}
+
+async function getPreferredIcloudLoginUrl(error = null, state = null) {
+  const currentState = state || await getState();
+  const configuredHost = getConfiguredIcloudHostPreference(currentState);
+  if (configuredHost) {
+    return getIcloudLoginUrlForHost(configuredHost);
+  }
+
+  const messageHint = getIcloudHostHintFromMessage(getErrorMessage(error));
+  if (messageHint) {
+    return getIcloudLoginUrlForHost(messageHint);
+  }
+
+  const savedHost = normalizeIcloudHost(currentState?.preferredIcloudHost);
+  if (savedHost) {
+    return getIcloudLoginUrlForHost(savedHost);
+  }
+
+  const openHost = await getOpenIcloudHostPreference();
+  if (openHost) {
+    return getIcloudLoginUrlForHost(openHost);
+  }
+
+  return ICLOUD_LOGIN_URLS[0];
+}
+
+async function getPreferredIcloudSetupUrls(state = null, error = null) {
+  const preferredLoginUrl = await getPreferredIcloudLoginUrl(error, state);
+  const preferredHost = normalizeIcloudHost(new URL(preferredLoginUrl).host);
+  const preferredSetupUrl = getIcloudSetupUrlForHost(preferredHost);
+  if (!preferredSetupUrl) {
+    return [...ICLOUD_SETUP_URLS];
+  }
+  return [
+    preferredSetupUrl,
+    ...ICLOUD_SETUP_URLS.filter((url) => url !== preferredSetupUrl),
+  ];
+}
+
+function isIcloudLoginRequiredError(error) {
+  const message = getErrorMessage(error).toLowerCase();
+  return message.includes('could not validate icloud session')
+    || message.includes('hide my email service was unavailable')
+    || /\bstatus (401|403|409|421)\b/.test(message);
+}
+
+let lastIcloudLoginPromptAt = 0;
+
+async function openIcloudLoginPage(preferredUrl) {
+  const tabs = await chrome.tabs.query({
+    url: [
+      'https://www.icloud.com/*',
+      'https://www.icloud.com.cn/*',
+    ],
+  });
+  const preferredHost = new URL(preferredUrl).host;
+  const existing = tabs.find((tab) => {
+    try {
+      return new URL(tab.url).host === preferredHost;
+    } catch {
+      return false;
+    }
+  });
+
+  if (existing?.id) {
+    await chrome.tabs.update(existing.id, { active: true });
+    if (existing.url !== preferredUrl) {
+      await chrome.tabs.update(existing.id, { url: preferredUrl });
+    }
+    return existing.id;
+  }
+
+  const created = await chrome.tabs.create({ url: preferredUrl, active: true });
+  return created.id;
+}
+
+async function promptIcloudLogin(error, actionLabel = 'iCloud 操作') {
+  const now = Date.now();
+  const preferredUrl = await getPreferredIcloudLoginUrl(error);
+  const originalError = getErrorMessage(error);
+
+  chrome.runtime.sendMessage({
+    type: 'ICLOUD_LOGIN_REQUIRED',
+    payload: {
+      actionLabel,
+      loginUrl: preferredUrl,
+      message: '需要先登录 iCloud,我已经为你打开登录页。',
+      detail: originalError,
+    },
+  }).catch(() => { });
+
+  if (now - lastIcloudLoginPromptAt < 15000) {
+    return;
+  }
+  lastIcloudLoginPromptAt = now;
+
+  await addLog(`iCloud:${actionLabel}时需要登录,正在打开 ${new URL(preferredUrl).host} ...`, 'warn');
+
+  try {
+    await openIcloudLoginPage(preferredUrl);
+  } catch (tabErr) {
+    await addLog(`iCloud:自动打开登录页失败:${getErrorMessage(tabErr)}`, 'warn');
+  }
+}
+
+async function withIcloudLoginHelp(actionLabel, action) {
+  try {
+    return await action();
+  } catch (err) {
+    if (isIcloudLoginRequiredError(err)) {
+      await promptIcloudLogin(err, actionLabel);
+      throw new Error('请先在新打开的 iCloud 页面中完成登录,再回来点击“我已登录”。');
+    }
+    throw err;
+  }
+}
+
+async function icloudRequest(method, url, options = {}) {
+  const { data } = options;
+  let response;
+  try {
+    response = await fetch(url, {
+      method,
+      credentials: 'include',
+      headers: data !== undefined ? { 'Content-Type': 'application/json' } : undefined,
+      body: data !== undefined ? JSON.stringify(data) : undefined,
+    });
+  } catch (err) {
+    throw new Error(`iCloud 请求失败:${method} ${url},${err.message}`);
+  }
+
+  if (!response.ok) {
+    throw new Error(`iCloud 请求失败:${method} ${url},status ${response.status}`);
+  }
+
+  try {
+    return await response.json();
+  } catch (err) {
+    throw new Error(`iCloud 返回的 JSON 无法解析:${method} ${url},${err.message}`);
+  }
+}
+
+async function validateIcloudSession(setupUrl) {
+  const data = await icloudRequest('POST', `${setupUrl}/validate`);
+  if (!data?.webservices?.premiummailsettings?.url) {
+    throw new Error('Could not validate iCloud session. Hide My Email service was unavailable.');
+  }
+  return data;
+}
+
+async function resolveIcloudPremiumMailService() {
+  const errors = [];
+  const state = await getState();
+  const setupUrls = await getPreferredIcloudSetupUrls(state);
+
+  for (const setupUrl of setupUrls) {
+    try {
+      const data = await validateIcloudSession(setupUrl);
+      const preferredIcloudHost = normalizeIcloudHost(new URL(setupUrl).host);
+      if (preferredIcloudHost && preferredIcloudHost !== normalizeIcloudHost(state.preferredIcloudHost)) {
+        await setState({ preferredIcloudHost });
+      }
+      return {
+        setupUrl,
+        serviceUrl: String(data.webservices.premiummailsettings.url || '').replace(/\/$/, ''),
+      };
+    } catch (err) {
+      errors.push(`${new URL(setupUrl).host}: ${getErrorMessage(err)}`);
+    }
+  }
+
+  throw new Error(errors.length
+    ? `Could not validate iCloud session. ${errors.join(' | ')}`
+    : 'Could not validate iCloud session. 请先在当前浏览器登录 icloud.com.cn 或 icloud.com。');
+}
+
+function getIcloudAliasLabel() {
+  const now = new Date();
+  const dateStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`;
+  return `MultiPage ${dateStr}`;
+}
+
+async function checkIcloudSession() {
+  return withIcloudLoginHelp('检查 iCloud 会话', async () => {
+    const { setupUrl } = await resolveIcloudPremiumMailService();
+    await addLog(`iCloud:会话校验通过(${new URL(setupUrl).host})`, 'ok');
+    return { ok: true, setupUrl };
+  });
+}
+
+async function listIcloudAliases() {
+  return withIcloudLoginHelp('加载 iCloud 隐私邮箱列表', async () => {
+    const { serviceUrl } = await resolveIcloudPremiumMailService();
+    const response = await icloudRequest('GET', `${serviceUrl}/v2/hme/list`);
+    const state = await getState();
+    return normalizeIcloudAliasList(response, {
+      usedEmails: getEffectiveUsedEmails(state),
+      preservedEmails: getPreservedAliasMap(state),
+    });
+  });
+}
+
+async function deleteIcloudAlias(payload) {
+  return withIcloudLoginHelp('删除 iCloud 隐私邮箱', async () => {
+    const alias = typeof payload === 'string'
+      ? { email: String(payload).trim().toLowerCase(), anonymousId: '' }
+      : {
+          email: String(payload?.email || '').trim().toLowerCase(),
+          anonymousId: String(payload?.anonymousId || '').trim(),
+        };
+
+    if (!alias.email) {
+      throw new Error('未提供需要删除的 iCloud 隐私邮箱。');
+    }
+    if (!alias.anonymousId) {
+      throw new Error(`缺少 ${alias.email} 的 anonymousId,请先刷新 iCloud 别名列表。`);
+    }
+
+    const { serviceUrl } = await resolveIcloudPremiumMailService();
+
+    try {
+      const directDelete = await icloudRequest('POST', `${serviceUrl}/v1/hme/delete`, {
+        data: { anonymousId: alias.anonymousId },
+      });
+      if (directDelete?.success === false) {
+        throw new Error(directDelete?.error?.errorMessage || 'delete failed');
+      }
+    } catch (err) {
+      await addLog(`iCloud:直接删除 ${alias.email} 失败,尝试先停用再删除...`, 'warn');
+
+      const deactivated = await icloudRequest('POST', `${serviceUrl}/v1/hme/deactivate`, {
+        data: { anonymousId: alias.anonymousId },
+      });
+      if (deactivated?.success === false) {
+        throw new Error(deactivated?.error?.errorMessage || `停用 ${alias.email} 失败`);
+      }
+
+      const deleted = await icloudRequest('POST', `${serviceUrl}/v1/hme/delete`, {
+        data: { anonymousId: alias.anonymousId },
+      });
+      if (deleted?.success === false) {
+        throw new Error(deleted?.error?.errorMessage || `删除 ${alias.email} 失败`);
+      }
+    }
+
+    const state = await getState();
+    const manualAliasUsage = getManualAliasUsageMap(state);
+    const preservedAliases = getPreservedAliasMap(state);
+    delete manualAliasUsage[alias.email];
+    delete preservedAliases[alias.email];
+    await setState({ manualAliasUsage, preservedAliases });
+
+    await addLog(`iCloud:已删除 ${alias.email}`, 'ok');
+    broadcastIcloudAliasesChanged({ reason: 'deleted', email: alias.email });
+    return { email: alias.email };
+  });
+}
+
+async function deleteUsedIcloudAliases() {
+  const aliases = await listIcloudAliases();
+  const usedAliases = aliases.filter((alias) => alias.used);
+  if (!usedAliases.length) {
+    return { deleted: [], skipped: [] };
+  }
+
+  const deleted = [];
+  const skipped = [];
+  for (const alias of usedAliases) {
+    if (alias.preserved) {
+      skipped.push({ email: alias.email, error: 'preserved' });
+      continue;
+    }
+    try {
+      await deleteIcloudAlias(alias);
+      deleted.push(alias.email);
+    } catch (err) {
+      skipped.push({ email: alias.email, error: getErrorMessage(err) });
+    }
+  }
+  return { deleted, skipped };
+}
+
+async function fetchIcloudHideMyEmail() {
+  return withIcloudLoginHelp('获取 iCloud 隐私邮箱', async () => {
+    throwIfStopped();
+    await addLog('iCloud:正在校验当前浏览器登录状态...', 'info');
+
+    const { serviceUrl, setupUrl } = await resolveIcloudPremiumMailService();
+    await addLog(`iCloud:已通过 ${new URL(setupUrl).host} 验证会话`, 'ok');
+
+    const existingAliasesResponse = await icloudRequest('GET', `${serviceUrl}/v2/hme/list`);
+    const state = await getState();
+    const existingAliases = normalizeIcloudAliasList(existingAliasesResponse, {
+      usedEmails: getEffectiveUsedEmails(state),
+      preservedEmails: getPreservedAliasMap(state),
+    });
+
+    const reusableAlias = pickReusableIcloudAlias(existingAliases);
+    if (reusableAlias) {
+      await setEmailState(reusableAlias.email);
+      await addLog(`iCloud:复用未使用别名 ${reusableAlias.email}`, 'ok');
+      broadcastIcloudAliasesChanged({ reason: 'selected', email: reusableAlias.email });
+      return reusableAlias.email;
+    }
+
+    await addLog('iCloud:没有可复用别名,开始生成新的 Hide My Email 地址...', 'warn');
+
+    const generated = await icloudRequest('POST', `${serviceUrl}/v1/hme/generate`);
+    if (!generated?.success || !generated?.result?.hme) {
+      throw new Error(generated?.error?.errorMessage || 'iCloud 隐私邮箱生成失败。');
+    }
+
+    const reserved = await icloudRequest('POST', `${serviceUrl}/v1/hme/reserve`, {
+      data: {
+        hme: generated.result.hme,
+        label: getIcloudAliasLabel(),
+        note: 'Generated through Multi-Page Automation',
+      },
+    });
+
+    if (!reserved?.success || !reserved?.result?.hme?.hme) {
+      throw new Error(reserved?.error?.errorMessage || 'iCloud 隐私邮箱保留失败。');
+    }
+
+    const alias = String(reserved.result.hme.hme || '').trim().toLowerCase();
+    await setEmailState(alias);
+    await addLog(`iCloud:已创建并保留新别名 ${alias}`, 'ok');
+    broadcastIcloudAliasesChanged({ reason: 'created', email: alias });
+    return alias;
+  });
+}
+
+async function finalizeIcloudAliasAfterSuccessfulFlow(state) {
+  const email = String(state?.email || '').trim().toLowerCase();
+  if (!email) {
+    return { handled: false, deleted: false };
+  }
+
+  const knownIcloudAlias = normalizeEmailGenerator(state?.emailGenerator) === 'icloud'
+    || Object.prototype.hasOwnProperty.call(getManualAliasUsageMap(state), email)
+    || Object.prototype.hasOwnProperty.call(getPreservedAliasMap(state), email);
+  if (!knownIcloudAlias) {
+    return { handled: false, deleted: false };
+  }
+
+  await setIcloudAliasUsedState({ email, used: true }, { silentLog: true });
+  await addLog(`iCloud:流程成功后已标记 ${email} 为已用。`, 'ok');
+
+  if (!state.autoDeleteUsedIcloudAlias) {
+    return { handled: true, deleted: false };
+  }
+
+  if (isAliasPreserved(state, email)) {
+    await addLog(`iCloud:${email} 已被标记为保留,跳过自动删除。`, 'info');
+    return { handled: true, deleted: false };
+  }
+
+  try {
+    const aliases = await listIcloudAliases();
+    const alias = findIcloudAliasByEmail(aliases, email);
+    if (!alias) {
+      await addLog(`iCloud:自动删除跳过,列表中未找到 ${email}。`, 'warn');
+      return { handled: true, deleted: false };
+    }
+    if (alias.preserved) {
+      await addLog(`iCloud:${email} 在最新别名列表中已是保留状态,跳过自动删除。`, 'info');
+      return { handled: true, deleted: false };
+    }
+    if (!alias.anonymousId) {
+      await addLog(`iCloud:自动删除跳过,${email} 缺少 anonymousId,请先刷新列表后重试。`, 'warn');
+      return { handled: true, deleted: false };
+    }
+    await deleteIcloudAlias(alias);
+    await addLog(`iCloud:流程成功后已自动删除 ${email}。`, 'ok');
+    return { handled: true, deleted: true };
+  } catch (err) {
+    await addLog(`iCloud:自动删除 ${email} 失败:${getErrorMessage(err)}`, 'warn');
+    return { handled: true, deleted: false };
+  }
+}
+
 // ============================================================
 // Tab Registry
 // ============================================================
@@ -1699,6 +2224,7 @@ async function closeTabsByUrlPrefix(prefix, options = {}) {
     .filter((tab) => typeof tab.url === 'string' && !excludedUrls.has(tab.url))
     .filter((tab) => !(excludeLocalhostCallbacks && isLocalhostOAuthCallbackUrl(tab.url)))
     .filter((tab) => typeof tab.url === 'string' && tab.url.startsWith(prefix))
+    .filter((tab) => !isLocalhostOAuthCallbackUrl(tab.url))
     .map((tab) => tab.id);
 
   if (!matchedIds.length) return 0;
@@ -3222,6 +3748,41 @@ async function handleMessage(message, sender) {
       return { ok: true, email };
     }
 
+    case 'CHECK_ICLOUD_SESSION': {
+      clearStopRequest();
+      return await checkIcloudSession();
+    }
+
+    case 'LIST_ICLOUD_ALIASES': {
+      clearStopRequest();
+      const aliases = await listIcloudAliases();
+      return { ok: true, aliases };
+    }
+
+    case 'SET_ICLOUD_ALIAS_USED_STATE': {
+      clearStopRequest();
+      const result = await setIcloudAliasUsedState(message.payload || {});
+      return { ok: true, ...result };
+    }
+
+    case 'SET_ICLOUD_ALIAS_PRESERVED_STATE': {
+      clearStopRequest();
+      const result = await setIcloudAliasPreservedState(message.payload || {});
+      return { ok: true, ...result };
+    }
+
+    case 'DELETE_ICLOUD_ALIAS': {
+      clearStopRequest();
+      const result = await deleteIcloudAlias(message.payload || {});
+      return { ok: true, ...result };
+    }
+
+    case 'DELETE_USED_ICLOUD_ALIASES': {
+      clearStopRequest();
+      const result = await deleteUsedIcloudAliases();
+      return { ok: true, ...result };
+    }
+
     case 'STOP_FLOW': {
       await requestStop();
       return { ok: true };
@@ -3308,6 +3869,7 @@ async function handleStepData(step, payload) {
           excludeLocalhostCallbacks: true,
         });
       }
+      await finalizeIcloudAliasAfterSuccessfulFlow(latestState);
       if (shouldUseCustomRegistrationEmail(latestState) && latestState.email) {
         await setEmailStateSilently(null);
       }
@@ -3599,6 +4161,9 @@ function getEmailGeneratorLabel(generator) {
   if (generator === 'custom') {
     return '自定义邮箱';
   }
+  if (generator === 'icloud') {
+    return 'iCloud 隐私邮箱';
+  }
   if (generator === 'cloudflare') return 'Cloudflare 邮箱';
   if (generator === CLOUDFLARE_TEMP_EMAIL_GENERATOR) return 'Cloudflare Temp Email';
   return 'Duck 邮箱';
@@ -3773,6 +4338,9 @@ async function fetchGeneratedEmail(state, options = {}) {
   if (generator === 'custom') {
     throw new Error('当前邮箱生成方式为自定义邮箱,请直接填写注册邮箱。');
   }
+  if (generator === 'icloud') {
+    return fetchIcloudHideMyEmail();
+  }
   if (generator === 'cloudflare') {
     return fetchCloudflareEmail(currentState, options);
   }

+ 177 - 0
icloud-utils.js

@@ -0,0 +1,177 @@
+(function icloudUtilsModule(root, factory) {
+  if (typeof module !== 'undefined' && module.exports) {
+    module.exports = factory();
+    return;
+  }
+
+  root.IcloudUtils = factory();
+})(typeof self !== 'undefined' ? self : globalThis, function createIcloudUtils() {
+  function normalizeIcloudHost(rawHost) {
+    const host = String(rawHost || '').trim().toLowerCase();
+    if (!host) return '';
+    if (host === 'icloud.com' || host === 'www.icloud.com' || host === 'setup.icloud.com') return 'icloud.com';
+    if (host === 'icloud.com.cn' || host === 'www.icloud.com.cn' || host === 'setup.icloud.com.cn') return 'icloud.com.cn';
+    return '';
+  }
+
+  function getConfiguredIcloudHostPreference(stateOrValue = '') {
+    const preference = typeof stateOrValue === 'object'
+      ? String(stateOrValue?.icloudHostPreference || '').trim().toLowerCase()
+      : String(stateOrValue || '').trim().toLowerCase();
+    if (!preference || preference === 'auto') return '';
+    return normalizeIcloudHost(preference);
+  }
+
+  function getIcloudLoginUrlForHost(host) {
+    const normalizedHost = normalizeIcloudHost(host);
+    if (normalizedHost === 'icloud.com') return 'https://www.icloud.com/';
+    if (normalizedHost === 'icloud.com.cn') return 'https://www.icloud.com.cn/';
+    return '';
+  }
+
+  function getIcloudSetupUrlForHost(host) {
+    const normalizedHost = normalizeIcloudHost(host);
+    if (normalizedHost === 'icloud.com') return 'https://setup.icloud.com/setup/ws/1';
+    if (normalizedHost === 'icloud.com.cn') return 'https://setup.icloud.com.cn/setup/ws/1';
+    return '';
+  }
+
+  function getIcloudHostHintFromMessage(message) {
+    const lower = String(message || '').toLowerCase();
+    if (lower.includes('setup.icloud.com.cn') || lower.includes('www.icloud.com.cn') || lower.includes('icloud.com.cn')) {
+      return 'icloud.com.cn';
+    }
+    if (lower.includes('setup.icloud.com') || lower.includes('www.icloud.com') || lower.includes('icloud.com')) {
+      return 'icloud.com';
+    }
+    return '';
+  }
+
+  function normalizeBooleanMap(rawValue = {}) {
+    if (!rawValue || typeof rawValue !== 'object' || Array.isArray(rawValue)) {
+      return {};
+    }
+
+    return Object.entries(rawValue).reduce((result, [key, value]) => {
+      const normalizedKey = String(key || '').trim().toLowerCase();
+      if (!normalizedKey) {
+        return result;
+      }
+      result[normalizedKey] = Boolean(value);
+      return result;
+    }, {});
+  }
+
+  function toNormalizedEmailSet(values = []) {
+    if (values instanceof Set) {
+      return new Set(Array.from(values, (item) => String(item || '').trim().toLowerCase()).filter(Boolean));
+    }
+    if (Array.isArray(values)) {
+      return new Set(values.map((item) => String(item || '').trim().toLowerCase()).filter(Boolean));
+    }
+    if (values && typeof values === 'object') {
+      const normalizedMap = normalizeBooleanMap(values);
+      return new Set(Object.entries(normalizedMap)
+        .filter(([, value]) => value)
+        .map(([email]) => email));
+    }
+    return new Set();
+  }
+
+  function findIcloudAliasArray(node, depth = 0) {
+    if (!node || depth > 4) return null;
+    if (Array.isArray(node)) {
+      return node.some((item) => item && typeof item === 'object') ? node : null;
+    }
+    if (typeof node !== 'object') return null;
+
+    const priorityKeys = ['hmeEmails', 'hmeEmailList', 'hmeList', 'hmes', 'aliases', 'items'];
+    for (const key of priorityKeys) {
+      if (Array.isArray(node[key])) return node[key];
+    }
+
+    for (const value of Object.values(node)) {
+      const nested = findIcloudAliasArray(value, depth + 1);
+      if (nested) return nested;
+    }
+
+    return null;
+  }
+
+  function normalizeIcloudAliasRecord(raw, options = {}) {
+    const usedEmails = toNormalizedEmailSet(options.usedEmails);
+    const preservedEmails = toNormalizedEmailSet(options.preservedEmails);
+    const anonymousId = String(raw?.anonymousId || raw?.id || '').trim();
+    const email = String(
+      raw?.hme
+        || raw?.email
+        || raw?.alias
+        || raw?.address
+        || raw?.metaData?.hme
+        || ''
+    ).trim().toLowerCase();
+
+    if (!email || !email.includes('@')) return null;
+
+    const label = String(raw?.label || raw?.metaData?.label || '').trim();
+    const note = String(raw?.note || raw?.metaData?.note || '').trim();
+    const state = String(raw?.state || raw?.status || '').trim().toLowerCase();
+    const createdAt = raw?.createTimestamp
+      || raw?.createTime
+      || raw?.createdAt
+      || raw?.createdDate
+      || null;
+
+    return {
+      anonymousId,
+      email,
+      label,
+      note,
+      state,
+      active: raw?.active !== false && raw?.isActive !== false && state !== 'inactive' && state !== 'deleted',
+      used: usedEmails.has(email),
+      preserved: preservedEmails.has(email),
+      createdAt,
+    };
+  }
+
+  function normalizeIcloudAliasList(response, options = {}) {
+    const aliases = findIcloudAliasArray(response);
+    if (!aliases) return [];
+
+    return aliases
+      .map((alias) => normalizeIcloudAliasRecord(alias, options))
+      .filter(Boolean)
+      .sort((left, right) => {
+        if (left.active !== right.active) return left.active ? -1 : 1;
+        if (left.used !== right.used) return left.used ? 1 : -1;
+        return String(left.email).localeCompare(String(right.email));
+      });
+  }
+
+  function pickReusableIcloudAlias(aliases = []) {
+    return (Array.isArray(aliases) ? aliases : []).find((alias) => alias?.active && !alias?.used) || null;
+  }
+
+  function findIcloudAliasByEmail(aliases = [], email = '') {
+    const normalizedEmail = String(email || '').trim().toLowerCase();
+    if (!normalizedEmail) return null;
+    return (Array.isArray(aliases) ? aliases : [])
+      .find((alias) => String(alias?.email || '').trim().toLowerCase() === normalizedEmail) || null;
+  }
+
+  return {
+    findIcloudAliasArray,
+    findIcloudAliasByEmail,
+    getConfiguredIcloudHostPreference,
+    getIcloudHostHintFromMessage,
+    getIcloudLoginUrlForHost,
+    getIcloudSetupUrlForHost,
+    normalizeBooleanMap,
+    normalizeIcloudAliasList,
+    normalizeIcloudAliasRecord,
+    normalizeIcloudHost,
+    pickReusableIcloudAlias,
+    toNormalizedEmailSet,
+  };
+});

+ 12 - 0
manifest.json

@@ -8,17 +8,29 @@
     "alarms",
     "tabs",
     "webNavigation",
+    "declarativeNetRequest",
     "debugger",
     "storage",
     "scripting",
     "activeTab"
   ],
   "host_permissions": [
+    "https://*.icloud.com/*",
+    "https://*.icloud.com.cn/*",
     "<all_urls>"
   ],
   "background": {
     "service_worker": "background.js"
   },
+  "declarative_net_request": {
+    "rule_resources": [
+      {
+        "id": "icloud_headers",
+        "enabled": true,
+        "path": "rules.json"
+      }
+    ]
+  },
   "side_panel": {
     "default_path": "sidepanel/sidepanel.html"
   },

+ 50 - 0
rules.json

@@ -0,0 +1,50 @@
+[
+  {
+    "id": 1,
+    "priority": 1,
+    "action": {
+      "type": "modifyHeaders",
+      "requestHeaders": [
+        {
+          "header": "Origin",
+          "operation": "set",
+          "value": "https://www.icloud.com"
+        },
+        {
+          "header": "Referer",
+          "operation": "set",
+          "value": "https://www.icloud.com/"
+        }
+      ]
+    },
+    "condition": {
+      "urlFilter": "|https://*.icloud.com/*",
+      "resourceTypes": ["xmlhttprequest"],
+      "excludedInitiatorDomains": ["apple.com", "icloud.com"]
+    }
+  },
+  {
+    "id": 2,
+    "priority": 1,
+    "action": {
+      "type": "modifyHeaders",
+      "requestHeaders": [
+        {
+          "header": "Origin",
+          "operation": "set",
+          "value": "https://www.icloud.com.cn"
+        },
+        {
+          "header": "Referer",
+          "operation": "set",
+          "value": "https://www.icloud.com.cn/"
+        }
+      ]
+    },
+    "condition": {
+      "urlFilter": "|https://*.icloud.com.cn/*",
+      "resourceTypes": ["xmlhttprequest"],
+      "excludedInitiatorDomains": ["apple.com.cn", "icloud.com.cn"]
+    }
+  }
+]

+ 157 - 0
sidepanel/sidepanel.css

@@ -877,6 +877,163 @@ header {
   text-align: center;
 }
 
+.icloud-card {
+  border: 1px solid var(--border-subtle);
+  border-radius: var(--radius-sm);
+  background: color-mix(in srgb, var(--bg-base) 84%, transparent);
+  padding: 10px;
+  display: flex;
+  flex-direction: column;
+  gap: 10px;
+}
+
+.icloud-summary {
+  color: var(--text-secondary);
+  font-size: 12px;
+  line-height: 1.6;
+}
+
+.icloud-login-help {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 10px;
+  border: 1px solid color-mix(in srgb, var(--orange) 26%, var(--border));
+  background: color-mix(in srgb, var(--orange-soft) 62%, var(--bg-base));
+  border-radius: var(--radius-sm);
+  padding: 10px;
+}
+
+.icloud-login-help-main {
+  min-width: 0;
+  display: flex;
+  flex-direction: column;
+  gap: 4px;
+}
+
+.icloud-login-help-title {
+  color: var(--text-primary);
+  font-weight: 700;
+  font-size: 13px;
+}
+
+.icloud-login-help-text {
+  color: var(--text-secondary);
+  font-size: 12px;
+  line-height: 1.5;
+}
+
+.icloud-toolbar {
+  display: flex;
+  gap: 8px;
+  align-items: center;
+}
+
+.icloud-search {
+  flex: 1;
+}
+
+.icloud-filter {
+  flex: 0 0 130px;
+}
+
+.icloud-bulkbar {
+  display: flex;
+  gap: 8px;
+  align-items: center;
+  justify-content: space-between;
+  flex-wrap: wrap;
+}
+
+.icloud-select-all {
+  flex: 0 0 auto;
+}
+
+.icloud-bulk-actions {
+  display: flex;
+  gap: 6px;
+  flex-wrap: wrap;
+}
+
+.icloud-list {
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+  max-height: 360px;
+  overflow: auto;
+  padding-right: 4px;
+}
+
+.icloud-item {
+  border: 1px solid var(--border);
+  background: var(--bg-base);
+  border-radius: var(--radius-sm);
+  padding: 10px;
+  display: grid;
+  grid-template-columns: auto minmax(0, 1fr) auto;
+  gap: 10px;
+  align-items: flex-start;
+}
+
+.icloud-item-check {
+  margin-top: 4px;
+}
+
+.icloud-item-main {
+  min-width: 0;
+  display: flex;
+  flex-direction: column;
+  gap: 6px;
+}
+
+.icloud-item-actions {
+  display: flex;
+  gap: 6px;
+  flex-wrap: wrap;
+  justify-content: flex-end;
+}
+
+.icloud-item-email {
+  font-weight: 600;
+  color: var(--text-primary);
+  word-break: break-all;
+}
+
+.icloud-item-meta {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 6px;
+}
+
+.icloud-tag {
+  display: inline-flex;
+  align-items: center;
+  padding: 2px 8px;
+  border-radius: 999px;
+  font-size: 11px;
+  background: var(--bg-surface);
+  color: var(--text-secondary);
+}
+
+.icloud-tag.used {
+  background: var(--orange-soft);
+  color: var(--orange);
+}
+
+.icloud-tag.active {
+  background: var(--green-soft);
+  color: var(--green);
+}
+
+.icloud-empty {
+  color: var(--text-muted);
+  font-size: 12px;
+  border: 1px dashed var(--border);
+  border-radius: var(--radius-sm);
+  padding: 12px;
+  text-align: center;
+}
+
 .data-select {
   flex: 1;
   padding: 7px 10px;

+ 61 - 0
sidepanel/sidepanel.html

@@ -169,6 +169,7 @@
         <select id="select-email-generator" class="data-select">
           <option value="duck">DuckDuckGo</option>
           <option value="cloudflare">Cloudflare</option>
+          <option value="icloud">iCloud 隐私邮箱</option>
           <option value="cloudflare-temp-email">Cloudflare Temp Email</option>
         </select>
       </div>
@@ -339,6 +340,66 @@
         <div id="hotmail-accounts-list" class="hotmail-accounts-list"></div>
       </div>
     </div>
+    <div id="icloud-section" class="data-card hotmail-card" style="display:none;">
+      <div class="section-mini-header">
+        <div class="section-mini-copy">
+          <span class="section-label">iCloud 隐私邮箱</span>
+        </div>
+        <div class="section-mini-actions">
+          <button id="btn-icloud-refresh" class="btn btn-ghost btn-xs" type="button">刷新</button>
+          <button id="btn-icloud-delete-used" class="btn btn-ghost btn-xs" type="button">删除已用</button>
+        </div>
+      </div>
+      <div class="data-row">
+        <span class="data-label">iCloud</span>
+        <select id="select-icloud-host-preference" class="data-select">
+          <option value="auto">自动</option>
+          <option value="icloud.com">iCloud.com</option>
+          <option value="icloud.com.cn">iCloud.com.cn</option>
+        </select>
+      </div>
+      <div class="data-row">
+        <span class="data-label">自动删除</span>
+        <label class="option-toggle" for="checkbox-auto-delete-icloud">
+          <input type="checkbox" id="checkbox-auto-delete-icloud" />
+          <span>成功使用后自动删除 iCloud 别名</span>
+        </label>
+      </div>
+      <div class="icloud-card">
+        <div id="icloud-summary" class="icloud-summary">加载你的 iCloud Hide My Email 别名以便在这里管理。</div>
+        <div id="icloud-login-help" class="icloud-login-help" style="display:none;">
+          <div class="icloud-login-help-main">
+            <div id="icloud-login-help-title" class="icloud-login-help-title">需要登录 iCloud</div>
+            <div id="icloud-login-help-text" class="icloud-login-help-text">我已经为你打开 iCloud 登录页。请在那个页面完成登录,然后回到这里点击“我已登录”。</div>
+          </div>
+          <button id="btn-icloud-login-done" class="btn btn-primary btn-xs" type="button">我已登录</button>
+        </div>
+        <div class="icloud-toolbar">
+          <input id="input-icloud-search" class="data-input icloud-search" type="text" placeholder="搜索邮箱 / 标签 / 备注" />
+          <select id="select-icloud-filter" class="data-select icloud-filter">
+            <option value="all">全部</option>
+            <option value="active">可用</option>
+            <option value="used">已用</option>
+            <option value="unused">未用</option>
+            <option value="preserved">保留</option>
+          </select>
+        </div>
+        <div class="icloud-bulkbar">
+          <label class="option-toggle icloud-select-all" for="checkbox-icloud-select-all">
+            <input type="checkbox" id="checkbox-icloud-select-all" />
+            <span id="icloud-selection-summary">已选 0 个</span>
+          </label>
+          <div class="icloud-bulk-actions">
+            <button id="btn-icloud-bulk-used" class="btn btn-outline btn-xs" type="button">标记已用</button>
+            <button id="btn-icloud-bulk-unused" class="btn btn-outline btn-xs" type="button">标记未用</button>
+            <button id="btn-icloud-bulk-preserve" class="btn btn-outline btn-xs" type="button">保留</button>
+            <button id="btn-icloud-bulk-unpreserve" class="btn btn-outline btn-xs" type="button">取消保留</button>
+            <button id="btn-icloud-bulk-delete" class="btn btn-outline btn-xs" type="button">删除</button>
+          </div>
+        </div>
+        <div id="icloud-list" class="icloud-list"></div>
+      </div>
+    </div>
     <div id="status-bar" class="status-bar">
       <div class="status-dot"></div>
       <span id="display-status">就绪</span>

+ 573 - 7
sidepanel/sidepanel.js

@@ -79,6 +79,26 @@ const selectTempEmailDomain = document.getElementById('select-temp-email-domain'
 const inputTempEmailDomain = document.getElementById('input-temp-email-domain');
 const btnTempEmailDomainMode = document.getElementById('btn-temp-email-domain-mode');
 const hotmailSection = document.getElementById('hotmail-section');
+const icloudSection = document.getElementById('icloud-section');
+const icloudSummary = document.getElementById('icloud-summary');
+const icloudList = document.getElementById('icloud-list');
+const icloudLoginHelp = document.getElementById('icloud-login-help');
+const icloudLoginHelpTitle = document.getElementById('icloud-login-help-title');
+const icloudLoginHelpText = document.getElementById('icloud-login-help-text');
+const btnIcloudLoginDone = document.getElementById('btn-icloud-login-done');
+const btnIcloudRefresh = document.getElementById('btn-icloud-refresh');
+const btnIcloudDeleteUsed = document.getElementById('btn-icloud-delete-used');
+const selectIcloudHostPreference = document.getElementById('select-icloud-host-preference');
+const checkboxAutoDeleteIcloud = document.getElementById('checkbox-auto-delete-icloud');
+const inputIcloudSearch = document.getElementById('input-icloud-search');
+const selectIcloudFilter = document.getElementById('select-icloud-filter');
+const checkboxIcloudSelectAll = document.getElementById('checkbox-icloud-select-all');
+const icloudSelectionSummary = document.getElementById('icloud-selection-summary');
+const btnIcloudBulkUsed = document.getElementById('btn-icloud-bulk-used');
+const btnIcloudBulkUnused = document.getElementById('btn-icloud-bulk-unused');
+const btnIcloudBulkPreserve = document.getElementById('btn-icloud-bulk-preserve');
+const btnIcloudBulkUnpreserve = document.getElementById('btn-icloud-bulk-unpreserve');
+const btnIcloudBulkDelete = document.getElementById('btn-icloud-bulk-delete');
 const rowHotmailServiceMode = document.getElementById('row-hotmail-service-mode');
 const hotmailServiceModeButtons = Array.from(document.querySelectorAll('[data-hotmail-service-mode]'));
 const rowHotmailRemoteBaseUrl = document.getElementById('row-hotmail-remote-base-url');
@@ -174,6 +194,11 @@ let settingsSaveInFlight = false;
 let settingsAutoSaveTimer = null;
 let cloudflareDomainEditMode = false;
 let cloudflareTempEmailDomainEditMode = false;
+let icloudRefreshQueued = false;
+let lastRenderedIcloudAliases = [];
+let icloudSelectedEmails = new Set();
+let icloudSearchTerm = '';
+let icloudFilterMode = 'all';
 let modalChoiceResolver = null;
 let currentModalActions = [];
 let modalResultBuilder = null;
@@ -1032,6 +1057,8 @@ function collectSettingsPayload() {
     customPassword: inputPassword.value,
     mailProvider: selectMailProvider.value,
     emailGenerator: selectEmailGenerator.value,
+    autoDeleteUsedIcloudAlias: checkboxAutoDeleteIcloud?.checked,
+    icloudHostPreference: selectIcloudHostPreference?.value || 'auto',
     emailPrefix: inputEmailPrefix.value.trim(),
     inbucketHost: inputInbucketHost.value.trim(),
     inbucketMailbox: inputInbucketMailbox.value.trim(),
@@ -1323,10 +1350,26 @@ function applySettingsState(state) {
       ? 'custom'
       : '163');
   selectMailProvider.value = restoredMailProvider;
-  const restoredEmailGenerator = String(state?.emailGenerator || '').trim().toLowerCase();
-  selectEmailGenerator.value = ['cloudflare', 'cloudflare-temp-email'].includes(restoredEmailGenerator)
-    ? restoredEmailGenerator
-    : 'duck';
+  {
+    const restoredGenerator = String(state?.emailGenerator || '').trim().toLowerCase();
+    if (restoredGenerator === 'icloud') {
+      selectEmailGenerator.value = 'icloud';
+    } else if (restoredGenerator === 'cloudflare') {
+      selectEmailGenerator.value = 'cloudflare';
+    } else if (restoredGenerator === 'cloudflare-temp-email') {
+      selectEmailGenerator.value = 'cloudflare-temp-email';
+    } else {
+      selectEmailGenerator.value = 'duck';
+    }
+  }
+  if (selectIcloudHostPreference) {
+    selectIcloudHostPreference.value = String(state?.icloudHostPreference || '').trim().toLowerCase() === 'icloud.com'
+      ? 'icloud.com'
+      : (String(state?.icloudHostPreference || '').trim().toLowerCase() === 'icloud.com.cn' ? 'icloud.com.cn' : 'auto');
+  }
+  if (checkboxAutoDeleteIcloud) {
+    checkboxAutoDeleteIcloud.checked = Boolean(state?.autoDeleteUsedIcloudAlias);
+  }
   inputEmailPrefix.value = state?.emailPrefix || '';
   inputInbucketHost.value = state?.inbucketHost || '';
   inputInbucketMailbox.value = state?.inbucketMailbox || '';
@@ -1362,6 +1405,9 @@ async function restoreState() {
   try {
     const state = await chrome.runtime.sendMessage({ type: 'GET_STATE', source: 'sidepanel' });
     applySettingsState(state);
+    if (getSelectedEmailGenerator() === 'icloud' && icloudSection?.style.display !== 'none') {
+      refreshIcloudAliases({ silent: true }).catch(() => { });
+    }
 
     if (state.oauthUrl) {
       displayOauthUrl.textContent = state.oauthUrl;
@@ -1609,6 +1655,9 @@ function getSelectedEmailGenerator() {
   if (generator === 'custom' || generator === 'manual') {
     return 'custom';
   }
+  if (generator === 'icloud') {
+    return 'icloud';
+  }
   if (generator === 'cloudflare') return 'cloudflare';
   if (generator === 'cloudflare-temp-email') return 'cloudflare-temp-email';
   return 'duck';
@@ -1618,6 +1667,14 @@ function getEmailGeneratorUiCopy() {
   if (getSelectedEmailGenerator() === 'custom') {
     return getCustomMailProviderUiCopy();
   }
+  if (getSelectedEmailGenerator() === 'icloud') {
+    return {
+      buttonLabel: '获取',
+      placeholder: '点击获取 iCloud 隐私邮箱,或手动粘贴邮箱',
+      successVerb: '获取',
+      label: 'iCloud 隐私邮箱',
+    };
+  }
   if (getSelectedEmailGenerator() === 'cloudflare') {
     return {
       buttonLabel: '生成',
@@ -1946,14 +2003,26 @@ function updateMailProviderUI() {
   const hotmailServiceMode = getSelectedHotmailServiceMode();
   rowInbucketHost.style.display = useInbucket ? '' : 'none';
   rowInbucketMailbox.style.display = useInbucket ? '' : 'none';
-  const useCloudflare = selectEmailGenerator.value === 'cloudflare';
-  const useCloudflareTempEmailGenerator = selectEmailGenerator.value === 'cloudflare-temp-email';
+  const selectedGenerator = getSelectedEmailGenerator();
+  const useCloudflare = selectedGenerator === 'cloudflare';
+  const useIcloud = selectedGenerator === 'icloud';
+  const useCloudflareTempEmailGenerator = selectedGenerator === 'cloudflare-temp-email';
   const showCloudflareDomain = useEmailGenerator && useCloudflare;
   const showCloudflareTempEmailSettings = useCloudflareTempEmailProvider || (useEmailGenerator && useCloudflareTempEmailGenerator);
   const showCloudflareTempEmailDomain = useEmailGenerator && useCloudflareTempEmailGenerator;
   if (rowEmailGenerator) {
     rowEmailGenerator.style.display = useEmailGenerator ? '' : 'none';
   }
+  if (icloudSection) {
+    const showIcloudSection = useEmailGenerator && useIcloud;
+    icloudSection.style.display = showIcloudSection ? '' : 'none';
+    if (showIcloudSection && !lastRenderedIcloudAliases.length) {
+      queueIcloudAliasRefresh();
+    }
+    if (!showIcloudSection) {
+      hideIcloudLoginHelp();
+    }
+  }
   rowCfDomain.style.display = showCloudflareDomain ? '' : 'none';
   const { domains } = getCloudflareDomainsFromState();
   if (showCloudflareDomain) {
@@ -2002,7 +2071,7 @@ function updateMailProviderUI() {
       ? '请先校验并选择一个 Hotmail 账号'
       : (useGeneratedAlias
         ? '步骤 3 会自动生成邮箱,无需手动获取'
-        : (useCustomEmail ? '请先填写自定义注册邮箱,成功一轮后会自动清空' : '先自动获取邮箱,或手动粘贴邮箱后再继续'));
+        : (useCustomEmail ? '请先填写自定义注册邮箱,成功一轮后会自动清空' : `先自动获取${uiCopy.label},或手动粘贴邮箱后再继续`));
   }
   if (useHotmail) {
     inputEmail.value = getCurrentHotmailEmail();
@@ -2166,6 +2235,11 @@ function updateButtonStates() {
   });
 
   btnReset.disabled = anyRunning || autoScheduled || isAutoRunPausedPhase() || autoLocked;
+  const disableIcloudControls = anyRunning || autoScheduled || autoLocked;
+  if (btnIcloudRefresh) btnIcloudRefresh.disabled = disableIcloudControls;
+  if (btnIcloudDeleteUsed) btnIcloudDeleteUsed.disabled = disableIcloudControls || !(lastRenderedIcloudAliases.some((alias) => alias.used && !alias.preserved));
+  if (selectIcloudHostPreference) selectIcloudHostPreference.disabled = disableIcloudControls;
+  if (checkboxAutoDeleteIcloud) checkboxAutoDeleteIcloud.disabled = disableIcloudControls;
   updateStopButtonState(anyRunning || autoScheduled || isAutoRunPausedPhase() || autoLocked);
 }
 
@@ -2288,6 +2362,375 @@ function escapeHtml(text) {
   return div.innerHTML;
 }
 
+function normalizeIcloudSearchText(value) {
+  return String(value || '').trim().toLowerCase();
+}
+
+function getFilteredIcloudAliases(aliases = lastRenderedIcloudAliases) {
+  const searchTerm = normalizeIcloudSearchText(icloudSearchTerm);
+  return (Array.isArray(aliases) ? aliases : []).filter((alias) => {
+    const matchesFilter = (() => {
+      switch (icloudFilterMode) {
+        case 'active': return Boolean(alias.active);
+        case 'used': return Boolean(alias.used);
+        case 'unused': return !alias.used;
+        case 'preserved': return Boolean(alias.preserved);
+        default: return true;
+      }
+    })();
+
+    if (!matchesFilter) return false;
+    if (!searchTerm) return true;
+
+    const haystack = [
+      alias.email,
+      alias.label,
+      alias.note,
+      alias.used ? '已用 used' : '未用 unused',
+      alias.active ? '可用 active' : '不可用 inactive',
+      alias.preserved ? '保留 preserved' : '',
+    ].join(' ').toLowerCase();
+
+    return haystack.includes(searchTerm);
+  });
+}
+
+function pruneIcloudSelection(aliases = lastRenderedIcloudAliases) {
+  const existing = new Set((Array.isArray(aliases) ? aliases : []).map((alias) => alias.email));
+  icloudSelectedEmails = new Set([...icloudSelectedEmails].filter((email) => existing.has(email)));
+}
+
+function updateIcloudBulkUI(visibleAliases = getFilteredIcloudAliases()) {
+  if (!checkboxIcloudSelectAll || !icloudSelectionSummary) {
+    return;
+  }
+
+  const visibleEmails = visibleAliases.map((alias) => alias.email);
+  const selectedVisibleCount = visibleEmails.filter((email) => icloudSelectedEmails.has(email)).length;
+  const hasVisible = visibleEmails.length > 0;
+
+  checkboxIcloudSelectAll.checked = hasVisible && selectedVisibleCount === visibleEmails.length;
+  checkboxIcloudSelectAll.indeterminate = selectedVisibleCount > 0 && selectedVisibleCount < visibleEmails.length;
+  checkboxIcloudSelectAll.disabled = !hasVisible;
+  icloudSelectionSummary.textContent = `已选 ${icloudSelectedEmails.size} 个(当前显示 ${visibleEmails.length} 个)`;
+
+  const hasSelection = icloudSelectedEmails.size > 0;
+  if (btnIcloudBulkUsed) btnIcloudBulkUsed.disabled = !hasSelection;
+  if (btnIcloudBulkUnused) btnIcloudBulkUnused.disabled = !hasSelection;
+  if (btnIcloudBulkPreserve) btnIcloudBulkPreserve.disabled = !hasSelection;
+  if (btnIcloudBulkUnpreserve) btnIcloudBulkUnpreserve.disabled = !hasSelection;
+  if (btnIcloudBulkDelete) btnIcloudBulkDelete.disabled = !hasSelection;
+}
+
+function setIcloudLoadingState(loading, summary = '') {
+  if (btnIcloudRefresh) btnIcloudRefresh.disabled = loading;
+  if (btnIcloudDeleteUsed) btnIcloudDeleteUsed.disabled = loading;
+  if (btnIcloudLoginDone) btnIcloudLoginDone.disabled = loading;
+  if (inputIcloudSearch) inputIcloudSearch.disabled = loading;
+  if (selectIcloudFilter) selectIcloudFilter.disabled = loading;
+  if (checkboxIcloudSelectAll) checkboxIcloudSelectAll.disabled = loading || getFilteredIcloudAliases().length === 0;
+  if (btnIcloudBulkUsed) btnIcloudBulkUsed.disabled = loading || icloudSelectedEmails.size === 0;
+  if (btnIcloudBulkUnused) btnIcloudBulkUnused.disabled = loading || icloudSelectedEmails.size === 0;
+  if (btnIcloudBulkPreserve) btnIcloudBulkPreserve.disabled = loading || icloudSelectedEmails.size === 0;
+  if (btnIcloudBulkUnpreserve) btnIcloudBulkUnpreserve.disabled = loading || icloudSelectedEmails.size === 0;
+  if (btnIcloudBulkDelete) btnIcloudBulkDelete.disabled = loading || icloudSelectedEmails.size === 0;
+  if (summary && icloudSummary) icloudSummary.textContent = summary;
+}
+
+function showIcloudLoginHelp(payload = {}) {
+  if (!icloudLoginHelp) return;
+  const loginUrl = String(payload.loginUrl || '').trim();
+  const host = loginUrl ? new URL(loginUrl).host : 'icloud.com.cn / icloud.com';
+  if (icloudLoginHelpTitle) icloudLoginHelpTitle.textContent = '需要登录 iCloud';
+  if (icloudLoginHelpText) icloudLoginHelpText.textContent = `我已经为你打开 ${host}。请在那个页面完成登录,然后回到这里点击“我已登录”。`;
+  icloudLoginHelp.style.display = 'flex';
+}
+
+function hideIcloudLoginHelp() {
+  if (icloudLoginHelp) {
+    icloudLoginHelp.style.display = 'none';
+  }
+}
+
+function renderIcloudAliases(aliases = []) {
+  if (!icloudList || !icloudSummary) return;
+
+  lastRenderedIcloudAliases = Array.isArray(aliases) ? aliases : [];
+  pruneIcloudSelection(lastRenderedIcloudAliases);
+  icloudList.innerHTML = '';
+
+  if (!aliases.length) {
+    icloudSelectedEmails.clear();
+    icloudList.innerHTML = '<div class="icloud-empty">未找到 iCloud Hide My Email 别名。</div>';
+    icloudSummary.textContent = '加载你的 iCloud Hide My Email 别名以便在这里管理。';
+    if (btnIcloudDeleteUsed) btnIcloudDeleteUsed.disabled = true;
+    updateIcloudBulkUI([]);
+    return;
+  }
+
+  const usedCount = aliases.filter((alias) => alias.used).length;
+  const deletableUsedCount = aliases.filter((alias) => alias.used && !alias.preserved).length;
+  icloudSummary.textContent = `已加载 ${aliases.length} 个别名,其中 ${usedCount} 个已标记为已用。`;
+  if (btnIcloudDeleteUsed) btnIcloudDeleteUsed.disabled = deletableUsedCount === 0;
+
+  const visibleAliases = getFilteredIcloudAliases(aliases);
+  if (!visibleAliases.length) {
+    icloudList.innerHTML = '<div class="icloud-empty">没有匹配当前筛选条件的别名。</div>';
+    updateIcloudBulkUI([]);
+    return;
+  }
+
+  for (const alias of visibleAliases) {
+    const item = document.createElement('div');
+    item.className = 'icloud-item';
+    item.innerHTML = `
+      <input class="icloud-item-check" type="checkbox" data-action="select" ${icloudSelectedEmails.has(alias.email) ? 'checked' : ''} />
+      <div class="icloud-item-main">
+        <div class="icloud-item-email">${escapeHtml(alias.email)}</div>
+        <div class="icloud-item-meta">
+          ${alias.used ? '<span class="icloud-tag used">已用</span>' : ''}
+          ${!alias.used && alias.active ? '<span class="icloud-tag active">可用</span>' : ''}
+          ${alias.preserved ? '<span class="icloud-tag">保留</span>' : ''}
+          ${alias.label ? `<span class="icloud-tag">${escapeHtml(alias.label)}</span>` : ''}
+          ${alias.note ? `<span class="icloud-tag">${escapeHtml(alias.note)}</span>` : ''}
+        </div>
+      </div>
+      <div class="icloud-item-actions">
+        <button class="btn btn-outline btn-xs" type="button" data-action="toggle-used">${escapeHtml(alias.used ? '标记未用' : '标记已用')}</button>
+        <button class="btn btn-outline btn-xs" type="button" data-action="toggle-preserved">${escapeHtml(alias.preserved ? '取消保留' : '保留')}</button>
+        <button class="btn btn-outline btn-xs" type="button" data-action="delete">删除</button>
+      </div>
+    `;
+
+    item.querySelector('[data-action="select"]').addEventListener('change', (event) => {
+      if (event.target.checked) {
+        icloudSelectedEmails.add(alias.email);
+      } else {
+        icloudSelectedEmails.delete(alias.email);
+      }
+      updateIcloudBulkUI(visibleAliases);
+    });
+    item.querySelector('[data-action="toggle-used"]').addEventListener('click', async () => {
+      await setSingleIcloudAliasUsedState(alias, !alias.used);
+    });
+    item.querySelector('[data-action="toggle-preserved"]').addEventListener('click', async () => {
+      await setSingleIcloudAliasPreservedState(alias, !alias.preserved);
+    });
+    item.querySelector('[data-action="delete"]').addEventListener('click', async () => {
+      await deleteSingleIcloudAlias(alias);
+    });
+    icloudList.appendChild(item);
+  }
+
+  updateIcloudBulkUI(visibleAliases);
+}
+
+async function refreshIcloudAliases(options = {}) {
+  const { silent = false } = options;
+  if (!icloudSection || icloudSection.style.display === 'none') {
+    return;
+  }
+
+  if (!silent) setIcloudLoadingState(true, '正在加载 iCloud 别名...');
+  try {
+    const response = await chrome.runtime.sendMessage({
+      type: 'LIST_ICLOUD_ALIASES',
+      source: 'sidepanel',
+      payload: {},
+    });
+    if (response?.error) throw new Error(response.error);
+    hideIcloudLoginHelp();
+    renderIcloudAliases(response?.aliases || []);
+  } catch (err) {
+    icloudSelectedEmails.clear();
+    if (icloudList) {
+      icloudList.innerHTML = '<div class="icloud-empty">无法加载 iCloud 别名。</div>';
+    }
+    if (icloudSummary) {
+      icloudSummary.textContent = err.message;
+    }
+    updateIcloudBulkUI([]);
+    if (!silent) showToast(`iCloud 别名加载失败:${err.message}`, 'error');
+  } finally {
+    setIcloudLoadingState(false);
+  }
+}
+
+function queueIcloudAliasRefresh() {
+  if (icloudRefreshQueued) return;
+  icloudRefreshQueued = true;
+  setTimeout(async () => {
+    icloudRefreshQueued = false;
+    await refreshIcloudAliases({ silent: true });
+  }, 150);
+}
+
+async function deleteSingleIcloudAlias(alias) {
+  const confirmed = await openConfirmModal({
+    title: '删除 iCloud 别名',
+    message: `确认删除 ${alias.email} 吗?此操作不可撤销。`,
+    confirmLabel: '确认删除',
+    confirmVariant: 'btn-danger',
+  });
+  if (!confirmed) {
+    return;
+  }
+
+  setIcloudLoadingState(true, `正在删除 ${alias.email} ...`);
+  try {
+    const response = await chrome.runtime.sendMessage({
+      type: 'DELETE_ICLOUD_ALIAS',
+      source: 'sidepanel',
+      payload: { email: alias.email, anonymousId: alias.anonymousId },
+    });
+    if (response?.error) throw new Error(response.error);
+    showToast(`已删除 ${alias.email}`, 'success', 2200);
+    await refreshIcloudAliases({ silent: true });
+  } catch (err) {
+    if (icloudSummary) icloudSummary.textContent = err.message;
+    showToast(`删除 iCloud 别名失败:${err.message}`, 'error');
+  } finally {
+    setIcloudLoadingState(false);
+  }
+}
+
+async function setSingleIcloudAliasUsedState(alias, used) {
+  setIcloudLoadingState(true, `正在更新 ${alias.email} 的使用状态...`);
+  try {
+    const response = await chrome.runtime.sendMessage({
+      type: 'SET_ICLOUD_ALIAS_USED_STATE',
+      source: 'sidepanel',
+      payload: { email: alias.email, used },
+    });
+    if (response?.error) throw new Error(response.error);
+    showToast(`${alias.email} 已${used ? '标记为已用' : '恢复为未用'}`, 'success', 2200);
+    await refreshIcloudAliases({ silent: true });
+  } catch (err) {
+    if (icloudSummary) icloudSummary.textContent = err.message;
+    showToast(`更新 iCloud 使用状态失败:${err.message}`, 'error');
+  } finally {
+    setIcloudLoadingState(false);
+  }
+}
+
+async function setSingleIcloudAliasPreservedState(alias, preserved) {
+  setIcloudLoadingState(true, `正在更新 ${alias.email} 的保留状态...`);
+  try {
+    const response = await chrome.runtime.sendMessage({
+      type: 'SET_ICLOUD_ALIAS_PRESERVED_STATE',
+      source: 'sidepanel',
+      payload: { email: alias.email, preserved },
+    });
+    if (response?.error) throw new Error(response.error);
+    showToast(`${alias.email} 已${preserved ? '设为保留' : '取消保留'}`, 'success', 2200);
+    await refreshIcloudAliases({ silent: true });
+  } catch (err) {
+    if (icloudSummary) icloudSummary.textContent = err.message;
+    showToast(`更新 iCloud 保留状态失败:${err.message}`, 'error');
+  } finally {
+    setIcloudLoadingState(false);
+  }
+}
+
+async function runBulkIcloudAction(action) {
+  const selectedAliases = lastRenderedIcloudAliases.filter((alias) => icloudSelectedEmails.has(alias.email));
+  if (!selectedAliases.length) {
+    updateIcloudBulkUI();
+    return;
+  }
+
+  if (action === 'delete') {
+    const confirmed = await openConfirmModal({
+      title: '批量删除 iCloud 别名',
+      message: `确认删除选中的 ${selectedAliases.length} 个 iCloud 别名吗?此操作不可撤销。`,
+      confirmLabel: '确认删除',
+      confirmVariant: 'btn-danger',
+    });
+    if (!confirmed) {
+      return;
+    }
+  }
+
+  const actionLabelMap = {
+    used: '标记已用',
+    unused: '标记未用',
+    preserve: '保留',
+    unpreserve: '取消保留',
+    delete: '删除',
+  };
+  setIcloudLoadingState(true, `正在批量${actionLabelMap[action] || '处理'} iCloud 别名...`);
+
+  try {
+    for (const alias of selectedAliases) {
+      let response = null;
+      if (action === 'used' || action === 'unused') {
+        response = await chrome.runtime.sendMessage({
+          type: 'SET_ICLOUD_ALIAS_USED_STATE',
+          source: 'sidepanel',
+          payload: { email: alias.email, used: action === 'used' },
+        });
+      } else if (action === 'preserve' || action === 'unpreserve') {
+        response = await chrome.runtime.sendMessage({
+          type: 'SET_ICLOUD_ALIAS_PRESERVED_STATE',
+          source: 'sidepanel',
+          payload: { email: alias.email, preserved: action === 'preserve' },
+        });
+      } else if (action === 'delete') {
+        response = await chrome.runtime.sendMessage({
+          type: 'DELETE_ICLOUD_ALIAS',
+          source: 'sidepanel',
+          payload: { email: alias.email, anonymousId: alias.anonymousId },
+        });
+        icloudSelectedEmails.delete(alias.email);
+      }
+
+      if (response?.error) {
+        throw new Error(response.error);
+      }
+    }
+
+    showToast(`已批量${actionLabelMap[action] || '处理'} ${selectedAliases.length} 个 iCloud 别名`, 'success', 2400);
+    await refreshIcloudAliases({ silent: true });
+  } catch (err) {
+    if (icloudSummary) icloudSummary.textContent = err.message;
+    showToast(`批量处理 iCloud 别名失败:${err.message}`, 'error');
+  } finally {
+    setIcloudLoadingState(false);
+    updateIcloudBulkUI();
+  }
+}
+
+async function deleteUsedIcloudAliases() {
+  const confirmed = await openConfirmModal({
+    title: '删除已用 iCloud 别名',
+    message: '确认删除所有未保留的已用 iCloud 别名吗?此操作不可撤销。',
+    confirmLabel: '确认删除',
+    confirmVariant: 'btn-danger',
+  });
+  if (!confirmed) {
+    return;
+  }
+
+  setIcloudLoadingState(true, '正在删除已用 iCloud 别名...');
+  try {
+    const response = await chrome.runtime.sendMessage({
+      type: 'DELETE_USED_ICLOUD_ALIASES',
+      source: 'sidepanel',
+      payload: {},
+    });
+    if (response?.error) throw new Error(response.error);
+    const deleted = response?.deleted || [];
+    const skipped = response?.skipped || [];
+    showToast(`已删除 ${deleted.length} 个已用别名,跳过 ${skipped.length} 个`, skipped.length ? 'warn' : 'success', 2800);
+    await refreshIcloudAliases({ silent: true });
+  } catch (err) {
+    if (icloudSummary) icloudSummary.textContent = err.message;
+    showToast(`删除已用 iCloud 别名失败:${err.message}`, 'error');
+  } finally {
+    setIcloudLoadingState(false);
+  }
+}
+
 async function fetchGeneratedEmail(options = {}) {
   const { showFailureToast = true } = options;
   const uiCopy = getEmailGeneratorUiCopy();
@@ -2316,6 +2759,9 @@ async function fetchGeneratedEmail(options = {}) {
     }
 
     inputEmail.value = response.email;
+    if (getSelectedEmailGenerator() === 'icloud') {
+      queueIcloudAliasRefresh();
+    }
     showToast(`已${uiCopy.successVerb} ${uiCopy.label}:${response.email}`, 'success', 2500);
     return response.email;
   } catch (err) {
@@ -2628,6 +3074,75 @@ btnFetchEmail.addEventListener('click', async () => {
   await fetchGeneratedEmail().catch(() => { });
 });
 
+btnIcloudRefresh?.addEventListener('click', async () => {
+  await refreshIcloudAliases();
+});
+
+btnIcloudDeleteUsed?.addEventListener('click', async () => {
+  await deleteUsedIcloudAliases();
+});
+
+inputIcloudSearch?.addEventListener('input', () => {
+  icloudSearchTerm = inputIcloudSearch.value || '';
+  renderIcloudAliases(lastRenderedIcloudAliases);
+});
+
+selectIcloudFilter?.addEventListener('change', () => {
+  icloudFilterMode = selectIcloudFilter.value || 'all';
+  renderIcloudAliases(lastRenderedIcloudAliases);
+});
+
+checkboxIcloudSelectAll?.addEventListener('change', () => {
+  const visibleAliases = getFilteredIcloudAliases();
+  if (checkboxIcloudSelectAll.checked) {
+    visibleAliases.forEach((alias) => icloudSelectedEmails.add(alias.email));
+  } else {
+    visibleAliases.forEach((alias) => icloudSelectedEmails.delete(alias.email));
+  }
+  renderIcloudAliases(lastRenderedIcloudAliases);
+});
+
+btnIcloudBulkUsed?.addEventListener('click', async () => {
+  await runBulkIcloudAction('used');
+});
+
+btnIcloudBulkUnused?.addEventListener('click', async () => {
+  await runBulkIcloudAction('unused');
+});
+
+btnIcloudBulkPreserve?.addEventListener('click', async () => {
+  await runBulkIcloudAction('preserve');
+});
+
+btnIcloudBulkUnpreserve?.addEventListener('click', async () => {
+  await runBulkIcloudAction('unpreserve');
+});
+
+btnIcloudBulkDelete?.addEventListener('click', async () => {
+  await runBulkIcloudAction('delete');
+});
+
+btnIcloudLoginDone?.addEventListener('click', async () => {
+  btnIcloudLoginDone.disabled = true;
+  try {
+    const response = await chrome.runtime.sendMessage({
+      type: 'CHECK_ICLOUD_SESSION',
+      source: 'sidepanel',
+      payload: {},
+    });
+    if (response?.error) {
+      throw new Error(response.error);
+    }
+    hideIcloudLoginHelp();
+    showToast('iCloud 会话已恢复,别名列表已刷新。', 'success', 2600);
+    await refreshIcloudAliases({ silent: true });
+  } catch (err) {
+    showToast(`看起来还没有登录完成:${err.message}`, 'warn', 4200);
+  } finally {
+    btnIcloudLoginDone.disabled = false;
+  }
+});
+
 btnToggleHotmailList?.addEventListener('click', () => {
   setHotmailListExpanded(!hotmailListExpanded);
 });
@@ -3106,6 +3621,15 @@ btnReset.addEventListener('click', async () => {
   displayStatus.textContent = '就绪';
   statusBar.className = 'status-bar';
   logArea.innerHTML = '';
+  icloudSelectedEmails.clear();
+  lastRenderedIcloudAliases = [];
+  if (icloudList) {
+    icloudList.innerHTML = '';
+  }
+  if (icloudSummary) {
+    icloudSummary.textContent = '加载你的 iCloud Hide My Email 别名以便在这里管理。';
+  }
+  updateIcloudBulkUI([]);
   document.querySelectorAll('.step-row').forEach(row => row.className = 'step-row');
   document.querySelectorAll('.step-status').forEach(el => el.textContent = '');
   setDefaultAutoRunButton();
@@ -3202,6 +3726,19 @@ selectEmailGenerator.addEventListener('change', () => {
   saveSettings({ silent: true }).catch(() => { });
 });
 
+selectIcloudHostPreference?.addEventListener('change', () => {
+  markSettingsDirty(true);
+  saveSettings({ silent: true }).catch(() => { });
+  if (getSelectedEmailGenerator() === 'icloud') {
+    queueIcloudAliasRefresh();
+  }
+});
+
+checkboxAutoDeleteIcloud?.addEventListener('change', () => {
+  markSettingsDirty(true);
+  saveSettings({ silent: true }).catch(() => { });
+});
+
 selectPanelMode.addEventListener('change', () => {
   updatePanelModeUI();
   markSettingsDirty(true);
@@ -3502,6 +4039,11 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
       displayStatus.textContent = '就绪';
       statusBar.className = 'status-bar';
       logArea.innerHTML = '';
+      icloudSelectedEmails.clear();
+      lastRenderedIcloudAliases = [];
+      if (icloudList) icloudList.innerHTML = '';
+      if (icloudSummary) icloudSummary.textContent = '加载你的 iCloud Hide My Email 别名以便在这里管理。';
+      updateIcloudBulkUI([]);
       document.querySelectorAll('.step-row').forEach(row => row.className = 'step-row');
       document.querySelectorAll('.step-status').forEach(el => el.textContent = '');
       syncAutoRunState({
@@ -3559,6 +4101,15 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
           inputEmail.value = getCurrentHotmailEmail();
         }
       }
+      if (message.payload.autoDeleteUsedIcloudAlias !== undefined && checkboxAutoDeleteIcloud) {
+        checkboxAutoDeleteIcloud.checked = Boolean(message.payload.autoDeleteUsedIcloudAlias);
+      }
+      if (message.payload.icloudHostPreference !== undefined && selectIcloudHostPreference) {
+        const hostPreference = String(message.payload.icloudHostPreference || '').trim().toLowerCase();
+        selectIcloudHostPreference.value = hostPreference === 'icloud.com'
+          ? 'icloud.com'
+          : (hostPreference === 'icloud.com.cn' ? 'icloud.com.cn' : 'auto');
+      }
       if (message.payload.autoRunSkipFailures !== undefined) {
         inputAutoSkipFailures.checked = Boolean(message.payload.autoRunSkipFailures);
         updateFallbackThreadIntervalInputState();
@@ -3582,6 +4133,21 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
       break;
     }
 
+    case 'ICLOUD_LOGIN_REQUIRED': {
+      const loginMessage = '需要登录 iCloud,我已经为你打开登录页。';
+      showToast(loginMessage, 'warn', 5000);
+      if (icloudSummary) {
+        icloudSummary.textContent = loginMessage;
+      }
+      showIcloudLoginHelp(message.payload || {});
+      break;
+    }
+
+    case 'ICLOUD_ALIASES_CHANGED': {
+      queueIcloudAliasRefresh();
+      break;
+    }
+
     case 'AUTO_RUN_STATUS': {
       syncLatestState({
         autoRunning: ['scheduled', 'running', 'waiting_step', 'waiting_email', 'retrying', 'waiting_interval'].includes(message.payload.phase),

+ 14 - 0
tests/auto-run-fresh-attempt-reset.test.js

@@ -189,6 +189,20 @@ function cancelPendingCommands() {}
 function normalizeAutoRunFallbackThreadIntervalMinutes(value) {
   return Math.max(0, Math.floor(Number(value) || 0));
 }
+function buildAutoRunRoundSummaries(totalRuns, rawSummaries = []) {
+  return Array.from({ length: totalRuns }, (_, index) => ({
+    round: index + 1,
+    status: rawSummaries[index]?.status || 'pending',
+    attempts: rawSummaries[index]?.attempts || 0,
+    failureReasons: [...(rawSummaries[index]?.failureReasons || [])],
+    finalFailureReason: rawSummaries[index]?.finalFailureReason || '',
+  }));
+}
+function serializeAutoRunRoundSummaries(totalRuns, roundSummaries = []) {
+  return buildAutoRunRoundSummaries(totalRuns, roundSummaries);
+}
+async function logAutoRunFinalSummary() {}
+async function waitBetweenAutoRunRounds() {}
 
 const chrome = {
   runtime: {

+ 265 - 0
tests/background-icloud.test.js

@@ -0,0 +1,265 @@
+const test = require('node:test');
+const assert = require('node:assert/strict');
+const fs = require('fs');
+
+const source = fs.readFileSync('background.js', 'utf8');
+
+function extractFunction(name) {
+  const markers = [`async function ${name}(`, `function ${name}(`];
+  const start = markers
+    .map((marker) => source.indexOf(marker))
+    .find((index) => index >= 0);
+  if (start < 0) {
+    throw new Error(`missing function ${name}`);
+  }
+
+  let parenDepth = 0;
+  let signatureEnded = false;
+  let braceStart = -1;
+  for (let i = start; i < source.length; i += 1) {
+    const ch = source[i];
+    if (ch === '(') {
+      parenDepth += 1;
+    } else if (ch === ')') {
+      parenDepth -= 1;
+      if (parenDepth === 0) {
+        signatureEnded = true;
+      }
+    } else if (ch === '{' && signatureEnded) {
+      braceStart = i;
+      break;
+    }
+  }
+  if (braceStart < 0) {
+    throw new Error(`missing body for function ${name}`);
+  }
+
+  let depth = 0;
+  let end = braceStart;
+  for (; end < source.length; end += 1) {
+    const ch = source[end];
+    if (ch === '{') depth += 1;
+    if (ch === '}') {
+      depth -= 1;
+      if (depth === 0) {
+        end += 1;
+        break;
+      }
+    }
+  }
+
+  return source.slice(start, end);
+}
+
+const bundle = [
+  extractFunction('normalizeEmailGenerator'),
+  extractFunction('getEmailGeneratorLabel'),
+  extractFunction('normalizePersistentSettingValue'),
+  extractFunction('finalizeIcloudAliasAfterSuccessfulFlow'),
+].join('\n');
+
+function createApi(overrides = {}) {
+  return new Function('overrides', `
+const HOTMAIL_PROVIDER = 'hotmail-api';
+const HOTMAIL_SERVICE_MODE_LOCAL = 'local';
+const CLOUDFLARE_TEMP_EMAIL_GENERATOR = 'cloudflare-temp-email';
+const DEFAULT_LOCAL_CPA_STEP9_MODE = 'submit';
+const DEFAULT_HOTMAIL_REMOTE_BASE_URL = '';
+const DEFAULT_HOTMAIL_LOCAL_BASE_URL = 'http://127.0.0.1:17373';
+const PERSISTED_SETTING_DEFAULTS = {
+  mailProvider: '163',
+  autoStepDelaySeconds: null,
+};
+
+const calls = {
+  setUsed: [],
+  logs: [],
+  deletes: [],
+  listCalls: 0,
+};
+
+function normalizeIcloudHost(value) {
+  const normalized = String(value || '').trim().toLowerCase();
+  return ['icloud.com', 'icloud.com.cn'].includes(normalized) ? normalized : '';
+}
+function normalizePanelMode(value = '') {
+  return String(value || '').trim().toLowerCase() === 'sub2api' ? 'sub2api' : 'cpa';
+}
+function normalizeMailProvider(value = '') {
+  return String(value || '').trim().toLowerCase() || '163';
+}
+function normalizeAutoRunFallbackThreadIntervalMinutes(value) {
+  return Math.max(0, Math.floor(Number(value) || 0));
+}
+function normalizeAutoRunDelayMinutes(value) {
+  return Math.max(1, Math.floor(Number(value) || 30));
+}
+function normalizeAutoStepDelaySeconds(value, fallback = null) {
+  const numeric = Number(value);
+  return Number.isFinite(numeric) ? Math.max(0, Math.floor(numeric)) : fallback;
+}
+function normalizeHotmailServiceMode() {
+  return HOTMAIL_SERVICE_MODE_LOCAL;
+}
+function normalizeHotmailRemoteBaseUrl(value = '') {
+  return String(value || '').trim() || DEFAULT_HOTMAIL_REMOTE_BASE_URL;
+}
+function normalizeHotmailLocalBaseUrl(value = '') {
+  return String(value || '').trim() || DEFAULT_HOTMAIL_LOCAL_BASE_URL;
+}
+function normalizeCloudflareDomain(value = '') {
+  return String(value || '').trim().toLowerCase();
+}
+function normalizeCloudflareDomains(values = []) {
+  return Array.isArray(values) ? values : [];
+}
+function normalizeHotmailAccounts(values = []) {
+  return Array.isArray(values) ? values : [];
+}
+function getManualAliasUsageMap(state) {
+  return { ...(state?.manualAliasUsage || {}) };
+}
+function getPreservedAliasMap(state) {
+  return { ...(state?.preservedAliases || {}) };
+}
+function isAliasPreserved(state, email) {
+  return Boolean(getPreservedAliasMap(state)[String(email || '').trim().toLowerCase()]);
+}
+async function setIcloudAliasUsedState(payload, options = {}) {
+  calls.setUsed.push({ payload, options });
+}
+async function addLog(message, level = 'info') {
+  calls.logs.push({ message, level });
+}
+async function deleteIcloudAlias(alias) {
+  calls.deletes.push(alias);
+}
+async function listIcloudAliases() {
+  calls.listCalls += 1;
+  return overrides.listIcloudAliases ? overrides.listIcloudAliases() : [];
+}
+function findIcloudAliasByEmail(aliases, email) {
+  return (aliases || []).find((alias) => String(alias.email || '').toLowerCase() === String(email || '').toLowerCase()) || null;
+}
+function getErrorMessage(error) {
+  return String(typeof error === 'string' ? error : error?.message || '');
+}
+
+${bundle}
+
+return {
+  calls,
+  normalizeEmailGenerator,
+  getEmailGeneratorLabel,
+  normalizePersistentSettingValue,
+  finalizeIcloudAliasAfterSuccessfulFlow,
+};
+`)(overrides);
+}
+
+test('normalizeEmailGenerator and label support icloud', () => {
+  const api = createApi();
+  assert.equal(api.normalizeEmailGenerator('icloud'), 'icloud');
+  assert.equal(api.getEmailGeneratorLabel('icloud'), 'iCloud 隐私邮箱');
+});
+
+test('normalizePersistentSettingValue handles icloud settings', () => {
+  const api = createApi();
+  assert.equal(api.normalizePersistentSettingValue('icloudHostPreference', 'icloud.com'), 'icloud.com');
+  assert.equal(api.normalizePersistentSettingValue('icloudHostPreference', 'bad-host'), 'auto');
+  assert.equal(api.normalizePersistentSettingValue('autoDeleteUsedIcloudAlias', 1), true);
+});
+
+test('finalizeIcloudAliasAfterSuccessfulFlow marks icloud aliases as used without deleting when auto-delete is off', async () => {
+  const api = createApi();
+  const result = await api.finalizeIcloudAliasAfterSuccessfulFlow({
+    email: 'alias@icloud.com',
+    emailGenerator: 'icloud',
+    autoDeleteUsedIcloudAlias: false,
+    manualAliasUsage: {},
+    preservedAliases: {},
+  });
+
+  assert.deepEqual(result, { handled: true, deleted: false });
+  assert.equal(api.calls.setUsed.length, 1);
+  assert.equal(api.calls.listCalls, 0);
+  assert.equal(api.calls.deletes.length, 0);
+});
+
+test('finalizeIcloudAliasAfterSuccessfulFlow skips deleting preserved aliases', async () => {
+  const api = createApi();
+  const result = await api.finalizeIcloudAliasAfterSuccessfulFlow({
+    email: 'alias@icloud.com',
+    emailGenerator: 'icloud',
+    autoDeleteUsedIcloudAlias: true,
+    manualAliasUsage: {},
+    preservedAliases: { 'alias@icloud.com': true },
+  });
+
+  assert.deepEqual(result, { handled: true, deleted: false });
+  assert.equal(api.calls.setUsed.length, 1);
+  assert.equal(api.calls.listCalls, 0);
+  assert.equal(api.calls.deletes.length, 0);
+});
+
+test('finalizeIcloudAliasAfterSuccessfulFlow skips deleting aliases that are preserved in the latest alias list', async () => {
+  const api = createApi({
+    listIcloudAliases() {
+      return [
+        { email: 'alias@icloud.com', anonymousId: 'anon-1', preserved: true },
+      ];
+    },
+  });
+
+  const result = await api.finalizeIcloudAliasAfterSuccessfulFlow({
+    email: 'alias@icloud.com',
+    emailGenerator: 'icloud',
+    autoDeleteUsedIcloudAlias: true,
+    manualAliasUsage: {},
+    preservedAliases: {},
+  });
+
+  assert.deepEqual(result, { handled: true, deleted: false });
+  assert.equal(api.calls.setUsed.length, 1);
+  assert.equal(api.calls.listCalls, 1);
+  assert.equal(api.calls.deletes.length, 0);
+});
+
+test('finalizeIcloudAliasAfterSuccessfulFlow deletes alias when auto-delete is enabled and alias exists', async () => {
+  const api = createApi({
+    listIcloudAliases() {
+      return [
+        { email: 'alias@icloud.com', anonymousId: 'anon-1', preserved: false },
+      ];
+    },
+  });
+
+  const result = await api.finalizeIcloudAliasAfterSuccessfulFlow({
+    email: 'alias@icloud.com',
+    emailGenerator: 'icloud',
+    autoDeleteUsedIcloudAlias: true,
+    manualAliasUsage: {},
+    preservedAliases: {},
+  });
+
+  assert.deepEqual(result, { handled: true, deleted: true });
+  assert.equal(api.calls.setUsed.length, 1);
+  assert.equal(api.calls.listCalls, 1);
+  assert.deepEqual(api.calls.deletes, [
+    { email: 'alias@icloud.com', anonymousId: 'anon-1', preserved: false },
+  ]);
+});
+
+test('finalizeIcloudAliasAfterSuccessfulFlow ignores non-icloud flows', async () => {
+  const api = createApi();
+  const result = await api.finalizeIcloudAliasAfterSuccessfulFlow({
+    email: 'plain@example.com',
+    emailGenerator: 'duck',
+    autoDeleteUsedIcloudAlias: true,
+    manualAliasUsage: {},
+    preservedAliases: {},
+  });
+
+  assert.deepEqual(result, { handled: false, deleted: false });
+  assert.equal(api.calls.setUsed.length, 0);
+});

+ 121 - 0
tests/icloud-utils.test.js

@@ -0,0 +1,121 @@
+const test = require('node:test');
+const assert = require('node:assert/strict');
+
+const {
+  findIcloudAliasArray,
+  findIcloudAliasByEmail,
+  getConfiguredIcloudHostPreference,
+  getIcloudHostHintFromMessage,
+  getIcloudLoginUrlForHost,
+  getIcloudSetupUrlForHost,
+  normalizeBooleanMap,
+  normalizeIcloudAliasList,
+  normalizeIcloudAliasRecord,
+  normalizeIcloudHost,
+  pickReusableIcloudAlias,
+  toNormalizedEmailSet,
+} = require('../icloud-utils.js');
+
+test('normalizeIcloudHost and host preference helpers resolve supported hosts', () => {
+  assert.equal(normalizeIcloudHost('www.icloud.com'), 'icloud.com');
+  assert.equal(normalizeIcloudHost('setup.icloud.com.cn'), 'icloud.com.cn');
+  assert.equal(normalizeIcloudHost('example.com'), '');
+
+  assert.equal(getConfiguredIcloudHostPreference({ icloudHostPreference: 'icloud.com' }), 'icloud.com');
+  assert.equal(getConfiguredIcloudHostPreference({ icloudHostPreference: 'auto' }), '');
+  assert.equal(getIcloudLoginUrlForHost('icloud.com.cn'), 'https://www.icloud.com.cn/');
+  assert.equal(getIcloudSetupUrlForHost('icloud.com'), 'https://setup.icloud.com/setup/ws/1');
+});
+
+test('getIcloudHostHintFromMessage can infer host from error text', () => {
+  assert.equal(getIcloudHostHintFromMessage('status 401 from setup.icloud.com.cn/setup/ws/1'), 'icloud.com.cn');
+  assert.equal(getIcloudHostHintFromMessage('request failed at https://www.icloud.com/'), 'icloud.com');
+  assert.equal(getIcloudHostHintFromMessage('unknown host'), '');
+});
+
+test('findIcloudAliasArray finds nested alias collections', () => {
+  const payload = {
+    result: {
+      data: {
+        items: [
+          { hme: 'first@icloud.com', anonymousId: 'a1' },
+          { hme: 'second@icloud.com', anonymousId: 'a2' },
+        ],
+      },
+    },
+  };
+
+  assert.deepEqual(findIcloudAliasArray(payload), payload.result.data.items);
+});
+
+test('normalizeIcloudAliasRecord merges used and preserved state', () => {
+  const alias = normalizeIcloudAliasRecord({
+    anonymousId: 'alias-1',
+    hme: 'Demo@iCloud.com',
+    label: 'Test',
+    note: 'Created by test',
+    state: 'active',
+  }, {
+    usedEmails: ['demo@icloud.com'],
+    preservedEmails: ['demo@icloud.com'],
+  });
+
+  assert.deepEqual(alias, {
+    anonymousId: 'alias-1',
+    email: 'demo@icloud.com',
+    label: 'Test',
+    note: 'Created by test',
+    state: 'active',
+    active: true,
+    used: true,
+    preserved: true,
+    createdAt: null,
+  });
+});
+
+test('normalizeIcloudAliasList orders active unused aliases before used aliases', () => {
+  const aliases = normalizeIcloudAliasList({
+    hmeEmails: [
+      { hme: 'used@icloud.com', anonymousId: 'u1', active: true },
+      { hme: 'fresh@icloud.com', anonymousId: 'f1', active: true },
+      { hme: 'inactive@icloud.com', anonymousId: 'i1', active: false },
+    ],
+  }, {
+    usedEmails: ['used@icloud.com'],
+    preservedEmails: ['inactive@icloud.com'],
+  });
+
+  assert.deepEqual(aliases.map((alias) => alias.email), [
+    'fresh@icloud.com',
+    'used@icloud.com',
+    'inactive@icloud.com',
+  ]);
+  assert.equal(aliases[2].preserved, true);
+});
+
+test('pickReusableIcloudAlias and findIcloudAliasByEmail select expected aliases', () => {
+  const aliases = normalizeIcloudAliasList({
+    hmeEmails: [
+      { hme: 'used@icloud.com', anonymousId: 'u1', active: true },
+      { hme: 'fresh@icloud.com', anonymousId: 'f1', active: true },
+    ],
+  }, {
+    usedEmails: ['used@icloud.com'],
+  });
+
+  assert.equal(pickReusableIcloudAlias(aliases)?.email, 'fresh@icloud.com');
+  assert.equal(findIcloudAliasByEmail(aliases, 'FRESH@ICLOUD.COM')?.anonymousId, 'f1');
+});
+
+test('normalizeBooleanMap and toNormalizedEmailSet normalize keys and truthy entries', () => {
+  const normalized = normalizeBooleanMap({
+    ' Demo@icloud.com ': 1,
+    'skip@icloud.com': 0,
+  });
+
+  assert.deepEqual(normalized, {
+    'demo@icloud.com': true,
+    'skip@icloud.com': false,
+  });
+  assert.deepEqual([...toNormalizedEmailSet(normalized)], ['demo@icloud.com']);
+});

+ 5 - 0
tests/step9-localhost-cleanup-scope.test.js

@@ -110,6 +110,11 @@ async function addLog(message) {
   logMessages.push(message);
 }
 
+async function finalizeIcloudAliasAfterSuccessfulFlow() {}
+function shouldUseCustomRegistrationEmail() {
+  return false;
+}
+
 ${bundle}
 
 return {