Joone Blog

[BlinkOn21] Fixing Drag-and-Drop in Chromium: What Was Broken and What Changed

Posted on Apr 20, 2026

I gave a lightning talk at BlinkOn 21 on fixing drag-and-drop in Chromium. Drag-and-drop is one of the oldest and most intuitive interactions in graphical user interfaces, yet several long-standing gaps in Chromium made it unreliable when data crossed the boundary between the browser and other applications. This post summarizes the three problems I worked on and how each was fixed.

Drag-and-Drop Issues in Chromium

The talk covered three distinct issues with the DataTransfer API:

  • text/uri-list drag type — multiple URLs were concatenated into a single, invalid URL.
  • DownloadURL drag type — only a single URL was supported.
  • JavaScript File object — dragging a constructed File out of the browser was not implemented, even though Firefox supports it.

1. text/uri-list: Multiple URLs Were Concatenated

When a web page calls setData('text/uri-list', uriList) with multiple URLs, Chromium's internal data handling stripped the CRLF delimiters. As a result, when the data was read back via getData('text/uri-list') during a drop event, the result was a single, concatenated, and invalid URL string.

For example, this correct input:

https://mozilla.org\r\nhttps://webkit.org\r\nhttps://chromium.org

was turned into this malformed URL:

https://mozilla.orghttps://webkit.orghttps://chromium.org

In contrast, Firefox returns the correct, original string. This bug broke both internal drops (for example, to another Chromium window) and external drops (for example, onto the desktop or another application), because the receiving target could not parse the malformed URL. This issue has been fixed since M145.

Implementation Details

2. DownloadURL: From a Single URL to a List

The DownloadURL drag type was introduced in Chromium in 2009 to support downloading a URL to the local desktop via drag-and-drop. The idea goes back to a WHATWG proposal, Proposal to drag virtual file out of browser: if a draggable element contains a URL, dragging it out of the browser normally copies only the URL value, but in many scenarios we actually want to download the file the URL points to. DownloadURL makes that possible, and it has long been used by apps such as Outlook and Gmail to download mail attachments to the user's desktop.

The limitation was that DownloadURL only supports a single URL:

e.dataTransfer.setData(
  "DownloadURL",
  "image/png:myImage.png:https://example.com/path/to/image.png"
);

To support multiple downloads in one drag, I introduced a new drag type, DownloadURL-list, whose payload is a JSON array that can hold information for multiple files:

const data = [
  {
    type: 'image/png',
    name: 'file2.png',
    url: 'http://example.com/file2.png'
  },
  {
    type: 'image/jpeg',
    name: 'file1.jpg',
    url: 'http://example.com/file1.jpg'
  }
];

event.dataTransfer.setData('DownloadURL-list', JSON.stringify(data));

Implementation Details

3. JavaScript File Object: Delivering the Bytes

Theoretically, a JavaScript File object can be attached to the DataTransfer API at dragstart and read back at drop:

source.addEventListener('dragstart', (e) => {
  // Create a File object with text content.
  const file = new File(['Hello from a JS File!'], 'hello.txt', {
    type: 'text/plain'
  });

  // Add the File to the drag data.
  e.dataTransfer.items.add(file);
  e.dataTransfer.effectAllowed = 'copy';
});

dropZone.addEventListener('drop', async (e) => {
  e.preventDefault();
  const files = e.dataTransfer.files;

  for (const file of files) {
    const text = await file.text();
    result.textContent += `${file.name} ${file.type} ${file.size} ${text}`;
  }
});

In practice, Chromium only dropped the file name as a long-missing fallback in third_party/blink/renderer/core/clipboard/data_object.cc; the actual bytes were discarded. Firefox, by contrast, already supports dragging and dropping JavaScript-created File objects out of the browser, with no file-type limitations.

I implemented the missing path for Windows, Linux, and macOS so that the bytes of a constructed File are delivered to the OS drop target. The initial change lists support image types only; support for other file types will be added after they land. Once merged, this lets users drag a File out of Chromium and drop it even onto Firefox.

Dragging a JavaScript File from Chromium and dropping it onto Firefox:

Dropping a constructed File onto an iframe in the same tab:

For more details on the design, see my earlier post, Design Doc: Support Dragging JS File Objects to Native Drop Targets.

Implementation Details

Demos

The demo videos above are also available in the X thread for this talk.

References