# Integration of ServiceWorker
# Purpose
This section aims to provide developers a comprehensive guide for integrating and consolidating the built-in ServiceWorker functionality in the SDK at the application layer. It addresses the ServiceWorker conflict issues and common problems.
Although the browser allows multiple Service Workers to be registered in the same domain, these Service Workers must have different scopes. In this way, each Service Worker can independently control the resources under different scopes. However, MessageWorker.js
occupies a scope (default is '/'), which will cause the application layer's Service Worker to fail to register successfully in the same scope. Therefore, when the Service Worker at the application layer needs to share the same scope with the SDK's built-in MessageWorker.js
, they need to be merged into a single file.
This section will introduce how to merge and customize parameters related to Service Worker configuration.
# Introduction to ServiceWorker
Service Worker is a script that runs in the background of a browser, which can be used to implement offline capabilities, push notifications, background synchronization, and other features. After the webpage is fully loaded, it operates in the background and can intercept network requests, cache resources, and provide other functions.
Key features of ServiceWorker:
- Offline Capabilities: ServiceWorker can cache resources, allowing users to access websites even when the network is disconnected.
- Push Notifications: ServiceWorker can receive push notifications from the server and display messages to the user.
- Background Synchronization: ServiceWorker can perform tasks in the background, such as uploading data or updating data.
- Network Interception: ServiceWorker can intercept network requests and process them as needed, such as caching data or modifying request headers.
# Overview of MessageWorker.js
MessageWorker.js
is a Service Worker script provided by the SDK to implement synchronous communication in the Web Worker layer. Since MessageWorker.js
occupies an independent scope, when the application layer's Service Worker needs to use the same scope, the two need to be merged into a single file.
# Environment Setup
Before starting to integrate Service Worker, please ensure your development environment meets the following requirements:
Browser supporting Service Worker: Ensure your target browser supports Service Worker, such as Chrome, Firefox, and Edge. For more specific information, you can refer to Can I use (opens new window).
@foxitsoftware/foxit-pdf-sdk-for-web-library
: The version must be10.0.0
or higher, and thelib
directory should contain theMessageWorker.js
file.HTTPS support: Your website must be accessed via HTTPS to use Service Worker. However, there are exceptions for the development environment.
localhost
and127.0.0.1
can use Service Worker without enabling HTTPS access.
# Registration and Configuration of Service Worker
Starting from 10.0.0
, a new messageSyncServiceWorker
(opens new window) parameter has been added to the PDFViewer
constructor arguments, used for specifying the registration method of the Service Worker.
There are two ways to use messageSyncServiceWorker
:
Method 1: Specify
url
andoptions
:url
is the registration path for the Service Worker;options
are the registration options for the Service Worker, you can refer to the specific description in MDN (opens new window).
const viewer = new PDFViewer({ messageSyncServiceWorker: { url: '/your-service-worker.js', options: { // Optional scope: '/foxit-lib/' } } // ... Other parameters })
Method 2: Specify
registration
:registration
is thePromise<ServiceWorkerRegistration>
object returned by thenavigator.serviceWorker.register()
method.
const viewer = new PDFViewer({ messageSyncServiceWorker: { registration: navigator.serviceWorker.register('/your-service-worker.js', { scope: '/foxit-lib/' }) } // ... Other parameters })
Method 1 will be determined by the SDK internally as to when to register the ServiceWorker. If you need to manually control the registration of the ServiceWorker, you must use method 2.
In the SDK release package, we have provided a complete example (/examples/PDFViewCtrl/integrate-service-worker
). You can directly refer to its implementation and run and view the effects according to the README.md document.
# Service-Worker-Allowed
Response Header
By default, the max scope allowed by a Service Worker is determined by its script location. Specifically, the scope of the Service Worker can only cover the directory where its script is located and its subdirectories. For example, if the Service Worker script is located at https://example.com/sub/worker.js
, it can only control resources under https://example.com/sub/
and its subpaths by default. If you forcibly specify the scope parameter to a larger scope, it will lead to the failure of the Service Worker registration, and the error message will be: The path of the provided scope ('/') is not under the max scope allowed ('/sub/')
.
However, in some cases, you may want to expand the scope of the Service Worker so that it can control resources within a larger range. At this point, the Service-Worker-Allowed
response header becomes particularly important. By configuring this response header, you can specify a broader path, allowing the Service Worker to be effective within a larger scope.
# Configuring Service-Worker-Allowed
Response Header
To use the Service-Worker-Allowed
response header, you need to include the following field in the HTTP response header of the Service Worker script. Its value is the max scope path allowed:
Service-Worker-Allowed /;
# Nginx Configuration Example
If you are using Nginx as your server, you can include the Service-Worker-Allowed
response header by modifying the Nginx configuration file. Below is a configuration example:
server {
location /sw.js {
add_header Service-Worker-Allowed /;
}
}
# Webpack Dev Server Configuration Example
If you are using Webpack Dev Server for local development, you can include the Service-Worker-Allowed
response header by configuring devServer. Below is a configuration example:
// webpack.config.js
module.exports = {
// Other configurations
devServer: {
headers: {
'Service-Worker-Allowed': '/'
}
}
};
# vue.config.js Configuration Example
If you are using Vue CLI, you can adjust the Webpack Dev Server by modifying vue.config.js
. Below is a configuration example:
// vue.config.js
module.exports = {
devServer: {
headers: {
'Service-Worker-Allowed': '/'
}
}
};
# Special Request Address
In the fetch
event listened by the Service Worker in the application layer, if the request address matches __foxitwebsdk-syncmsg__
, please ignore this request directly. This is also mentioned in our example code (examples/PDFViewCtrl/integrate-service-worker/src/service-worker.js
).
# Common Questions and Troubleshooting
ServiceWorker Not Registered
Issue description: Unable to find the ServiceWorker registration results in the developer tools.
Possible reasons:
- The path is set incorrectly, and the Service Worker js request returns 404 or other errors.
- The browser does not support ServiceWorker.
- The HTTPS protocol is not enabled. For security reasons, ServiceWorker can only be used under the HTTPS protocol. An exception to this is localhost and 127.0.0.1, which do not require HTTPS.
Solutions:
- Check the Service Worker's registration code and path configurations.
- Check browser compatibility. For Service Worker compatibility, please refer to Can I use (opens new window).
- Enable HTTPS protocol.
ServiceWorker Registration Failed
- Issue description:A prompt during registration indicates that the scope exceeds the max scope allowed. Example of error message:
register a ServiceWorker for scope ('http://localhost:9899/') with script ('http://localhost:9899/lib/MessageWorker.js?b=http://localhost:9899/__foxitwebsdk-syncmsg__'): The path of the provided scope ('/') is not under the max scope allowed ('/lib/'). Adjust the scope, move the Service Worker script, or use the Service-Worker-Allowed HTTP header to allow the scope.
Possible reasons: The registration path or scope of the Service Worker is not set correctly. It should be noted that the max scope allowed by the Service Worker depends on the location of the Service Worker script itself (refer to: MDN (opens new window)).
Solutions:
- Understand the scope rules of Service Worker.
- Check the registration code and scope settings of Service Worker.
- Adjust the scope of Service Worker, that is, modify the scope (opens new window) parameter.
- Adjust the
Service-Worker-Allowed
HTTP response header to allow a larger scope. - Check the path of Service Worker script and server configuration.
ServiceWorker Registration Indicates Unsupported MIME Type
- Issue description: A prompt during registration indicates an unsupported MIME type ('***/***'). Example of error message:
Failed to register a ServiceWorker for scope ('http://localhost:5173/assets/') with script ('http://localhost:5173/assets/service-worker.js'): The script has an unsupported MIME type ('text/html').
- Possible reasons: The registration path of the ServiceWorker is incorrect or the target file does not exist.
- Solutions:
- Check the registration path of the ServiceWorker.
- Check if the target file exists.
- Check the server configuration.
Browser Forced Refresh Prevents ServiceWorker from Intercepting Requests
- Issue Description: Refer to the MDN: ServiceWorkerContainer: controller property (opens new window) documentation, which describes that after a user performs a forced refresh (Shift + Refresh) of the page, the service worker loses control of the page, and the
navigator.serviceWorker.controller
property is set to null. - Possible Cause: The built-in
MessageWorker.js
of the SDK has automatically handled this issue. If your ServiceWorker code does not reuse MessageWorker.js and does not handle this situation, this phenomenon may occur. - Solution:
- After successfully registering the serviceWorker, check if
navigator.serviceWorker.controller
is null. - If it is null, send a message to the ServiceWorker via the registration (opens new window).active.postMessage method, for example, the message we send is:
{id: 'clientsClaim'}
. - Listen for this message in the ServiceWorker context and call the
clients.claim()
method after receiving the message.
- After successfully registering the serviceWorker, check if
Here is a simple example:
main.js:
const registration = await navigator.serviceWorker.register( "service-worker.js" /* ... more options */ ); if (!navigator.serviceWorker.controller && registration.active) { function onControllerChange() { navigator.serviceWorker.removeEventListener( "controllerchange", onControllerChange ); if (navigator.serviceWorker.controller) { resolve(true); } } navigator.serviceWorker.addEventListener( "controllerchange", onControllerChange ); registration.active.postMessage({ id: "clientsClaim" }); } const pdfViewer = new PDFViewCtrl.PDFViewer({ messageSyncServiceWorker: { registration: registration, } }
service-worker.js
// ...... self.addEventListener("message", (e) => { if (e.data?.id === "clientsClaim") { // Set the current service worker as the controller for all clients within its scope clients.claim(); } });
- Issue Description: Refer to the MDN: ServiceWorkerContainer: controller property (opens new window) documentation, which describes that after a user performs a forced refresh (Shift + Refresh) of the page, the service worker loses control of the page, and the