1
- import { ipcMain } from 'electron'
1
+ import { app , ipcMain , Menu , MenuItem } from 'electron'
2
2
import { EventEmitter } from 'events'
3
+ import { MenuItemConstructorOptions } from 'electron/main'
4
+ import { ExtensionAPIState } from '../api-state'
5
+ import { getIconPath } from './common'
6
+
7
+ type ContextItemProps = chrome . contextMenus . CreateProperties
8
+
9
+ type ContextType =
10
+ | 'all'
11
+ | 'page'
12
+ | 'frame'
13
+ | 'selection'
14
+ | 'link'
15
+ | 'editable'
16
+ | 'image'
17
+ | 'video'
18
+ | 'audio'
19
+ | 'launcher'
20
+ | 'browser_action'
21
+ | 'page_action'
22
+ | 'action'
23
+
24
+ const getContextTypesFromParams = ( params : Electron . ContextMenuParams ) : Set < ContextType > => {
25
+ const contexts = new Set < ContextType > ( [ 'all' ] )
26
+
27
+ switch ( params . mediaType ) {
28
+ case 'audio' :
29
+ case 'video' :
30
+ case 'image' :
31
+ contexts . add ( params . mediaType )
32
+ }
33
+
34
+ if ( params . pageURL ) contexts . add ( 'page' )
35
+ if ( params . linkURL ) contexts . add ( 'link' )
36
+ if ( params . frameURL ) contexts . add ( 'frame' )
37
+ if ( params . selectionText ) contexts . add ( 'selection' )
38
+ if ( params . isEditable ) contexts . add ( 'editable' )
39
+
40
+ return contexts
41
+ }
3
42
4
43
export class ContextMenusAPI extends EventEmitter {
5
- private menus = new Map < /* extensionId */ string , any > ( )
44
+ private menus = new Map <
45
+ /* extensionId */ string ,
46
+ Map < /* menuItemId */ string , ContextItemProps >
47
+ > ( )
6
48
7
- constructor ( ) {
49
+ constructor ( private state : ExtensionAPIState ) {
8
50
super ( )
9
51
10
52
ipcMain . handle ( 'contextMenus.create' , this . create )
53
+ ipcMain . handle ( 'contextMenus.remove' , this . remove )
54
+ ipcMain . handle ( 'contextMenus.removeAll' , this . removeAll )
55
+
56
+ this . state . session . on ( 'extension-unloaded' as any , ( event , extensionId ) => {
57
+ if ( this . menus . has ( extensionId ) ) {
58
+ this . menus . delete ( extensionId )
59
+ }
60
+ } )
11
61
}
12
62
13
- private addContextItem ( extensionId : string , item : any ) {
63
+ private addContextItem ( extensionId : string , props : ContextItemProps ) {
14
64
let contextItems = this . menus . get ( extensionId )
15
65
if ( ! contextItems ) {
16
- contextItems = [ ]
66
+ contextItems = new Map ( )
17
67
this . menus . set ( extensionId , contextItems )
18
68
}
19
- contextItems . push ( item )
69
+ contextItems . set ( props . id ! , props )
70
+ }
71
+
72
+ buildMenuItems ( params : Electron . ContextMenuParams ) {
73
+ const buildMenuItem = ( extension : Electron . Extension , props : ContextItemProps ) => {
74
+ const menuItemOptions : MenuItemConstructorOptions = {
75
+ id : props . id ,
76
+ type : props . type as any ,
77
+ label : props . title ,
78
+ icon : getIconPath ( extension ) ,
79
+ click : ( ) => {
80
+ // TODO
81
+ this . onClicked ( { } as any , { } )
82
+ } ,
83
+ }
84
+ const menuItem = new MenuItem ( menuItemOptions )
85
+ return menuItem
86
+ }
87
+
88
+ const menuItems = [ ]
89
+ const contextTypes = getContextTypesFromParams ( params )
90
+
91
+ for ( const [ extensionId , propItems ] of this . menus ) {
92
+ const extension = this . state . session . getExtension ( extensionId )
93
+ if ( ! extension ) continue
94
+
95
+ for ( const [ , props ] of propItems ) {
96
+ if ( props . enabled === false ) continue
97
+
98
+ if ( props . contexts ) {
99
+ const inContext = props . contexts . some ( ( context ) =>
100
+ contextTypes . has ( context as ContextType )
101
+ )
102
+ if ( ! inContext ) continue
103
+ }
104
+
105
+ const menuItem = buildMenuItem ( extension , props )
106
+ menuItems . push ( menuItem )
107
+ }
108
+ }
109
+
110
+ return menuItems
20
111
}
21
112
22
113
private create = (
23
114
event : Electron . IpcMainInvokeEvent ,
24
115
extensionId : string ,
25
- createProperties : chrome . contextMenus . CreateProperties
116
+ createProperties : ContextItemProps
26
117
) => {
27
118
const { id, type, title } = createProperties
28
119
@@ -42,4 +133,26 @@ export class ContextMenusAPI extends EventEmitter {
42
133
this . addContextItem ( extensionId , createProperties )
43
134
}
44
135
}
136
+
137
+ private remove = (
138
+ event : Electron . IpcMainInvokeEvent ,
139
+ extensionId : string ,
140
+ menuItemId : string
141
+ ) => {
142
+ const items = this . menus . get ( extensionId )
143
+ if ( items && items . has ( menuItemId ) ) {
144
+ items . delete ( menuItemId )
145
+ if ( items . size === 0 ) {
146
+ this . menus . delete ( extensionId )
147
+ }
148
+ }
149
+ }
150
+
151
+ private removeAll = ( event : Electron . IpcMainInvokeEvent , extensionId : string ) => {
152
+ this . menus . delete ( extensionId )
153
+ }
154
+
155
+ private onClicked ( info : chrome . contextMenus . OnClickData , tab : any ) {
156
+ this . state . sendToHosts ( 'tabs.onCreated' , info , tab )
157
+ }
45
158
}
0 commit comments