diff --git a/app/common/state/defaultBrowserState.js b/app/common/state/defaultBrowserState.js
new file mode 100644
index 00000000000..9e2c833e4c8
--- /dev/null
+++ b/app/common/state/defaultBrowserState.js
@@ -0,0 +1,12 @@
+/* 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/. */
+
+const getSetting = require('../../../js/settings').getSetting
+const settings = require('../../../js/constants/settings')
+
+module.exports.shouldDisplayDialog = (state) => {
+ return !getSetting(settings.IS_DEFAULT_BROWSER) &&
+ !state.get('defaultBrowserCheckComplete') &&
+ getSetting(settings.CHECK_DEFAULT_ON_STARTUP)
+}
diff --git a/app/extensions/brave/locales/en-US/app.properties b/app/extensions/brave/locales/en-US/app.properties
index 905a6555e2e..787ea3ffa7c 100644
--- a/app/extensions/brave/locales/en-US/app.properties
+++ b/app/extensions/brave/locales/en-US/app.properties
@@ -223,3 +223,7 @@ cookies=Cookies
licenseTextOk=Ok
closeFirefoxWarningOk=Ok
importSuccessOk=Ok
+makeBraveDefault=Ready to make Brave your default Browser?
+checkDefaultOnStartup=Always check on startup
+useBrave=Use Brave
+notNow=Not Now
diff --git a/app/extensions/brave/locales/en-US/preferences.properties b/app/extensions/brave/locales/en-US/preferences.properties
index 6bca8a185a8..8ee99a137ac 100644
--- a/app/extensions/brave/locales/en-US/preferences.properties
+++ b/app/extensions/brave/locales/en-US/preferences.properties
@@ -249,3 +249,7 @@ sendUsageStatistics=Automatically send usage statistics to Brave
bookmarksBarTextOnly=Text only
bookmarksBarTextAndFavicon=Text and Favicons
bookmarksBarFaviconOnly=Favicons only
+defaultBrowser=Brave is your default browser.
+notDefaultBrowser=Brave is not your default browser.
+setAsDefault=Set as default…
+checkDefaultOnStartup=Always check on startup
diff --git a/app/index.js b/app/index.js
index e2fb842f523..f4a4386a8a4 100644
--- a/app/index.js
+++ b/app/index.js
@@ -82,6 +82,7 @@ const privacy = require('../js/state/privacy')
const basicAuth = require('./browser/basicAuth')
const async = require('async')
const tabs = require('./browser/tabs')
+const settings = require('../js/constants/settings')
// temporary fix for #4517, #4518 and #4472
app.commandLine.appendSwitch('enable-use-zoom-for-dsf', 'false')
@@ -107,6 +108,8 @@ const prefsRestartLastValue = {}
const unsafeTestMasterKey = 'c66af15fc6555ebecf7cee3a5b82c108fd3cb4b587ab0b299d28e39c79ecc708'
+const defaultProtocols = ['http', 'https']
+
const sessionStoreQueue = async.queue((task, callback) => {
task(callback)
}, 1)
@@ -441,6 +444,10 @@ app.on('ready', () => {
}
process.emit(messages.APP_INITIALIZED)
+ // Default browser checking
+ let isDefaultBrowser = defaultProtocols.every(p => app.isDefaultProtocolClient(p))
+ appActions.changeSetting(settings.IS_DEFAULT_BROWSER, isDefaultBrowser)
+
if (CmdLine.newWindowURL) {
appActions.newWindow(Immutable.fromJS({
location: CmdLine.newWindowURL
diff --git a/app/renderer/components/checkDefaultBrowserDialog.js b/app/renderer/components/checkDefaultBrowserDialog.js
new file mode 100644
index 00000000000..41f06bf4134
--- /dev/null
+++ b/app/renderer/components/checkDefaultBrowserDialog.js
@@ -0,0 +1,51 @@
+/* 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/. */
+
+const React = require('react')
+const ImmutableComponent = require('../../../js/components/immutableComponent')
+const Dialog = require('../../../js/components/dialog')
+const Button = require('../../../js/components/button')
+const SwitchControl = require('../../../js/components/switchControl')
+const appActions = require('../../../js/actions/appActions')
+const windowActions = require('../../../js/actions/windowActions')
+const settings = require('../../../js/constants/settings')
+
+class CheckDefaultBrowserDialog extends ImmutableComponent {
+ constructor () {
+ super()
+ this.onCheckDefaultOnStartup = this.onCheckDefaultOnStartup.bind(this)
+ this.onNotNow = this.onNotNow.bind(this)
+ this.onUseBrave = this.onUseBrave.bind(this)
+ }
+
+ onCheckDefaultOnStartup (e) {
+ windowActions.setModalDialogDetail('checkDefaultBrowserDialog', {checkDefaultOnStartup: e.target.value})
+ }
+ onNotNow () {
+ appActions.defaultBrowserUpdated(false)
+ appActions.defaultBrowserCheckComplete()
+ appActions.changeSetting(settings.CHECK_DEFAULT_ON_STARTUP, this.props.checkDefaultOnStartup)
+ }
+ onUseBrave () {
+ appActions.defaultBrowserUpdated(true)
+ appActions.defaultBrowserCheckComplete()
+ appActions.changeSetting(settings.CHECK_DEFAULT_ON_STARTUP, this.props.checkDefaultOnStartup)
+ }
+ render () {
+ return
+ }
+}
+
+module.exports = CheckDefaultBrowserDialog
diff --git a/app/sessionStore.js b/app/sessionStore.js
index 3309bf64b44..ea3da5b6265 100644
--- a/app/sessionStore.js
+++ b/app/sessionStore.js
@@ -234,6 +234,8 @@ module.exports.cleanAppData = (data, isShutdown) => {
data.temporarySiteSettings = {}
// Delete Flash state since this is checked on startup
delete data.flashInitialized
+ // Delete defaultBrowserCheckComplete state since this is checked on startup
+ delete data.defaultBrowserCheckComplete
// Delete Recovery status on shut down
try {
delete data.ui.about.preferences.recoverySucceeded
diff --git a/docs/appActions.md b/docs/appActions.md
index a8647eac992..2d1ceb23418 100644
--- a/docs/appActions.md
+++ b/docs/appActions.md
@@ -466,6 +466,22 @@ Dispatches a message to submit feedback
+### defaultBrowserUpdated(useBrave)
+
+Dispatch a message to set default browser
+
+**Parameters**
+
+**useBrave**: `boolean`, whether set Brave as default browser
+
+
+
+### defaultBrowserCheckComplete()
+
+Dispatch a message to indicate default browser check is complete
+
+
+
* * *
diff --git a/docs/state.md b/docs/state.md
index c8844ee51ec..3bafe3e2d30 100644
--- a/docs/state.md
+++ b/docs/state.md
@@ -150,6 +150,8 @@ AppStore
'general.downloads.default-save-path': string, // default path for saving files
'general.autohide-menu': boolean, // true if the Windows menu should be autohidden
'general.disable-title-mode': boolean, // true if title mode should always be disabled
+ 'general.check-default-on-startup': boolean, // true to check whether brave is default browser on startup
+ 'general.is-default-browser': boolean, // true if brave is default browser
'search.default-search-engine': string, // name of search engine, from js/data/searchProviders.js
'search.offer-search-suggestions': boolean, // true if suggestions should be offered from the default search engine when available.
'tabs.switch-to-new-tabs': boolean, // true if newly opened tabs should be focused immediately
@@ -205,7 +207,8 @@ AppStore
},
menu: {
template: object // used on Windows and by our tests: template object with Menubar control
- }
+ },
+ defaultBrowserCheckComplete: boolean // true to indicate default browser check is complete
}
```
@@ -527,6 +530,12 @@ WindowStore
favorites: boolean,
mergeFavorites: boolean,
cookies: boolean
+ },
+ modalDialogDetail: {
+ [className]: {
+ Object // props
+ },
+ ...
}
}
```
diff --git a/docs/windowActions.md b/docs/windowActions.md
index bd7426feca1..b7a9c27a35c 100644
--- a/docs/windowActions.md
+++ b/docs/windowActions.md
@@ -914,6 +914,18 @@ Fired when window receives or loses focus
+### setModalDialogDetail(className, props)
+
+Set Modal Dialog detail
+
+**Parameters**
+
+**className**: `string`, name of modal dialog
+
+**props**: `Object`, properties of the modal dialog
+
+
+
* * *
diff --git a/js/about/aboutActions.js b/js/about/aboutActions.js
index 0b49bdc1ae3..76fbf8159a6 100644
--- a/js/about/aboutActions.js
+++ b/js/about/aboutActions.js
@@ -328,6 +328,16 @@ const aboutActions = {
originalDetail,
destinationDetail
})
+ },
+
+ /**
+ * Dispatch a message to set default browser
+ */
+ setAsDefaultBrowser: function () {
+ aboutActions.dispatchAction({
+ actionType: appConstants.APP_DEFAULT_BROWSER_UPDATED,
+ useBrave: true
+ })
}
}
module.exports = aboutActions
diff --git a/js/about/preferences.js b/js/about/preferences.js
index daab6c2f1f9..08eba2cebe6 100644
--- a/js/about/preferences.js
+++ b/js/about/preferences.js
@@ -581,6 +581,7 @@ class GeneralTab extends ImmutableComponent {
super()
this.importBrowserDataNow = this.importBrowserDataNow.bind(this)
this.onChangeSetting = this.onChangeSetting.bind(this)
+ this.setAsDefaultBrowser = this.setAsDefaultBrowser.bind(this)
}
importBrowserDataNow () {
@@ -598,6 +599,10 @@ class GeneralTab extends ImmutableComponent {
this.props.onChangeSetting(key, value)
}
+ setAsDefaultBrowser () {
+ aboutActions.setAsDefaultBrowser()
+ }
+
enabled (keyArray) {
return keyArray.every((key) => getSetting(key, this.props.settings) === true)
}
@@ -613,6 +618,13 @@ class GeneralTab extends ImmutableComponent {
const disableShowHomeButton = !homepage || !homepage.length
const disableBookmarksBarSelect = !getSetting(settings.SHOW_BOOKMARKS_TOOLBAR, this.props.settings)
const defaultLanguage = this.props.languageCodes.find((lang) => lang.includes(navigator.language)) || 'en-US'
+ const defaultBrowser = getSetting(settings.IS_DEFAULT_BROWSER, this.props.settings)
+ ?
+ :
return
@@ -661,6 +673,11 @@ class GeneralTab extends ImmutableComponent {
onClick={this.importBrowserDataNow} />
+
+ {defaultBrowser}
+
+
}
}
diff --git a/js/actions/appActions.js b/js/actions/appActions.js
index 30c5748ca4c..c033eff76f1 100644
--- a/js/actions/appActions.js
+++ b/js/actions/appActions.js
@@ -545,6 +545,27 @@ const appActions = {
AppDispatcher.dispatch({
actionType: AppConstants.APP_SUBMIT_FEEDBACK
})
+ },
+
+ /**
+ * Dispatch a message to set default browser
+ *
+ * @param {boolean} useBrave - whether set Brave as default browser
+ */
+ defaultBrowserUpdated: function (useBrave) {
+ AppDispatcher.dispatch({
+ actionType: AppConstants.APP_DEFAULT_BROWSER_UPDATED,
+ useBrave
+ })
+ },
+
+ /**
+ * Dispatch a message to indicate default browser check is complete
+ */
+ defaultBrowserCheckComplete: function () {
+ AppDispatcher.dispatch({
+ actionType: AppConstants.APP_DEFAULT_BROWSER_CHECK_COMPLETE
+ })
}
}
diff --git a/js/actions/windowActions.js b/js/actions/windowActions.js
index 38494b70104..f2e02049991 100644
--- a/js/actions/windowActions.js
+++ b/js/actions/windowActions.js
@@ -1146,6 +1146,19 @@ const windowActions = {
actionType: WindowConstants.WINDOW_ON_FOCUS_CHANGED,
hasFocus
})
+ },
+
+ /**
+ * Set Modal Dialog detail
+ * @param {string} className - name of modal dialog
+ * @param {Object} props - properties of the modal dialog
+ */
+ setModalDialogDetail: function (className, props) {
+ dispatch({
+ actionType: WindowConstants.WINDOW_SET_MODAL_DIALOG_DETAIL,
+ className,
+ props
+ })
}
}
diff --git a/js/components/main.js b/js/components/main.js
index 3776458ebed..49386246a30 100644
--- a/js/components/main.js
+++ b/js/components/main.js
@@ -41,6 +41,7 @@ const NoScriptInfo = require('./noScriptInfo')
const LongPressButton = require('./longPressButton')
const Menubar = require('../../app/renderer/components/menubar')
const WindowCaptionButtons = require('../../app/renderer/components/windowCaptionButtons')
+const CheckDefaultBrowserDialog = require('../../app/renderer/components/checkDefaultBrowserDialog')
// Constants
const config = require('../constants/config')
@@ -59,6 +60,7 @@ const basicAuthState = require('../../app/common/state/basicAuthState')
const extensionState = require('../../app/common/state/extensionState')
const FrameStateUtil = require('../state/frameStateUtil')
const searchProviders = require('../data/searchProviders')
+const defaultBrowserState = require('../../app/common/state/defaultBrowserState')
// Util
const cx = require('../lib/classSet')
@@ -92,6 +94,7 @@ class Main extends ImmutableComponent {
this.onHideAutofillCreditCardPanel = this.onHideAutofillCreditCardPanel.bind(this)
this.onHideNoScript = this.onHideNoScript.bind(this)
this.onHideReleaseNotes = this.onHideReleaseNotes.bind(this)
+ this.onHideCheckDefaultBrowserDialog = this.onHideCheckDefaultBrowserDialog.bind(this)
this.onBraveMenu = this.onBraveMenu.bind(this)
this.onHamburgerMenu = this.onHamburgerMenu.bind(this)
this.onTabContextMenu = this.onTabContextMenu.bind(this)
@@ -623,6 +626,10 @@ class Main extends ImmutableComponent {
windowActions.setReleaseNotesVisible(false)
}
+ onHideCheckDefaultBrowserDialog () {
+ windowActions.setModalDialogDetail('checkDefaultBrowserDialog')
+ }
+
enableNoScript (settings) {
return siteSettings.activeSettings(settings, this.props.appState, appConfig).noScript === true
}
@@ -826,6 +833,8 @@ class Main extends ImmutableComponent {
const activeRequestedLocation = this.activeRequestedLocation
const noScriptIsVisible = this.props.windowState.getIn(['ui', 'noScriptInfo', 'isVisible'])
const releaseNotesIsVisible = this.props.windowState.getIn(['ui', 'releaseNotes', 'isVisible'])
+ const checkDefaultBrowserDialogIsVisible =
+ currentWindow.isFocused() && defaultBrowserState.shouldDisplayDialog(this.props.appState)
const braverySettings = siteSettings.activeSettings(activeSiteSettings, this.props.appState, appConfig)
const loginRequiredDetail = activeFrame ? basicAuthState.getLoginRequiredDetail(this.props.appState, activeFrame.get('tabId')) : null
const customTitlebar = this.customTitlebar
@@ -838,6 +847,7 @@ class Main extends ImmutableComponent {
!autofillAddressPanelIsVisible &&
!autofillCreditCardPanelIsVisible &&
!releaseNotesIsVisible &&
+ !checkDefaultBrowserDialogIsVisible &&
!noScriptIsVisible &&
activeFrame && !activeFrame.getIn(['security', 'loginRequiredDetail']) &&
!customTitlebar.menubarSelectedIndex
@@ -1018,6 +1028,17 @@ class Main extends ImmutableComponent {
onHide={this.onHideReleaseNotes} />
: null
}
+ {
+ checkDefaultBrowserDialogIsVisible
+ ?
+ : null
+ }
{
diff --git a/js/constants/appConfig.js b/js/constants/appConfig.js
index b4f1ffa04f7..caeaa800ee3 100644
--- a/js/constants/appConfig.js
+++ b/js/constants/appConfig.js
@@ -96,6 +96,7 @@ module.exports = {
'general.show-home-button': false,
'general.useragent.value': null, // Set at runtime
'general.autohide-menu': true,
+ 'general.check-default-on-startup': true,
'search.default-search-engine': 'Google',
'search.offer-search-suggestions': false, // false by default for privacy reasons
'tabs.switch-to-new-tabs': false,
diff --git a/js/constants/appConstants.js b/js/constants/appConstants.js
index 041e9f9c2a5..498a1a93508 100644
--- a/js/constants/appConstants.js
+++ b/js/constants/appConstants.js
@@ -60,7 +60,9 @@ const AppConstants = {
APP_SET_MENUBAR_TEMPLATE: _,
APP_UPDATE_ADBLOCK_DATAFILES: _,
APP_UPDATE_ADBLOCK_CUSTOM_RULES: _,
- APP_SUBMIT_FEEDBACK: _
+ APP_SUBMIT_FEEDBACK: _,
+ APP_DEFAULT_BROWSER_UPDATED: _,
+ APP_DEFAULT_BROWSER_CHECK_COMPLETE: _
}
module.exports = mapValuesByKeys(AppConstants)
diff --git a/js/constants/settings.js b/js/constants/settings.js
index f754f54f713..2e494e19444 100644
--- a/js/constants/settings.js
+++ b/js/constants/settings.js
@@ -14,6 +14,8 @@ const settings = {
BOOKMARKS_TOOLBAR_MODE: 'general.bookmarks-toolbar-mode',
SHOW_BOOKMARKS_TOOLBAR: 'bookmarks.toolbar.show',
LANGUAGE: 'general.language',
+ CHECK_DEFAULT_ON_STARTUP: 'general.check-default-on-startup',
+ IS_DEFAULT_BROWSER: 'general.is-default-browser',
// Search tab
DEFAULT_SEARCH_ENGINE: 'search.default-search-engine',
OFFER_SEARCH_SUGGESTIONS: 'search.offer-search-suggestions',
diff --git a/js/constants/windowConstants.js b/js/constants/windowConstants.js
index 2da41ade7d5..f2af206e62f 100644
--- a/js/constants/windowConstants.js
+++ b/js/constants/windowConstants.js
@@ -81,7 +81,8 @@ const windowConstants = {
WINDOW_SET_LAST_FOCUSED_SELECTOR: _,
WINDOW_GOT_RESPONSE_DETAILS: _,
WINDOW_SET_BOOKMARKS_TOOLBAR_SELECTED_FOLDER_ID: _,
- WINDOW_ON_FOCUS_CHANGED: _
+ WINDOW_ON_FOCUS_CHANGED: _,
+ WINDOW_SET_MODAL_DIALOG_DETAIL: _
}
module.exports = mapValuesByKeys(windowConstants)
diff --git a/js/stores/appStore.js b/js/stores/appStore.js
index 86253317be3..3252e4241d3 100644
--- a/js/stores/appStore.js
+++ b/js/stores/appStore.js
@@ -44,6 +44,8 @@ const isWindows = process.platform === 'win32'
// Only used internally
const CHANGE_EVENT = 'app-state-change'
+const defaultProtocols = ['https', 'http']
+
let appState
let lastEmittedState
@@ -723,6 +725,18 @@ const handleAppAction = (action) => {
const subject = encodeURIComponent(`Brave ${platform} ${os.arch()} ${app.getVersion()}${channel()} feedback`)
electron.shell.openExternal(`${appConfig.contactUrl}?subject=${subject}`)
break
+ case AppConstants.APP_DEFAULT_BROWSER_UPDATED:
+ if (action.useBrave) {
+ for (const p of defaultProtocols) {
+ app.setAsDefaultProtocolClient(p)
+ }
+ }
+ let isDefaultBrowser = defaultProtocols.every(p => app.isDefaultProtocolClient(p))
+ appState = appState.setIn(['settings', settings.IS_DEFAULT_BROWSER], isDefaultBrowser)
+ break
+ case AppConstants.APP_DEFAULT_BROWSER_CHECK_COMPLETE:
+ appState = appState.set('defaultBrowserCheckComplete', {})
+ break
default:
}
diff --git a/js/stores/windowStore.js b/js/stores/windowStore.js
index 9fe070bba8a..891be3d88c7 100644
--- a/js/stores/windowStore.js
+++ b/js/stores/windowStore.js
@@ -842,6 +842,15 @@ const doAction = (action) => {
case WindowConstants.WINDOW_ON_FOCUS_CHANGED:
windowState = windowState.setIn(['ui', 'hasFocus'], action.hasFocus)
break
+ case WindowConstants.WINDOW_SET_MODAL_DIALOG_DETAIL:
+ if (action.className && action.props === undefined) {
+ windowState = windowState.deleteIn(['modalDialogDetail', action.className])
+ } else if (action.className) {
+ windowState = windowState.setIn(['modalDialogDetail', action.className], Immutable.fromJS(action.props))
+ }
+ // Since the input values of address are bound, we need to notify the controls sync.
+ windowStore.emitChanges()
+ break
default:
}
diff --git a/less/forms.less b/less/forms.less
index 04b66e9d72b..0f2783154cb 100644
--- a/less/forms.less
+++ b/less/forms.less
@@ -196,6 +196,63 @@
}
}
+.checkDefaultBrowserDialog {
+ .checkDefaultBrowser {
+ .flyoutDialog;
+ background-color: #f7f7f7;
+ border-radius: @borderRadius;
+ max-width: 422px;
+ padding: 1;
+ text-align: left;
+ width: 614px;
+ height: 120px;
+ -webkit-user-select: none;
+ cursor: default;
+ color: #3B3B3B;
+ overflow-x: hidden;
+ overflow-y: hidden;
+ max-height: 100%;
+
+ .clickable {
+ color: #5B5B5B;
+ &:hover {
+ color: #000;
+ }
+ }
+
+ .checkDefaultBrowserButtons {
+ text-align: right;
+ padding: 16px 10px;
+ position: relative;
+ top: -30px;
+ }
+
+ .makeBraveDefault {
+ font-weight: bold;
+ display: inline-block;
+ position: relative;
+ top: -35px;
+ margin-left: 15px;
+ }
+
+ .braveIcon {
+ background-image: -webkit-image-set(url(../app/extensions/brave/img/braveAbout.png) 2x);
+ background-repeat: no-repeat;
+ height: 64px;
+ width: 64px;
+ display: inline-block;
+ position: relative;
+ top: 10px;
+ }
+
+ .checkDefaultOnStartup {
+ left: 75px;
+ position: relative;
+ top: -25px;
+ }
+ }
+}
+
.braveryPanelContainer {
.braveryPanel {