From 2aed8b3e52a455507becd3f8763882bc069c920c Mon Sep 17 00:00:00 2001 From: Anthony Tseng Date: Wed, 26 Oct 2016 19:01:35 -0400 Subject: [PATCH] Add chrome.contextMenus.create and chrome.contextMenus.removeAll fix #4689 requires https://github.com/brave/electron/pull/82 Auditors: @bridiver, @jonathansampson Test Plan: n/a --- app/browser/extensions/contextMenus.js | 18 +++++++++ app/common/actions/extensionActions.js | 44 +++++++++++++++++++++ app/common/constants/extensionConstants.js | 5 ++- app/common/state/extensionState.js | 45 ++++++++++++++++++++++ app/extensions.js | 2 + docs/state.md | 5 +++ js/contextMenus.js | 34 ++++++++++++++++ js/stores/appStore.js | 10 +++++ 8 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 app/browser/extensions/contextMenus.js diff --git a/app/browser/extensions/contextMenus.js b/app/browser/extensions/contextMenus.js new file mode 100644 index 00000000000..bb79d311e57 --- /dev/null +++ b/app/browser/extensions/contextMenus.js @@ -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 diff --git a/app/common/actions/extensionActions.js b/app/common/actions/extensionActions.js index 9f65b6e34bb..3aac2b2e6b7 100644 --- a/app/common/actions/extensionActions.js +++ b/app/common/actions/extensionActions.js @@ -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 + }) } } diff --git a/app/common/constants/extensionConstants.js b/app/common/constants/extensionConstants.js index 64e7f18ae80..fc5c2c3a515 100644 --- a/app/common/constants/extensionConstants.js +++ b/app/common/constants/extensionConstants.js @@ -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) diff --git a/app/common/state/extensionState.js b/app/common/state/extensionState.js index 066b74fc19d..7f34f3c969e 100644 --- a/app/common/state/extensionState.js +++ b/app/common/state/extensionState.js @@ -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 } } diff --git a/app/extensions.js b/app/extensions.js index 1c3bc7945d1..50845bb531d 100644 --- a/app/extensions.js +++ b/app/extensions.js @@ -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 {fileUrl} = require('../js/lib/appUrlUtil') @@ -177,6 +178,7 @@ const extensionInfo = { module.exports.init = () => { browserActions.init() + contextMenus.init() const {componentUpdater, session} = require('electron') componentUpdater.on('component-checking-for-updates', () => { diff --git a/docs/state.md b/docs/state.md index 9b0311fc60d..2f34e05fe5c 100644 --- a/docs/state.md +++ b/docs/state.md @@ -26,6 +26,11 @@ AppStore [tabId]: { browserAction: {} // tab specific browser action properties } + }, + contextMenus: { + extensionId: string, + menuItemId: string, + properties: object } } }, diff --git a/js/contextMenus.js b/js/contextMenus.js index 5d9fd8a7588..f2fa5411833 100644 --- a/js/contextMenus.js +++ b/js/contextMenus.js @@ -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' @@ -1099,6 +1102,37 @@ function mainTemplateInit (nodeProps, frame) { }) } + const extensionContextMenus = + extensionState.getContextMenusProperties(appStore.state) + if (extensionContextMenus.length) { + template.push(CommonMenu.separatorMenuItem) + let info = {} + if (isTextSelected) { + info['selectionText'] = nodeProps.selectionText + } + if (isLink) { + info['linkUrl'] = nodeProps.linkURL + } + extensionContextMenus.forEach((extensionContextMenu) => { + // TODO (Anthony): Browser Action context menu + if (!(extensionContextMenu.properties.contexts.length === 1 && + extensionContextMenu.properties.contexts[0] === 'browser_action')) { + info['menuItemId'] = extensionContextMenu.menuItemId + template.push( + { + label: extensionContextMenu.properties.title, + click: (item, focusedWindow) => { + if (focusedWindow) { + console.log(extensionContextMenu.extensionId) + extensionActions.contextMenuClicked( + extensionContextMenu.extensionId, frame.get('tabId'), info) + } + } + }) + } + }) + } + if (frame.get('location') === 'about:bookmarks') { template.push( CommonMenu.separatorMenuItem, diff --git a/js/stores/appStore.js b/js/stores/appStore.js index 3252e4241d3..99c698f0893 100644 --- a/js/stores/appStore.js +++ b/js/stores/appStore.js @@ -693,6 +693,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