w3c / editing

Specs and explainers maintained by the editing task force
http://w3c.github.io/editing/
Other
192 stars 40 forks source link

[Delayed Clipboard Rendering] What happens to the clipboard data on Tab/Browser Close? #424

Closed snianu closed 1 year ago

snianu commented 1 year ago

From #417 @inexorabletash posted:

A comment I made that didn't make it into the notes: UAs could consider integrating with the Reporting API for the scenario where a site places deferred content on the clipboard, the user navigates away from the site, and the user attempts to paste. This could act as a signal to sites that they should consider doing something in "beforeunload" handling, e.g. prompting the user that clipboard data will be lost.

I think I've seen native Excel do something like this at least on macOS; I think it has heuristics and makes the clipboard content deferred if over a certain size, and warns on app shutdown?

ETA: To clarify: someone else in the call suggested the beforeunload approach, which would be good to document as part of the proposal. I was suggesting that if a site doesn't do that, the Reporting API might be a way for the UA to provide a signal to web developers that it's something they should consider implementing.

@annevk @sanketj @evanstade @a-sully @whsieh

snianu commented 1 year ago

Few options that we want to highlight to handle the tab/browser close scenario when there is a delay rendered format written by the web author:

  1. Add a beforeunload event listener: A web author could choose to preventDefault beforeunload event and notify the user that there is some data pending to be written into the clipboard. In this event handler, web authors could use a new clipboard API (like navigator.clipboard.undelayFormats([""]) or something similar) to write the data for the formats that were delay rendered.

e.g.

function generateExpensiveCustomFormatBlob() {
// Produce the expensive data here...
....
return new Blob....
}
const clipboard_item = new ClipboardItem({
                'text/html': Promise.resolve(generateExpensiveCustomFormatBlob)
                });
navigator.clipboard.write([clipboard_item]);

const beforeUnloadListener = (event) => {
      event.preventDefault();
       navigator.clipboard.undelayFormats(['text/html']`);
      return (event.returnValue = "");
 };

Problems with this approach Chrome doesn't allow to customize the message that is shown to the user, so this could be confusing to the user if the default dialog shows up due to formats being delay rendered. Moreover, it's also detrimental to the performance of the site if there is a beforeunload event listener attached. Some sites provide options to paste as plain text or html during the paste operation, so if the web author chooses to delay render both formats, but only provide the data for one format in the new web API during beforeunload event, then it would be a bad experience for the user if they want to choose the delay rendered format for paste.

  1. Browser could choose to trigger all the callbacks before it fires beforeunload event so it could populate the data for the delay rendered formats. That way the web authors don't have to register for beforeunload event. In this case, a timeout can also be added to prevent sites from abusing the delay rendering of formats.

Problems with this approach Could slow down the browser/tab close operation. The timeout is a way to mitigate this performance issue, but it could lead to other problems like the web author may not have enough time to generate the payload for the format, so the clipboard would have empty data for that format.

  1. Choose to only keep the formats that are cheaper to produce and remove the rest (the formats that are delay rendered) by performing another clipboard.write operation: Native Excel has some logic to clear the clipboard and write a smaller subset of the initial formats that were on the clipboard when a user decides to close the app. Web authors can do this as well, but it would affect the experience of the user if they are unable to paste the format that has high fidelity content and is delay rendered by the web author. Also, this has to be done during beforeunload event which affects performance of the site and slow down browser/tab close operation.

  2. Throw away all the delay rendered formats and put empty data for those formats in the clipboard: It would be a bad user experience, but a web author could also choose to not populate the formats that were registered for delay rendering.

  3. Browsers could choose to show a confirmation dialog if there is any delay rendered formats in the clipboard: Although it is similar to option 1, here the text of the dialog explicitly mentions delay rendering of formats which is more informative to the the user. This option has similar concerns as option 1, but it does affect navigations, so it slows down browser/tab shutdown behavior.

inexorabletash commented 1 year ago

Additional considerations:

Any complication around beforeunload goes deeper - per the page lifecycle https://developer.chrome.com/blog/page-lifecycle-api/#overview-of-page-lifecycle-states-and-events (and apologies if that's too Chromium-specific - better link welcome!) - a page can transition from active to passive to hidden (e.g. tab changed) to frozen to discarded without events where the developer can do anything.

And I think we want to avoid adding new functionality that opts a page out of BFCache.

snianu commented 1 year ago

Created a document to discuss these options in more detail. Please let us know if you have any comments/concerns.

fergald commented 1 year ago

Moreover, it's also detrimental to the performance of the site if there is a beforeunload event listener attached as it would disable bfcache.

Chrome and Safari do not block BFCache on beforeunload, only Mozilla does. That is not specced behaviour and as far as I am aware it is an implementation detail that Mozilla could address if they chose to, there's nothing that makes beforeunload incompatible with BFCache and use of beforeunload is not particularly discouraged in dev documentation. That said, it seems like the handler would have to generate the clipboard data synchronously before the prompt was display.

Perhaps the pagehide event would be a better choice. I doesn't block anything, there is no user interruption, you would just generate the data in the background (although on Chrome and maybe other engines, if you were navigating from a.com/foo to a.com/bar the navigation would be blocked until the pagehide handler completed as both pages would share a renderer).

evanstade commented 1 year ago

I did not realize that bfcache and beforeunload are compatible with each other. MDN docs state:

The beforeunload event is not compatible with the back/forward cache (bfcache), because many pages using this event assume that the page will not continue to exist after the event is fired. To combat this, browsers will not place pages in the bfcache if they have beforeunload listeners, and this is bad for performance.

The wording doesn't make it seem like this is specific to ff (so perhaps it warrants updating).

fergald commented 1 year ago

@evanstade thanks, that is definitely incorrect. I'll see about getting it fixed.

edit: https://github.com/mdn/content/issues/26739

snianu commented 1 year ago

Removing Agenda+ as we're still working through some security issues in the delayed clipboard rendering prototype. Once we have addressed all the concerns, we will reengage with the Editing WG for feedback.

snianu commented 1 year ago

Created a document to discuss these options in more detail. Please let us know if you have any comments/concerns.

We want to discuss the options listed in the above document, but want to propose the following option which is sort of a hybrid of options 2 and 4:

  1. At least one built-in format must be written to the clipboard without delay as part of the write operation so there will be some data on the clipboard (could be a low fidelity text/plain format) if the browser/tab closes and the site doesn't get a chance to generate the data for the delay rendered formats within the timeout window. If the site only writes one format to the clipboard, then it can't be delay rendered.
  2. If a delay rendered callback is already running before the page unloads, cancel the callback after a timeout period if the callback hasn't completed yet. When the callback is cancelled, set empty data to the clipboard for that delay rendered format.
snianu commented 1 year ago

Created a document to discuss these options in more detail. Please let us know if you have any comments/concerns.

We want to discuss the options listed in the above document, but want to propose the following option which is sort of a hybrid of options 2 and 4:

  1. At least one built-in format must be written to the clipboard without delay as part of the write operation so there will be some data on the clipboard (could be a low fidelity text/plain format) if the browser/tab closes and the site doesn't get a chance to generate the data for the delay rendered formats within the timeout window. If the site only writes one format to the clipboard, then it can't be delay rendered.
  2. If a delay rendered callback is already running before the page unloads, cancel the callback after a timeout period if the callback hasn't completed yet. When the callback is cancelled, set empty data to the clipboard for that delay rendered format.

@evanstade @inexorabletash If this addresses all your concerns, then we can mark it as resolved for now unless we discover something in the future, then we can reopen it. WDYT?

snianu commented 1 year ago

(TPAC meeting minutes) Anupam: What happens to data if browser tab closes? Do we render before closing the tab? We discussed different options. We came up with hybrid of solution 2 and 4. Solution: one built-in format that has to be written to clipboard without delay. If a callback is already running, then we cancel it after timeout. Potentially put empty item into clipboard. Johannes: Will not this create same issues as other solutions: User will get different results at different times without knowing why? Anupam: This is only if the site has no beforeunload event handler. This is just fallback for that case. Website can show dialog: “You are losing clipboard formats if you close now…” Olli: not blocking bf cache in any of the browsers Anupam: Beforeunload event loader can choose one of 3: Show dialog with generic text about losing clipboard formats (text provided by browser). Render clipboard formats Not do anything RESOLUTION: Microsoft will prototype this solution. It seems to answer all concerns. Will revisit when prototype is ready. Proposed solution:

We want to discuss the options listed in the above document, but want to propose the following option which is sort of a hybrid of options 2 and 4:

  1. At least one built-in format must be written to the clipboard without delay as part of the write operation so there will be some data on the clipboard (could be a low fidelity text/plain format) if the browser/tab closes and the site doesn't get a chance to generate the data for the delay rendered formats within the timeout window. If the site only writes one format to the clipboard, then it can't be delay rendered.
  2. If a delay rendered callback is already running before the page unloads, cancel the callback after a timeout period if the callback hasn't completed yet. When the callback is cancelled, set empty data to the clipboard for that delay rendered format.

@evanstade @inexorabletash agreed that this solution is better than what was proposed in the document, so we are going to resolve this issue with this resolution and will revisit if we find any issues in the future. @sanketj FYI..