Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
0fb4406c
Unverified
Commit
0fb4406c
authored
Feb 09, 2023
by
Kevin Chisholm
Committed by
GitHub
Feb 09, 2023
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Revert "[web] Move JS content to its own `.js` files (#117691)" (#120275)
This reverts commit
e03029ef
.
parent
ef854a3d
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
568 additions
and
667 deletions
+568
-667
artifacts.dart
packages/flutter_tools/lib/src/artifacts.dart
+0
-39
web.dart
packages/flutter_tools/lib/src/build_system/targets/web.dart
+1
-7
devfs_web.dart
packages/flutter_tools/lib/src/isolated/devfs_web.dart
+1
-4
flutter_js.dart
...flutter_tools/lib/src/web/file_generators/flutter_js.dart
+379
-8
flutter_service_worker_js.dart
...ib/src/web/file_generators/flutter_service_worker_js.dart
+179
-19
flutter.js
...s/flutter_tools/lib/src/web/file_generators/js/flutter.js
+0
-375
flutter_service_worker.js
.../lib/src/web/file_generators/js/flutter_service_worker.js
+0
-172
web_test.dart
...ols/test/general.shard/build_system/targets/web_test.dart
+7
-37
project.dart
...utter_tools/test/integration.shard/test_data/project.dart
+1
-6
No files found.
packages/flutter_tools/lib/src/artifacts.dart
View file @
0fb4406c
...
@@ -9,7 +9,6 @@ import 'base/common.dart';
...
@@ -9,7 +9,6 @@ import 'base/common.dart';
import
'base/file_system.dart'
;
import
'base/file_system.dart'
;
import
'base/os.dart'
;
import
'base/os.dart'
;
import
'base/platform.dart'
;
import
'base/platform.dart'
;
import
'base/user_messages.dart'
;
import
'base/utils.dart'
;
import
'base/utils.dart'
;
import
'build_info.dart'
;
import
'build_info.dart'
;
import
'cache.dart'
;
import
'cache.dart'
;
...
@@ -63,9 +62,6 @@ enum Artifact {
...
@@ -63,9 +62,6 @@ enum Artifact {
/// Tools related to subsetting or icon font files.
/// Tools related to subsetting or icon font files.
fontSubset
,
fontSubset
,
constFinder
,
constFinder
,
/// The location of file generators.
flutterToolsFileGenerators
,
}
}
/// A subset of [Artifact]s that are platform and build mode independent
/// A subset of [Artifact]s that are platform and build mode independent
...
@@ -206,8 +202,6 @@ String? _artifactToFileName(Artifact artifact, Platform hostPlatform, [ BuildMod
...
@@ -206,8 +202,6 @@ String? _artifactToFileName(Artifact artifact, Platform hostPlatform, [ BuildMod
return
'font-subset
$exe
'
;
return
'font-subset
$exe
'
;
case
Artifact
.
constFinder
:
case
Artifact
.
constFinder
:
return
'const_finder.dart.snapshot'
;
return
'const_finder.dart.snapshot'
;
case
Artifact
.
flutterToolsFileGenerators
:
return
''
;
}
}
}
}
...
@@ -531,8 +525,6 @@ class CachedArtifacts implements Artifacts {
...
@@ -531,8 +525,6 @@ class CachedArtifacts implements Artifacts {
case
Artifact
.
windowsCppClientWrapper
:
case
Artifact
.
windowsCppClientWrapper
:
case
Artifact
.
windowsDesktopPath
:
case
Artifact
.
windowsDesktopPath
:
return
_getHostArtifactPath
(
artifact
,
platform
,
mode
);
return
_getHostArtifactPath
(
artifact
,
platform
,
mode
);
case
Artifact
.
flutterToolsFileGenerators
:
return
_getFileGeneratorsPath
();
}
}
}
}
...
@@ -570,8 +562,6 @@ class CachedArtifacts implements Artifacts {
...
@@ -570,8 +562,6 @@ class CachedArtifacts implements Artifacts {
case
Artifact
.
windowsCppClientWrapper
:
case
Artifact
.
windowsCppClientWrapper
:
case
Artifact
.
windowsDesktopPath
:
case
Artifact
.
windowsDesktopPath
:
return
_getHostArtifactPath
(
artifact
,
platform
,
mode
);
return
_getHostArtifactPath
(
artifact
,
platform
,
mode
);
case
Artifact
.
flutterToolsFileGenerators
:
return
_getFileGeneratorsPath
();
}
}
}
}
...
@@ -621,8 +611,6 @@ class CachedArtifacts implements Artifacts {
...
@@ -621,8 +611,6 @@ class CachedArtifacts implements Artifacts {
case
Artifact
.
windowsCppClientWrapper
:
case
Artifact
.
windowsCppClientWrapper
:
case
Artifact
.
windowsDesktopPath
:
case
Artifact
.
windowsDesktopPath
:
return
_getHostArtifactPath
(
artifact
,
platform
,
mode
);
return
_getHostArtifactPath
(
artifact
,
platform
,
mode
);
case
Artifact
.
flutterToolsFileGenerators
:
return
_getFileGeneratorsPath
();
}
}
}
}
...
@@ -697,8 +685,6 @@ class CachedArtifacts implements Artifacts {
...
@@ -697,8 +685,6 @@ class CachedArtifacts implements Artifacts {
case
Artifact
.
fuchsiaFlutterRunner
:
case
Artifact
.
fuchsiaFlutterRunner
:
case
Artifact
.
fuchsiaKernelCompiler
:
case
Artifact
.
fuchsiaKernelCompiler
:
throw
StateError
(
'Artifact
$artifact
not available for platform
$platform
.'
);
throw
StateError
(
'Artifact
$artifact
not available for platform
$platform
.'
);
case
Artifact
.
flutterToolsFileGenerators
:
return
_getFileGeneratorsPath
();
}
}
}
}
...
@@ -966,8 +952,6 @@ class CachedLocalEngineArtifacts implements Artifacts {
...
@@ -966,8 +952,6 @@ class CachedLocalEngineArtifacts implements Artifacts {
case
Artifact
.
dart2wasmSnapshot
:
case
Artifact
.
dart2wasmSnapshot
:
case
Artifact
.
frontendServerSnapshotForEngineDartSdk
:
case
Artifact
.
frontendServerSnapshotForEngineDartSdk
:
return
_fileSystem
.
path
.
join
(
_getDartSdkPath
(),
'bin'
,
'snapshots'
,
artifactFileName
);
return
_fileSystem
.
path
.
join
(
_getDartSdkPath
(),
'bin'
,
'snapshots'
,
artifactFileName
);
case
Artifact
.
flutterToolsFileGenerators
:
return
_getFileGeneratorsPath
();
}
}
}
}
...
@@ -1115,7 +1099,6 @@ class CachedLocalWebSdkArtifacts implements Artifacts {
...
@@ -1115,7 +1099,6 @@ class CachedLocalWebSdkArtifacts implements Artifacts {
case
Artifact
.
fuchsiaFlutterRunner
:
case
Artifact
.
fuchsiaFlutterRunner
:
case
Artifact
.
fontSubset
:
case
Artifact
.
fontSubset
:
case
Artifact
.
constFinder
:
case
Artifact
.
constFinder
:
case
Artifact
.
flutterToolsFileGenerators
:
break
;
break
;
}
}
}
}
...
@@ -1315,11 +1298,6 @@ class _TestArtifacts implements Artifacts {
...
@@ -1315,11 +1298,6 @@ class _TestArtifacts implements Artifacts {
BuildMode
?
mode
,
BuildMode
?
mode
,
EnvironmentType
?
environmentType
,
EnvironmentType
?
environmentType
,
})
{
})
{
// The path to file generators is the same even in the test environment.
if
(
artifact
==
Artifact
.
flutterToolsFileGenerators
)
{
return
_getFileGeneratorsPath
();
}
final
StringBuffer
buffer
=
StringBuffer
();
final
StringBuffer
buffer
=
StringBuffer
();
buffer
.
write
(
artifact
);
buffer
.
write
(
artifact
);
if
(
platform
!=
null
)
{
if
(
platform
!=
null
)
{
...
@@ -1362,20 +1340,3 @@ class _TestLocalEngine extends _TestArtifacts {
...
@@ -1362,20 +1340,3 @@ class _TestLocalEngine extends _TestArtifacts {
@override
@override
final
LocalEngineInfo
localEngineInfo
;
final
LocalEngineInfo
localEngineInfo
;
}
}
String
_getFileGeneratorsPath
(
)
{
final
String
flutterRoot
=
Cache
.
defaultFlutterRoot
(
fileSystem:
globals
.
localFileSystem
,
platform:
const
LocalPlatform
(),
userMessages:
UserMessages
(),
);
return
globals
.
localFileSystem
.
path
.
join
(
flutterRoot
,
'packages'
,
'flutter_tools'
,
'lib'
,
'src'
,
'web'
,
'file_generators'
,
);
}
packages/flutter_tools/lib/src/build_system/targets/web.dart
View file @
0fb4406c
...
@@ -529,10 +529,7 @@ class WebBuiltInAssets extends Target {
...
@@ -529,10 +529,7 @@ class WebBuiltInAssets extends Target {
// Write the flutter.js file
// Write the flutter.js file
final
File
flutterJsFile
=
environment
.
outputDir
.
childFile
(
'flutter.js'
);
final
File
flutterJsFile
=
environment
.
outputDir
.
childFile
(
'flutter.js'
);
final
String
fileGeneratorsPath
=
flutterJsFile
.
writeAsStringSync
(
flutter_js
.
generateFlutterJsFile
());
globals
.
artifacts
!.
getArtifactPath
(
Artifact
.
flutterToolsFileGenerators
);
flutterJsFile
.
writeAsStringSync
(
flutter_js
.
generateFlutterJsFile
(
fileGeneratorsPath
));
}
}
}
}
...
@@ -601,10 +598,7 @@ class WebServiceWorker extends Target {
...
@@ -601,10 +598,7 @@ class WebServiceWorker extends Target {
final
ServiceWorkerStrategy
serviceWorkerStrategy
=
_serviceWorkerStrategyFromString
(
final
ServiceWorkerStrategy
serviceWorkerStrategy
=
_serviceWorkerStrategyFromString
(
environment
.
defines
[
kServiceWorkerStrategy
],
environment
.
defines
[
kServiceWorkerStrategy
],
);
);
final
String
fileGeneratorsPath
=
globals
.
artifacts
!.
getArtifactPath
(
Artifact
.
flutterToolsFileGenerators
);
final
String
serviceWorker
=
generateServiceWorker
(
final
String
serviceWorker
=
generateServiceWorker
(
fileGeneratorsPath
,
urlToHash
,
urlToHash
,
<
String
>[
<
String
>[
'main.dart.js'
,
'main.dart.js'
,
...
...
packages/flutter_tools/lib/src/isolated/devfs_web.dart
View file @
0fb4406c
...
@@ -828,10 +828,7 @@ class WebDevFS implements DevFS {
...
@@ -828,10 +828,7 @@ class WebDevFS implements DevFS {
'stack_trace_mapper.js'
,
stackTraceMapper
.
readAsBytesSync
());
'stack_trace_mapper.js'
,
stackTraceMapper
.
readAsBytesSync
());
webAssetServer
.
writeFile
(
webAssetServer
.
writeFile
(
'manifest.json'
,
'{"info":"manifest not generated in run mode."}'
);
'manifest.json'
,
'{"info":"manifest not generated in run mode."}'
);
final
String
fileGeneratorsPath
=
globals
.
artifacts
!
webAssetServer
.
writeFile
(
'flutter.js'
,
flutter_js
.
generateFlutterJsFile
());
.
getArtifactPath
(
Artifact
.
flutterToolsFileGenerators
);
webAssetServer
.
writeFile
(
'flutter.js'
,
flutter_js
.
generateFlutterJsFile
(
fileGeneratorsPath
));
webAssetServer
.
writeFile
(
'flutter_service_worker.js'
,
webAssetServer
.
writeFile
(
'flutter_service_worker.js'
,
'// Service worker not loaded in run mode.'
);
'// Service worker not loaded in run mode.'
);
webAssetServer
.
writeFile
(
webAssetServer
.
writeFile
(
...
...
packages/flutter_tools/lib/src/web/file_generators/flutter_js.dart
View file @
0fb4406c
...
@@ -2,17 +2,388 @@
...
@@ -2,17 +2,388 @@
// Use of this source code is governed by a BSD-style license that can be
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// found in the LICENSE file.
import
'../../globals.dart'
as
globals
;
/// Generates the flutter.js file.
/// Generates the flutter.js file.
///
///
/// flutter.js should be completely static, so **do not use any parameter or
/// flutter.js should be completely static, so **do not use any parameter or
/// environment variable to generate this file**.
/// environment variable to generate this file**.
String
generateFlutterJsFile
(
String
fileGeneratorsPath
)
{
String
generateFlutterJsFile
(
)
{
final
String
flutterJsPath
=
globals
.
localFileSystem
.
path
.
join
(
return
r''
'
fileGeneratorsPath
,
// Copyright 2014 The Flutter Authors. All rights reserved.
'js'
,
// Use of this source code is governed by a BSD-style license that can be
'flutter.js'
,
// found in the LICENSE file.
);
return
globals
.
localFileSystem
.
file
(
flutterJsPath
).
readAsStringSync
();
if (!_flutter) {
var _flutter = {};
}
_flutter.loader = null;
(function () {
"use strict";
const baseUri = ensureTrailingSlash(getBaseURI());
function getBaseURI() {
const base = document.querySelector("base");
return (base && base.getAttribute("href")) || "";
}
function ensureTrailingSlash(uri) {
if (uri == "") {
return uri;
}
return uri.endsWith("/") ? uri : `
${uri}
/`;
}
/**
* Wraps `promise` in a timeout of the given `duration` in ms.
*
* Resolves/rejects with whatever the original `promises` does, or rejects
* if `promise` takes longer to complete than `duration`. In that case,
* `debugName` is used to compose a legible error message.
*
* If `duration` is < 0, the original `promise` is returned unchanged.
* @param {Promise} promise
* @param {number} duration
* @param {string} debugName
* @returns {Promise} a wrapped promise.
*/
async function timeout(promise, duration, debugName) {
if (duration < 0) {
return promise;
}
let timeoutId;
const _clock = new Promise((_, reject) => {
timeoutId = setTimeout(() => {
reject(
new Error(
`
${debugName}
took more than
${duration}
ms to resolve. Moving on.`,
{
cause: timeout,
}
)
);
}, duration);
});
return Promise.race([promise, _clock]).finally(() => {
clearTimeout(timeoutId);
});
}
/**
* Handles the creation of a TrustedTypes `policy` that validates URLs based
* on an (optional) incoming array of RegExes.
*/
class FlutterTrustedTypesPolicy {
/**
* Constructs the policy.
* @param {[RegExp]} validPatterns the patterns to test URLs
* @param {String} policyName the policy name (optional)
*/
constructor(validPatterns, policyName = "flutter-js") {
const patterns = validPatterns || [
/
\
.dart
\
.js
$
/,
/^flutter_service_worker.js
$
/
];
if (window.trustedTypes) {
this.policy = trustedTypes.createPolicy(policyName, {
createScriptURL: function(url) {
const parsed = new URL(url, window.location);
const file = parsed.pathname.split("/").pop();
const matches = patterns.some((pattern) => pattern.test(file));
if (matches) {
return parsed.toString();
}
console.error(
"URL rejected by TrustedTypes policy",
policyName, ":", url, "(download prevented)");
}
});
}
}
}
/**
* Handles loading/reloading Flutter'
s
service
worker
,
if
configured
.
*
*
@see
:
https:
//developers.google.com/web/fundamentals/primers/service-workers
*/
class
FlutterServiceWorkerLoader
{
/**
* Injects a TrustedTypesPolicy (or undefined if the feature is not supported).
* @param {TrustedTypesPolicy | undefined} policy
*/
setTrustedTypesPolicy
(
policy
)
{
this
.
_ttPolicy
=
policy
;
}
/**
* Returns a Promise that resolves when the latest Flutter service worker,
* configured by `settings` has been loaded and activated.
*
* Otherwise, the promise is rejected with an error message.
* @param {*} settings Service worker settings
* @returns {Promise} that resolves when the latest serviceWorker is ready.
*/
loadServiceWorker
(
settings
)
{
if
(!(
"serviceWorker"
in
navigator
)
||
settings
==
null
)
{
// In the future, settings = null -> uninstall service worker?
return
Promise
.
reject
(
new
Error
(
"Service worker not supported (or configured)."
)
);
}
const
{
serviceWorkerVersion
,
serviceWorkerUrl
=
`$
{
baseUri
}
flutter_service_worker
.
js
?
v
=
$
{
serviceWorkerVersion
}
`
,
timeoutMillis
=
4000
,
}
=
settings
;
// Apply the TrustedTypes policy, if present.
let
url
=
serviceWorkerUrl
;
if
(
this
.
_ttPolicy
!=
null
)
{
url
=
this
.
_ttPolicy
.
createScriptURL
(
url
);
}
const
serviceWorkerActivation
=
navigator
.
serviceWorker
.
register
(
url
)
.
then
(
this
.
_getNewServiceWorker
)
.
then
(
this
.
_waitForServiceWorkerActivation
);
// Timeout race promise
return
timeout
(
serviceWorkerActivation
,
timeoutMillis
,
"prepareServiceWorker"
);
}
/**
* Returns the latest service worker for the given `serviceWorkerRegistrationPromise`.
*
* This might return the current service worker, if there's no new service worker
* awaiting to be installed/updated.
*
* @param {Promise<ServiceWorkerRegistration>} serviceWorkerRegistrationPromise
* @returns {Promise<ServiceWorker>}
*/
async
_getNewServiceWorker
(
serviceWorkerRegistrationPromise
)
{
const
reg
=
await
serviceWorkerRegistrationPromise
;
if
(!
reg
.
active
&&
(
reg
.
installing
||
reg
.
waiting
))
{
// No active web worker and we have installed or are installing
// one for the first time. Simply wait for it to activate.
console
.
debug
(
"Installing/Activating first service worker."
);
return
reg
.
installing
||
reg
.
waiting
;
}
else
if
(!
reg
.
active
.
scriptURL
.
endsWith
(
serviceWorkerVersion
))
{
// When the app updates the serviceWorkerVersion changes, so we
// need to ask the service worker to update.
return
reg
.
update
().
then
((
newReg
)
=>
{
console
.
debug
(
"Updating service worker."
);
return
newReg
.
installing
||
newReg
.
waiting
||
newReg
.
active
;
});
}
else
{
console
.
debug
(
"Loading from existing service worker."
);
return
reg
.
active
;
}
}
/**
* Returns a Promise that resolves when the `latestServiceWorker` changes its
* state to "activated".
*
* @param {Promise<ServiceWorker>} latestServiceWorkerPromise
* @returns {Promise<void>}
*/
async
_waitForServiceWorkerActivation
(
latestServiceWorkerPromise
)
{
const
serviceWorker
=
await
latestServiceWorkerPromise
;
if
(!
serviceWorker
||
serviceWorker
.
state
==
"activated"
)
{
if
(!
serviceWorker
)
{
return
Promise
.
reject
(
new
Error
(
"Cannot activate a null service worker!"
)
);
}
else
{
console
.
debug
(
"Service worker already active."
);
return
Promise
.
resolve
();
}
}
return
new
Promise
((
resolve
,
_
)
=>
{
serviceWorker
.
addEventListener
(
"statechange"
,
()
=>
{
if
(
serviceWorker
.
state
==
"activated"
)
{
console
.
debug
(
"Activated new service worker."
);
resolve
();
}
});
});
}
}
/**
* Handles injecting the main Flutter web entrypoint (main.dart.js), and notifying
* the user when Flutter is ready, through `didCreateEngineInitializer`.
*
* @see https://docs.flutter.dev/development/platform-integration/web/initialization
*/
class
FlutterEntrypointLoader
{
/**
* Creates a FlutterEntrypointLoader.
*/
constructor
()
{
// Watchdog to prevent injecting the main entrypoint multiple times.
this
.
_scriptLoaded
=
false
;
}
/**
* Injects a TrustedTypesPolicy (or undefined if the feature is not supported).
* @param {TrustedTypesPolicy | undefined} policy
*/
setTrustedTypesPolicy
(
policy
)
{
this
.
_ttPolicy
=
policy
;
}
/**
* Loads flutter main entrypoint, specified by `entrypointUrl`, and calls a
* user-specified `onEntrypointLoaded` callback with an EngineInitializer
* object when it's done.
*
* @param {*} options
* @returns {Promise | undefined} that will eventually resolve with an
* EngineInitializer, or will be rejected with the error caused by the loader.
* Returns undefined when an `onEntrypointLoaded` callback is supplied in `options`.
*/
async
loadEntrypoint
(
options
)
{
const
{
entrypointUrl
=
`$
{
baseUri
}
main
.
dart
.
js
`
,
onEntrypointLoaded
}
=
options
||
{};
return
this
.
_loadEntrypoint
(
entrypointUrl
,
onEntrypointLoaded
);
}
/**
* Resolves the promise created by loadEntrypoint, and calls the `onEntrypointLoaded`
* function supplied by the user (if needed).
*
* Called by Flutter through `_flutter.loader.didCreateEngineInitializer` method,
* which is bound to the correct instance of the FlutterEntrypointLoader by
* the FlutterLoader object.
*
* @param {Function} engineInitializer @see https://github.com/flutter/engine/blob/main/lib/web_ui/lib/src/engine/js_interop/js_loader.dart#L42
*/
didCreateEngineInitializer
(
engineInitializer
)
{
if
(
typeof
this
.
_didCreateEngineInitializerResolve
===
"function"
)
{
this
.
_didCreateEngineInitializerResolve
(
engineInitializer
);
// Remove the resolver after the first time, so Flutter Web can hot restart.
this
.
_didCreateEngineInitializerResolve
=
null
;
// Make the engine revert to "auto" initialization on hot restart.
delete
_flutter
.
loader
.
didCreateEngineInitializer
;
}
if
(
typeof
this
.
_onEntrypointLoaded
===
"function"
)
{
this
.
_onEntrypointLoaded
(
engineInitializer
);
}
}
/**
* Injects a script tag into the DOM, and configures this loader to be able to
* handle the "entrypoint loaded" notifications received from Flutter web.
*
* @param {string} entrypointUrl the URL of the script that will initialize
* Flutter.
* @param {Function} onEntrypointLoaded a callback that will be called when
* Flutter web notifies this object that the entrypoint is
* loaded.
* @returns {Promise | undefined} a Promise that resolves when the entrypoint
* is loaded, or undefined if `onEntrypointLoaded`
* is a function.
*/
_loadEntrypoint
(
entrypointUrl
,
onEntrypointLoaded
)
{
const
useCallback
=
typeof
onEntrypointLoaded
===
"function"
;
if
(!
this
.
_scriptLoaded
)
{
this
.
_scriptLoaded
=
true
;
const
scriptTag
=
this
.
_createScriptTag
(
entrypointUrl
);
if
(
useCallback
)
{
// Just inject the script tag, and return nothing; Flutter will call
// `didCreateEngineInitializer` when it's done.
console
.
debug
(
"Injecting <script> tag. Using callback."
);
this
.
_onEntrypointLoaded
=
onEntrypointLoaded
;
document
.
body
.
append
(
scriptTag
);
}
else
{
// Inject the script tag and return a promise that will get resolved
// with the EngineInitializer object from Flutter when it calls
// `didCreateEngineInitializer` later.
return
new
Promise
((
resolve
,
reject
)
=>
{
console
.
debug
(
"Injecting <script> tag. Using Promises. Use the callback approach instead!"
);
this
.
_didCreateEngineInitializerResolve
=
resolve
;
scriptTag
.
addEventListener
(
"error"
,
reject
);
document
.
body
.
append
(
scriptTag
);
});
}
}
}
/**
* Creates a script tag for the given URL.
* @param {string} url
* @returns {HTMLScriptElement}
*/
_createScriptTag
(
url
)
{
const
scriptTag
=
document
.
createElement
(
"script"
);
scriptTag
.
type
=
"application/javascript"
;
// Apply TrustedTypes validation, if available.
let
trustedUrl
=
url
;
if
(
this
.
_ttPolicy
!=
null
)
{
trustedUrl
=
this
.
_ttPolicy
.
createScriptURL
(
url
);
}
scriptTag
.
src
=
trustedUrl
;
return
scriptTag
;
}
}
/**
* The public interface of _flutter.loader. Exposes two methods:
* * loadEntrypoint (which coordinates the default Flutter web loading procedure)
* * didCreateEngineInitializer (which is called by Flutter to notify that its
* Engine is ready to be initialized)
*/
class
FlutterLoader
{
/**
* Initializes the Flutter web app.
* @param {*} options
* @returns {Promise?} a (Deprecated) Promise that will eventually resolve
* with an EngineInitializer, or will be rejected with
* any error caused by the loader. Or Null, if the user
* supplies an `onEntrypointLoaded` Function as an option.
*/
async
loadEntrypoint
(
options
)
{
const
{
serviceWorker
,
...
entrypoint
}
=
options
||
{};
// A Trusted Types policy that is going to be used by the loader.
const
flutterTT
=
new
FlutterTrustedTypesPolicy
();
// The FlutterServiceWorkerLoader instance could be injected as a dependency
// (and dynamically imported from a module if not present).
const
serviceWorkerLoader
=
new
FlutterServiceWorkerLoader
();
serviceWorkerLoader
.
setTrustedTypesPolicy
(
flutterTT
.
policy
);
await
serviceWorkerLoader
.
loadServiceWorker
(
serviceWorker
).
catch
(
e
=>
{
// Regardless of what happens with the injection of the SW, the show must go on
console
.
warn
(
"Exception while loading service worker:"
,
e
);
});
// The FlutterEntrypointLoader instance could be injected as a dependency
// (and dynamically imported from a module if not present).
const
entrypointLoader
=
new
FlutterEntrypointLoader
();
entrypointLoader
.
setTrustedTypesPolicy
(
flutterTT
.
policy
);
// Install the `didCreateEngineInitializer` listener where Flutter web expects it to be.
this
.
didCreateEngineInitializer
=
entrypointLoader
.
didCreateEngineInitializer
.
bind
(
entrypointLoader
);
return
entrypointLoader
.
loadEntrypoint
(
entrypoint
);
}
}
_flutter
.
loader
=
new
FlutterLoader
();
})();
''';
}
}
packages/flutter_tools/lib/src/web/file_generators/flutter_service_worker_js.dart
View file @
0fb4406c
...
@@ -2,14 +2,11 @@
...
@@ -2,14 +2,11 @@
// Use of this source code is governed by a BSD-style license that can be
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// found in the LICENSE file.
import
'../../globals.dart'
as
globals
;
/// The caching strategy for the generated service worker.
/// The caching strategy for the generated service worker.
enum
ServiceWorkerStrategy
{
enum
ServiceWorkerStrategy
{
/// Download the app shell eagerly and all other assets lazily.
/// Download the app shell eagerly and all other assets lazily.
/// Prefer the offline cached version.
/// Prefer the offline cached version.
offlineFirst
,
offlineFirst
,
/// Do not generate a service worker,
/// Do not generate a service worker,
none
,
none
,
}
}
...
@@ -21,7 +18,6 @@ enum ServiceWorkerStrategy {
...
@@ -21,7 +18,6 @@ enum ServiceWorkerStrategy {
/// invalidation will automatically reactivate workers whenever a new
/// invalidation will automatically reactivate workers whenever a new
/// version is deployed.
/// version is deployed.
String
generateServiceWorker
(
String
generateServiceWorker
(
String
fileGeneratorsPath
,
Map
<
String
,
String
>
resources
,
Map
<
String
,
String
>
resources
,
List
<
String
>
coreBundle
,
{
List
<
String
>
coreBundle
,
{
required
ServiceWorkerStrategy
serviceWorkerStrategy
,
required
ServiceWorkerStrategy
serviceWorkerStrategy
,
...
@@ -29,21 +25,185 @@ String generateServiceWorker(
...
@@ -29,21 +25,185 @@ String generateServiceWorker(
if
(
serviceWorkerStrategy
==
ServiceWorkerStrategy
.
none
)
{
if
(
serviceWorkerStrategy
==
ServiceWorkerStrategy
.
none
)
{
return
''
;
return
''
;
}
}
return
'''
'
use
strict
';
const MANIFEST = '
flutter
-
app
-
manifest
';
const TEMP = '
flutter
-
temp
-
cache
';
const CACHE_NAME = '
flutter
-
app
-
cache
';
const RESOURCES = {
${resources.entries.map((MapEntry<String, String> entry) => '"${entry.key}
": "
${entry.value}
"'
).
join
(
",
\n
"
)}
};
// The application shell files that are downloaded before a service worker can
// start.
const
CORE
=
[
$
{
coreBundle
.
map
((
String
file
)
=>
'"
$file
"'
).
join
(
',
\n
'
)}];
// During install, the TEMP cache is populated with the application shell files.
self
.
addEventListener
(
"install"
,
(
event
)
=>
{
self
.
skipWaiting
();
return
event
.
waitUntil
(
caches
.
open
(
TEMP
).
then
((
cache
)
=>
{
return
cache
.
addAll
(
CORE
.
map
((
value
)
=>
new
Request
(
value
,
{
'cache'
:
'reload'
})));
})
);
});
// During activate, the cache is populated with the temp files downloaded in
// install. If this service worker is upgrading from one with a saved
// MANIFEST, then use this to retain unchanged resource files.
self
.
addEventListener
(
"activate"
,
function
(
event
)
{
return
event
.
waitUntil
(
async
function
()
{
try
{
var
contentCache
=
await
caches
.
open
(
CACHE_NAME
);
var
tempCache
=
await
caches
.
open
(
TEMP
);
var
manifestCache
=
await
caches
.
open
(
MANIFEST
);
var
manifest
=
await
manifestCache
.
match
(
'manifest'
);
// When there is no prior manifest, clear the entire cache.
if
(!
manifest
)
{
await
caches
.
delete
(
CACHE_NAME
);
contentCache
=
await
caches
.
open
(
CACHE_NAME
);
for
(
var
request
of
await
tempCache
.
keys
())
{
var
response
=
await
tempCache
.
match
(
request
);
await
contentCache
.
put
(
request
,
response
);
}
await
caches
.
delete
(
TEMP
);
// Save the manifest to make future upgrades efficient.
await
manifestCache
.
put
(
'manifest'
,
new
Response
(
JSON
.
stringify
(
RESOURCES
)));
// Claim client to enable caching on first launch
self
.
clients
.
claim
();
return
;
}
var
oldManifest
=
await
manifest
.
json
();
var
origin
=
self
.
location
.
origin
;
for
(
var
request
of
await
contentCache
.
keys
())
{
var
key
=
request
.
url
.
substring
(
origin
.
length
+
1
);
if
(
key
==
""
)
{
key
=
"/"
;
}
// If a resource from the old manifest is not in the new cache, or if
// the MD5 sum has changed, delete it. Otherwise the resource is left
// in the cache and can be reused by the new service worker.
if
(!
RESOURCES
[
key
]
||
RESOURCES
[
key
]
!=
oldManifest
[
key
])
{
await
contentCache
.
delete
(
request
);
}
}
// Populate the cache with the app shell TEMP files, potentially overwriting
// cache files preserved above.
for
(
var
request
of
await
tempCache
.
keys
())
{
var
response
=
await
tempCache
.
match
(
request
);
await
contentCache
.
put
(
request
,
response
);
}
await
caches
.
delete
(
TEMP
);
// Save the manifest to make future upgrades efficient.
await
manifestCache
.
put
(
'manifest'
,
new
Response
(
JSON
.
stringify
(
RESOURCES
)));
// Claim client to enable caching on first launch
self
.
clients
.
claim
();
return
;
}
catch
(
err
)
{
// On an unhandled exception the state of the cache cannot be guaranteed.
console
.
error
(
'Failed to upgrade service worker: '
+
err
);
await
caches
.
delete
(
CACHE_NAME
);
await
caches
.
delete
(
TEMP
);
await
caches
.
delete
(
MANIFEST
);
}
}());
});
// The fetch handler redirects requests for RESOURCE files to the service
// worker cache.
self
.
addEventListener
(
"fetch"
,
(
event
)
=>
{
if
(
event
.
request
.
method
!==
'GET'
)
{
return
;
}
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
;
});
})
})
);
});
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
;
}
});
final
String
flutterServiceWorkerJsPath
=
globals
.
localFileSystem
.
path
.
join
(
// Download offline will check the RESOURCES for all files not in the cache
fileGeneratorsPath
,
// and populate them.
'js'
,
async
function
downloadOffline
(
)
{
'flutter_service_worker.js'
,
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
;
}
throw
error
;
});
});
})
);
);
return
globals
.
localFileSystem
}
.
file
(
flutterServiceWorkerJsPath
)
''';
.
readAsStringSync
()
.
replaceAll
(
r'$$RESOURCES_MAP'
,
'{
${resources.entries.map((MapEntry<String, String> entry) => '"${entry.key}
": "
${entry.value}
"'
).
join
(
",
\n
"
)}}
',
)
.replaceAll(
r'
$
$CORE_LIST
',
'
[
$
{
coreBundle
.
map
((
String
file
)
=>
'"
$file
"'
).
join
(
',
\n
'
)}]
',
);
}
}
packages/flutter_tools/lib/src/web/file_generators/js/flutter.js
deleted
100644 → 0
View file @
ef854a3d
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
if
(
!
_flutter
)
{
var
_flutter
=
{};
}
_flutter
.
loader
=
null
;
(
function
()
{
"use strict"
;
const
baseUri
=
ensureTrailingSlash
(
getBaseURI
());
function
getBaseURI
()
{
const
base
=
document
.
querySelector
(
"base"
);
return
(
base
&&
base
.
getAttribute
(
"href"
))
||
""
;
}
function
ensureTrailingSlash
(
uri
)
{
if
(
uri
==
""
)
{
return
uri
;
}
return
uri
.
endsWith
(
"/"
)
?
uri
:
`
${
uri
}
/`
;
}
/**
* Wraps `promise` in a timeout of the given `duration` in ms.
*
* Resolves/rejects with whatever the original `promises` does, or rejects
* if `promise` takes longer to complete than `duration`. In that case,
* `debugName` is used to compose a legible error message.
*
* If `duration` is < 0, the original `promise` is returned unchanged.
* @param {Promise} promise
* @param {number} duration
* @param {string} debugName
* @returns {Promise} a wrapped promise.
*/
async
function
timeout
(
promise
,
duration
,
debugName
)
{
if
(
duration
<
0
)
{
return
promise
;
}
let
timeoutId
;
const
_clock
=
new
Promise
((
_
,
reject
)
=>
{
timeoutId
=
setTimeout
(()
=>
{
reject
(
new
Error
(
`
${
debugName
}
took more than
${
duration
}
ms to resolve. Moving on.`
,
{
cause
:
timeout
,
}
)
);
},
duration
);
});
return
Promise
.
race
([
promise
,
_clock
]).
finally
(()
=>
{
clearTimeout
(
timeoutId
);
});
}
/**
* Handles the creation of a TrustedTypes `policy` that validates URLs based
* on an (optional) incoming array of RegExes.
*/
class
FlutterTrustedTypesPolicy
{
/**
* Constructs the policy.
* @param {[RegExp]} validPatterns the patterns to test URLs
* @param {String} policyName the policy name (optional)
*/
constructor
(
validPatterns
,
policyName
=
"flutter-js"
)
{
const
patterns
=
validPatterns
||
[
/
\.
dart
\.
js$/
,
/^flutter_service_worker.js$/
];
if
(
window
.
trustedTypes
)
{
this
.
policy
=
trustedTypes
.
createPolicy
(
policyName
,
{
createScriptURL
:
function
(
url
)
{
const
parsed
=
new
URL
(
url
,
window
.
location
);
const
file
=
parsed
.
pathname
.
split
(
"/"
).
pop
();
const
matches
=
patterns
.
some
((
pattern
)
=>
pattern
.
test
(
file
));
if
(
matches
)
{
return
parsed
.
toString
();
}
console
.
error
(
"URL rejected by TrustedTypes policy"
,
policyName
,
":"
,
url
,
"(download prevented)"
);
}
});
}
}
}
/**
* Handles loading/reloading Flutter's service worker, if configured.
*
* @see: https://developers.google.com/web/fundamentals/primers/service-workers
*/
class
FlutterServiceWorkerLoader
{
/**
* Injects a TrustedTypesPolicy (or undefined if the feature is not supported).
* @param {TrustedTypesPolicy | undefined} policy
*/
setTrustedTypesPolicy
(
policy
)
{
this
.
_ttPolicy
=
policy
;
}
/**
* Returns a Promise that resolves when the latest Flutter service worker,
* configured by `settings` has been loaded and activated.
*
* Otherwise, the promise is rejected with an error message.
* @param {*} settings Service worker settings
* @returns {Promise} that resolves when the latest serviceWorker is ready.
*/
loadServiceWorker
(
settings
)
{
if
(
!
(
"serviceWorker"
in
navigator
)
||
settings
==
null
)
{
// In the future, settings = null -> uninstall service worker?
return
Promise
.
reject
(
new
Error
(
"Service worker not supported (or configured)."
)
);
}
const
{
serviceWorkerVersion
,
serviceWorkerUrl
=
`
${
baseUri
}
flutter_service_worker.js?v=
${
serviceWorkerVersion
}
`
,
timeoutMillis
=
4000
,
}
=
settings
;
// Apply the TrustedTypes policy, if present.
let
url
=
serviceWorkerUrl
;
if
(
this
.
_ttPolicy
!=
null
)
{
url
=
this
.
_ttPolicy
.
createScriptURL
(
url
);
}
const
serviceWorkerActivation
=
navigator
.
serviceWorker
.
register
(
url
)
.
then
(
this
.
_getNewServiceWorker
)
.
then
(
this
.
_waitForServiceWorkerActivation
);
// Timeout race promise
return
timeout
(
serviceWorkerActivation
,
timeoutMillis
,
"prepareServiceWorker"
);
}
/**
* Returns the latest service worker for the given `serviceWorkerRegistrationPromise`.
*
* This might return the current service worker, if there's no new service worker
* awaiting to be installed/updated.
*
* @param {Promise<ServiceWorkerRegistration>} serviceWorkerRegistrationPromise
* @returns {Promise<ServiceWorker>}
*/
async
_getNewServiceWorker
(
serviceWorkerRegistrationPromise
)
{
const
reg
=
await
serviceWorkerRegistrationPromise
;
if
(
!
reg
.
active
&&
(
reg
.
installing
||
reg
.
waiting
))
{
// No active web worker and we have installed or are installing
// one for the first time. Simply wait for it to activate.
console
.
debug
(
"Installing/Activating first service worker."
);
return
reg
.
installing
||
reg
.
waiting
;
}
else
if
(
!
reg
.
active
.
scriptURL
.
endsWith
(
serviceWorkerVersion
))
{
// When the app updates the serviceWorkerVersion changes, so we
// need to ask the service worker to update.
return
reg
.
update
().
then
((
newReg
)
=>
{
console
.
debug
(
"Updating service worker."
);
return
newReg
.
installing
||
newReg
.
waiting
||
newReg
.
active
;
});
}
else
{
console
.
debug
(
"Loading from existing service worker."
);
return
reg
.
active
;
}
}
/**
* Returns a Promise that resolves when the `latestServiceWorker` changes its
* state to "activated".
*
* @param {Promise<ServiceWorker>} latestServiceWorkerPromise
* @returns {Promise<void>}
*/
async
_waitForServiceWorkerActivation
(
latestServiceWorkerPromise
)
{
const
serviceWorker
=
await
latestServiceWorkerPromise
;
if
(
!
serviceWorker
||
serviceWorker
.
state
==
"activated"
)
{
if
(
!
serviceWorker
)
{
return
Promise
.
reject
(
new
Error
(
"Cannot activate a null service worker!"
)
);
}
else
{
console
.
debug
(
"Service worker already active."
);
return
Promise
.
resolve
();
}
}
return
new
Promise
((
resolve
,
_
)
=>
{
serviceWorker
.
addEventListener
(
"statechange"
,
()
=>
{
if
(
serviceWorker
.
state
==
"activated"
)
{
console
.
debug
(
"Activated new service worker."
);
resolve
();
}
});
});
}
}
/**
* Handles injecting the main Flutter web entrypoint (main.dart.js), and notifying
* the user when Flutter is ready, through `didCreateEngineInitializer`.
*
* @see https://docs.flutter.dev/development/platform-integration/web/initialization
*/
class
FlutterEntrypointLoader
{
/**
* Creates a FlutterEntrypointLoader.
*/
constructor
()
{
// Watchdog to prevent injecting the main entrypoint multiple times.
this
.
_scriptLoaded
=
false
;
}
/**
* Injects a TrustedTypesPolicy (or undefined if the feature is not supported).
* @param {TrustedTypesPolicy | undefined} policy
*/
setTrustedTypesPolicy
(
policy
)
{
this
.
_ttPolicy
=
policy
;
}
/**
* Loads flutter main entrypoint, specified by `entrypointUrl`, and calls a
* user-specified `onEntrypointLoaded` callback with an EngineInitializer
* object when it's done.
*
* @param {*} options
* @returns {Promise | undefined} that will eventually resolve with an
* EngineInitializer, or will be rejected with the error caused by the loader.
* Returns undefined when an `onEntrypointLoaded` callback is supplied in `options`.
*/
async
loadEntrypoint
(
options
)
{
const
{
entrypointUrl
=
`
${
baseUri
}
main.dart.js`
,
onEntrypointLoaded
}
=
options
||
{};
return
this
.
_loadEntrypoint
(
entrypointUrl
,
onEntrypointLoaded
);
}
/**
* Resolves the promise created by loadEntrypoint, and calls the `onEntrypointLoaded`
* function supplied by the user (if needed).
*
* Called by Flutter through `_flutter.loader.didCreateEngineInitializer` method,
* which is bound to the correct instance of the FlutterEntrypointLoader by
* the FlutterLoader object.
*
* @param {Function} engineInitializer @see https://github.com/flutter/engine/blob/main/lib/web_ui/lib/src/engine/js_interop/js_loader.dart#L42
*/
didCreateEngineInitializer
(
engineInitializer
)
{
if
(
typeof
this
.
_didCreateEngineInitializerResolve
===
"function"
)
{
this
.
_didCreateEngineInitializerResolve
(
engineInitializer
);
// Remove the resolver after the first time, so Flutter Web can hot restart.
this
.
_didCreateEngineInitializerResolve
=
null
;
// Make the engine revert to "auto" initialization on hot restart.
delete
_flutter
.
loader
.
didCreateEngineInitializer
;
}
if
(
typeof
this
.
_onEntrypointLoaded
===
"function"
)
{
this
.
_onEntrypointLoaded
(
engineInitializer
);
}
}
/**
* Injects a script tag into the DOM, and configures this loader to be able to
* handle the "entrypoint loaded" notifications received from Flutter web.
*
* @param {string} entrypointUrl the URL of the script that will initialize
* Flutter.
* @param {Function} onEntrypointLoaded a callback that will be called when
* Flutter web notifies this object that the entrypoint is
* loaded.
* @returns {Promise | undefined} a Promise that resolves when the entrypoint
* is loaded, or undefined if `onEntrypointLoaded`
* is a function.
*/
_loadEntrypoint
(
entrypointUrl
,
onEntrypointLoaded
)
{
const
useCallback
=
typeof
onEntrypointLoaded
===
"function"
;
if
(
!
this
.
_scriptLoaded
)
{
this
.
_scriptLoaded
=
true
;
const
scriptTag
=
this
.
_createScriptTag
(
entrypointUrl
);
if
(
useCallback
)
{
// Just inject the script tag, and return nothing; Flutter will call
// `didCreateEngineInitializer` when it's done.
console
.
debug
(
"Injecting <script> tag. Using callback."
);
this
.
_onEntrypointLoaded
=
onEntrypointLoaded
;
document
.
body
.
append
(
scriptTag
);
}
else
{
// Inject the script tag and return a promise that will get resolved
// with the EngineInitializer object from Flutter when it calls
// `didCreateEngineInitializer` later.
return
new
Promise
((
resolve
,
reject
)
=>
{
console
.
debug
(
"Injecting <script> tag. Using Promises. Use the callback approach instead!"
);
this
.
_didCreateEngineInitializerResolve
=
resolve
;
scriptTag
.
addEventListener
(
"error"
,
reject
);
document
.
body
.
append
(
scriptTag
);
});
}
}
}
/**
* Creates a script tag for the given URL.
* @param {string} url
* @returns {HTMLScriptElement}
*/
_createScriptTag
(
url
)
{
const
scriptTag
=
document
.
createElement
(
"script"
);
scriptTag
.
type
=
"application/javascript"
;
// Apply TrustedTypes validation, if available.
let
trustedUrl
=
url
;
if
(
this
.
_ttPolicy
!=
null
)
{
trustedUrl
=
this
.
_ttPolicy
.
createScriptURL
(
url
);
}
scriptTag
.
src
=
trustedUrl
;
return
scriptTag
;
}
}
/**
* The public interface of _flutter.loader. Exposes two methods:
* * loadEntrypoint (which coordinates the default Flutter web loading procedure)
* * didCreateEngineInitializer (which is called by Flutter to notify that its
* Engine is ready to be initialized)
*/
class
FlutterLoader
{
/**
* Initializes the Flutter web app.
* @param {*} options
* @returns {Promise?} a (Deprecated) Promise that will eventually resolve
* with an EngineInitializer, or will be rejected with
* any error caused by the loader. Or Null, if the user
* supplies an `onEntrypointLoaded` Function as an option.
*/
async
loadEntrypoint
(
options
)
{
const
{
serviceWorker
,
...
entrypoint
}
=
options
||
{};
// A Trusted Types policy that is going to be used by the loader.
const
flutterTT
=
new
FlutterTrustedTypesPolicy
();
// The FlutterServiceWorkerLoader instance could be injected as a dependency
// (and dynamically imported from a module if not present).
const
serviceWorkerLoader
=
new
FlutterServiceWorkerLoader
();
serviceWorkerLoader
.
setTrustedTypesPolicy
(
flutterTT
.
policy
);
await
serviceWorkerLoader
.
loadServiceWorker
(
serviceWorker
).
catch
(
e
=>
{
// Regardless of what happens with the injection of the SW, the show must go on
console
.
warn
(
"Exception while loading service worker:"
,
e
);
});
// The FlutterEntrypointLoader instance could be injected as a dependency
// (and dynamically imported from a module if not present).
const
entrypointLoader
=
new
FlutterEntrypointLoader
();
entrypointLoader
.
setTrustedTypesPolicy
(
flutterTT
.
policy
);
// Install the `didCreateEngineInitializer` listener where Flutter web expects it to be.
this
.
didCreateEngineInitializer
=
entrypointLoader
.
didCreateEngineInitializer
.
bind
(
entrypointLoader
);
return
entrypointLoader
.
loadEntrypoint
(
entrypoint
);
}
}
_flutter
.
loader
=
new
FlutterLoader
();
})();
packages/flutter_tools/lib/src/web/file_generators/js/flutter_service_worker.js
deleted
100644 → 0
View file @
ef854a3d
'use strict'
;
const
MANIFEST
=
'flutter-app-manifest'
;
const
TEMP
=
'flutter-temp-cache'
;
const
CACHE_NAME
=
'flutter-app-cache'
;
const
RESOURCES
=
$$RESOURCES_MAP
;
// The application shell files that are downloaded before a service worker can
// start.
const
CORE
=
$$CORE_LIST
;
// During install, the TEMP cache is populated with the application shell files.
self
.
addEventListener
(
"install"
,
(
event
)
=>
{
self
.
skipWaiting
();
return
event
.
waitUntil
(
caches
.
open
(
TEMP
).
then
((
cache
)
=>
{
return
cache
.
addAll
(
CORE
.
map
((
value
)
=>
new
Request
(
value
,
{
'cache'
:
'reload'
})));
})
);
});
// During activate, the cache is populated with the temp files downloaded in
// install. If this service worker is upgrading from one with a saved
// MANIFEST, then use this to retain unchanged resource files.
self
.
addEventListener
(
"activate"
,
function
(
event
)
{
return
event
.
waitUntil
(
async
function
()
{
try
{
var
contentCache
=
await
caches
.
open
(
CACHE_NAME
);
var
tempCache
=
await
caches
.
open
(
TEMP
);
var
manifestCache
=
await
caches
.
open
(
MANIFEST
);
var
manifest
=
await
manifestCache
.
match
(
'manifest'
);
// When there is no prior manifest, clear the entire cache.
if
(
!
manifest
)
{
await
caches
.
delete
(
CACHE_NAME
);
contentCache
=
await
caches
.
open
(
CACHE_NAME
);
for
(
var
request
of
await
tempCache
.
keys
())
{
var
response
=
await
tempCache
.
match
(
request
);
await
contentCache
.
put
(
request
,
response
);
}
await
caches
.
delete
(
TEMP
);
// Save the manifest to make future upgrades efficient.
await
manifestCache
.
put
(
'manifest'
,
new
Response
(
JSON
.
stringify
(
RESOURCES
)));
// Claim client to enable caching on first launch
self
.
clients
.
claim
();
return
;
}
var
oldManifest
=
await
manifest
.
json
();
var
origin
=
self
.
location
.
origin
;
for
(
var
request
of
await
contentCache
.
keys
())
{
var
key
=
request
.
url
.
substring
(
origin
.
length
+
1
);
if
(
key
==
""
)
{
key
=
"/"
;
}
// If a resource from the old manifest is not in the new cache, or if
// the MD5 sum has changed, delete it. Otherwise the resource is left
// in the cache and can be reused by the new service worker.
if
(
!
RESOURCES
[
key
]
||
RESOURCES
[
key
]
!=
oldManifest
[
key
])
{
await
contentCache
.
delete
(
request
);
}
}
// Populate the cache with the app shell TEMP files, potentially overwriting
// cache files preserved above.
for
(
var
request
of
await
tempCache
.
keys
())
{
var
response
=
await
tempCache
.
match
(
request
);
await
contentCache
.
put
(
request
,
response
);
}
await
caches
.
delete
(
TEMP
);
// Save the manifest to make future upgrades efficient.
await
manifestCache
.
put
(
'manifest'
,
new
Response
(
JSON
.
stringify
(
RESOURCES
)));
// Claim client to enable caching on first launch
self
.
clients
.
claim
();
return
;
}
catch
(
err
)
{
// On an unhandled exception the state of the cache cannot be guaranteed.
console
.
error
(
'Failed to upgrade service worker: '
+
err
);
await
caches
.
delete
(
CACHE_NAME
);
await
caches
.
delete
(
TEMP
);
await
caches
.
delete
(
MANIFEST
);
}
}());
});
// The fetch handler redirects requests for RESOURCE files to the service
// worker cache.
self
.
addEventListener
(
"fetch"
,
(
event
)
=>
{
if
(
event
.
request
.
method
!==
'GET'
)
{
return
;
}
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
;
});
})
})
);
});
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
;
}
throw
error
;
});
});
})
);
}
packages/flutter_tools/test/general.shard/build_system/targets/web_test.dart
View file @
0fb4406c
...
@@ -785,40 +785,19 @@ void main() {
...
@@ -785,40 +785,19 @@ void main() {
}));
}));
test
(
'Generated service worker is empty with none-strategy'
,
()
{
test
(
'Generated service worker is empty with none-strategy'
,
()
{
final
String
fileGeneratorsPath
=
final
String
result
=
generateServiceWorker
(<
String
,
String
>{
'/foo'
:
'abcd'
},
<
String
>[],
serviceWorkerStrategy:
ServiceWorkerStrategy
.
none
);
environment
.
artifacts
.
getArtifactPath
(
Artifact
.
flutterToolsFileGenerators
);
final
String
result
=
generateServiceWorker
(
fileGeneratorsPath
,
<
String
,
String
>{
'/foo'
:
'abcd'
},
<
String
>[],
serviceWorkerStrategy:
ServiceWorkerStrategy
.
none
,
);
expect
(
result
,
''
);
expect
(
result
,
''
);
});
});
test
(
'Generated service worker correctly inlines file hashes'
,
()
{
test
(
'Generated service worker correctly inlines file hashes'
,
()
{
final
String
fileGeneratorsPath
=
final
String
result
=
generateServiceWorker
(<
String
,
String
>{
'/foo'
:
'abcd'
},
<
String
>[],
serviceWorkerStrategy:
ServiceWorkerStrategy
.
offlineFirst
);
environment
.
artifacts
.
getArtifactPath
(
Artifact
.
flutterToolsFileGenerators
);
final
String
result
=
generateServiceWorker
(
fileGeneratorsPath
,
<
String
,
String
>{
'/foo'
:
'abcd'
},
<
String
>[],
serviceWorkerStrategy:
ServiceWorkerStrategy
.
offlineFirst
,
);
expect
(
result
,
contains
(
'{
"/foo": "abcd"
};'
));
expect
(
result
,
contains
(
'{
\n
"/foo": "abcd"
\n
};'
));
});
});
test
(
'Generated service worker includes core files'
,
()
{
test
(
'Generated service worker includes core files'
,
()
{
final
String
fileGeneratorsPath
=
final
String
result
=
generateServiceWorker
(<
String
,
String
>{
'/foo'
:
'abcd'
},
<
String
>[
'foo'
,
'bar'
],
serviceWorkerStrategy:
ServiceWorkerStrategy
.
offlineFirst
);
environment
.
artifacts
.
getArtifactPath
(
Artifact
.
flutterToolsFileGenerators
);
final
String
result
=
generateServiceWorker
(
fileGeneratorsPath
,
<
String
,
String
>{
'/foo'
:
'abcd'
},
<
String
>[
'foo'
,
'bar'
],
serviceWorkerStrategy:
ServiceWorkerStrategy
.
offlineFirst
,
);
expect
(
result
,
contains
(
'"foo",
\n
"bar"'
));
expect
(
result
,
contains
(
'"foo",
\n
"bar"'
));
});
});
...
@@ -875,10 +854,7 @@ void main() {
...
@@ -875,10 +854,7 @@ void main() {
}));
}));
test
(
'flutter.js sanity checks'
,
()
{
test
(
'flutter.js sanity checks'
,
()
{
final
String
fileGeneratorsPath
=
environment
.
artifacts
final
String
flutterJsContents
=
flutter_js
.
generateFlutterJsFile
();
.
getArtifactPath
(
Artifact
.
flutterToolsFileGenerators
);
final
String
flutterJsContents
=
flutter_js
.
generateFlutterJsFile
(
fileGeneratorsPath
);
expect
(
flutterJsContents
,
contains
(
'"use strict";'
));
expect
(
flutterJsContents
,
contains
(
'"use strict";'
));
expect
(
flutterJsContents
,
contains
(
'main.dart.js'
));
expect
(
flutterJsContents
,
contains
(
'main.dart.js'
));
expect
(
flutterJsContents
,
contains
(
'flutter_service_worker.js?v='
));
expect
(
flutterJsContents
,
contains
(
'flutter_service_worker.js?v='
));
...
@@ -897,14 +873,8 @@ void main() {
...
@@ -897,14 +873,8 @@ void main() {
await
WebBuiltInAssets
(
globals
.
fs
,
globals
.
cache
,
false
).
build
(
environment
);
await
WebBuiltInAssets
(
globals
.
fs
,
globals
.
cache
,
false
).
build
(
environment
);
// No caching of source maps.
// No caching of source maps.
final
String
fileGeneratorsPath
=
environment
.
artifacts
expect
(
environment
.
outputDir
.
childFile
(
'flutter.js'
).
readAsStringSync
(),
.
getArtifactPath
(
Artifact
.
flutterToolsFileGenerators
);
equals
(
flutter_js
.
generateFlutterJsFile
()));
final
String
flutterJsContents
=
flutter_js
.
generateFlutterJsFile
(
fileGeneratorsPath
);
expect
(
environment
.
outputDir
.
childFile
(
'flutter.js'
).
readAsStringSync
(),
equals
(
flutterJsContents
),
);
}));
}));
test
(
'wasm build copies and generates specific files'
,
()
=>
testbed
.
run
(()
async
{
test
(
'wasm build copies and generates specific files'
,
()
=>
testbed
.
run
(()
async
{
...
...
packages/flutter_tools/test/integration.shard/test_data/project.dart
View file @
0fb4406c
...
@@ -3,7 +3,6 @@
...
@@ -3,7 +3,6 @@
// found in the LICENSE file.
// found in the LICENSE file.
import
'package:file/file.dart'
;
import
'package:file/file.dart'
;
import
'package:flutter_tools/src/artifacts.dart'
;
import
'package:flutter_tools/src/web/file_generators/flutter_js.dart'
;
import
'package:flutter_tools/src/web/file_generators/flutter_js.dart'
;
import
'../test_utils.dart'
;
import
'../test_utils.dart'
;
...
@@ -60,13 +59,9 @@ abstract class Project {
...
@@ -60,13 +59,9 @@ abstract class Project {
}
}
deferredComponents
?.
setUpIn
(
dir
);
deferredComponents
?.
setUpIn
(
dir
);
final
String
fileGeneratorsPath
=
Artifacts
.
test
().
getArtifactPath
(
Artifact
.
flutterToolsFileGenerators
);
final
String
flutterJsContents
=
generateFlutterJsFile
(
fileGeneratorsPath
);
// Setup for different flutter web initializations
// Setup for different flutter web initializations
writeFile
(
fileSystem
.
path
.
join
(
dir
.
path
,
'web'
,
'index.html'
),
indexHtml
);
writeFile
(
fileSystem
.
path
.
join
(
dir
.
path
,
'web'
,
'index.html'
),
indexHtml
);
writeFile
(
fileSystem
.
path
.
join
(
dir
.
path
,
'web'
,
'flutter.js'
),
flutterJsContents
);
writeFile
(
fileSystem
.
path
.
join
(
dir
.
path
,
'web'
,
'flutter.js'
),
generateFlutterJsFile
()
);
writeFile
(
fileSystem
.
path
.
join
(
dir
.
path
,
'web'
,
'flutter_service_worker.js'
),
''
);
writeFile
(
fileSystem
.
path
.
join
(
dir
.
path
,
'web'
,
'flutter_service_worker.js'
),
''
);
writePackages
(
dir
.
path
);
writePackages
(
dir
.
path
);
await
getPackages
(
dir
.
path
);
await
getPackages
(
dir
.
path
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment