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

Commit

Permalink
Add chrome.contextMenus.create and chrome.contextMenus.removeAll
Browse files Browse the repository at this point in the history
fix #4689

requires brave/muon#82

Auditors: @bridiver, @jonathansampson

Test Plan: n/a
  • Loading branch information
darkdh committed Oct 27, 2016
1 parent 957b748 commit d602b3c
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 1 deletion.
18 changes: 18 additions & 0 deletions app/browser/extensions/contextMenus.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const extensionActions = require('../../common/actions/extensionActions')

const contextMenus = {
init: () => {
process.on('chrome-context-menus-remove-all', (extensionId) => {
setImmediate(() => {
extensionActions.contextMenuAllRemoved(extensionId)
})
})
process.on('chrome-context-menus-create', (extensionId, menuItemId, properties) => {
setImmediate(() => {
extensionActions.contextMenuCreated(extensionId, menuItemId, properties)
})
})
}
}

module.exports = contextMenus
44 changes: 44 additions & 0 deletions app/common/actions/extensionActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,50 @@ const extensionActions = {
actionType: ExtensionConstants.EXTENSION_DISABLED,
extensionId
})
},

/**
* Dispatched when an extension has created item in context menu
*
* @param {string} extensionId - the extension id
* @param {string} menuItemId - the id of the menu item that was clicked
* @param {object} properties - createProperties of chrome.contextMenus.create
*/
contextMenuCreated: function (extensionId, menuItemId, properties) {
AppDispatcher.dispatch({
actionType: ExtensionConstants.CONTEXT_MENU_CREATED,
extensionId,
menuItemId,
properties
})
},

/**
* Dispatched when an extension has removed all item in context menu
*
* @param {string} extensionId - the extension id
*/
contextMenuAllRemoved: function (extensionId) {
AppDispatcher.dispatch({
actionType: ExtensionConstants.CONTEXT_MENU_ALL_REMOVED,
extensionId
})
},

/**
* Dispatched when an menu item created by extension is clicked
*
* @param {string} extensionId - the extension id
* @param {string} tabId - the tab id
* @param {object} info - the arg of onclick callback
*/
contextMenuClicked: function (extensionId, tabId, info) {
AppDispatcher.dispatch({
actionType: ExtensionConstants.CONTEXT_MENU_CLICKED,
extensionId,
tabId,
info
})
}
}

Expand Down
5 changes: 4 additions & 1 deletion app/common/constants/extensionConstants.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ const ExtensionConstants = {
EXTENSION_INSTALLED: _,
EXTENSION_UNINSTALLED: _,
EXTENSION_ENABLED: _,
EXTENSION_DISABLED: _
EXTENSION_DISABLED: _,
CONTEXT_MENU_CREATED: _,
CONTEXT_MENU_ALL_REMOVED: _,
CONTEXT_MENU_CLICKED: _
}

module.exports = mapValuesByKeys(ExtensionConstants)
45 changes: 45 additions & 0 deletions app/common/state/extensionState.js
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,51 @@ const extensionState = {
extension = extension.delete(field)
})
return extension
},

contextMenuCreated: (state, action) => {
action = makeImmutable(action)
state = makeImmutable(state)
let extensionId = action.get('extensionId').toString()
let extension = extensionState.getExtensionById(state, extensionId)
if (extension) {
if (state.getIn(['extensions', action.get('extensionId'), 'contextMenus']) === undefined) {
state = state.setIn(['extensions', action.get('extensionId'), 'contextMenus'], new Immutable.List())
}
let contextMenus = state.getIn(['extensions', action.get('extensionId'), 'contextMenus'])
return state.setIn(['extensions', action.get('extensionId'), 'contextMenus'],
contextMenus.push({
extensionId: action.get('extensionId'),
menuItemId: action.get('menuItemId'),
properties: action.get('properties').toJS()
}))
} else {
return state
}
},

contextMenuAllRemoved: (state, action) => {
action = makeImmutable(action)
state = makeImmutable(state)
let extensionId = action.get('extensionId').toString()
let extension = extensionState.getExtensionById(state, extensionId)
if (extension) {
return state.deleteIn(['extensions', action.get('extensionId'), 'contextMenus'])
} else {
return state
}
},

getContextMenusProperties: (state) => {
let allProperties = []
let extensions = extensionState.getEnabledExtensions(state)
extensions && extensions.forEach((extension) => {
let contextMenus = extension.get('contextMenus')
contextMenus && contextMenus.forEach((contextMenu) => {
allProperties.push(contextMenu.toJS())
})
})
return allProperties
}
}

Expand Down
2 changes: 2 additions & 0 deletions app/extensions.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const browserActions = require('./browser/extensions/browserActions')
const contextMenus = require('./browser/extensions/contextMenus')
const extensionActions = require('./common/actions/extensionActions')
const config = require('../js/constants/config')
const appConfig = require('../js/constants/appConfig')
Expand Down Expand Up @@ -184,6 +185,7 @@ const isWidevine = (componentId) =>

module.exports.init = () => {
browserActions.init()
contextMenus.init()

const {componentUpdater, session} = require('electron')
componentUpdater.on('component-checking-for-updates', () => {
Expand Down
5 changes: 5 additions & 0 deletions docs/state.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ AppStore
[tabId]: {
browserAction: {} // tab specific browser action properties
}
},
contextMenus: {
extensionId: string,
menuItemId: string,
properties: object
}
}
},
Expand Down
63 changes: 63 additions & 0 deletions js/contextMenus.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ const eventUtil = require('./lib/eventUtil')
const currentWindow = require('../app/renderer/currentWindow')
const config = require('./constants/config')
const bookmarksToolbarMode = require('../app/common/constants/bookmarksToolbarMode')
const extensionState = require('../app/common/state/extensionState')
const extensionActions = require('../app/common/actions/extensionActions')
const appStore = require('./stores/appStoreRenderer')

const isDarwin = process.platform === 'darwin'

Expand Down Expand Up @@ -886,6 +889,8 @@ function mainTemplateInit (nodeProps, frame) {

const isLink = nodeProps.linkURL && nodeProps.linkURL !== ''
const isImage = nodeProps.mediaType === 'image'
const isVideo = nodeProps.mediaType === 'video'
const isAudio = nodeProps.mediaType === 'audio'
const isInputField = nodeProps.isEditable || nodeProps.inputFieldType !== 'none'
const isTextSelected = nodeProps.selectionText.length > 0

Expand Down Expand Up @@ -1099,6 +1104,64 @@ function mainTemplateInit (nodeProps, frame) {
})
}

const extensionContextMenus =
extensionState.getContextMenusProperties(appStore.state)
if (extensionContextMenus.length) {
template.push(CommonMenu.separatorMenuItem)
extensionContextMenus.forEach((extensionContextMenu) => {
let info = {}
let contextsPassed = false
extensionContextMenu.properties.contexts.forEach((context) => {
if (isTextSelected && (context === 'selection' || context === 'all')) {
info['selectionText'] = nodeProps.selectionText
contextsPassed = true
} else if (isLink && (context === 'link' || context === 'all')) {
info['linkUrl'] = nodeProps.linkURL
contextsPassed = true
} else if (isImage && (context === 'image' || context === 'all')) {
info['mediaType'] = 'image'
contextsPassed = true
} else if (isInputField && (context === 'editable' || context === 'all')) {
info['editable'] = true
contextsPassed = true
} else if (nodeProps.pageURL && (context === 'page' || context === 'all')) {
info['pageUrl'] = nodeProps.pageURL
contextsPassed = true
} else if (isVideo && (context === 'video' || context === 'all')) {
info['mediaType'] = 'video'
contextsPassed = true
} else if (isAudio && (context === 'audio' || context === 'all')) {
info['mediaType'] = 'audio'
contextsPassed = true
} else if (nodeProps.frameURL && (context === 'frame' || context === 'all')) {
info['frameURL'] = nodeProps.frameURL
contextsPassed = true
}
})
if (nodeProps.srcURL) {
info['srcURL'] = nodeProps.srcURL
}
// TODO (Anthony): Browser Action context menu
if (extensionContextMenu.properties.contexts.length === 1 &&
extensionContextMenu.properties.contexts[0] === 'browser_action') {
contextsPassed = false
}
if (contextsPassed) {
info['menuItemId'] = extensionContextMenu.menuItemId
template.push(
{
label: extensionContextMenu.properties.title,
click: (item, focusedWindow) => {
if (focusedWindow) {
extensionActions.contextMenuClicked(
extensionContextMenu.extensionId, frame.get('tabId'), info)
}
}
})
}
})
}

if (frame.get('location') === 'about:bookmarks') {
template.push(
CommonMenu.separatorMenuItem,
Expand Down
10 changes: 10 additions & 0 deletions js/stores/appStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -696,6 +696,16 @@ const handleAppAction = (action) => {
case ExtensionConstants.EXTENSION_DISABLED:
appState = extensionState.extensionDisabled(appState, action)
break
case ExtensionConstants.CONTEXT_MENU_CREATED:
appState = extensionState.contextMenuCreated(appState, action)
break
case ExtensionConstants.CONTEXT_MENU_ALL_REMOVED:
appState = extensionState.contextMenuAllRemoved(appState, action)
break
case ExtensionConstants.CONTEXT_MENU_CLICKED:
process.emit('chrome-context-menus-clicked',
action.extensionId, action.tabId, action.info.toJS())
break
case AppConstants.APP_SET_MENUBAR_TEMPLATE:
appState = appState.setIn(['menu', 'template'], action.menubarTemplate)
break
Expand Down

0 comments on commit d602b3c

Please sign in to comment.