0

I have a UserScript running with @grant none, installed directly in chrome://extensions without Tampermonkey, and I am trying to define a function into the host page global namespace:

window.setRefinedTwitterLiteFeatures = features => {
  settings = Object.assign(settings, features)
  localStorage.setItem(storageKey, JSON.stringify(settings))
  setFeatures()
}

Unfortunately this doesn't seem to be working. I've also tried to use unsafeWindow without luck.

wOxxOm
  • 65,848
  • 11
  • 132
  • 136
G.G.
  • 634
  • 1
  • 5
  • 10
  • 1
    Seems like you install it as described in the repo - that is directly into chrome://extensions - but Chrome doesn't support nonsandboxed environment unlike Tampermonkey. The only way to mimic it is to create a DOM `script` element, assign the code you want to expose to textContent and append to document.body, for example. Same as [Insert code into the page context using a content script](//stackoverflow.com/a/9517879). – wOxxOm Feb 23 '19 at 09:43
  • I guess that answers my question. Would you like to post it as an answer? – G.G. Feb 23 '19 at 11:12
  • I was confused by the fact that I can access globals and register event listeners but can't modify built-ins like `window.history` or add new globals. FWIW none of the options from your link work I think as twitter.com implements CSP. – G.G. Feb 23 '19 at 12:44

1 Answers1

2

Seems like you install it as described in the repo - that is directly into chrome://extensions - but Chrome doesn't support nonsandboxed environment unlike Tampermonkey. The only way to mimic it is to create a DOM script element, assign the code you want to expose to textContent and append to document.body, for example. Just as shown in the canonic answer “Insert code into the page context using a content script”.

The next problem is twitter's CSP that disallows the code in inline script elements. Luckily, as we can see in devtools network inspector for the main page request, the CSP has a nonce exception, which we can reuse:

function runScriptInPageContext(text) {
  const script = document.createElement('script');
  script.text = text;
  script.nonce = (document.querySelector('script[nonce]') || {}).nonce;
  document.documentElement.appendChild(script);
  script.remove();
}

Usage examples running code immediately:

runScriptInPageContext("alert('foo')");

runScriptInPageContext(`(${() => {
  alert('foo');
})()`);

Usage example exposing a global function:

runScriptInPageContext(function foo() {
  alert('foo');
});
wOxxOm
  • 65,848
  • 11
  • 132
  • 136
  • Awesome thanks! I haven't tried this yet but I think I can even use eval: `eval("window.foo = function () { alert('foo') }")` – G.G. Feb 24 '19 at 14:14