<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE bugzilla SYSTEM "https://bugs.webkit.org/page.cgi?id=bugzilla.dtd">

<bugzilla version="5.0.4.1"
          urlbase="https://bugs.webkit.org/"
          
          maintainer="admin@webkit.org"
>

    <bug>
          <bug_id>231706</bug_id>
          
          <creation_ts>2021-10-13 16:29:03 -0700</creation_ts>
          <short_desc>Implement File System standard</short_desc>
          <delta_ts>2025-10-17 08:10:54 -0700</delta_ts>
          <reporter_accessible>1</reporter_accessible>
          <cclist_accessible>1</cclist_accessible>
          <classification_id>1</classification_id>
          <classification>Unclassified</classification>
          <product>WebKit</product>
          <component>New Bugs</component>
          <version>WebKit Nightly Build</version>
          <rep_platform>Unspecified</rep_platform>
          <op_sys>Unspecified</op_sys>
          <bug_status>REOPENED</bug_status>
          <resolution></resolution>
          
          
          <bug_file_loc></bug_file_loc>
          <status_whiteboard></status_whiteboard>
          <keywords>InRadar</keywords>
          <priority>P2</priority>
          <bug_severity>Normal</bug_severity>
          <target_milestone>---</target_milestone>
          <dependson>229593</dependson>
    
    <dependson>230101</dependson>
    
    <dependson>230484</dependson>
    
    <dependson>230805</dependson>
    
    <dependson>230861</dependson>
    
    <dependson>230965</dependson>
    
    <dependson>230989</dependson>
    
    <dependson>231142</dependson>
    
    <dependson>231185</dependson>
    
    <dependson>231250</dependson>
    
    <dependson>231466</dependson>
    
    <dependson>231676</dependson>
    
    <dependson>231677</dependson>
    
    <dependson>232067</dependson>
    
    <dependson>232127</dependson>
    
    <dependson>232146</dependson>
    
    <dependson>232363</dependson>
    
    <dependson>247071</dependson>
    
    <dependson>250194</dependson>
    
    <dependson>254726</dependson>
    
    <dependson>283841</dependson>
    
    <dependson>284735</dependson>
          
          <everconfirmed>1</everconfirmed>
          <reporter name="Sihui Liu">sihui_liu</reporter>
          <assigned_to name="Nobody">webkit-unassigned</assigned_to>
          <cc>03_placid_daft</cc>
    
    <cc>adam.zielinski</cc>
    
    <cc>andre</cc>
    
    <cc>andrewhodel</cc>
    
    <cc>annevk</cc>
    
    <cc>apolostudioapps</cc>
    
    <cc>asully</cc>
    
    <cc>joelgustafson</cc>
    
    <cc>jozefchutka</cc>
    
    <cc>mrskman</cc>
    
    <cc>nimajneb0905</cc>
    
    <cc>philn</cc>
    
    <cc>tomac</cc>
    
    <cc>webkit-bug-importer</cc>
    
    <cc>woodlxf00</cc>
          

      

      

      

          <comment_sort_order>oldest_to_newest</comment_sort_order>  
          <long_desc isprivate="0" >
    <commentid>1804055</commentid>
    <comment_count>0</comment_count>
    <who name="Sihui Liu">sihui_liu</who>
    <bug_when>2021-10-13 16:29:03 -0700</bug_when>
    <thetext>...</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>1806914</commentid>
    <comment_count>1</comment_count>
    <who name="Radar WebKit Bug Importer">webkit-bug-importer</who>
    <bug_when>2021-10-20 16:30:17 -0700</bug_when>
    <thetext>&lt;rdar://problem/84484202&gt;</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>1841939</commentid>
    <comment_count>2</comment_count>
    <who name="Jen Simmons">jensimmons</who>
    <bug_when>2022-02-15 14:09:42 -0800</bug_when>
    <thetext>*** Bug 213775 has been marked as a duplicate of this bug. ***</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>1877587</commentid>
    <comment_count>3</comment_count>
    <who name="Thomas Steiner">tomac</who>
    <bug_when>2022-06-22 23:53:41 -0700</bug_when>
    <thetext>Now that we have the Origin Private File System, any word about the picker methods?
showOpenFilePicker()
showSaveFilePicker()
showDirectoryPicker()</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>1960731</commentid>
    <comment_count>4</comment_count>
    <who name="Anne van Kesteren">annevk</who>
    <bug_when>2023-06-09 05:08:40 -0700</bug_when>
    <thetext>I think this can be considered done. This work essentially morphed into supporting the File System standard. As per https://github.com/WebKit/standards-positions/issues/28 we don&apos;t think the methods mentioned in comment 3 are a good idea.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>1960759</commentid>
    <comment_count>5</comment_count>
    <who name="Sihui Liu">sihui_liu</who>
    <bug_when>2023-06-09 09:19:37 -0700</bug_when>
    <thetext>Current spec link: https://fs.spec.whatwg.org/</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>1960761</commentid>
    <comment_count>6</comment_count>
    <who name="Sihui Liu">sihui_liu</who>
    <bug_when>2023-06-09 09:22:23 -0700</bug_when>
    <thetext>Reopen this as</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>1960762</commentid>
    <comment_count>7</comment_count>
    <who name="Sihui Liu">sihui_liu</who>
    <bug_when>2023-06-09 09:23:29 -0700</bug_when>
    <thetext>Reopened as we have one interface `FileSystemWritableFileStream` unimplemented as in current spec:
https://fs.spec.whatwg.org/#api-filesystemwritablefilestream</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>1963589</commentid>
    <comment_count>8</comment_count>
    <who name="Adam Zielinski">adam.zielinski</who>
    <bug_when>2023-06-26 03:23:01 -0700</bug_when>
    <thetext>I&apos;m a WordPress core committer working at Automattic and WordPress could really use this feature in the WordPress Playground [1] project which runs an entire WordPress in the browser via WebAssembly.

The in-browser storage [2] already works great in Safari, but loading and saving changes to the local directory only works in Google Chrome [3]. Implementing it in WebKit would unlock using Safari as a WordPress development environment or even as a runtime for the Blocknotes app [4] where all notes are stored on the disk and synchronized across all the devices through iCloud. Ditto for other WordPress-based portable WASM apps.

[1] https://developer.wordpress.org/playground
[2] https://github.com/WordPress/wordpress-playground/pull/548
[3] https://github.com/WordPress/wordpress-playground/pull/547
[4] https://wptavern.com/blocknotes-app-runs-wordpress-natively-on-ios-now-in-public-beta</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>1967792</commentid>
    <comment_count>9</comment_count>
    <who name="Alex Titarenko">03_placid_daft</who>
    <bug_when>2023-07-23 16:54:44 -0700</bug_when>
    <thetext>FileSystemWritableFileStream will be helpful for my note-taking app as well. It&apos;s working in Chrome but not Safari, and currently only working solution with using Web Worker is not very practical for me, so I need this to be implemented in Safari.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>2053728</commentid>
    <comment_count>10</comment_count>
    <who name="Andrew Hodel">andrewhodel</who>
    <bug_when>2024-08-19 09:05:55 -0700</bug_when>
    <thetext>This is needed in Safari, requiring HTTP range requests and a request per file does not allow a protocol buffer implementation to handle transmission priority.

This works in every browser except Safari and allows the ability to prioritize messages, that&apos;s a requirement for control messages.

Protocol Buffer JavaScript Example:
```
var inbound_message_callback = function(m) {

	console.log(&apos;inbound_message_callback() called.&apos;, m);

	var c = create_message_element(inbound_element, m, &apos;inbound&apos;);

	var file_handle = null;
	var writable_stream = null;
	var file_mem_buf = [];
	var file_data_index = 0;
	var data_chunks_read_into_memory = 0;
	var downloaded = false;

	c.c.addEventListener(&apos;dblclick&apos;, function(e) {

		if (downloaded === true) {
			alert(&apos;already downloaded&apos;);
			return;
		}

		// create new file handle with name
		var fh_promise = window.showSaveFilePicker();

		fh_promise.then(function(fh_fullfillment) {

			// if the fullfillment happens of window.showSaveFilePicker()
			// do not allow it to happen again, in order to regain the memory used by file_mem_buf
			// as an inbound message can be terabytes in size
			// and the download to file can be started during and after the message arrival
			downloaded = true;

			file_handle = fh_fullfillment

			console.log(&apos;created file handle for message_type 1 message data&apos;, file_handle);

			var wrs_promise = fh_fullfillment.createWritable();

			wrs_promise.then(function(wrs_fullfillment) {

				writable_stream = wrs_fullfillment;
				console.log(&apos;created writable stream for message_type 1 message data&apos;, writable_stream);

				if (data_chunks_read_into_memory === m.total_chunks) {

					// data is already in memory
					// no more activity_callback() invocations

					// add data from memory buffer to file_handle
					for (var l in file_mem_buf) {
						writable_stream.write(file_mem_buf[l]);
					}

					// empty file_mem_buf, this is in the main thread and this activity_callback function must complete before another is run
					file_mem_buf = [];

					// close the writable stream
					writable_stream.close();

				}

			}, function(wrs_rejection) {

				console.log(&apos;wrs_rejection&apos;, wrs_rejection);

			});

		}, function(fh_rejection) {

			console.log(&apos;fh_rejection&apos;, fh_rejection);
		
		});

	});

	m.activity_callback = function(e, data_progress, data) {

		// (event String, data_progress Number, data ArrayBuffer)
		// data is always nil unless it is a &quot;data_progress&quot; or &quot;data_complete&quot; event of a message in the ReceiveQueue

		//console.log(&apos;inbound message activity_callback&apos;, e, data_progress, data, m.transfer_rate_bits_per_microsecond + &apos;mbps&apos;);

		c.update(data_progress);

		if (e === &apos;data_progress&apos;) {

			c.data_chunk(data);

			if (writable_stream === null) {

				// add data to memory buffer
				file_mem_buf.push(data);

				data_chunks_read_into_memory++;

			} else {

				// add data from memory buffer to file_handle
				for (var l in file_mem_buf) {
					writable_stream.write(file_mem_buf[l]);
				}

				// empty file_mem_buf, this is in the main thread and this activity_callback function must complete before another is run
				file_mem_buf = [];

				// add data to file_handle
				writable_stream.write(data);

			}

			// increment the file_data_index
			file_data_index++;

		} else if (e === &apos;data_complete&apos;) {

			if (writable_stream !== null &amp;&amp; file_mem_buf.length === 0) {

				// all data in file_mem_buf has been written to a file

				// close the writable stream
				writable_stream.close();

			}

		}

	}

}
```</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>2071636</commentid>
    <comment_count>11</comment_count>
    <who name="Andrew Hodel">andrewhodel</who>
    <bug_when>2024-10-30 10:19:32 -0700</bug_when>
    <thetext>It isn&apos;t reasonable that `FileSystemFileHandle.createSyncAccessHandle()` is the only choice to write a file in `OPFS` with Safari because of the requirement of using a `Worker` thread.

The problem with a `Worker` thread is the wasted resource if you are already receiving data to write in blocks in the main thread, it&apos;s not worth opening a new `Worker` because the intent is confusion, not work.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>2071655</commentid>
    <comment_count>12</comment_count>
    <who name="Andrew Hodel">andrewhodel</who>
    <bug_when>2024-10-30 11:11:32 -0700</bug_when>
    <thetext>Without access to this in the main thread, any module that writes files uses non required resource to write files by using a `Worker` thread.

If any other module provides data it is either going to provide it to the main thread or be included in a `Worker` itself where this wouldn&apos;t be a problem as the sync handle can write the file from the `Worker`.

The problem with the `Worker` thread is that many times the module cannot be included in the `Worker` with consideration of RAM because if you are requiring a worker pool every worker must have a copy of the module in RAM.

This is very problematic when you get into 3D fonts, because you have 1-4GB of RAM allocated to each worker having the ability to draw fonts and that significantly takes from what is available.

Worker&apos;s aren&apos;t like subroutines (Golang) or threads (C99 pthread) with regards to modules being accessible globally.  That is why the main thread is often used and what I think comments such as #4 are not considering.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>2071656</commentid>
    <comment_count>13</comment_count>
    <who name="Andrew Hodel">andrewhodel</who>
    <bug_when>2024-10-30 11:16:01 -0700</bug_when>
    <thetext>It&apos;s confusing because when you read the documentation of the specification that the browser claims to implement and then test in a Safari and result failure; you must then read to find out that instead of changing the spec, the browser developer&apos;s chose to partially implement it and didn&apos;t make an error message that explains their reasoning when `FileSystemFileHandle.createWritable` is used as specified.</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>2080241</commentid>
    <comment_count>14</comment_count>
    <who name="Andrew Hodel">andrewhodel</who>
    <bug_when>2024-12-09 10:43:17 -0800</bug_when>
    <thetext>@Sihui Liu - thank you

https://github.com/WebKit/WebKit/commit/92cb5665c378bef8d73a61a782ad57291520f77d</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>2093183</commentid>
    <comment_count>15</comment_count>
    <who name="Andrew Hodel">andrewhodel</who>
    <bug_when>2025-02-08 11:10:15 -0800</bug_when>
    <thetext>When is this going to be available in Safari?</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>2122659</commentid>
    <comment_count>16</comment_count>
    <who name="">andre</who>
    <bug_when>2025-06-09 23:00:05 -0700</bug_when>
    <thetext>From the Safari 26 release notes:

&gt; Added support for the File System WritableStream API. (145875384)


https://developer.apple.com/documentation/safari-release-notes/safari-26-release-notes</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>2151803</commentid>
    <comment_count>17</comment_count>
    <who name="Apolo">apolostudioapps</who>
    <bug_when>2025-10-16 14:00:37 -0700</bug_when>
    <thetext>@andre@popovit.ch but this changes nothing still, how are we supposed to get the WritableStream?</thetext>
  </long_desc><long_desc isprivate="0" >
    <commentid>2152061</commentid>
    <comment_count>18</comment_count>
    <who name="">andre</who>
    <bug_when>2025-10-17 08:10:54 -0700</bug_when>
    <thetext>(In reply to Apolo from comment #17)
&gt; @andre@popovit.ch but this changes nothing still, how are we supposed to get
&gt; the WritableStream?

Have you tried this?

    // Get the OPFS root directory
    const root = await navigator.storage.getDirectory();

    // Create or get a file handle
    const fileHandle = await root.getFileHandle(&apos;example.txt&apos;, { create: true });

    // Create a writable stream using createWritable()
    const writable = await fileHandle.createWritable();

    // Write some data
    await writable.write(&apos;Hello OPFS!&apos;);

    // Close the stream to persist changes
    await writable.close();</thetext>
  </long_desc>
      
      

    </bug>

</bugzilla>