Skip to content

service worker removal #173609

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 138 commits into
base: master
Choose a base branch
from

Conversation

SydneyBao
Copy link
Contributor

@SydneyBao SydneyBao commented Aug 12, 2025

Create a service worker that removes the old cached service worker.

Related issue: 156910

Pre-launch Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [Tree Hygiene] wiki page, which explains my responsibilities.
  • I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement].
  • I signed the [CLA].
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is [test-exempt].
  • I followed the [breaking change policy] and added [Data Driven Fixes] where supported.
  • All existing and new tests are passing.

@SydneyBao SydneyBao requested a review from matanlurey as a code owner August 12, 2025 07:54
@github-actions github-actions bot added tool Affects the "flutter" command-line tool. See also t: labels. engine flutter/engine repository. See also e: labels. platform-web Web applications specifically labels Aug 12, 2025
@SydneyBao SydneyBao mentioned this pull request Aug 12, 2025
9 tasks
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request removes the automatic service worker registration from Flutter web applications, replacing it with a cleanup worker designed to remove old caches from previous versions. The changes are well-contained within flutter_tools, flutter.js, and the relevant test files. The migration strategy appears sound. I have identified one critical issue in the new test implementation and one medium-severity issue regarding a duplicated test case in the web test suite.

@bkonyi bkonyi requested review from mdebbar and bkonyi and removed request for matanlurey August 12, 2025 14:44
@mdebbar
Copy link
Contributor

mdebbar commented Aug 13, 2025

/gemini review

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request removes the automatic service worker registration from Flutter's web loader and replaces the default service worker with a cleanup worker that removes old caches and unregisters itself. The changes are well-implemented and the accompanying test verifies the cleanup behavior. I have a few suggestions to improve test realism and code clarity.

environment: <String, String>{'FLUTTER_WEB': 'true'},
const String oldCachingWorkerContent = '''
'use strict';
const CACHE_NAME = 'flutter-test-cache-v1';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To make the test more realistic and thorough, consider using one of the actual old cache names that the cleanup worker is designed to remove, such as flutter-app-cache. This would not only test the unregistration of the service worker but also implicitly test the cache deletion logic, making the test more robust.

  const CACHE_NAME = 'flutter-app-cache';

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Comment on lines 12 to 16
* default Flutter service worker resolves immediately.
* @param {import("./types").ServiceWorkerSettings} settings Service worker settings
* @returns {Promise} that resolves when the latest serviceWorker is ready.
* @returns {Promise<void>} that resolves immediately
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The comment 'default Flutter service worker resolves immediately' is a bit vague. It would be clearer to state that service worker registration is now disabled in flutter.js and this method is a stub that no longer performs any action, other than warning about the deprecation.

   * Disables service worker registration from flutter.js. This is now a no-op,
   * and a warning will be logged if any `settings` are provided.
   * @param {import("./types").ServiceWorkerSettings} settings Service worker settings
   * @returns {Promise<void>} that resolves immediately
   */

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed!

Comment on lines 1 to +43
var origin = self.location.origin;
var key = event.request.url.substring(origin.length + 1);
// Redirect URLs to the index.html
if (key.indexOf('?v=') != -1) {
key = key.split('?v=')[0];
}
if (event.request.url == origin || event.request.url.startsWith(origin + '/#') || key == '') {
key = '/';
}
// If the URL is not the RESOURCE list then return to signal that the
// browser should take over.
if (!RESOURCES[key]) {
return;
}
// If the URL is the index.html, perform an online-first request.
if (key == '/') {
return onlineFirst(event);
}
event.respondWith(caches.open(CACHE_NAME)
.then((cache) => {
return cache.match(event.request).then((response) => {
// Either respond with the cached resource, or perform a fetch and
// lazily populate the cache only if the resource was successfully fetched.
return response || fetch(event.request).then((response) => {
if (response && Boolean(response.ok)) {
cache.put(event.request, response.clone());
}
return response;

try {
const clients = await self.clients.matchAll({
type: 'window',
includeUncontrolled: true,
});
})
})
);
});
self.addEventListener('message', (event) => {
// SkipWaiting can be used to immediately activate a waiting service worker.
// This will also require a page refresh triggered by the main worker.
if (event.data === 'skipWaiting') {
self.skipWaiting();
return;
}
if (event.data === 'downloadOffline') {
downloadOffline();
return;
}
});
// Download offline will check the RESOURCES for all files not in the cache
// and populate them.
async function downloadOffline() {
var resources = [];
var contentCache = await caches.open(CACHE_NAME);
var currentContent = {};
for (var request of await contentCache.keys()) {
var key = request.url.substring(origin.length + 1);
if (key == "") {
key = "/";
}
currentContent[key] = true;
}
for (var resourceKey of Object.keys(RESOURCES)) {
if (!currentContent[resourceKey]) {
resources.push(resourceKey);
}
}
return contentCache.addAll(resources);
}
// Attempt to download the resource online before falling back to
// the offline cache.
function onlineFirst(event) {
return event.respondWith(
fetch(event.request).then((response) => {
return caches.open(CACHE_NAME).then((cache) => {
cache.put(event.request, response.clone());
return response;
});
}).catch((error) => {
return caches.open(CACHE_NAME).then((cache) => {
return cache.match(event.request).then((response) => {
if (response != null) {
return response;
// Reload clients to ensure they are not using the old service worker.
clients.forEach((client) => {
if (client.url && 'navigate' in client) {
client.navigate(client.url);
}
throw error;
});
});
})
} catch (e) {
console.warn('Failed to navigate service worker clients:', e);
}
})()
);
}
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This new service worker implementation is static and does not require any resource map injection. However, the Dart code that generates this service worker in packages/flutter_tools/lib/src/web/file_generators/flutter_service_worker_js.dart still contains logic to replace $$RESOURCES_MAP and $$CORE_LIST placeholders. This logic is now dead code.

While the file containing the generator is not part of this PR, it would be good to simplify generateServiceWorker function and the call site in packages/flutter_tools/lib/src/build_system/targets/web.dart to remove the unused parameters and resource map computation as part of this change or in a follow-up PR to improve maintainability.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gemini-code-assist can you generate a diff for what you are suggesting?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the follow-up! My previous comment highlighted that the new flutter_service_worker.js is static and no longer uses the $$RESOURCES_MAP and $$CORE_LIST placeholders. While this PR correctly updates the flutter_service_worker.js file itself, the logic that generates this file in packages/flutter_tools/lib/src/web/file_generators/flutter_service_worker_js.dart and its call site in packages/flutter_tools/lib/src/build_system/targets/web.dart still contain code to compute and pass these unused parameters.

Since those generator files are not part of this specific pull request, I cannot provide a committable diff directly for them. However, for a follow-up PR, here are the suggested changes to remove that dead code:

1. packages/flutter_tools/lib/src/web/file_generators/flutter_service_worker_js.dart

// Old function signature and body:
// String generateServiceWorker({
//   required Map<String, String> resourcesMap,
//   required List<String> coreList,
// }) {
//   // ... existing logic that uses resourcesMap and coreList to build the old service worker content ...
// }

// New function signature and body:
String generateServiceWorker() {
  return '''
'use strict';

const OLD_CACHE_NAMES = ['flutter-app-manifest', 'flutter-app-cache', 'flutter-temp-cache'];

self.addEventListener('install', () => {
  self.skipWaiting();
  console.log('Deprecated service worker installed. It will not be used.');
});

// remove old caches and unregister the service worker
self.addEventListener('activate', (event) => {
  event.waitUntil(
    (async () => {
      try {
        const deletePromises = OLD_CACHE_NAMES.map((key) => self.caches.delete(key));
        await Promise.all(deletePromises);
      } catch (e) {
        console.warn('Failed to delete old service worker caches:', e);
      }

      try {
        await self.registration.unregister();
      } catch (e) {
        console.warn('Failed to unregister service worker:', e);
      }

      try {
        const clients = await self.clients.matchAll({
          type: 'window',
          includeUncontrolled: true,
        });
        // Reload clients to ensure they are not using the old service worker.
        clients.forEach((client) => {
          if (client.url && 'navigate' in client) {
            client.navigate(client.url);
          }
        });
      } catch (e) {
        console.warn('Failed to navigate service worker clients:', e);
      }
    })()
  );
});
''';
}

2. packages/flutter_tools/lib/src/build_system/targets/web.dart

// Old call site (inside a build step or function):
// final Map<String, String> resourcesMap = <String, String>{ /* ... compute ... */ };
// final List<String> coreList = <String>[ /* ... compute ... */ ];
// final String serviceWorkerContent = generateServiceWorker(
//   resourcesMap: resourcesMap,
//   coreList: coreList,
// );

// New call site:
final String serviceWorkerContent = generateServiceWorker();

These changes would remove the now-unused parameters and the logic to compute them, aligning the generator with the new static service worker content.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@SydneyBao Gemini is right that we should update that function, but the diff it produced isn't right 😅

We should do something like this:

String generateServiceWorker(
  String fileGeneratorsPath, {
  required ServiceWorkerStrategy serviceWorkerStrategy,
}) {
  if (serviceWorkerStrategy == ServiceWorkerStrategy.none) {
    return '';
  }

  final String flutterServiceWorkerJsPath = globals.localFileSystem.path.join(
    fileGeneratorsPath,
    'js',
    'flutter_service_worker.js',
  );
  return globals.localFileSystem
      .file(flutterServiceWorkerJsPath)
      .readAsStringSync();
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch! Fixed it!

}
print('END runWebServiceWorkerTestWithCustomServiceWorkerVersion(headless: $headless)');
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new line

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!

@@ -740,4 +705,4 @@ class WebTestsSuite {
throw Exception('WebDriver not available.');
}
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new line

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!

Comment on lines 205 to 202
() => _runWebStackTraceTest('release', 'lib/stack_trace.dart'),
() => _runWebStackTraceTest('release', 'lib/framework_stack_trace.dart'),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These 2 are now repeated twice.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't believe I didn't catch that. Thank you!

Comment on lines 203 to 204
() => _runWebStackTraceTest('debug', 'lib/stack_trace.dart'),
() => _runWebStackTraceTest('debug', 'lib/framework_stack_trace.dart'),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These 2 didn't exist before. They seem unrelated to the service worker, is there a reason they were added?

Copy link
Contributor Author

@SydneyBao SydneyBao Aug 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Must've been added when I copied my code from an older branch. They've been removed. Thank you!

Comment on lines 201 to 202
() => runWebCanvasKitUnitTests(),
() => runWebSkwasmUnitTests(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These 2 aren't supposed to be run as part of this shard. They are run separately as you can see here:

flutter/dev/bots/test.dart

Lines 134 to 139 in c932faf

// All the unit/widget tests run using `flutter test --platform=chrome`
'web_canvaskit_tests': webTestsSuite.runWebCanvasKitUnitTests,
// All the unit/widget tests run using `flutter test --platform=chrome --wasm`
'web_skwasm_tests': webTestsSuite.runWebSkwasmUnitTests,
// All web integration tests
'web_long_running_tests': webTestsSuite.webLongRunningTestsRunner,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed!

}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

new line

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done!

This reverts commit 27e54df.

revert
@SydneyBao SydneyBao requested review from a team as code owners August 14, 2025 21:47
@github-actions github-actions bot added a: tests "flutter test", flutter_test, or one of our tests a: text input Entering text in a text field or keyboard related problems platform-android Android applications specifically platform-ios iOS applications specifically framework flutter/packages/flutter repository. See also f: labels. f: material design flutter/packages/flutter/material repository. a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) a: internationalization Supporting other languages or locales. (aka i18n) platform-fuchsia Fuchsia code specifically f: scrolling Viewports, list views, slivers, etc. f: cupertino flutter/packages/flutter/cupertino repository platform-windows Building on or for Windows specifically d: examples Sample code and demos f: routes Navigator, Router, and related APIs. labels Aug 14, 2025
SydneyBao and others added 9 commits August 22, 2025 13:03
Reapply "update removal"

This reverts commit 31cc418.
This reverts commit 8aec333.
…r#174235)

https://dart.googlesource.com/sdk.git/+log/0d0a0c394381..c153c5259e62

2025-08-21 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com
Version 3.10.0-125.0.dev
2025-08-21 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com
Version 3.10.0-124.0.dev
2025-08-21 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com
Version 3.10.0-123.0.dev
2025-08-21 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com
Version 3.10.0-122.0.dev
2025-08-21 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com
Version 3.10.0-121.0.dev
2025-08-20 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com
Version 3.10.0-120.0.dev
2025-08-20 dart-internal-merge@dart-ci-internal.iam.gserviceaccount.com
Version 3.10.0-119.0.dev

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/dart-sdk-flutter
Please CC dart-vm-team@google.com,jimgraham@google.com on the revert to
ensure that a human
is aware of the problem.

To file a bug in Flutter:
https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
@SydneyBao SydneyBao force-pushed the final_service_worker_removal branch from 7ef7927 to 3996aad Compare August 22, 2025 17:05
@github-actions github-actions bot removed a: text input Entering text in a text field or keyboard related problems platform-android Android applications specifically platform-ios iOS applications specifically framework flutter/packages/flutter repository. See also f: labels. f: material design flutter/packages/flutter/material repository. a: accessibility Accessibility, e.g. VoiceOver or TalkBack. (aka a11y) f: scrolling Viewports, list views, slivers, etc. f: cupertino flutter/packages/flutter/cupertino repository platform-windows Building on or for Windows specifically d: api docs Issues with https://api.flutter.dev/ d: examples Sample code and demos f: routes Navigator, Router, and related APIs. platform-linux Building on or for Linux specifically a: desktop Running on desktop f: focus Focus traversal, gaining or losing focus team-android Owned by Android platform team team-ios Owned by iOS platform team d: docs/ flutter/flutter/docs, for contributors labels Aug 22, 2025
@SydneyBao
Copy link
Contributor Author

SydneyBao commented Aug 22, 2025

@SydneyBao Are the files that you modified expected or did you have a bad merge?

fixed!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
engine flutter/engine repository. See also e: labels. platform-web Web applications specifically tool Affects the "flutter" command-line tool. See also t: labels.
Projects
None yet
Development

Successfully merging this pull request may close these issues.