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

Commit

Permalink
Merge pull request #14395 from brave/tor/progress-ui
Browse files Browse the repository at this point in the history
Add tor loading progress UI
  • Loading branch information
petemill authored Jun 14, 2018
2 parents 051109c + 087eeb0 commit 3070a3e
Show file tree
Hide file tree
Showing 12 changed files with 218 additions and 22 deletions.
28 changes: 28 additions & 0 deletions app/browser/reducers/torReducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

'use strict'

const filtering = require('../../filtering')
const appConstants = require('../../../js/constants/appConstants')

const torReducer = (state, action) => {
switch (action.actionType) {
case appConstants.APP_SET_TOR_NEW_IDENTITY:
filtering.setTorNewIdentity(action.url, action.tabId)
break
case appConstants.APP_ON_TOR_INIT_ERROR:
state = state.setIn(['tor', 'initializationError'], action.message)
break
case appConstants.APP_ON_TOR_INIT_SUCCESS:
state = state.setIn(['tor', 'initializationError'], false).setIn(['tor', 'percentInitialized'], null)
break
case appConstants.APP_ON_TOR_INIT_PERCENTAGE:
state = state.setIn(['tor', 'percentInitialized'], action.percentage)
break
}
return state
}

module.exports = torReducer
9 changes: 8 additions & 1 deletion app/extensions/brave/locales/en-US/app.properties
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,11 @@ tabsSuggestionTitle=Tabs
topSiteSuggestionTitle=Top Site
torrentBlockedInTor=For your privacy, torrents are blocked in private tabs when Tor is enabled.
torrentWarningOk=Ok
torConnectionError=Unable to connect to the Tor network
torConnectionErrorInfo=Brave could not make a connection to the Tor network. Disable Tor to continue private browsing without Tor protection.
torConnectionErrorDisable=Disable Tor
torConnectionErrorRetry=To retry connecting to the Tor network,
torConnectionErrorRestart=click here to restart Brave.
turnOffNotifications=Turn off notifications
unknownError=Oops, something went wrong.
unmuteTab=Unmute tab
Expand All @@ -257,7 +262,9 @@ updateNow=Update
updateOops=Oops!
updateRequiresRelaunch=Requires a quick relaunch…
updateViewLog=View log
urlbar.placeholder=Enter a URL or search term
urlbarPlaceholder=Enter a URL or search term
urlbarPlaceholderTorSuccess=Successfully connected to the Tor network!
urlbarPlaceholderTorProgress=Connecting to the Tor network
urlCopied=URL copied to clipboard
useBrave=Use Brave
verifiedPublisher.title=This is a verified publisher. Click to enable this publisher for payments
Expand Down
47 changes: 37 additions & 10 deletions app/filtering.js
Original file line number Diff line number Diff line change
Expand Up @@ -726,7 +726,11 @@ const initPartition = (partition) => {
options.parent_partition = ''
}
if (isTorPartition) {
setupTor()
try {
setupTor()
} catch (e) {
appActions.onTorInitError(`Could not start Tor: ${e}`)
}
// TODO(riastradh): Duplicate logic in app/browser/tabs.js.
options.isolated_storage = true
options.parent_partition = ''
Expand Down Expand Up @@ -756,40 +760,63 @@ const initPartition = (partition) => {
module.exports.initPartition = initPartition

function setupTor () {
let torInitialized = null
// If Tor has not successfully initialized or thrown an error within 20s,
// assume it's broken.
setTimeout(() => {
if (torInitialized === null) {
appActions.onTorInitError(`Tor could not start.`)
}
}, 20000)
// Set up the tor daemon watcher. (NOTE: We don't actually start
// the tor daemon here; that happens in C++ code. But we do talk to
// its control socket.)
const torDaemon = new tor.TorDaemon()
torDaemon.setup((err) => {
if (err) {
console.log(`tor: failed to make directories: ${err}`)
appActions.onTorInitError(`Tor failed to make directories: ${err}`)
torInitialized = false
return
}
torDaemon.on('exit', () => console.log('tor: daemon exited'))
torDaemon.on('exit', () => {
appActions.onTorInitError('The Tor process has stopped.')
torInitialized = false
})
torDaemon.on('launch', (socksAddr) => {
console.log(`tor: daemon listens on ${socksAddr}`)
const bootstrapped = (err, progress) => {
// TODO(riastradh): Visually update a progress bar!
if (err) {
console.log(`tor: bootstrap error: ${err}`)
appActions.onTorInitError(`Tor bootstrap error: ${err}`)
torInitialized = false
return
}
console.log(`tor: bootstrapped ${progress}%`)
appActions.onTorInitPercentage(progress)
}
const circuitEstablished = (err, ok) => {
if (ok) {
console.log(`tor: ready`)
console.log('Tor ready!')
appActions.onTorInitSuccess()
torInitialized = true
} else {
console.log(err ? `tor: not ready: ${err}` : `tor: not ready`)
if (err) {
appActions.onTorInitError(`Tor not ready: ${err}`)
torInitialized = false
} else {
// Simply log the error but don't show error UI since Tor might
// finish opening a circuit.
console.log('tor still not ready')
}
}
}
torDaemon.onBootstrap(bootstrapped, (err) => {
if (err) {
console.log(`tor: error subscribing to bootstrap: ${err}`)
appActions.onTorInitError(`Tor error bootstrapping: ${err}`)
torInitialized = false
}
torDaemon.onCircuitEstablished(circuitEstablished, (err) => {
if (err) {
console.log(`tor: error subscribing to circuit ready: ${err}`)
appActions.onTorInitError(`Tor error opening a circuit: ${err}`)
torInitialized = false
}
})
})
Expand Down
4 changes: 4 additions & 0 deletions app/locale.js
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ var rendererIdentifiers = function () {
'smartphoneTitle',
'updateLater',
'updateHello',
'urlbarPlaceholder',
'urlbarPlaceholderTorSuccess',
'urlbarPlaceholderTorProgress',
'torConnectionError',
// notifications
'notificationPasswordWithUserName',
'notificationUpdatePasswordWithUserName',
Expand Down
7 changes: 5 additions & 2 deletions app/renderer/components/main/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,8 @@ class Main extends React.Component {
const widevinePanelDetail = currentWindow.get('widevinePanelDetail', Immutable.Map())
const loginRequiredDetails = basicAuthState.getLoginRequiredDetail(state, activeTabId)
const focused = isFocused(state)
const isTor = frameStateUtil.isTor(activeFrame)
const torConnectionError = state.getIn(['tor', 'initializationError'])

const props = {}
// used in renderer
Expand All @@ -553,8 +555,9 @@ class Main extends React.Component {
props.captionButtonsVisible = isWindows
props.showContextMenu = currentWindow.has('contextMenuDetail')
props.showPopupWindow = currentWindow.has('popupWindowDetail')
props.showSiteInfo = currentWindow.getIn(['ui', 'siteInfo', 'isVisible']) &&
!isSourceAboutUrl(activeFrame.get('location'))
props.showSiteInfo = (currentWindow.getIn(['ui', 'siteInfo', 'isVisible']) &&
!isSourceAboutUrl(activeFrame.get('location'))) ||
(torConnectionError && isTor)
props.showBravery = shieldState.braveShieldsEnabled(activeFrame) &&
!!currentWindow.get('braveryPanelDetail')
props.showClearData = currentWindow.getIn(['ui', 'isClearBrowsingDataPanelVisible'], false)
Expand Down
64 changes: 63 additions & 1 deletion app/renderer/components/main/siteInfo.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,16 @@ const urlUtil = require('../../../../js/lib/urlutil')
const globalStyles = require('../styles/global')
const commonStyles = require('../styles/commonStyles')

// Constants
const settings = require('../../../../js/constants/settings')

class SiteInfo extends React.Component {
constructor (props) {
super(props)
this.onAllowRunInsecureContent = this.onAllowRunInsecureContent.bind(this)
this.onDenyRunInsecureContent = this.onDenyRunInsecureContent.bind(this)
this.onViewCertificate = this.onViewCertificate.bind(this)
this.onDisableTor = this.onDisableTor.bind(this)
}

onAllowRunInsecureContent () {
Expand All @@ -61,7 +65,20 @@ class SiteInfo extends React.Component {
windowActions.setSiteInfoVisible(false)
}

onDisableTor () {
appActions.changeSetting(settings.USE_TOR_PRIVATE_TABS, false)
appActions.recreateTorTab(false, this.props.activeTabId,
this.props.activeTabIndex)
}

onRestart () {
appActions.shuttingDown(true)
}

get secureIcon () {
if (this.props.torConnectionError) {
return <div className={css(styles.connectionInfo__header)} data-l10n-id='torConnectionError' />
}
if (this.props.isFullySecured) {
// fully secure
return <div className={css(styles.secureIcon)}>
Expand Down Expand Up @@ -149,7 +166,26 @@ class SiteInfo extends React.Component {
site: this.props.location
}

if (this.props.maybePhishingLocation) {
if (this.props.torConnectionError) {
// Log the error for advanced users to debug
console.log('Tor connection error:', this.props.torConnectionError)
return <div>
<div className={css(styles.torBody)}>
<div className={css(styles.torConnectionInfo)} data-l10n-id='torConnectionErrorInfo' />
<Button
l10nId='torConnectionErrorDisable'
className='primaryButton'
onClick={this.onDisableTor}
/>
</div>
<div className={css(styles.torFooter)}>
<div data-l10n-id='torConnectionErrorRetry' />
<div data-l10n-id='torConnectionErrorRestart'
className={css(styles.link)}
onClick={this.onRestart} />
</div>
</div>
} else if (this.props.maybePhishingLocation) {
return <div className={css(styles.connectionInfo)}>
<div data-l10n-id='phishingConnectionInfo' data-test-id='phishingConnectionInfo' />
</div>
Expand Down Expand Up @@ -232,10 +268,12 @@ class SiteInfo extends React.Component {
props.secureConnection = isSecure === true
props.partiallySecureConnection = isSecure === 1
props.certErrorConnection = isSecure === 2
props.torConnectionError = frameStateUtil.isTor(activeFrame) && state.getIn(['tor', 'initializationError'])

// used in other function
props.isPrivate = activeFrame.get('isPrivate')
props.activeTabId = activeFrame.get('tabId', tabState.TAB_ID_NONE)
props.activeTabIndex = frameStateUtil.getIndexByTabId(currentWindow, props.activeTabId)

return props
}
Expand Down Expand Up @@ -287,12 +325,36 @@ const styles = StyleSheet.create({
margin: `${globalStyles.spacing.dialogInsideMargin} 0 0 ${globalStyles.spacing.dialogInsideMargin}`
},

connectionInfo__header: {
color: globalStyles.color.braveOrange,
fontSize: '1rem'
},

connectionInfo__viewCertificateButton: {
display: 'flex',
justifyContent: 'flex-end',
marginTop: globalStyles.spacing.dialogInsideMargin
},

torConnectionInfo: {
marginTop: '15px',
marginBottom: '20px'
},

torBody: {
paddingBottom: '15px',
lineHeight: '1.5em'
},

torFooter: {
lineHeight: '1.5em'
},

link: {
color: globalStyles.color.braveOrange,
cursor: 'pointer'
},

siteInfo: {
maxHeight: '300px',
maxWidth: '400px',
Expand Down
31 changes: 30 additions & 1 deletion app/renderer/components/navigation/urlBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const {normalizeLocation, getNormalizedSuggestion} = require('../../../common/li
const isDarwin = require('../../../common/lib/platformUtil').isDarwin()
const publisherUtil = require('../../../common/lib/publisherUtil')
const historyUtil = require('../../../common/lib/historyUtil')
const locale = require('../../../../js/l10n')

// Icons
const iconNoScript = require('../../../../img/url-bar-no-script.svg')
Expand Down Expand Up @@ -390,6 +391,23 @@ class UrlBar extends React.Component {
}
}

get placeholderValue () {
if (this.props.isTor) {
if (this.props.torInitializationError) {
return `${locale.translation('torConnectionError')}.`
} else if (this.props.torPercentInitialized) {
// Don't show 100% since it sometimes gets stuck at 100%
const percentInitialized = this.props.torPercentInitialized === '100' ? '99' : this.props.torPercentInitialized
return `${locale.translation('urlbarPlaceholderTorProgress')}: ${percentInitialized}%...`
} else if (this.props.torInitializationError === false) {
return locale.translation('urlbarPlaceholderTorSuccess')
} else {
return `${locale.translation('urlbarPlaceholderTorProgress')}...`
}
}
return locale.translation('urlbarPlaceholder')
}

get titleValue () {
// For about:newtab we don't want the top of the browser saying New Tab
// Instead just show "Brave"
Expand Down Expand Up @@ -437,6 +455,7 @@ class UrlBar extends React.Component {
const selectedIndex = urlbar.getIn(['suggestions', 'selectedIndex'])
const allSiteSettings = siteSettingsState.getAllSiteSettings(state, activeFrameIsPrivate)
const braverySettings = siteSettings.getSiteSettingsForURL(allSiteSettings, location)
const isTor = frameStateUtil.isTor(activeFrame)

// TODO(bridiver) - these definitely needs a helpers
const publisherKey = ledgerState.getLocationProp(state, baseUrl, 'publisher')
Expand All @@ -460,6 +479,9 @@ class UrlBar extends React.Component {
props.startLoadTime = activeFrame.get('startLoadTime')
props.endLoadTime = activeFrame.get('endLoadTime')
props.loading = activeFrame.get('loading')
props.isTor = isTor
props.torPercentInitialized = state.getIn(['tor', 'percentInitialized'])
props.torInitializationError = state.getIn(['tor', 'initializationError'])
props.showDisplayTime = !props.titleMode && props.displayURL === location
props.showNoScriptInfo = enableNoScript && scriptsBlocked && scriptsBlocked.size
props.evCert = activeFrame.getIn(['security', 'evCert'])
Expand Down Expand Up @@ -495,6 +517,12 @@ class UrlBar extends React.Component {
return <span className='evCert' title={this.props.evCert}> {this.props.evCert} </span>
}

get shouldDisable () {
return (this.props.displayURL === undefined && this.loadTime === '') ||
(this.props.isTor &&
(this.props.torPercentInitialized || this.props.torInitializationError !== false))
}

render () {
const urlbarIconContainer = this.props.evCert
? (<div className='urlbarIconContainer'>
Expand Down Expand Up @@ -526,7 +554,7 @@ class UrlBar extends React.Component {
</div>
: <input type='text'
spellCheck='false'
disabled={this.props.displayURL === undefined && this.loadTime === ''}
disabled={this.shouldDisable}
onFocus={this.onFocus}
onBlur={this.onBlur}
onKeyDown={this.onKeyDown}
Expand All @@ -539,6 +567,7 @@ class UrlBar extends React.Component {
onClick={this.onClick}
onContextMenu={this.onContextMenu}
data-l10n-id='urlbar'
placeholder={this.placeholderValue}
className={cx({
testHookLoadDone: !this.props.loading
})}
Expand Down
3 changes: 3 additions & 0 deletions app/sessionStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,8 @@ module.exports.cleanAppData = (immutableData, isShutdown) => {
immutableData = immutableData.set('notifications', Immutable.List())
// Delete temp site settings
immutableData = immutableData.set('temporarySiteSettings', Immutable.Map())
// Delete Tor init state
immutableData = immutableData.set('tor', Immutable.Map())

if (immutableData.getIn(['settings', settings.CHECK_DEFAULT_ON_STARTUP]) === true) {
// Delete defaultBrowserCheckComplete state since this is checked on startup
Expand Down Expand Up @@ -1135,6 +1137,7 @@ module.exports.defaultAppState = () => {
passwords: [],
notifications: [],
temporarySiteSettings: {},
tor: {},
autofill: {
addresses: {
guid: [],
Expand Down
4 changes: 4 additions & 0 deletions docs/state.md
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,10 @@ AppStore
// Same as siteSettings but never gets written to disk
// XXX: This was intended for Private Browsing but is currently unused.
},
tor: {
percentInitialized: number, // percentage initialized
initializationError: string|boolean, // error message. false means successfully initialized.
},
updates: {
lastCheckTimestamp: boolean,
metadata: {
Expand Down
Loading

0 comments on commit 3070a3e

Please sign in to comment.