mirror of
https://github.com/dart-lang/sdk
synced 2024-10-14 17:40:04 +00:00
[dart2js] Add retry mechanism to deferred loading when file fails to register as loaded.
In order to have the browser send a new request for each retry (each new appended script tag), a query token must be appended to the URI. We don't include any extra tokens on the initial request. Change-Id: I846660894c16345a441193cd9c7b4784364a3c54 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/311200 Reviewed-by: Sigmund Cherem <sigmund@google.com> Commit-Queue: Nate Biggs <natebiggs@google.com>
This commit is contained in:
parent
9216f830c6
commit
d9b676b0be
|
@ -2782,8 +2782,8 @@ String getIsolateAffinityTag(String name) {
|
|||
return JS('String', '#(#)', isolateTagGetter, name);
|
||||
}
|
||||
|
||||
final Map<String, Future<Null>?> _loadingLibraries = <String, Future<Null>?>{};
|
||||
final Set<String> _loadedLibraries = new Set<String>();
|
||||
final Map<String, Completer<Null>?> _loadingLibraries = {};
|
||||
final Set<String> _loadedLibraries = {};
|
||||
|
||||
typedef void DeferredLoadCallback();
|
||||
|
||||
|
@ -2881,8 +2881,9 @@ Future<Null> loadDeferredLibrary(String loadId, int priority) {
|
|||
part: uri, hash: hash, event: 'alreadyInitialized', loadId: loadId);
|
||||
continue;
|
||||
}
|
||||
// On strange scenarios, e.g. if js encounters parse errors, we might get
|
||||
// an "success" callback on the script load but the hunk will be null.
|
||||
// This check is just an extra precaution, `isHunkLoaded(hash)` should
|
||||
// always be true at this point. `_loadHunk` does this check and throws
|
||||
// for any part that failed to load.
|
||||
if (JS('bool', '#(#)', isHunkLoaded, hash)) {
|
||||
_addEvent(part: uri, hash: hash, event: 'initialize', loadId: loadId);
|
||||
JS('void', '#(#)', initializer, hash);
|
||||
|
@ -2897,11 +2898,12 @@ Future<Null> loadDeferredLibrary(String loadId, int priority) {
|
|||
}
|
||||
|
||||
Future loadAndInitialize(int i) {
|
||||
if (JS('bool', '#(#)', isHunkLoaded, hashes[i])) {
|
||||
final hash = hashes[i];
|
||||
if (JS('bool', '#(#)', isHunkLoaded, hash)) {
|
||||
waitingForLoad[i] = false;
|
||||
return new Future.value();
|
||||
}
|
||||
return _loadHunk(uris[i], loadId, priority).then((Null _) {
|
||||
return _loadHunk(uris[i], loadId, priority, hash, 0).then((Null _) {
|
||||
waitingForLoad[i] = false;
|
||||
initializeSomeLoadedHunks();
|
||||
});
|
||||
|
@ -2993,16 +2995,16 @@ Object _computePolicy() {
|
|||
/// loading is changed to use a more structured layout with subdirectories, this
|
||||
/// method will need to be updated to make the URL still clearly safe by
|
||||
/// construction.
|
||||
Object _getBasedScriptUrl(String component) {
|
||||
Object _getBasedScriptUrl(String component, String suffix) {
|
||||
final base = _thisScriptBaseUrl;
|
||||
final encodedComponent = _encodeURIComponent(component);
|
||||
final url = '$base$encodedComponent';
|
||||
final url = '$base$encodedComponent$suffix';
|
||||
final policy = _deferredLoadingTrustedTypesPolicy;
|
||||
return JS('', '#.createScriptURL(#)', policy, url);
|
||||
}
|
||||
|
||||
Object getBasedScriptUrlForTesting(String component) =>
|
||||
_getBasedScriptUrl(component);
|
||||
_getBasedScriptUrl(component, '');
|
||||
|
||||
String _encodeURIComponent(String component) {
|
||||
return JS('', 'self.encodeURIComponent(#)', component);
|
||||
|
@ -3057,17 +3059,21 @@ String _computeThisScriptFromTrace() {
|
|||
throw new UnsupportedError('Cannot extract URI from "$stack"');
|
||||
}
|
||||
|
||||
Future<Null> _loadHunk(String hunkName, String loadId, int priority) {
|
||||
Future<Null> _loadHunk(
|
||||
String hunkName, String loadId, int priority, String hash, int retryCount) {
|
||||
const int maxRetries = 3;
|
||||
var initializationEventLog = JS_EMBEDDED_GLOBAL('', INITIALIZATION_EVENT_LOG);
|
||||
|
||||
var future = _loadingLibraries[hunkName];
|
||||
var completer = _loadingLibraries[hunkName];
|
||||
_addEvent(part: hunkName, event: 'startLoad', loadId: loadId);
|
||||
if (future != null) {
|
||||
if (completer != null && retryCount == 0) {
|
||||
_addEvent(part: hunkName, event: 'reuse', loadId: loadId);
|
||||
return future.then((Null _) => null);
|
||||
return completer.future;
|
||||
}
|
||||
completer ??= _loadingLibraries[hunkName] = Completer();
|
||||
|
||||
Object trustedScriptUri = _getBasedScriptUrl(hunkName);
|
||||
Object trustedScriptUri = _getBasedScriptUrl(
|
||||
hunkName, retryCount > 0 ? '?dart2jsRetry=$retryCount' : '');
|
||||
// [trustedScriptUri] is either a String, in which case `toString()` is an
|
||||
// identity function, or it is a TrustedScriptURL and `toString()` returns the
|
||||
// sanitized URL.
|
||||
|
@ -3076,22 +3082,33 @@ Future<Null> _loadHunk(String hunkName, String loadId, int priority) {
|
|||
_addEvent(part: hunkName, event: 'download', loadId: loadId);
|
||||
|
||||
var deferredLibraryLoader = JS('', 'self.dartDeferredLibraryLoader');
|
||||
Completer<Null> completer = Completer();
|
||||
|
||||
void success() {
|
||||
_addEvent(part: hunkName, event: 'downloadSuccess', loadId: loadId);
|
||||
completer.complete(null);
|
||||
}
|
||||
|
||||
void failure(error, String context, StackTrace? stackTrace) {
|
||||
_addEvent(part: hunkName, event: 'downloadFailure', loadId: loadId);
|
||||
_loadingLibraries[hunkName] = null;
|
||||
stackTrace ??= StackTrace.current;
|
||||
completer.completeError(
|
||||
DeferredLoadException('Loading $uriAsString failed: $error\n'
|
||||
'Context: $context\n'
|
||||
'event log:\n${_getEventLog()}\n'),
|
||||
stackTrace);
|
||||
if (retryCount < maxRetries) {
|
||||
_addEvent(part: hunkName, event: 'retry$retryCount', loadId: loadId);
|
||||
_loadHunk(hunkName, loadId, priority, hash, retryCount + 1);
|
||||
} else {
|
||||
_addEvent(part: hunkName, event: 'downloadFailure', loadId: loadId);
|
||||
_loadingLibraries[hunkName] = null;
|
||||
stackTrace ??= StackTrace.current;
|
||||
completer!.completeError(
|
||||
DeferredLoadException('Loading $uriAsString failed: $error\n'
|
||||
'Context: $context\n'
|
||||
'event log:\n${_getEventLog()}\n'),
|
||||
stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
void success() {
|
||||
var isHunkLoaded = JS_EMBEDDED_GLOBAL('', IS_HUNK_LOADED);
|
||||
|
||||
if (JS('bool', '#(#)', isHunkLoaded, hash)) {
|
||||
_addEvent(part: hunkName, event: 'downloadSuccess', loadId: loadId);
|
||||
completer!.complete(null);
|
||||
} else {
|
||||
failure(
|
||||
'Success callback invoked but part $hunkName not loaded.', '', null);
|
||||
}
|
||||
}
|
||||
|
||||
var jsSuccess = convertDartClosureToJS(success, 0);
|
||||
|
@ -3160,7 +3177,6 @@ Future<Null> _loadHunk(String hunkName, String loadId, int priority) {
|
|||
JS('', '#.addEventListener("error", #, false)', script, jsFailure);
|
||||
JS('', 'document.body.appendChild(#)', script);
|
||||
}
|
||||
_loadingLibraries[hunkName] = completer.future;
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,20 +14,31 @@ main() {
|
|||
// invocation.
|
||||
js.context.callMethod("eval", [
|
||||
"""
|
||||
retryCount = 0;
|
||||
if (self.document) {
|
||||
oldAppendChild = document.body.appendChild;
|
||||
document.body.appendChild = function(element) {
|
||||
replacement = function(element) {
|
||||
element.src = "non_existing.js";
|
||||
document.body.appendChild = oldAppendChild;
|
||||
document.body.appendChild(element);
|
||||
if (retryCount < 3) {
|
||||
retryCount++;
|
||||
document.body.appendChild = replacement;
|
||||
}
|
||||
}
|
||||
document.body.appendChild = replacement;
|
||||
}
|
||||
if (self.load) {
|
||||
oldLoad = load;
|
||||
load = function(uri) {
|
||||
replacement = function(uri) {
|
||||
load = oldLoad;
|
||||
load("non_existing.js");
|
||||
if (retryCount < 3) {
|
||||
retryCount++;
|
||||
load = replacement;
|
||||
}
|
||||
}
|
||||
load = replacement;
|
||||
}
|
||||
"""
|
||||
]);
|
||||
|
|
|
@ -16,20 +16,31 @@ main() {
|
|||
// invocation.
|
||||
js.context.callMethod("eval", [
|
||||
"""
|
||||
retryCount = 0;
|
||||
if (self.document) {
|
||||
oldAppendChild = document.body.appendChild;
|
||||
document.body.appendChild = function(element) {
|
||||
replacement = function(element) {
|
||||
element.src = "non_existing.js";
|
||||
document.body.appendChild = oldAppendChild;
|
||||
document.body.appendChild(element);
|
||||
if (retryCount < 3) {
|
||||
retryCount++;
|
||||
document.body.appendChild = replacement;
|
||||
}
|
||||
}
|
||||
document.body.appendChild = replacement;
|
||||
}
|
||||
if (self.load) {
|
||||
oldLoad = load;
|
||||
load = function(uri) {
|
||||
replacement = function(uri) {
|
||||
load = oldLoad;
|
||||
load("non_existing.js");
|
||||
if (retryCount < 3) {
|
||||
retryCount++;
|
||||
load = replacement;
|
||||
}
|
||||
}
|
||||
load = replacement;
|
||||
}
|
||||
"""
|
||||
]);
|
||||
|
|
Loading…
Reference in a new issue