mirror of https://github.com/flutter/samples.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
145 lines
5.5 KiB
145 lines
5.5 KiB
3 years ago
|
// 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.
|
||
|
|
||
|
/**
|
||
|
* 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
|
||
|
*/
|
||
|
|
||
|
if (!_flutter) {
|
||
|
var _flutter = {};
|
||
|
}
|
||
|
_flutter.loader = null;
|
||
|
|
||
|
(function() {
|
||
|
"use strict";
|
||
|
class FlutterLoader {
|
||
|
// TODO: Move the below methods to "#private" once supported by all the browsers
|
||
|
// we support. In the meantime, we use the "revealing module" pattern.
|
||
|
|
||
|
// Watchdog to prevent injecting the main entrypoint multiple times.
|
||
|
_scriptLoaded = null;
|
||
|
|
||
|
// Resolver for the pending promise returned by loadEntrypoint.
|
||
|
_didCreateEngineInitializerResolve = null;
|
||
|
|
||
|
/**
|
||
|
* Initializes the main.dart.js with/without serviceWorker.
|
||
|
* @param {*} options
|
||
|
* @returns a Promise that will eventually resolve with an EngineInitializer,
|
||
|
* or will be rejected with the error caused by the loader.
|
||
|
*/
|
||
|
loadEntrypoint(options) {
|
||
|
const {
|
||
|
entrypointUrl = "main.dart.js",
|
||
|
serviceWorker,
|
||
|
} = (options || {});
|
||
|
return this._loadWithServiceWorker(entrypointUrl, serviceWorker);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Resolves the promise created by loadEntrypoint. Called by Flutter.
|
||
|
* Needs to be weirdly bound like it is, so "this" is preserved across
|
||
|
* the JS <-> Flutter jumps.
|
||
|
* @param {*} engineInitializer
|
||
|
*/
|
||
|
didCreateEngineInitializer = (function(engineInitializer) {
|
||
|
if (typeof this._didCreateEngineInitializerResolve != "function") {
|
||
|
console.warn("Do not call didCreateEngineInitializer by hand. Start with loadEntrypoint instead.");
|
||
|
}
|
||
|
this._didCreateEngineInitializerResolve(engineInitializer);
|
||
|
// Remove this method after it's done, so Flutter Web can hot restart.
|
||
|
delete this.didCreateEngineInitializer;
|
||
|
}).bind(this);
|
||
|
|
||
|
_loadEntrypoint(entrypointUrl) {
|
||
|
if (!this._scriptLoaded) {
|
||
|
this._scriptLoaded = new Promise((resolve, reject) => {
|
||
|
let scriptTag = document.createElement("script");
|
||
|
scriptTag.src = entrypointUrl;
|
||
|
scriptTag.type = "application/javascript";
|
||
|
this._didCreateEngineInitializerResolve = resolve; // Cache the resolve, so it can be called from Flutter.
|
||
|
scriptTag.addEventListener("error", reject);
|
||
|
document.body.append(scriptTag);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
return this._scriptLoaded;
|
||
|
}
|
||
|
|
||
|
_waitForServiceWorkerActivation(serviceWorker, entrypointUrl) {
|
||
|
if (!serviceWorker || serviceWorker.state == "activated") {
|
||
|
if (!serviceWorker) {
|
||
|
console.warn("Cannot activate a null service worker. Falling back to plain <script> tag.");
|
||
|
} else {
|
||
|
console.debug("Service worker already active.");
|
||
|
}
|
||
|
return this._loadEntrypoint(entrypointUrl);
|
||
|
}
|
||
|
return new Promise((resolve, _) => {
|
||
|
serviceWorker.addEventListener("statechange", () => {
|
||
|
if (serviceWorker.state == "activated") {
|
||
|
console.debug("Installed new service worker.");
|
||
|
resolve(this._loadEntrypoint(entrypointUrl));
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
_loadWithServiceWorker(entrypointUrl, serviceWorkerOptions) {
|
||
|
if (!("serviceWorker" in navigator) || serviceWorkerOptions == null) {
|
||
|
console.warn("Service worker not supported (or configured). Falling back to plain <script> tag.", serviceWorkerOptions);
|
||
|
return this._loadEntrypoint(entrypointUrl);
|
||
|
}
|
||
|
|
||
|
const {
|
||
|
serviceWorkerVersion,
|
||
|
timeoutMillis = 4000,
|
||
|
} = serviceWorkerOptions;
|
||
|
|
||
|
let serviceWorkerUrl = "flutter_service_worker.js?v=" + serviceWorkerVersion;
|
||
|
let loader = navigator.serviceWorker.register(serviceWorkerUrl)
|
||
|
.then((reg) => {
|
||
|
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.
|
||
|
let sw = reg.installing || reg.waiting;
|
||
|
return this._waitForServiceWorkerActivation(sw, entrypointUrl);
|
||
|
} else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
|
||
|
// When the app updates the serviceWorkerVersion changes, so we
|
||
|
// need to ask the service worker to update.
|
||
|
console.debug("New service worker available.");
|
||
|
return reg.update().then((reg) => {
|
||
|
console.debug("Service worker updated.");
|
||
|
let sw = reg.installing || reg.waiting || reg.active;
|
||
|
return this._waitForServiceWorkerActivation(sw, entrypointUrl);
|
||
|
});
|
||
|
} else {
|
||
|
// Existing service worker is still good.
|
||
|
console.debug("Loading app from service worker.");
|
||
|
return this._loadEntrypoint(entrypointUrl);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// Timeout race promise
|
||
|
let timeout;
|
||
|
if (timeoutMillis > 0) {
|
||
|
timeout = new Promise((resolve, _) => {
|
||
|
setTimeout(() => {
|
||
|
if (!this._scriptLoaded) {
|
||
|
console.warn("Failed to load app from service worker. Falling back to plain <script> tag.");
|
||
|
resolve(this._loadEntrypoint(entrypointUrl));
|
||
|
}
|
||
|
}, timeoutMillis);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
return Promise.race([loader, timeout]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_flutter.loader = new FlutterLoader();
|
||
|
}());
|