Skip to content

Latest commit

 

History

History
209 lines (146 loc) · 14.9 KB

File metadata and controls

209 lines (146 loc) · 14.9 KB

Selective Clipboard Format Read

Improving Paste Performance through Selective Clipboard Reads

Author: Abhishek Singh

Co-authors: Ashish Kumar, Rakesh Goulikar, Rohan Raja, Shweta Bindal

Participate

Table of Contents

Introduction

This proposal introduces selective clipboard format reading, an enhancement to the Asynchronous Clipboard Read API that enables web applications to read only the clipboard formats they need by deferring actual data retrieval until getType() is called, resulting in performance gains and reduced memory footprint:

// Example Javascript code
const items = await navigator.clipboard.read(); // No data fetched yet

const text = await items[0].getType('text/plain'); // Only 'text/plain' data fetched here

The current implementation of navigator.clipboard.read() copies all available clipboard formats from the operating system's clipboard into the browser's memory, regardless of what the web application needs.

User Problem

Web applications that support rich content editing, such as document editors, email clients, and data grids, routinely deal with multiple types of clipboard payloads, including large HTML fragments, images, and custom MIME types. These apps often implement features like “Paste as plain text” or “Paste with formatting,” where only a subset of the clipboard data is needed.

However, the current navigator.clipboard.read(), that gets triggered by the paste options, indiscriminately fetches all available formats from the clipboard, regardless of what the application needs. This blanket behaviour adds significant overhead, especially when large data formats like HTML or images are present and are not required by the app.

The impact is especially pronounced in large-scale web applications, such as online spreadsheets and document editors, that collectively handle hundreds of millions of paste interactions across their user base, where maintaining responsiveness during each operation is critical. Delays caused by fetching and discarding irrelevant clipboard data degrade user experience and add avoidable memory and CPU costs. (refer Appendix for an example read-time analysis demonstrating performance impact in a representative scenario)

Goals

  • Improve copy-paste responsiveness for large data by avoiding unnecessary reads, especially when only specific formats like plaintext or HTML are needed by web authors.
  • Ensure interoperability across different platforms.

Non-Goals

  • Modifying clipboard writing or other clipboard APIs such as readText().
  • This proposal does not define any rules for how the browser should prioritize or rank different clipboard formats internally.

Proposal: Defer actual data read until ClipboardItem.getType()

This proposal defers clipboard data retrieval from the OS until the web app explicitly calls getType(). In this model, navigator.clipboard.read() returns ClipboardItem objects listing available MIME types, but without the data. The browser fetches the requested data only when getType(mimeType) is called, and caches it to avoid repeated clipboard accesses for the same type.

If the clipboard contents change between the call to read() and a subsequent call to getType(), the promise will be rejected with an appropriate DOMException (e.g., NotSupportedError). This applies even if a previous getType() call succeeded and data was cached—once the clipboard changes, all subsequent getType() calls on the same ClipboardItem will fail.

Example: Reading formats selectively

// Scenario: OS clipboard contains 'text/plain', 'text/html', and 'image/png' data
const items = await navigator.clipboard.read();
const item = items[0];

const availableTypes = item.types; // ['text/plain', 'text/html', 'image/png']

// Only fetch the format needed
const htmlBlob = await item.getType('text/html'); // Only 'text/html' data fetched
const html = await htmlBlob.text();
// 'text/plain' and 'image/png' data never read from OS clipboard

Example: Caching behavior for repeated access

// Scenario: OS clipboard contains 'text/plain', 'text/html', and 'image/png' data
const items = await navigator.clipboard.read();
const item = items[0];

// First call fetches data from OS clipboard
const blob1 = await item.getType('text/plain'); // Data fetched and cached

// Subsequent calls return cached data without re-reading OS clipboard
const blob2 = await item.getType('text/plain'); // Returns cached data

Example: Requesting unavailable type

// Scenario: OS clipboard contains 'text/plain' data only
const items = await navigator.clipboard.read();
const item = items[0];

const availableTypes = item.types; // ['text/plain']

// ✅ Resolves successfully
const plainText = await item.getType('text/plain');

// ❌ Throws error: The type was not found
const html = await item.getType('text/html');

Boundary Scenarios

  • The types property of ClipboardItem reflects all MIME types available on the clipboard at the time of the read() call, but no data is fetched until getType() is invoked.
  • If the clipboard contents change between read() and getType() calls, the getType() promise will be rejected due to stale data.
  • Once data is fetched via getType(), it is cached in the ClipboardItem. Subsequent getType() calls for the same type return the cached data, unless the clipboard has changed.
// Scenario: Clipboard changes between read() and subsequent getType() calls
const items = await navigator.clipboard.read();
const item = items[0];

const availableTypes = item.types; // ['text/plain', 'text/html']

// ✅ First getType() succeeds - clipboard unchanged since read()
const plainTextBlob = await item.getType('text/plain');
const text = await plainTextBlob.text();

// User or another app performs a copy/write operation, changing clipboard contents
await navigator.clipboard.writeText('new content');

// ❌ Throws error: Subsequent getType() on same ClipboardItem is rejected
// Clipboard data has changed since read() was called
const htmlBlob = await item.getType('text/html');

Pros

  • Preserves the existing read() API shape (no API changes required).
  • Automatic optimization for all web applications without requiring code changes or opt-in from developers.
  • Already implemented in Safari, demonstrating real-world viability and cross-browser alignment potential. Firefox has indicated willingness to adopt this approach.
  • Only websites that explicitly need to hold clipboard data in memory pay the memory cost; others benefit automatically from reduced memory usage.
  • Complementary event support: The clipboardchange event enables web applications to be notified when the clipboard has changed, helping them handle scenarios where clipboard data changes after a read() call.

Cons

  • Changes the semantic meaning of ClipboardItem from a data snapshot to a lazy fetcher. It can't be used as a persistent cache for clipboard contents like today, where getType() reliably returns the same data without re-reading the system clipboard.
  • Developers must anticipate potential latency when calling getType() which contrasts with today’s expectation of immediate access.
  • Clipboard state may change between read() and getType() calls, leading to a rejected promise due to stale data.

Based on discussions in the W3C Clipboard APIs Working Group, this approach is the preferred solution. While it does change the semantic behavior of ClipboardItem, the working group consensus is that this trade-off is acceptable given the benefits for end users across the web platform.

Alternatives Considered

API signature change with types parameter

An alternative approach considered was to modify the clipboard.read() API signature to allow web authors to specify the MIME types they intend to read. This would rename the optional argument ClipboardUnsanitizedFormats to ClipboardReadOptions and extend it with a new types property—a list of MIME types to retrieve. The browser would selectively read only the requested formats.

Example:

// Scenario: OS clipboard contains 'text/plain' and 'text/html' data
const items = await navigator.clipboard.read({
  types: ['text/plain']
});

const item = items[0];
const availableTypes = item.types; // ['text/plain']. Only requested types that are available.

const plainTextBlob = await item.getType('text/plain');
const text = await plainTextBlob.text();

Pros:

  • This approach is backward compatible. Existing implementations and web applications that use navigator.clipboard.read() would continue to behave as before when types is undefined, receiving all available clipboard formats.
  • Web developers have explicit control over which formats to read.

Cons:

  • Requires web developers to explicitly opt-in by modifying their code to pass the types parameter. Websites without dedicated teams or proper incentives may not adopt this optimization, limiting the benefits.
  • Adding both types and unsanitized to ClipboardReadOptions may cause confusion about how they interact.

Accessibility, Privacy, and Security Considerations

This proposal has no known impact on accessibility or privacy and does not alter the permission or security model of the Async Clipboard API (navigator.clipboard). A user gesture requirement (transient user activation) and existing async clipboard API security measures (focus document, permission prompts) will remain as they are.

With the deferred data retrieval approach, security checks (transient user activation, document focus, and clipboard-read permission) are performed when read() is called. Once read() resolves successfully, subsequent getType() calls on the returned ClipboardItem objects do not re-validate these conditions. This is acceptable because getType() already checks whether the clipboard contents have changed since read() was called, and rejects the promise if they have. Since clipboard data cannot be accessed if it has been modified, the security guarantee is preserved—any attempt to access stale or externally modified clipboard data will fail, regardless of the current permission or focus state.

Appendix

Read Time Analysis and Takeaways

We ran experiments simulating real-world clipboard usage to evaluate the performance impact of selectively reading specific clipboard formats. The results showed substantial improvements in the read time when applications read only the required formats instead of the entire clipboard. For example, in a scenario where the clipboard payload was 7.7 MB (comprising 0.7 MB of plain text and 7 MB of HTML), selectively reading just the text reduced the read time by 93%—from 179.5 ms down to 10.8 ms.

As we scaled up the data size, we observed that read times increased proportionally with payload size, reinforcing that the benefits of selective reads become more significant with larger clipboard data. Moreover, the type of format had a notable impact on performance. HTML formats consistently exhibited higher read latencies compared to plain text, even when only slightly larger in size, likely due to additional processing like browser-side sanitization for security. Avoiding unnecessary HTML reads can deliver substantial latency improvements, especially in mixed-format clipboards where the application only needs text.

Reproducibility : For developers interested in reproducing these results or running similar benchmarks, we’ve published a minimal experiment demonstrating Selective Clipboard Format Read and associated timing comparisons. To use live demo, open this in a browser that supports the Selective Clipboard Format Read.

References and Acknowledgements

References :

Many thanks for valuable feedback and advice from: