offscreen Permission
8 min readoffscreen Permission
The offscreen permission enables the Chrome chrome.offscreen API, which allows extensions to create offscreen documents for performing operations that require DOM access from the extension’s service worker context. This is a Manifest V3-only feature that replaces the background page DOM access available in Manifest V2.
Overview
| Property | Value |
|---|---|
| Permission string | "offscreen" |
| API | chrome.offscreen |
| Minimum Chrome version | 94+ |
| Manifest requirement | Must be declared in permissions array |
The offscreen document is a hidden HTML document that runs in the context of your extension but has no visible UI. It provides a way to perform DOM-related operations from the service worker, which otherwise has no access to the DOM.
API Methods
createDocument
Creates an offscreen document with the specified parameters:
chrome.offscreen.createDocument({
url: 'offscreen.html',
reasons: [chrome.offscreen.Reason.DOM_SCRAPING],
justification: 'Need to parse HTML content for data extraction'
});
Parameters:
url(string): Path to the HTML file relative to the extension rootreasons(array): Array ofchrome.offscreen.Reasonenum valuesjustification(string): Explanation of why the offscreen document is needed (required for Chrome Web Store review)
closeDocument
Closes the currently open offscreen document:
await chrome.offscreen.closeDocument();
hasDocument
Checks whether an offscreen document currently exists (Chrome 116+):
const exists = await chrome.offscreen.hasDocument();
Reason Enum Values
The chrome.offscreen.Reason enum provides various reasons for creating an offscreen document. Chrome supports the following reasons:
| Reason | Description |
|---|---|
TESTING |
Used for automated testing scenarios |
AUDIO_PLAYBACK |
Audio playback from background context |
IFRAME_SCRIPTING |
Scripting iframes from background |
DOM_SCRAPING |
Parsing and extracting data from HTML |
BLOBS |
Working with Blob objects |
DOM_PARSER |
HTML/XML parsing operations |
USER_MEDIA |
Capturing user media (webcam/microphone) |
DISPLAY_MEDIA |
Capturing display media |
WEB_RTC |
WebRTC operations |
CLIPBOARD |
Clipboard read/write operations |
LOCAL_STORAGE |
Local storage operations |
WORKERS |
Web Worker management |
BATTERY_STATUS |
Battery API access |
MATCH_MEDIA |
Media query matching |
GEOLOCATION |
Geolocation API access |
Constraints
One Document Limit
Only one offscreen document can exist at a time per extension. Attempting to create a new one while one already exists will result in an error. Always check with hasDocument() before creating:
async function createOffscreenIfNeeded() {
if (await chrome.offscreen.hasDocument()) {
return;
}
await chrome.offscreen.createDocument({
url: 'offscreen.html',
reasons: [chrome.offscreen.Reason.DOM_SCRAPING],
justification: 'Need to parse HTML for data extraction'
});
}
Reason Requirement
At least one reason must be specified when creating the document. Providing an empty array will cause an error.
No UI Visibility
The offscreen document has no visible UI to the user. It exists purely in memory and is not rendered in any window.
Communication via Messaging
Since the offscreen document runs in isolation, communication with your service worker or content scripts must use chrome.runtime messaging:
// In service worker
chrome.runtime.sendMessage({ action: 'doWork', data: '...' });
// In offscreen document
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
// Handle message and respond
sendResponse({ result: '...' });
});
Manifest Declaration
To use the offscreen API, declare the permission in your manifest.json:
{
"name": "My Extension",
"version": "1.0",
"manifest_version": 3,
"permissions": [
"offscreen"
],
"background": {
"service_worker": "background.js"
}
}
Note: The offscreen permission is only available in Manifest V3. It is not available in Manifest V2.
Use Cases
DOM Parsing
Parse HTML content that cannot be handled directly in the service worker:
// In service worker
async function parseHTML(html) {
await chrome.offscreen.createDocument({
url: 'offscreen.html',
reasons: [chrome.offscreen.Reason.DOM_PARSER],
justification: 'Parsing HTML content for extraction'
});
// Send HTML to offscreen document for parsing
chrome.runtime.sendMessage({
type: 'PARSE_HTML',
html: html
});
// Wait for response (implementation details vary)
await chrome.offscreen.closeDocument();
}
Audio Playback
Play audio from the background service worker context:
async function playAudio(audioUrl) {
await chrome.offscreen.createDocument({
url: 'audio-player.html',
reasons: [chrome.offscreen.Reason.AUDIO_PLAYBACK],
justification: 'Playing audio notifications from background'
});
chrome.runtime.sendMessage({
type: 'PLAY_AUDIO',
url: audioUrl
});
}
Clipboard Operations
Perform advanced clipboard operations:
async function readClipboard() {
if (await chrome.offscreen.hasDocument()) {
await chrome.offscreen.closeDocument();
}
await chrome.offscreen.createDocument({
url: 'clipboard.html',
reasons: [chrome.offscreen.Reason.CLIPBOARD],
justification: 'Reading clipboard content'
});
return new Promise((resolve) => {
chrome.runtime.onMessage.addListener(function onMessage(message) {
if (message.type === 'CLIPBOARD_CONTENT') {
chrome.runtime.onMessage.removeListener(onMessage);
resolve(message.content);
}
});
});
}
Geolocation
Access geolocation from the background context:
async function getLocation() {
await chrome.offscreen.createDocument({
url: 'geolocation.html',
reasons: [chrome.offscreen.Reason.GEOLOCATION],
justification: 'Tracking user location for notifications'
});
return new Promise((resolve) => {
chrome.runtime.onMessage.addListener(function onMessage(message) {
if (message.type === 'LOCATION') {
chrome.runtime.onMessage.removeListener(onMessage);
resolve(message.coords);
}
});
});
}
Code Examples
Guard Pattern
Always check if an offscreen document exists before creating:
async function ensureOffscreen(reason) {
if (await chrome.offscreen.hasDocument()) {
return;
}
await chrome.offscreen.createDocument({
url: 'offscreen.html',
reasons: [reason],
justification: 'Required for extension functionality'
});
}
Complete Workflow
A complete example of creating, communicating, and closing:
async function scrapePageData(url) {
// Create offscreen document
await chrome.offscreen.createDocument({
url: 'offscreen.html',
reasons: [chrome.offscreen.Reason.DOM_SCRAPING],
justification: 'Scraping page content for data extraction'
});
// Send message to offscreen document
const response = await chrome.runtime.sendMessage({
type: 'SCRAPE_URL',
url: url
});
// Close the document when done
await chrome.offscreen.closeDocument();
return response.data;
}
Cross-references
- MV3 Offscreen Documents - Detailed guide on using offscreen documents
- Offscreen Document Patterns - Common patterns and best practices
- Geolocation Permission - Related permission for location access
See Also
Frequently Asked Questions
What are offscreen documents in Manifest V3?
Offscreen documents are hidden pages that extension can create to perform tasks that require a DOM, like playing audio or using certain APIs.
How do I create an offscreen document?
Use chrome.offscreen.createDocument() with a specified reason and the HTML file to load. Only one offscreen document can exist per extension at a time. —
Part of the Chrome Extension Guide by theluckystrike. Built at zovo.one.