From e2e5d4f1d50c22aa1846e05cbc83abd81949ab2a Mon Sep 17 00:00:00 2001 From: Nivedin <53208152+nivedin@users.noreply.github.com> Date: Tue, 18 Mar 2025 13:32:07 +0530 Subject: [PATCH] fix: resolve issues around rendering HTML response previews (#4890) Co-authored-by: jamesgeorge007 <25279263+jamesgeorge007@users.noreply.github.com> --- .../lenses/renderers/HTMLLensRenderer.vue | 9 ++++- .../src/composables/lens-actions.ts | 40 +++++++++++++------ 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/packages/hoppscotch-common/src/components/lenses/renderers/HTMLLensRenderer.vue b/packages/hoppscotch-common/src/components/lenses/renderers/HTMLLensRenderer.vue index 9abc0bbd..8cea688b 100644 --- a/packages/hoppscotch-common/src/components/lenses/renderers/HTMLLensRenderer.vue +++ b/packages/hoppscotch-common/src/components/lenses/renderers/HTMLLensRenderer.vue @@ -98,6 +98,7 @@ import IconEyeOff from "~icons/lucide/eye-off" import IconWrapText from "~icons/lucide/wrap-text" import IconSave from "~icons/lucide/save" import { HoppRESTRequestResponse } from "@hoppscotch/data" +import { computedAsync } from "@vueuse/core" const t = useI18n() const persistenceService = useService(PersistenceService) @@ -136,8 +137,12 @@ const { downloadIcon, downloadResponse } = useDownloadResponse( request_name: responseName.value, }) ) -const defaultPreview = - (await persistenceService.getLocalConfig("lens_html_preview")) === "true" + +const defaultPreview = computedAsync( + async () => + (await persistenceService.getLocalConfig("lens_html_preview")) === "true", + false +) const { previewFrame, previewEnabled, togglePreview } = usePreview( defaultPreview, diff --git a/packages/hoppscotch-common/src/composables/lens-actions.ts b/packages/hoppscotch-common/src/composables/lens-actions.ts index 6af3d9e0..ff070b4c 100644 --- a/packages/hoppscotch-common/src/composables/lens-actions.ts +++ b/packages/hoppscotch-common/src/composables/lens-actions.ts @@ -1,7 +1,7 @@ import { HoppRESTResponse } from "@helpers/types/HoppRESTResponse" import { copyToClipboard } from "@helpers/utils/clipboard" import { refAutoReset } from "@vueuse/core" -import { computed, ComputedRef, onMounted, ref, Ref } from "vue" +import { computed, ComputedRef, ref, Ref, watch } from "vue" import jsonToLanguage from "~/helpers/utils/json-to-language" import { platform } from "~/platform" @@ -88,7 +88,7 @@ export function useDownloadResponse( } export function usePreview( - previewEnabledDefault: boolean, + previewEnabled: Ref, responseBodyText: Ref ): { previewFrame: Ref @@ -96,18 +96,8 @@ export function usePreview( togglePreview: () => void } { const previewFrame: Ref = ref(null) - const previewEnabled = ref(previewEnabledDefault) const url = ref("") - // `previewFrame` is a template ref that gets attached to the `iframe` element when the component mounts - // Ensures the HTML content is rendered immediately after a request, persists between tab switches, and is not limited to preview toggles - onMounted(() => updatePreviewFrame()) - - // Prevent updating the `iframe` element attributes during preview toggle actions after they are set initially - const shouldUpdatePreviewFrame = computed( - () => previewFrame.value?.getAttribute("data-previewing-url") !== url.value - ) - const updatePreviewFrame = () => { if ( previewEnabled.value && @@ -126,9 +116,35 @@ export function usePreview( // Finally, set the iframe source to the resulting HTML. previewFrame.value.srcdoc = previewDocument.documentElement.outerHTML previewFrame.value.setAttribute("data-previewing-url", url.value) + + // Enable sandboxing for the iframe but this can have security implications + // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-sandbox + // https://stackoverflow.com/a/30785417 + // previewFrame.value.setAttribute( + // "sandbox", + // "allow-scripts allow-same-origin" + // ) } } + // `previewFrame` is a template ref that gets attached to the `iframe` element when the component mounts + // Ensures the HTML content is rendered immediately after a request, persists between tab switches, and is not limited to preview toggles + // Also watches for changes in the `previewEnabled` state to update the `iframe` element attributes + watch( + previewEnabled, + () => { + updatePreviewFrame() + }, + { + immediate: true, + } + ) + + // Prevent updating the `iframe` element attributes during preview toggle actions after they are set initially + const shouldUpdatePreviewFrame = computed( + () => previewFrame.value?.getAttribute("data-previewing-url") !== url.value + ) + const togglePreview = () => { previewEnabled.value = !previewEnabled.value updatePreviewFrame()