Skip to content

Opt-in Mechanism for Structured Clone Serialization and JSON Fallback Considerations #931

@justinlulejian

Description

@justinlulejian

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions