Implement onDeterminingFilename#316
Closed
ssm9 wants to merge 19 commits into
Closed
Conversation
Add the onDeterminingFilename event definition to the downloads API
schema. Listeners receive a DownloadItem and may return an optional
{ filename, conflictAction } object to influence the final filename
before the download begins.
Add _determineFilenameCallbacks set and determineFilename() method to DownloadIntegration. This provides a registration point for the WebExtensions downloads API to intercept filename determination for all downloads before execution begins.
…tart() Make Download.start() async and call DownloadIntegration.determineFilename() before execution begins. This is the single interception point that covers all downloads — both extension-initiated and browser-initiated (legacy) — since start() is always called before the target path is locked in by the saver.
Replace the ignoreEvent() stub with a real EventManager implementation.
Listeners register a callback with DownloadIntegration._determineFilenameCallbacks
that fires fire.async(item.serialize()) for each download before it starts.
The first listener to return a { filename, conflictAction } suggestion wins;
the suggestion is validated (no absolute paths, no path traversal) and
applied to download.target.path before execution begins.
Covers both extension-initiated downloads (via downloads.download()) and
all other browser-initiated downloads via the DownloadCore.start() hook.
… test onDeterminingFilename is now a real event, not an empty event handler, so remove the comment that said otherwise.
Test cases cover: - Event fires with correct DownloadItem properties - Listener can rename the downloaded file - Listener can place the file in a subdirectory - conflictAction "uniquify" creates a unique filename when target exists - conflictAction "overwrite" replaces an existing file - Absolute path suggestions are silently rejected - Path traversal (..) suggestions are silently rejected - First listener's non-null suggestion wins when multiple are registered - removeListener stops the callback from firing - Download proceeds normally when no listener is registered
Move onDeterminingFilename from an ad-hoc EventManager with an explicit register function into PERSISTENT_EVENTS so that the listener survives event-page suspension. Also stop iterating callbacks after the first one that successfully changes the target path (first suggestion wins).
Log each determineFilename() call and each callback invocation for debugging. Stop iterating after the first callback that changes the download's target path so that the first listener's suggestion wins.
Chrome's onDeterminingFilename API passes a suggest() callback as the second argument to listeners. Functions cannot cross the IPC boundary, so inject suggest() in the child process by wrapping each listener at addListener() time. Handles both the synchronous call pattern and the async pattern where the listener returns true and calls suggest() later.
Adds determineFilenameBeforeDialog() to fire onDeterminingFilename listeners before the auto-save path or Save As dialog is chosen. The _determinedDownloads WeakSet prevents determineFilename() in Download.start() from firing the event a second time for the same download. Also adds _processedLaunchers to track which nsIHelperAppLauncher objects have already been through HelperAppDlg, so DownloadLegacy can set the guard when creating the Download object.
When HelperAppDlg fires onDeterminingFilename before showing the Save As dialog, mark the nsIHelperAppLauncher as processed. DownloadLegacy checks this flag when the Download object is created and calls markFilenameAlreadyDetermined() so Download.start() skips the event.
…oads.download() When saveAs is true, fire onDeterminingFilename listeners on a synthetic download before showing the file picker, so extensions can redirect the filename before the user sees the dialog. Uses _syntheticSerialized to avoid adding the download to DownloadMap prematurely. Marks the download as already determined so Download.start() does not re-fire the event.
…h is chosen Call determineFilenameBeforeDialog() at the start of promptForSaveToFileAsync() so onDeterminingFilename listeners can redirect the download before auto-save or the Save As file picker runs. For auto-save, create any suggested subdirectory and pass only the leaf name to validateLeafName(). For the file picker, open the picker in the suggested subdirectory and show only the leaf name as the default string, skipping the lastDir override when a subdirectory was explicitly suggested by an extension.
…pplyFilenameSuggestion createNiceUniqueFile physically creates a zero-byte file to claim the name, which is correct when a download is about to start but wrong when called from applyFilenameSuggestion — the file picker hasn't run yet, so the placeholder appears in the directory and triggers a "file already exists" prompt when the user tries to save. Inline the same base(N)ext loop using splitBaseNameAndExtension to find a non-conflicting name without touching the filesystem. The actual placeholder is created later by validateLeafName (auto-save path) or the download core (file picker path).
…vior - test_onDeterminingFilename_double_fire_guard: verifies that markFilenameAlreadyDetermined() prevents the listener from firing in Download.start() when the event was already dispatched earlier (e.g. by HelperAppDlg before the Save As dialog). - test_onDeterminingFilename_uniquify_increments_counter: verifies that when both file.txt and file(1).txt already exist, the suggestion resolves to file(2).txt without creating any placeholder file. - test_onDeterminingFilename_default_conflict_action_is_uniquify: verifies that omitting conflictAction defaults to "uniquify" rather than silently overwriting the existing file.
No Taskcluster jobs started for this pull requestThe |
Contributor
|
(Automated Close) Please do not file pull requests here, see https://firefox-source-docs.mozilla.org/contributing/how_to_submit_a_patch.html |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A first pass implementation of chrome.downloads.onDeterminingFilename, as reported in https://bugzilla.mozilla.org/show_bug.cgi?id=1245652
Tested out with https://github.com/unintended/download-organizer-chrome-extension with minimal changes for Firefox compatibility.
Happy to iterate on this :)