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
631087d8
Unverified
Commit
631087d8
authored
May 16, 2020
by
Jonah Williams
Committed by
GitHub
May 16, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
[flutter_tools] cache-bust in service worker (#56786)
parent
e7b6b521
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
264 additions
and
11 deletions
+264
-11
service_worker_test.dart
dev/bots/service_worker_test.dart
+143
-0
browser.dart
dev/devicelab/lib/framework/browser.dart
+23
-3
service_worker_test.dart
dev/integration_tests/web/lib/service_worker_test.dart
+19
-0
pubspec.yaml
dev/integration_tests/web/pubspec.yaml
+5
-0
index.html
dev/integration_tests/web/web/index.html
+16
-3
manifest.json
dev/integration_tests/web/web/manifest.json
+12
-0
web.dart
packages/flutter_tools/lib/src/build_system/targets/web.dart
+46
-5
No files found.
dev/bots/service_worker_test.dart
0 → 100644
View file @
631087d8
// 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.
import
'dart:async'
;
import
'dart:io'
;
import
'package:path/path.dart'
as
path
;
import
'package:flutter_devicelab/framework/browser.dart'
;
import
'package:meta/meta.dart'
;
import
'package:shelf/shelf.dart'
;
import
'package:shelf_static/shelf_static.dart'
;
import
'package:shelf/shelf_io.dart'
as
shelf_io
;
final
String
bat
=
Platform
.
isWindows
?
'.bat'
:
''
;
final
String
flutterRoot
=
path
.
dirname
(
path
.
dirname
(
path
.
dirname
(
path
.
fromUri
(
Platform
.
script
))));
final
String
flutter
=
path
.
join
(
flutterRoot
,
'bin'
,
'flutter
$bat
'
);
// Run a web service worker test. The expectations are currently stored here
// instead of in the application. This is not run on CI due to the requirement
// of having a headful chrome instance.
Future
<
void
>
main
()
async
{
await
_runWebServiceWorkerTest
(
'lib/service_worker_test.dart'
);
}
Future
<
void
>
_runWebServiceWorkerTest
(
String
target
,
{
List
<
String
>
additionalArguments
=
const
<
String
>[],
})
async
{
final
String
testAppDirectory
=
path
.
join
(
flutterRoot
,
'dev'
,
'integration_tests'
,
'web'
);
final
String
appBuildDirectory
=
path
.
join
(
testAppDirectory
,
'build'
,
'web'
);
// Build the app.
await
Process
.
run
(
flutter
,
<
String
>[
'clean'
],
workingDirectory:
testAppDirectory
,
);
await
Process
.
run
(
flutter
,
<
String
>[
'build'
,
'web'
,
'--release'
,
...
additionalArguments
,
'-t'
,
target
,
],
workingDirectory:
testAppDirectory
,
environment:
<
String
,
String
>{
'FLUTTER_WEB'
:
'true'
,
},
);
final
List
<
Uri
>
requests
=
<
Uri
>[];
final
List
<
Map
<
String
,
String
>>
headers
=
<
Map
<
String
,
String
>>[];
await
runRecordingServer
(
appUrl:
'http://localhost:8080/'
,
appDirectory:
appBuildDirectory
,
requests:
requests
,
headers:
headers
,
browserDebugPort:
null
,
);
final
List
<
String
>
requestedPaths
=
requests
.
map
((
Uri
uri
)
=>
uri
.
toString
()).
toList
();
final
List
<
String
>
expectedPaths
=
<
String
>[
// Initial page load
''
,
'main.dart.js'
,
'assets/FontManifest.json'
,
'flutter_service_worker.js'
,
'manifest.json'
,
'favicon.ico'
,
// Service worker install.
'main.dart.js'
,
'index.html'
,
'assets/LICENSE'
,
'assets/AssetManifest.json'
,
'assets/FontManifest.json'
,
''
,
// Second page load all cached.
];
print
(
'requests:
$requestedPaths
'
);
// The exact order isn't important or deterministic.
for
(
final
String
path
in
requestedPaths
)
{
if
(!
expectedPaths
.
remove
(
path
))
{
print
(
'unexpected service worker request:
$path
'
);
exit
(
1
);
}
}
if
(
expectedPaths
.
isNotEmpty
)
{
print
(
'Missing service worker requests from expected paths:
$expectedPaths
'
);
exit
(
1
);
}
}
/// This server runs a release web application and verifies that the service worker
/// caches files correctly, by checking the request resources over HTTP.
///
/// When it receives a request for `CLOSE` the server will be torn down.
///
/// Expects a path to the `build/web` directory produced from `flutter build web`.
Future
<
void
>
runRecordingServer
({
@required
String
appUrl
,
@required
String
appDirectory
,
@required
List
<
Uri
>
requests
,
@required
List
<
Map
<
String
,
String
>>
headers
,
int
serverPort
=
8080
,
int
browserDebugPort
=
8081
,
})
async
{
Chrome
chrome
;
HttpServer
server
;
final
Completer
<
void
>
completer
=
Completer
<
void
>();
Directory
userDataDirectory
;
try
{
server
=
await
HttpServer
.
bind
(
'localhost'
,
serverPort
);
final
Cascade
cascade
=
Cascade
()
.
add
((
Request
request
)
async
{
if
(
request
.
url
.
toString
().
contains
(
'CLOSE'
))
{
completer
.
complete
();
return
Response
.
notFound
(
''
);
}
requests
.
add
(
request
.
url
);
headers
.
add
(
request
.
headers
);
return
Response
.
notFound
(
''
);
})
.
add
(
createStaticHandler
(
appDirectory
,
defaultDocument:
'index.html'
));
shelf_io
.
serveRequests
(
server
,
cascade
.
handler
);
userDataDirectory
=
Directory
.
systemTemp
.
createTempSync
(
'chrome_user_data_'
);
chrome
=
await
Chrome
.
launch
(
ChromeOptions
(
headless:
false
,
debugPort:
browserDebugPort
,
url:
appUrl
,
userDataDirectory:
userDataDirectory
.
path
,
windowHeight:
500
,
windowWidth:
500
,
),
onError:
completer
.
completeError
);
await
completer
.
future
;
}
finally
{
chrome
?.
stop
();
await
server
?.
close
();
userDataDirectory
.
deleteSync
(
recursive:
true
);
}
}
dev/devicelab/lib/framework/browser.dart
View file @
631087d8
...
...
@@ -7,6 +7,7 @@ import 'dart:convert' show json, utf8, LineSplitter, JsonEncoder;
import
'dart:io'
as
io
;
import
'dart:math'
as
math
;
import
'package:path/path.dart'
as
path
;
import
'package:meta/meta.dart'
;
import
'package:webkit_inspection_protocol/webkit_inspection_protocol.dart'
;
...
...
@@ -76,8 +77,12 @@ class Chrome {
/// process encounters an error. In particular, [onError] is called when the
/// Chrome process exits prematurely, i.e. before [stop] is called.
static
Future
<
Chrome
>
launch
(
ChromeOptions
options
,
{
String
workingDirectory
,
@required
ChromeErrorCallback
onError
})
async
{
final
io
.
ProcessResult
versionResult
=
io
.
Process
.
runSync
(
_findSystemChromeExecutable
(),
const
<
String
>[
'--version'
]);
print
(
'Launching
${versionResult.stdout}
'
);
if
(!
io
.
Platform
.
isWindows
)
{
final
io
.
ProcessResult
versionResult
=
io
.
Process
.
runSync
(
_findSystemChromeExecutable
(),
const
<
String
>[
'--version'
]);
print
(
'Launching
${versionResult.stdout}
'
);
}
else
{
print
(
'Launching Chrome...'
);
}
final
bool
withDebugging
=
options
.
debugPort
!=
null
;
final
List
<
String
>
args
=
<
String
>[
...
...
@@ -217,8 +222,23 @@ String _findSystemChromeExecutable() {
return
(
which
.
stdout
as
String
).
trim
();
}
else
if
(
io
.
Platform
.
isMacOS
)
{
return
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
;
}
else
if
(
io
.
Platform
.
isWindows
)
{
const
String
kWindowsExecutable
=
r'Google\Chrome\Application\chrome.exe'
;
final
List
<
String
>
kWindowsPrefixes
=
<
String
>[
io
.
Platform
.
environment
[
'LOCALAPPDATA'
],
io
.
Platform
.
environment
[
'PROGRAMFILES'
],
io
.
Platform
.
environment
[
'PROGRAMFILES(X86)'
],
];
final
String
windowsPrefix
=
kWindowsPrefixes
.
firstWhere
((
String
prefix
)
{
if
(
prefix
==
null
)
{
return
false
;
}
final
String
expectedPath
=
path
.
join
(
prefix
,
kWindowsExecutable
);
return
io
.
File
(
expectedPath
).
existsSync
();
},
orElse:
()
=>
'.'
);
return
path
.
join
(
windowsPrefix
,
kWindowsExecutable
);
}
else
{
throw
Exception
(
'Web benchmarks cannot run on
${io.Platform.operatingSystem}
yet
.'
);
throw
Exception
(
'Web benchmarks cannot run on
${io.Platform.operatingSystem}
.'
);
}
}
...
...
dev/integration_tests/web/lib/service_worker_test.dart
0 → 100644
View file @
631087d8
// 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.
import
'dart:html'
as
html
;
Future
<
void
>
main
()
async
{
final
html
.
ServiceWorkerRegistration
worker
=
await
html
.
window
.
navigator
.
serviceWorker
.
ready
;
if
(
worker
.
active
!=
null
)
{
await
Future
.
delayed
(
const
Duration
(
seconds:
5
));
await
html
.
HttpRequest
.
getString
(
'CLOSE'
);
return
;
}
worker
.
addEventListener
(
'statechange'
,
(
event
)
async
{
if
(
worker
.
active
!=
null
)
{
await
Future
.
delayed
(
const
Duration
(
seconds:
5
));
await
html
.
HttpRequest
.
getString
(
'CLOSE'
);
}
});
}
dev/integration_tests/web/pubspec.yaml
View file @
631087d8
...
...
@@ -5,6 +5,11 @@ environment:
# The pub client defaults to an <2.0.0 sdk constraint which we need to explicitly overwrite.
sdk
:
"
>=2.6.0
<3.0.0"
flutter
:
assets
:
-
lib/a.dart
-
lib/b.dart
dependencies
:
flutter
:
sdk
:
flutter
...
...
dev/integration_tests/web/web/index.html
View file @
631087d8
...
...
@@ -2,12 +2,25 @@
<!-- 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. -->
<html
lang=
"en"
>
<html>
<head>
<meta
charset=
"UTF-8"
>
<title>
web_integration
</title>
<script
defer
src=
"main.dart.js"
type=
"application/javascript"
></script>
<meta
content=
"IE=Edge"
http-equiv=
"X-UA-Compatible"
>
<title>
Web Test
</title>
<link
rel=
"manifest"
href=
"manifest.json"
>
</head>
<body>
<!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers -->
<script>
if
(
'serviceWorker'
in
navigator
)
{
window
.
addEventListener
(
'load'
,
function
()
{
navigator
.
serviceWorker
.
register
(
'flutter_service_worker.js'
);
});
}
</script>
<script
src=
"main.dart.js"
type=
"application/javascript"
></script>
</body>
</html>
dev/integration_tests/web/web/manifest.json
0 → 100644
View file @
631087d8
{
"name"
:
"web_integration_test"
,
"short_name"
:
"web_integration_test"
,
"start_url"
:
"."
,
"display"
:
"standalone"
,
"background_color"
:
"#0175C2"
,
"theme_color"
:
"#0175C2"
,
"description"
:
"A new Flutter project."
,
"orientation"
:
"portrait-primary"
,
"prefer_related_applications"
:
false
,
"icons"
:
[]
}
packages/flutter_tools/lib/src/build_system/targets/web.dart
View file @
631087d8
...
...
@@ -378,11 +378,12 @@ class WebServiceWorker extends Target {
.
childFile
(
'flutter_service_worker.js'
);
final
Depfile
depfile
=
Depfile
(
contents
,
<
File
>[
serviceWorkerFile
]);
final
String
serviceWorker
=
generateServiceWorker
(
urlToHash
,
<
String
>[
'main.dart.js'
,
'/'
,
'main.dart.js'
,
'index.html'
,
'assets/LICENSE'
,
'assets/AssetManifest.json'
,
if
(
urlToHash
.
containsKey
(
'assets/AssetManifest.json'
))
'assets/AssetManifest.json'
,
if
(
urlToHash
.
containsKey
(
'assets/FontManifest.json'
))
'assets/FontManifest.json'
,
]);
...
...
@@ -424,7 +425,8 @@ const CORE = [
self
.
addEventListener
(
"install"
,
(
event
)
=>
{
return
event
.
waitUntil
(
caches
.
open
(
TEMP
).
then
((
cache
)
=>
{
return
cache
.
addAll
(
CORE
);
// Provide a no-cache param to ensure the latest version is downloaded.
return
cache
.
addAll
(
CORE
.
map
((
value
)
=>
new
Request
(
value
,
{
'cache'
:
'no-cache'
})));
})
);
});
...
...
@@ -443,6 +445,7 @@ self.addEventListener("activate", function(event) {
// 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
);
...
...
@@ -492,6 +495,10 @@ self.addEventListener("activate", function(event) {
self
.
addEventListener
(
"fetch"
,
(
event
)
=>
{
var
origin
=
self
.
location
.
origin
;
var
key
=
event
.
request
.
url
.
substring
(
origin
.
length
+
1
);
// Redirect URLs to the index.html
if
(
event
.
request
.
url
==
origin
||
event
.
request
.
url
.
startsWith
(
origin
+
'/#'
))
{
key
=
'/'
;
}
// If the URL is not the the RESOURCE list, skip the cache.
if
(!
RESOURCES
[
key
])
{
return
event
.
respondWith
(
fetch
(
event
.
request
));
...
...
@@ -500,8 +507,10 @@ self.addEventListener("fetch", (event) => {
.
then
((
cache
)
=>
{
return
cache
.
match
(
event
.
request
).
then
((
response
)
=>
{
// Either respond with the cached resource, or perform a fetch and
// lazily populate the cache.
return
response
||
fetch
(
event
.
request
).
then
((
response
)
=>
{
// lazily populate the cache. Ensure the resources are not cached
// by the browser for longer than the service worker expects.
var
modifiedRequest
=
new
Request
(
event
.
request
,
{
'cache'
:
'no-cache'
});
return
response
||
fetch
(
modifiedRequest
).
then
((
response
)
=>
{
cache
.
put
(
event
.
request
,
response
.
clone
());
return
response
;
});
...
...
@@ -510,5 +519,37 @@ self.addEventListener("fetch", (event) => {
);
});
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
.
message
==
'skipWaiting'
)
{
return
self
.
skipWaiting
();
}
if
(
event
.
message
=
'downloadOffline'
)
{
downloadOffline
();
}
});
// 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
in
Object
.
keys
(
RESOURCES
))
{
if
(!
currentContent
[
resourceKey
])
{
resources
.
add
(
resourceKey
);
}
}
return
Cache
.
addAll
(
resources
);
}
''';
}
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