callumlocke/json-formatter

When will json global variable be back

kykungz opened this issue · 13 comments

The README.md stated that:

  • Parsed JSON is exported as a global variable, json, so you can inspect it in the console Disabled for now, due to difficulties getting it working with Manifest v3 upgrade.

But is there an estimated date that it will be back?

I've found a workaround for now - paste the following into your console:

json = JSON.parse(document.getElementById("jsonFormatterRaw").querySelector("pre").innerText)

The workaround provided by @ChrisBAshton works very well. I suggest the rep owner add this information on the README.md file as a suggestion while the global variation option is not available again. I'm glad I found this information here! :)

Thanks all, just added that workaround to the readme.

I'm not sure if it's possible to make it work again out of the box. I think the issue is there's now a stricter separation of contexts, so content scripts can't access the same window object as the page itself, i.e. can't create window.json. But maybe there's some trick to do it, so I'll leave this open a while in case anyone else wants to have a go at fixing it.

One potential way of doing this would be to add a setInterval with a debugger; statement.
That way DevTools switches to the context of the extension the moment it is opened:

setInterval(() => {
	debugger;
}, 500);

Downside of this is that the page is no longer interactive. But you could measure how long it takes to 'execute' the debugger statement using perfomance.now() and stop the interval the moment it takes more than a couple of milliseconds.
That way users only have to resume the debugger only once:

const interval = setInterval(() => {
  const then = performance.now()
  debugger
  if (performance.now() - then > 100) {
    clearInterval(interval)
  }
}, 500)

Alternatively it seems like chrome.scripting.executeScript() might make it possible to inject a static script included with the extension. But it requires a background script, so I didn't actually try this. I'm not sure whether injected scripts like these have access to the global scope of the page.

I did try the debugger statement though and that seems to work.

I've found a workaround for now - paste the following into your console:

json = JSON.parse(document.getElementById("jsonFormatterRaw").querySelector("pre").innerText)

Tampermonkey version that will automatically run on any .json URL:

// ==UserScript==
// @name         JSON formatter `json` global
// @namespace    https://github.com/lionel-rowe/
// @version      0.1
// @description  try to take over the world!
// @author       https://github.com/lionel-rowe/
// @match        *://*/*.json
// @match        *://*/*.json?*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=json.org
// @grant        none
// ==/UserScript==

const el = document.querySelector('#jsonFormatterRaw pre');
if (el) window.json ??= JSON.parse(el.textContent);

How did tampermonkey manage to work around the manifest changes? Are they still using manifest v2?

How did tampermonkey manage to work around the manifest changes? Are they still using manifest v2?

Given Tampermonkey #644 [Chrome] Manifest V3: examine the effects is still open, I guess so. It looks like Google has delayed the retirement of v2, though it doesn't seem like there's currently a concrete updated timeline.

While we are stuck with the new Chrome manifest v3 limitation, perhaps we could push a quick and dirty solution to ease things a little, so that users won't need to manually copy-paste the script or create their own bookmarklet?

For example, adding a button that injects the JSON to window.json:
Screenshot 2566-04-25 at 19 38 23

@kykungz I don't think that would work. After all, if you can inject a button, why not just inject a <script> tag?

@kykungz I don't think that would work. After all, if you can inject a button, why not just inject a <script> tag?

Good point, I've never develop an extension before, so I'm not really sure how it works haha.

For what it's worth,

I tried attaching a click listener to a button, and yes, it does not work (window variable seems unaffected).

So I tried again by injecting the script right into the HTML attribute like this:

const buttonInject = document.createElement('button')
buttonInject.innerText = 'Inject window.json'
buttonInject.setAttribute('onclick', `window.json = ${JSON.stringify(parsedJsonValue)}`)
optionBar.appendChild(buttonInject)

and it seems to work.

Does this change anything? Or it will only work locally?

Content scripts can be injected in the main world by setting a manifest key.

 "content_scripts": [
   {
     "matches": ["<all_urls>"],
     "js": ["content.js"],
     "run_at": "document_end",
+    "world": "MAIN"
   }
 ],

This hasn't been supported from the beginning, but I haven't found a clear answer to what Chrome version added support for this. Chrome Developers says the ExecutionWorld object is in Chrome 95+, but MDN says 102+ and 111+ as a manifest key...?

It is possible for webpages to interfere with these scripts, so caution should be taken when running a script in the main world on untrusted websites.

Or, would a button to copy the JSON to the clipboard be a fine replacement? (related: #145)