-
Notifications
You must be signed in to change notification settings - Fork 79
Description
Summary
This proposal suggests a standard mechanism for browsers to enable Structured Clone serialization for extension messaging (e.g., runtime.sendMessage, Port.postMessage, etc.) if they do not support it already. We are seeking feedback on which approach (or another) better serves the ecosystem.
Problem
Structured cloning offers significant advantages over JSON serialization (supporting more types like Map, Set, ArrayBuffer, and circular references), but it is not a drop-in replacement. It introduces potential breaking changes, such as stricter data handling (like failing on objects with keys that are functions, and etc.) where JSON might have silently ignored them, and behavioral differences (such as toJSON() methods on classes where structured cloning ignores them). Due to these potential breaking changes, a developer-controlled opt-in mechanism seems necessary to minimize disruption from adoption.
Proposal: Manifest Key
Chromium’s preference is to add a new manifest key that sets the default serialization behavior for the entire extension.
Manifest Example:
{
"name": "My Extension",
"version": "1.0",
"manifest_version": 3,
"message_serialization": "structured_cloning"
} When this key is set to "structured_cloning", the browser will use the structured clone algorithm ("json" for JSON.stringify) for all message passing APIs within the extension context. If omitted the browser defaults to its current implementation (for Chromium that’s JSON.stringify) to preserve compatibility.
Advantages
- Wholesale Adoption: Simplifies global migration by eliminating the need to add a new "options" argument to every individual messaging call site.
- Simpler removal: In the future, if all browsers defaulted to structured cloning, removing a single manifest key (or ignoring it) is much simpler than requiring developers to change individual messaging callsites.
Disadvantages
- Library Compatibility: Third-party libraries that use extension messaging in the extension will also be forced to use structured cloning. If a library relies on specific JSON behavior (
toJSON(), etc.), it may break without updates. This means extensions that rely on these libraries need to use whichever format the libraries do (they cannot update if they rely on a library that needs JSON serialization).
Other options:
Runtime Indication (Per-call Opt-in)
Alternatively, the serialization format could be specified at the individual call site via the existing options/info objects. For example, let’s look at the runtime API:
runtime.sendMessage
The options parameter would accept a serialization property.
chrome.runtime.sendMessage(
extensionId,
message,
{ serialization: 'structured_clone' }
); runtime.connect
The connectInfo parameter would accept a serialization property, determining the format for that specific port's lifetime.
const port = chrome.runtime.connect(extensionId, {
name: 'my-port',
serialization: 'structured_clone'
});
port.postMessage(new Map()); // Uses structured clone Advantages
- Granular Control: Developers can adopt structured cloning for specific features or messages and migrate libraries as they are able.
Disadvantages
- Verbosity: Requires developers to explicitly opt-in at every call site, increasing boilerplate and developer operational load.
- Standardization: If the WECG’s goal is for extension messaging to ultimately exclusively use structured cloning for serialization, then this could encourage a divided situation where developers use both serialization methods indefinitely.
JSON Fallback
Given that the above choices are binary, there perhaps can be a middle-ground where we don't need the opt-in and structured cloning can be enabled with backwards compatibility built-in? We could enable structured and use it in most cases, but could fallback to JSON in some edge cases? One example of where we might want to fall back to JSON serialization is classes with toJSON() methods:
class User {
constructor(name, password) {
this.name = name;
this.password = password;
}
// Hide password when converting to JSON
toJSON() {
return { name: this.name };
}
}
const user = new User("Alice", "secret123");
console.log(JSON.stringify(user));
// Output: {"name":"Alice"} <-- Password hidden
const clone = structuredClone(user);
console.log(clone);
// Output: { name: "Alice", password: "secret123" } <-- Password unintentionally exposed Advantages
- Ease: Developers can gain the benefits of structured clone serialization without having to take explicit action.
Disadvantages
- Maintenance Scalability: A fallback creates a complex, hybrid serialization logic. As the platform evolves, maintaining a growing list of exceptions for specific edge cases (like
toJSON) may become technical-debt-heavy and difficult to support reliably across all browser engines. - Inconsistent between browsers: Browsers would (continue to) behave differently when serializing messages, unless other browsers added support for a JSON fallback (which is then a behavior change for them).
- Hard to sunset: There is no clear migration path to remove support for JSON serialization in this case.