working without injection

feature/default_network_editable
brunobar79 6 years ago
parent d5929e5c42
commit 74fd6d1d12
  1. 3
      app/manifest.json
  2. 41
      app/scripts/contentscript.js
  3. 18
      app/scripts/metamask-controller.js
  4. 6
      app/scripts/platforms/extension.js
  5. 41
      ui/app/actions.js
  6. 7
      ui/app/app.js
  7. 2
      ui/app/components/qr-scanner/index.js
  8. 152
      ui/app/components/qr-scanner/qr-scanner.component.js
  9. 20
      ui/app/components/send/send.component.js
  10. 2
      ui/app/components/send/send.container.js
  11. 5
      ui/app/components/send/send.selectors.js
  12. 20
      ui/app/reducers/app.js
  13. 1
      ui/app/selectors.js

@ -76,5 +76,6 @@
"ids": [ "ids": [
"*" "*"
] ]
} },
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'"
} }

@ -6,7 +6,6 @@ const PongStream = require('ping-pong-stream/pong')
const ObjectMultiplex = require('obj-multiplex') const ObjectMultiplex = require('obj-multiplex')
const extension = require('extensionizer') const extension = require('extensionizer')
const PortStream = require('./lib/port-stream.js') const PortStream = require('./lib/port-stream.js')
const Instascan = require('instascan')
const inpageContent = fs.readFileSync(path.join(__dirname, '..', '..', 'dist', 'chrome', 'inpage.js')).toString() const inpageContent = fs.readFileSync(path.join(__dirname, '..', '..', 'dist', 'chrome', 'inpage.js')).toString()
const inpageSuffix = '//# sourceURL=' + extension.extension.getURL('inpage.js') + '\n' const inpageSuffix = '//# sourceURL=' + extension.extension.getURL('inpage.js') + '\n'
@ -201,43 +200,3 @@ function redirectToPhishingWarning () {
window.location.href = 'https://metamask.io/phishing.html' window.location.href = 'https://metamask.io/phishing.html'
} }
function initQrCodeScanner () {
// Append preview div
const preview = document.createElement('div')
preview.id = 'metamask-preview-wrapper'
preview.style = 'position:fixed; top: 20px; left: 20px; width: 300px; height: 300px; overflow: hidden; z-index: 999999999;'
const previewVideo = document.createElement('video')
previewVideo.id = 'metamask-preview-video'
previewVideo.style = 'width: 100%; height: 100%; object-fit: none; margin-left: -10%; margin-top: 10%;'
preview.appendChild(previewVideo)
document.body.appendChild(preview)
console.log('injected')
const scanner = new Instascan.Scanner({
video: document.getElementById('metamask-preview-video'),
backgroundScan: false,
continuous: true,
})
scanner.addListener('scan', function (content) {
scanner.stop().then(_ => {
extension.runtime.sendMessage({
action: 'qr-code-scanner-data',
data: content,
})
document.getElementById('metamask-preview-wrapper').parentElement.removeChild(document.getElementById('metamask-preview-wrapper'))
})
})
Instascan.Camera.getCameras().then(function (cameras) {
if (cameras.length > 0) {
scanner.start(cameras[0])
} else {
console.error('No cameras found.')
}
}).catch(function (e) {
console.error(e)
})
}
extension.runtime.onMessage.addListener(({ action }) => {
initQrCodeScanner()
})

@ -380,9 +380,6 @@ module.exports = class MetamaskController extends EventEmitter {
// TREZOR // TREZOR
unlockTrezorAccount: nodeify(this.unlockTrezorAccount, this), unlockTrezorAccount: nodeify(this.unlockTrezorAccount, this),
// QR code scanner
scanQrCode: nodeify(this.scanQrCode, this),
// vault management // vault management
submitPassword: nodeify(this.submitPassword, this), submitPassword: nodeify(this.submitPassword, this),
@ -658,21 +655,6 @@ module.exports = class MetamaskController extends EventEmitter {
return { ...keyState, identities } return { ...keyState, identities }
} }
scanQrCode () {
return new Promise((resolve, reject) => {
// Tell contentscript to inject the QR reader
this.platform.sendMessageToActiveTab('qr-code-scanner-init')
// Wait for the scanner to send something back
this.platform.addMessageListener(({ action, data }) => {
if (action && action === 'qr-code-scanner-data') {
const normalizedAddress = data.replace('ethereum:', '')
resolve(normalizedAddress)
}
})
})
}
// //
// Account Management // Account Management
// //

@ -36,12 +36,6 @@ class ExtensionPlatform {
extension.runtime.onMessage.addListener(cb) extension.runtime.onMessage.addListener(cb)
} }
sendMessageToActiveTab (message, query = {}) {
extension.tabs.query(query, tabs => {
const activeTab = tabs.filter(tab => tab.active)[0]
extension.tabs.sendMessage(activeTab.id, message)
})
}
} }
module.exports = ExtensionPlatform module.exports = ExtensionPlatform

@ -31,6 +31,12 @@ var actions = {
ALERT_CLOSE: 'UI_ALERT_CLOSE', ALERT_CLOSE: 'UI_ALERT_CLOSE',
showAlert: showAlert, showAlert: showAlert,
hideAlert: hideAlert, hideAlert: hideAlert,
QR_SCANNER_OPEN: 'UI_QR_SCANNER_OPEN',
QR_SCANNER_CLOSE: 'UI_QR_SCANNER_CLOSE',
QR_CODE_DETECTED: 'UI_QR_CODE_DETECTED',
showQrScanner,
hideQrScanner,
qrCodeDetected,
// network dropdown open // network dropdown open
NETWORK_DROPDOWN_OPEN: 'UI_NETWORK_DROPDOWN_OPEN', NETWORK_DROPDOWN_OPEN: 'UI_NETWORK_DROPDOWN_OPEN',
NETWORK_DROPDOWN_CLOSE: 'UI_NETWORK_DROPDOWN_CLOSE', NETWORK_DROPDOWN_CLOSE: 'UI_NETWORK_DROPDOWN_CLOSE',
@ -1752,6 +1758,25 @@ function hideAlert () {
} }
} }
function showQrScanner () {
return {
type: actions.QR_SCANNER_OPEN,
}
}
function qrCodeDetected (qrCodeData) {
return {
type: actions.QR_CODE_DETECTED,
value: qrCodeData,
}
}
function hideQrScanner () {
return {
type: actions.QR_SCANNER_CLOSE,
}
}
function showLoadingIndication (message) { function showLoadingIndication (message) {
return { return {
@ -2197,21 +2222,7 @@ function clearPendingTokens () {
} }
function scanQrCode () { function scanQrCode () {
log.debug(`background.scanQrCode`)
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch(actions.showLoadingIndication()) dispatch(actions.showQrScanner())
return new Promise((resolve, reject) => {
background.scanQrCode((err, data) => {
log.debug(`background.scanQrCode resolved!`, err, data)
if (err) {
log.error(err)
dispatch(actions.displayWarning(err.message))
return reject(err)
}
dispatch(actions.hideLoadingIndication())
return resolve(data)
})
})
} }
} }

@ -39,6 +39,8 @@ const Modal = require('./components/modals/index').Modal
// Global Alert // Global Alert
const Alert = require('./components/alert') const Alert = require('./components/alert')
const QrScanner = require('./components/qr-scanner')
const AppHeader = require('./components/app-header') const AppHeader = require('./components/app-header')
import UnlockPage from './components/pages/unlock-page' import UnlockPage from './components/pages/unlock-page'
@ -132,6 +134,8 @@ class App extends Component {
// global alert // global alert
h(Alert, {visible: this.props.alertOpen, msg: alertMessage}), h(Alert, {visible: this.props.alertOpen, msg: alertMessage}),
h(QrScanner, {visible: this.props.qrScannerOpen}),
h(AppHeader), h(AppHeader),
// sidebar // sidebar
@ -270,6 +274,7 @@ App.propTypes = {
currentView: PropTypes.object, currentView: PropTypes.object,
sidebarOpen: PropTypes.bool, sidebarOpen: PropTypes.bool,
alertOpen: PropTypes.bool, alertOpen: PropTypes.bool,
qrScannerOpen: PropTypes.bool,
hideSidebar: PropTypes.func, hideSidebar: PropTypes.func,
isMascara: PropTypes.bool, isMascara: PropTypes.bool,
isOnboarding: PropTypes.bool, isOnboarding: PropTypes.bool,
@ -306,6 +311,7 @@ function mapStateToProps (state) {
networkDropdownOpen, networkDropdownOpen,
sidebarOpen, sidebarOpen,
alertOpen, alertOpen,
qrScannerOpen,
alertMessage, alertMessage,
isLoading, isLoading,
loadingMessage, loadingMessage,
@ -333,6 +339,7 @@ function mapStateToProps (state) {
networkDropdownOpen, networkDropdownOpen,
sidebarOpen, sidebarOpen,
alertOpen, alertOpen,
qrScannerOpen,
alertMessage, alertMessage,
isLoading, isLoading,
loadingMessage, loadingMessage,

@ -0,0 +1,2 @@
import QrScanner from './qr-scanner.component'
module.exports = QrScanner

@ -0,0 +1,152 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import { hideQrScanner, qrCodeDetected} from '../../actions'
import Instascan from 'instascan'
class QrScanner extends Component {
static propTypes = {
visible: PropTypes.bool,
hideQrScanner: PropTypes.func,
qrCodeDetected: PropTypes.func,
}
constructor (props) {
super(props)
this.state = {
msg: 'Place the QR code in front of your camera so we can read it...',
}
this.scanning = false
}
parseContent (content) {
let type = 'unknown'
let values = {}
// Here we could add more cases
// To parse other codes (transactions for ex.)
if (content.split('ethereum:').length > 1) {
type = 'address'
values = {'address': content.split('ethereum:')[1] }
}
return {type, values}
}
componentDidUpdate () {
if (this.props.visible && this.camera && !this.scanning) {
const scanner = new Instascan.Scanner({
video: this.camera,
backgroundScan: false,
continuous: true,
})
scanner.addListener('scan', (content) => {
scanner.stop().then(_ => {
const result = this.parseContent(content)
if (result.type !== 'unknown') {
console.log('QR-SCANNER: CODE DETECTED', result)
this.props.qrCodeDetected(result)
this.props.hideQrScanner()
} else {
this.setState({msg: 'Error: We couldn\'t identify that QR code'})
}
})
})
Instascan.Camera.getCameras().then((cameras) => {
if (cameras.length > 0) {
scanner.start(cameras[0])
console.log('QR-SCANNER: started scanning with camera', cameras[0])
} else {
console.log('QR-SCANNER: no cameras found')
}
}).catch(function (e) {
console.error(e)
})
this.scanning = true
}
}
render () {
const { visible } = this.props
if (!visible) {
return null
}
return (
<div className={'qr-code-modal-wrapper'}>
<div className={'qr-scanner'}
style={{
position: 'fixed',
top: '50%',
left: '50%',
zIndex: 1050,
minWidth: '320px',
minHeight: '400px',
maxWidth: '300px',
maxHeight: '300px',
transform: 'translate(-50%, -50%)',
backgroundColor: '#ffffff',
padding: '15px',
}}
>
<h3 style={{
textAlign: 'center',
marginBottom: '20px',
}}>
Scan QR code
</h3>
<div
className={'qr-code-video-wrapper'}
style={{
overflow: 'hidden',
width: '100%',
height: '275px',
}}>
<video
style={{
width: 'auto',
height: '275px',
marginLeft: '-15%',
}}
ref={(cam) => {
this.camera = cam
}}
/>
</div>
<div className={'qr-code-help'} style={{textAlign: 'center', fontSize: '12px', padding: '15px'}}>
{this.state.msg}
</div>
</div>
<div
className={'qr-code-modal-overlay'}
style={{
position: 'fixed',
top: '0',
right: '0',
bottom: '0',
left: '0',
zIndex: '1040',
backgroundColor: 'rgba(0, 0, 0, 0.5)',
animationFillMode: 'forwards',
animationDuration: '0.3s',
animationName: 'anim_171532470906313',
animationTimingFunction: 'ease-out',
}}
onClick={_ => this.props.hideQrScanner() }
/>
</div>
)
}
}
function mapDispatchToProps (dispatch) {
return {
hideQrScanner: () => dispatch(hideQrScanner()),
qrCodeDetected: (data) => dispatch(qrCodeDetected(data)),
}
}
function mapStateToProps (state) {
return {}
}
export default connect(mapStateToProps, mapDispatchToProps)(QrScanner)

@ -39,16 +39,26 @@ export default class SendTransactionScreen extends PersistentForm {
updateSendErrors: PropTypes.func, updateSendErrors: PropTypes.func,
updateSendTokenBalance: PropTypes.func, updateSendTokenBalance: PropTypes.func,
scanQrCode: PropTypes.func, scanQrCode: PropTypes.func,
qrCodeData: PropTypes.object,
}; };
static contextTypes = { static contextTypes = {
t: PropTypes.func, t: PropTypes.func,
}; };
scanQrCode = async () => { componentWillReceiveProps (nextProps) {
const scannedAddress = await this.props.scanQrCode() if (nextProps.qrCodeData) {
this.props.updateSendTo(scannedAddress) if (nextProps.qrCodeData.type === 'address') {
this.updateGas({ to: scannedAddress }) const scannedAddress = nextProps.qrCodeData.values.address.toLowerCase()
const currentAddress = this.props.to && this.props.to.toLowerCase()
if (currentAddress !== scannedAddress) {
this.props.updateSendTo(scannedAddress)
this.updateGas({ to: scannedAddress })
// Here we should clear props.qrCodeData
}
}
}
} }
updateGas ({ to: updatedToAddress, amount: value } = {}) { updateGas ({ to: updatedToAddress, amount: value } = {}) {
@ -179,7 +189,7 @@ export default class SendTransactionScreen extends PersistentForm {
<SendHeader history={history}/> <SendHeader history={history}/>
<SendContent <SendContent
updateGas={(updateData) => this.updateGas(updateData)} updateGas={(updateData) => this.updateGas(updateData)}
scanQrCode={_ => this.scanQrCode()} scanQrCode={_ => this.props.scanQrCode()}
/> />
<SendFooter history={history}/> <SendFooter history={history}/>
</div> </div>

@ -21,6 +21,7 @@ import {
getSendFromObject, getSendFromObject,
getSendTo, getSendTo,
getTokenBalance, getTokenBalance,
getQrCodeData,
} from './send.selectors' } from './send.selectors'
import { import {
updateSendTo, updateSendTo,
@ -62,6 +63,7 @@ function mapStateToProps (state) {
tokenBalance: getTokenBalance(state), tokenBalance: getTokenBalance(state),
tokenContract: getSelectedTokenContract(state), tokenContract: getSelectedTokenContract(state),
tokenToFiatRate: getSelectedTokenToFiatRate(state), tokenToFiatRate: getSelectedTokenToFiatRate(state),
qrCodeData: getQrCodeData(state),
} }
} }

@ -46,6 +46,7 @@ const selectors = {
getTokenExchangeRate, getTokenExchangeRate,
getUnapprovedTxs, getUnapprovedTxs,
transactionsSelector, transactionsSelector,
getQrCodeData,
} }
module.exports = selectors module.exports = selectors
@ -282,3 +283,7 @@ function transactionsSelector (state) {
: txsToRender : txsToRender
.sort((a, b) => b.time - a.time) .sort((a, b) => b.time - a.time)
} }
function getQrCodeData (state) {
return state.appState.qrCodeData
}

@ -50,7 +50,9 @@ function reduceApp (state, action) {
}, },
sidebarOpen: false, sidebarOpen: false,
alertOpen: false, alertOpen: false,
qrScannerOpen: false,
alertMessage: null, alertMessage: null,
qrCodeData: null,
networkDropdownOpen: false, networkDropdownOpen: false,
currentView: seedWords ? seedConfView : defaultView, currentView: seedWords ? seedConfView : defaultView,
accountDetail: { accountDetail: {
@ -90,7 +92,7 @@ function reduceApp (state, action) {
sidebarOpen: false, sidebarOpen: false,
}) })
// sidebar methods // alert methods
case actions.ALERT_OPEN: case actions.ALERT_OPEN:
return extend(appState, { return extend(appState, {
alertOpen: true, alertOpen: true,
@ -102,6 +104,22 @@ function reduceApp (state, action) {
alertOpen: false, alertOpen: false,
alertMessage: null, alertMessage: null,
}) })
// qr scanner methods
case actions.QR_SCANNER_OPEN:
return extend(appState, {
qrScannerOpen: true,
})
case actions.QR_SCANNER_CLOSE:
return extend(appState, {
qrScannerOpen: false,
})
case actions.QR_CODE_DETECTED:
return extend(appState, {
qrCodeData: action.value,
})
// modal methods: // modal methods:
case actions.MODAL_OPEN: case actions.MODAL_OPEN:

@ -194,3 +194,4 @@ function getTotalUnapprovedCount ({ metamask }) {
return Object.keys(unapprovedTxs).length + unapprovedMsgCount + unapprovedPersonalMsgCount + return Object.keys(unapprovedTxs).length + unapprovedMsgCount + unapprovedPersonalMsgCount +
unapprovedTypedMessagesCount unapprovedTypedMessagesCount
} }

Loading…
Cancel
Save