diff --git a/components/performance_manager/v8_memory/web_memory_impl.cc b/components/performance_manager/v8_memory/web_memory_impl.cc
index 44e3d379c95d7..07a4d31561d55 100644
--- a/components/performance_manager/v8_memory/web_memory_impl.cc
+++ b/components/performance_manager/v8_memory/web_memory_impl.cc
@@ -78,8 +78,8 @@ void CheckIsCrossOriginIsolatedOnUISeq(
     // Frame was deleted before the task ran.
     return;
   }
-  if (rfh->GetCrossOriginIsolationStatus() ==
-          content::RenderFrameHost::CrossOriginIsolationStatus::kNotIsolated &&
+  if (rfh->GetWebExposedIsolationLevel() ==
+          content::RenderFrameHost::WebExposedIsolationLevel::kNotIsolated &&
       !base::CommandLine::ForCurrentProcess()->HasSwitch(
           switches::kDisableWebSecurity)) {
     std::move(bad_message_callback)
diff --git a/content/browser/direct_sockets/direct_sockets_service_impl.cc b/content/browser/direct_sockets/direct_sockets_service_impl.cc
index d18ca03d3c359..2977f51c4ee3d 100644
--- a/content/browser/direct_sockets/direct_sockets_service_impl.cc
+++ b/content/browser/direct_sockets/direct_sockets_service_impl.cc
@@ -287,6 +287,12 @@ void DirectSocketsServiceImpl::OpenTcpSocket(
     mojo::PendingReceiver<network::mojom::TCPConnectedSocket> receiver,
     mojo::PendingRemote<network::mojom::SocketObserver> observer,
     OpenTcpSocketCallback callback) {
+  if (!frame_host_ || frame_host_->GetWebExposedIsolationLevel() <
+                          RenderFrameHost::WebExposedIsolationLevel::
+                              kMaybeIsolatedApplication) {
+    mojo::ReportBadMessage("Insufficient isolation to open socket.");
+    return;
+  }
   if (!options) {
     mojo::ReportBadMessage("Invalid request to open socket");
     return;
@@ -314,6 +320,12 @@ void DirectSocketsServiceImpl::OpenUdpSocket(
     mojo::PendingReceiver<network::mojom::UDPSocket> receiver,
     mojo::PendingRemote<network::mojom::UDPSocketListener> listener,
     OpenUdpSocketCallback callback) {
+  if (!frame_host_ || frame_host_->GetWebExposedIsolationLevel() <
+                          RenderFrameHost::WebExposedIsolationLevel::
+                              kMaybeIsolatedApplication) {
+    mojo::ReportBadMessage("Insufficient isolation to open socket.");
+    return;
+  }
   if (!options) {
     mojo::ReportBadMessage("Invalid request to open socket");
     return;
diff --git a/content/browser/renderer_host/render_frame_host_impl.cc b/content/browser/renderer_host/render_frame_host_impl.cc
index ef9aac14d612f..b1647921ee6ed 100644
--- a/content/browser/renderer_host/render_frame_host_impl.cc
+++ b/content/browser/renderer_host/render_frame_host_impl.cc
@@ -1875,18 +1875,23 @@ bool RenderFrameHostImpl::RequiresProxyToParent() {
   return GetSiteInstance() != parent_->GetSiteInstance();
 }
 
-RenderFrameHost::CrossOriginIsolationStatus
-RenderFrameHostImpl::GetCrossOriginIsolationStatus() {
+RenderFrameHost::WebExposedIsolationLevel
+RenderFrameHostImpl::GetWebExposedIsolationLevel() {
   ProcessLock process_lock = GetSiteInstance()->GetProcessLock();
-  if (process_lock.is_invalid() ||
-      !process_lock.web_exposed_isolation_info().is_isolated()) {
-    // Cross-origin isolated frames must be hosted in cross-origin isolated
-    // processes.
-    return RenderFrameHost::CrossOriginIsolationStatus::kNotIsolated;
+  if (process_lock.is_invalid())
+    return RenderFrameHost::WebExposedIsolationLevel::kNotIsolated;
+
+  WebExposedIsolationInfo info = process_lock.web_exposed_isolation_info();
+  if (info.is_isolated_application()) {
+    // TODO(crbug.com/1159832): Check the document policy once it's available to
+    // find out if this frame is actually isolated.
+    return RenderFrameHost::WebExposedIsolationLevel::kMaybeIsolatedApplication;
+  } else if (info.is_isolated()) {
+    // TODO(crbug.com/1159832): Check the document policy once it's available to
+    // find out if this frame is actually isolated.
+    return RenderFrameHost::WebExposedIsolationLevel::kMaybeIsolated;
   }
-  // TODO(crbug.com/1159832): Check the document policy once it's available to
-  // find out if this frame is actually isolated.
-  return RenderFrameHost::CrossOriginIsolationStatus::kMaybeIsolated;
+  return RenderFrameHost::WebExposedIsolationLevel::kNotIsolated;
 }
 
 const GURL& RenderFrameHostImpl::GetLastCommittedURL() {
diff --git a/content/browser/renderer_host/render_frame_host_impl.h b/content/browser/renderer_host/render_frame_host_impl.h
index 410ba79caf4f3..ec2afec05ebd2 100644
--- a/content/browser/renderer_host/render_frame_host_impl.h
+++ b/content/browser/renderer_host/render_frame_host_impl.h
@@ -336,7 +336,7 @@ class CONTENT_EXPORT RenderFrameHostImpl
   const base::Optional<gfx::Size>& GetFrameSize() override;
   size_t GetFrameDepth() override;
   bool IsCrossProcessSubframe() override;
-  CrossOriginIsolationStatus GetCrossOriginIsolationStatus() override;
+  WebExposedIsolationLevel GetWebExposedIsolationLevel() override;
   const GURL& GetLastCommittedURL() override;
   const url::Origin& GetLastCommittedOrigin() override;
   const net::NetworkIsolationKey& GetNetworkIsolationKey() override;
diff --git a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
index a4276f2d36aa0..33681aa4b800d 100644
--- a/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
+++ b/content/browser/renderer_host/render_frame_host_impl_browsertest.cc
@@ -5139,20 +5139,56 @@ IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
 }
 
 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
-                       GetCrossOriginIsolationStatus) {
+                       GetWebExposedIsolationLevel) {
+  // Not isolated:
   EXPECT_TRUE(
       NavigateToURL(shell(), embedded_test_server()->GetURL("/empty.html")));
-  EXPECT_EQ(RenderFrameHost::CrossOriginIsolationStatus::kNotIsolated,
-            root_frame_host()->GetCrossOriginIsolationStatus());
+  EXPECT_EQ(RenderFrameHost::WebExposedIsolationLevel::kNotIsolated,
+            root_frame_host()->GetWebExposedIsolationLevel());
 
+  // Cross-Origin Isolated:
   EXPECT_TRUE(NavigateToURL(shell(),
                             embedded_test_server()->GetURL(
                                 "/set-header?"
                                 "Cross-Origin-Opener-Policy: same-origin&"
                                 "Cross-Origin-Embedder-Policy: require-corp")));
   // Status can be kIsolated or kMaybeIsolated.
-  EXPECT_NE(RenderFrameHost::CrossOriginIsolationStatus::kNotIsolated,
-            root_frame_host()->GetCrossOriginIsolationStatus());
+  EXPECT_LT(RenderFrameHost::WebExposedIsolationLevel::kNotIsolated,
+            root_frame_host()->GetWebExposedIsolationLevel());
+  EXPECT_GT(
+      RenderFrameHost::WebExposedIsolationLevel::kMaybeIsolatedApplication,
+      root_frame_host()->GetWebExposedIsolationLevel());
+}
+
+class RenderFrameHostImplBrowserTestWithDirectSockets
+    : public RenderFrameHostImplBrowserTest {
+ public:
+  RenderFrameHostImplBrowserTestWithDirectSockets() {
+    feature_list_.InitAndEnableFeature(features::kDirectSockets);
+  }
+
+ private:
+  base::test::ScopedFeatureList feature_list_;
+};
+
+IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTestWithDirectSockets,
+                       GetWebExposedIsolationLevel) {
+  // Not isolated:
+  EXPECT_TRUE(
+      NavigateToURL(shell(), embedded_test_server()->GetURL("/empty.html")));
+  EXPECT_EQ(RenderFrameHost::WebExposedIsolationLevel::kNotIsolated,
+            root_frame_host()->GetWebExposedIsolationLevel());
+
+  // Isolated Application:
+
+  EXPECT_TRUE(NavigateToURL(shell(),
+                            embedded_test_server()->GetURL(
+                                "/set-header?"
+                                "Cross-Origin-Opener-Policy: same-origin&"
+                                "Cross-Origin-Embedder-Policy: require-corp")));
+  // Status can be kIsolatedApplication or kMaybeIsolatedApplication.
+  EXPECT_LT(RenderFrameHost::WebExposedIsolationLevel::kIsolated,
+            root_frame_host()->GetWebExposedIsolationLevel());
 }
 
 IN_PROC_BROWSER_TEST_F(RenderFrameHostImplBrowserTest,
diff --git a/content/browser/worker_host/worker_browsertest.cc b/content/browser/worker_host/worker_browsertest.cc
index 5fc9b38933637..16a4a747feb27 100644
--- a/content/browser/worker_host/worker_browsertest.cc
+++ b/content/browser/worker_host/worker_browsertest.cc
@@ -338,8 +338,8 @@ IN_PROC_BROWSER_TEST_P(WorkerTest, SharedWorkerWithoutCoepInDifferentProcess) {
       shell()->web_contents()->GetMainFrame());
   auto page_lock = page_rfh->GetSiteInstance()->GetProcessLock();
   EXPECT_TRUE(page_lock.web_exposed_isolation_info().is_isolated());
-  EXPECT_NE(page_rfh->GetCrossOriginIsolationStatus(),
-            RenderFrameHost::CrossOriginIsolationStatus::kNotIsolated);
+  EXPECT_GT(page_rfh->GetWebExposedIsolationLevel(),
+            RenderFrameHost::WebExposedIsolationLevel::kNotIsolated);
 
   // Create a shared worker from the cross-origin-isolated page.
   // The worker must be in a different process because shared workers isn't
diff --git a/content/public/browser/render_frame_host.h b/content/public/browser/render_frame_host.h
index 9bf669e25e8fb..f387432a31250 100644
--- a/content/public/browser/render_frame_host.h
+++ b/content/public/browser/render_frame_host.h
@@ -334,60 +334,77 @@ class CONTENT_EXPORT RenderFrameHost : public IPC::Listener,
   // Returns true if the frame is out of process relative to its parent.
   virtual bool IsCrossProcessSubframe() = 0;
 
-  // Indicates whether this frame is in a cross-origin isolated agent cluster.
-  // See [1] and [2] for a description of what this means for web content.
-  // Specifically, an agent cluster may be cross-origin isolated if:
-  // - its top-level document has "Cross-Origin-Opener-Policy: same-origin" and
-  //   "Cross-Origin-Embedder-Policy: require-corp" HTTP headers; or,
-  // - its top-level worker script has a
-  //   "Cross-Origin-Embedder-Policy: require-corp" HTTP header.
+  // Reflects the web-exposed isolation properties of a given frame, which
+  // depends both on the process in which the frame lives, as well as the agent
+  // cluster into which it has been placed.
   //
-  // In practice this means that the frame is guaranteed to be hosted in a
-  // process that is isolated to the frame's origin. The process may also host
-  // cross-origin frames and workers only if they have opted in to being
-  // embedded with CORS or CORP headers.
+  // Three broad categories are possible:
   //
-  // Certain advanced web platform APIs are gated behind this property. It will
-  // correspond to the value returned by accessing
-  // "WindowOrWorkerGlobalScope.crossOriginIsolated" in Javascript.
+  // 1.  The frame may not be isolated in a web-facing way.
   //
-  // NOTE: some of the information needed to fully determine a frame's
-  // cross-isolation status is currently not available in the browser process.
-  // Access to web platform API's must be checked in the renderer, with the
-  // CrossOriginIsolationStatus on the browser side only used as a backup to
-  // catch misbehaving renderers.
+  // 2.  The frame may be "cross-origin isolated", corresponding to the value
+  //     returned by `WorkerOrWindowGlobalScope.crossOriginIsolated`, and gating
+  //     the set of APIs which specify [CrossOriginIsolated] attributes. The
+  //     requirements for this level of isolation are described in [1] and [2]
+  //     below.
+  //
+  //     In practice this means that the frame is guaranteed to be hosted in a
+  //     process that is isolated to the frame's origin. The process may also
+  //     host cross-origin frames and workers only if they have opted in to
+  //     being embedded by asserting CORS or CORP headers.
+  //
+  // 3.  The frame may be an "isolated application", corresponding to a mostly
+  //     TBD set of restrictions we're exploring in https://crbug.com/1206150,
+  //     and which currently gate the set of APIs which specify
+  //     [DirectSocketEnabled] attributes.
+  //
+  // The enum below is ordered from least-isolated to most-isolated.
   //
   // [1]
   // https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/crossOriginIsolated
   // [2] https://w3c.github.io/webappsec-permissions-policy/
-  enum class CrossOriginIsolationStatus {
-    // The frame is in a cross-origin isolated process and agent cluster.
-    // It is allowed to call web platform API's gated behind the
-    // crossOriginIsolated property.
-    kIsolated,
-
-    // The frame is not in a cross-origin isolated agent cluster. It may be
-    // hosted in a cross-origin isolated process but it is not allowed to call
-    // web platform API's gated behind the crossOriginIsolated property.
+  //
+  // NOTE: some of the information needed to fully determine a frame's
+  // isolation status is currently not available in the browser process.
+  // Access to web platform API's must be checked in the renderer, with the
+  // WebExposedIsolationLevel on the browser side only used as a backup to
+  // catch misbehaving renderers.
+  enum class WebExposedIsolationLevel {
+    // The frame is not in a cross-origin isolated agent cluster. It may not
+    // meet the requirements for such isolation in itself, or it may be
+    // hosted in a process capable of supporting cross-origin isolation or
+    // application isolation, but barred from using those capabilities by
+    // its embedder.
     kNotIsolated,
 
-    // The frame is in a cross-origin isolated process, but it's not possible
-    // to determine whether it's in a cross-origin isolated agent cluster. The
-    // browser process should not prevent it from calling web platform API's
-    // gated behind the crossOriginIsolated property because it may be allowed.
-    // TODO(clamy): Remove this status once the document policy is available on
-    // the browser side.
+    // The frame is in a cross-origin isolated process and agent cluster,
+    // allowed to access web platform APIs gated on [CrossOriginIsolated].
+    //
+    // TODO(clamy): Remove this "maybe" status once it is possible to determine
+    // conclusively whether the document is capable of calling cross-origin
+    // isolated APIs by examining the active document policy.
     kMaybeIsolated,
+    kIsolated,
+
+    // The frame is in a cross-origin isolated process and agent cluster that
+    // supports application isolation, allowing access to web platform APIs
+    // gated on both [CrossOriginIsolated] and [DirectSocketEnabled].
+    //
+    // TODO(clamy): Remove this "maybe" status once it is possible to determine
+    // conclusively whether the document is capable of calling cross-origin
+    // isolated APIs by examining the active document policy.
+    kMaybeIsolatedApplication,
+    kIsolatedApplication
   };
 
-  // Returns whether the frame is in a cross-origin isolated agent cluster.
+  // Returns the web-exposed isolation level of a frame's agent cluster.
   //
   // Note that this is a property of the document so can change as the frame
   // navigates.
   //
   // TODO(https://936696): Once RenderDocument ships this should be exposed as
   // an invariant of the document host.
-  virtual CrossOriginIsolationStatus GetCrossOriginIsolationStatus() = 0;
+  virtual WebExposedIsolationLevel GetWebExposedIsolationLevel() = 0;
 
   // Returns the last committed URL of this RenderFrameHost. This will be empty
   // until the first commit in this RenderFrameHost.