0

I am attempting to inject vue into a page from a content script before other scripts run. I have the framework saved as a file in my extension directory and I can inject it with this code:

let s = document.createElement('link');
s.as = 'script';
s.rel = 'preload';
s.href = chrome.runtime.getURL('vue.min.js');
document.documentElement.insertBefore(s, document.documentElement.firstChild);

The content script is run at document_start, so I would expect it to inject this script into the DOM before it is even built and so be the first script to load. This is not true: Network panel in dev tools shows it loading after another script

It's loaded later. And even though it has the higher priority it still runs after the script even if I set the script to defer and vue is finished loading way before. Why does this happen and is there any way to ensure my script loads/executes first?

1 Answers1

0

Loading a separate script file via a URL puts it into the shared queue so it competes with the page scripts, see https://crbug.com/634381.

The only solution is to use a script element with textContent as a literal string (example: see method 2). And of course the content script should be declared with "run_at": "document_start" in manifest.json.

If your code is big, in order to keep it maintainable you would want to use a separate file naturally so you can configure your webpack (or whatever else) and use a special placeholder in the content script which will be then replaced with a literal string containing the file text.

wOxxOm
  • 65,848
  • 11
  • 132
  • 136
  • Crap, that means that to accomplish what I want to do I will have to wait for chrome to fix their bug. – TheOneAndOnly Official Aug 29 '20 at 07:44
  • @wOxxOm, now that manifest V3 broke inline scripts (crbug.com/1137396), what do you recommend to run a script in main world before any other scripts do? (btw thanks for all your work) – dukitieneflow Feb 23 '22 at 19:26
  • Currently MV3 doesn't have a solution. In the future chrome.scripting.registerContentScripts will allow setting `world: 'MAIN'`. – wOxxOm Feb 23 '22 at 19:28
  • 1
    Looks like the inline script CSP can be easily bypassed, am I missing something? (And the code below runs before any other scripts, because there's nothing async on it) `const btn = document.createElement("button"); btn.setAttribute("onclick", "alert(chrome.runtime?.id === undefined ? 'hello from page world!' : '')"); document.documentElement.appendChild(btn); btn.click();` – dukitieneflow Feb 24 '22 at 17:36
  • You're right. This is yet another thing in MV3 that doesn't work as they intended in the real world. – wOxxOm Feb 24 '22 at 17:54
  • @wOxxOm I see that on crbug.com/1207006 you classified this as a bug, but I don't think it should be. There's infinite ways to run inline JavaScript (when allowed by main world CSP) by manipulating the DOM. Covering every situation is complex, see DOMPurify, it's not feasible. I think we should get Chrome developers to give up on this, just like on crbug.com/1016997 – dukitieneflow Feb 24 '22 at 18:37
  • Someone should explain it to them like I did in 1016997 but I'm not an expert on inline code. – wOxxOm Feb 24 '22 at 18:53