Skip to content

Conversation

@JayPanoz
Copy link
Contributor

@JayPanoz JayPanoz commented Jan 23, 2026

This PR adds an Injection API so that resources can be passed to the Navigator in order to inject them in a given list of resources – regex is also an option.

For instance:

const injectables: IInjectablesConfig = {
          allowedDomains: [
            'https://fonts.googleapis.com',
            'https://fonts.gstatic.com'
          ],
          rules: [
            // In all XHTML files, inject a Google Font
            { 
              resources: [/\.xhtml$/], 
              prepend: [
                {
                  as: 'link',
                  target: 'head',
                  url: 'https://fonts.googleapis.com',
                  rel: 'preconnect'
                },
                {
                  as: 'link',
                  target: 'head',
                  url: 'https://fonts.gstatic.com',
                  rel: 'preconnect',
                  attributes: {
                    crossorigin: true
                  }
                }
              ],
              append: [
                // Google Fonts stylesheet
                {
                  as: 'link',
                  target: 'head',
                  url: 'https://fonts.googleapis.com/css2?family=Momo+Signature&display=swap',
                  rel: 'stylesheet'
                },
                // Custom styles
                {
                  as: 'link',
                  target: 'head',
                  rel: 'stylesheet',
                  type: 'text/css',
                  blob: new Blob([`
                    p {
                      font-family: "Momo Signature", system-ui !important;
                      font-weight: 400 !important;
                      font-style: normal !important;
                    }
                  `], { type: 'text/css' })
                }
              ]
            }
          ]
        }

At the moment, the API accepts either a Blob or a url. For CSP, a list of allowedDomains has to be passed to check whether url is valid and create a link/script to it. It does not accept a raw string.

An Injector class is taking care of applying rules, etc. Blobs URL are also shared and released when possible, so that they are not duplicated for each document.

More testing to do. Although no memory leaks have surfaced during the initial extensive testing, there is additional testing to do with more complex use cases such as significant scripts. I must also migrate the internal scripts and stylesheets we inject by default to the API.

Probably some improvements to make as well, as you need to be very explicit through attributes, etc.

So it’s time to open a draft PR so that issues can be caught early, and inputs/feedbacks collected for API improvements.

// Validate allowed domains - they should be proper URLs for external resources
this.allowedDomains = (config.allowedDomains || []).map(domain => {
try {
new URL(domain);
Copy link
Member

Choose a reason for hiding this comment

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

Note the existence of this API: https://caniuse.com/wf-url-canparse

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do we want to drop support for Safari < 17? I’ll gladly switch to URL.canParse if we do, but I prefer to double-check yet as it was my initial reason not to use it. 😅

@tefkah
Copy link
Contributor

tefkah commented Jan 27, 2026

So it’s time to open a draft PR so that issues can be caught early, and inputs/feedbacks collected for API improvements.

only replying because of this (and bc i'm nosy), please ignore me if you don't want my feedback!

very excited for this! seems like a great improvement over manually injecting custom styles and fonts!

based off of nothing other than seeing the option to inject scripts, and since it's been a longer-standing goal to mark the _cFrames on the Navigator as private at some point, would this be considered the alternative to manually interacting with the frame? because it seems like it would be able to provide the same level of interactivity, but it being harder to communicate back to the parent window (which may be the intention), and with a worse DX (injecting plaintext).

I guess I don't have a good alternative if limiting the amount of manual interaction possible with the Navigator is the main goal of deprecating _cFrames! but if the/a main goal of deprecating _cFrames is improving DX, i think it would be nice to be able to inject functions (which take window as an argument for instance) in addition to JS Blobs!

Probably an overly premature commet, knowing very little about the surrounding context!

@JayPanoz
Copy link
Contributor Author

@tefkah

only replying because of this (and bc i'm nosy), please ignore me if you don't want my feedback!

Oh trust me this is greatly appreciated as we need this type of feedback and input to get things right, especially as this API is meant to be shared across toolkits 🙏 It will help tremendously for discussing this with other maintainers.

would this be considered the alternative to manually interacting with the frame?

Perhaps not the solution to everything. As you are aware we want to implement the Decorator API properly for instance, but it should be an alternative to cover features we do not have helpers/other APIs for. So it should be flexible enough to allow users to implement what we won’t or haven’t yet.

improving DX, i think it would be nice to be able to inject functions (which take window as an argument for instance) in addition to JS Blobs!
Probably an overly premature comment, knowing very little about the surrounding context!

Interestingly, I have been anticipating more advanced use cases myself. Admittedly, our first iteration is kept simple because we need to start with something and we need support for fonts, and some other simple scripts, but I was trying to imagine cases where the current interface would be problematic.

Injecting functions is an interesting idea because blob is very TS-Toolkit specific – we’ve been historically using this for scripts and styles we inject into FrameBlobBuilder so it was a natural option for this first iteration, and allowed us to migrate built-ins to this new API relatively quickly and easily. And I’m not sure what mobile toolkits will use in replacement yet. Conceptually, it is a file-like object of immutable, raw data, so file would be logical but we haven’t discussed the API in details yet, we will tomorrow.

DX in general, and functions in particular, are something we want to investigate for sure. I can think about it but if you have examples off the top of your head and expectations of how it could preferably work and some time to document them, this will be warmly welcome.

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.

4 participants