Bug 1778492 - Add an origin trial for coep: credentialless r=emilio,necko-reviewers,kershaw, a=dsmith
authorSean Feng <sefeng@mozilla.com>
Wed, 27 Jul 2022 20:33:19 +0000
changeset 696113 020a34d50aa935cd9c56dab065ba5d4d89929c50
parent 696112 a67d1e512a866e1cdba92b9b95fd258690b12644
child 696114 638cd4dcc05c29ff8ef4ef1ef2bc4ed767e07417
push id16927
push userdsmith@mozilla.com
push dateWed, 03 Aug 2022 11:45:55 +0000
treeherdermozilla-beta@dd6503f6943c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersemilio, necko-reviewers, kershaw, dsmith
bugs1778492
milestone104.0
Bug 1778492 - Add an origin trial for coep: credentialless r=emilio,necko-reviewers,kershaw, a=dsmith Differential Revision: https://phabricator.services.mozilla.com/D151381
dom/base/Document.cpp
dom/base/nsDOMWindowUtils.cpp
dom/fetch/tests/browser.ini
dom/fetch/tests/browser_origin_trial_coep_credentialless_cache.js
dom/fetch/tests/browser_origin_trial_coep_credentialless_fetch_1.js
dom/fetch/tests/browser_origin_trial_coep_credentialless_fetch_2.js
dom/fetch/tests/browser_origin_trial_coep_credentialless_fetch_3.js
dom/fetch/tests/browser_origin_trial_coep_credentialless_worker.js
dom/fetch/tests/credentialless_resource.sjs
dom/fetch/tests/credentialless_worker.sjs
dom/fetch/tests/open_credentialless_document.sjs
dom/fetch/tests/store_header.sjs
dom/interfaces/base/nsIDOMWindowUtils.idl
dom/origin-trials/OriginTrials.cpp
dom/origin-trials/OriginTrials.h
dom/origin-trials/ffi/lib.rs
dom/origin-trials/tests/mochitest.ini
dom/origin-trials/tests/test_meta_simple.html
dom/workers/loader/CacheLoadHandler.cpp
dom/workers/loader/ScriptResponseHeaderProcessor.cpp
ipc/glue/BackgroundUtils.cpp
modules/libpref/init/StaticPrefList.yaml
netwerk/base/LoadInfo.cpp
netwerk/base/LoadInfo.h
netwerk/base/TRRLoadInfo.cpp
netwerk/base/nsILoadInfo.idl
netwerk/base/nsNetUtil.cpp
netwerk/base/nsNetUtil.h
netwerk/ipc/NeckoChannelParams.ipdlh
netwerk/protocol/http/ClassifierDummyChannel.cpp
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/http/nsIHttpChannelInternal.idl
--- a/dom/base/Document.cpp
+++ b/dom/base/Document.cpp
@@ -3993,17 +3993,18 @@ nsresult Document::InitCOEP(nsIChannel* 
   nsCOMPtr<nsIHttpChannelInternal> intChannel = do_QueryInterface(httpChannel);
 
   if (!intChannel) {
     return NS_OK;
   }
 
   nsILoadInfo::CrossOriginEmbedderPolicy policy =
       nsILoadInfo::EMBEDDER_POLICY_NULL;
-  if (NS_SUCCEEDED(intChannel->GetResponseEmbedderPolicy(&policy))) {
+  if (NS_SUCCEEDED(intChannel->GetResponseEmbedderPolicy(
+          mTrials.IsEnabled(OriginTrial::CoepCredentialless), &policy))) {
     mEmbedderPolicy = Some(policy);
   }
 
   return NS_OK;
 }
 
 void Document::StopDocumentLoad() {
   if (mParser) {
@@ -6869,16 +6870,25 @@ void Document::SetHeaderData(nsAtom* aHe
     mMayNeedFontPrefsUpdate = true;
     if (auto* presContext = GetPresContext()) {
       presContext->ContentLanguageChanged();
     }
   }
 
   if (aHeaderField == nsGkAtoms::origin_trial) {
     mTrials.UpdateFromToken(aData, NodePrincipal());
+    if (mTrials.IsEnabled(OriginTrial::CoepCredentialless)) {
+      InitCOEP(mChannel);
+
+      WindowContext* ctx = GetWindowContext();
+      MOZ_ASSERT(ctx);
+      if (mEmbedderPolicy) {
+        Unused << ctx->SetEmbedderPolicy(mEmbedderPolicy.value());
+      }
+    }
   }
 
   if (aHeaderField == nsGkAtoms::headerDefaultStyle) {
     SetPreferredStyleSheetSet(aData);
   }
 
   if (aHeaderField == nsGkAtoms::refresh && !IsStaticDocument()) {
     // We get into this code before we have a script global yet, so get to our
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -4702,16 +4702,28 @@ nsDOMWindowUtils::IsCssPropertyRecordedI
 
   bool knownProp = false;
   *aRecorded = Servo_IsCssPropertyRecordedInUseCounter(
       doc->GetStyleUseCounters(), &aPropName, &knownProp);
   return knownProp ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
+nsDOMWindowUtils::IsCoepCredentialless(bool* aResult) {
+  Document* doc = GetDocument();
+  if (!doc) {
+    return NS_ERROR_FAILURE;
+  }
+
+  *aResult = IsCoepCredentiallessEnabled(
+      doc->Trials().IsEnabled(OriginTrial::CoepCredentialless));
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDOMWindowUtils::GetLayersId(uint64_t* aOutLayersId) {
   nsIWidget* widget = GetWidget();
   if (!widget) {
     return NS_ERROR_FAILURE;
   }
   BrowserChild* child = widget->GetOwningBrowserChild();
   if (!child) {
     return NS_ERROR_FAILURE;
--- a/dom/fetch/tests/browser.ini
+++ b/dom/fetch/tests/browser.ini
@@ -1,2 +1,23 @@
 [DEFAULT]
 [browser_blobFromFile.js]
+[browser_origin_trial_coep_credentialless_fetch_1.js]
+support-files =
+  open_credentialless_document.sjs
+  store_header.sjs
+[browser_origin_trial_coep_credentialless_fetch_2.js]
+support-files =
+  open_credentialless_document.sjs
+  store_header.sjs
+[browser_origin_trial_coep_credentialless_fetch_3.js]
+support-files =
+  open_credentialless_document.sjs
+  store_header.sjs
+[browser_origin_trial_coep_credentialless_worker.js]
+support-files =
+  open_credentialless_document.sjs
+  store_header.sjs
+  credentialless_worker.sjs
+[browser_origin_trial_coep_credentialless_cache.js]
+support-files =
+  open_credentialless_document.sjs
+  credentialless_resource.sjs
new file mode 100644
--- /dev/null
+++ b/dom/fetch/tests/browser_origin_trial_coep_credentialless_cache.js
@@ -0,0 +1,142 @@
+const TOP_LEVEL_URL =
+  getRootDirectory(gTestPath).replace(
+    "chrome://mochitests/content",
+    "https://example.com"
+  ) + "open_credentialless_document.sjs";
+
+const SAME_ORIGIN = "https://example.com";
+const CROSS_ORIGIN = "https://test1.example.com";
+
+const USE_CREDENTIALLESS = true;
+const NO_CREDENTIALLESS = false;
+
+const RESOURCE_URL =
+  getRootDirectory(gTestPath).replace(
+    "chrome://mochitests/content",
+    "https://test1.example.com"
+  ) + "credentialless_resource.sjs";
+
+async function store(storer, url, requestCredentialMode) {
+  await SpecialPowers.spawn(
+    storer.linkedBrowser,
+    [url, requestCredentialMode],
+    async function(url, requestCredentialMode) {
+      const cache = await content.caches.open("v1");
+      const fetchRequest = new content.Request(url, {
+        mode: "no-cors",
+        credentials: requestCredentialMode,
+      });
+
+      const fetchResponse = await content.fetch(fetchRequest);
+      content.wrappedJSObject.console.log(fetchResponse.headers);
+      await cache.put(fetchRequest, fetchResponse);
+    }
+  );
+}
+
+async function retrieve(retriever, resourceURL) {
+  return await SpecialPowers.spawn(
+    retriever.linkedBrowser,
+    [resourceURL],
+    async function(url) {
+      const cache = await content.caches.open("v1");
+      try {
+        await cache.match(url);
+        return "retrieved";
+      } catch (error) {
+        return "error";
+      }
+    }
+  );
+}
+
+async function testCache(
+  storer,
+  storeRequestCredentialMode,
+  resourceCOEP,
+  retriever,
+  expectation
+) {
+  const resourceURL = RESOURCE_URL + "?" + resourceCOEP;
+
+  await store(storer, resourceURL, storeRequestCredentialMode);
+  const result = await retrieve(retriever, resourceURL);
+
+  is(result, expectation);
+}
+
+add_task(async function() {
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["browser.tabs.remote.coep.credentialless", false],
+      ["dom.origin-trials.enabled", true],
+      ["dom.origin-trials.test-key.enabled", true],
+    ],
+  });
+
+  const noneTab = await BrowserTestUtils.openNewForegroundTab(
+    gBrowser,
+    TOP_LEVEL_URL
+  );
+  const requireCorpTab = await BrowserTestUtils.openNewForegroundTab(
+    gBrowser,
+    TOP_LEVEL_URL + "?requirecorp"
+  );
+  const credentiallessTab = await BrowserTestUtils.openNewForegroundTab(
+    gBrowser,
+    TOP_LEVEL_URL + "?credentialless"
+  );
+
+  await testCache(noneTab, "include", "", noneTab, "retrieved");
+  await testCache(noneTab, "include", "", credentiallessTab, "error");
+  await testCache(noneTab, "omit", "", credentiallessTab, "retrieved");
+  await testCache(
+    noneTab,
+    "include",
+    "corp_cross_origin",
+    credentiallessTab,
+    "retrieved"
+  );
+  await testCache(noneTab, "include", "", requireCorpTab, "error");
+  await testCache(
+    noneTab,
+    "include",
+    "corp_cross_origin",
+    requireCorpTab,
+    "retrieved"
+  );
+  await testCache(credentiallessTab, "include", "", noneTab, "retrieved");
+  await testCache(
+    credentiallessTab,
+    "include",
+    "",
+    credentiallessTab,
+    "retrieved"
+  );
+  await testCache(credentiallessTab, "include", "", requireCorpTab, "error");
+  await testCache(
+    requireCorpTab,
+    "include",
+    "corp_cross_origin",
+    noneTab,
+    "retrieved"
+  );
+  await testCache(
+    requireCorpTab,
+    "include",
+    "corp_cross_origin",
+    credentiallessTab,
+    "retrieved"
+  );
+  await testCache(
+    requireCorpTab,
+    "include",
+    "corp_cross_origin",
+    requireCorpTab,
+    "retrieved"
+  );
+
+  await BrowserTestUtils.removeTab(noneTab);
+  await BrowserTestUtils.removeTab(requireCorpTab);
+  await BrowserTestUtils.removeTab(credentiallessTab);
+});
new file mode 100644
--- /dev/null
+++ b/dom/fetch/tests/browser_origin_trial_coep_credentialless_fetch_1.js
@@ -0,0 +1,134 @@
+const TOP_LEVEL_URL =
+  getRootDirectory(gTestPath).replace(
+    "chrome://mochitests/content",
+    "https://example.com"
+  ) + "open_credentialless_document.sjs";
+
+const SAME_ORIGIN = "https://example.com";
+const CROSS_ORIGIN = "https://test1.example.com";
+
+const USE_CREDENTIALLESS = true;
+const NO_CREDENTIALLESS = false;
+
+const GET_STATE_URL =
+  getRootDirectory(gTestPath).replace(
+    "chrome://mochitests/content",
+    "https://example.com"
+  ) + "store_header.sjs?getstate";
+
+async function addCookieToOrigin(origin) {
+  const fetchRequestURL =
+    getRootDirectory(gTestPath).replace("chrome://mochitests/content", origin) +
+    "store_header.sjs?addcookie";
+
+  const addcookieTab = await BrowserTestUtils.openNewForegroundTab(
+    gBrowser,
+    fetchRequestURL
+  );
+
+  await SpecialPowers.spawn(addcookieTab.linkedBrowser, [], async function() {
+    content.document.cookie = "coep=credentialless; SameSite=None; Secure";
+  });
+  await BrowserTestUtils.removeTab(addcookieTab);
+}
+
+async function testOrigin(
+  fetchOrigin,
+  isCredentialless,
+  fetchRequestMode,
+  fetchRequestCrendentials,
+  expectedCookieResult
+) {
+  let topLevelUrl = TOP_LEVEL_URL;
+  if (isCredentialless) {
+    topLevelUrl += "?credentialless";
+  }
+
+  const noCredentiallessTab = await BrowserTestUtils.openNewForegroundTab(
+    gBrowser,
+    topLevelUrl
+  );
+
+  const fetchRequestURL =
+    getRootDirectory(gTestPath).replace(
+      "chrome://mochitests/content",
+      fetchOrigin
+    ) + "store_header.sjs?checkheader";
+
+  await SpecialPowers.spawn(
+    noCredentiallessTab.linkedBrowser,
+    [
+      fetchRequestURL,
+      fetchRequestMode,
+      fetchRequestCrendentials,
+      GET_STATE_URL,
+      expectedCookieResult,
+    ],
+    async function(
+      fetchRequestURL,
+      fetchRequestMode,
+      fetchRequestCrendentials,
+      getStateURL,
+      expectedCookieResult
+    ) {
+      // When store_header.sjs receives this request, it will store
+      // whether it has received the cookie as a shared state.
+      await content.fetch(fetchRequestURL, {
+        mode: fetchRequestMode,
+        credentials: fetchRequestCrendentials,
+      });
+
+      // This request is used to get the saved state from the
+      // previous fetch request.
+      const response = await content.fetch(getStateURL, {
+        mode: "cors",
+      });
+      const text = await response.text();
+      is(text, expectedCookieResult);
+    }
+  );
+
+  await BrowserTestUtils.removeTab(noCredentiallessTab);
+}
+
+async function doTest(
+  origin,
+  fetchRequestMode,
+  fetchRequestCrendentials,
+  expectedCookieResultForNoCredentialless,
+  expectedCookieResultForCredentialless
+) {
+  await testOrigin(
+    origin,
+    USE_CREDENTIALLESS,
+    fetchRequestMode,
+    fetchRequestCrendentials,
+    expectedCookieResultForCredentialless
+  );
+  await testOrigin(
+    origin,
+    NO_CREDENTIALLESS,
+    fetchRequestMode,
+    fetchRequestCrendentials,
+    expectedCookieResultForNoCredentialless
+  );
+}
+
+add_task(async function() {
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["browser.tabs.remote.coep.credentialless", false],
+      ["dom.origin-trials.enabled", true],
+      ["dom.origin-trials.test-key.enabled", true],
+    ],
+  });
+
+  await addCookieToOrigin(SAME_ORIGIN);
+  await addCookieToOrigin(CROSS_ORIGIN);
+
+  // Cookies never sent with omit
+  await doTest(SAME_ORIGIN, "no-cors", "omit", "noCookie", "noCookie");
+  await doTest(SAME_ORIGIN, "cors", "omit", "noCookie", "noCookie");
+  await doTest(CROSS_ORIGIN, "no-cors", "omit", "noCookie", "noCookie");
+  await doTest(CROSS_ORIGIN, "cors", "omit", "noCookie", "noCookie");
+});
new file mode 100644
--- /dev/null
+++ b/dom/fetch/tests/browser_origin_trial_coep_credentialless_fetch_2.js
@@ -0,0 +1,134 @@
+const TOP_LEVEL_URL =
+  getRootDirectory(gTestPath).replace(
+    "chrome://mochitests/content",
+    "https://example.com"
+  ) + "open_credentialless_document.sjs";
+
+const SAME_ORIGIN = "https://example.com";
+const CROSS_ORIGIN = "https://test1.example.com";
+
+const USE_CREDENTIALLESS = true;
+const NO_CREDENTIALLESS = false;
+
+const GET_STATE_URL =
+  getRootDirectory(gTestPath).replace(
+    "chrome://mochitests/content",
+    "https://example.com"
+  ) + "store_header.sjs?getstate";
+
+async function addCookieToOrigin(origin) {
+  const fetchRequestURL =
+    getRootDirectory(gTestPath).replace("chrome://mochitests/content", origin) +
+    "store_header.sjs?addcookie";
+
+  const addcookieTab = await BrowserTestUtils.openNewForegroundTab(
+    gBrowser,
+    fetchRequestURL
+  );
+
+  await SpecialPowers.spawn(addcookieTab.linkedBrowser, [], async function() {
+    content.document.cookie = "coep=credentialless; SameSite=None; Secure";
+  });
+  await BrowserTestUtils.removeTab(addcookieTab);
+}
+
+async function testOrigin(
+  fetchOrigin,
+  isCredentialless,
+  fetchRequestMode,
+  fetchRequestCrendentials,
+  expectedCookieResult
+) {
+  let topLevelUrl = TOP_LEVEL_URL;
+  if (isCredentialless) {
+    topLevelUrl += "?credentialless";
+  }
+
+  const noCredentiallessTab = await BrowserTestUtils.openNewForegroundTab(
+    gBrowser,
+    topLevelUrl
+  );
+
+  const fetchRequestURL =
+    getRootDirectory(gTestPath).replace(
+      "chrome://mochitests/content",
+      fetchOrigin
+    ) + "store_header.sjs?checkheader";
+
+  await SpecialPowers.spawn(
+    noCredentiallessTab.linkedBrowser,
+    [
+      fetchRequestURL,
+      fetchRequestMode,
+      fetchRequestCrendentials,
+      GET_STATE_URL,
+      expectedCookieResult,
+    ],
+    async function(
+      fetchRequestURL,
+      fetchRequestMode,
+      fetchRequestCrendentials,
+      getStateURL,
+      expectedCookieResult
+    ) {
+      // When store_header.sjs receives this request, it will store
+      // whether it has received the cookie as a shared state.
+      await content.fetch(fetchRequestURL, {
+        mode: fetchRequestMode,
+        credentials: fetchRequestCrendentials,
+      });
+
+      // This request is used to get the saved state from the
+      // previous fetch request.
+      const response = await content.fetch(getStateURL, {
+        mode: "cors",
+      });
+      const text = await response.text();
+      is(text, expectedCookieResult);
+    }
+  );
+
+  await BrowserTestUtils.removeTab(noCredentiallessTab);
+}
+
+async function doTest(
+  origin,
+  fetchRequestMode,
+  fetchRequestCrendentials,
+  expectedCookieResultForNoCredentialless,
+  expectedCookieResultForCredentialless
+) {
+  await testOrigin(
+    origin,
+    USE_CREDENTIALLESS,
+    fetchRequestMode,
+    fetchRequestCrendentials,
+    expectedCookieResultForCredentialless
+  );
+  await testOrigin(
+    origin,
+    NO_CREDENTIALLESS,
+    fetchRequestMode,
+    fetchRequestCrendentials,
+    expectedCookieResultForNoCredentialless
+  );
+}
+
+add_task(async function() {
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["browser.tabs.remote.coep.credentialless", false],
+      ["dom.origin-trials.enabled", true],
+      ["dom.origin-trials.test-key.enabled", true],
+    ],
+  });
+
+  await addCookieToOrigin(SAME_ORIGIN);
+  await addCookieToOrigin(CROSS_ORIGIN);
+
+  // Same-origin request contains Cookies.
+  await doTest(SAME_ORIGIN, "no-cors", "include", "hasCookie", "hasCookie");
+  await doTest(SAME_ORIGIN, "cors", "include", "hasCookie", "hasCookie");
+  await doTest(SAME_ORIGIN, "no-cors", "same-origin", "hasCookie", "hasCookie");
+  await doTest(SAME_ORIGIN, "cors", "same-origin", "hasCookie", "hasCookie");
+});
new file mode 100644
--- /dev/null
+++ b/dom/fetch/tests/browser_origin_trial_coep_credentialless_fetch_3.js
@@ -0,0 +1,139 @@
+const TOP_LEVEL_URL =
+  getRootDirectory(gTestPath).replace(
+    "chrome://mochitests/content",
+    "https://example.com"
+  ) + "open_credentialless_document.sjs";
+
+const SAME_ORIGIN = "https://example.com";
+const CROSS_ORIGIN = "https://test1.example.com";
+
+const USE_CREDENTIALLESS = true;
+const NO_CREDENTIALLESS = false;
+
+const GET_STATE_URL =
+  getRootDirectory(gTestPath).replace(
+    "chrome://mochitests/content",
+    "https://example.com"
+  ) + "store_header.sjs?getstate";
+
+async function addCookieToOrigin(origin) {
+  const fetchRequestURL =
+    getRootDirectory(gTestPath).replace("chrome://mochitests/content", origin) +
+    "store_header.sjs?addcookie";
+
+  const addcookieTab = await BrowserTestUtils.openNewForegroundTab(
+    gBrowser,
+    fetchRequestURL
+  );
+
+  await SpecialPowers.spawn(addcookieTab.linkedBrowser, [], async function() {
+    content.document.cookie = "coep=credentialless; SameSite=None; Secure";
+  });
+  await BrowserTestUtils.removeTab(addcookieTab);
+}
+
+async function testOrigin(
+  fetchOrigin,
+  isCredentialless,
+  fetchRequestMode,
+  fetchRequestCrendentials,
+  expectedCookieResult
+) {
+  let topLevelUrl = TOP_LEVEL_URL;
+  if (isCredentialless) {
+    topLevelUrl += "?credentialless";
+  }
+
+  const noCredentiallessTab = await BrowserTestUtils.openNewForegroundTab(
+    gBrowser,
+    topLevelUrl
+  );
+
+  const fetchRequestURL =
+    getRootDirectory(gTestPath).replace(
+      "chrome://mochitests/content",
+      fetchOrigin
+    ) + "store_header.sjs?checkheader";
+
+  await SpecialPowers.spawn(
+    noCredentiallessTab.linkedBrowser,
+    [
+      fetchRequestURL,
+      fetchRequestMode,
+      fetchRequestCrendentials,
+      GET_STATE_URL,
+      expectedCookieResult,
+    ],
+    async function(
+      fetchRequestURL,
+      fetchRequestMode,
+      fetchRequestCrendentials,
+      getStateURL,
+      expectedCookieResult
+    ) {
+      // When store_header.sjs receives this request, it will store
+      // whether it has received the cookie as a shared state.
+      await content.fetch(fetchRequestURL, {
+        mode: fetchRequestMode,
+        credentials: fetchRequestCrendentials,
+      });
+
+      // This request is used to get the saved state from the
+      // previous fetch request.
+      const response = await content.fetch(getStateURL, {
+        mode: "cors",
+      });
+      const text = await response.text();
+      is(text, expectedCookieResult);
+    }
+  );
+
+  await BrowserTestUtils.removeTab(noCredentiallessTab);
+}
+
+async function doTest(
+  origin,
+  fetchRequestMode,
+  fetchRequestCrendentials,
+  expectedCookieResultForNoCredentialless,
+  expectedCookieResultForCredentialless
+) {
+  await testOrigin(
+    origin,
+    USE_CREDENTIALLESS,
+    fetchRequestMode,
+    fetchRequestCrendentials,
+    expectedCookieResultForCredentialless
+  );
+  await testOrigin(
+    origin,
+    NO_CREDENTIALLESS,
+    fetchRequestMode,
+    fetchRequestCrendentials,
+    expectedCookieResultForNoCredentialless
+  );
+}
+
+add_task(async function() {
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["browser.tabs.remote.coep.credentialless", false],
+      ["dom.origin-trials.enabled", true],
+      ["dom.origin-trials.test-key.enabled", true],
+    ],
+  });
+
+  await addCookieToOrigin(SAME_ORIGIN);
+  await addCookieToOrigin(CROSS_ORIGIN);
+
+  // Cross-origin CORS requests contains Cookies, if credentials mode is set to
+  // 'include'. This does not depends on COEP.
+  await doTest(CROSS_ORIGIN, "cors", "include", "hasCookie", "hasCookie");
+  await doTest(CROSS_ORIGIN, "cors", "same-origin", "noCookie", "noCookie");
+
+  // Cross-origin no-CORS requests includes Cookies when:
+  // 1. credentials mode is 'include'
+  // 2. COEP: is not credentialless.
+  await doTest(CROSS_ORIGIN, "no-cors", "include", "hasCookie", "noCookie");
+  await doTest(CROSS_ORIGIN, "no-cors", "same-origin", "noCookie", "noCookie");
+});
new file mode 100644
--- /dev/null
+++ b/dom/fetch/tests/browser_origin_trial_coep_credentialless_worker.js
@@ -0,0 +1,164 @@
+const TOP_LEVEL_URL =
+  getRootDirectory(gTestPath).replace(
+    "chrome://mochitests/content",
+    "https://example.com"
+  ) + "open_credentialless_document.sjs";
+
+const WORKER_URL =
+  getRootDirectory(gTestPath).replace(
+    "chrome://mochitests/content",
+    "https://example.com"
+  ) + "credentialless_worker.sjs";
+
+const GET_STATE_URL =
+  getRootDirectory(gTestPath).replace(
+    "chrome://mochitests/content",
+    "https://example.com"
+  ) + "store_header.sjs?getstate";
+
+const SAME_ORIGIN = "https://example.com";
+const CROSS_ORIGIN = "https://test1.example.com";
+
+const WORKER_USES_CREDENTIALLESS = "credentialless";
+const WORKER_NOT_USE_CREDENTIALLESS = "";
+
+async function addCookieToOrigin(origin) {
+  const fetchRequestURL =
+    getRootDirectory(gTestPath).replace("chrome://mochitests/content", origin) +
+    "store_header.sjs?addcookie";
+
+  const addcookieTab = await BrowserTestUtils.openNewForegroundTab(
+    gBrowser,
+    fetchRequestURL
+  );
+
+  await SpecialPowers.spawn(addcookieTab.linkedBrowser, [], async function() {
+    content.document.cookie = "coep=credentialless; SameSite=None; Secure";
+  });
+  await BrowserTestUtils.removeTab(addcookieTab);
+}
+
+async function testOrigin(
+  fetchOrigin,
+  isCredentialless,
+  workerUsesCredentialless,
+  expectedCookieResult
+) {
+  let topLevelUrl = TOP_LEVEL_URL;
+  if (isCredentialless) {
+    topLevelUrl += "?credentialless";
+  }
+  const noCredentiallessTab = await BrowserTestUtils.openNewForegroundTab(
+    gBrowser,
+    topLevelUrl
+  );
+
+  const fetchRequestURL =
+    getRootDirectory(gTestPath).replace(
+      "chrome://mochitests/content",
+      fetchOrigin
+    ) + "store_header.sjs?checkheader";
+
+  let workerScriptURL = WORKER_URL + "?" + workerUsesCredentialless;
+
+  await SpecialPowers.spawn(
+    noCredentiallessTab.linkedBrowser,
+    [fetchRequestURL, GET_STATE_URL, workerScriptURL, expectedCookieResult],
+    async function(
+      fetchRequestURL,
+      getStateURL,
+      workerScriptURL,
+      expectedCookieResult
+    ) {
+      const worker = new content.Worker(workerScriptURL, {});
+
+      // When the worker receives this message, it'll send
+      // a fetch request to fetchRequestURL, and fetchRequestURL
+      // will store whether it has received the cookie as a
+      // shared state.
+      worker.postMessage(fetchRequestURL);
+
+      if (expectedCookieResult == "error") {
+        await new Promise(r => {
+          worker.onerror = function() {
+            ok(true, "worker has error");
+            r();
+          };
+        });
+      } else {
+        await new Promise(r => {
+          worker.addEventListener("message", async function() {
+            // This request is used to get the saved state from the
+            // previous fetch request.
+            const response = await content.fetch(getStateURL, {
+              mode: "cors",
+            });
+            const text = await response.text();
+            is(text, expectedCookieResult);
+            r();
+          });
+        });
+      }
+    }
+  );
+  await BrowserTestUtils.removeTab(noCredentiallessTab);
+}
+
+async function dedicatedWorkerTest(
+  origin,
+  workerCOEP,
+  expectedCookieResultForNoCredentialless,
+  expectedCookieResultForCredentialless
+) {
+  await testOrigin(
+    origin,
+    false,
+    workerCOEP,
+    expectedCookieResultForNoCredentialless
+  );
+  await testOrigin(
+    origin,
+    true,
+    workerCOEP,
+    expectedCookieResultForCredentialless
+  );
+}
+
+add_task(async function() {
+  await SpecialPowers.pushPrefEnv({
+    set: [
+      ["browser.tabs.remote.coep.credentialless", false], // Explicitly set credentialless to false because we want to test origin trial
+      ["dom.origin-trials.enabled", true],
+      ["dom.origin-trials.test-key.enabled", true],
+    ],
+  });
+
+  await addCookieToOrigin(SAME_ORIGIN);
+  await addCookieToOrigin(CROSS_ORIGIN);
+
+  await dedicatedWorkerTest(
+    SAME_ORIGIN,
+    WORKER_NOT_USE_CREDENTIALLESS,
+    "hasCookie",
+    "error"
+  );
+  await dedicatedWorkerTest(
+    SAME_ORIGIN,
+    WORKER_USES_CREDENTIALLESS,
+    "hasCookie",
+    "hasCookie"
+  );
+
+  await dedicatedWorkerTest(
+    CROSS_ORIGIN,
+    WORKER_NOT_USE_CREDENTIALLESS,
+    "hasCookie",
+    "error"
+  );
+  await dedicatedWorkerTest(
+    CROSS_ORIGIN,
+    WORKER_USES_CREDENTIALLESS,
+    "noCookie",
+    "noCookie"
+  );
+});
new file mode 100644
--- /dev/null
+++ b/dom/fetch/tests/credentialless_resource.sjs
@@ -0,0 +1,21 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+// small red image
+const IMG_BYTES = atob(
+  "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12" +
+    "P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=="
+);
+
+function handleRequest(request, response) {
+  response.seizePower();
+  response.write("HTTP/1.1 200 OK\r\n");
+  response.write("Content-Type: image/png\r\n");
+  if (request.queryString === "corp_cross_origin") {
+    response.write("Cross-Origin-Resource-Policy: cross-origin\r\n");
+  }
+  response.write("\r\n");
+  response.write(IMG_BYTES);
+  response.finish();
+}
new file mode 100644
--- /dev/null
+++ b/dom/fetch/tests/credentialless_worker.sjs
@@ -0,0 +1,25 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const WORKER = `
+  onmessage = function(event) {
+    fetch(event.data, {
+      mode: "no-cors",
+      credentials: "include"
+    }).then(function() {
+      postMessage("fetch done");
+    });
+  }
+`;
+
+function handleRequest(request, response) {
+  if (request.queryString === "credentialless") {
+    response.setHeader("Cross-Origin-Embedder-Policy", "credentialless", true);
+  }
+
+  response.setHeader("Content-Type", "application/javascript", false);
+  response.setStatusLine(request.httpVersion, "200", "Found");
+  response.write(WORKER);
+}
new file mode 100644
--- /dev/null
+++ b/dom/fetch/tests/open_credentialless_document.sjs
@@ -0,0 +1,23 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const HTML = `<!DOCTYPE HTML>
+<head>
+  <!-- Created with: mktoken --origin 'https://example.com' --feature CoepCredentialless --expiry 'Wed, 01 Jan 3000 01:00:00 +0100' --sign test-keys/test-ecdsa.pkcs8 -->
+  <meta http-equiv="origin-trial" content="Az+DK2Kczk8Xz1cAlD+TkvPZmuM2uJZ2CFefbp2hLuCU9FbUqxWTyQ2tEYr50r0syKELcOZLAPaABw8aYTLHn5YAAABUeyJvcmlnaW4iOiJodHRwczovL2V4YW1wbGUuY29tIiwiZmVhdHVyZSI6IkNvZXBDcmVkZW50aWFsbGVzcyIsImV4cGlyeSI6MzI1MDM2ODAwMDB9">
+</head>
+<html>
+  <body>Hello World</body>
+</html>`;
+
+function handleRequest(request, response) {
+  if (request.queryString == "credentialless") {
+    response.setHeader("Cross-Origin-Embedder-Policy", "credentialless");
+  } else if (request.queryString === "requirecorp") {
+    response.setHeader("Cross-Origin-Embedder-Policy", "require-corp");
+  }
+  response.setHeader("Content-Type", "text/html;charset=utf-8", false);
+  response.setStatusLine(request.httpVersion, "200", "Found");
+  response.write(HTML);
+}
new file mode 100644
--- /dev/null
+++ b/dom/fetch/tests/store_header.sjs
@@ -0,0 +1,23 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const key = "store_header";
+function handleRequest(request, response) {
+  response.setHeader("Content-Type", "text/plain");
+  response.setHeader("Access-Control-Allow-Origin", "https://example.com");
+  response.setHeader("Access-Control-Allow-Credentials", "true");
+
+  if (request.queryString === "getstate") {
+    response.write(getSharedState(key));
+  } else if (request.queryString === "checkheader") {
+    if (request.hasHeader("Cookie")) {
+      setSharedState(key, "hasCookie");
+    } else {
+      setSharedState(key, "noCookie");
+    }
+  } else {
+    // This is the first request which sets the cookie
+  }
+}
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -2206,16 +2206,18 @@ interface nsIDOMWindowUtils : nsISupport
 
   /**
    * Calls SetInitialViewport on the MobileViewportManager, which effectively
    * causes it to refresh all of its internal state and update things that
    * need updating.
    */
   void resetMobileViewportManager();
 
+  bool isCoepCredentialless();
+
   /**
    * NOTE: Currently works only on GTK+.
    */
   attribute ACString systemFont;
 
   /**
    * Returns the number of times this document for this window has
    * been painted to the screen.
--- a/dom/origin-trials/OriginTrials.cpp
+++ b/dom/origin-trials/OriginTrials.cpp
@@ -207,39 +207,44 @@ OriginTrials OriginTrials::FromWindow(co
 }
 
 static int32_t PrefState(OriginTrial aTrial) {
   switch (aTrial) {
     case OriginTrial::TestTrial:
       return StaticPrefs::dom_origin_trials_test_trial_state();
     case OriginTrial::OffscreenCanvas:
       return StaticPrefs::dom_origin_trials_offscreen_canvas_state();
+    case OriginTrial::CoepCredentialless:
+      return StaticPrefs::dom_origin_trials_coep_credentialless_state();
     case OriginTrial::MAX:
       MOZ_ASSERT_UNREACHABLE("Unknown trial!");
       break;
   }
   return 0;
 }
 
-bool OriginTrials::IsEnabled(JSContext* aCx, JSObject* aObject,
-                             OriginTrial aTrial) {
-  if (nsContentUtils::ThreadsafeIsSystemCaller(aCx)) {
-    return true;
-  }
-  LOG("OriginTrials::IsEnabled(%d)\n", int(aTrial));
-
+bool OriginTrials::IsEnabled(OriginTrial aTrial) const {
   switch (PrefState(aTrial)) {
     case 1:
       return true;
     case 2:
       return false;
     default:
       break;
   }
 
+  return mEnabledTrials.contains(aTrial);
+}
+
+bool OriginTrials::IsEnabled(JSContext* aCx, JSObject* aObject,
+                             OriginTrial aTrial) {
+  if (nsContentUtils::ThreadsafeIsSystemCaller(aCx)) {
+    return true;
+  }
+  LOG("OriginTrials::IsEnabled(%d)\n", int(aTrial));
   nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
   MOZ_ASSERT(global);
   return global && global->Trials().IsEnabled(aTrial);
 }
 
 #undef LOG
 
 }  // namespace mozilla
--- a/dom/origin-trials/OriginTrials.h
+++ b/dom/origin-trials/OriginTrials.h
@@ -35,19 +35,17 @@ class OriginTrials final {
   const RawType& Raw() const { return mEnabledTrials; }
 
   // Parses and verifies a base64-encoded token from either a header or a meta
   // tag. If the token is valid and not expired, this will enable the relevant
   // feature.
   void UpdateFromToken(const nsAString& aBase64EncodedToken,
                        nsIPrincipal* aPrincipal);
 
-  bool IsEnabled(OriginTrial aTrial) const {
-    return mEnabledTrials.contains(aTrial);
-  }
+  bool IsEnabled(OriginTrial aTrial) const;
 
   // Checks whether a given origin trial is enabled for a given call.
   static bool IsEnabled(JSContext*, JSObject*, OriginTrial);
 
   // Computes the currently-applying trials for our global.
   static OriginTrials FromWindow(const nsGlobalWindowInner*);
 
  private:
--- a/dom/origin-trials/ffi/lib.rs
+++ b/dom/origin-trials/ffi/lib.rs
@@ -5,25 +5,27 @@
 use origin_trial_token::{RawToken, Token, TokenValidationError, Usage};
 use std::ffi::c_void;
 
 #[repr(u8)]
 pub enum OriginTrial {
     // NOTE(emilio): 0 is reserved for WebIDL usage.
     TestTrial = 1,
     OffscreenCanvas = 2,
+    CoepCredentialless = 3,
 
     MAX,
 }
 
 impl OriginTrial {
     fn from_str(s: &str) -> Option<Self> {
         Some(match s {
             "TestTrial" => Self::TestTrial,
             "OffscreenCanvas" => Self::OffscreenCanvas,
+            "CoepCredentialless" => Self::CoepCredentialless,
             _ => return None,
         })
     }
 }
 
 #[repr(u8)]
 pub enum OriginTrialResult {
     Ok { trial: OriginTrial },
--- a/dom/origin-trials/tests/mochitest.ini
+++ b/dom/origin-trials/tests/mochitest.ini
@@ -1,12 +1,13 @@
 [DEFAULT]
 prefs =
     dom.origin-trials.enabled=true
     dom.origin-trials.test-key.enabled=true
+    browser.tabs.remote.coep.credentialless=false
 support-files =
     test_header_simple.html^headers^
     common.js
 #  * Test interfaces only exposed on DEBUG builds.
 #  * xorigin tests run in example.org rather than example.com, so token
 #    verification fails, expectedly.
 skip-if = !debug || xorigin
 # AudioWorklet requires secure context
--- a/dom/origin-trials/tests/test_meta_simple.html
+++ b/dom/origin-trials/tests/test_meta_simple.html
@@ -1,13 +1,18 @@
 <!doctype html>
 <!-- Created with: mktoken --origin 'https://example.com' --feature TestTrial --expiry 'Wed, 01 Jan 3000 01:00:00 +0100' --sign test-keys/test-ecdsa.pkcs8 -->
 <meta http-equiv="origin-trial" content="AyGdETIKWLLqe+chG57f74gZcjYSfbdYAapEq7DA49E6CmaYaPmaoXh/4tAe5XJJJdwwpFVal7hz/irC+Wvp1HgAAABLeyJvcmlnaW4iOiJodHRwczovL2V4YW1wbGUuY29tIiwiZmVhdHVyZSI6IlRlc3RUcmlhbCIsImV4cGlyeSI6MzI1MDM2ODAwMDB9">
 <!-- Created with: mktoken --origin 'https://example.com' --feature OffscreenCanvas --expiry 'Wed, 01 Jan 3000 01:00:00 +0100' --sign test-keys/test-ecdsa.pkcs8 -->
 <meta http-equiv="origin-trial" content="Ay92n3CdO5VIYbmQB7t7r7e4c34nT1k9zbX5ON2JthrXaOFxLn5NieN7ITlKhPbmPSLA4qoS+TBdshqEUwmVaIwAAABReyJvcmlnaW4iOiJodHRwczovL2V4YW1wbGUuY29tIiwiZmVhdHVyZSI6Ik9mZnNjcmVlbkNhbnZhcyIsImV4cGlyeSI6MzI1MDM2ODAwMDB9">
+<!-- Created with: mktoken --origin 'https://example.com' --feature CoepCredentialless --expiry 'Wed, 01 Jan 3000 01:00:00 +0100' --sign test-keys/test-ecdsa.pkcs8 -->
+<meta http-equiv="origin-trial" content="Az+DK2Kczk8Xz1cAlD+TkvPZmuM2uJZ2CFefbp2hLuCU9FbUqxWTyQ2tEYr50r0syKELcOZLAPaABw8aYTLHn5YAAABUeyJvcmlnaW4iOiJodHRwczovL2V4YW1wbGUuY29tIiwiZmVhdHVyZSI6IkNvZXBDcmVkZW50aWFsbGVzcyIsImV4cGlyeSI6MzI1MDM2ODAwMDB9">
 <script src="/tests/SimpleTest/SimpleTest.js"></script>
 <script src="common.js"></script>
 <script>
   assertTestTrialActive(true);
   add_task(function() {
     ok(!!self.OffscreenCanvas, "OffscreenCanvas trial works.");
   });
+  add_task(function() {
+    ok(!!SpecialPowers.DOMWindowUtils.isCoepCredentialless(), "CoepCredentialless trial works.");
+  });
 </script>
--- a/dom/workers/loader/CacheLoadHandler.cpp
+++ b/dom/workers/loader/CacheLoadHandler.cpp
@@ -377,17 +377,19 @@ void CacheLoadHandler::ResolvedCallback(
                mCSPReportOnlyHeaderValue, IgnoreErrors());
   headers->Get("referrer-policy"_ns, mReferrerPolicyHeaderValue,
                IgnoreErrors());
 
   nsAutoCString coepHeader;
   headers->Get("cross-origin-embedder-policy"_ns, coepHeader, IgnoreErrors());
 
   nsILoadInfo::CrossOriginEmbedderPolicy coep =
-      NS_GetCrossOriginEmbedderPolicyFromHeader(coepHeader);
+      NS_GetCrossOriginEmbedderPolicyFromHeader(
+          coepHeader,
+          mWorkerPrivate->Trials().IsEnabled(OriginTrial::CoepCredentialless));
 
   rv = ScriptResponseHeaderProcessor::ProcessCrossOriginEmbedderPolicyHeader(
       mWorkerPrivate, coep, mLoader->IsMainScript());
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     Fail(rv);
     return;
   }
--- a/dom/workers/loader/ScriptResponseHeaderProcessor.cpp
+++ b/dom/workers/loader/ScriptResponseHeaderProcessor.cpp
@@ -44,17 +44,19 @@ nsresult ScriptResponseHeaderProcessor::
     if (mIsMainScript) {
       mWorkerPrivate->InheritOwnerEmbedderPolicyOrNull(aRequest);
     }
 
     return NS_OK;
   }
 
   nsILoadInfo::CrossOriginEmbedderPolicy coep;
-  MOZ_TRY(httpChannel->GetResponseEmbedderPolicy(&coep));
+  MOZ_TRY(httpChannel->GetResponseEmbedderPolicy(
+      mWorkerPrivate->Trials().IsEnabled(OriginTrial::CoepCredentialless),
+      &coep));
 
   return ProcessCrossOriginEmbedderPolicyHeader(mWorkerPrivate, coep,
                                                 mIsMainScript);
 }
 
 }  // namespace loader
 }  // namespace workerinternals
 
--- a/ipc/glue/BackgroundUtils.cpp
+++ b/ipc/glue/BackgroundUtils.cpp
@@ -532,17 +532,19 @@ nsresult LoadInfoToLoadInfoArgs(nsILoadI
       aLoadInfo->GetHasValidUserGestureActivation(),
       aLoadInfo->GetAllowDeprecatedSystemRequests(),
       aLoadInfo->GetIsInDevToolsContext(), aLoadInfo->GetParserCreatedScript(),
       aLoadInfo->GetIsFromProcessingFrameAttributes(),
       aLoadInfo->GetIsMediaRequest(), aLoadInfo->GetIsMediaInitialRequest(),
       aLoadInfo->GetIsFromObjectOrEmbed(), cookieJarSettingsArgs,
       aLoadInfo->GetRequestBlockingReason(), maybeCspToInheritInfo,
       aLoadInfo->GetStoragePermission(), aLoadInfo->GetIsMetaRefresh(),
-      aLoadInfo->GetLoadingEmbedderPolicy(), unstrippedURI));
+      aLoadInfo->GetLoadingEmbedderPolicy(),
+      aLoadInfo->GetIsOriginTrialCoepCredentiallessEnabledForTopLevel(),
+      unstrippedURI));
 
   return NS_OK;
 }
 
 nsresult LoadInfoArgsToLoadInfo(
     const Maybe<LoadInfoArgs>& aOptionalLoadInfoArgs,
     nsILoadInfo** outLoadInfo) {
   return LoadInfoArgsToLoadInfo(aOptionalLoadInfoArgs, nullptr, outLoadInfo);
@@ -774,17 +776,19 @@ nsresult LoadInfoArgsToLoadInfo(
       loadInfoArgs.needForCheckingAntiTrackingHeuristic(),
       loadInfoArgs.cspNonce(), loadInfoArgs.skipContentSniffing(),
       loadInfoArgs.httpsOnlyStatus(),
       loadInfoArgs.hasValidUserGestureActivation(),
       loadInfoArgs.allowDeprecatedSystemRequests(),
       loadInfoArgs.isInDevToolsContext(), loadInfoArgs.parserCreatedScript(),
       loadInfoArgs.storagePermission(), loadInfoArgs.isMetaRefresh(),
       loadInfoArgs.requestBlockingReason(), loadingContext,
-      loadInfoArgs.loadingEmbedderPolicy(), loadInfoArgs.unstrippedURI());
+      loadInfoArgs.loadingEmbedderPolicy(),
+      loadInfoArgs.originTrialCoepCredentiallessEnabledForTopLevel(),
+      loadInfoArgs.unstrippedURI());
 
   if (loadInfoArgs.isFromProcessingFrameAttributes()) {
     loadInfo->SetIsFromProcessingFrameAttributes();
   }
 
   if (loadInfoArgs.isMediaRequest()) {
     loadInfo->SetIsMediaRequest(true);
 
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -1537,16 +1537,17 @@
   mirror: always
 
 # This pref makes `credentialless` a valid value for
 # Cross-Origin-Embedder-Policy header
 - name: browser.tabs.remote.coep.credentialless
   type: RelaxedAtomicBool
   value: @IS_NIGHTLY_BUILD@
   mirror: always
+  do_not_use_directly: true
 
 # When this pref is enabled top level loads with a mismatched
 # Cross-Origin-Opener-Policy header will be loaded in a separate process.
 - name: browser.tabs.remote.useCrossOriginOpenerPolicy
   type: RelaxedAtomicBool
   value: true
   mirror: always
 
@@ -2992,16 +2993,23 @@
 
 # Origin trial state for OffscreenCanvas.
 # 0: normal, 1: always-enabled, 2: always-disabled
 - name: dom.origin-trials.offscreen-canvas.state
   type: RelaxedAtomicInt32
   value: 0
   mirror: always
 
+# Origin trial state for COEP: Credentialless.
+# 0: normal, 1: always-enabled, 2: always-disabled
+- name: dom.origin-trials.coep-credentialless.state
+  type: RelaxedAtomicInt32
+  value: 0
+  mirror: always
+
 # Is support for Window.paintWorklet enabled?
 - name: dom.paintWorklet.enabled
   type: bool
   value: false
   mirror: always
 
 # Enable/disable the PaymentRequest API
 - name: dom.payments.request.enabled
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.cpp
@@ -508,16 +508,21 @@ LoadInfo::LoadInfo(dom::WindowGlobalPare
   if (parentBC->IsChrome()) {
     MOZ_ASSERT(mOriginAttributes.mPrivateBrowsingId == 0,
                "chrome docshell shouldn't have mPrivateBrowsingId set.");
   }
 
   RefPtr<WindowContext> ctx = WindowContext::GetById(mInnerWindowID);
   if (ctx) {
     mLoadingEmbedderPolicy = ctx->GetEmbedderPolicy();
+
+    if (Document* document = ctx->GetDocument()) {
+      mIsOriginTrialCoepCredentiallessEnabledForTopLevel =
+          document->Trials().IsEnabled(OriginTrial::CoepCredentialless);
+    }
   }
 }
 
 // Used for TYPE_FRAME or TYPE_IFRAME load.
 LoadInfo::LoadInfo(dom::CanonicalBrowsingContext* aBrowsingContext,
                    nsIPrincipal* aTriggeringPrincipal,
                    nsSecurityFlags aSecurityFlags, uint32_t aSandboxFlags)
     : LoadInfo(aBrowsingContext->GetParentWindowContext(), aTriggeringPrincipal,
@@ -596,16 +601,18 @@ LoadInfo::LoadInfo(const LoadInfo& rhs)
       mParserCreatedScript(rhs.mParserCreatedScript),
       mStoragePermission(rhs.mStoragePermission),
       mIsMetaRefresh(rhs.mIsMetaRefresh),
       mIsFromProcessingFrameAttributes(rhs.mIsFromProcessingFrameAttributes),
       mIsMediaRequest(rhs.mIsMediaRequest),
       mIsMediaInitialRequest(rhs.mIsMediaInitialRequest),
       mIsFromObjectOrEmbed(rhs.mIsFromObjectOrEmbed),
       mLoadingEmbedderPolicy(rhs.mLoadingEmbedderPolicy),
+      mIsOriginTrialCoepCredentiallessEnabledForTopLevel(
+          rhs.mIsOriginTrialCoepCredentiallessEnabledForTopLevel),
       mUnstrippedURI(rhs.mUnstrippedURI) {}
 
 LoadInfo::LoadInfo(
     nsIPrincipal* aLoadingPrincipal, nsIPrincipal* aTriggeringPrincipal,
     nsIPrincipal* aPrincipalToInherit, nsIPrincipal* aTopLevelPrincipal,
     nsIURI* aResultPrincipalURI, nsICookieJarSettings* aCookieJarSettings,
     nsIContentSecurityPolicy* aCspToInherit,
     const nsID& aSandboxedNullPrincipalID, const Maybe<ClientInfo>& aClientInfo,
@@ -635,16 +642,17 @@ LoadInfo::LoadInfo(
     bool aAllowListFutureDocumentsCreatedFromThisRedirectChain,
     bool aNeedForCheckingAntiTrackingHeuristic, const nsAString& aCspNonce,
     bool aSkipContentSniffing, uint32_t aHttpsOnlyStatus,
     bool aHasValidUserGestureActivation, bool aAllowDeprecatedSystemRequests,
     bool aIsInDevToolsContext, bool aParserCreatedScript,
     nsILoadInfo::StoragePermissionState aStoragePermission, bool aIsMetaRefresh,
     uint32_t aRequestBlockingReason, nsINode* aLoadingContext,
     nsILoadInfo::CrossOriginEmbedderPolicy aLoadingEmbedderPolicy,
+    bool aIsOriginTrialCoepCredentiallessEnabledForTopLevel,
     nsIURI* aUnstrippedURI)
     : mLoadingPrincipal(aLoadingPrincipal),
       mTriggeringPrincipal(aTriggeringPrincipal),
       mPrincipalToInherit(aPrincipalToInherit),
       mTopLevelPrincipal(aTopLevelPrincipal),
       mResultPrincipalURI(aResultPrincipalURI),
       mCookieJarSettings(aCookieJarSettings),
       mCspToInherit(aCspToInherit),
@@ -702,16 +710,18 @@ LoadInfo::LoadInfo(
       mHasValidUserGestureActivation(aHasValidUserGestureActivation),
       mAllowDeprecatedSystemRequests(aAllowDeprecatedSystemRequests),
       mIsInDevToolsContext(aIsInDevToolsContext),
       mParserCreatedScript(aParserCreatedScript),
       mStoragePermission(aStoragePermission),
       mIsMetaRefresh(aIsMetaRefresh),
 
       mLoadingEmbedderPolicy(aLoadingEmbedderPolicy),
+      mIsOriginTrialCoepCredentiallessEnabledForTopLevel(
+          aIsOriginTrialCoepCredentiallessEnabledForTopLevel),
       mUnstrippedURI(aUnstrippedURI) {
   // Only top level TYPE_DOCUMENT loads can have a null loadingPrincipal
   MOZ_ASSERT(mLoadingPrincipal ||
              aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT);
   MOZ_ASSERT(mTriggeringPrincipal);
 }
 
 // static
@@ -2099,16 +2109,32 @@ LoadInfo::GetLoadingEmbedderPolicy(
 
 NS_IMETHODIMP
 LoadInfo::SetLoadingEmbedderPolicy(
     nsILoadInfo::CrossOriginEmbedderPolicy aPolicy) {
   mLoadingEmbedderPolicy = aPolicy;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+LoadInfo::GetIsOriginTrialCoepCredentiallessEnabledForTopLevel(
+    bool* aIsOriginTrialCoepCredentiallessEnabledForTopLevel) {
+  *aIsOriginTrialCoepCredentiallessEnabledForTopLevel =
+      mIsOriginTrialCoepCredentiallessEnabledForTopLevel;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+LoadInfo::SetIsOriginTrialCoepCredentiallessEnabledForTopLevel(
+    bool aIsOriginTrialCoepCredentiallessEnabledForTopLevel) {
+  mIsOriginTrialCoepCredentiallessEnabledForTopLevel =
+      aIsOriginTrialCoepCredentiallessEnabledForTopLevel;
+  return NS_OK;
+}
+
 already_AddRefed<nsIContentSecurityPolicy> LoadInfo::GetCsp() {
   // Before querying the CSP from the client we have to check if the
   // triggeringPrincipal originates from an addon and potentially
   // overrides the CSP stored within the client.
   if (mLoadingPrincipal && BasePrincipal::Cast(mTriggeringPrincipal)
                                ->OverridesCSP(mLoadingPrincipal)) {
     nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(mTriggeringPrincipal);
     nsCOMPtr<nsIContentSecurityPolicy> addonCSP;
--- a/netwerk/base/LoadInfo.h
+++ b/netwerk/base/LoadInfo.h
@@ -227,16 +227,17 @@ class LoadInfo final : public nsILoadInf
       bool aNeedForCheckingAntiTrackingHeuristic, const nsAString& aCspNonce,
       bool aSkipContentSniffing, uint32_t aHttpsOnlyStatus,
       bool aHasValidUserGestureActivation, bool aAllowDeprecatedSystemRequests,
       bool aIsInDevToolsContext, bool aParserCreatedScript,
       nsILoadInfo::StoragePermissionState aStoragePermission,
       bool aIsMetaRefresh, uint32_t aRequestBlockingReason,
       nsINode* aLoadingContext,
       nsILoadInfo::CrossOriginEmbedderPolicy aLoadingEmbedderPolicy,
+      bool aIsOriginTrialCoepCredentiallessEnabledForTopLevel,
       nsIURI* aUnstrippedURI);
   LoadInfo(const LoadInfo& rhs);
 
   NS_IMETHOD GetRedirects(JSContext* aCx,
                           JS::MutableHandle<JS::Value> aRedirects,
                           const RedirectHistoryArray& aArra);
 
   friend nsresult mozilla::ipc::LoadInfoArgsToLoadInfo(
@@ -354,16 +355,18 @@ class LoadInfo final : public nsILoadInf
 
   // The cross origin embedder policy that the loading need to respect.
   // If the value is nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP, CORP checking
   // must be performed for the loading.
   // See https://wicg.github.io/cross-origin-embedder-policy/#corp-check.
   nsILoadInfo::CrossOriginEmbedderPolicy mLoadingEmbedderPolicy =
       nsILoadInfo::EMBEDDER_POLICY_NULL;
 
+  bool mIsOriginTrialCoepCredentiallessEnabledForTopLevel = false;
+
   nsCOMPtr<nsIURI> mUnstrippedURI;
 };
 
 // This is exposed solely for testing purposes and should not be used outside of
 // LoadInfo
 already_AddRefed<nsIPrincipal> CreateTruncatedPrincipal(nsIPrincipal*);
 
 }  // namespace net
--- a/netwerk/base/TRRLoadInfo.cpp
+++ b/netwerk/base/TRRLoadInfo.cpp
@@ -742,16 +742,28 @@ TRRLoadInfo::GetLoadingEmbedderPolicy(
 
 NS_IMETHODIMP
 TRRLoadInfo::SetLoadingEmbedderPolicy(
     nsILoadInfo::CrossOriginEmbedderPolicy aPolicy) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
+TRRLoadInfo::GetIsOriginTrialCoepCredentiallessEnabledForTopLevel(
+    bool* aIsOriginTrialCoepCredentiallessEnabledForTopLevel) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TRRLoadInfo::SetIsOriginTrialCoepCredentiallessEnabledForTopLevel(
+    bool aIsOriginTrialCoepCredentiallessEnabledForTopLevel) {
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
 TRRLoadInfo::GetUnstrippedURI(nsIURI** aURI) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 TRRLoadInfo::SetUnstrippedURI(nsIURI* aURI) { return NS_ERROR_NOT_IMPLEMENTED; }
 
 }  // namespace net
--- a/netwerk/base/nsILoadInfo.idl
+++ b/netwerk/base/nsILoadInfo.idl
@@ -1390,16 +1390,21 @@ interface nsILoadInfo : nsISupports
    * innerWindowIID in the nsILoadInfo.
    * It also could be set by workers when fetch is called under
    * the workers' scope.
    */
   [infallible] attribute nsILoadInfo_CrossOriginEmbedderPolicy
         loadingEmbedderPolicy;
 
   /**
+   * This attribute will be true if the top level document has COEP:
+   * credentialless enabled in Origin Trial.
+   */
+  [infallible] attribute boolean isOriginTrialCoepCredentiallessEnabledForTopLevel;
+  /**
    * This attribute will be true if this is a load triggered by a media
    * element.
    */
   [infallible] attribute boolean isMediaRequest;
 
   /**
    * This attribute will be true if this is a load triggered by a media
    * element and it's an initial request.
--- a/netwerk/base/nsNetUtil.cpp
+++ b/netwerk/base/nsNetUtil.cpp
@@ -2600,17 +2600,18 @@ nsresult NS_MaybeOpenChannelUsingOpen(ns
 
 nsresult NS_MaybeOpenChannelUsingAsyncOpen(nsIChannel* aChannel,
                                            nsIStreamListener* aListener) {
   nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
   return aChannel->AsyncOpen(aListener);
 }
 
 nsILoadInfo::CrossOriginEmbedderPolicy
-NS_GetCrossOriginEmbedderPolicyFromHeader(const nsACString& aHeader) {
+NS_GetCrossOriginEmbedderPolicyFromHeader(
+    const nsACString& aHeader, bool aIsOriginTrialCoepCredentiallessEnabled) {
   nsCOMPtr<nsISFVService> sfv = GetSFVService();
 
   nsCOMPtr<nsISFVItem> item;
   nsresult rv = sfv->ParseItem(aHeader, getter_AddRefs(item));
   if (NS_FAILED(rv)) {
     return nsILoadInfo::EMBEDDER_POLICY_NULL;
   }
 
@@ -2629,17 +2630,18 @@ NS_GetCrossOriginEmbedderPolicyFromHeade
   rv = token->GetValue(embedderPolicy);
   if (NS_FAILED(rv)) {
     return nsILoadInfo::EMBEDDER_POLICY_NULL;
   }
 
   if (embedderPolicy.EqualsLiteral("require-corp")) {
     return nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP;
   } else if (embedderPolicy.EqualsLiteral("credentialless") &&
-             StaticPrefs::browser_tabs_remote_coep_credentialless()) {
+             IsCoepCredentiallessEnabled(
+                 aIsOriginTrialCoepCredentiallessEnabled)) {
     return nsILoadInfo::EMBEDDER_POLICY_CREDENTIALLESS;
   }
 
   return nsILoadInfo::EMBEDDER_POLICY_NULL;
 }
 
 /** Given the first (disposition) token from a Content-Disposition header,
  * tell whether it indicates the content is inline or attachment
@@ -3896,8 +3898,14 @@ void CheckForBrokenChromeURL(nsILoadInfo
       Unused << xpc->DebugDumpJSStack(false, false, false);
     }
 #endif
     MOZ_CRASH_UNSAFE_PRINTF("Missing chrome or resource URLs: %s", spec.get());
   } else {
     printf_stderr("Missing chrome or resource URL: %s\n", spec.get());
   }
 }
+
+bool IsCoepCredentiallessEnabled(bool aIsOriginTrialCoepCredentiallessEnabled) {
+  return StaticPrefs::
+             browser_tabs_remote_coep_credentialless_DoNotUseDirectly() ||
+         aIsOriginTrialCoepCredentiallessEnabled;
+}
--- a/netwerk/base/nsNetUtil.h
+++ b/netwerk/base/nsNetUtil.h
@@ -54,16 +54,17 @@ class nsIStreamListener;
 class nsIStreamLoader;
 class nsIStreamLoaderObserver;
 class nsIIncrementalStreamLoader;
 class nsIIncrementalStreamLoaderObserver;
 
 namespace mozilla {
 class Encoding;
 class OriginAttributes;
+class OriginTrials;
 namespace dom {
 class ClientInfo;
 class PerformanceStorage;
 class ServiceWorkerDescriptor;
 }  // namespace dom
 
 namespace ipc {
 class FileDescriptor;
@@ -826,17 +827,18 @@ nsresult NS_MaybeOpenChannelUsingAsyncOp
 
 /**
  * Returns nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP if `aHeader` is
  * "require-corp" and nsILoadInfo::EMBEDDER_POLICY_NULL otherwise.
  *
  * See: https://mikewest.github.io/corpp/#parsing
  */
 nsILoadInfo::CrossOriginEmbedderPolicy
-NS_GetCrossOriginEmbedderPolicyFromHeader(const nsACString& aHeader);
+NS_GetCrossOriginEmbedderPolicyFromHeader(
+    const nsACString& aHeader, bool aIsOriginTrialCoepCredentiallessEnabled);
 
 /** Given the first (disposition) token from a Content-Disposition header,
  * tell whether it indicates the content is inline or attachment
  * @param aDispToken the disposition token from the content-disposition header
  */
 uint32_t NS_GetContentDispositionFromToken(const nsAString& aDispToken);
 
 /** Determine the disposition (inline/attachment) of the content based on the
@@ -1064,9 +1066,11 @@ void WarnIgnoredPreload(const mozilla::d
  * @param aInput The host to be analyzed.
  * @param aHost  The host to compare to.
  */
 nsresult NS_HasRootDomain(const nsACString& aInput, const nsACString& aHost,
                           bool* aResult);
 
 void CheckForBrokenChromeURL(nsILoadInfo* aLoadInfo, nsIURI* aURI);
 
+bool IsCoepCredentiallessEnabled(bool aIsOriginTrialCoepCredentiallessEnabled);
+
 #endif  // !nsNetUtil_h__
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -161,16 +161,17 @@ struct LoadInfoArgs
   bool                        isMediaInitialRequest;
   bool                        isFromObjectOrEmbed;
   CookieJarSettingsArgs       cookieJarSettings;
   uint32_t                    requestBlockingReason;
   CSPInfo?                    cspToInheritInfo;
   StoragePermissionState      storagePermission;
   bool                        isMetaRefresh;
   CrossOriginEmbedderPolicy   loadingEmbedderPolicy;
+  bool                        originTrialCoepCredentiallessEnabledForTopLevel;
   nsIURI                      unstrippedURI;
 };
 
 /**
  * This structure is used to carry selected properties of a LoadInfo
  * object to child processes to merge LoadInfo changes from the parent
  * process.  We don't want to use LoadInfoArgs for that since it's
  * too huge and we only care about small subpart of properties anyway.
--- a/netwerk/protocol/http/ClassifierDummyChannel.cpp
+++ b/netwerk/protocol/http/ClassifierDummyChannel.cpp
@@ -767,16 +767,17 @@ NS_IMETHODIMP ClassifierDummyChannel::Is
       UrlClassifierCommon::IsSocialTrackingClassificationFlag(
           mThirdPartyClassificationFlags);
   return NS_OK;
 }
 
 void ClassifierDummyChannel::DoDiagnosticAssertWhenOnStopNotCalledOnDestroy() {}
 
 NS_IMETHODIMP ClassifierDummyChannel::GetResponseEmbedderPolicy(
+    bool aIsOriginTrialCoepCredentiallessEnabled,
     nsILoadInfo::CrossOriginEmbedderPolicy* aOutPolicy) {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP ClassifierDummyChannel::SetWaitForHTTPSSVCRecord() {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -12,16 +12,17 @@
 #include <utility>
 
 #include "HttpBaseChannel.h"
 #include "HttpLog.h"
 #include "LoadInfo.h"
 #include "ReferrerInfo.h"
 #include "mozIRemoteLazyInputStream.h"
 #include "mozIThirdPartyUtil.h"
+#include "mozilla/LoadInfo.h"
 #include "mozilla/AntiTrackingUtils.h"
 #include "mozilla/BasePrincipal.h"
 #include "mozilla/BinarySearch.h"
 #include "mozilla/ConsoleReportCollector.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/InputStreamLengthHelper.h"
 #include "mozilla/NullPrincipal.h"
 #include "mozilla/PermissionManager.h"
@@ -2398,17 +2399,21 @@ nsresult HttpBaseChannel::ProcessCrossOr
           ExtContentPolicy::TYPE_DOCUMENT &&
       mLoadInfo->GetExternalContentPolicyType() !=
           ExtContentPolicy::TYPE_SUBDOCUMENT) {
     return NS_OK;
   }
 
   nsILoadInfo::CrossOriginEmbedderPolicy resultPolicy =
       nsILoadInfo::EMBEDDER_POLICY_NULL;
-  rv = GetResponseEmbedderPolicy(&resultPolicy);
+  bool isCoepCredentiallessEnabled;
+  rv = mLoadInfo->GetIsOriginTrialCoepCredentiallessEnabledForTopLevel(
+      &isCoepCredentiallessEnabled);
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = GetResponseEmbedderPolicy(isCoepCredentiallessEnabled, &resultPolicy);
   if (NS_FAILED(rv)) {
     return NS_OK;
   }
 
   // https://html.spec.whatwg.org/multipage/origin.html#coep
   if (mLoadInfo->GetExternalContentPolicyType() ==
           ExtContentPolicy::TYPE_SUBDOCUMENT &&
       mLoadInfo->GetLoadingEmbedderPolicy() !=
@@ -2464,18 +2469,17 @@ nsresult HttpBaseChannel::ProcessCrossOr
 
   nsAutoCString content;
   Unused << mResponseHead->GetHeader(nsHttp::Cross_Origin_Resource_Policy,
                                      content);
 
   if (StaticPrefs::browser_tabs_remote_useCrossOriginEmbedderPolicy()) {
     if (content.IsEmpty()) {
       if (mLoadInfo->GetLoadingEmbedderPolicy() ==
-              nsILoadInfo::EMBEDDER_POLICY_CREDENTIALLESS &&
-          StaticPrefs::browser_tabs_remote_coep_credentialless()) {
+          nsILoadInfo::EMBEDDER_POLICY_CREDENTIALLESS) {
         bool requestIncludesCredentials = false;
         nsresult rv = GetCorsIncludeCredentials(&requestIncludesCredentials);
         if (NS_FAILED(rv)) {
           return NS_OK;
         }
         // COEP: Set policy to `same-origin` if: response’s
         // request-includes-credentials is true, or forNavigation is true.
         if (requestIncludesCredentials ||
@@ -5665,32 +5669,33 @@ HttpBaseChannel::CancelByURLClassifier(n
   return Cancel(aErrorCode);
 }
 
 void HttpBaseChannel::SetIPv4Disabled() { mCaps |= NS_HTTP_DISABLE_IPV4; }
 
 void HttpBaseChannel::SetIPv6Disabled() { mCaps |= NS_HTTP_DISABLE_IPV6; }
 
 NS_IMETHODIMP HttpBaseChannel::GetResponseEmbedderPolicy(
+    bool aIsOriginTrialCoepCredentiallessEnabled,
     nsILoadInfo::CrossOriginEmbedderPolicy* aOutPolicy) {
   *aOutPolicy = nsILoadInfo::EMBEDDER_POLICY_NULL;
   if (!mResponseHead) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   if (!nsContentUtils::ComputeIsSecureContext(this)) {
     // Feature is only available for secure contexts.
     return NS_OK;
   }
 
   nsAutoCString content;
   Unused << mResponseHead->GetHeader(nsHttp::Cross_Origin_Embedder_Policy,
                                      content);
-
-  *aOutPolicy = NS_GetCrossOriginEmbedderPolicyFromHeader(content);
+  *aOutPolicy = NS_GetCrossOriginEmbedderPolicyFromHeader(
+      content, aIsOriginTrialCoepCredentiallessEnabled);
   return NS_OK;
 }
 
 // Obtain a cross-origin opener-policy from a response response and a
 // cross-origin opener policy initiator.
 // https://gist.github.com/annevk/6f2dd8c79c77123f39797f6bdac43f3e
 NS_IMETHODIMP HttpBaseChannel::ComputeCrossOriginOpenerPolicy(
     nsILoadInfo::CrossOriginOpenerPolicy aInitiatorPolicy,
@@ -5742,21 +5747,25 @@ NS_IMETHODIMP HttpBaseChannel::ComputeCr
   nsILoadInfo::CrossOriginOpenerPolicy policy =
       nsILoadInfo::OPENER_POLICY_UNSAFE_NONE;
 
   if (openerPolicy.EqualsLiteral("same-origin")) {
     policy = nsILoadInfo::OPENER_POLICY_SAME_ORIGIN;
   } else if (openerPolicy.EqualsLiteral("same-origin-allow-popups")) {
     policy = nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_ALLOW_POPUPS;
   }
-
   if (policy == nsILoadInfo::OPENER_POLICY_SAME_ORIGIN) {
     nsILoadInfo::CrossOriginEmbedderPolicy coep =
         nsILoadInfo::EMBEDDER_POLICY_NULL;
-    if (NS_SUCCEEDED(GetResponseEmbedderPolicy(&coep)) &&
+    bool isCoepCredentiallessEnabled;
+    rv = mLoadInfo->GetIsOriginTrialCoepCredentiallessEnabledForTopLevel(
+        &isCoepCredentiallessEnabled);
+    NS_ENSURE_SUCCESS(rv, rv);
+    if (NS_SUCCEEDED(
+            GetResponseEmbedderPolicy(isCoepCredentiallessEnabled, &coep)) &&
         (coep == nsILoadInfo::EMBEDDER_POLICY_REQUIRE_CORP ||
          coep == nsILoadInfo::EMBEDDER_POLICY_CREDENTIALLESS)) {
       policy =
           nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP;
     }
   }
 
   *aOutPolicy = policy;
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -325,16 +325,17 @@ class HttpBaseChannel : public nsHashPro
   virtual void SetIPv6Disabled(void) override;
   NS_IMETHOD GetCrossOriginOpenerPolicy(
       nsILoadInfo::CrossOriginOpenerPolicy* aCrossOriginOpenerPolicy) override;
   NS_IMETHOD ComputeCrossOriginOpenerPolicy(
       nsILoadInfo::CrossOriginOpenerPolicy aInitiatorPolicy,
       nsILoadInfo::CrossOriginOpenerPolicy* aOutPolicy) override;
   NS_IMETHOD HasCrossOriginOpenerPolicyMismatch(bool* aIsMismatch) override;
   NS_IMETHOD GetResponseEmbedderPolicy(
+      bool aIsOriginTrialCoepCredentiallessEnabled,
       nsILoadInfo::CrossOriginEmbedderPolicy* aOutPolicy) override;
 
   inline void CleanRedirectCacheChainIfNecessary() {
     mRedirectedCachekeys = nullptr;
   }
   NS_IMETHOD HTTPUpgrade(const nsACString& aProtocolName,
                          nsIHttpUpgradeListener* aListener) override;
   void DoDiagnosticAssertWhenOnStopNotCalledOnDestroy() override;
--- a/netwerk/protocol/http/nsIHttpChannelInternal.idl
+++ b/netwerk/protocol/http/nsIHttpChannelInternal.idl
@@ -437,17 +437,17 @@ interface nsIHttpChannelInternal : nsISu
     [noscript]
     nsILoadInfo_CrossOriginOpenerPolicy computeCrossOriginOpenerPolicy(
         in nsILoadInfo_CrossOriginOpenerPolicy aInitiatorPolicy);
 
     [noscript]
     bool hasCrossOriginOpenerPolicyMismatch();
 
     [noscript]
-    nsILoadInfo_CrossOriginEmbedderPolicy getResponseEmbedderPolicy();
+    nsILoadInfo_CrossOriginEmbedderPolicy getResponseEmbedderPolicy(in boolean aIsOriginTrialCoepCredentiallessEnabled);
 
     [noscript, notxpcom, nostdcall]
     void DoDiagnosticAssertWhenOnStopNotCalledOnDestroy();
 
     /**
      * If this is called, this channel's transaction will not be dispatched
      * until the HTTPSSVC record is available.
      */