forked from mdn/webextensions-examples
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* New user script example This PR is the content from [PR 426](mdn#426), originally submitted by Irene, addressing the feedback on the readme content. * Including the following changes: * renaming the content script example and updating the readme file to reference the user script example * renaming the user script example and versioning as v1 * addressing feedback on the user script example: limiting input to hosts and script, addressing code and HTML/CSS comments I haven't changed the "eating" script example, I feel it is useful to have similar demonstrations in the content and user script examples to make it easier for developers to compare and contrast. * Various changes for feedback * Added a user script ID that is stored in the registered script's metadata and then used in the API script to key the data stored in local storage * Update to emphasize scoping of local storage using an ID provided in the user script metadata Co-authored-by: Richard Bloor <rbloor@atlassian.com>
- Loading branch information
Showing
18 changed files
with
285 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,24 +1,26 @@ | ||
# User script | ||
# Content script registration | ||
|
||
This extension demonstrates the `browser.contentScripts.register()` API, which enables an extension to register URL-matching content scripts at runtime. | ||
|
||
The contentScripts.register() API is intended to enable an extension to register scripts that are packaged in the extension. If you want to register third-party user scripts, use the [userScripts API](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/userScripts). Please refer to [user script registration](https://github.com/mdn/webextensions-examples/tree/master/user-script-register) for an example of that API. | ||
|
||
This extension adds a browser action that shows a popup. The popup lets you specify: | ||
|
||
* some code that comprises your content script | ||
* one or more [match patterns](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Match_patterns), comma-separated. The content script will only be loaded into pages whose URLs match these patterns. | ||
|
||
Once these are set up you can register the content script by clicking "Register script". The extension will then register a content script with the given properties by calling `browser.contentScripts.register()`. | ||
|
||
To keep things simple, you can only have one script registered at any time: if you click "Register script" again, the old script is unregistered. To do this, the extension keeps a reference to the `RegisteredContentScript` object returned from `browser.contentScripts.register()`: this object provides the `unregister()` method. | ||
To keep things simple, you can only have one script registered at any time: if you click "Register script" again, the active script is unregistered. To do this, the extension keeps a reference to the `RegisteredContentScript` object returned from `browser.contentScripts.register()`: this object provides the `unregister()` method. | ||
|
||
Note that the extension uses a background script to register the content scripts and to keep a reference to the `RegisteredContentScript` object. If it did not do this, then as soon as the popup window closed, the `RegisteredContentScript` would go out of scope and be destroyed, and the browser would then unregister the content script as part of cleanup. | ||
|
||
## Default settings | ||
|
||
The popup is initialized with some default values for the pattern and the code: | ||
The popup is initialized with default values for the pattern and the code: | ||
|
||
* the pattern `*://*.org/*`, which will load the script into any HTTP or HTTPS pages on a `.org` domain. | ||
* the pattern `*://*.org/*`, which loads the script into any HTTP or HTTPS pages on a `.org` domain. | ||
* the code `document.body.innerHTML = '<h1>This page has been eaten<h1>'` | ||
|
||
To try the extension out quickly, just click "Register script" with these defaults, and load http://example.org/ or | ||
https://www.mozilla.org/. Then try changing the pattern or the code, and reloading these or related pages. | ||
To try the extension, open the browser action, click "Register script" with the defaults, and load http://example.org/ or | ||
https://www.mozilla.org/. Then change the pattern or the code, and reload these or related pages. |
File renamed without changes.
File renamed without changes.
File renamed without changes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# User script registration | ||
|
||
This extension demonstrates the [`browser.userScripts.register()`](https://wiki.developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/userScripts/Register) API. | ||
|
||
The extension includes an [API script](https://wiki.developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/user_scripts) (`customUserScriptAPIs.js`) that enables user scripts to make use of `browser.storage.local`. | ||
|
||
To enable a user script to be specified and registered, this extension includes a sidebar action. The sidebar enables you to define the following properties that control the execution of a registered script, with default values provided: | ||
|
||
* the host pattern `*://*.org/*`, which loads the script into any HTTP or HTTPS pages on a `.org` domain. | ||
* script code that replaces the content of the pattern matched page with the message "This page has been eaten". The script also uses the API script stubs to save and recall the URL of each page "eaten". Information on the last and current "eaten" page is then included in the "eaten" message. | ||
* a script ID that is stored in the user script metadata and then used in the API script to store separate values for each registered script. | ||
|
||
All other properties use their default value. | ||
|
||
Clicking "Register script" registers the script by calling `browser.userScripts.register()`. | ||
|
||
In this example, only one script can be registered at a time: registering a new script unregisters the active script. The extension does this by keeping a reference to the `RegisteredUserScript` object returned from `browser.userScripts.register()`: this object provides the `unregister()` method. | ||
|
||
To try the extension, click "Register script" with the defaults and load http://example.org/ or | ||
https://www.mozilla.org/. Then change the pattern or the code and reload these or related pages. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
'use strict'; | ||
|
||
let registered = null; | ||
|
||
async function registerScript(message) { | ||
const { | ||
hosts, | ||
code, | ||
userScriptID, | ||
} = message; | ||
|
||
if (registered) { | ||
await registered.unregister(); | ||
registered = null; | ||
} | ||
|
||
registered = await browser.userScripts.register({ | ||
matches: hosts, | ||
js: [{code}], | ||
scriptMetadata: {userScriptID}, | ||
}); | ||
} | ||
|
||
browser.runtime.onMessage.addListener(registerScript); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
|
||
browser.userScripts.onBeforeScript.addListener(script => { | ||
|
||
const scriptMetadata = script.metadata; | ||
const id = scriptMetadata.userScriptID; | ||
|
||
function getScopedName(name) { | ||
return `${id}:${name}`; | ||
} | ||
|
||
script.defineGlobals({ | ||
async GM_getValue(name) { | ||
const scopedName = getScopedName(name); | ||
const res = await browser.storage.local.get(scopedName); | ||
console.log("GM_getValue", {id, name, res, scriptMetadata}); | ||
return res[scopedName]; | ||
}, | ||
GM_setValue(name, value) { | ||
console.log("GM_setValue", {id, name, value, scriptMetadata}); | ||
return browser.storage.local.set({[getScopedName(name)]: value}); | ||
}, | ||
}); | ||
|
||
console.log("custom userScripts APIs defined"); | ||
}); | ||
|
||
console.log("apiScript executed and userScripts.onBeforeScript listener subscribed"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
The icon "if_source_code_103710.svg" is from picol.org (http://www.picol.org/) and is used under the terms of the Creative Commons Attribution license: http://creativecommons.org/licenses/by/3.0/. |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
{ | ||
|
||
"manifest_version": 2, | ||
"name": "User script registration", | ||
"version": "1.0", | ||
|
||
"browser_specific_settings": { | ||
"gecko": { | ||
"id": "user-script-example@mozilla.org", | ||
"strict_min_version": "65.0" | ||
} | ||
}, | ||
|
||
"description": "Demonstration of userScripts.register.", | ||
"icons": { | ||
"48": "icons/if_source_code_103710.svg" | ||
}, | ||
|
||
"permissions": [ | ||
"<all_urls>", | ||
"storage" | ||
], | ||
|
||
"sidebar_action": { | ||
"default_icon": { | ||
"32" : "icons/if_source_code_103710.svg" | ||
}, | ||
"default_title": "User script", | ||
"default_panel": "popup/user-script.html", | ||
"browser_style": true | ||
}, | ||
|
||
"background": { | ||
"scripts": ["background.js"] | ||
}, | ||
|
||
"user_scripts": { | ||
"api_script": "customUserScriptAPIs.js" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
body { | ||
padding: 1.0em; | ||
} | ||
|
||
input { | ||
width: 100%; | ||
margin-bottom: 1em; | ||
} | ||
|
||
textarea { | ||
width: 100%; | ||
resize: none; | ||
border: 1px solid #e4e4e4; | ||
margin-bottom: 1em; | ||
min-height: 380px; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
<!DOCTYPE html> | ||
|
||
<html> | ||
<head> | ||
<meta charset="utf-8"> | ||
<link rel="stylesheet" href="user-script.css"/> | ||
</head> | ||
|
||
<body> | ||
|
||
<div> | ||
|
||
<label for="hosts">Hosts</label> | ||
<input id="hosts" type="text"> | ||
|
||
<label for="code">Code</label> | ||
<textarea id="code" rows="10"></textarea> | ||
|
||
<label for="userScriptID">User Script ID</label> | ||
<input id="userScriptID" type="text"> | ||
|
||
<button id="register">Register script</button> | ||
|
||
<div id="lastError" style="color: red;"> | ||
</div> | ||
<div id="lastResult" style="color: blue;"> | ||
</div> | ||
</div> | ||
|
||
<script src="user-script.js"></script> | ||
</body> | ||
|
||
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
'use strict'; | ||
|
||
const hostsInput = document.querySelector("#hosts"); | ||
const codeInput = document.querySelector("#code"); | ||
const userScriptIDInput = document.querySelector("#userScriptID"); | ||
const lastErrorEl = document.querySelector("#lastError"); | ||
const lastResultEl = document.querySelector("#lastResult"); | ||
|
||
const defaultHosts = "*://*.org/*"; | ||
const defaultCode = `(async function () { | ||
console.log("USER SCRIPT EXECUTING on", window.location.href, { | ||
documentReadyState: document.readyState, | ||
}); | ||
const oldStoredValue = await GM_getValue("testkey"); | ||
await GM_setValue("testkey", window.location.href); | ||
const newStoredValue = await GM_getValue("testkey"); | ||
const overwriteBody = () => { | ||
document.body.innerHTML = \`<h1>This page has been eaten: $\{JSON.stringify({oldStoredValue, newStoredValue})}<h1>\` | ||
} | ||
if (document.body) { | ||
overwriteBody(); | ||
} else { | ||
window.addEventListener("load", overwriteBody, {once: true}); | ||
} | ||
})();`; | ||
const defaultUserScriptID = "user_script_01"; | ||
|
||
hostsInput.value = defaultHosts; | ||
codeInput.value = defaultCode; | ||
userScriptIDInput.value = defaultUserScriptID; | ||
|
||
async function loadLastSetValues() { | ||
const params = await browser.storage.local.get(); | ||
|
||
const { | ||
hosts, | ||
code, | ||
userScriptID, | ||
} = params.lastSetValues || {}; | ||
|
||
hostsInput.value = hosts ? hosts.join(",") : defaultHosts; | ||
codeInput.value = code ? code : defaultCode; | ||
userScriptIDInput.value = userScriptID ? userScriptID : defaultUserScriptID; | ||
|
||
lastErrorEl.textContent = params.lastError || ""; | ||
} | ||
|
||
function stringToArray(value) { | ||
const res = value.split(",").map(el => el.trim()).filter(el => el !== ""); | ||
|
||
return res.length > 0 ? res : null; | ||
} | ||
|
||
async function registerScript() { | ||
const params = { | ||
hosts: stringToArray(hostsInput.value), | ||
code: codeInput.value, | ||
userScriptID: userScriptID.value, | ||
}; | ||
|
||
// Store the last submitted values to the extension storage | ||
// (so that they can be restored when the popup is opened | ||
// the next time). | ||
await browser.storage.local.set({ | ||
lastSetValues: params, | ||
}); | ||
|
||
try { | ||
// Clear the last userScripts.register result. | ||
lastResultEl.textContent = ""; | ||
|
||
await browser.runtime.sendMessage(params); | ||
lastResultEl.textContent = "UserScript successfully registered"; | ||
// Clear the last userScripts.register error. | ||
lastErrorEl.textContent = ""; | ||
|
||
// Clear the last error stored. | ||
await browser.storage.local.remove("lastError"); | ||
} catch (e) { | ||
// There was an error on registering the contentScript, | ||
// let's show the error message in the popup and store | ||
// the last error into the extension storage. | ||
|
||
const lastError = `${e}`; | ||
// Show the last userScripts.register error. | ||
lastErrorEl.textContent = lastError; | ||
|
||
// Store the last error. | ||
await browser.storage.local.set({lastError}); | ||
} | ||
} | ||
|
||
loadLastSetValues(); | ||
|
||
document.querySelector("#register").addEventListener('click', registerScript); |