Revert "feat!: remove features related to the old thread page view"

This reverts commit 879d44aef99524c84719d91c628e772fba9335cb.

Reason for revert: It turns out the old reply editor can still be used
in the new thread page view by pressing the "R" key, and this can/will
make the CC communicate with the draft endpoints to save/retrieve
information.

Apart from this, some PEs were using the "old thread page" option as
well, even if it doesn't load all replies.

Finally, this revert also contains some changes to the modifications to
the data startup object so they work correctly (since now we have to use
StartupDataStorage, otherwise changes might not be saved).

Bug: twpowertools:124
Change-Id: Ie8153b9c8ef150b2417cb19b73bde0a5593876e2
diff --git a/src/common/options/optionsPermissions.js b/src/common/options/optionsPermissions.js
index f005131..4dea4bc 100644
--- a/src/common/options/optionsPermissions.js
+++ b/src/common/options/optionsPermissions.js
@@ -9,6 +9,9 @@
 const requiredPermissions = {
   permissions: new Set([
     'storage', 'alarms',
+    // #!if ['chromium', 'chromium_mv3'].includes(browser_target)
+    'declarativeNetRequestWithHostAccess',
+    // #!endif
     // #!if browser_target == 'chromium_mv3'
     'scripting',
     // #!endif
diff --git a/src/common/options/optionsPrototype.ts b/src/common/options/optionsPrototype.ts
index 62f3929..a5f7a93 100644
--- a/src/common/options/optionsPrototype.ts
+++ b/src/common/options/optionsPrototype.ts
@@ -32,6 +32,11 @@
     context: OptionContext.Options,
     killSwitchType: KillSwitchType.Option,
   },
+  loaddrafts: {
+    defaultValue: false,
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Option,
+  },
   increasecontrast: {
     defaultValue: false,
     context: OptionContext.Options,
@@ -109,11 +114,28 @@
     context: OptionContext.Options,
     killSwitchType: KillSwitchType.Option,
   },
+  // #!if ['chromium', 'chromium_mv3'].includes(browser_target)
+  blockdrafts: {
+    defaultValue: false,
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Option,
+  },
+  // #!endif
   perforumstats: {
     defaultValue: false,
     context: OptionContext.Options,
     killSwitchType: KillSwitchType.Option,
   },
+  interopthreadpage: {
+    defaultValue: false,
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Option,
+  },
+  interopthreadpage_mode: {
+    defaultValue: 'previous',
+    context: OptionContext.Options,
+    killSwitchType: KillSwitchType.Ignore,
+  },
   uispacing: {
     defaultValue: false,
     context: OptionContext.Options,
@@ -192,28 +214,6 @@
     context: OptionContext.Deprecated,
     killSwitchType: KillSwitchType.Deprecated,
   },
-  loaddrafts: {
-    defaultValue: false,
-    context: OptionContext.Deprecated,
-    killSwitchType: KillSwitchType.Deprecated,
-  },
-  // #!if ['chromium', 'chromium_mv3'].includes(browser_target)
-  blockdrafts: {
-    defaultValue: false,
-    context: OptionContext.Deprecated,
-    killSwitchType: KillSwitchType.Deprecated,
-  },
-  // #!endif
-  interopthreadpage: {
-    defaultValue: false,
-    context: OptionContext.Deprecated,
-    killSwitchType: KillSwitchType.Deprecated,
-  },
-  interopthreadpage_mode: {
-    defaultValue: 'previous',
-    context: OptionContext.Deprecated,
-    killSwitchType: KillSwitchType.Ignore,
-  },
   fixpekb269560789: {
     defaultValue: false,
     context: OptionContext.Deprecated,
diff --git a/src/common/options/specialOptions.json5 b/src/common/options/specialOptions.json5
index b444c82..ada9ac4 100644
--- a/src/common/options/specialOptions.json5
+++ b/src/common/options/specialOptions.json5
@@ -2,4 +2,5 @@
   'profileindicatoralt_months',
   'ccdarktheme_mode',
   'ccdarktheme_switch_status',
+  'interopthreadpage_mode',
 ]
diff --git a/src/contentScripts/communityConsole/main.js b/src/contentScripts/communityConsole/main.js
index 890f3b0..f5d5a47 100644
--- a/src/contentScripts/communityConsole/main.js
+++ b/src/contentScripts/communityConsole/main.js
@@ -43,6 +43,9 @@
   // Thread list (used for the autorefresh feature)
   'ec-thread-list',
 
+  // Thread page main content
+  'ec-thread > .page > .material-content > div[role="list"]',
+
   // Thread page reply section (for the thread page toolbar)
   kRepliesSectionSelector,
 
@@ -90,6 +93,12 @@
       avatars.injectIfEnabled(node);
     }
 
+    // Inject old thread page design warning if applicable
+    if (node.matches(
+            'ec-thread > .page > .material-content > div[role="list"]')) {
+      window.TWPTThreadPageDesignWarning.injectWarningIfApplicable(node);
+    }
+
     // Inject thread toolbar
     if (threadToolbar.shouldInject(node)) {
       threadToolbar.injectIfApplicable(node);
diff --git a/src/contentScripts/communityConsole/start.js b/src/contentScripts/communityConsole/start.js
index f890bd0..64bc2a0 100644
--- a/src/contentScripts/communityConsole/start.js
+++ b/src/contentScripts/communityConsole/start.js
@@ -2,8 +2,13 @@
 import {getOptions} from '../../common/options/optionsUtils.js';
 
 import FlattenThreadsReplyActionHandler from './flattenThreads/replyActionHandler.js';
+import ThreadPageDesignWarning from './threadPageDesignWarning.js';
 
 getOptions(null).then(options => {
+  // Initialized here instead of in main.js so the first event is received if it
+  // happens when the page loads.
+  window.TWPTThreadPageDesignWarning = new ThreadPageDesignWarning();
+
   if (options.uispacing) {
     injectStylesheet(chrome.runtime.getURL('css/ui_spacing/shared.css'));
     injectStylesheet(chrome.runtime.getURL('css/ui_spacing/console.css'));
diff --git a/src/contentScripts/communityConsole/threadPageDesignWarning.js b/src/contentScripts/communityConsole/threadPageDesignWarning.js
new file mode 100644
index 0000000..955108a
--- /dev/null
+++ b/src/contentScripts/communityConsole/threadPageDesignWarning.js
@@ -0,0 +1,162 @@
+import {MDCTooltip} from '@material/tooltip';
+import {waitFor} from 'poll-until-promise';
+
+import {parseUrl} from '../../common/commonUtils.js';
+import {injectStylesheet} from '../../common/contentScriptsUtils';
+import {getDocURL} from '../../common/extUtils.js';
+import {getOptions} from '../../common/options/optionsUtils.js';
+
+import {createExtBadge} from './utils/common.js';
+
+const kSMEINestedReplies = 15;
+const kViewThreadResponse = 'TWPT_ViewThreadResponse';
+
+export default class ThreadPageDesignWarning {
+  constructor() {
+    this.lastThread = {
+      body: {},
+      id: -1,
+      timestamp: 0,
+    };
+    this.setUpHandler();
+
+    // We have to save whether the old UI was enabled at startup, since that's
+    // the moment when it takes effect. If the option changes while the tab is
+    // open, it won't take effect.
+    getOptions([
+      'interopthreadpage', 'interopthreadpage_mode'
+    ]).then(options => {
+      this.shouldShowWarning = options.interopthreadpage &&
+          options.interopthreadpage_mode == 'previous';
+
+      if (this.shouldShowWarning) {
+        injectStylesheet(
+            chrome.runtime.getURL('css/thread_page_design_warning.css'));
+      } else {
+        this.removeHandler();
+      }
+    });
+
+    this.isExperimentEnabled = this.isNestedRepliesExperimentEnabled();
+  }
+
+  isNestedRepliesExperimentEnabled() {
+    if (!document.documentElement.hasAttribute('data-startup')) return false;
+
+    let startup =
+        JSON.parse(document.documentElement.getAttribute('data-startup'));
+    return startup?.[1]?.[6]?.includes?.(kSMEINestedReplies);
+  }
+
+  eventHandler(e) {
+    if (e.detail.id < this.lastThread.id) return;
+
+    this.lastThread = {
+      body: e.detail.body,
+      id: e.detail.id,
+      timestamp: Date.now(),
+    };
+  }
+
+  setUpHandler() {
+    window.addEventListener(kViewThreadResponse, this.eventHandler.bind(this));
+  }
+
+  removeHandler() {
+    window.removeEventListener(
+        kViewThreadResponse, this.eventHandler.bind(this));
+  }
+
+  injectWarning(content) {
+    let div = document.createElement('div');
+    div.classList.add('TWPT-warning');
+
+    let icon = document.createElement('material-icon');
+    icon.classList.add('TWPT-warning--icon');
+
+    let iconContent = document.createElement('i');
+    iconContent.classList.add('material-icon-i', 'material-icons-extended');
+    iconContent.setAttribute('role', 'img');
+    iconContent.setAttribute('aria-hidden', 'true');
+    iconContent.textContent = 'warning';
+
+    icon.append(iconContent);
+
+    let text = document.createElement('div');
+    text.classList.add('TWPT-warning--text');
+    text.textContent =
+        chrome.i18n.getMessage('inject_threadpagedesign_warning');
+
+    let btn = document.createElement('a');
+    btn.classList.add('TWPT-warning--btn');
+    btn.href =
+        getDocURL('features.md#Thread-page-design-in-the-Community-Console');
+    btn.setAttribute('target', '_blank');
+    btn.setAttribute('rel', 'noopener noreferrer');
+
+    const [badge, badgeTooltip] = createExtBadge();
+
+    let btnText = document.createElement('div');
+    btnText.textContent = chrome.i18n.getMessage('btn_learnmore');
+
+    btn.append(badge, btnText);
+
+    div.append(icon, text, btn);
+    content.prepend(div);
+
+    new MDCTooltip(badgeTooltip);
+  }
+
+  injectWarningIfApplicable(content) {
+    return waitFor(
+               () => {
+                 if (this.shouldShowWarning === undefined)
+                   return Promise.reject(
+                       new Error('shouldShowWarning is not defined.'));
+
+                 return Promise.resolve({result: this.shouldShowWarning});
+               },
+               {
+                 interval: 500,
+                 timeout: 10 * 1000,
+               })
+        .then(preShouldShowWarning => {
+          if (!preShouldShowWarning.result) return;
+
+          // If the global SMEI experiment is enabled, all threads use nested
+          // replies, so we'll skip the per-thread check and always show the
+          // warning banner.
+          if (this.isExperimentEnabled) return Promise.resolve({result: true});
+
+          let currentThread = parseUrl(location.href);
+          if (currentThread === false)
+            throw new Error('current thread id cannot be parsed.');
+
+          return waitFor(() => {
+            let now = Date.now();
+            let lastThreadInfo = this.lastThread.body['1']?.['2']?.['1'];
+            if (now - this.lastThread.timestamp > 30 * 1000 ||
+                lastThreadInfo?.['1'] != currentThread.thread ||
+                lastThreadInfo?.['3'] != currentThread.forum)
+              throw new Error(
+                  'cannot obtain information about current thread.');
+
+            // If the messageOrGap field contains any items, the thread is using
+            // nested replies. Otherwise, it probably isn't using them.
+            return Promise.resolve(
+                {result: this.lastThread.body['1']?.['40']?.length > 0});
+          }, {
+            interval: 500,
+            timeout: 10 * 1000,
+          });
+        })
+        .then(shouldShowWarning => {
+          if (shouldShowWarning.result) this.injectWarning(content);
+        })
+        .catch(err => {
+          console.error(
+              '[threadPageDesignWarning] An error ocurred while trying to decide whether to show warning: ',
+              err);
+        });
+  }
+}
diff --git a/src/features/Features.ts b/src/features/Features.ts
index 67c93aa..afcba59 100644
--- a/src/features/Features.ts
+++ b/src/features/Features.ts
@@ -5,6 +5,8 @@
 import ExtraInfoFeature from './extraInfo/extraInfo.feature';
 import WorkflowsFeature from './workflows/workflows.feature';
 import CCDarkThemeFeature from './ccDarkTheme/ccDarkTheme.feature';
+import LoadDraftsFeature from './loadDrafts/loadDrafts.feature';
+import InteropThreadPageFeature from './interopThreadPage/interopThreadPage.feature';
 
 export type ConcreteFeatureClass = { new (): Feature };
 
@@ -14,6 +16,8 @@
     CCDarkThemeFeature,
     ExtraInfoFeature,
     InfiniteScrollFeature,
+    InteropThreadPageFeature,
+    LoadDraftsFeature,
     WorkflowsFeature,
   ];
   private initializedFeatures: Feature[];
diff --git a/src/features/interopThreadPage/interopThreadPage.feature.ts b/src/features/interopThreadPage/interopThreadPage.feature.ts
new file mode 100644
index 0000000..0d84075
--- /dev/null
+++ b/src/features/interopThreadPage/interopThreadPage.feature.ts
@@ -0,0 +1,16 @@
+import Feature from '../../common/architecture/features/Feature';
+import { ConcreteScript } from '../../common/architecture/scripts/Script';
+import { OptionCodename } from '../../common/options/optionsPrototype';
+import SetThreadPageInDataStartupScript from './scripts/setThreadPageInDataStartupScript.script';
+
+export default class InteropThreadPageFeature extends Feature {
+  public readonly scripts: ConcreteScript[] = [
+    SetThreadPageInDataStartupScript,
+  ];
+
+  readonly codename = 'interopThreadPage';
+  readonly relatedOptions: OptionCodename[] = [
+    'interopthreadpage',
+    'interopthreadpage_mode',
+  ];
+}
diff --git a/src/features/interopThreadPage/scripts/setThreadPageInDataStartupScript.script.ts b/src/features/interopThreadPage/scripts/setThreadPageInDataStartupScript.script.ts
new file mode 100644
index 0000000..32c17eb
--- /dev/null
+++ b/src/features/interopThreadPage/scripts/setThreadPageInDataStartupScript.script.ts
@@ -0,0 +1,53 @@
+import DependenciesProviderSingleton, {
+  OptionsProviderDependency,
+  StartupDataStorageDependency,
+} from '../../../common/architecture/dependenciesProvider/DependenciesProvider';
+import Script, {
+  ScriptEnvironment,
+  ScriptPage,
+  ScriptRunPhase,
+} from '../../../common/architecture/scripts/Script';
+
+const SMEI_RCE_THREAD_INTEROP = 22;
+
+export default class SetThreadPageInDataStartupScript extends Script {
+  page = ScriptPage.CommunityConsole;
+  environment = ScriptEnvironment.ContentScript;
+  runPhase = ScriptRunPhase.Start;
+
+  async execute() {
+    const dependenciesProvider = DependenciesProviderSingleton.getInstance();
+    const optionsProvider = dependenciesProvider.getDependency(
+      OptionsProviderDependency,
+    );
+    if (await optionsProvider.isEnabled('interopthreadpage')) {
+      const startupDataStorage = dependenciesProvider.getDependency(
+        StartupDataStorageDependency,
+      );
+      const mode = await optionsProvider.getOptionValue(
+        'interopthreadpage_mode',
+      );
+      startupDataStorage.enqueueModification((startupDataModel) => {
+        const index = startupDataModel.data[1][6].indexOf(
+          SMEI_RCE_THREAD_INTEROP,
+        );
+
+        switch (mode) {
+          case 'previous':
+            if (index > -1) {
+              startupDataModel.data[1][6].splice(index, 1);
+            }
+            break;
+
+          case 'next':
+            if (index == -1) {
+              startupDataModel.data[1][6].push(SMEI_RCE_THREAD_INTEROP);
+            }
+            break;
+        }
+      });
+      // NOTE: Workaround because otherwise the modifications would be applied too late.
+      startupDataStorage.applyModifications();
+    }
+  }
+}
diff --git a/src/features/loadDrafts/loadDrafts.feature.ts b/src/features/loadDrafts/loadDrafts.feature.ts
new file mode 100644
index 0000000..23f6873
--- /dev/null
+++ b/src/features/loadDrafts/loadDrafts.feature.ts
@@ -0,0 +1,13 @@
+import Feature from '../../common/architecture/features/Feature';
+import { ConcreteScript } from '../../common/architecture/scripts/Script';
+import { OptionCodename } from '../../common/options/optionsPrototype';
+import EnableLoadDraftsFlagInDataStartupScript from './scripts/setThreadPageInDataStartupScript.script';
+
+export default class LoadDraftsFeature extends Feature {
+  public readonly scripts: ConcreteScript[] = [
+    EnableLoadDraftsFlagInDataStartupScript,
+  ];
+
+  readonly codename = 'loadDrafts';
+  readonly relatedOptions: OptionCodename[] = ['loaddrafts'];
+}
diff --git a/src/features/loadDrafts/scripts/setThreadPageInDataStartupScript.script.ts b/src/features/loadDrafts/scripts/setThreadPageInDataStartupScript.script.ts
new file mode 100644
index 0000000..6319fb7
--- /dev/null
+++ b/src/features/loadDrafts/scripts/setThreadPageInDataStartupScript.script.ts
@@ -0,0 +1,32 @@
+import DependenciesProviderSingleton, {
+  OptionsProviderDependency,
+  StartupDataStorageDependency,
+} from '../../../common/architecture/dependenciesProvider/DependenciesProvider';
+import Script, {
+  ScriptEnvironment,
+  ScriptPage,
+  ScriptRunPhase,
+} from '../../../common/architecture/scripts/Script';
+
+export default class EnableLoadDraftsFlagInDataStartupScript extends Script {
+  page = ScriptPage.CommunityConsole;
+  environment = ScriptEnvironment.ContentScript;
+  runPhase = ScriptRunPhase.Start;
+
+  async execute() {
+    const dependenciesProvider = DependenciesProviderSingleton.getInstance();
+    const optionsProvider = dependenciesProvider.getDependency(
+      OptionsProviderDependency,
+    );
+    if (await optionsProvider.isEnabled('loaddrafts')) {
+      const startupDataStorage = dependenciesProvider.getDependency(
+        StartupDataStorageDependency,
+      );
+      startupDataStorage.enqueueModification((startupDataModel) => {
+        startupDataModel.data[4][13] = true;
+      });
+      // NOTE: Workaround because otherwise the modifications would be applied too late.
+      startupDataStorage.applyModifications();
+    }
+  }
+}
diff --git a/src/options/bgHandler.js b/src/options/bgHandler.js
index 828eb22..c97c935 100644
--- a/src/options/bgHandler.js
+++ b/src/options/bgHandler.js
@@ -3,23 +3,42 @@
 // content scripts or injected scripts but instead in the background
 // script/service worker.
 //
-// An example was the "blockdrafts" feature, which when enabled enabled the
+// An example is the "blockdrafts" feature, which when enabled should enable the
 // static ruleset blocking *DraftMessages requests.
-//
-// The "blockdrafts" feature was removed, but this logic has been kept in case
-// it is needed in the future.
 
 import {isOptionEnabled} from '../common/options/optionsUtils.js';
 
 // List of features controled in the background:
-export var bgFeatures = [];
+export var bgFeatures = [
+  'blockdrafts',
+];
+
+const blockDraftsRuleset = 'blockDrafts';
 
 export function handleBgOptionChange(feature) {
   isOptionEnabled(feature)
-      // eslint-disable-next-line no-unused-vars
       .then(enabled => {
-        // eslint-disable-next-line no-empty
-        switch (feature) {}
+        switch (feature) {
+          // #!if ['chromium', 'chromium_mv3'].includes(browser_target)
+          case 'blockdrafts':
+            chrome.declarativeNetRequest.getEnabledRulesets(rulesets => {
+              if (rulesets === undefined) {
+                throw new Error(
+                    chrome.runtime.lastError.message ??
+                    'Unknown error in chrome.declarativeNetRequest.getEnabledRulesets()');
+              }
+
+              let isRulesetEnabled = rulesets.includes(blockDraftsRuleset);
+              if (!isRulesetEnabled && enabled)
+                chrome.declarativeNetRequest.updateEnabledRulesets(
+                    {enableRulesetIds: [blockDraftsRuleset]});
+              if (isRulesetEnabled && !enabled)
+                chrome.declarativeNetRequest.updateEnabledRulesets(
+                    {disableRulesetIds: [blockDraftsRuleset]});
+            });
+            break;
+            // #!endif
+        }
       })
       .catch(err => {
         console.error(
diff --git a/src/options/optionsCommon.js b/src/options/optionsCommon.js
index badeaf1..6d41a39 100644
--- a/src/options/optionsCommon.js
+++ b/src/options/optionsCommon.js
@@ -19,6 +19,9 @@
       case 'ccdarktheme_mode':
         return document.getElementById(opt).value || 'switch';
 
+      case 'interopthreadpage_mode':
+        return document.getElementById(opt).value || 'previous';
+
       default:
         console.warn('Unrecognized option: ' + opt);
         return undefined;
@@ -223,6 +226,25 @@
                 .appendChild(select);
             break;
 
+          case 'interopthreadpage_mode':
+            var select = document.createElement('select');
+            select.id = 'interopthreadpage_mode';
+
+            const threadPageModes = ['previous', 'next'];
+            for (const mode of threadPageModes) {
+              let modeOption = document.createElement('option');
+              modeOption.value = mode;
+              modeOption.textContent = chrome.i18n.getMessage(
+                  'options_interopthreadpage_mode_' + mode);
+              if (items.interopthreadpage_mode == mode)
+                modeOption.selected = true;
+              select.appendChild(modeOption);
+            }
+
+            document.getElementById('interopthreadpage_mode--container')
+                .appendChild(select);
+            break;
+
           default:
             console.warn('Unrecognized option: ' + opt);
             break;
diff --git a/src/options/optionsPage.json5 b/src/options/optionsPage.json5
index 3234770..9435ece 100644
--- a/src/options/optionsPage.json5
+++ b/src/options/optionsPage.json5
@@ -16,6 +16,9 @@
         {codename: 'perforumstats'},
         {codename: 'batchlock'},
         {codename: 'autorefreshlist'},
+        // #!if ['chromium', 'chromium_mv3'].includes(browser_target)
+        {codename: 'blockdrafts'},
+        // #!endif
       ],
     },
     {
@@ -31,6 +34,7 @@
       options: [
         {codename: 'fixedtoolbar'},
         {codename: 'redirect'},
+        {codename: 'loaddrafts', experimental: true},
         {codename: 'increasecontrast'},
         {codename: 'stickysidebarheaders'},
         {codename: 'ccforcehidedrawer'},
@@ -40,6 +44,7 @@
         {codename: 'enhancedannouncementsdot'},
         {codename: 'repositionexpandthread', experimental: true},
         {codename: 'imagemaxheight'},
+        {codename: 'interopthreadpage'},
         {codename: 'uispacing'},
       ],
     },
diff --git a/src/static/_locales/en/messages.json b/src/static/_locales/en/messages.json
index 27c732c..4c655f7 100644
--- a/src/static/_locales/en/messages.json
+++ b/src/static/_locales/en/messages.json
@@ -63,6 +63,10 @@
     "message": "Redirect all threads opened in TW to the Community Console.",
     "description": "Feature checkbox in the options page"
   },
+  "options_loaddrafts": {
+    "message": "Activate the <code class=\"help\" title=\"This flag allows the Community Console to load a previously autosaved reply when loading a thread and clicking the reply button.\">enableLoadingDraftMessages<\/code> Community Console flag.",
+    "description": "Feature checkbox in the options page"
+  },
   "options_experimental_label": {
     "message": "(experimental)",
     "description": "Label which is placed next to an option to indicate that it may not work well or may break at any time"
@@ -135,6 +139,10 @@
     "message": "Show the number of questions and replies written by the OP within the last <span id='profileindicatoralt_months--container'></span> months next to their username.",
     "description": "Feature checkbox in the options page"
   },
+  "options_blockdrafts": {
+    "message": "Block the sending of your replies as you type to Google servers in the Community Console.",
+    "description": "Feature checkbox in the options page"
+  },
   "options_workflows": {
     "message": "Enable the workflows feature.",
     "description": "Feature checkbox in the options page"
@@ -151,6 +159,18 @@
     "message": "Show per-forum activity in profiles.",
     "description": "Feature checkbox in the options page"
   },
+  "options_interopthreadpage": {
+    "message": "Show <span id='interopthreadpage_mode--container'></span> in the Community Console.",
+    "description": "Feature checkbox in the options page. \"<span id='interopthreadpage_mode--container'></span>\" will be substituted with a selector which allows users to select whether they want the new thread page design or the old one."
+  },
+  "options_interopthreadpage_mode_previous": {
+    "message": "the old thread page design",
+    "description": "Select option added in #interopthreadpage_mode--container, in the options_interopthreadpage string"
+  },
+  "options_interopthreadpage_mode_next": {
+    "message": "the new thread page design",
+    "description": "Select option added in #interopthreadpage_mode--container, in the options_interopthreadpage string"
+  },
   "options_uispacing": {
     "message": "Reduce the whitespace in the Community Console and TW.",
     "description": "Feature checkbox in the options page"
diff --git a/templates/manifest.gjson b/templates/manifest.gjson
index 80c86a9..2f3f4cf 100644
--- a/templates/manifest.gjson
+++ b/templates/manifest.gjson
@@ -76,6 +76,9 @@
 #if defined(CHROMIUM || GECKO)
     "https://support.google.com/*",
 #endif
+#if defined(CHROMIUM || CHROMIUM_MV3)
+    "declarativeNetRequestWithHostAccess",
+#endif
 #if defined(CHROMIUM_MV3)
     "scripting",
 #endif
@@ -138,6 +141,15 @@
     }
 #endif
   ],
+#if defined(CHROMIUM || CHROMIUM_MV3)
+  "declarative_net_request": {
+    "rule_resources": [{
+      "id": "blockDrafts",
+      "enabled": false,
+      "path": "rulesets/blockDrafts.json"
+    }]
+  },
+#endif
 #if defined(CHROMIUM || GECKO)
   "browser_action": {},
 #endif