Skip to content

Commit

Permalink
Add GM.registerMenuCommand()
Browse files Browse the repository at this point in the history
Resolves one part of greasemonkey#2714 .
  • Loading branch information
esperecyan committed Jan 6, 2018
1 parent 056f978 commit 4362541
Show file tree
Hide file tree
Showing 11 changed files with 580 additions and 7 deletions.
54 changes: 54 additions & 0 deletions doc/Messages.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,24 @@ Received by: `bg/is-enabled.js`.

Send this to toggle the global enabled status. No data.

# ListMenuCommands
Sent by: `browser/monkey-menu.js`
Received by: `bg/on-user-script-menu-command.js`

Triggered when the popup menu is loaded.
Lists menu commands registered by the `GM.registerMenuCommand()` method called by user scripts on the specified tab.

Data:

* `tabId` A tab's ID (integer).

Response data:

* An array of command objects. Each object has
- `id` A command id that can be used as HTML/XML ID.
- `caption` A string given as the first parameter of the `GM.registerMenuCommand()` method.
- `accessKey` A code point (string) or empty string given as the third parameter of the `GM.registerMenuCommand()` method.

# ListUserScripts
Received by: `bg/user-script-registry.js`.

Expand All @@ -125,6 +143,22 @@ progress as a percentage. Sent specifically back to the content process
* `errors` A (possibly empty) list of string error messages.
* `progress` A number, 0.0 to 1.0, representing the completion so far.

# MenuCommandClick
Sent by: `browser/monkey-menu.js`
Received by: `bg/on-user-script-menu-command.js`

Triggered when a command button on the popup menu is clicked by the user.
Posts message `{type: 'onclick'}` on `UserScriptMenuCommand` channel
to call a function given as the second parameter of the `GM.registerMenuCommand()` method.

Data:

* `id` The command id (string) as returned by `ListMenuCommands` message.

Response data:

* `undefined`

# UserScriptChanged
Sent by: `bg/user-script-registry.js`

Expand Down Expand Up @@ -158,6 +192,26 @@ user. Data:

Callers should specify one or the other, not both.

# UserScriptMenuCommand
Sent by: `content/api-provider-source.js`
Received by: `bg/on-user-script-menu-command.js`

This is a channel (not a message).
Triggered when the `GM.registerMenuCommand()` method is called by an user script.

Data:

* `details` The details object specifying the command. It has
- `caption` A string given as the first parameter of the `GM.registerMenuCommand()` method.
- `accessKey` A code point (string) or empty string given as the third parameter of the `GM.registerMenuCommand()` method.

Response data:

* `undefined`

Messages exchanged via this channel are private to its implementation.
See sender and receiver for further detail.

# UserScriptToggleEnabled
Sent by: `content/manage-user-scripts.js`
Received by: `bg/user-script-registry.js`
Expand Down
1 change: 1 addition & 0 deletions karma.conf.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module.exports = function(config) {
config.set({
files: [
'./node_modules/sinon-chrome/bundle/sinon-chrome-webextensions.min.js',
'./test/setup.js',
'./third-party/convert2RegExp.js',
'./third-party/MatchPattern.js',
Expand Down
1 change: 1 addition & 0 deletions manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"src/bg/on-message.js",
"src/bg/on-user-script-notification.js",
"src/bg/on-user-script-open-in-tab.js",
"src/bg/on-user-script-menu-command.js",
"src/bg/on-user-script-xhr.js",
"src/bg/user-script-install.js",
"src/bg/user-script-registry.js",
Expand Down
35 changes: 34 additions & 1 deletion src/bg/api-provider-source.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const SUPPORTED_APIS = new Set([
'GM.getResourceUrl',
'GM.notification',
'GM.openInTab',
'GM.registerMenuCommand',
'GM.setClipboard',
'GM.xmlHttpRequest',
]);
Expand Down Expand Up @@ -55,6 +56,10 @@ function apiProviderSource(userScript) {
source += 'GM.openInTab = ' + GM_openInTab.toString() + ';\n\n';
}

if (grants.includes('GM.registerMenuCommand')) {
source += 'GM.registerMenuCommand = ' + GM_registerMenuCommand.toString() + ';\n\n';
}

if (grants.includes('GM.setClipboard')) {
source += 'GM.setClipboard = ' + GM_setClipboard.toString() + ';\n\n';
}
Expand All @@ -63,7 +68,6 @@ function apiProviderSource(userScript) {
source += 'GM.xmlHttpRequest = ' + GM_xmlHttpRequest.toString() + ';\n\n';
}

// TODO: GM_registerMenuCommand -- maybe.
// TODO: GM_getResourceText -- maybe.

source += '})();';
Expand Down Expand Up @@ -196,6 +200,35 @@ function GM_openInTab(url, openInBackground) {
}


function GM_registerMenuCommand(caption, commandFunc, accessKey = '') {
if (typeof caption != 'string') {
caption = String(caption);
}

if (typeof commandFunc != 'function') {
throw new Error('GM.registerMenuCommand: "commandFunc" must be a function');
}

if (typeof accessKey != 'string' || Array.from(accessKey).length > 1) {
throw new Error('GM.registerMenuCommand: "accessKey" must be a single character');
}

let port = chrome.runtime.connect({name: 'UserScriptMenuCommand'});
port.onMessage.addListener(msg => {
if (msg.type === 'onclick') {
commandFunc();
}
});
port.postMessage({
name: 'register',
details: {
caption,
accessKey,
}
});
}


function GM_setClipboard(text) {
function onCopy(event) {
document.removeEventListener('copy', onCopy, true);
Expand Down
71 changes: 71 additions & 0 deletions src/bg/on-user-script-menu-command.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
This file is responsible for providing the GM.registerMenuCommand API method.
*/

// Private implementation
(function () {

const commandMap = new Map();


function _randomId() {
return 'id' + window.crypto.getRandomValues(new Uint8Array(16)).join('');
}


function onListMenuCommands(message, sender, sendResponse) {
sendResponse(
Array.from(commandMap.values())
.filter(command => command.port.sender.tab.id === message.tabId)
.map(command => ({
id: command.id,
caption: command.caption,
accessKey: command.accessKey,
}))
);
}
window.onListMenuCommands = onListMenuCommands;


function onMenuCommandClick(message, sender, sendResponse) {
if (commandMap.has(message.id)) {
commandMap.get(message.id).port.postMessage({type: 'onclick'});
}
}
window.onMenuCommandClick = onMenuCommandClick;


function registerMenuCommand({port, caption, accessKey}) {
const command = {
id: _randomId(),
port,
caption,
accessKey,
};

commandMap.set(command.id, command);

port.onDisconnect.addListener(port => {
commandMap.delete(command.id);
});
}


function onUserScriptMenuCommand(port) {
if (port.name != 'UserScriptMenuCommand') return;

port.onMessage.addListener((msg, port) => {
switch (msg.name) {
case 'register':
registerMenuCommand(Object.assign({
port,
}, msg.details));
break;
default:
console.warn('UserScriptMenuCommand port un-handled message name:', msg.name);
}
});
}
chrome.runtime.onConnect.addListener(onUserScriptMenuCommand);

})();
12 changes: 8 additions & 4 deletions src/browser/monkey-menu.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ body {
}
body.rendering { display: none; }

body #user-script-detail, body.detail #menu { display: none; }
body #menu, body.detail #user-script-detail { display: block; }
body #user-script-detail, body.detail #menu, body #menu-commands, body.commands #menu { display: none; }
body #menu, body.detail #user-script-detail, body.commands #menu-commands { display: block; }
#templates { display: none; }


Expand Down Expand Up @@ -58,6 +58,10 @@ section a:hover {
.menu-item:hover, .menu-item:focus {
background: #EEE;
}
.menu-item .access-key {
text-decoration: underline;
text-transform: uppercase;
}

hr {
margin: 4px 0;
Expand All @@ -73,10 +77,10 @@ header * {
padding: 6px;
vertical-align: middle;
}
header #back {
header .back {
padding: 8px 6px 4px 6px;
}
header #back:hover, header #back:focus {
header .back:hover, header .back:focus {
background: #EEE;
}
header #name {
Expand Down
26 changes: 25 additions & 1 deletion src/browser/monkey-menu.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@
</div>
-->

<hr rv-hide="menuCommands | empty">
<a href="#menu-commands" class="menu-item" id="open-menu-commands" rv-hide="menuCommands | empty">
<div class="icon fa fa-fw fa-list-alt "></div>
<div class="text">User script commands ...</div>
</a>

<div rv-hide="userScripts.active | empty">
<hr>
<div class="menu-item heading">
Expand Down Expand Up @@ -81,9 +87,27 @@
</section>


<section id="menu-commands">
<header>
<a href="#open-menu-commands" class="fa fa-lg fa-chevron-left back"></a>
<div class="text heading">User script commands</div>
</header>
<hr>

<a rv-each-command="menuCommands"
rv-href="command.id | mmUuidMenu"
rv-aria-keyshortcuts="command.accessKey"
class="menu-item">
<div class="text">
{command.caption}<span rv-if="command.accessKey">(<span class="access-key">{command.accessKey}</span>)</span>
</div>
</a>
</section>


<section id="user-script-detail">
<header>
<a href="#menu-top" class="fa fa-lg fa-chevron-left" id="back"></a>
<a href="#menu-top" class="fa fa-lg fa-chevron-left back"></a>
<div id="name">{activeScript.name}</div>
</header>
<hr>
Expand Down

0 comments on commit 4362541

Please sign in to comment.