extension-develop 4.1.19 → 4.1.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/832~0.mjs +26 -6
  2. package/dist/840~0.mjs +75 -6
  3. package/dist/950~0.mjs +3 -2
  4. package/dist/command-preview.d.ts +0 -8
  5. package/dist/dev-server/control-bridge/actions-file.d.ts +0 -1
  6. package/dist/dev-server/control-bridge/broker.d.ts +6 -4
  7. package/dist/dev-server/control-bridge/consumer-client.d.ts +0 -10
  8. package/dist/dev-server/control-bridge/contracts.d.ts +1 -43
  9. package/dist/dev-server/control-bridge/logs-file.d.ts +0 -1
  10. package/dist/dev-server/control-bridge/logs-query.d.ts +0 -16
  11. package/dist/dev-server/control-bridge/producer-runtime.d.ts +1 -2
  12. package/dist/dev-server~0.mjs +17 -5
  13. package/dist/extension-js-devtools/edge/manifest.json +0 -1
  14. package/dist/lib/build-summary.d.ts +0 -11
  15. package/dist/lib/constants.d.ts +1 -0
  16. package/dist/lib/ensure-develop-artifacts.d.ts +0 -7
  17. package/dist/lib/has-dependency.d.ts +0 -5
  18. package/dist/lib/merge-options.d.ts +0 -40
  19. package/dist/lib/messages.d.ts +1 -1
  20. package/dist/lib/project-manifest.d.ts +0 -1
  21. package/dist/lib/session-paths.d.ts +0 -22
  22. package/dist/plugin-browsers/index.d.ts +0 -35
  23. package/dist/plugin-browsers/safari-dev-plugin.d.ts +4 -0
  24. package/dist/plugin-compatibility/feature-polyfill.d.ts +0 -4
  25. package/dist/plugin-compilation/env.d.ts +0 -5
  26. package/dist/plugin-css/css-lib/dead-url-refs.d.ts +0 -6
  27. package/dist/plugin-css/css-lib/inline-content-script-css.d.ts +0 -15
  28. package/dist/plugin-css/css-lib/resolve-css-asset.d.ts +0 -8
  29. package/dist/plugin-perf-budgets/index.d.ts +0 -21
  30. package/dist/plugin-playwright/index.d.ts +3 -2
  31. package/dist/plugin-reload/classify-reload.d.ts +0 -4
  32. package/dist/plugin-reload/index.d.ts +1 -19
  33. package/dist/plugin-reload/reload-dispatch.d.ts +6 -3
  34. package/dist/plugin-reload/steps/setup-chunk-loading-target.d.ts +0 -13
  35. package/dist/plugin-special-folders/folder-extensions/types.d.ts +0 -13
  36. package/dist/plugin-special-folders/index.d.ts +0 -9
  37. package/dist/plugin-web-extension/feature-html/html-lib/utils.d.ts +0 -1
  38. package/dist/plugin-web-extension/feature-html/index.d.ts +0 -27
  39. package/dist/plugin-web-extension/feature-icons/index.d.ts +0 -12
  40. package/dist/plugin-web-extension/feature-json/index.d.ts +0 -9
  41. package/dist/plugin-web-extension/feature-locales/index.d.ts +0 -4
  42. package/dist/plugin-web-extension/feature-manifest/index.d.ts +0 -10
  43. package/dist/plugin-web-extension/feature-manifest/manifest-lib/filter-key-edge.d.ts +5 -0
  44. package/dist/plugin-web-extension/feature-manifest/manifest-lib/filter-keys-safari.d.ts +11 -0
  45. package/dist/plugin-web-extension/feature-manifest/manifest-lib/gecko-data-collection.d.ts +0 -12
  46. package/dist/plugin-web-extension/feature-manifest/manifest-lib/legacy-paths.d.ts +0 -13
  47. package/dist/plugin-web-extension/feature-manifest/manifest-lib/sanitize-fatal-shapes.d.ts +0 -36
  48. package/dist/plugin-web-extension/feature-manifest/messages.d.ts +19 -0
  49. package/dist/plugin-web-extension/feature-manifest/steps/apply-dev-defaults-lib/dev-injected-hosts.d.ts +0 -5
  50. package/dist/plugin-web-extension/feature-manifest/steps/patch-war.d.ts +0 -5
  51. package/dist/plugin-web-extension/feature-manifest/steps/patch-webkit-background.d.ts +2 -0
  52. package/dist/plugin-web-extension/feature-manifest/steps/update-manifest.d.ts +1 -0
  53. package/dist/plugin-web-extension/feature-manifest/steps/warn-gecko-unsupported-apis.d.ts +117 -13
  54. package/dist/plugin-web-extension/feature-scripts/index.d.ts +0 -10
  55. package/dist/plugin-web-extension/feature-scripts/steps/native-geturl-import-loader.d.ts +0 -3
  56. package/dist/plugin-web-extension/feature-web-resources/index.d.ts +0 -10
  57. package/dist/plugin-web-extension/feature-web-resources/web-resources-lib/clean-matches.d.ts +0 -6
  58. package/dist/plugin-web-extension/shared/discover-devtools-panels.d.ts +0 -6
  59. package/dist/plugin-web-extension/shared/paths.d.ts +0 -1
  60. package/dist/rspack-config~0.mjs +666 -133
  61. package/dist/types.d.ts +0 -190
  62. package/package.json +1 -1
  63. package/runtime/process-shim.cjs +2 -0
@@ -33,8 +33,6 @@ import * as __rspack_external_node_fs_1b05aee1 from "node:fs";
33
33
  import * as __rspack_external_node_path_806ed179 from "node:path";
34
34
  import * as __rspack_external_dotenv from "dotenv";
35
35
  import * as __rspack_external_parse5_utilities_4a00fdd6 from "parse5-utilities";
36
- import * as __rspack_external_fs from "fs";
37
- import * as __rspack_external_path from "path";
38
36
  import * as __rspack_external_node_crypto_2e7c4b46 from "node:crypto";
39
37
  function normalizePath(filePath) {
40
38
  return filePath.replace(/\\/g, '/');
@@ -707,7 +705,7 @@ function optionsPage(manifest, manifestPath) {
707
705
  })()
708
706
  };
709
707
  }
710
- function optionsUi(manifest, manifestPath) {
708
+ function options_ui_optionsUi(manifest, manifestPath) {
711
709
  return manifest.options_ui && {
712
710
  options_ui: {
713
711
  ...manifest.options_ui,
@@ -806,7 +804,7 @@ function manifestCommon(manifest, manifestPath, projectPath) {
806
804
  ...commands_commands(manifest),
807
805
  ...permissions_permissions(manifest),
808
806
  ...optionsPage(manifest, manifestPath),
809
- ...optionsUi(manifest, manifestPath),
807
+ ...options_ui_optionsUi(manifest, manifestPath),
810
808
  ...sandbox(manifest, manifestPath),
811
809
  ...storage(manifest, manifestPath, projectPath),
812
810
  ...theme_theme(manifest, manifestPath),
@@ -1119,6 +1117,330 @@ function getManifestOverrides(manifestPath, manifest, projectPath) {
1119
1117
  if (Object.keys(backgroundMerged).length) merged.background = backgroundMerged;
1120
1118
  return JSON.stringify(dropMv2ObjectPolicy(dropMv2HostKeys(merged)), null, 2);
1121
1119
  }
1120
+ function serverRestartRequiredFromManifestError(fileAdded, fileRemoved) {
1121
+ const lines = [];
1122
+ lines.push("Entrypoint references changed.");
1123
+ if (fileRemoved) lines.push(`${pintor.gray('EXPECTED')} ${pintor.underline(fileRemoved)}`);
1124
+ if (fileAdded) lines.push(`${pintor.gray('GOT')} ${pintor.underline(fileAdded)}`);
1125
+ lines.push("Restart the dev server to pick up changes to manifest entrypoints.");
1126
+ return lines.join('\n');
1127
+ }
1128
+ function legacyManifestPathWarning(field, legacyPath, modernPath) {
1129
+ const lines = [];
1130
+ lines.push(`${messaging_prefix('warn')} The ${pintor.blue(field)} field uses a deprecated scaffold path.`);
1131
+ lines.push(`${pintor.gray('PATH')} ${pintor.underline(legacyPath)}`);
1132
+ lines.push(`Point it at ${pintor.underline(modernPath)}, Extension.js already emits the page there.`);
1133
+ return lines.join('\n');
1134
+ }
1135
+ function fatalManifestShapeFixed(field, detail) {
1136
+ const lines = [];
1137
+ lines.push(`${messaging_prefix('warn')} Repaired the ${pintor.blue(field)} field, which Chrome refuses to load the extension over.`);
1138
+ lines.push(`${pintor.gray('REASON')} ${pintor.underline(detail)}`);
1139
+ lines.push(`Fix the field in your ${pintor.blue('manifest.json')} file.`);
1140
+ return lines.join('\n');
1141
+ }
1142
+ function invalidThemeValue(field, detail, value) {
1143
+ const lines = [];
1144
+ lines.push(`Check the ${pintor.yellow(field)} field in your ${pintor.yellow('manifest.json')} file.`);
1145
+ lines.push(detail);
1146
+ lines.push(`Chrome rejects the whole extension when this value is malformed.\nThe build stops here to protect you.`);
1147
+ lines.push('');
1148
+ lines.push(`${pintor.red('INVALID VALUE')} ${value}`);
1149
+ return lines.join('\n');
1150
+ }
1151
+ function themeNotSupportedByBrowser(browser) {
1152
+ const lines = [];
1153
+ lines.push(`${messaging_prefix('warn')} ${pintor.blue(browser)} does not support the ${pintor.yellow('theme')} field.`);
1154
+ lines.push("The field ships unchanged in the manifest and Safari ignores it.");
1155
+ return lines.join('\n');
1156
+ }
1157
+ function missingGeckoDataCollectionPermissions() {
1158
+ const lines = [];
1159
+ lines.push(`${messaging_prefix('warn')} addons.mozilla.org requires ${pintor.blue('browser_specific_settings.gecko.data_collection_permissions')} for new add-ons.`);
1160
+ lines.push(`Declare ${pintor.blue('{"required": ["none"]}')} if this extension transmits no data.`);
1161
+ lines.push(`See ${pintor.underline('https://extensionworkshop.com/documentation/develop/firefox-builtin-data-consent/')} for details.`);
1162
+ return lines.join('\n');
1163
+ }
1164
+ function manifestInvalidError(error) {
1165
+ const lines = [];
1166
+ lines.push(`Can't read your ${pintor.blue('manifest.json')} file.`);
1167
+ lines.push(`${pintor.gray('REASON')} ${pintor.underline(String(error))}`);
1168
+ lines.push("Update your manifest and try again.");
1169
+ return lines.join('\n');
1170
+ }
1171
+ function manifestIncludeSummary(browser, manifestPath) {
1172
+ return `${messaging_prefix('debug')} manifest include browser=${browser} path=${manifestPath}`;
1173
+ }
1174
+ function manifestEmitSuccess() {
1175
+ return `${messaging_prefix('debug')} manifest emitted=true schemaStripped=true`;
1176
+ }
1177
+ function manifestOverridesSummary(overrideKeys, devCssStubsAdded) {
1178
+ return `${messaging_prefix('debug')} manifest overrides keys=${String(overrideKeys)} devCssStubs=${String(devCssStubsAdded)}`;
1179
+ }
1180
+ function manifestDepsTracked(addedCount) {
1181
+ return `${messaging_prefix('debug')} manifest deps=${String(addedCount)}`;
1182
+ }
1183
+ function manifestLegacyWarningsSummary(count) {
1184
+ return `${messaging_prefix('debug')} manifest legacyWarnings=${String(count)}`;
1185
+ }
1186
+ function pageActionNotSupportedByBrowser(browser) {
1187
+ return `${messaging_prefix('warn')} ${pintor.blue(browser)} does not show the ${pintor.yellow('page_action')} address bar surface.\nThe field was dropped from the built manifest and its page was not emitted.`;
1188
+ }
1189
+ function pageActionDroppedForBrowserAction(browser) {
1190
+ return `${messaging_prefix('warn')} ${pintor.blue(browser)} refuses a Manifest V2 manifest that declares both ${pintor.yellow('browser_action')} and ${pintor.yellow('page_action')}.\nThe build kept ${pintor.yellow('browser_action')} and dropped ${pintor.yellow('page_action')} from the built manifest, so its page was not emitted. Remove one of the two keys, or scope page_action with the firefox: prefix.`;
1191
+ }
1192
+ function vendorPrefixedKeyDropped(key, familyKey, vendor, browser) {
1193
+ const vendorName = 'edge' === vendor ? 'Edge' : 'Chrome';
1194
+ return `${messaging_prefix('warn')} ${pintor.yellow(key)} now applies only to ${vendorName} builds, so the ${pintor.blue(browser)} build dropped it.\nRename it to ${pintor.yellow(familyKey)} to keep it on every Chromium-based browser. Leave it as is if it's meant for ${vendorName} only.`;
1195
+ }
1196
+ function edgeStoreKeyDropped(browser) {
1197
+ return `${messaging_prefix('warn')} Edge Add-ons refuses a package whose manifest carries ${pintor.yellow('key')}, so the ${pintor.blue(browser)} production build dropped it.\nPartner Center assigns the id that ${pintor.yellow('key')} pins everywhere else, so the field has no use in this package. Write it as ${pintor.yellow('chrome:key')} if it is meant for the Chrome Web Store build only.`;
1198
+ }
1199
+ function mv2SandboxPolicyDropped(browser) {
1200
+ return `${messaging_prefix('warn')} ${pintor.blue(browser)} reads a Manifest V2 ${pintor.yellow('content_security_policy')} as one string, so the ${pintor.yellow('sandbox')} slot has nowhere to go.\nThe build wrote the ${pintor.yellow('extension_pages')} policy as that string and dropped the sandbox policy from the built manifest. Scope the object form with the chromium: prefix, or declare a Manifest V3 build for this browser.`;
1201
+ }
1202
+ function geckoSidePanelUnsupported(file) {
1203
+ const lines = [];
1204
+ lines.push(`${messaging_prefix('warn')} ${pintor.underline(file)} uses chrome.sidePanel, which is Chromium only.`);
1205
+ lines.push(`Firefox opens a sidebar through the ${pintor.yellow('sidebar_action')} manifest key on every manifest version, and addons-linter flags the call as UNSUPPORTED_API.`);
1206
+ lines.push(`Move the call behind a build-time branch on ${pintor.blue('import.meta.env.EXTENSION_PUBLIC_BROWSER')} so the Firefox bundle drops it.`);
1207
+ return lines.join('\n');
1208
+ }
1209
+ function safariUnsupportedApi(file, api, detail) {
1210
+ const lines = [];
1211
+ lines.push(`${messaging_prefix('warn')} ${pintor.underline(file)} calls chrome.${api}, which Safari does not have.`);
1212
+ lines.push(detail);
1213
+ lines.push(`Move the call behind a build-time branch on ${pintor.blue('import.meta.env.EXTENSION_PUBLIC_BROWSER')}, or guard it with ${pintor.yellow(`chrome.${api}?.`)}.`);
1214
+ return lines.join('\n');
1215
+ }
1216
+ const safariBackgroundDies = "The call throws when the script runs, Safari drops the background, and nothing reports an error.";
1217
+ function safariSidePanelUnsupported(file) {
1218
+ return safariUnsupportedApi(file, 'sidePanel', "The call throws when the script runs. In a background service worker, Safari then never starts the worker, and nothing reports an error.");
1219
+ }
1220
+ function safariOffscreenUnsupported(file) {
1221
+ return safariUnsupportedApi(file, 'offscreen', `Safari has no offscreen documents, so that DOM work has no place to run. ${safariBackgroundDies}`);
1222
+ }
1223
+ function safariTabGroupsUnsupported(file) {
1224
+ return safariUnsupportedApi(file, 'tabGroups', `Safari has no tab groups, and its tabs carry no group id. ${safariBackgroundDies}`);
1225
+ }
1226
+ function safariManagementUnsupported(file) {
1227
+ return safariUnsupportedApi(file, 'management', `Safari exposes no management namespace at all, not even getSelf. ${safariBackgroundDies}`);
1228
+ }
1229
+ function safariUserScriptsUnsupported(file) {
1230
+ return safariUnsupportedApi(file, 'userScripts', `Safari has no userScripts namespace, and scripting.registerContentScripts is the closest it offers. ${safariBackgroundDies}`);
1231
+ }
1232
+ function safariIdentityUnsupported(file) {
1233
+ return safariUnsupportedApi(file, 'identity', `Safari has no identity namespace, and Apple points OAuth flows at a normal tab instead. ${safariBackgroundDies}`);
1234
+ }
1235
+ function safariNotificationsUnsupported(file) {
1236
+ return safariUnsupportedApi(file, 'notifications', `Safari has no notifications namespace, so a page has to raise the web Notification instead. ${safariBackgroundDies}`);
1237
+ }
1238
+ function safariOmniboxUnsupported(file) {
1239
+ return safariUnsupportedApi(file, 'omnibox', `Safari has no omnibox keyword API. ${safariBackgroundDies}`);
1240
+ }
1241
+ function safariBookmarksUnsupported(file) {
1242
+ return safariUnsupportedApi(file, 'bookmarks', `Safari does not expose bookmarks to extensions on any version. ${safariBackgroundDies}`);
1243
+ }
1244
+ function safariHistoryUnsupported(file) {
1245
+ return safariUnsupportedApi(file, 'history', `Safari does not expose browsing history to extensions on any version. ${safariBackgroundDies}`);
1246
+ }
1247
+ function safariDownloadsUnsupported(file) {
1248
+ return safariUnsupportedApi(file, 'downloads', `Safari has no downloads namespace. ${safariBackgroundDies}`);
1249
+ }
1250
+ function safariIdleUnsupported(file) {
1251
+ return safariUnsupportedApi(file, 'idle', `Safari has no idle namespace. ${safariBackgroundDies}`);
1252
+ }
1253
+ const safariMissingMemberDetails = {
1254
+ 'action.getUserSettings': `Safari has no pinned-state query for the toolbar button, so there is no function to call. ${safariBackgroundDies}`,
1255
+ 'action.getBadgeTextColor': `Safari draws the badge in its own colors and offers no badge text color call. ${safariBackgroundDies}`,
1256
+ 'action.setBadgeTextColor': `Safari draws the badge in its own colors and offers no badge text color call. ${safariBackgroundDies}`,
1257
+ 'action.onUserSettingsChanged': `Safari raises no event when the toolbar button settings change, so the listener attaches to nothing. ${safariBackgroundDies}`,
1258
+ 'storage.managed': `Safari has no managed storage area, since it carries no enterprise policy channel for web extensions. ${safariBackgroundDies}`,
1259
+ 'runtime.getContexts': `Safari keeps no inventory of extension contexts to hand back. ${safariBackgroundDies}`,
1260
+ 'runtime.onSuspend': `Safari tears a background down without announcing it, so this lifecycle event does not exist. ${safariBackgroundDies}`,
1261
+ 'runtime.onSuspendCanceled': `Safari tears a background down without announcing it, so this lifecycle event does not exist. ${safariBackgroundDies}`,
1262
+ 'runtime.onUpdateAvailable': `Safari ships an extension inside its host app, so the browser raises no update event for it. ${safariBackgroundDies}`,
1263
+ 'declarativeNetRequest.getAvailableStaticRuleCount': `Safari implements the blocking core of declarativeNetRequest and not its rule-budget queries. ${safariBackgroundDies}`,
1264
+ 'declarativeNetRequest.getDisabledRuleIds': `Safari cannot disable individual static rules, so it has nothing to report here. ${safariBackgroundDies}`,
1265
+ 'declarativeNetRequest.updateStaticRules': `Safari cannot toggle individual static rules, only whole rulesets through updateEnabledRulesets. ${safariBackgroundDies}`,
1266
+ 'declarativeNetRequest.testMatchOutcome': `Safari has no rule-matching test harness, which is a Chrome debugging aid. ${safariBackgroundDies}`,
1267
+ 'declarativeNetRequest.onRuleMatchedDebug': `Safari raises no rule-match debug event, which Chrome offers to unpacked extensions only. ${safariBackgroundDies}`,
1268
+ 'tabs.group': `Safari has no tab groups at all, so there is no group to move a tab into. ${safariBackgroundDies}`,
1269
+ 'tabs.ungroup': `Safari has no tab groups at all, so there is no group to take a tab out of. ${safariBackgroundDies}`,
1270
+ 'webNavigation.onCreatedNavigationTarget': `Safari reports the main navigation events only, and this one is not among them. ${safariBackgroundDies}`,
1271
+ 'webNavigation.onHistoryStateUpdated': `Safari reports the main navigation events only, so a History API navigation goes unannounced. ${safariBackgroundDies}`,
1272
+ 'webNavigation.onReferenceFragmentUpdated': `Safari reports the main navigation events only, so a fragment change goes unannounced. ${safariBackgroundDies}`,
1273
+ 'webNavigation.onTabReplaced': `Safari reports the main navigation events only, and this one is not among them. ${safariBackgroundDies}`,
1274
+ 'windows.onBoundsChanged': `Safari raises no event when a window is moved or resized. ${safariBackgroundDies}`
1275
+ };
1276
+ function safariMemberUnsupported(file, api, member, kind) {
1277
+ const guard = 'call' === kind ? `chrome.${api}.${member}?.()` : `chrome.${api}.${member}?.`;
1278
+ const lines = [];
1279
+ lines.push(`${messaging_prefix('warn')} ${pintor.underline(file)} calls chrome.${api}.${member}, which Safari does not have.`);
1280
+ lines.push(safariMissingMemberDetails[`${api}.${member}`] || `Safari ships chrome.${api} without this member. ${safariBackgroundDies}`);
1281
+ lines.push(`Safari has ${pintor.blue(`chrome.${api}`)} itself, so a guard on the namespace does not help. Move the call behind a build-time branch on ${pintor.blue('import.meta.env.EXTENSION_PUBLIC_BROWSER')}, or guard it with ${pintor.yellow(guard)}.`);
1282
+ return lines.join('\n');
1283
+ }
1284
+ function geckoActionUnsupportedOnMv2(file) {
1285
+ const lines = [];
1286
+ lines.push(`${messaging_prefix('warn')} ${pintor.underline(file)} uses chrome.action, which Manifest V2 does not have on Firefox.`);
1287
+ lines.push("Firefox Manifest V2 exposes the toolbar button as browserAction, and addons-linter flags the call as UNSUPPORTED_API.");
1288
+ lines.push(`Use browserAction behind a build-time branch on ${pintor.blue('import.meta.env.EXTENSION_PUBLIC_BROWSER')}, or declare Manifest V3 for Firefox with ${pintor.yellow('firefox:manifest_version')}.`);
1289
+ return lines.join('\n');
1290
+ }
1291
+ function webkitUnsupportedKeysDropped(browser, dropped) {
1292
+ const count = dropped.length;
1293
+ const lines = [];
1294
+ lines.push(`${messaging_prefix('warn')} Safari has no support for ${String(count)} manifest ${1 === count ? 'key' : 'keys'} this build inherited from its Chromium manifest, so the ${pintor.blue(browser)} build dropped ${1 === count ? 'it' : 'them'}.`);
1295
+ for (const entry of dropped)lines.push(`${pintor.yellow(entry.path)} ${pintor.gray(entry.reason)}`);
1296
+ lines.push(`Every key above is inert on Safari, so the built app lost nothing it could have run. Declare a Safari-only replacement with the ${pintor.yellow('safari:')} prefix if you have one.`);
1297
+ return lines.join('\n');
1298
+ }
1299
+ const UNSUPPORTED_TOP_LEVEL_KEYS = {
1300
+ side_panel: 'Safari has no side panel surface',
1301
+ sandbox: 'Safari cannot run sandboxed extension pages',
1302
+ user_scripts: 'Safari has no userScripts API',
1303
+ omnibox: 'Safari has no omnibox keyword API',
1304
+ chrome_settings_overrides: 'Safari lets no extension override these settings',
1305
+ tts_engine: 'Safari has no speech engine API',
1306
+ file_browser_handlers: 'ChromeOS only',
1307
+ input_components: 'ChromeOS only',
1308
+ cross_origin_embedder_policy: 'Safari reads no COEP key from a manifest',
1309
+ cross_origin_opener_policy: 'Safari reads no COOP key from a manifest',
1310
+ requirements: 'Chrome Web Store only',
1311
+ oauth2: 'Safari has no identity API to consume it',
1312
+ nacl_modules: 'Native Client is Chrome only',
1313
+ replacement_web_app: 'Chrome Web Store only',
1314
+ offline_enabled: 'Chrome Web Store only',
1315
+ differential_fingerprint: 'Chrome Web Store only',
1316
+ incognito: 'Safari always runs extensions in spanning mode and ignores the declared value'
1317
+ };
1318
+ const UNSUPPORTED_PERMISSIONS = new Set([
1319
+ 'accessibilityFeatures.modify',
1320
+ 'accessibilityFeatures.read',
1321
+ 'audio',
1322
+ 'background',
1323
+ 'bookmarks',
1324
+ 'browsingData',
1325
+ 'certificateProvider',
1326
+ 'clipboardRead',
1327
+ 'contentSettings',
1328
+ 'debugger',
1329
+ 'declarativeContent',
1330
+ 'desktopCapture',
1331
+ 'documentScan',
1332
+ 'downloads',
1333
+ 'enterprise.deviceAttributes',
1334
+ 'enterprise.platformKeys',
1335
+ 'experimental',
1336
+ 'favicon',
1337
+ 'fileSystemProvider',
1338
+ 'fontSettings',
1339
+ 'gcm',
1340
+ 'geolocation',
1341
+ 'history',
1342
+ 'identity',
1343
+ 'identity.email',
1344
+ 'idle',
1345
+ 'management',
1346
+ 'notifications',
1347
+ 'offscreen',
1348
+ 'pageCapture',
1349
+ 'platformKeys',
1350
+ 'power',
1351
+ 'printerProvider',
1352
+ 'privacy',
1353
+ 'processes',
1354
+ 'proxy',
1355
+ 'readingList',
1356
+ 'search',
1357
+ 'sessions',
1358
+ 'sidePanel',
1359
+ 'system.cpu',
1360
+ 'system.display',
1361
+ 'system.memory',
1362
+ 'system.storage',
1363
+ 'systemLog',
1364
+ 'tabCapture',
1365
+ 'tabGroups',
1366
+ 'topSites',
1367
+ 'tts',
1368
+ 'ttsEngine',
1369
+ 'userScripts',
1370
+ 'vpnProvider',
1371
+ 'wallpaper',
1372
+ 'webAuthenticationProxy',
1373
+ 'webRequestBlocking'
1374
+ ]);
1375
+ const PERMISSION_LISTS = [
1376
+ 'permissions',
1377
+ 'optional_permissions'
1378
+ ];
1379
+ const UNSUPPORTED_OPTIONS_UI_KEYS = {
1380
+ open_in_tab: 'Safari always opens the options page in a tab'
1381
+ };
1382
+ function isPlainObject(value) {
1383
+ return !!value && 'object' == typeof value && !Array.isArray(value);
1384
+ }
1385
+ function dropWebkitUnsupportedKeys(manifest, browser) {
1386
+ const dropped = [];
1387
+ if (!isWebkitBasedBrowser(String(browser))) return {
1388
+ manifest,
1389
+ dropped
1390
+ };
1391
+ const next = {
1392
+ ...manifest
1393
+ };
1394
+ for (const [key, reason] of Object.entries(UNSUPPORTED_TOP_LEVEL_KEYS))if (key in next) {
1395
+ delete next[key];
1396
+ dropped.push({
1397
+ path: key,
1398
+ reason
1399
+ });
1400
+ }
1401
+ for (const listName of PERMISSION_LISTS){
1402
+ const list = next[listName];
1403
+ if (!Array.isArray(list)) continue;
1404
+ const kept = list.filter((entry)=>{
1405
+ if ('string' != typeof entry || !UNSUPPORTED_PERMISSIONS.has(entry)) return true;
1406
+ dropped.push({
1407
+ path: `${listName}.${entry}`,
1408
+ reason: `Safari has no ${entry} API`
1409
+ });
1410
+ return false;
1411
+ });
1412
+ if (kept.length !== list.length) if (0 === kept.length) delete next[listName];
1413
+ else next[listName] = kept;
1414
+ }
1415
+ const optionsUi = next.options_ui;
1416
+ if (isPlainObject(optionsUi)) {
1417
+ const patched = {
1418
+ ...optionsUi
1419
+ };
1420
+ let changed = false;
1421
+ for (const [key, reason] of Object.entries(UNSUPPORTED_OPTIONS_UI_KEYS))if (key in patched) {
1422
+ delete patched[key];
1423
+ changed = true;
1424
+ dropped.push({
1425
+ path: `options_ui.${key}`,
1426
+ reason
1427
+ });
1428
+ }
1429
+ if (changed) next.options_ui = patched;
1430
+ }
1431
+ return {
1432
+ manifest: next,
1433
+ dropped
1434
+ };
1435
+ }
1436
+ const reportedDrops = new Set();
1437
+ function reportWebkitDroppedKeys(dropped, browser) {
1438
+ if (0 === dropped.length) return;
1439
+ const signature = `${String(browser)}\0${dropped.map((entry)=>entry.path).join(',')}`;
1440
+ if (reportedDrops.has(signature)) return;
1441
+ reportedDrops.add(signature);
1442
+ humanLine(webkitUnsupportedKeysDropped(String(browser), dropped));
1443
+ }
1122
1444
  const manifest_cjsRequire = createRequire(import.meta.url);
1123
1445
  const manifestSourceStore = new WeakMap();
1124
1446
  function manifestSourceEntry(compilation) {
@@ -1185,10 +1507,13 @@ function getManifestContent(compilation, manifestPath) {
1185
1507
  function buildCanonicalManifest(manifestPath, manifest, browser, projectPath) {
1186
1508
  const filteredManifest = filterKeysForThisBrowser(manifest, browser);
1187
1509
  const forOverrides = shouldDropPageAction(filteredManifest, browser) ? dropPageAction(filteredManifest) : filteredManifest;
1188
- return dropMv2ObjectPolicy(dropMv2HostKeys({
1510
+ const canonical = dropMv2ObjectPolicy(dropMv2HostKeys({
1189
1511
  ...forOverrides,
1190
1512
  ...JSON.parse(getManifestOverrides(manifestPath, forOverrides, projectPath))
1191
1513
  }));
1514
+ const webkit = dropWebkitUnsupportedKeys(canonical, browser);
1515
+ reportWebkitDroppedKeys(webkit.dropped, browser);
1516
+ return webkit.manifest;
1192
1517
  }
1193
1518
  const IMPORT_META_URL_RUNTIME = '(function(){var h=typeof document!=="undefined"&&document.baseURI||self.location.href;try{var g=globalThis,r=(g.browser||g.chrome).runtime.getURL("/");return h.indexOf(r)===0?h:r}catch(_){return h}})()';
1194
1519
  function resolveProcessShim() {
@@ -3531,14 +3856,28 @@ const BRIDGE_PRODUCER_SOURCE = `;(function () {
3531
3856
  var connectFailures = 0;
3532
3857
  var MAX_QUEUE = 1000;
3533
3858
  var MAX_RESULT_BYTES = 256 * 1024;
3859
+ // Where error frames are parked so a context that dies before its socket
3860
+ // opens does not take them along. Capped, and old entries are dropped.
3861
+ var PENDING_ERRORS_KEY = "__extjsPendingErrors";
3862
+ var MAX_PENDING_ERRORS = 20;
3863
+ var PENDING_ERROR_TTL = 300000;
3534
3864
 
3535
3865
  function nowId() {
3536
3866
  return Date.now().toString(36) + Math.random().toString(36).slice(2, 8);
3537
3867
  }
3538
3868
 
3869
+ // The extension's own URL scheme names the engine outright, which beats a
3870
+ // user agent where Chrome and Safari both say "Safari".
3539
3871
  function engineName() {
3540
3872
  try {
3541
- return (typeof navigator !== "undefined" && /firefox/i.test(navigator.userAgent)) ? "firefox" : "chromium";
3873
+ var scheme = (typeof location !== "undefined" && location.protocol) || "";
3874
+ if (scheme.indexOf("safari-web-extension") === 0) return "webkit";
3875
+ if (scheme.indexOf("moz-extension") === 0) return "firefox";
3876
+ if (scheme.indexOf("chrome-extension") === 0) return "chromium";
3877
+ var ua = (typeof navigator !== "undefined" && navigator.userAgent) || "";
3878
+ if (/firefox/i.test(ua)) return "firefox";
3879
+ if (/safari/i.test(ua) && !/chrome|chromium|edg/i.test(ua)) return "webkit";
3880
+ return "chromium";
3542
3881
  } catch (e) { return "chromium"; }
3543
3882
  }
3544
3883
 
@@ -3687,7 +4026,7 @@ const BRIDGE_PRODUCER_SOURCE = `;(function () {
3687
4026
  // MV3 forbids eval of strings in the SW/extension pages and
3688
4027
  // rejects 'unsafe-eval'. Surface it honestly with an alternative.
3689
4028
  if (/Content Security Policy|unsafe-eval/i.test(msg)) {
3690
- replyErr(cmdId, "Unsupported", "eval is blocked in the MV3 service worker by CSP. Use --context page --tab <id> (eval runs in the page's MAIN world), or run on an MV2/Firefox build. Engine: " + engineName());
4029
+ replyErr(cmdId, "Unsupported", "eval is blocked in the extension background by CSP, which allows no unsafe-eval. Use --context page --tab <id> (eval runs in the page's MAIN world), or run on a build whose CSP permits it. Engine: " + engineName());
3691
4030
  } else {
3692
4031
  replyErr(cmdId, (e && e.name) || "EvalError", msg);
3693
4032
  }
@@ -3837,6 +4176,11 @@ const BRIDGE_PRODUCER_SOURCE = `;(function () {
3837
4176
  try { chrome.runtime.openOptionsPage(function () { replyOk(cmdId, {opened: "options"}); }); }
3838
4177
  catch (e) { replyErr(cmdId, "Unsupported", "openOptionsPage: " + e); }
3839
4178
  } else if (surface === "sidebar") {
4179
+ // The two member reads below are exactly what the Safari
4180
+ // unsupported-API scan looks for, and this runtime is prepended to
4181
+ // the background asset at PROCESS_ASSETS_STAGE_REPORT + 101, after
4182
+ // that scan runs at SUMMARIZE + 1. Move the injection earlier and
4183
+ // every Safari dev session warns about this line.
3840
4184
  if (chrome.sidePanel && chrome.sidePanel.open && chrome.windows) {
3841
4185
  chrome.windows.getCurrent(function (w) {
3842
4186
  nsCall("sidePanel", "open", [{windowId: w.id}], function (err) {
@@ -4289,6 +4633,80 @@ const BRIDGE_PRODUCER_SOURCE = `;(function () {
4289
4633
  }
4290
4634
  }
4291
4635
  if (queue.length < MAX_QUEUE) queue.push(frame);
4636
+ parkPendingError(frame);
4637
+ }
4638
+
4639
+ // The queue above lives in this context's memory only, so a background
4640
+ // that throws while it evaluates dies with the error still queued: the
4641
+ // socket has not finished its handshake yet. Park error frames in
4642
+ // extension storage too, where the NEXT context to connect can ship them.
4643
+ // Best effort by construction: the write is asynchronous, so a teardown
4644
+ // can cut it before the browser records anything.
4645
+ var parkedErrors = [];
4646
+ function storageLocalArea() {
4647
+ try {
4648
+ var ns = (g.chrome && g.chrome.storage) || (g.browser && g.browser.storage);
4649
+ var area = ns && ns.local;
4650
+ if (!area || typeof area.get !== "function" || typeof area.set !== "function") return null;
4651
+ return area;
4652
+ } catch (e) { return null; }
4653
+ }
4654
+ function parkPendingError(frame) {
4655
+ if (open) return;
4656
+ if (!frame || frame.type !== "log" || !frame.event || frame.event.level !== "error") return;
4657
+ var area = storageLocalArea();
4658
+ if (!area) return;
4659
+ // The whole list is rewritten on every park, so two errors in one turn
4660
+ // cannot lose each other to a read-modify-write, and the cap holds.
4661
+ parkedErrors.push(frame);
4662
+ while (parkedErrors.length > MAX_PENDING_ERRORS) parkedErrors.shift();
4663
+ var items = {};
4664
+ items[PENDING_ERRORS_KEY] = parkedErrors;
4665
+ try { area.set(items, noopLastError); } catch (e) {
4666
+ // Ignore
4667
+ }
4668
+ }
4669
+ function parkedHere(id) {
4670
+ for (var i = 0; i < parkedErrors.length; i++) {
4671
+ var ev = parkedErrors[i] && parkedErrors[i].event;
4672
+ if (ev && ev.id === id) return true;
4673
+ }
4674
+ return false;
4675
+ }
4676
+ // Ship what an earlier context parked and never got to send. Entries from
4677
+ // another build (a different instance id) or from an older run are dropped,
4678
+ // and the key is cleared on read so one failure is reported once.
4679
+ function flushPendingErrors() {
4680
+ var area = storageLocalArea();
4681
+ if (!area) return;
4682
+ var read = false;
4683
+ function onRead(res) {
4684
+ if (read) return;
4685
+ read = true;
4686
+ var list = res && res[PENDING_ERRORS_KEY];
4687
+ if (!list || !list.length) return;
4688
+ try {
4689
+ if (typeof area.remove === "function") area.remove(PENDING_ERRORS_KEY, noopLastError);
4690
+ } catch (e) {
4691
+ // Ignore
4692
+ }
4693
+ var now = Date.now();
4694
+ for (var i = 0; i < list.length; i++) {
4695
+ var frame = list[i];
4696
+ var ev = frame && frame.event;
4697
+ if (!ev || ev.runId !== INSTANCE_ID) continue;
4698
+ // This context parked it, so the queue flush above already has it.
4699
+ if (parkedHere(ev.id)) continue;
4700
+ if (typeof ev.timestamp !== "number" || now - ev.timestamp > PENDING_ERROR_TTL) continue;
4701
+ send(frame);
4702
+ }
4703
+ }
4704
+ try {
4705
+ var ret = area.get(PENDING_ERRORS_KEY, function (res) { noopLastError(); onRead(res); });
4706
+ if (ret && typeof ret.then === "function") ret.then(onRead, function () {});
4707
+ } catch (e) {
4708
+ // Ignore
4709
+ }
4292
4710
  }
4293
4711
 
4294
4712
  // The baked PORT is a snapshot of an ephemeral port. After a dev-server
@@ -4342,6 +4760,9 @@ const BRIDGE_PRODUCER_SOURCE = `;(function () {
4342
4760
  // Ignore
4343
4761
  }
4344
4762
  flush();
4763
+ // Hello went first and this context's own queue is out; now replay any
4764
+ // error a previous context parked before it died.
4765
+ flushPendingErrors();
4345
4766
  };
4346
4767
  socket.onmessage = function (ev) {
4347
4768
  var frame;
@@ -4809,10 +5230,12 @@ class InjectBridgeProducer {
4809
5230
  name: InjectBridgeProducer.name,
4810
5231
  stage: core_Compilation.PROCESS_ASSETS_STAGE_REPORT + 101
4811
5232
  }, ()=>{
4812
- if (!compilation.getAsset(CONTROL_PORT_ASSET_NAME)) compilation.emitAsset(CONTROL_PORT_ASSET_NAME, new core_sources.RawSource(JSON.stringify({
5233
+ const controlAsset = new core_sources.RawSource(JSON.stringify({
4813
5234
  port: controlPort,
4814
5235
  instanceId
4815
- })));
5236
+ }));
5237
+ if (compilation.getAsset(CONTROL_PORT_ASSET_NAME)) compilation.updateAsset(CONTROL_PORT_ASSET_NAME, controlAsset);
5238
+ else compilation.emitAsset(CONTROL_PORT_ASSET_NAME, controlAsset);
4816
5239
  for (const asset of compilation.getAssets()){
4817
5240
  if (!BACKGROUND_ASSET.test(asset.name)) continue;
4818
5241
  const original = asset.source.source().toString();
@@ -9537,17 +9960,17 @@ function isManagedSchemaFeature(feature) {
9537
9960
  function isCriticalJsonFeature(feature) {
9538
9961
  return feature.startsWith('declarative_net_request') || isManagedSchemaFeature(feature);
9539
9962
  }
9540
- function isPlainObject(value) {
9963
+ function json_validation_isPlainObject(value) {
9541
9964
  return 'object' == typeof value && null !== value && !Array.isArray(value);
9542
9965
  }
9543
9966
  function getDnrRuleRejection(rule) {
9544
- if (!isPlainObject(rule)) return 'the rule is not an object';
9967
+ if (!json_validation_isPlainObject(rule)) return 'the rule is not an object';
9545
9968
  const id = rule.id;
9546
9969
  if ('number' != typeof id || !Number.isInteger(id) || id < 1) return 'the rule id is not a positive integer';
9547
9970
  const action = rule.action;
9548
- if (!isPlainObject(action)) return 'the rule has no action object';
9971
+ if (!json_validation_isPlainObject(action)) return 'the rule has no action object';
9549
9972
  if ('string' != typeof action.type || 0 === action.type.length) return 'the rule action has no type';
9550
- if (!isPlainObject(rule.condition)) return 'the rule has no condition object';
9973
+ if (!json_validation_isPlainObject(rule.condition)) return 'the rule has no condition object';
9551
9974
  }
9552
9975
  function getDnrRuleSoftIssue(rule) {
9553
9976
  const priority = rule.priority;
@@ -10060,99 +10483,6 @@ class LocalesPlugin {
10060
10483
  });
10061
10484
  }
10062
10485
  }
10063
- function serverRestartRequiredFromManifestError(fileAdded, fileRemoved) {
10064
- const lines = [];
10065
- lines.push("Entrypoint references changed.");
10066
- if (fileRemoved) lines.push(`${pintor.gray('EXPECTED')} ${pintor.underline(fileRemoved)}`);
10067
- if (fileAdded) lines.push(`${pintor.gray('GOT')} ${pintor.underline(fileAdded)}`);
10068
- lines.push("Restart the dev server to pick up changes to manifest entrypoints.");
10069
- return lines.join('\n');
10070
- }
10071
- function legacyManifestPathWarning(field, legacyPath, modernPath) {
10072
- const lines = [];
10073
- lines.push(`${messaging_prefix('warn')} The ${pintor.blue(field)} field uses a deprecated scaffold path.`);
10074
- lines.push(`${pintor.gray('PATH')} ${pintor.underline(legacyPath)}`);
10075
- lines.push(`Point it at ${pintor.underline(modernPath)}, Extension.js already emits the page there.`);
10076
- return lines.join('\n');
10077
- }
10078
- function fatalManifestShapeFixed(field, detail) {
10079
- const lines = [];
10080
- lines.push(`${messaging_prefix('warn')} Repaired the ${pintor.blue(field)} field, which Chrome refuses to load the extension over.`);
10081
- lines.push(`${pintor.gray('REASON')} ${pintor.underline(detail)}`);
10082
- lines.push(`Fix the field in your ${pintor.blue('manifest.json')} file.`);
10083
- return lines.join('\n');
10084
- }
10085
- function invalidThemeValue(field, detail, value) {
10086
- const lines = [];
10087
- lines.push(`Check the ${pintor.yellow(field)} field in your ${pintor.yellow('manifest.json')} file.`);
10088
- lines.push(detail);
10089
- lines.push(`Chrome rejects the whole extension when this value is malformed.\nThe build stops here to protect you.`);
10090
- lines.push('');
10091
- lines.push(`${pintor.red('INVALID VALUE')} ${value}`);
10092
- return lines.join('\n');
10093
- }
10094
- function themeNotSupportedByBrowser(browser) {
10095
- const lines = [];
10096
- lines.push(`${messaging_prefix('warn')} ${pintor.blue(browser)} does not support the ${pintor.yellow('theme')} field.`);
10097
- lines.push("The field ships unchanged in the manifest and Safari ignores it.");
10098
- return lines.join('\n');
10099
- }
10100
- function missingGeckoDataCollectionPermissions() {
10101
- const lines = [];
10102
- lines.push(`${messaging_prefix('warn')} addons.mozilla.org requires ${pintor.blue('browser_specific_settings.gecko.data_collection_permissions')} for new add-ons.`);
10103
- lines.push(`Declare ${pintor.blue('{"required": ["none"]}')} if this extension transmits no data.`);
10104
- lines.push(`See ${pintor.underline('https://extensionworkshop.com/documentation/develop/firefox-builtin-data-consent/')} for details.`);
10105
- return lines.join('\n');
10106
- }
10107
- function manifestInvalidError(error) {
10108
- const lines = [];
10109
- lines.push(`Can't read your ${pintor.blue('manifest.json')} file.`);
10110
- lines.push(`${pintor.gray('REASON')} ${pintor.underline(String(error))}`);
10111
- lines.push("Update your manifest and try again.");
10112
- return lines.join('\n');
10113
- }
10114
- function manifestIncludeSummary(browser, manifestPath) {
10115
- return `${messaging_prefix('debug')} manifest include browser=${browser} path=${manifestPath}`;
10116
- }
10117
- function manifestEmitSuccess() {
10118
- return `${messaging_prefix('debug')} manifest emitted=true schemaStripped=true`;
10119
- }
10120
- function manifestOverridesSummary(overrideKeys, devCssStubsAdded) {
10121
- return `${messaging_prefix('debug')} manifest overrides keys=${String(overrideKeys)} devCssStubs=${String(devCssStubsAdded)}`;
10122
- }
10123
- function manifestDepsTracked(addedCount) {
10124
- return `${messaging_prefix('debug')} manifest deps=${String(addedCount)}`;
10125
- }
10126
- function manifestLegacyWarningsSummary(count) {
10127
- return `${messaging_prefix('debug')} manifest legacyWarnings=${String(count)}`;
10128
- }
10129
- function pageActionNotSupportedByBrowser(browser) {
10130
- return `${messaging_prefix('warn')} ${pintor.blue(browser)} does not show the ${pintor.yellow('page_action')} address bar surface.\nThe field was dropped from the built manifest and its page was not emitted.`;
10131
- }
10132
- function pageActionDroppedForBrowserAction(browser) {
10133
- return `${messaging_prefix('warn')} ${pintor.blue(browser)} refuses a Manifest V2 manifest that declares both ${pintor.yellow('browser_action')} and ${pintor.yellow('page_action')}.\nThe build kept ${pintor.yellow('browser_action')} and dropped ${pintor.yellow('page_action')} from the built manifest, so its page was not emitted. Remove one of the two keys, or scope page_action with the firefox: prefix.`;
10134
- }
10135
- function vendorPrefixedKeyDropped(key, familyKey, vendor, browser) {
10136
- const vendorName = 'edge' === vendor ? 'Edge' : 'Chrome';
10137
- return `${messaging_prefix('warn')} ${pintor.yellow(key)} now applies only to ${vendorName} builds, so the ${pintor.blue(browser)} build dropped it.\nRename it to ${pintor.yellow(familyKey)} to keep it on every Chromium-based browser. Leave it as is if it's meant for ${vendorName} only.`;
10138
- }
10139
- function mv2SandboxPolicyDropped(browser) {
10140
- return `${messaging_prefix('warn')} ${pintor.blue(browser)} reads a Manifest V2 ${pintor.yellow('content_security_policy')} as one string, so the ${pintor.yellow('sandbox')} slot has nowhere to go.\nThe build wrote the ${pintor.yellow('extension_pages')} policy as that string and dropped the sandbox policy from the built manifest. Scope the object form with the chromium: prefix, or declare a Manifest V3 build for this browser.`;
10141
- }
10142
- function geckoSidePanelUnsupported(file) {
10143
- const lines = [];
10144
- lines.push(`${messaging_prefix('warn')} ${pintor.underline(file)} uses chrome.sidePanel, which is Chromium only.`);
10145
- lines.push(`Firefox opens a sidebar through the ${pintor.yellow('sidebar_action')} manifest key on every manifest version, and addons-linter flags the call as UNSUPPORTED_API.`);
10146
- lines.push(`Move the call behind a build-time branch on ${pintor.blue('import.meta.env.EXTENSION_PUBLIC_BROWSER')} so the Firefox bundle drops it.`);
10147
- return lines.join('\n');
10148
- }
10149
- function geckoActionUnsupportedOnMv2(file) {
10150
- const lines = [];
10151
- lines.push(`${messaging_prefix('warn')} ${pintor.underline(file)} uses chrome.action, which Manifest V2 does not have on Firefox.`);
10152
- lines.push("Firefox Manifest V2 exposes the toolbar button as browserAction, and addons-linter flags the call as UNSUPPORTED_API.");
10153
- lines.push(`Use browserAction behind a build-time branch on ${pintor.blue('import.meta.env.EXTENSION_PUBLIC_BROWSER')}, or declare Manifest V3 for Firefox with ${pintor.yellow('firefox:manifest_version')}.`);
10154
- return lines.join('\n');
10155
- }
10156
10486
  class AddDependencies {
10157
10487
  dependencyList;
10158
10488
  constructor(dependencyList){
@@ -10582,9 +10912,9 @@ function findInjectedOnlyPermissionUses(compilation, declared, injected) {
10582
10912
  if (!resource) continue;
10583
10913
  let source;
10584
10914
  try {
10585
- const stat = __rspack_external_fs.statSync(resource);
10915
+ const stat = __rspack_external_node_fs_1b05aee1.statSync(resource);
10586
10916
  if (stat.size > 1048576) continue;
10587
- source = __rspack_external_fs.readFileSync(resource, 'utf-8');
10917
+ source = __rspack_external_node_fs_1b05aee1.readFileSync(resource, 'utf-8');
10588
10918
  } catch {
10589
10919
  continue;
10590
10920
  }
@@ -10678,7 +11008,7 @@ class ApplyDevDefaults {
10678
11008
  const declared = new Set(canonicalManifest.permissions || []);
10679
11009
  const uses = findInjectedOnlyPermissionUses(compilation, declared, injectedPermissions);
10680
11010
  for (const [api, file] of uses){
10681
- const relative = __rspack_external_path.relative(__rspack_external_path.dirname(this.manifestPath), file);
11011
+ const relative = __rspack_external_node_path_806ed179.relative(__rspack_external_node_path_806ed179.dirname(this.manifestPath), file);
10682
11012
  const text = optionalPermissions.has(api) ? `manifest.json only lists the "${api}" permission under optional_permissions, but ${relative} uses chrome.${api}. It works in development only because the dev instrumentation injects "${api}" as required: the production build has it only after a runtime chrome.permissions.request, so guard the use or move "${api}" to permissions.` : partiallyGatedWarning(api, relative) || `manifest.json does not declare the "${api}" permission, but ${relative} uses chrome.${api}. It works in development only because the dev instrumentation injects "${api}": the production build will fail at runtime. Add "${api}" to permissions in manifest.json.`;
10683
11013
  pushDevWarning('DevInjectedPermissionWarning', optionalPermissions.has(api) ? text + partiallyGatedNote(api) : text);
10684
11014
  }
@@ -10687,7 +11017,7 @@ class ApplyDevDefaults {
10687
11017
  const injectedOnlyHosts = findInjectedOnlyHostUses(compilation.modules, contentScriptMatches, declaredHostPatterns(canonicalManifest), optionalHostPatterns(canonicalManifest));
10688
11018
  const hostKey = 3 === canonicalManifest.manifest_version ? 'host_permissions' : 'permissions';
10689
11019
  for (const use of injectedOnlyHosts){
10690
- const relative = __rspack_external_path.relative(__rspack_external_path.dirname(this.manifestPath), use.file);
11020
+ const relative = __rspack_external_node_path_806ed179.relative(__rspack_external_node_path_806ed179.dirname(this.manifestPath), use.file);
10691
11021
  const text = use.optional ? `manifest.json only lists "${use.pattern}" under optional host permissions, but ${relative} requests ${use.url}. It works in development only because a content script matches "${use.pattern}" and the dev build grants that host as required so it can re-inject the script on save. The production build has the host only after a runtime chrome.permissions.request, so guard the request or add "${use.pattern}" to ${hostKey} in manifest.json.` : `manifest.json does not grant host access to ${use.url}, but ${relative} requests it. The dev build grants that host because a content script matches "${use.pattern}" and it re-injects the script on save. The production build has no host permission for it, so the request is subject to the server's CORS policy and is blocked when the server does not allow it. If it needs the permission, add "${requestOriginPattern(use.url, use.pattern)}" to ${hostKey} in manifest.json rather than the content-script pattern.`;
10692
11022
  pushDevWarning('DevInjectedHostWarning', text);
10693
11023
  }
@@ -11674,6 +12004,22 @@ class PersistManifestToDisk {
11674
12004
  });
11675
12005
  }
11676
12006
  }
12007
+ function dropEdgeStoreKey(manifest, browser) {
12008
+ if ('edge' !== String(browser)) return {
12009
+ manifest,
12010
+ dropped: false
12011
+ };
12012
+ const source = manifest;
12013
+ if (!('key' in source)) return {
12014
+ manifest,
12015
+ dropped: false
12016
+ };
12017
+ const { key: _key, ...rest } = source;
12018
+ return {
12019
+ manifest: rest,
12020
+ dropped: true
12021
+ };
12022
+ }
11677
12023
  function gecko_data_collection_missingGeckoDataCollectionPermissions(manifest) {
11678
12024
  const bss = manifest.browser_specific_settings;
11679
12025
  const declaration = bss?.gecko?.data_collection_permissions;
@@ -12784,7 +13130,149 @@ function patchGeckoBackground(manifest, browser) {
12784
13130
  }
12785
13131
  };
12786
13132
  }
12787
- function geckoUnsupportedApis(manifestVersion) {
13133
+ function patchWebkitBackground(manifest, browser) {
13134
+ if (!isWebkitBasedBrowser(String(browser))) return manifest;
13135
+ const background = manifest.background;
13136
+ if (!background || !background.service_worker || background.scripts) return manifest;
13137
+ const { service_worker, type, ...rest } = background;
13138
+ return {
13139
+ ...manifest,
13140
+ background: {
13141
+ ...rest,
13142
+ scripts: [
13143
+ service_worker
13144
+ ]
13145
+ }
13146
+ };
13147
+ }
13148
+ const webkitUnsupportedApis = {
13149
+ sidePanel: safariSidePanelUnsupported,
13150
+ offscreen: safariOffscreenUnsupported,
13151
+ tabGroups: safariTabGroupsUnsupported,
13152
+ management: safariManagementUnsupported,
13153
+ userScripts: safariUserScriptsUnsupported,
13154
+ identity: safariIdentityUnsupported,
13155
+ notifications: safariNotificationsUnsupported,
13156
+ omnibox: safariOmniboxUnsupported,
13157
+ bookmarks: safariBookmarksUnsupported,
13158
+ history: safariHistoryUnsupported,
13159
+ downloads: safariDownloadsUnsupported,
13160
+ idle: safariIdleUnsupported
13161
+ };
13162
+ const webkitUnsupportedMembers = [
13163
+ {
13164
+ api: 'action',
13165
+ member: 'getUserSettings',
13166
+ kind: 'call'
13167
+ },
13168
+ {
13169
+ api: 'action',
13170
+ member: 'getBadgeTextColor',
13171
+ kind: 'call'
13172
+ },
13173
+ {
13174
+ api: 'action',
13175
+ member: 'setBadgeTextColor',
13176
+ kind: 'call'
13177
+ },
13178
+ {
13179
+ api: 'action',
13180
+ member: 'onUserSettingsChanged',
13181
+ kind: 'read'
13182
+ },
13183
+ {
13184
+ api: 'storage',
13185
+ member: 'managed',
13186
+ kind: 'read'
13187
+ },
13188
+ {
13189
+ api: 'runtime',
13190
+ member: 'getContexts',
13191
+ kind: 'call'
13192
+ },
13193
+ {
13194
+ api: 'runtime',
13195
+ member: 'onSuspend',
13196
+ kind: 'read'
13197
+ },
13198
+ {
13199
+ api: 'runtime',
13200
+ member: 'onSuspendCanceled',
13201
+ kind: 'read'
13202
+ },
13203
+ {
13204
+ api: 'runtime',
13205
+ member: 'onUpdateAvailable',
13206
+ kind: 'read'
13207
+ },
13208
+ {
13209
+ api: 'declarativeNetRequest',
13210
+ member: 'getAvailableStaticRuleCount',
13211
+ kind: 'call'
13212
+ },
13213
+ {
13214
+ api: 'declarativeNetRequest',
13215
+ member: 'getDisabledRuleIds',
13216
+ kind: 'call'
13217
+ },
13218
+ {
13219
+ api: 'declarativeNetRequest',
13220
+ member: 'updateStaticRules',
13221
+ kind: 'call'
13222
+ },
13223
+ {
13224
+ api: 'declarativeNetRequest',
13225
+ member: 'testMatchOutcome',
13226
+ kind: 'call'
13227
+ },
13228
+ {
13229
+ api: 'declarativeNetRequest',
13230
+ member: 'onRuleMatchedDebug',
13231
+ kind: 'read'
13232
+ },
13233
+ {
13234
+ api: 'tabs',
13235
+ member: 'group',
13236
+ kind: 'call'
13237
+ },
13238
+ {
13239
+ api: 'tabs',
13240
+ member: 'ungroup',
13241
+ kind: 'call'
13242
+ },
13243
+ {
13244
+ api: 'webNavigation',
13245
+ member: 'onCreatedNavigationTarget',
13246
+ kind: 'read'
13247
+ },
13248
+ {
13249
+ api: 'webNavigation',
13250
+ member: 'onHistoryStateUpdated',
13251
+ kind: 'read'
13252
+ },
13253
+ {
13254
+ api: 'webNavigation',
13255
+ member: 'onReferenceFragmentUpdated',
13256
+ kind: 'read'
13257
+ },
13258
+ {
13259
+ api: 'webNavigation',
13260
+ member: 'onTabReplaced',
13261
+ kind: 'read'
13262
+ },
13263
+ {
13264
+ api: 'windows',
13265
+ member: 'onBoundsChanged',
13266
+ kind: 'read'
13267
+ }
13268
+ ];
13269
+ const webkitMemberEntries = webkitUnsupportedMembers.filter((entry)=>!(entry.api in webkitUnsupportedApis));
13270
+ const webkitMemberByKey = new Map(webkitUnsupportedMembers.map((entry)=>[
13271
+ `${entry.api}.${entry.member}`,
13272
+ entry
13273
+ ]));
13274
+ function geckoUnsupportedApis(manifestVersion, engine = 'gecko') {
13275
+ if ('webkit' === engine) return Object.keys(webkitUnsupportedApis);
12788
13276
  return 2 === manifestVersion ? [
12789
13277
  'sidePanel',
12790
13278
  'action'
@@ -12792,10 +13280,31 @@ function geckoUnsupportedApis(manifestVersion) {
12792
13280
  'sidePanel'
12793
13281
  ];
12794
13282
  }
12795
- function usesGeckoUnsupportedApi(source, api) {
12796
- const memberRe = new RegExp(`\\b(?:chrome|browser)\\s*\\.\\s*${api}\\s*\\??\\.\\s*[A-Za-z_$]`);
13283
+ function usesGeckoUnsupportedApi(source, api, engine = 'gecko') {
13284
+ const chain = 'webkit' === engine ? '' : '\\??';
13285
+ const memberRe = new RegExp(`\\b(?:chrome|browser)\\s*\\.\\s*${api}\\s*${chain}\\.\\s*[A-Za-z_$]`);
13286
+ return memberRe.test(source);
13287
+ }
13288
+ function usesWebkitUnsupportedMember(source, entry) {
13289
+ const tail = 'call' === entry.kind ? '\\s*\\(' : '\\s*\\.\\s*[A-Za-z_$]';
13290
+ const memberRe = new RegExp(`\\b(?:chrome|browser)\\s*\\.\\s*${entry.api}\\s*\\.\\s*${entry.member}(?![\\w$])${tail}`);
12797
13291
  return memberRe.test(source);
12798
13292
  }
13293
+ function scanTargets(manifestVersion, engine) {
13294
+ const targets = geckoUnsupportedApis(manifestVersion, engine).map((api)=>({
13295
+ key: api,
13296
+ api,
13297
+ test: (text)=>usesGeckoUnsupportedApi(text, api, engine)
13298
+ }));
13299
+ if ('webkit' !== engine) return targets;
13300
+ for (const entry of webkitMemberEntries)targets.push({
13301
+ key: `${entry.api}.${entry.member}`,
13302
+ api: entry.api,
13303
+ member: entry.member,
13304
+ test: (text)=>usesWebkitUnsupportedMember(text, entry)
13305
+ });
13306
+ return targets;
13307
+ }
12799
13308
  const EMITTED_SCRIPT_RE = /\.[cm]?js$/;
12800
13309
  const MAX_SOURCE_BYTES = 1048576;
12801
13310
  const MAX_ASSET_BYTES = 16777216;
@@ -12833,8 +13342,8 @@ function emittedFilesOf(compilation, module) {
12833
13342
  return;
12834
13343
  }
12835
13344
  }
12836
- function findGeckoUnsupportedApiUses(compilation, manifestVersion) {
12837
- const apis = geckoUnsupportedApis(manifestVersion);
13345
+ function findGeckoUnsupportedApiUses(compilation, manifestVersion, engine = 'gecko', requireEmittedEvidence = false) {
13346
+ const targets = scanTargets(manifestVersion, engine);
12838
13347
  const emitted = readEmittedScripts(compilation);
12839
13348
  const explained = new Set();
12840
13349
  const uses = new Map();
@@ -12848,20 +13357,23 @@ function findGeckoUnsupportedApiUses(compilation, manifestVersion) {
12848
13357
  const resource = scannableSourcePath(module.resource);
12849
13358
  if (!resource) continue;
12850
13359
  const source = readProjectSource(resource);
12851
- if (void 0 !== source) for (const api of apis){
12852
- const key = `${api}\0${resource}`;
12853
- if (uses.has(key) || !usesGeckoUnsupportedApi(source, api)) continue;
13360
+ if (void 0 !== source) for (const target of targets){
13361
+ const key = `${target.key}\0${resource}`;
13362
+ if (uses.has(key) || !target.test(source)) continue;
12854
13363
  const files = emittedFilesOf(compilation, outer);
12855
13364
  if (files) {
12856
13365
  const carrying = files.filter((file)=>{
12857
13366
  const text = emitted.get(file);
12858
- return void 0 !== text && usesGeckoUnsupportedApi(text, api);
13367
+ return void 0 !== text && target.test(text);
12859
13368
  });
12860
13369
  if (!carrying.length) continue;
12861
- for (const file of carrying)explained.add(`${api}\0${file}`);
12862
- }
13370
+ for (const file of carrying)explained.add(`${target.key}\0${file}`);
13371
+ } else if (requireEmittedEvidence) continue;
12863
13372
  uses.set(key, {
12864
- api,
13373
+ api: target.api,
13374
+ ...target.member ? {
13375
+ member: target.member
13376
+ } : {},
12865
13377
  file: resource,
12866
13378
  emitted: false
12867
13379
  });
@@ -12871,10 +13383,13 @@ function findGeckoUnsupportedApiUses(compilation, manifestVersion) {
12871
13383
  if (!compilation.chunkGraph) return [
12872
13384
  ...uses.values()
12873
13385
  ];
12874
- for (const [name, text] of emitted)for (const api of apis){
12875
- const key = `${api}\0${name}`;
12876
- if (!explained.has(key) && usesGeckoUnsupportedApi(text, api)) uses.set(key, {
12877
- api,
13386
+ for (const [name, text] of emitted)for (const target of targets){
13387
+ const key = `${target.key}\0${name}`;
13388
+ if (!explained.has(key) && target.test(text)) uses.set(key, {
13389
+ api: target.api,
13390
+ ...target.member ? {
13391
+ member: target.member
13392
+ } : {},
12878
13393
  file: name,
12879
13394
  emitted: true
12880
13395
  });
@@ -12896,16 +13411,25 @@ function relativeToProject(projectPath, file) {
12896
13411
  }
12897
13412
  return __rspack_external_node_path_806ed179.relative(projectPath, file) || file;
12898
13413
  }
12899
- function reportGeckoUnsupportedApis(compilation, compiler, browser, manifest, projectPath) {
12900
- if ('production' !== compiler.options.mode) return;
12901
- if (!isGeckoBasedBrowser(String(browser))) return;
13414
+ function reportGeckoUnsupportedApis(compilation, compiler, browser, manifest, projectPath, reported) {
13415
+ const engine = isGeckoBasedBrowser(String(browser)) ? 'gecko' : isWebkitBasedBrowser(String(browser)) ? 'webkit' : void 0;
13416
+ if (!engine) return;
13417
+ const isProduction = 'production' === compiler.options.mode;
13418
+ if (!isProduction && 'webkit' !== engine) return;
12902
13419
  try {
12903
- const uses = findGeckoUnsupportedApiUses(compilation, manifest.manifest_version);
13420
+ const uses = findGeckoUnsupportedApiUses(compilation, manifest.manifest_version, engine, !isProduction);
12904
13421
  for (const use of uses){
12905
13422
  const label = use.emitted ? use.file : relativeToProject(projectPath, use.file);
12906
- const text = 'sidePanel' === use.api ? geckoSidePanelUnsupported(label) : geckoActionUnsupportedOnMv2(label);
13423
+ if (!isProduction && reported) {
13424
+ const signature = `${use.api}.${use.member || ''}\0${label}`;
13425
+ if (reported.has(signature)) continue;
13426
+ reported.add(signature);
13427
+ }
13428
+ const memberEntry = use.member ? webkitMemberByKey.get(`${use.api}.${use.member}`) : void 0;
13429
+ const webkitMessage = 'webkit' !== engine || memberEntry ? void 0 : webkitUnsupportedApis[use.api];
13430
+ const text = memberEntry ? safariMemberUnsupported(label, memberEntry.api, memberEntry.member, memberEntry.kind) : webkitMessage ? webkitMessage(label) : 'sidePanel' === use.api ? geckoSidePanelUnsupported(label) : geckoActionUnsupportedOnMv2(label);
12907
13431
  const warn = new core_WebpackError(text);
12908
- warn.name = 'GeckoUnsupportedApiWarning';
13432
+ warn.name = 'webkit' === engine ? 'SafariUnsupportedApiWarning' : 'GeckoUnsupportedApiWarning';
12909
13433
  warn.file = label;
12910
13434
  compilation.warnings.push(warn);
12911
13435
  }
@@ -12915,6 +13439,7 @@ class UpdateManifest {
12915
13439
  manifestPath;
12916
13440
  browser;
12917
13441
  reportedFatalFixes = new Set();
13442
+ reportedUnsupportedApis = new Set();
12918
13443
  constructor(options){
12919
13444
  this.manifestPath = options.manifestPath;
12920
13445
  this.browser = options.browser || 'chrome';
@@ -12948,8 +13473,16 @@ class UpdateManifest {
12948
13473
  for (const dropped of findDroppedVendorKeys(manifest, this.browser))if (dropped.appliedBefore) reportToCompilation(compilation, compiler, vendorPrefixedKeyDropped(dropped.path, dropped.familyPath, dropped.vendor, String(this.browser)), 'warning', 'manifest.json');
12949
13474
  const projectPath = compiler.options.context || __rspack_external_node_path_806ed179.dirname(this.manifestPath);
12950
13475
  let patchedManifest = buildCanonicalManifest(this.manifestPath, manifest, this.browser, projectPath);
13476
+ if ('production' === compiler.options.mode) {
13477
+ const edgeKey = dropEdgeStoreKey(patchedManifest, this.browser);
13478
+ if (edgeKey.dropped) {
13479
+ patchedManifest = edgeKey.manifest;
13480
+ reportToCompilation(compilation, compiler, edgeStoreKeyDropped(String(this.browser)), 'warning', 'manifest.json');
13481
+ }
13482
+ }
12951
13483
  patchedManifest = patchGeckoBackground(patchedManifest, this.browser);
12952
13484
  patchedManifest = patchChromiumBackground(patchedManifest, this.browser);
13485
+ patchedManifest = patchWebkitBackground(patchedManifest, this.browser);
12953
13486
  patchedManifest = patchChromiumThemeColors(patchedManifest, this.browser);
12954
13487
  const overrides = getManifestOverrides(this.manifestPath, manifest, projectPath);
12955
13488
  if ('development' === compiler.options.mode) {
@@ -12989,7 +13522,7 @@ class UpdateManifest {
12989
13522
  warn.file = 'manifest.json';
12990
13523
  compilation.warnings.push(warn);
12991
13524
  }
12992
- reportGeckoUnsupportedApis(compilation, compiler, this.browser, patchedManifest, projectPath);
13525
+ reportGeckoUnsupportedApis(compilation, compiler, this.browser, patchedManifest, projectPath, this.reportedUnsupportedApis);
12993
13526
  const source = JSON.stringify(patchedManifest, null, 2);
12994
13527
  const rawSource = new core_sources.RawSource(source);
12995
13528
  setCurrentManifestContent(compilation, source);