Skip to content

Handle cases when location.protocol is about: or data: #13226

Open
@CNSeniorious000

Description

@CNSeniorious000

Describe the problem

I am using svelte in several embedded webviews. I used to have to run a static file server before bundleStrategy: 'inline' became available yesterday.

Normally (if we click this html file) the url will start with file://, and everything works fine.

But In the webview implementation I use the url is a data url, and hydration always fails because of:

Uncaught TypeError: Failed to construct 'URL': Invalid URL
    at data:text/html;chars…8L2h0bWw+Cg==:16:13

In another case, I want to parse metadata from an url using an iframe, but sveltekit apps always fail to hydrate because of a similar error:

Uncaught TypeError: Failed to construct 'URL': Invalid URL
    at about:blank:16:13

Describe the proposed solution

Sveltekit always generates

  <body data-sveltekit-preload-data="hover">
    <div style="display: contents">
      <script>
        {
          __sveltekit_1bknoa9 = {
            base: new URL('.', location).pathname.slice(0, -1)
        };

where new URL('.', location) will fail when location.protocol is data:// or about://

Alternatives considered

No response

Importance

would make my life easier

Additional Information

No response

Activity

changed the title [-]Handle cases when location.protocal is `about:` or `data:`[/-] [+]Handle cases when location.protocol is `about:` or `data:`[/+] on Dec 23, 2024
CNSeniorious000

CNSeniorious000 commented on Dec 23, 2024

@CNSeniorious000
Author

// resolve e.g. '../..' against current location, then remove trailing slash
base_expression = `new URL(${s(base)}, location).pathname.slice(0, -1)`;
if (!paths.assets || (paths.assets[0] === '/' && paths.assets !== SVELTE_KIT_ASSETS)) {
assets = base;
}
} else if (options.hash_routing) {
// we have to assume that we're in the right place
base_expression = "new URL('.', location).pathname.slice(0, -1)";
}

An ugly fix would be

- `new URL(${s(base)}, location).pathname.slice(0, -1)`
+ `["about:", "data:"].includes(location.protocol) ? `${s(base)} : new URL(${s(base)}, location).pathname.slice(0, -1)`

- new URL('.', location).pathname.slice(0, -1)
+ ["about:", "data:"].includes(location.protocol) ? "" : new URL('.', location).pathname.slice(0, -1)
eltigerchino

eltigerchino commented on Dec 23, 2024

@eltigerchino
Member

You are welcome to open a pull request. Are there any other protocols we need to consider? Maybe we can create a set and search that.

CNSeniorious000

CNSeniorious000 commented on Dec 23, 2024

@CNSeniorious000
Author

I think maybe ftp:? But it doesn't disrupt the current implementation. The only issues seem to arise with data: and about:.

eltigerchino

eltigerchino commented on Jan 10, 2025

@eltigerchino
Member

Did some investigation and there seems to be two ways of embedding SvelteKit through HTML text:

1. iframe srcdoc="..."

The document will have an embedded URL of about:srcdoc which is an invalid URL base.

2. iframe src="data:text/html;charset=utf-8,..."

The document will have an embedded URL of data:text/html... which is an invalid URL base.

Overall, there's a number of changes we need to make to avoid the "invalid base URL" error when constructing a new URL object to improve embedding SvelteKit apps as HTML text.

base_expression = `new URL(${s(base)}, location).pathname.slice(0, -1)`;

base_expression = "new URL('.', location).pathname.slice(0, -1)";

let baseURI = document.baseURI;
if (!baseURI) {
const baseTags = document.getElementsByTagName('base');
baseURI = baseTags.length ? baseTags[0].href : document.URL;
}
return new URL(url, baseURI);

url = new URL(a instanceof SVGAElement ? a.href.baseVal : a.href, document.baseURI);

const { href } = new URL(resource, location.href);

await native_navigation(new URL(error.location, location.href));

cc: @kran6a

Also, when using a hash router, it's possible for the page to infinitely refresh due to the embedded URL pathname not passing any of the conditions below (#13287):

if (hash_routing) {
if (url.pathname === base + '/') {
return false;
}
// be lenient if serving from filesystem
if (url.protocol === 'file:' && url.pathname.replace(/\/[^/]+\.html?$/, '') === base) {
return false;
}
return true;
}

Fortunately, we can check if location.url.origin === 'null' to detect non-http/https URLs or if the kit.config.embedded option is set and perhaps handle these situations more gracefully.

kran6a

kran6a commented on Jan 20, 2025

@kran6a

I applied this patch on my node_modules and it works for my use-case. There are no invalid base URL errors and no infinite reloads.
Absolute links work, relative links won't ever work because I am using an iframe with srcdoc which makes relative URL take the parent base.

One strange thing I noticed that also broke when migrating widgets from raw svelte (bundling to a single .js file then inlining it into an HTML shell) to sveltekit is that previously I injected some classes into the iframe from the host website via

iframe_element.contentWindow.props = {some_class: Class}; //Class is a class definition, not an instance

These classes use runes and this approach worked on svelte but reactivity broke after transitioning to sveltekit.
Importing the classes from the widget code fixes reactivity but the point of injecting those classes is that widgets don't need to bundle them to reduce their size, dependencies and maintenance as they all will use the most recent version of those classes.
I am not attempting to achieve cross-document reactivity, classes are instanced and meant to be reactive only within the iframe.
I am a bit puzzled on why this does not work. The class state is updated correctly and if you trigger a reload somehow (e.g: navigating to another route and then back) you can see the updated state correctly but reactivity is still nonexistant.

diff --git a/node_modules/@sveltejs/kit/src/runtime/client/utils.js b/node_modules/@sveltejs/kit/src/runtime/client/utils.js
index 28a06d8..24b6ee7 100644
--- a/node_modules/@sveltejs/kit/src/runtime/client/utils.js
+++ b/node_modules/@sveltejs/kit/src/runtime/client/utils.js
@@ -311,6 +311,9 @@ export function is_external_url(url, base, hash_routing) {
 	}
 
 	if (hash_routing) {
+		if (url.protocol === "about:" || url.protocol === "data:"){
+			return false;
+		}
 		if (url.pathname === base + '/' || url.pathname === base + '/index.html') {
 			return false;
 		}
diff --git a/node_modules/@sveltejs/kit/src/runtime/server/page/render.js b/node_modules/@sveltejs/kit/src/runtime/server/page/render.js
index d8fbe32..82867e0 100644
--- a/node_modules/@sveltejs/kit/src/runtime/server/page/render.js
+++ b/node_modules/@sveltejs/kit/src/runtime/server/page/render.js
@@ -109,7 +109,7 @@ export async function render_response({
 			}
 		} else if (options.hash_routing) {
 			// we have to assume that we're in the right place
-			base_expression = "new URL('.', location).pathname.slice(0, -1)";
+			base_expression = "location.protocol === 'about:' || location.protocol === 'data:' ? '' : new URL('.', location.protocol).pathname.slice(0, -1)";
 		}
 	}
 
linked a pull request that will close this issue on Feb 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Participants

      @kran6a@eltigerchino@CNSeniorious000

      Issue actions

        Handle cases when location.protocol is `about:` or `data:` · Issue #13226 · sveltejs/kit