Skip to content

Conversation

@RihanArfan
Copy link
Contributor

πŸ”— Linked issue

resolves #214

related nuxt-hub/core#684

❓ Type of change

Adds ability to pass additional permissions to the iframe when registering a custom tab. Previously they were hard coded in #215.

  • πŸ“– Documentation (updates to the documentation or readme)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • πŸ‘Œ Enhancement (improving an existing functionality)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

πŸ“š Description

const view = props.tab.view as ModuleIframeView
const isPersistent = view.persistent !== false
const allowedPermissions = ['clipboard-write', 'clipboard-read']
const allowedPermissions = ['clipboard-write', 'clipboard-read', ...(view.permissions || [])]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cached iframes don't update their permissions when remounted with different permission configurations. The allow attribute is only set when creating a new iframe, not when reusing a cached one.

View Details
πŸ“ Patch Details
diff --git a/packages/devtools/client/components/IframeView.vue b/packages/devtools/client/components/IframeView.vue
index b9d87d69..fe8b119d 100644
--- a/packages/devtools/client/components/IframeView.vue
+++ b/packages/devtools/client/components/IframeView.vue
@@ -3,7 +3,7 @@ import { useElementBounding } from '@vueuse/core'
 import { computed, nextTick, onMounted, onUnmounted, reactive, ref, watchEffect } from 'vue'
 import { getColorMode, useInjectionClient } from '~/composables/client'
 
-const iframeCacheMap = new Map<string, HTMLIFrameElement>()
+const iframeCacheMap = new Map<string, { element: HTMLIFrameElement, allowAttribute: string }>()
 </script>
 
 <script setup lang="ts">
@@ -24,18 +24,20 @@ onMounted(() => {
   const view = props.tab.view as ModuleIframeView
   const isPersistent = view.persistent !== false
   const allowedPermissions = ['clipboard-write', 'clipboard-read', ...(view.permissions || [])]
+  const allowAttribute = allowedPermissions.join('; ')
 
-  if (iframeCacheMap.get(key.value) && isPersistent) {
-    iframeEl.value = iframeCacheMap.get(key.value)!
+  const cached = iframeCacheMap.get(key.value)
+  if (cached && isPersistent && cached.allowAttribute === allowAttribute) {
+    iframeEl.value = cached.element
     iframeEl.value.style.visibility = 'visible'
   }
   else {
     iframeEl.value = document.createElement('iframe')
-    iframeEl.value.setAttribute('allow', allowedPermissions.join('; '))
+    iframeEl.value.setAttribute('allow', allowAttribute)
     iframeEl.value.setAttribute('aria-label', 'Nuxt Devtools')
 
     if (isPersistent)
-      iframeCacheMap.set(key.value, iframeEl.value)
+      iframeCacheMap.set(key.value, { element: iframeEl.value, allowAttribute })
     iframeEl.value.src = view.src
     // CORS
     try {

Analysis

Cached iframes don't update permissions when remounted with different configurations

What fails: IframeView.vue caches iframes by tab name and reuses them on remount. When a cached iframe is remounted with different permissions in the tab configuration, the iframe retains its original allow attribute instead of updating to the new permissions.

How to reproduce:

  1. Create a module with a custom tab that uses a factory function for addCustomTab()
  2. Mount the tab with permissions: ['camera']
  3. Navigate away (iframe is cached but hidden)
  4. Call refreshCustomTabs() to trigger a refresh where the factory returns the same tab name but with permissions: ['microphone', 'geolocation']
  5. Navigate back to the tab
  6. The cached iframe still has allow="clipboard-write; clipboard-read; camera" instead of the new permissions

Result: The iframe retains the old permission set. Since the allow attribute cannot be updated after an iframe loads per browser security specifications, the cached iframe cannot dynamically acquire new permissions.

Expected: When a cached iframe is reused, its allow attribute should match the current permissions configuration. If permissions have changed, either the cache should be invalidated or a new iframe should be created.

Technical context: The bug occurs because:

  • The cache is keyed by tab.name only, not considering the permissions configuration
  • When retrieving a cached iframe (lines 28-31), the code skips permission setup entirely
  • The allow attribute is only set when creating new iframes (line 33)
  • Per web standards, the allow attribute's permissions are evaluated at iframe load time and cannot be updated afterward

@atinux atinux requested a review from antfu November 20, 2025 11:32
@antfu antfu merged commit bc1d11c into nuxt:main Nov 21, 2025
1 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow permissions within iframe element.

3 participants