Safari Browser Scripts PRO
Safari Browser Scripts let Scripting run userscripts in Safari through the Safari Web Extension. The runtime supports Greasemonkey-style GM.* APIs and selected Scripting APIs.
Where Scripts Run
There are two sources:
browser.tsx in a Scripting script project. It is built to browser.js.
- Installed
.user.js / .js files from Safari's extension popup.
Scripts installed from Safari's extension popup are stored under:
scripting-safari-extension/userscripts/
Downloads and GM storage live in the same root:
scripting-safari-extension/downloads/
scripting-safari-extension/storages/
This root follows the Safari Browser Data storage location configured in Settings, including WebDAV when enabled.
Create or install a .user.js / .js file with a userscript metadata block:
// ==UserScript==
// @name GitHub Demo
// @match https://github.com/*
// @grant GM.log
// @grant Scripting.FileManager
// ==/UserScript==
GM.log("loaded", location.href)
Common metadata keys:
// @name
// @namespace
// @version
// @description
// @author
// @match
// @include
// @exclude
// @exclude-match
// @connect
// @grant
// @require
// @resource
// @run-at document-start | document-end | document-idle
// @inject-into content | page
// @weight 1..999
// @noframes
// @homepageURL
// @supportURL
// @updateURL
// @downloadURL
// @license
@weight controls execution order when multiple scripts match the page. Larger weights run first. If no @run-at is provided, scripts run at document-end.
Use @inject-into page only for page-context scripts that do not need privileged APIs. GM.*, Scripting.*, and extension messaging are only available in content-context scripts.
Permissions
Privileged APIs must be declared with @grant.
// @grant GM.getValue
// @grant GM.setValue
// @grant GM.xmlHttpRequest
// @grant GM.cookie
// @grant Scripting.FileManager
@grant none disables GM APIs, but GM_info and GM.info remain available for compatibility.
Cross-origin network, download, resource, and cookie access must be allowed with @connect.
// @connect api.github.com
// @connect https://api.github.com/*
// @connect *
When a required grant or connect rule is missing, the runtime throws an error with a stable code such as permissionDenied or connectDenied.
GM_info
GM_info and GM.info expose parsed metadata and runtime details:
GM_info.script.name
GM_info.script.version
GM_info.script.matches
GM_info.script.connects
GM_info.script.runAt
GM_info.script.injectInto
GM_info.script.weight
GM_info.scriptHandler
GM_info.version
Supported GM APIs
Storage
await GM.getValue(key, defaultValue)
await GM.setValue(key, value)
await GM.deleteValue(key)
await GM.listValues()
const id = GM.addValueChangeListener(key, (key, oldValue, newValue, remote) => {})
GM.removeValueChangeListener(id)
GM storage is stored as JSON under scripting-safari-extension/storages/.
DOM and Menu
GM.log(...items)
GM.addStyle(css)
GM.addElement("div", { textContent: "Hello" })
GM.addElement(document.body, "button", { textContent: "Run" })
const id = GM.registerMenuCommand("Run", () => {})
GM.unregisterMenuCommand(id)
GM.removeMenuCommand(id)
Menu commands are shown in Safari's extension popup for the current page.
Tabs, Clipboard, Notification
const tab = await GM.openInTab("https://github.com/trending", { active: false })
tab.close()
await GM.closeTab()
await GM.setClipboard("Hello")
await GM.notification({ title: "Scripting", text: "Done" })
GM.closeTab() closes the current tab when supported by Safari. GM.openInTab() returns a tab control with close().
Resources
const text = await GM.getResourceText("name")
const url = await GM.getResourceURL("name")
Declare resources in the metadata block:
// @resource name https://example.com/file.txt
Download
await GM.download({
url: "https://example.com/file.txt",
name: "file.txt",
onload(response) {
GM.log(response.path)
}
})
Downloaded files are stored under scripting-safari-extension/downloads/.
XHR
await GM.xmlHttpRequest({
method: "GET",
url: "https://api.github.com/zen",
responseType: "text",
overrideMimeType: "text/plain",
onloadstart(event) {},
onprogress(event) {},
onreadystatechange(response) {},
onload(response) {},
onloadend(response) {}
})
The runtime supports responseType values text, json, arraybuffer, blob, and document, plus user, password, headers, data, timeout, binary, overrideMimeType, upload callbacks, finalUrl, and responseURL.
Cookies
await GM.cookie.set({
url: location.href,
name: "scripting_test",
value: "1",
path: "/",
secure: true
})
const cookies = await GM.cookie.list({ url: location.href, name: "scripting_test" })
await GM.cookie.delete({ url: location.href, name: "scripting_test" })
Callback style is also supported:
GM.cookie.list({ url: location.href }, cookies => {
GM.log(cookies)
})
Scripting.FileManager
Use @grant Scripting.FileManager or @grant Scripting.* to access the API.
Properties
Scripting.FileManager.documentsDirectory: string
Scripting.FileManager.iCloudDocumentsDirectory: string | null
Scripting.FileManager.appGroupDocumentsDirectory: string | null
Scripting.FileManager.safariBrowserDirectory: string
Scripting.FileManager.safariBrowserStorageDirectory: string
Scripting.FileManager.safariBrowserDownloadsDirectory: string
Scripting.FileManager.safariBrowserUserscriptsDirectory: string
Scripting.FileManager.isiCloudEnabled: boolean
In regular Scripting app scripts, the same Safari data directories are available as FileManager.safariBrowserDirectory, FileManager.safariBrowserStorageDirectory, FileManager.safariBrowserDownloadsDirectory, and FileManager.safariBrowserUserscriptsDirectory.
Methods
await Scripting.FileManager.readAsString(path)
await Scripting.FileManager.writeAsString(path, contents)
await Scripting.FileManager.createDirectory(path, true)
await Scripting.FileManager.readDirectory(path)
await Scripting.FileManager.exists(path)
await Scripting.FileManager.remove(path)
All file operations are limited to directories exposed by Scripting.FileManager.
Installed Scripts
Safari's extension popup can install userscripts from the current page or from a URL. Installed scripts can be enabled, disabled, updated, or deleted in the popup and in Tools > Development > Safari Browser Scripts.
Use the Tools page to inspect:
- Installed userscripts.
- GM storage JSON files.
- Downloads created by
GM.download.
Example
// ==UserScript==
// @name Save Page URL
// @match https://github.com/*
// @grant GM.log
// @grant Scripting.FileManager
// ==/UserScript==
const fm = Scripting.FileManager
const dir = `${fm.appGroupDocumentsDirectory ?? fm.documentsDirectory}/Safari Notes`
const file = `${dir}/last-url.txt`
await fm.createDirectory(dir)
await fm.writeAsString(file, location.href)
GM.log(await fm.readAsString(file))