Skip to content
This repository has been archived by the owner on Apr 11, 2023. It is now read-only.

Support sendMessage to other add-ons #812

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/_layouts/default.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ <h1>Guide</h1>
<li><a href='./blacklist.html'>Blacklist</a></li>
<li><a href='./search_engines.html'>Search engines</a></li>
<li><a href='./properties.html'>Properties</a></li>
<li><a href='./sendmessage.html'>SendMessage</a></li>
</ul>
</aside>

Expand Down
31 changes: 31 additions & 0 deletions docs/sendmessage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
title: SendMessage
---

# SendMessage

Vim Vixen can send messages to other add-ons to controll their functionality by keyboard, if they support. To use this feature, you need to specify `addon.sendmessage` as `type`, `extensionId` and `message` (this can be string or object) for keymaps.

Note that, currently this feature can be set only when using "Use plain JSON".

## Example

Following example enables to toggle collapsed state of [Tree Style Tab](https://addons.mozilla.org/firefox/addon/tree-style-tab/)'s active tab by pressing <kbd>zc</kbd>. This kind of API might be described in add-ons' web site, for example you can find Tree Style Tab's API reference [here](https://github.com/piroor/treestyletab/wiki/API-for-other-addons).

```json
{
"keymaps": {
"zc": {
"type": "addon.sendmessage",
"extensionId": "[email protected]",
"message": {
"type": "toggle-tree-collapsed",
"tab": "active"
}
}
}
}
```

You may want to see [the Wiki page for the same feature on Gesturefy](https://twitter.com/tomo_ahm/status/1297849816907575296) for more example.

8 changes: 8 additions & 0 deletions src/content/controllers/KeymapController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { injectable, inject } from "tsyringe";
import * as operations from "../../shared/operations";
import KeymapUseCase from "../usecases/KeymapUseCase";
import AddonEnabledUseCase from "../usecases/AddonEnabledUseCase";
import AddonSendmessageUseCase from "../usecases/AddonSendmessageUseCase";
import FindSlaveUseCase from "../usecases/FindSlaveUseCase";
import ScrollUseCase from "../usecases/ScrollUseCase";
import FocusUseCase from "../usecases/FocusUseCase";
Expand All @@ -16,6 +17,7 @@ export default class KeymapController {
constructor(
private keymapUseCase: KeymapUseCase,
private addonEnabledUseCase: AddonEnabledUseCase,
private addonSendmessageUseCase: AddonSendmessageUseCase,
private findSlaveUseCase: FindSlaveUseCase,
private scrollUseCase: ScrollUseCase,
private focusUseCase: FocusUseCase,
Expand Down Expand Up @@ -48,6 +50,12 @@ export default class KeymapController {
return () => this.addonEnabledUseCase.disable();
case operations.ADDON_TOGGLE_ENABLED:
return () => this.addonEnabledUseCase.toggle();
case operations.ADDON_SENDMESSAGE:
return () =>
this.addonSendmessageUseCase.sendMessage(
op.extensionId,
op.message
);
case operations.FIND_NEXT:
return () => this.findSlaveUseCase.findNext();
case operations.FIND_PREV:
Expand Down
17 changes: 17 additions & 0 deletions src/content/usecases/AddonSendmessageUseCase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { injectable, inject } from "tsyringe";
import ConsoleClient from "../client/ConsoleClient";

@injectable()
export default class AddonSendmessageUseCase {
constructor(@inject("ConsoleClient") private consoleClient: ConsoleClient) {}

async sendMessage(extensionId: string, message: any) {
const sending = browser.runtime.sendMessage(extensionId, message);
sending.catch((reason: any) => {
this.consoleClient.error(
`Error on sending message to ${extensionId}: ${reason}`
);
});
return sending;
}
}
25 changes: 25 additions & 0 deletions src/shared/operations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const CANCEL = "cancel";
export const ADDON_ENABLE = "addon.enable";
export const ADDON_DISABLE = "addon.disable";
export const ADDON_TOGGLE_ENABLED = "addon.toggle.enabled";
export const ADDON_SENDMESSAGE = "addon.sendmessage";

// Command
export const COMMAND_SHOW = "command.show";
Expand Down Expand Up @@ -97,6 +98,12 @@ export interface AddonToggleEnabledOperation {
type: typeof ADDON_TOGGLE_ENABLED;
}

export interface AddonSendmessageOperation {
type: typeof ADDON_SENDMESSAGE;
extensionId: string;
message: string | Record<string, string>;
}

export interface CommandShowOperation {
type: typeof COMMAND_SHOW;
}
Expand Down Expand Up @@ -315,6 +322,7 @@ export type Operation =
| AddonEnableOperation
| AddonDisableOperation
| AddonToggleEnabledOperation
| AddonSendmessageOperation
| CommandShowOperation
| CommandShowOpenOperation
| CommandShowTabopenOperation
Expand Down Expand Up @@ -409,12 +417,29 @@ const assertRequiredString = (obj: any, name: string) => {
}
};

const assertRequiredObjectOrString = (obj: any, name: string) => {
if (
!Object.prototype.hasOwnProperty.call(obj, name) ||
!(typeof obj[name] === "string" || typeof obj[name] == "object")
) {
throw new TypeError(`Missing object or string parameter: '${name}`);
}
};

// eslint-disable-next-line complexity, max-lines-per-function
export const valueOf = (o: any): Operation => {
if (!Object.prototype.hasOwnProperty.call(o, "type")) {
throw new TypeError(`Missing 'type' field`);
}
switch (o.type) {
case ADDON_SENDMESSAGE:
assertRequiredString(o, "extensionId");
assertRequiredObjectOrString(o, "message");
return {
type: o.type,
extensionId: o.extensionId,
message: o.message,
};
case COMMAND_SHOW_OPEN:
case COMMAND_SHOW_TABOPEN:
case COMMAND_SHOW_WINOPEN:
Expand Down