(
+
+);
+
+export default ReadClipboard;
diff --git a/src/components/tw-security-manager-modal/record-audio.jsx b/src/components/tw-security-manager-modal/record-audio.jsx
new file mode 100644
index 000000000..ad94079d6
--- /dev/null
+++ b/src/components/tw-security-manager-modal/record-audio.jsx
@@ -0,0 +1,25 @@
+import React from 'react';
+import {FormattedMessage} from 'react-intl';
+
+const RecordAudio = () => (
+
+);
+
+export default RecordAudio;
diff --git a/src/components/tw-security-manager-modal/record-video.jsx b/src/components/tw-security-manager-modal/record-video.jsx
new file mode 100644
index 000000000..75dab1a6a
--- /dev/null
+++ b/src/components/tw-security-manager-modal/record-video.jsx
@@ -0,0 +1,25 @@
+import React from 'react';
+import {FormattedMessage} from 'react-intl';
+
+const RecordVideo = () => (
+
+);
+
+export default RecordVideo;
diff --git a/src/components/tw-security-manager-modal/redirect.jsx b/src/components/tw-security-manager-modal/redirect.jsx
index c1fcace70..e7bdb168f 100644
--- a/src/components/tw-security-manager-modal/redirect.jsx
+++ b/src/components/tw-security-manager-modal/redirect.jsx
@@ -8,7 +8,7 @@ const RedirectModal = props => (
@@ -16,7 +16,7 @@ const RedirectModal = props => (
(
) : props.type === SecurityModals.Redirect ? (
+ ) : props.type === SecurityModals.RecordAudio ? (
+
+ ) : props.type === SecurityModals.RecordVideo ? (
+
+ ) : props.type === SecurityModals.ReadClipboard ? (
+
+ ) : props.type === SecurityModals.Notify ? (
+
) : null}
diff --git a/src/containers/tw-security-manager.jsx b/src/containers/tw-security-manager.jsx
index 74e1c6e8a..6acb0d480 100644
--- a/src/containers/tw-security-manager.jsx
+++ b/src/containers/tw-security-manager.jsx
@@ -98,34 +98,46 @@ const parseURL = url => {
return parsed;
};
+let allowedAudio = false;
+let allowedVideo = false;
+let allowedReadClipboard = false;
+let allowedNotify = false;
+
+const SECURITY_MANAGER_METHODS = [
+ 'getSandboxMode',
+ 'canLoadExtensionFromProject',
+ 'canFetch',
+ 'canOpenWindow',
+ 'canRedirect',
+ 'canRecordAudio',
+ 'canRecordVideo',
+ 'canReadClipboard',
+ 'canNotify'
+];
+
class TWSecurityManagerComponent extends React.Component {
constructor (props) {
super(props);
bindAll(this, [
- 'getSandboxMode',
- 'canLoadExtensionFromProject',
- 'canFetch',
- 'canOpenWindow',
- 'canRedirect',
'handleAllowed',
'handleDenied'
]);
+ bindAll(this, SECURITY_MANAGER_METHODS);
this.nextModalCallbacks = [];
this.modalLocked = false;
this.state = {
type: null,
data: null,
- callback: null
+ callback: null,
+ modalCount: 0
};
}
componentDidMount () {
const securityManager = this.props.vm.extensionManager.securityManager;
- securityManager.getSandboxMode = this.getSandboxMode;
- securityManager.canLoadExtensionFromProject = this.canLoadExtensionFromProject;
- securityManager.canFetch = this.canFetch;
- securityManager.canOpenWindow = this.canOpenWindow;
- securityManager.canRedirect = this.canRedirect;
+ for (const method of SECURITY_MANAGER_METHODS) {
+ securityManager[method] = this[method];
+ }
}
// eslint-disable-next-line valid-jsdoc
@@ -162,11 +174,12 @@ class TWSecurityManagerComponent extends React.Component {
const showModal = async (type, data) => {
const result = await new Promise(resolve => {
- this.setState({
+ this.setState(oldState => ({
type,
data,
- callback: resolve
- });
+ callback: resolve,
+ modalCount: oldState.modalCount + 1
+ }));
});
releaseLock();
return result;
@@ -291,6 +304,50 @@ class TWSecurityManagerComponent extends React.Component {
});
}
+ /**
+ * @returns {Promise} True if audio can be recorded
+ */
+ async canRecordAudio () {
+ if (!allowedAudio) {
+ const {showModal} = await this.acquireModalLock();
+ allowedAudio = await showModal(SecurityModals.RecordAudio);
+ }
+ return allowedAudio;
+ }
+
+ /**
+ * @returns {Promise} True if video can be recorded
+ */
+ async canRecordVideo () {
+ if (!allowedVideo) {
+ const {showModal} = await this.acquireModalLock();
+ allowedVideo = await showModal(SecurityModals.RecordVideo);
+ }
+ return allowedVideo;
+ }
+
+ /**
+ * @returns {Promise} True if the clipboard can be read
+ */
+ async canReadClipboard () {
+ if (!allowedReadClipboard) {
+ const {showModal} = await this.acquireModalLock();
+ allowedReadClipboard = await showModal(SecurityModals.ReadClipboard);
+ }
+ return allowedReadClipboard;
+ }
+
+ /**
+ * @returns {Promise} True if the notifications are allowed
+ */
+ async canNotify () {
+ if (!allowedNotify) {
+ const {showModal} = await this.acquireModalLock();
+ allowedNotify = await showModal(SecurityModals.Notify);
+ }
+ return allowedNotify;
+ }
+
render () {
if (this.state.type) {
return (
@@ -299,6 +356,7 @@ class TWSecurityManagerComponent extends React.Component {
data={this.state.data}
onAllowed={this.handleAllowed}
onDenied={this.handleDenied}
+ key={this.state.modalCount}
/>
);
}
@@ -309,13 +367,12 @@ class TWSecurityManagerComponent extends React.Component {
TWSecurityManagerComponent.propTypes = {
vm: PropTypes.shape({
extensionManager: PropTypes.shape({
- securityManager: PropTypes.shape({
- getSandboxMode: PropTypes.func.isRequired,
- canLoadExtensionFromProject: PropTypes.func.isRequired,
- canFetch: PropTypes.func.isRequired,
- canOpenWindow: PropTypes.func.isRequired,
- canRedirect: PropTypes.func.isRequired
- }).isRequired
+ securityManager: PropTypes.shape(
+ SECURITY_MANAGER_METHODS.reduce((obj, method) => {
+ obj[method] = PropTypes.func.isRequired;
+ return obj;
+ }, {})
+ ).isRequired
}).isRequired
}).isRequired
};
diff --git a/src/lib/tw-security-manager-constants.js b/src/lib/tw-security-manager-constants.js
index 7b0260251..edfab5079 100644
--- a/src/lib/tw-security-manager-constants.js
+++ b/src/lib/tw-security-manager-constants.js
@@ -2,7 +2,11 @@ const SecurityModals = {
LoadExtension: 'LoadExtension',
Fetch: 'Fetch',
OpenWindow: 'OpenWindow',
- Redirect: 'Redirect'
+ Redirect: 'Redirect',
+ RecordAudio: 'RecordAudio',
+ RecordVideo: 'RecordVideo',
+ ReadClipboard: 'ReadClipboard',
+ Notify: 'Notify'
};
export default SecurityModals;