Skip to content

fix: ServiceWorker job queue persistence and cross-page progress display - #124

Open
enhk2728-ui wants to merge 1 commit into
doufen-org:masterfrom
enhk2728-ui:fix/service-worker-job-queue-persistence
Open

enhk2728-ui wants to merge 1 commit into
doufen-org:masterfrom
enhk2728-ui:fix/service-worker-job-queue-persistence

Conversation

@enhk2728-ui

Copy link
Copy Markdown

Summary

This PR fixes an architectural issue in the MV3 ServiceWorker task service that prevented the progress bar from appearing after creating a backup job — known as the "新建任务后看不到进度条" (progress bar invisible after creating new task) problem.

Root Cause Analysis

The task service (Service) was being instantiated independently in each Chrome extension page context (backup.html, options.html, background.js). In Manifest V3, each page has its own isolated JavaScript context, so Service._instance was not shared between them.

When a user clicked "新建任务" in backup.html:

  1. A Job was created and enqueued in the backup.html page's Service instance
  2. The page immediately jumped to options.html#service via location.href
  3. This destroyed the backup.html page context — along with its Service instance and the enqueued job
  4. options.html loaded its own Service instance, which had no currentJob — so the progress table rendering returned early

Additionally, the session state persistence was broken:

  • service.toJSON() was saving _jobQueue.promises (internal Promise objects from AsyncBlockingQueue) which are not serializable
  • service.fromJSON() was trying to restore queue items via taskFromJSON() instead of Job.fromJSON()
  • Task.step() only incremented a counter but never fired UI refresh events

Changes

1. services/AsyncBlockingQueue.js

  • Added _queuedItems[] array that tracks actual data objects (not internal Promises)
  • enqueue() pushes items to _queuedItems; dequeue() removes them once consumed
  • Exposed via queuedItems getter for serialization

2. service.js

  • toJSON() now serializes _jobQueue.queuedItems (real Job objects via job.toJSON()) instead of internal Promise objects
  • fromJSON() now restores queue using Job.fromJSON() instead of taskFromJSON()

3. services/Task.js

  • Task now extends EventTarget (not just a plain class)
  • step() dispatches a 'taskstep' event so consumers can react to progress changes

4. services/Job.js

  • Job.run() now listens to each task's taskstep event and forwards it as a Service progress event
  • This drives real-time progress bar updates during task execution

5. backup.js

  • Key behavioral change: Instead of navigating away (location.href = ...) after creating a job, the page now stays in backup.html and renders an inline progress panel (ServicePanelInline)
  • This prevents the page context destruction that was the primary cause of invisible progress bars
  • A link to options.html is displayed for the full service panel view

6. options.js

  • ServicePanel now monitors chrome.storage.onChanged for session state updates
  • When another page (e.g., backup.html) saves updated Job state, options.html renders it from the stored JSON
  • Added _renderFromStoredJob() to render progress from serialized Job data
  • Extracted _renderTasks() helper for cleaner code reuse

Testing

These changes have been verified in a local extension build. The workflow:

  1. Open backup page (backup.html)
  2. Click "新建任务", select backup tasks, confirm
  3. Progress bar appears in backup.html without page navigation
  4. Opening options.html separately also shows progress via storage.onChanged

Related Issues

Fixes the "新建任务后看不到进度条" regression introduced by the MV3 migration.

The task service had several architectural issues in its MV3 ServiceWorker
context that prevented the progress bar from appearing after creating a
backup job:

Root causes fixed:

1. Queue persistence saved Promises instead of Jobs (service.js)
   - toJSON() was serializing _jobQueue.promises (internal Promise
     objects), which are not serializable — this broke session restore.
   - Fix: serialize _jobQueue.queuedItems (actual Job objects with
     proper toJSON()) and deserialize with Job.fromJSON().

2. AsyncBlockingQueue lacked item tracking (AsyncBlockingQueue.js)
   - Added _queuedItems[] array that mirrors enqueue/dequeue operations.
   - Added queuedItems getter for serializable queue state access.

3. Task.step() did not trigger progress events (Task.js)
   - Task now extends EventTarget and dispatches 'taskstep' event on
     each step() call, enabling real-time progress bar updates.

4. Job did not forward task progress to Service (Job.js)
   - Job.run() now listens to taskstep events on each task and forwards
     them as Service progress events to refresh the UI.

5. Page navigation destroyed the Service context (backup.js)
   - Creating a job then immediately jumping to options.html destroyed
     the backup.html page's Service instance (MV3 isolated contexts).
   - Fix: added ServicePanelInline class that displays task progress
     directly in backup.html without page navigation.

6. Cross-page progress awareness via storage.onChanged (options.js)
   - ServicePanel now monitors chrome.storage.onChanged for session
     state updates from other pages, enabling options.html to show
     progress even when the job runs from a different page context.

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant