Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inject only once with multiple frames #23

Closed
dariop opened this issue Jan 3, 2020 · 4 comments
Closed

Inject only once with multiple frames #23

dariop opened this issue Jan 3, 2020 · 4 comments

Comments

@dariop
Copy link

dariop commented Jan 3, 2020

Hi,
I'm trying to enhance an old web application that I use at work.
This website has a lot of nested frames and iframes, some scripts that force reloads, so for every useful injection of JS/CSS, Witchcraft does 23 (!) unuseful injections!
Is there a way to do injection only once per page?
I'm trying to do it detecting in which frame the script is injected, but the overhead (and wasted RAM) is still huge.

Thank you!

Dario

@luciopaiva
Copy link
Owner

luciopaiva commented Jan 4, 2020

Hi @dariop,

Witchcraft injects itself into every frame by design. When registering as an extension, Witchcraft asks Chrome for all-frames access. Here's an excerpt from manifest.json:

 "content_scripts": [{
    "all_frames": true,

That's the only way to support scripts that require frame/iframe injection. The downside is, of course, if you don't want that feature, you'll need to deal with it.

Every time I face that same issue, I usually only want to inject Witchcraft into the topmost window. If that's your case, it can easily be accomplished by doing the check below:

function isTopmostWindow() {
    return window.self === window.top;
}

This function will only return true for the topmost window.

Don't worry about the CPU overhead. This check is a really simple operation, just a boolean comparison. Considering the web application loads 23 frames, Witchcraft loading itself into each frame is the least of your concerns 😄. Witchcraft is a lean extension, just check the code. It doesn't depend on any libraries and the code is really tiny, especially the part that is injected into every frame. It is so small that I can even paste the complete code here:

/*
   This is the part that gets injected in each frame of the page. It signals the Witchcraft background script that a new
   frame has been loaded and gets ready (by creating a listener) to receive and load any matching scripts.
 */

chrome.runtime.onMessage.addListener(({scriptType, scriptContents}) => {
    if (scriptType === "js") {
        Function(scriptContents)();
    } else if (scriptType === "css") {
        const style = document.createElement('style');
        style.type = 'text/css';
        style.appendChild(document.createTextNode(scriptContents));
        style.setAttribute("data-witchcraft", "");  // to make it easy finding the element if we want to
        document.head.appendChild(style);
    }
});

chrome.runtime.sendMessage(location.hostname);

So you shouldn't worry about memory usage either.

Well, that's it. I hope my answer made sense to you. Great question, anyway. I will add it to the FAQ.

I'm closing this ticket, but feel free to discuss it further if you want and please let me know if you need more help.

Thanks!

@luciopaiva
Copy link
Owner

Question added to the FAQ.

@dariop
Copy link
Author

dariop commented Jan 9, 2020

Hello @luciopaiva ,
thank you for your kind answer.

I was concerned about injection of my code, not yours :)
Furthermore, I need to inject some data (~1MB), so I need to be accurate

Anyway, for some reasons regarding the web application I want to extend, I can't inject my code in the topmost window, so I solved in another way: I check if we are in the frame I care about, and in this case (and only in this case) I do some stuff.

if(window.name==='my_frame'){
    $(document).ready(function(){
        //load my data once
        if(!!!window.top.myData){
            $.ajax({
                dataType: "json",
                url: 'http://127.0.0.1:5743/myData.json',
                async: false
            })
            .done(function(json) {
              window.top.myData= json;
            })
        }
        doStuff();
        doStuffUsingMyData();
    });
}

I used

$.ajax()

instead of

// @include myData.js 

to load my data, in order to avoid an injection of ~1MB (size of my data) for every frame.

Thanks again!

@luciopaiva
Copy link
Owner

Got it, nice and elegant solution!

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

No branches or pull requests

2 participants