DEV Community

Cover image for Update: Google reversed it. The Chrome Web Store rejected my free WebAssembly extension for not using permissions it uses.
Todd Tanner
Todd Tanner

Posted on Edited on

Update: Google reversed it. The Chrome Web Store rejected my free WebAssembly extension for not using permissions it uses.

Update, October 14, 2026: Google reversed this. At 8:30 AM Eastern the Chrome Web Store published Anaglyphohol 4.0.0, and at 8:40 Chrome Web Store Developer Support replied to my appeal:

We took a closer look at your item again and found it to be compliant with our policies.

Your item has been reinstated and will be available in the store shortly. We apologize for the inconvenience caused to you in this matter.

The listing now shows version 4.0.0. Thank you to whoever took the second look, and thank you for the apology. I wrote back to say so. Google accepted the same 4.0.0 build it had rejected twice. The night before, Microsoft Edge Add-ons had published the same 4.0.0 package without a rejection. Google's reply did not say what changed, so I can't tell you why it went differently this time. The rest of this article is the record of the week before the reversal, kept as it was written, with the parts that are no longer true corrected.

In April I wrote about building a Chrome extension in Blazor WebAssembly, rewriting it in JavaScript, and going back to Blazor. I went back. Anaglyphohol 4.0.0 is done and it is the best version I have ever shipped. For a week, Google would not let Chrome users have it.

This is what happened, with every email, line of code and commit linked. The full record is on GitHub: anaglyphohol-chrome-web-store.

What Anaglyphohol is

Anaglyphohol is a browser extension that turns the videos and images on web pages into 3D as you browse. Put on red/cyan glasses, open a video, and the picture gains depth. A depth estimation AI runs on your own graphics card through WebGPU. Nothing is uploaded, there is no account, no ads, no tracking. It is free on every site, and the source is public.

Version 4.0.0 is written in C# (.NET 10, Blazor WebAssembly) and runs the depth models and 3D kernels on the GPU with my own open source libraries, SpawnDev.ILGPU and SpawnDev.ILGPU.ML.

Rejection 1: "Keyword spam"

On October 7 the Chrome Web Store rejected 4.0.0 for "excessive and / or irrelevant keywords" in the description. The offending text:

"YouTube, Twitch, Pluto TV, Tubi, Bing, Google, Yahoo and more."

Those are the sites the extension is built for. It turns 3D on by default on exactly those sites. Google called a list of supported sites, one of which is Google, "irrelevant". Version 2.0.0 had a list just like it in its description and passed review.

I removed the list anyway and used the exact description Google had already approved for version 3.0.14. Resubmitted the same day.

Rejection 2: "Requesting but not using" storage and offscreen

On October 9 it was rejected again. This time the claim was that Anaglyphohol requests the storage and offscreen permissions and does not use them.

Both are used.

  • storage saves every setting the user changes: 3D on or off, per-site switches, 3D mode, depth level, focus, toolbar state. Without it, no setting survives a page load.
  • offscreen runs the "Shared 3D converter", an option that runs one depth and 3D converter for every tab instead of one per page.

Notice what this rejection is not. Google did not say Anaglyphohol is malware, a scam, or a privacy risk. It did not say the extension does anything harmful. It said the extension asks for two permissions and does not use them: in other words, that it does too little. Neither permission even shows users a warning at install time (Chrome's permission list gives history, tabs and others a "Warning displayed" line; storage and offscreen have none). And the claim is false: both are used. Google rejected a free extension for not using code that it does use.

Here is why a scan that only reads JavaScript misses them. Anaglyphohol is C# compiled to WebAssembly. Chrome supports WebAssembly in Manifest V3 extensions; wasm-unsafe-eval exists for exactly this. The compiled code reaches the extension APIs through an interop library, SpawnDev.SpawnJS.BrowserExtension, which reads properties off the global chrome object by name:

// Browser.cs
public Storage? Storage => JSRef!.Get<Storage?>("storage");
public Offscreen? Offscreen => JSRef!.Get<Offscreen?>("offscreen");

// Offscreen/Offscreen.cs
public Task CreateDocument(OffscreenCreateParameters parameters) => JSRef!.CallVoidAsync("createDocument", parameters);
public Task<bool> HasDocument() => JSRef!.CallAsync<bool>("hasDocument");
Enter fullscreen mode Exit fullscreen mode

So chrome.storage and chrome.offscreen never appear as literal text in the package's .js files. If your review is a grep of the JavaScript, you will not find them. The strings are still in the package: storage, offscreen, createDocument and hasDocument sit in the compiled .wasm, stored as UTF-16 the way .NET stores strings.

The appeal

Google's appeal reply asked for "evidence(code snippet) of usage of above permission".

So I sent it. Every call site, file names, line numbers, the interop code that calls chrome.storage.sync.get/set and chrome.offscreen.createDocument/hasDocument/closeDocument, GitHub permalinks to the exact commits in the build, and the steps to watch the offscreen document run in chrome://extensions.

The answer, the next morning:

We are not able to see the provided code in the extension package. So, we request you to update by removing "storage" and "offscreen" permissions from the manifest file.

[...] Please reply to this email with evidence that the functionality is not working after removing the permissions.

They asked for code. They got code. They said they cannot see the code. Then they asked me to break my own extension and send proof that it is broken.

Checking whether a permission is used is the review. It takes about a minute: install the extension, change a setting, reload the page, see that it stuck. Handing that job back to the developer, and asking for a deliberately broken build as "evidence", was the wrong way to do it.

That was the second denial, on October 10. The reversal came four days later (see the update at the top).

Google already approved this exact pattern. Twice.

This is the part that makes it indefensible.

Version Built with Permissions Chrome Web Store
1.0.x .NET 8 Blazor WebAssembly storage, used from WebAssembly Approved
2.0.0 .NET 8 Blazor WebAssembly storage, used from WebAssembly Approved
3.0.14 JavaScript and HTML storage, offscreen Approved, live today
4.0.0 .NET 10 Blazor WebAssembly storage, offscreen, used from WebAssembly Rejected twice as "not used", appeals denied twice, then reinstated October 14

Versions 1 and 2 were Blazor WebAssembly apps that called storage from compiled code, invisible to a text search, exactly like version 4. Google approved both. Version 3.0.14, still being served to Chrome users today, requests the exact same two permissions as 4.0.0 and has the exact same description. Google approved the extension it was serving and rejected the update that asked for nothing more, until it reversed itself.

What the record showed

Put it together. Chrome supports WebAssembly in extensions; wasm-unsafe-eval exists for it. For a week, the review treated a permission as unused unless the package's JavaScript named it. When I sent the code, the answer was that they could not see it, and the fix they demanded was removing the permissions, not reading the code. Google's own AI describes the review as a string-matching scanner and advises faking the JavaScript (more on that below).

That is why I titled this article "hostile to WebAssembly". An extension written in C#, Rust, Go, C++ or anything else that compiles to WebAssembly can use exactly the APIs it declares and still be rejected by a scan that only reads JavaScript. My versions 1 and 2 got through. Version 4 did not, and nothing in the rules told me which outcome to expect.

Google has now looked again and approved the same build it rejected, which is the right result. What I can't tell you is whether the scan itself changed. If you ship WebAssembly to the Chrome Web Store and get this rejection, appeal with the code, and keep appealing.

Free work

Extensions are a large part of what makes Chrome worth using, and developers build them for free. Anaglyphohol asks Google for nothing but a listing. The check that settles a claim like this takes about a minute: install the extension, change a setting, reload, see that it stuck. It took two denied appeals and a week to get that second look.

What it costs to give something away on Google's store

To put a free extension on the Chrome Web Store, I paid Google a registration fee, and Google required my full name, home address and phone number. As a "trader", all three were published on the listing page. The dashboard later switched me to "non-trader" without asking me to confirm. Switching back meant uploading my driver's license again, so I left it. My name and home street address are still on the public page.

Then every update means a full release build (over an hour; it is AOT compiled), a submission, a wait, and a form letter. This one update cost days. The time should have gone into the extension and the open source libraries other developers use for free.

And it is not only Chrome. Google now requires Android developers to register and verify their identity with Google before their apps can be installed on certified Android devices, even apps that never touch the Play Store. Enforcement began September 30, 2026 in Brazil, Indonesia, Singapore and Thailand, with a worldwide rollout planned for 2027. Independent developers are being treated as the problem.

Not the first time

This is not my first write-up of Google treating the people who use and build on its platforms as a resource to be managed.

In April I called that the cost of publishing on Google's platform. In October I paid it and still could not publish.

What I did not do

The obvious workaround is to ship a JavaScript file that names chrome.storage and chrome.offscreen literally, just so the scan finds them. It would change nothing about what the extension does. It would only confirm that a keyword search counts as a review. I did not do it.

That workaround came from Google itself. When I asked Gemini, Google's own AI, about the rejection, it described the review as a "naive static string-matching analyzer" and recommended what it called "The Canary String Trick (Easiest Bypass)":

// Add this dummy function somewhere in an unminified JS file included in your extension package
function __chrome_store_compliance_canary() {
    // Explicit string literals for the static analyzer
    if (false) {
        console.log(chrome.storage, chrome.offscreen);
    }
}
Enter fullscreen mode Exit fullscreen mode

Dead code that can never run, there only so "the scanner will see them and clear the build" (Gemini's full reply). Claude, the AI I build with, pointed out that this would likely make things worse: code that exists only to pass a scan looks like an attempt to mislead the review if a reviewer ever notices it. Google's AI advises gaming Google's review. Google's review rejects honest code.

Where to get Anaglyphohol

Anaglyphohol 4.0.0 is on the Chrome Web Store again, and it stays free and open source.

If you build extensions in C#, Rust, Go or anything else that compiles to WebAssembly: Chrome supports it, and in the end its review did too. Send the code, and appeal.

Top comments (0)