diff --git a/app/browser/reducers/urlBarSuggestionsReducer.js b/app/browser/reducers/urlBarSuggestionsReducer.js
new file mode 100644
index 00000000000..9c9403b72ce
--- /dev/null
+++ b/app/browser/reducers/urlBarSuggestionsReducer.js
@@ -0,0 +1,43 @@
+/* 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 appConstants = require('../../../js/constants/appConstants')
+const {generateNewSuggestionsList, generateNewSearchXHRResults} = require('../../common/lib/suggestion')
+const {init, add} = require('../../common/lib/siteSuggestions')
+const Immutable = require('immutable')
+const {makeImmutable} = require('../../common/state/immutableUtil')
+const tabState = require('../../common/state/tabState')
+
+const urlBarSuggestionsReducer = (state, action) => {
+ switch (action.actionType) {
+ case appConstants.APP_ADD_SITE:
+ if (Immutable.List.isList(action.siteDetail)) {
+ action.siteDetail.forEach((s) => {
+ add(s)
+ })
+ } else {
+ add(action.siteDetail)
+ }
+ break
+ case appConstants.APP_SET_STATE:
+ init(Object.values(action.appState.get('sites').toJS()))
+ break
+ case appConstants.APP_URL_BAR_TEXT_CHANGED:
+ generateNewSuggestionsList(state, action.windowId, action.tabId, action.input)
+ generateNewSearchXHRResults(state, action.windowId, action.tabId, action.input)
+ break
+ case appConstants.APP_SEARCH_SUGGESTION_RESULTS_AVAILABLE:
+ state = state.set('searchResults', makeImmutable(action.searchResults))
+ if (action.query) {
+ const windowId = tabState.windowId(state, action.tabId)
+ generateNewSuggestionsList(state, windowId, action.tabId, action.query)
+ }
+ break
+ }
+ return state
+}
+
+module.exports = urlBarSuggestionsReducer
diff --git a/app/common/lib/fetchSearchSuggestions.js b/app/common/lib/fetchSearchSuggestions.js
new file mode 100644
index 00000000000..f1b4fc9477f
--- /dev/null
+++ b/app/common/lib/fetchSearchSuggestions.js
@@ -0,0 +1,32 @@
+/* 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 appActions = require('../../../js/actions/appActions')
+const {request} = require('../../../js/lib/request')
+const debounce = require('../../../js/lib/debounce')
+
+const fetchSearchSuggestions = debounce((windowId, tabId, autocompleteURL, searchTerms) => {
+ autocompleteURL.replace('{searchTerms}', encodeURIComponent(searchTerms))
+ request(autocompleteURL.replace('{searchTerms}', encodeURIComponent(searchTerms)), (err, response, body) => {
+ if (err) {
+ return
+ }
+
+ let searchResults
+ let query
+ try {
+ const parsed = JSON.parse(body)
+ query = parsed[0]
+ searchResults = parsed[1]
+ } catch (e) {
+ console.warn(e)
+ return
+ }
+
+ // Once we have the online suggestions, append them to the others
+ appActions.searchSuggestionResultsAvailable(tabId, query, searchResults)
+ })
+}, 10)
+
+module.exports = fetchSearchSuggestions
diff --git a/app/common/lib/siteSuggestions.js b/app/common/lib/siteSuggestions.js
new file mode 100644
index 00000000000..3bbdbb44f23
--- /dev/null
+++ b/app/common/lib/siteSuggestions.js
@@ -0,0 +1,138 @@
+/* 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 Bloodhound = require('bloodhound-js')
+const {isUrl} = require('../../../js/lib/appUrlUtil')
+const siteTags = require('../../../js/constants/siteTags')
+const urlParse = require('../urlParse')
+
+let initialized = false
+let engine
+let lastQueryOptions
+
+// Same as sortByAccessCountWithAgeDecay but if one is a prefix of the
+// other then it is considered always sorted first.
+const sortForSuggestions = (s1, s2) => {
+ return lastQueryOptions.internalSort(s1, s2)
+}
+
+const getSiteIdentity = (data) => {
+ if (typeof data === 'string') {
+ return data
+ }
+ return (data.location || '') + (data.partitionNumber ? '|' + data.partitionNumber : '')
+}
+
+const init = (sites) => {
+ engine = new Bloodhound({
+ local: sites.toJS ? sites.toJS() : sites,
+ sorter: sortForSuggestions,
+ queryTokenizer: tokenizeInput,
+ datumTokenizer: tokenizeInput,
+ identify: getSiteIdentity
+ })
+ const promise = engine.initialize()
+ promise.then(() => {
+ initialized = true
+ })
+ return promise
+}
+
+const getPartsFromNonUrlInput = (input) =>
+ input.toLowerCase().split(/[,-.\s\\/?&]/)
+
+const getTagToken = (tag) => '|' + tag + '|'
+
+const tokenizeInput = (data) => {
+ let url = data || ''
+ let parts = []
+
+ const isSiteObject = typeof data === 'object' && data !== null
+ if (isSiteObject) {
+ // When lastAccessTime is 1 it is a default built-in entry which we don't want
+ // to appear in suggestions.
+ if (data.lastAccessedTime === 1) {
+ return []
+ }
+ url = data.location
+ if (data.title) {
+ parts = getPartsFromNonUrlInput(data.title)
+ }
+ if (data.tags) {
+ parts = parts.concat(data.tags.map(getTagToken))
+ }
+ } else {
+ if (lastQueryOptions && !lastQueryOptions.historySuggestionsOn && lastQueryOptions.bookmarkSuggestionsOn) {
+ parts.push(getTagToken(siteTags.BOOKMARK))
+ }
+ }
+
+ if (url && isUrl(url)) {
+ const parsedUrl = urlParse(url.toLowerCase())
+ // Cache parsed value for latter use when sorting
+ if (isSiteObject) {
+ data.parsedUrl = parsedUrl
+ }
+ if (parsedUrl.hash) {
+ parts.push(parsedUrl.hash.slice(1))
+ }
+ if (parsedUrl.host) {
+ parts = parts.concat(parsedUrl.host.split('.'))
+ }
+ if (parsedUrl.pathname) {
+ parts = parts.concat(parsedUrl.pathname.split(/[.\s\\/]/))
+ }
+ if (parsedUrl.query) {
+ parts = parts.concat(parsedUrl.query.split(/[&=]/))
+ }
+ if (parsedUrl.protocol) {
+ parts = parts.concat(parsedUrl.protocol)
+ }
+ } else if (url) {
+ parts = parts.concat(getPartsFromNonUrlInput(url))
+ }
+ return parts.filter(x => !!x)
+}
+
+const add = (data) => {
+ if (!initialized) {
+ return
+ }
+ if (typeof data === 'string') {
+ engine.add(data)
+ } else {
+ engine.add(data.toJS ? data.toJS() : data)
+ }
+}
+
+const query = (input, options = {}) => {
+ if (!initialized) {
+ return Promise.resolve([])
+ }
+
+ return new Promise((resolve, reject) => {
+ const {getSortForSuggestions} = require('./suggestion')
+ input = (input || '').toLowerCase()
+ lastQueryOptions = Object.assign({}, options, {
+ input,
+ internalSort: getSortForSuggestions(input)
+ })
+ if (lastQueryOptions.historySuggestionsOn !== false || lastQueryOptions.bookmarkSuggestionsOn !== false) {
+ engine.search(input, function (results) {
+ resolve(results)
+ }, function (err) {
+ reject(err)
+ })
+ } else {
+ resolve([])
+ }
+ })
+}
+
+module.exports = {
+ init,
+ add,
+ tokenizeInput,
+ query
+}
diff --git a/app/common/lib/suggestion.js b/app/common/lib/suggestion.js
new file mode 100644
index 00000000000..093273750a8
--- /dev/null
+++ b/app/common/lib/suggestion.js
@@ -0,0 +1,566 @@
+/* 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 urlParse = require('../../common/urlParse')
+const appConfig = require('../../../js/constants/appConfig')
+const _ = require('underscore')
+const Immutable = require('immutable')
+const {makeImmutable} = require('../../common/state/immutableUtil')
+const {isUrl, aboutUrls, isNavigatableAboutPage, isSourceAboutUrl} = require('../../../js/lib/appUrlUtil')
+const suggestionTypes = require('../../../js/constants/suggestionTypes')
+const getSetting = require('../../../js/settings').getSetting
+const settings = require('../../../js/constants/settings')
+const config = require('../../../js/constants/config')
+const top500 = require('../../../js/data/top500')
+const fetchSearchSuggestions = require('./fetchSearchSuggestions')
+const {getFrameByTabId, getTabsByWindowId} = require('../../common/state/tabState')
+const {query} = require('./siteSuggestions')
+const debounce = require('../../../js/lib/debounce')
+
+const sigmoid = (t) => {
+ return 1 / (1 + Math.pow(Math.E, -t))
+}
+
+const ONE_DAY = 1000 * 60 * 60 * 24
+
+/*
+ * Calculate the sorting priority for a history item based on number of
+ * accesses and time since last access
+ *
+ * @param {number} count - The number of times this site has been accessed
+ * @param {number} currentTime - Current epoch millisecnds
+ * @param {boolean} lastAccessedTime - Epoch milliseconds of last access
+ *
+ */
+const sortingPriority = (count, currentTime, lastAccessedTime, ageDecayConstant) => {
+ // number of days since last access (with fractional component)
+ const ageInDays = (currentTime - (lastAccessedTime || currentTime)) / ONE_DAY
+ // decay factor based on age
+ const ageFactor = 1 - ((sigmoid(ageInDays / ageDecayConstant) - 0.5) * 2)
+ // sorting priority
+ // console.log(count, ageInDays, ageFactor, count * ageFactor)
+ return count * ageFactor
+}
+
+/*
+ * Sort two history items by priority
+ *
+ * @param {ImmutableObject} s1 - first history item
+ * @param {ImmutableObject} s2 - second history item
+ *
+ * Return the relative order of two site entries taking into consideration
+ * the number of times the site has been accessed and the length of time
+ * since the last access.
+ *
+ * The base sort order is determined by the count attribute of the site
+ * entry. A modifier is then computed based on the length of time since
+ * the last access. A sigmoid function is used to weight more recent
+ * entries higher than entries in the past. This is not a linear function,
+ * entries in the far past with many counts will still be discounted
+ * heavily as the sigmoid modifier will cancel most of the count
+ * base parameter.
+ *
+ * Below is a sample comparison of two sites that have been accessed
+ * recently (but not at the identical time). Each site is accessed
+ * 9 times. The count is discounted by an aging factor calculated
+ * using the sigmoid decay function.
+ *
+ * http://www.gm.ca/gm/
+ *
+ * ageInDays 0.17171469907407408
+ * ageFactor 0.9982828546969802
+ * count 9
+ * priority 0.9982828546969802
+ *
+ * http://www.gm.com/index.html
+ *
+ * ageInDays 0.17148791666666666
+ * ageFactor 0.9982851225143763
+ * count 9
+ * priority 0.9982851225143763
+ *
+ */
+const sortByAccessCountWithAgeDecay = (s1, s2) => {
+ const now = new Date()
+ const s1Priority = sortingPriority(
+ s1.count || 0,
+ now.getTime(),
+ s1.lastAccessedTime || now.getTime(),
+ appConfig.urlSuggestions.ageDecayConstant
+ )
+ const s2Priority = sortingPriority(
+ s2.count || 0,
+ now.getTime(),
+ s2.lastAccessedTime || now.getTime(),
+ appConfig.urlSuggestions.ageDecayConstant
+ )
+ return s2Priority - s1Priority
+}
+
+/*
+ * Return true1 the url is 'simple' as in without query, search or
+ * hash components. Return false otherwise.
+ *
+ * @param {object} An already normalized simple URL
+ *
+ */
+const isSimpleDomainNameValue = (site) => isParsedUrlSimpleDomainNameValue(urlParse(getURL(site)))
+const isParsedUrlSimpleDomainNameValue = (parsed) => {
+ if ((parsed.hash === null || parsed.hash === '#') &&
+ parsed.search === null && parsed.query === null && parsed.pathname === '/') {
+ return true
+ } else {
+ return false
+ }
+}
+
+/*
+ * Normalize a location for url suggestion sorting
+ *
+ * @param {string} location - history item location
+ *
+ */
+const normalizeLocation = (location) => {
+ if (typeof location === 'string') {
+ location = location.replace(/www\./, '')
+ location = location.replace(/^http:\/\//, '')
+ location = location.replace(/^https:\/\//, '')
+ }
+ return location
+}
+
+/*
+ * Determines based on user input if the location should
+ * be normalized. If the user is typing http prefix then
+ * they are specifying something explicitly.
+ *
+ * @return true if urls being compared should be normalized
+ */
+const shouldNormalizeLocation = (input) => {
+ const prefixes = ['http://', 'https://', 'www.']
+ return prefixes.every((prefix) => {
+ if (input.length > prefix.length) {
+ return true
+ }
+ for (let i = 0; i < Math.min(prefix.length, input.length); i++) {
+ if (input[i] !== prefix[i]) {
+ return true
+ }
+ }
+ return false
+ })
+}
+
+/*
+ * return a site representing the simple location for a
+ * set of related sites without a history item for the
+ * simple location.
+ *
+ * This is used to show a history suggestion for something
+ * like www.google.com if it has not been visited but
+ * there are two or more locations with that prefix containing
+ * path info or parameters
+ *
+ * @param {Array[Object]} sites - array of similar sites
+ */
+var virtualSite = (sites) => {
+ // array of sites without paths or query params
+ var simple = sites.filter((parsed) => {
+ return (parsed.hash === null && parsed.search === null && parsed.query === null && parsed.pathname === '/')
+ })
+ // if there are no simple locations then we will build and return one
+ if (simple.length === 0) {
+ // we need to create a virtual history item
+ return Immutable.Map({
+ location: sites[0].protocol + '//' + sites[0].host,
+ count: 0,
+ title: sites[0].host,
+ lastAccessedTime: (new Date()).getTime()
+ })
+ }
+}
+
+/*
+ * Create an array of simple locations from history
+ * The simple locations will be the root domain for a location
+ * without parameters or path
+ *
+ * @param {ImmutableList[ImmutableMap]} - history
+ */
+const createVirtualHistoryItems = (historySites) => {
+ historySites = makeImmutable(historySites || {})
+
+ // parse each history item
+ const parsedHistorySites = []
+ historySites.forEach((site) => {
+ if (site && site.get('location')) {
+ parsedHistorySites.push(
+ urlParse(site.get('location'))
+ )
+ }
+ })
+ // group them by host
+ var grouped = _.groupBy(parsedHistorySites, (parsedSite) => {
+ return parsedSite.host || 'unknown'
+ })
+ // find groups with more than 2 of the same host
+ var multiGroupKeys = _.filter(_.keys(grouped), (k) => {
+ return grouped[k].length > 0
+ })
+ // potentially create virtual history items
+ var virtualHistorySites = _.map(multiGroupKeys, (location) => {
+ return virtualSite(grouped[location])
+ })
+ virtualHistorySites = _.filter(virtualHistorySites, (site) => {
+ return !!site
+ })
+ return Immutable.fromJS(_.object(virtualHistorySites.map((site) => {
+ return [site.get('location'), site]
+ })))
+}
+
+/**
+ * Returns a function that sorts 2 sites by their host.
+ * The result of that function is a postive, negative, or 0 result.
+ * 3 or -3 for a strong indicator of a superior result.
+ * 2 or -2 for a good indicator of a superior result.
+ * 1 or -1 for a weak indicator of a superior result.
+ * 0 if no determination can be made.
+ */
+const getSortByDomain = (userInputLower, userInputHost) => {
+ return (s1, s2) => {
+ // Check for matches on hostname which if found overrides
+ // any count or frequency calculation.
+ // Note that for parsed URLs that are not complete, the pathname contains
+ // what the user is entering as the host and the host is null.
+ const host1 = s1.parsedUrl.host || s1.parsedUrl.pathname
+ const host2 = s2.parsedUrl.host || s2.parsedUrl.pathname
+
+ let pos1 = host1.indexOf(userInputHost)
+ let pos2 = host2.indexOf(userInputHost)
+ if (pos1 !== -1 && pos2 === -1) {
+ return -3
+ }
+ if (pos1 === -1 && pos2 !== -1) {
+ return 3
+ }
+ if (pos1 !== -1 && pos2 !== -1) {
+ // Try to match on the first position without taking into account decay sort.
+ // This is because autocomplete is based on matching prefixes.
+ if (pos1 === 0 && pos2 !== 0) {
+ return -2
+ }
+ if (pos1 !== 0 && pos2 === 0) {
+ return 2
+ }
+
+ // Try the same to see if taking off www. helps.
+ if (!userInputLower.startsWith('www.')) {
+ pos1 = host1.indexOf('www.' + userInputLower)
+ pos2 = host2.indexOf('www.' + userInputLower)
+ if (pos1 === 0 && pos2 !== 0) {
+ return -1
+ }
+ if (pos1 !== 0 && pos2 === 0) {
+ return 1
+ }
+ }
+
+ const sortBySimpleURLResult = sortBySimpleURL(s1, s2)
+ if (sortBySimpleURLResult !== 0) {
+ return sortBySimpleURLResult
+ }
+ }
+ // Can't determine what is the best match
+ return 0
+ }
+}
+
+/**
+ * Sorts 2 URLS by if they are a simple URL or not.
+ * Returns the normal -1, 1, or 0 result for sort functions.
+ */
+const sortBySimpleURL = (s1, s2) => {
+ // If one of the URLs is a simpleURL and the other isn't then sort the simple one first
+ const url1IsSimple = isParsedUrlSimpleDomainNameValue(s1.parsedUrl)
+ const url2IsSimple = isParsedUrlSimpleDomainNameValue(s2.parsedUrl)
+ if (url1IsSimple && !url2IsSimple) {
+ return -1
+ }
+ if (!url1IsSimple && url2IsSimple) {
+ return 1
+ }
+ const url1IsSecure = s1.parsedUrl.protocol === 'https:'
+ const url2IsSecure = s2.parsedUrl.protocol === 'https:'
+ if (url1IsSimple && url2IsSimple) {
+ if (url1IsSecure && !url2IsSecure) {
+ return -1
+ }
+ if (!url1IsSecure && url2IsSecure) {
+ return 1
+ }
+ }
+ return 0
+}
+
+/**
+ * Returns a function that sorts 2 sites by their host.
+ * The result of that function is a postive, negative, or 0 result.
+ */
+const getSortByPath = (userInputLower) => {
+ return (path1, path2) => {
+ const pos1 = path1.indexOf(userInputLower)
+ const pos2 = path2.indexOf(userInputLower)
+ if (pos1 !== -1 && pos2 === -1) {
+ return -1
+ }
+ if (pos1 === -1 && pos2 !== -1) {
+ return 1
+ }
+ // Can't determine what is the best match
+ return 0
+ }
+}
+
+// Same as sortByAccessCountWithAgeDecay but if one is a prefix of the
+// other then it is considered always sorted first.
+const getSortForSuggestions = (userInputLower) => {
+ userInputLower = userInputLower.replace(/^http:\/\//, '')
+ userInputLower = userInputLower.replace(/^https:\/\//, '')
+ const userInputParts = userInputLower.split('/')
+ const userInputHost = userInputParts[0]
+ const userInputValue = userInputParts[1] || ''
+ const sortByDomain = getSortByDomain(userInputLower, userInputHost)
+ const sortByPath = getSortByPath(userInputLower)
+ const {sortByAccessCountWithAgeDecay} = require('./suggestion')
+
+ return (s1, s2) => {
+ s1.parsedUrl = s1.parsedUrl || urlParse(getURL(s1) || '')
+ s2.parsedUrl = s2.parsedUrl || urlParse(getURL(s2) || '')
+
+ if (!userInputValue) {
+ const sortByDomainResult = sortByDomain(s1, s2)
+ if (sortByDomainResult !== 0) {
+ return sortByDomainResult
+ }
+ }
+
+ const path1 = s1.parsedUrl.host + s1.parsedUrl.path + (s1.parsedUrl.hash || '')
+ const path2 = s2.parsedUrl.host + s2.parsedUrl.path + (s2.parsedUrl.hash || '')
+ const sortByPathResult = sortByPath(path1, path2)
+ if (sortByPathResult !== 0) {
+ return sortByPathResult
+ }
+
+ return sortByAccessCountWithAgeDecay(s1, s2)
+ }
+}
+
+// Currently we sort only sites that are not immutableJS and
+const getURL = (x) => {
+ if (typeof x === 'string') {
+ return x
+ }
+
+ if (x.get) {
+ return x.get('location') || x.get('url')
+ }
+
+ return x.location || x.url
+}
+
+const getMapListToElements = (urlLocationLower) => ({data, maxResults, type,
+ sortHandler = (x) => x, filterValue = (site) => {
+ return site.toLowerCase().indexOf(urlLocationLower) !== -1
+ }
+}) => {
+ const suggestionsList = Immutable.List()
+ const formatTitle = (x) => typeof x === 'object' && x !== null ? x.get('title') : x
+ const formatTabId = (x) => typeof x === 'object' && x !== null ? x.get('tabId') : x
+ // Filter out things which are already in our own list at a smaller index
+ // Filter out things which are already in the suggestions list
+ let filteredData = data.filter((site) =>
+ suggestionsList.findIndex((x) => (x.location || '').toLowerCase() === (getURL(site) || '').toLowerCase()) === -1 ||
+ // Tab autosuggestions should always be included since they will almost always be in history
+ type === suggestionTypes.TAB)
+ // Per suggestion provider filter
+ if (filterValue) {
+ filteredData = filteredData.filter(filterValue)
+ }
+
+ return makeImmutable(filteredData
+ .sort(sortHandler)
+ .take(maxResults)
+ .map((site) => {
+ return Immutable.fromJS({
+ title: formatTitle(site),
+ location: getURL(site),
+ tabId: formatTabId(site),
+ type
+ })
+ }))
+}
+
+const getHistorySuggestions = (state, urlLocationLower) => {
+ return new Promise((resolve, reject) => {
+ const sortHandler = getSortForSuggestions(urlLocationLower)
+ const mapListToElements = getMapListToElements(urlLocationLower)
+ const options = {
+ historySuggestionsOn: getSetting(settings.HISTORY_SUGGESTIONS),
+ bookmarkSuggestionsOn: getSetting(settings.BOOKMARK_SUGGESTIONS)
+ }
+
+ query(urlLocationLower, options).then((results) => {
+ results = makeImmutable(results)
+ results = results.take(config.urlBarSuggestions.maxHistorySites)
+ results = results.concat(createVirtualHistoryItems(results))
+
+ const suggestionsList = mapListToElements({
+ data: results,
+ maxResults: config.urlBarSuggestions.maxHistorySites,
+ type: options.historySuggestionsOn ? suggestionTypes.HISTORY : suggestionTypes.BOOKMARK,
+ sortHandler,
+ filterValue: null
+ })
+ resolve(suggestionsList)
+ })
+ })
+}
+
+const getAboutSuggestions = (state, urlLocationLower) => {
+ return new Promise((resolve, reject) => {
+ const mapListToElements = getMapListToElements(urlLocationLower)
+ const suggestionsList = mapListToElements({
+ data: aboutUrls.keySeq().filter((x) => isNavigatableAboutPage(x)),
+ maxResults: config.urlBarSuggestions.maxAboutPages,
+ type: suggestionTypes.ABOUT_PAGES
+ })
+ resolve(suggestionsList)
+ })
+}
+
+const getOpenedTabSuggestions = (state, windowId, urlLocationLower) => {
+ return new Promise((resolve, reject) => {
+ const sortHandler = getSortForSuggestions(urlLocationLower)
+ const mapListToElements = getMapListToElements(urlLocationLower)
+ const tabs = getTabsByWindowId(state, windowId)
+ let suggestionsList = Immutable.List()
+ if (getSetting(settings.OPENED_TAB_SUGGESTIONS)) {
+ suggestionsList = mapListToElements({
+ data: tabs,
+ maxResults: config.urlBarSuggestions.maxOpenedFrames,
+ type: suggestionTypes.TAB,
+ sortHandler,
+ filterValue: (tab) => !isSourceAboutUrl(tab.get('url')) &&
+ !tab.get('active') &&
+ (
+ (tab.get('title') && tab.get('title').toLowerCase().indexOf(urlLocationLower) !== -1) ||
+ (tab.get('url') && tab.get('url').toLowerCase().indexOf(urlLocationLower) !== -1)
+ )
+ })
+ }
+ resolve(suggestionsList)
+ })
+}
+
+const getSearchSuggestions = (state, tabId, urlLocationLower) => {
+ return new Promise((resolve, reject) => {
+ const mapListToElements = getMapListToElements(urlLocationLower)
+ let suggestionsList = Immutable.List()
+ if (getSetting(settings.OFFER_SEARCH_SUGGESTIONS)) {
+ const searchResults = state.get('searchResults')
+ if (searchResults) {
+ suggestionsList = mapListToElements({
+ data: searchResults,
+ maxResults: config.urlBarSuggestions.maxSearch,
+ type: suggestionTypes.SEARCH
+ })
+ }
+ }
+ resolve(suggestionsList)
+ })
+}
+
+const getAlexaSuggestions = (state, urlLocationLower) => {
+ return new Promise((resolve, reject) => {
+ const mapListToElements = getMapListToElements(urlLocationLower)
+ const suggestionsList = mapListToElements({
+ data: top500,
+ maxResults: config.urlBarSuggestions.maxTopSites,
+ type: suggestionTypes.TOP_SITE
+ })
+ resolve(suggestionsList)
+ })
+}
+
+const generateNewSuggestionsList = debounce((state, windowId, tabId, urlLocation) => {
+ if (!urlLocation) {
+ return
+ }
+ const urlLocationLower = urlLocation.toLowerCase()
+ Promise.all([
+ getHistorySuggestions(state, urlLocationLower),
+ getAboutSuggestions(state, urlLocationLower),
+ getOpenedTabSuggestions(state, windowId, urlLocationLower),
+ getSearchSuggestions(state, tabId, urlLocationLower),
+ getAlexaSuggestions(state, urlLocationLower)
+ ]).then(([...suggestionsLists]) => {
+ const appActions = require('../../../js/actions/appActions')
+ // Flatten only 1 level deep for perf only, nested will be objects within arrrays
+ appActions.urlBarSuggestionsChanged(windowId, makeImmutable(suggestionsLists).flatten(1))
+ })
+}, 5)
+
+const generateNewSearchXHRResults = debounce((state, windowId, tabId, input) => {
+ const frame = getFrameByTabId(state, tabId)
+ if (!frame) {
+ // Frame info may not be available yet in app store
+ return
+ }
+ const frameSearchDetail = frame.getIn(['navbar', 'urlbar', 'searchDetail'])
+ const searchDetail = state.get('searchDetail')
+ if (!searchDetail && !frameSearchDetail) {
+ return
+ }
+ const autocompleteURL = frameSearchDetail
+ ? frameSearchDetail.get('autocomplete')
+ : searchDetail.get('autocompleteURL')
+
+ const shouldDoSearchSuggestions = getSetting(settings.OFFER_SEARCH_SUGGESTIONS) &&
+ autocompleteURL &&
+ !isUrl(input) &&
+ input.length !== 0
+
+ if (shouldDoSearchSuggestions) {
+ if (searchDetail) {
+ const replaceRE = new RegExp('^' + searchDetail.get('shortcut') + ' ', 'g')
+ input = input.replace(replaceRE, '')
+ }
+ fetchSearchSuggestions(windowId, tabId, autocompleteURL, input)
+ } else {
+ const appActions = require('../../../js/actions/appActions')
+ appActions.searchSuggestionResultsAvailable(tabId, undefined, Immutable.List())
+ }
+}, 10)
+
+module.exports = {
+ sortingPriority,
+ sortByAccessCountWithAgeDecay,
+ getSortForSuggestions,
+ getSortByPath,
+ sortBySimpleURL,
+ getSortByDomain,
+ isSimpleDomainNameValue,
+ normalizeLocation,
+ shouldNormalizeLocation,
+ createVirtualHistoryItems,
+ getMapListToElements,
+ getHistorySuggestions,
+ getAboutSuggestions,
+ getOpenedTabSuggestions,
+ getSearchSuggestions,
+ getAlexaSuggestions,
+ generateNewSuggestionsList,
+ generateNewSearchXHRResults
+}
diff --git a/app/common/state/navigationBarState.js b/app/common/state/navigationBarState.js
index 248500c7a7a..bfe5c63d1e4 100644
--- a/app/common/state/navigationBarState.js
+++ b/app/common/state/navigationBarState.js
@@ -81,6 +81,11 @@ const api = {
return api.getUrlBar(state, tabId).getIn(['suggestions', 'urlSuffix']) || ''
},
+ hasSuggestionMatch: (state, tabId) => {
+ state = validateState(state)
+ return api.getUrlBar(state, tabId).getIn(['suggestions', 'hasSuggestionMatch']) || false
+ },
+
hasLocationValueSuffix: (state, tabId) => {
state = validateState(state)
return api.locationValueSuffix(state, tabId).length > 0
diff --git a/app/renderer/components/navigation/urlBar.js b/app/renderer/components/navigation/urlBar.js
index 3b113af4c56..d972d8d753d 100644
--- a/app/renderer/components/navigation/urlBar.js
+++ b/app/renderer/components/navigation/urlBar.js
@@ -37,10 +37,14 @@ const UrlUtil = require('../../../../js/lib/urlutil')
const {eventElHasAncestorWithClasses, isForSecondaryAction} = require('../../../../js/lib/eventUtil')
const {getBaseUrl, isUrl, isIntermediateAboutPage} = require('../../../../js/lib/appUrlUtil')
const {getCurrentWindowId} = require('../../currentWindow')
+const {normalizeLocation} = require('../../../common/lib/suggestion')
// Icons
const iconNoScript = require('../../../../img/url-bar-no-script.svg')
+// Stores
+const appStoreRenderer = require('../../../../js/stores/appStoreRenderer')
+
class UrlBar extends React.Component {
constructor (props) {
super(props)
@@ -56,20 +60,26 @@ class UrlBar extends React.Component {
this.onContextMenu = this.onContextMenu.bind(this)
this.keyPressed = false
this.showAutocompleteResult = debounce(() => {
- if (this.keyPressed || !this.urlInput || this.props.locationValueSuffix.length === 0) {
+ if (this.keyPressed || !this.urlInput) {
return
}
- this.updateAutocomplete(this.lastVal, this.props.locationValue + this.props.locationValueSuffix)
+ this.updateAutocomplete(this.lastVal)
}, 10)
}
+ maybeUrlBarTextChanged (value) {
+ if (value !== this.props.locationValue) {
+ appActions.urlBarTextChanged(getCurrentWindowId(), this.props.activeTabId, value)
+ }
+ }
+
// restores the url bar to the current location
restore () {
const location = UrlUtil.getDisplayLocation(this.props.location, getSetting(settings.PDFJS_ENABLED))
if (this.urlInput) {
this.setValue(location)
}
- windowActions.setNavBarUserInput(location)
+ this.maybeUrlBarTextChanged(location)
}
/**
@@ -94,7 +104,6 @@ class UrlBar extends React.Component {
if (this.props.autocompleteEnabled) {
windowActions.urlBarAutocompleteEnabled(false)
}
- windowActions.setUrlBarSuggestions(undefined, null)
windowActions.setRenderUrlBarSuggestions(false)
}
@@ -190,7 +199,14 @@ class UrlBar extends React.Component {
this.hideAutoComplete()
break
case KeyCodes.TAB:
- this.hideAutoComplete()
+ if (this.shouldRenderUrlBarSuggestions) {
+ if (e.shiftKey) {
+ windowActions.previousUrlBarSuggestionSelected()
+ } else {
+ windowActions.nextUrlBarSuggestionSelected()
+ }
+ e.preventDefault()
+ }
break
default:
this.keyPressed = true
@@ -226,9 +242,16 @@ class UrlBar extends React.Component {
}
}
- updateAutocomplete (newValue, suggestion = this.lastVal + this.lastSuffix) {
- if (suggestion.startsWith(newValue)) {
- const newSuffix = suggestion.substring(newValue.length)
+ updateAutocomplete (newValue) {
+ let suggestion = ''
+ let suggestionNormalized = ''
+ if (this.props.suggestionList && this.props.suggestionList.size > 0) {
+ suggestion = this.props.suggestionList.getIn([this.activeIndex || 0, 'location']) || ''
+ suggestionNormalized = normalizeLocation(suggestion)
+ }
+ const newValueNormalized = normalizeLocation(newValue)
+ if (suggestionNormalized.startsWith(newValueNormalized) && suggestionNormalized.length > 0) {
+ const newSuffix = suggestionNormalized.substring(newValueNormalized.length)
this.setValue(newValue, newSuffix)
this.urlInput.setSelectionRange(newValue.length, newValue.length + newSuffix.length + 1)
return true
@@ -269,8 +292,6 @@ class UrlBar extends React.Component {
onChange (e) {
if (e.target.value !== this.lastVal + this.lastSuffix) {
e.preventDefault()
- // clear any current arrow or mouse hover selection
- windowActions.setUrlBarSuggestions(undefined, null)
this.setValue(e.target.value)
}
}
@@ -292,7 +313,7 @@ class UrlBar extends React.Component {
if (!this.keyPress) {
// if this is a key press don't sent the update until keyUp so
// showAutocompleteResult can handle the result
- windowActions.setNavBarUserInput(val)
+ this.maybeUrlBarTextChanged(val)
}
}
}
@@ -301,16 +322,18 @@ class UrlBar extends React.Component {
switch (e.keyCode) {
case KeyCodes.UP:
case KeyCodes.DOWN:
+ case KeyCodes.TAB:
case KeyCodes.ESC:
+ case KeyCodes.LEFT:
+ case KeyCodes.SHIFT:
+ case KeyCodes.RIGHT:
return
}
if (this.props.isSelected) {
windowActions.setUrlBarSelected(false)
}
- // clear any current arrow or mouse hover selection
- windowActions.setUrlBarSuggestions(undefined, null)
this.keyPressed = false
- windowActions.setNavBarUserInput(this.lastVal)
+ this.maybeUrlBarTextChanged(this.lastVal)
}
select () {
@@ -365,12 +388,11 @@ class UrlBar extends React.Component {
if (this.props.isFocused) {
this.focus()
}
- windowActions.setUrlBarSuggestions(undefined, null)
windowActions.setRenderUrlBarSuggestions(false)
} else if (this.props.location !== prevProps.location) {
// This is a url nav change
this.setValue(UrlUtil.getDisplayLocation(this.props.location, pdfjsEnabled))
- } else if (this.props.hasLocationValueSuffix &&
+ } else if (this.props.hasSuggestionMatch &&
this.props.isActive &&
this.props.locationValueSuffix !== this.lastSuffix) {
this.showAutocompleteResult()
@@ -463,7 +485,7 @@ class UrlBar extends React.Component {
const activateSearchEngine = urlbar.getIn(['searchDetail', 'activateSearchEngine'])
const urlbarSearchDetail = urlbar.get('searchDetail')
- let searchURL = currentWindow.getIn(['searchDetail', 'searchURL'])
+ let searchURL = appStoreRenderer.state.getIn(['searchDetail', 'searchURL'])
let searchShortcut = ''
// remove shortcut from the search terms
if (activateSearchEngine && urlbarSearchDetail !== null) {
@@ -486,7 +508,7 @@ class UrlBar extends React.Component {
props.title = activeFrame.get('title') || ''
props.scriptsBlocked = activeFrame.getIn(['noScript', 'blocked'])
props.isSecure = activeFrame.getIn(['security', 'isSecure'])
- props.hasLocationValueSuffix = urlbar.getIn(['suggestions', 'urlSuffix'])
+ props.hasSuggestionMatch = urlbar.getIn(['suggestions', 'hasSuggestionMatch'])
props.startLoadTime = activeFrame.get('startLoadTime')
props.endLoadTime = activeFrame.get('endLoadTime')
props.loading = activeFrame.get('loading')
@@ -593,7 +615,7 @@ class UrlBar extends React.Component {
?
: null
}
diff --git a/app/renderer/components/navigation/urlBarSuggestionItem.js b/app/renderer/components/navigation/urlBarSuggestionItem.js
index 027277a9e65..ed7b9655244 100644
--- a/app/renderer/components/navigation/urlBarSuggestionItem.js
+++ b/app/renderer/components/navigation/urlBarSuggestionItem.js
@@ -20,21 +20,21 @@ class UrlBarSuggestionItem extends ImmutableComponent {
return
{ this.node = node }}
className={cx({
selected: this.props.selected,
suggestionItem: true,
- [this.props.suggestion.type]: true
+ [this.props.suggestion.get('type')]: true
})}>
{
- this.props.suggestion.type !== suggestionTypes.TOP_SITE && this.props.suggestion.title
- ? {this.props.suggestion.title}
+ this.props.suggestion.get('type') !== suggestionTypes.TOP_SITE && this.props.suggestion.get('title')
+ ? {this.props.suggestion.get('title')}
: null
}
{
- this.props.suggestion.type !== suggestionTypes.SEARCH && this.props.suggestion.type !== suggestionTypes.ABOUT_PAGES
- ? {this.props.suggestion.location}
+ this.props.suggestion.get('type') !== suggestionTypes.SEARCH && this.props.suggestion.get('type') !== suggestionTypes.ABOUT_PAGES
+ ? {this.props.suggestion.get('location')}
: null
}
diff --git a/app/renderer/components/navigation/urlBarSuggestions.js b/app/renderer/components/navigation/urlBarSuggestions.js
index 4f06ea0ca7d..0f7e9948ca0 100644
--- a/app/renderer/components/navigation/urlBarSuggestions.js
+++ b/app/renderer/components/navigation/urlBarSuggestions.js
@@ -7,10 +7,12 @@ const ImmutableComponent = require('../immutableComponent')
const UrlBarSuggestionItem = require('./urlBarSuggestionItem')
const windowActions = require('../../../../js/actions/windowActions')
+const appActions = require('../../../../js/actions/appActions')
const suggestionTypes = require('../../../../js/constants/suggestionTypes')
const cx = require('../../../../js/lib/classSet')
const locale = require('../../../../js/l10n')
const {isForSecondaryAction} = require('../../../../js/lib/eventUtil')
+const {getCurrentWindowId} = require('../../currentWindow')
class UrlBarSuggestions extends ImmutableComponent {
constructor () {
@@ -27,7 +29,7 @@ class UrlBarSuggestions extends ImmutableComponent {
}
blur () {
- windowActions.setUrlBarSuggestions(null, null)
+ appActions.urlBarSuggestionsChanged(getCurrentWindowId(), null, null)
}
onSuggestionClicked (e) {
@@ -36,12 +38,12 @@ class UrlBarSuggestions extends ImmutableComponent {
render () {
const suggestions = this.props.suggestionList
- const bookmarkSuggestions = suggestions.filter((s) => s.type === suggestionTypes.BOOKMARK)
- const historySuggestions = suggestions.filter((s) => s.type === suggestionTypes.HISTORY)
- const aboutPagesSuggestions = suggestions.filter((s) => s.type === suggestionTypes.ABOUT_PAGES)
- const tabSuggestions = suggestions.filter((s) => s.type === suggestionTypes.TAB)
- const searchSuggestions = suggestions.filter((s) => s.type === suggestionTypes.SEARCH)
- const topSiteSuggestions = suggestions.filter((s) => s.type === suggestionTypes.TOP_SITE)
+ const bookmarkSuggestions = suggestions.filter((s) => s.get('type') === suggestionTypes.BOOKMARK)
+ const historySuggestions = suggestions.filter((s) => s.get('type') === suggestionTypes.HISTORY)
+ const aboutPagesSuggestions = suggestions.filter((s) => s.get('type') === suggestionTypes.ABOUT_PAGES)
+ const tabSuggestions = suggestions.filter((s) => s.get('type') === suggestionTypes.TAB)
+ const searchSuggestions = suggestions.filter((s) => s.get('type') === suggestionTypes.SEARCH)
+ const topSiteSuggestions = suggestions.filter((s) => s.get('type') === suggestionTypes.TOP_SITE)
let items = []
let index = 0
@@ -63,7 +65,7 @@ class UrlBarSuggestions extends ImmutableComponent {
}
items = items.concat(suggestions.map((suggestion, i) => {
const currentIndex = index + i
- const selected = this.activeIndex === currentIndex || (!this.activeIndex && currentIndex === 0 && this.props.hasLocationValueSuffix)
+ const selected = this.activeIndex === currentIndex || (!this.activeIndex && currentIndex === 0 && this.props.hasSuggestionMatch)
return suggestions.size) {
newIndex = null
}
- windowActions.setUrlBarSuggestions(suggestions, newIndex)
+ appActions.urlBarSuggestionsChanged(getCurrentWindowId(), suggestions, newIndex)
}
}
diff --git a/app/renderer/fetchSearchSuggestions.js b/app/renderer/fetchSearchSuggestions.js
deleted file mode 100644
index 13402756dc0..00000000000
--- a/app/renderer/fetchSearchSuggestions.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/* 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 Immutable = require('immutable')
-const windowActions = require('../../js/actions/windowActions')
-const debounce = require('../../js/lib/debounce')
-
-const fetchSearchSuggestions = debounce((tabId, autocompleteURL, searchTerms) => {
- const xhr = new window.XMLHttpRequest()
- xhr.open('GET', autocompleteURL
- .replace('{searchTerms}', encodeURIComponent(searchTerms)), true)
- xhr.responseType = 'json'
- xhr.send()
- xhr.onload = () => {
- // Once we have the online suggestions, append them to the others
- windowActions.searchSuggestionResultsAvailable(tabId, Immutable.fromJS(xhr.response[1]))
- }
-}, 100)
-
-module.exports = fetchSearchSuggestions
diff --git a/app/renderer/lib/suggestion.js b/app/renderer/lib/suggestion.js
deleted file mode 100644
index fed465eefd4..00000000000
--- a/app/renderer/lib/suggestion.js
+++ /dev/null
@@ -1,210 +0,0 @@
-/* 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 urlParse = require('../../common/urlParse')
-const appConfig = require('../../../js/constants/appConfig')
-const _ = require('underscore')
-const Immutable = require('immutable')
-const {makeImmutable} = require('../../common/state/immutableUtil')
-
-const sigmoid = (t) => {
- return 1 / (1 + Math.pow(Math.E, -t))
-}
-
-const ONE_DAY = 1000 * 60 * 60 * 24
-
-/*
- * Calculate the sorting priority for a history item based on number of
- * accesses and time since last access
- *
- * @param {number} count - The number of times this site has been accessed
- * @param {number} currentTime - Current epoch millisecnds
- * @param {boolean} lastAccessedTime - Epoch milliseconds of last access
- *
- */
-module.exports.sortingPriority = (count, currentTime, lastAccessedTime, ageDecayConstant) => {
- // number of days since last access (with fractional component)
- const ageInDays = (currentTime - (lastAccessedTime || currentTime)) / ONE_DAY
- // decay factor based on age
- const ageFactor = 1 - ((sigmoid(ageInDays / ageDecayConstant) - 0.5) * 2)
- // sorting priority
- // console.log(count, ageInDays, ageFactor, count * ageFactor)
- return count * ageFactor
-}
-
-/*
- * Sort two history items by priority
- *
- * @param {ImmutableObject} s1 - first history item
- * @param {ImmutableObject} s2 - second history item
- *
- * Return the relative order of two site entries taking into consideration
- * the number of times the site has been accessed and the length of time
- * since the last access.
- *
- * The base sort order is determined by the count attribute of the site
- * entry. A modifier is then computed based on the length of time since
- * the last access. A sigmoid function is used to weight more recent
- * entries higher than entries in the past. This is not a linear function,
- * entries in the far past with many counts will still be discounted
- * heavily as the sigmoid modifier will cancel most of the count
- * base parameter.
- *
- * Below is a sample comparison of two sites that have been accessed
- * recently (but not at the identical time). Each site is accessed
- * 9 times. The count is discounted by an aging factor calculated
- * using the sigmoid decay function.
- *
- * http://www.gm.ca/gm/
- *
- * ageInDays 0.17171469907407408
- * ageFactor 0.9982828546969802
- * count 9
- * priority 0.9982828546969802
- *
- * http://www.gm.com/index.html
- *
- * ageInDays 0.17148791666666666
- * ageFactor 0.9982851225143763
- * count 9
- * priority 0.9982851225143763
- *
- */
-module.exports.sortByAccessCountWithAgeDecay = (s1, s2) => {
- const now = new Date()
- const s1Priority = module.exports.sortingPriority(
- s1.get('count') || 0,
- now.getTime(),
- s1.get('lastAccessedTime') || now.getTime(),
- appConfig.urlSuggestions.ageDecayConstant
- )
- const s2Priority = module.exports.sortingPriority(
- s2.get('count') || 0,
- now.getTime(),
- s2.get('lastAccessedTime') || now.getTime(),
- appConfig.urlSuggestions.ageDecayConstant
- )
- return s2Priority - s1Priority
-}
-
-/*
- * Return a 1 if the url is 'simple' as in without query, search or
- * hash components. Return 0 otherwise.
- *
- * @param {ImmutableObject} site - object represent history entry
- *
- */
-module.exports.simpleDomainNameValue = (site) => {
- const parsed = urlParse(site.get('location'))
- if (parsed.hash === null && parsed.search === null && parsed.query === null && parsed.pathname === '/') {
- return 1
- } else {
- return 0
- }
-}
-
-/*
- * Normalize a location for url suggestion sorting
- *
- * @param {string} location - history item location
- *
- */
-module.exports.normalizeLocation = (location) => {
- if (typeof location === 'string') {
- location = location.replace(/www\./, '')
- location = location.replace(/^http:\/\//, '')
- location = location.replace(/^https:\/\//, '')
- }
- return location
-}
-
-/*
- * Determines based on user input if the location should
- * be normalized. If the user is typing http prefix then
- * they are specifying something explicitly.
- *
- * @return true if urls being compared should be normalized
- */
-module.exports.shouldNormalizeLocation = (input) => {
- const prefixes = ['http://', 'https://', 'www.']
- return prefixes.every((prefix) => {
- if (input.length > prefix.length) {
- return true
- }
- for (let i = 0; i < Math.min(prefix.length, input.length); i++) {
- if (input[i] !== prefix[i]) {
- return true
- }
- }
- return false
- })
-}
-
-/*
- * return a site representing the simple location for a
- * set of related sites without a history item for the
- * simple location.
- *
- * This is used to show a history suggestion for something
- * like www.google.com if it has not been visited but
- * there are two or more locations with that prefix containing
- * path info or parameters
- *
- * @param {Array[Object]} sites - array of similar sites
- */
-var virtualSite = (sites) => {
- // array of sites without paths or query params
- var simple = sites.filter((parsed) => {
- return (parsed.hash === null && parsed.search === null && parsed.query === null && parsed.pathname === '/')
- })
- // if there are no simple locations then we will build and return one
- if (simple.length === 0) {
- // we need to create a virtual history item
- return Immutable.Map({
- location: sites[0].protocol + '//' + sites[0].host,
- count: 0,
- title: sites[0].host,
- lastAccessedTime: (new Date()).getTime()
- })
- }
-}
-
-/*
- * Create an array of simple locations from history
- * The simple locations will be the root domain for a location
- * without parameters or path
- *
- * @param {ImmutableList[ImmutableMap]} - history
- */
-module.exports.createVirtualHistoryItems = (historySites) => {
- historySites = makeImmutable(historySites || {})
-
- // parse each history item
- const parsedHistorySites = []
- historySites.forEach((site) => {
- if (site && site.get('location')) {
- parsedHistorySites.push(
- urlParse(site.get('location'))
- )
- }
- })
- // group them by host
- var grouped = _.groupBy(parsedHistorySites, (parsedSite) => {
- return parsedSite.host || 'unknown'
- })
- // find groups with more than 2 of the same host
- var multiGroupKeys = _.filter(_.keys(grouped), (k) => {
- return grouped[k].length > 0
- })
- // potentially create virtual history items
- var virtualHistorySites = _.map(multiGroupKeys, (location) => {
- return virtualSite(grouped[location])
- })
- virtualHistorySites = _.filter(virtualHistorySites, (site) => {
- return !!site
- })
- return Immutable.fromJS(_.object(virtualHistorySites.map((site) => {
- return [site.location, site]
- })))
-}
diff --git a/app/renderer/reducers/urlBarReducer.js b/app/renderer/reducers/urlBarReducer.js
index 1df5cd82a6b..d1b8b0c5ef1 100644
--- a/app/renderer/reducers/urlBarReducer.js
+++ b/app/renderer/reducers/urlBarReducer.js
@@ -5,22 +5,13 @@
'use strict'
const windowConstants = require('../../../js/constants/windowConstants')
-const {aboutUrls, isNavigatableAboutPage, isSourceAboutUrl, isUrl, getSourceAboutUrl, getSourceMagnetUrl} = require('../../../js/lib/appUrlUtil')
+const appConstants = require('../../../js/constants/appConstants')
+const {isUrl, getSourceAboutUrl, getSourceMagnetUrl} = require('../../../js/lib/appUrlUtil')
const {isURL, isPotentialPhishingUrl, getUrlFromInput} = require('../../../js/lib/urlutil')
-const {getFrameByKey, getFrameKeyByTabId, activeFrameStatePath, frameStatePath, getActiveFrame, getFrameByTabId} = require('../../../js/state/frameStateUtil')
-const getSetting = require('../../../js/settings').getSetting
-const {isBookmark, isDefaultEntry, isHistoryEntry} = require('../../../js/state/siteUtil')
-const fetchSearchSuggestions = require('../fetchSearchSuggestions')
+const {getFrameByKey, activeFrameStatePath, frameStatePath, getActiveFrame, getFrameByTabId} = require('../../../js/state/frameStateUtil')
const searchProviders = require('../../../js/data/searchProviders')
-const settings = require('../../../js/constants/settings')
const Immutable = require('immutable')
-const config = require('../../../js/constants/config')
-const top500 = require('../../../js/data/top500')
-const suggestion = require('../lib/suggestion')
-const suggestionTypes = require('../../../js/constants/suggestionTypes')
-const {navigateSiteClickHandler, frameClickHandler} = require('../suggestionClickHandlers')
-const appStoreRenderer = require('../../../js/stores/appStoreRenderer')
-
+const {navigateSiteClickHandler} = require('../suggestionClickHandlers')
const navigationBarState = require('../../common/state/navigationBarState')
const tabState = require('../../common/state/tabState')
@@ -46,36 +37,6 @@ const updateSearchEngineInfoFromInput = (state, frameProps) => {
return state
}
-const searchXHR = (state, frameProps, searchOnline) => {
- const searchDetail = state.get('searchDetail')
- const frameSearchDetail = frameProps.getIn(['navbar', 'urlbar', 'searchDetail'])
- if (!searchDetail && !frameSearchDetail) {
- return state
- }
- let autocompleteURL = frameSearchDetail
- ? frameSearchDetail.get('autocomplete')
- : searchDetail.get('autocompleteURL')
- if (!getSetting(settings.OFFER_SEARCH_SUGGESTIONS) || !autocompleteURL) {
- state = state.setIn(activeFrameStatePath(state).concat(['navbar', 'urlbar', 'suggestions', 'searchResults']), Immutable.fromJS([]))
- return state
- }
-
- let input = frameProps.getIn(['navbar', 'urlbar', 'location'])
- if (!isUrl(input) && input.length > 0) {
- if (searchDetail) {
- const replaceRE = new RegExp('^' + searchDetail.get('shortcut') + ' ', 'g')
- input = input.replace(replaceRE, '')
- }
-
- if (searchOnline) {
- fetchSearchSuggestions(frameProps.get('tabId'), autocompleteURL, input)
- }
- } else {
- state = state.setIn(activeFrameStatePath(state).concat(['navbar', 'urlbar', 'suggestions', 'searchResults']), Immutable.fromJS([]))
- }
- return state
-}
-
const setUrlSuggestions = (state, suggestionList) => {
if (suggestionList !== undefined) {
state = state.setIn(activeFrameStatePath(state).concat(['navbar', 'urlbar', 'suggestions', 'suggestionList']), suggestionList)
@@ -110,218 +71,27 @@ const updateUrlSuffix = (state, suggestionList) => {
}
const suggestion = suggestionList && suggestionList.get(selectedIndex)
let suffix = ''
+ let hasSuggestionMatch = false
if (suggestion) {
const autocompleteEnabled = state.getIn(activeFrameStatePath(state).concat(['navbar', 'urlbar', 'suggestions', 'autocompleteEnabled']))
if (autocompleteEnabled) {
const location = state.getIn(activeFrameStatePath(state).concat(['navbar', 'urlbar', 'location'])) || ''
- const index = suggestion.location.toLowerCase().indexOf(location.toLowerCase())
+ const index = suggestion.get('location').toLowerCase().indexOf(location.toLowerCase())
if (index !== -1) {
- const beforePrefix = suggestion.location.substring(0, index)
+ const beforePrefix = suggestion.get('location').substring(0, index)
if (beforePrefix.endsWith('://') || beforePrefix.endsWith('://www.') || index === 0) {
- suffix = suggestion.location.substring(index + location.length)
+ suffix = suggestion.get('location').substring(index + location.length)
+ hasSuggestionMatch = true
}
}
}
}
state = state.setIn(activeFrameStatePath(state).concat(['navbar', 'urlbar', 'suggestions', 'urlSuffix']), suffix)
+ state = state.setIn(activeFrameStatePath(state).concat(['navbar', 'urlbar', 'suggestions', 'hasSuggestionMatch']), hasSuggestionMatch)
return state
}
-const generateNewSuggestionsList = (state) => {
- const activeFrameKey = state.get('activeFrameKey')
- const urlLocation = state.getIn(activeFrameStatePath(state).concat(['navbar', 'urlbar', 'location']))
- const searchResults = state.getIn(activeFrameStatePath(state).concat(['navbar', 'urlbar', 'suggestions', 'searchResults']))
- const frameSearchDetail = state.getIn(activeFrameStatePath(state).concat(['navbar', 'urlbar', 'searchDetail']))
- const searchDetail = state.get('searchDetail')
-
- if (!urlLocation) {
- return state
- }
-
- const urlLocationLower = urlLocation.toLowerCase()
- let suggestionsList = new Immutable.List()
- const defaultme = (x) => x
- const mapListToElements = ({data, maxResults, type, clickHandler = navigateSiteClickHandler.bind(this),
- sortHandler = defaultme, formatTitle = defaultme, formatUrl = defaultme,
- filterValue = (site) => {
- return site.toLowerCase().indexOf(urlLocationLower) !== -1
- }
- }) => {
- // Filter out things which are already in our own list at a smaller index
- // Filter out things which are already in the suggestions list
- let filteredData = data.filter((site) =>
- suggestionsList.findIndex((x) => (x.location || '').toLowerCase() === (formatUrl(site) || '').toLowerCase()) === -1 ||
- // Tab autosuggestions should always be included since they will almost always be in history
- type === suggestionTypes.TAB)
- // Per suggestion provider filter
- if (filterValue) {
- filteredData = filteredData.filter(filterValue)
- }
-
- return filteredData
- .sort(sortHandler)
- .take(maxResults)
- .map((site) => {
- return {
- onClick: clickHandler.bind(null, site),
- title: formatTitle(site),
- location: formatUrl(site),
- type
- }
- })
- }
-
- const shouldNormalize = suggestion.shouldNormalizeLocation(urlLocationLower)
- const urlLocationLowerNormalized = suggestion.normalizeLocation(urlLocationLower)
- const sortBasedOnLocationPos = (s1, s2) => {
- const location1 = shouldNormalize ? suggestion.normalizeLocation(s1.get('location')) : s1.get('location')
- const location2 = shouldNormalize ? suggestion.normalizeLocation(s2.get('location')) : s2.get('location')
- const pos1 = location1.indexOf(urlLocationLowerNormalized)
- const pos2 = location2.indexOf(urlLocationLowerNormalized)
- if (pos1 === -1 && pos2 === -1) {
- return 0
- } else if (pos1 === -1) {
- return 1
- } else if (pos2 === -1) {
- return -1
- } else {
- if (pos1 - pos2 !== 0) {
- return pos1 - pos2
- } else {
- // sort site.com higher than site.com/somepath
- const sdnv1 = suggestion.simpleDomainNameValue(s1)
- const sdnv2 = suggestion.simpleDomainNameValue(s2)
- if (sdnv1 !== sdnv2) {
- return sdnv2 - sdnv1
- } else {
- // If there's a tie on the match location, use the age
- // decay modified access count
- return suggestion.sortByAccessCountWithAgeDecay(s1, s2)
- }
- }
- }
- }
-
- // NOTE: Iterating sites can take a long time! Please be mindful when
- // working with the history and bookmark suggestion code.
- const historySuggestionsOn = getSetting(settings.HISTORY_SUGGESTIONS)
- const bookmarkSuggestionsOn = getSetting(settings.BOOKMARK_SUGGESTIONS)
- const shouldIterateSites = historySuggestionsOn || bookmarkSuggestionsOn
- if (shouldIterateSites) {
- // Note: Bookmark sites are now included in history. This will allow
- // sites to appear in the auto-complete regardless of their bookmark
- // status. If history is turned off, bookmarked sites will appear
- // in the bookmark section.
- const sitesFilter = (site) => {
- const location = site.get('location')
- if (!location) {
- return false
- }
- const title = site.get('title')
- return location.toLowerCase().indexOf(urlLocationLower) !== -1 ||
- (title && title.toLowerCase().indexOf(urlLocationLower) !== -1)
- }
-
- let historySites = new Immutable.List()
- let bookmarkSites = new Immutable.List()
- const sites = appStoreRenderer.state.get('sites')
- sites.forEach(site => {
- if (!sitesFilter(site)) {
- return
- }
- if (historySuggestionsOn && isHistoryEntry(site) && !isDefaultEntry(site)) {
- historySites = historySites.push(site)
- return
- }
- if (bookmarkSuggestionsOn && isBookmark(site) && !isDefaultEntry(site)) {
- bookmarkSites = bookmarkSites.push(site)
- }
- })
-
- if (historySites.size > 0) {
- historySites = historySites.concat(suggestion.createVirtualHistoryItems(historySites))
-
- suggestionsList = suggestionsList.concat(mapListToElements({
- data: historySites,
- maxResults: config.urlBarSuggestions.maxHistorySites,
- type: suggestionTypes.HISTORY,
- clickHandler: navigateSiteClickHandler((site) => {
- return site.get('location')
- }),
- sortHandler: sortBasedOnLocationPos,
- formatTitle: (site) => site.get('title'),
- formatUrl: (site) => site.get('location'),
- filterValue: null
- }))
- }
-
- if (bookmarkSites.size > 0) {
- suggestionsList = suggestionsList.concat(mapListToElements({
- data: bookmarkSites,
- maxResults: config.urlBarSuggestions.maxBookmarkSites,
- type: suggestionTypes.BOOKMARK,
- clickHandler: navigateSiteClickHandler((site) => {
- return site.get('location')
- }),
- sortHandler: sortBasedOnLocationPos,
- formatTitle: (site) => site.get('title'),
- formatUrl: (site) => site.get('location'),
- filterValue: null
- }))
- }
- }
-
- // about pages
- suggestionsList = suggestionsList.concat(mapListToElements({
- data: aboutUrls.keySeq().filter((x) => isNavigatableAboutPage(x)),
- maxResults: config.urlBarSuggestions.maxAboutPages,
- type: suggestionTypes.ABOUT_PAGES,
- clickHandler: navigateSiteClickHandler((x) => x)}))
-
- // opened frames
- if (getSetting(settings.OPENED_TAB_SUGGESTIONS)) {
- suggestionsList = suggestionsList.concat(mapListToElements({
- data: state.get('frames'),
- maxResults: config.urlBarSuggestions.maxOpenedFrames,
- type: suggestionTypes.TAB,
- clickHandler: frameClickHandler,
- sortHandler: sortBasedOnLocationPos,
- formatTitle: (frame) => frame.get('title'),
- formatUrl: (frame) => frame.get('location'),
- filterValue: (frame) => !isSourceAboutUrl(frame.get('location')) &&
- frame.get('key') !== activeFrameKey &&
- (
- (frame.get('title') && frame.get('title').toLowerCase().indexOf(urlLocationLower) !== -1) ||
- (frame.get('location') && frame.get('location').toLowerCase().indexOf(urlLocationLower) !== -1)
- )
- }))
- }
-
- // Search suggestions
- if (getSetting(settings.OFFER_SEARCH_SUGGESTIONS) && searchResults) {
- suggestionsList = suggestionsList.concat(mapListToElements({
- data: searchResults,
- maxResults: config.urlBarSuggestions.maxSearch,
- type: suggestionTypes.SEARCH,
- clickHandler: navigateSiteClickHandler((searchTerms) => {
- let searchURL = frameSearchDetail
- ? frameSearchDetail.get('search') : searchDetail.get('searchURL')
- return searchURL.replace('{searchTerms}', encodeURIComponent(searchTerms))
- })
- }))
- }
-
- // Alexa top 500
- suggestionsList = suggestionsList.concat(mapListToElements({
- data: top500,
- maxResults: config.urlBarSuggestions.maxTopSites,
- type: suggestionTypes.TOP_SITE,
- clickHandler: navigateSiteClickHandler((x) => x)}))
-
- return setUrlSuggestions(state, suggestionsList)
-}
-
const getLocation = (location) => {
location = location.trim()
location = getSourceAboutUrl(location) ||
@@ -363,9 +133,6 @@ const setNavBarUserInput = (state, location) => {
state = updateNavBarInput(state, location)
const activeFrameProps = getActiveFrame(state)
state = updateSearchEngineInfoFromInput(state, activeFrameProps)
- state = searchXHR(state, activeFrameProps, true)
- state = generateNewSuggestionsList(state)
- state = updateUrlSuffix(state, state.getIn(activeFrameStatePath(state).concat(['navbar', 'urlbar', 'suggestions', 'suggestionList']), Immutable.Map()))
if (!location) {
state = setRenderUrlBarSuggestions(state, false)
}
@@ -386,10 +153,15 @@ const setActive = (state, isActive) => {
const urlBarReducer = (state, action) => {
const tabId = state.getIn(activeFrameStatePath(state).concat(['tabId']), tabState.TAB_ID_NONE)
-
switch (action.actionType) {
- case windowConstants.WINDOW_SET_NAVBAR_INPUT:
- state = setNavBarUserInput(state, action.location)
+ case appConstants.APP_URL_BAR_TEXT_CHANGED:
+ state = setNavBarUserInput(state, action.input)
+ break
+ case appConstants.APP_URL_BAR_SUGGESTIONS_CHANGED:
+ if (action.selectedIndex !== undefined) {
+ state = state.setIn(activeFrameStatePath(state).concat(['navbar', 'urlbar', 'suggestions', 'selectedIndex']), action.selectedIndex)
+ }
+ state = setUrlSuggestions(state, action.suggestionList)
break
case windowConstants.WINDOW_SET_NAVIGATED:
// For about: URLs, make sure we store the URL as about:something
@@ -469,8 +241,8 @@ const urlBarReducer = (state, action) => {
const selectedIndexPath = activeFrameStatePath(state).concat(['navbar', 'urlbar', 'suggestions', 'selectedIndex'])
const suggestionList = state.getIn(activeFrameStatePath(state).concat(['navbar', 'urlbar', 'suggestions', 'suggestionList']))
const selectedIndex = state.getIn(selectedIndexPath)
- const lastSuffix = state.getIn(activeFrameStatePath(state).concat(['navbar', 'urlbar', 'suggestions', 'urlSuffix']))
- if (!selectedIndex && selectedIndex !== 0 && !lastSuffix) {
+ const hasSuggestionMatch = state.getIn(activeFrameStatePath(state).concat(['navbar', 'urlbar', 'suggestions', 'hasSuggestionMatch']))
+ if (!selectedIndex && selectedIndex !== 0 && !hasSuggestionMatch) {
state = state.setIn(selectedIndexPath, 0)
} else if (selectedIndex > 0) {
state = state.setIn(selectedIndexPath, selectedIndex - 1)
@@ -484,8 +256,8 @@ const urlBarReducer = (state, action) => {
const selectedIndexPath = activeFrameStatePath(state).concat(['navbar', 'urlbar', 'suggestions', 'selectedIndex'])
const suggestionList = state.getIn(activeFrameStatePath(state).concat(['navbar', 'urlbar', 'suggestions', 'suggestionList']))
const selectedIndex = state.getIn(selectedIndexPath)
- const lastSuffix = state.getIn(activeFrameStatePath(state).concat(['navbar', 'urlbar', 'suggestions', 'urlSuffix']))
- if (!selectedIndex && selectedIndex !== 0 && !lastSuffix) {
+ const hasSuggestionMatch = state.getIn(activeFrameStatePath(state).concat(['navbar', 'urlbar', 'suggestions', 'hasSuggestionMatch']))
+ if (!selectedIndex && selectedIndex !== 0 && !hasSuggestionMatch) {
state = state.setIn(selectedIndexPath, 0)
} else if (selectedIndex < suggestionList.size - 1) {
state = state.setIn(selectedIndexPath, selectedIndex + 1)
@@ -495,18 +267,9 @@ const urlBarReducer = (state, action) => {
state = updateUrlSuffix(state, state.getIn(activeFrameStatePath(state).concat(['navbar', 'urlbar', 'suggestions', 'suggestionList']), suggestionList))
break
}
- case windowConstants.WINDOW_SEARCH_SUGGESTION_RESULTS_AVAILABLE:
- const frameKey = getFrameKeyByTabId(state, action.tabId)
- state = state.setIn(frameStatePath(state, frameKey).concat(['navbar', 'urlbar', 'suggestions', 'searchResults']), action.searchResults)
- state = generateNewSuggestionsList(state)
- break
case windowConstants.WINDOW_URL_BAR_AUTOCOMPLETE_ENABLED:
state = state.setIn(activeFrameStatePath(state).concat(['navbar', 'urlbar', 'suggestions', 'autocompleteEnabled']), action.enabled)
break
- case windowConstants.WINDOW_SET_URL_BAR_SUGGESTIONS:
- state = state.setIn(activeFrameStatePath(state).concat(['navbar', 'urlbar', 'suggestions', 'selectedIndex']), action.selectedIndex)
- state = setUrlSuggestions(state, action.suggestionList)
- break
case windowConstants.WINDOW_SET_URL_BAR_ACTIVE:
state = setActive(state, action.isActive)
break
@@ -518,8 +281,10 @@ const urlBarReducer = (state, action) => {
const suggestionList = state.getIn(activeFrameStatePath(state).concat(['navbar', 'urlbar', 'suggestions', 'suggestionList']))
if (suggestionList.size > 0) {
// It's important this doesn't run sync or else the returned state below will overwrite anything done in the click handler
- setImmediate(() =>
- suggestionList.get(selectedIndex).onClick(action.isForSecondaryAction, action.shiftKey))
+ setImmediate(() => {
+ const suggestion = suggestionList.get(selectedIndex)
+ navigateSiteClickHandler(suggestion, action.isForSecondaryAction, action.shiftKey)
+ })
}
break
}
diff --git a/app/renderer/suggestionClickHandlers.js b/app/renderer/suggestionClickHandlers.js
index 72c41b6038e..eff83d0af23 100644
--- a/app/renderer/suggestionClickHandlers.js
+++ b/app/renderer/suggestionClickHandlers.js
@@ -7,16 +7,39 @@
const appActions = require('../../js/actions/appActions')
const windowActions = require('../../js/actions/windowActions')
const windowStore = require('../../js/stores/windowStore')
+const appStoreRenderer = require('../../js/stores/appStoreRenderer')
+const suggestionTypes = require('../../js/constants/suggestionTypes')
+const {makeImmutable} = require('../common/state/immutableUtil')
const {getActiveFrame} = require('../../js/state/frameStateUtil')
-const navigateSiteClickHandler = (formatUrl) => (site, isForSecondaryAction, shiftKey) => {
- const url = formatUrl(site)
+const navigateSiteClickHandler = (suggestionData, isForSecondaryAction, shiftKey) => {
// When clicked make sure to hide autocomplete
windowActions.setRenderUrlBarSuggestions(false)
+
+ suggestionData = makeImmutable(suggestionData)
+ const type = suggestionData.get('type')
+ let partitionNumber
+ let url
+ if (type === suggestionTypes.SEARCH) {
+ const frameSearchDetail = suggestionData.getIn(['navbar', 'urlbar', 'searchDetail'])
+ const searchDetail = appStoreRenderer.state.get('searchDetail')
+ let searchURL = frameSearchDetail
+ ? frameSearchDetail.get('search') : searchDetail.get('searchURL')
+ url = searchURL.replace('{searchTerms}', encodeURIComponent(suggestionData.get('location')))
+ } else if (type === suggestionTypes.TAB) {
+ appActions.tabActivateRequested(suggestionData.get('tabId'))
+ return
+ } else if (type === suggestionTypes.TOP_SITE) {
+ url = suggestionData.get('location')
+ } else {
+ url = suggestionData.get('location')
+ partitionNumber = (suggestionData && suggestionData.get && suggestionData.get('partitionNumber')) || undefined
+ }
+
if (isForSecondaryAction) {
appActions.createTabRequested({
url,
- partitionNumber: (site && site.get && site.get('partitionNumber')) || undefined,
+ partitionNumber,
active: !!shiftKey
})
} else {
@@ -26,10 +49,6 @@ const navigateSiteClickHandler = (formatUrl) => (site, isForSecondaryAction, shi
}
}
-const frameClickHandler = (frameProps) =>
- appActions.tabActivateRequested(frameProps.get('tabId'))
-
module.exports = {
- navigateSiteClickHandler,
- frameClickHandler
+ navigateSiteClickHandler
}
diff --git a/app/sessionStore.js b/app/sessionStore.js
index 71f6f8360a5..5deb3aee881 100644
--- a/app/sessionStore.js
+++ b/app/sessionStore.js
@@ -658,7 +658,8 @@ module.exports.defaultAppState = () => {
httpsEverywhere: {
count: 0
},
- defaultWindowParams: {}
+ defaultWindowParams: {},
+ searchDetail: null
}
}
diff --git a/docs/appActions.md b/docs/appActions.md
index fbe64eda399..f5f8fa5bd23 100644
--- a/docs/appActions.md
+++ b/docs/appActions.md
@@ -1023,6 +1023,18 @@ Notifies autoplay has been blocked
+### savePassword()
+
+Handle 'save-password' event from muon
+
+
+
+### updatePassword()
+
+Handle 'update-password' event from muon
+
+
+
### deletePassword(passwordDetail)
Deletes login credentials
@@ -1045,6 +1057,57 @@ Delete legacy "never saved password" list
+### urlBarTextChanged(windowId, tabId, input)
+
+Indicates that the urlbar text has changed, usually from user input
+
+**Parameters**
+
+**windowId**: `number`, The window ID the text is being changed inside of
+
+**tabId**: `number`, The tab ID the text is being changed inside of
+
+**input**: `string`, The text that was entered into the URL bar
+
+
+
+### searchSuggestionResultsAvailable(tabId, searchResults)
+
+New URL bar suggestion search results are available.
+This is typically from a service like Duck Duck Go auto complete for the portion of text that the user typed in.
+
+**Parameters**
+
+**tabId**: `number`, the tab id for the action
+
+**searchResults**: , The search results for the currently entered URL bar text.
+
+
+
+### urlBarSuggestionsChanged(windowId, suggestionList, selectedIndex)
+
+Indicates URL bar suggestions and selected index.
+
+**Parameters**
+
+**windowId**: `number`, the window ID
+
+**suggestionList**: `Array.<Object>`, The list of suggestions for the entered URL bar text. This can be generated from history, bookmarks, etc.
+
+**selectedIndex**: `number`, The index for the selected item (users can select items with down arrow on their keyboard)
+
+
+
+### defaultSearchEngineLoaded(searchDetail)
+
+Dispatches a message to set the search engine details.
+
+**Parameters**
+
+**searchDetail**: `Object`, the search details
+
+
+
* * *
diff --git a/docs/state.md b/docs/state.md
index 5077232623e..9c4307bab9a 100644
--- a/docs/state.md
+++ b/docs/state.md
@@ -357,7 +357,11 @@ AppStore
width: number,
// session properties
windowId: number // the muon id for the window
- }]
+ }],
+ searchDetail: {
+ autocompleteURL: string, // ditto re: {searchTerms}
+ searchURL: string // with replacement var in string: {searchTerms}
+ },
}
```
@@ -465,7 +469,6 @@ WindowStore
selected: boolean, // is the urlbar text selected
suggestions: {
autocompleteEnabled: boolean, // used to enable or disable autocomplete
- searchResults: array, // autocomplete server results if enabled
selectedIndex: number, // index of the item in focus
shouldRender: boolean, // if the suggestions should render
suggestionList: {
@@ -624,10 +627,7 @@ WindowStore
minPublisherVisits: number // e.g., 0
}
},
- searchDetail: {
- autocompleteURL: string, // ditto re: {searchTerms}
- searchURL: string // with replacement var in string: {searchTerms}
- },
+ searchResults: array, // autocomplete server results if enabled
ui: {
bookmarksToolbar: {
selectedFolderId: number // folderId from the siteDetail of the currently expanded folder
diff --git a/docs/windowActions.md b/docs/windowActions.md
index b7dc3227e48..b55e4f5ea9b 100644
--- a/docs/windowActions.md
+++ b/docs/windowActions.md
@@ -98,17 +98,6 @@ Dispatches a message to set the frame error state
-### setNavBarUserInput(location)
-
-Dispatches a message to the store to set the user entered text for the URL bar.
-Unlike setLocation and loadUrl, this does not modify the state of src and location.
-
-**Parameters**
-
-**location**: `string`, The text to set as the new navbar URL input
-
-
-
### setFindbarShown(frameKey, shown)
Shows/hides the find-in-page bar.
@@ -291,18 +280,6 @@ Dispatches a message to the store to indicate that the specified frame should mo
-### setUrlBarSuggestions(suggestionList, selectedIndex)
-
-Sets the URL bar suggestions and selected index.
-
-**Parameters**
-
-**suggestionList**: `Array.<Object>`, The list of suggestions for the entered URL bar text. This can be generated from history, bookmarks, etc.
-
-**selectedIndex**: `number`, The index for the selected item (users can select items with down arrow on their keyboard)
-
-
-
### activeSuggestionClicked(isForSecondaryAction, shiftKey)
The active URL bar suggestion was clicked
@@ -342,19 +319,6 @@ This is sometimes only temporarily disabled, e.g. a user is pressing backspace.
-### searchSuggestionResultsAvailable(tabId, searchResults)
-
-New URL bar suggestion search results are available.
-This is typically from a service like Duck Duck Go auto complete for the portion of text that the user typed in.
-
-**Parameters**
-
-**tabId**: `number`, the tab id for the action
-
-**searchResults**: , The search results for the currently entered URL bar text.
-
-
-
### setUrlBarSelected(isSelected)
Marks the URL bar text as selected or not
@@ -393,16 +357,6 @@ set from an IPC call.
-### setSearchDetail(searchDetail)
-
-Dispatches a message to set the search engine details.
-
-**Parameters**
-
-**searchDetail**: `Object`, the search details
-
-
-
### setFindDetail(frameKey, findDetail)
Dispatches a message to set the find-in-page details.
diff --git a/js/actions/appActions.js b/js/actions/appActions.js
index 702047dc026..361a60ab69c 100644
--- a/js/actions/appActions.js
+++ b/js/actions/appActions.js
@@ -3,7 +3,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
'use strict'
-const AppDispatcher = require('../dispatcher/appDispatcher')
+const {dispatch} = require('../dispatcher/appDispatcher')
const appConstants = require('../constants/appConstants')
const appActions = {
@@ -14,7 +14,7 @@ const appActions = {
* @param {object} appState - Initial app state object (not yet converted to ImmutableJS)
*/
setState: function (appState) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_SET_STATE,
appState
})
@@ -28,7 +28,7 @@ const appActions = {
* @param {function} cb - Callback to call after the window is loaded, will only work if called from the main process.
*/
newWindow: function (frameOpts, browserOpts, restoredState, cb) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_NEW_WINDOW,
frameOpts,
browserOpts,
@@ -38,35 +38,35 @@ const appActions = {
},
windowReady: function (windowId) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_WINDOW_READY,
windowId
})
},
closeWindow: function (windowId) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_CLOSE_WINDOW,
windowId
})
},
windowClosed: function (windowValue) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_WINDOW_CLOSED,
windowValue
})
},
windowCreated: function (windowValue) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_WINDOW_CREATED,
windowValue
})
},
windowUpdated: function (windowValue) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_WINDOW_UPDATED,
windowValue
})
@@ -77,7 +77,7 @@ const appActions = {
* @param {Object} frame
*/
frameChanged: function (frame) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_FRAME_CHANGED,
frame
})
@@ -88,7 +88,7 @@ const appActions = {
* @param {Object} tabValue
*/
tabCreated: function (tabValue) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_TAB_CREATED,
tabValue
})
@@ -102,7 +102,7 @@ const appActions = {
* @param {Number} windowId
*/
tabMoved: function (tabId, frameOpts, browserOpts, windowId) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_TAB_MOVED,
tabId,
frameOpts,
@@ -116,7 +116,7 @@ const appActions = {
* @param {Object} createProperties
*/
createTabRequested: function (createProperties, activateIfOpen = false, isRestore = false) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_CREATE_TAB_REQUESTED,
createProperties,
activateIfOpen,
@@ -130,7 +130,7 @@ const appActions = {
* @param {string} url - The url to load
*/
loadURLRequested: function (tabId, url) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_LOAD_URL_REQUESTED,
tabId,
url
@@ -143,7 +143,7 @@ const appActions = {
* @param {string} url - The url to load
*/
loadURLInActiveTabRequested: function (windowId, url) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_LOAD_URL_IN_ACTIVE_TAB_REQUESTED,
windowId,
url
@@ -156,7 +156,7 @@ const appActions = {
* @param {string} shareType - The type of share to do, must be one of: "email", "facebook", "pinterest", "twitter", "googlePlus", "linkedIn", "buffer", "reddit", or "digg"
*/
simpleShareActiveTabRequested: function (windowId, shareType) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_SIMPLE_SHARE_ACTIVE_TAB_REQUESTED,
windowId,
shareType
@@ -169,7 +169,7 @@ const appActions = {
* @param {Object} changeInfo from chrome-tabs-updated
*/
tabUpdated: function (tabValue, changeInfo) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_TAB_UPDATED,
tabValue,
changeInfo,
@@ -185,7 +185,7 @@ const appActions = {
* @param {Number} tabId - the tabId to activate
*/
tabActivateRequested: function (tabId) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_TAB_ACTIVATE_REQUESTED,
tabId
})
@@ -198,7 +198,7 @@ const appActions = {
* @param {Number} index - the new index
*/
tabIndexChanged: function (tabId, index) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_TAB_INDEX_CHANGED,
tabId,
index
@@ -212,7 +212,7 @@ const appActions = {
* @param {Boolean} forceClosePinned - force close if pinned
*/
tabCloseRequested: function (tabId, forceClosePinned = false) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_TAB_CLOSE_REQUESTED,
tabId,
forceClosePinned
@@ -224,7 +224,7 @@ const appActions = {
* @param {number} tabId
*/
tabClosed: function (tabId, windowId) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_TAB_CLOSED,
tabId,
queryInfo: {
@@ -243,7 +243,7 @@ const appActions = {
* @param {boolean} skipSync - Set true if a site isn't eligible for Sync (e.g. if addSite was triggered by Sync)
*/
addSite: function (siteDetail, tag, originalSiteDetail, destinationDetail, skipSync) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_ADD_SITE,
siteDetail,
tag,
@@ -260,7 +260,7 @@ const appActions = {
* @param {boolean} skipSync - Set true if a site isn't eligible for Sync (e.g. if this removal was triggered by Sync)
*/
removeSite: function (siteDetail, tag, skipSync) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_REMOVE_SITE,
siteDetail,
tag,
@@ -278,7 +278,7 @@ const appActions = {
* @param {boolean} destinationIsParent - Whether or not the destinationDetail should be considered the new parent.
*/
moveSite: function (sourceKey, destinationKey, prepend, destinationIsParent) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_MOVE_SITE,
sourceKey,
destinationKey,
@@ -294,7 +294,7 @@ const appActions = {
* @param {Object} downloadDetail - Properties for the download
*/
mergeDownloadDetail: function (downloadId, downloadDetail) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_MERGE_DOWNLOAD_DETAIL,
downloadId,
downloadDetail
@@ -305,7 +305,7 @@ const appActions = {
* Dispatches a message to clear all completed downloads
*/
clearCompletedDownloads: function () {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_CLEAR_COMPLETED_DOWNLOADS
})
},
@@ -314,7 +314,7 @@ const appActions = {
* Dispatches a message indicating ledger recovery succeeded
*/
ledgerRecoverySucceeded: function () {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_LEDGER_RECOVERY_STATUS_CHANGED,
recoverySucceeded: true
})
@@ -324,7 +324,7 @@ const appActions = {
* Dispatches a message indicating ledger recovery failed
*/
ledgerRecoveryFailed: function () {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_LEDGER_RECOVERY_STATUS_CHANGED,
recoverySucceeded: false
})
@@ -336,7 +336,7 @@ const appActions = {
* @param {Array} position - [x, y]
*/
defaultWindowParamsChanged: function (size, position) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_DEFAULT_WINDOW_PARAMS_CHANGED,
size,
position
@@ -351,7 +351,7 @@ const appActions = {
* @param {string} etag - The etag of the reosurce from the http response
*/
setResourceETag: function (resourceName, etag) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_SET_DATA_FILE_ETAG,
resourceName,
etag
@@ -364,7 +364,7 @@ const appActions = {
* @param {number} lastCheck - The last check date of the reosurce from the http response
*/
setResourceLastCheck: function (resourceName, lastCheckVersion, lastCheckDate) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_SET_DATA_FILE_LAST_CHECK,
resourceName,
lastCheckVersion,
@@ -378,7 +378,7 @@ const appActions = {
* @param {boolean} enabled - true if the resource is enabled.
*/
setResourceEnabled: function (resourceName, enabled) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_SET_RESOURCE_ENABLED,
resourceName,
enabled
@@ -390,7 +390,7 @@ const appActions = {
* @param {string} resourceName - 'widevine'
*/
resourceReady: function (resourceName) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_RESOURCE_READY,
resourceName
})
@@ -402,7 +402,7 @@ const appActions = {
* @param {number} count - number of blocked resources to add to the global count
*/
addResourceCount: function (resourceName, count) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_ADD_RESOURCE_COUNT,
resourceName,
count
@@ -414,7 +414,7 @@ const appActions = {
* epoch timestamp (milliseconds)
*/
setUpdateLastCheck: function () {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_UPDATE_LAST_CHECK
})
},
@@ -426,7 +426,7 @@ const appActions = {
* @param {object} metadata - Metadata from the pdate server, with info like release notes.
*/
setUpdateStatus: function (status, verbose, metadata) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_SET_UPDATE_STATUS,
status,
verbose,
@@ -440,7 +440,7 @@ const appActions = {
* @param {string} value - The value of the setting
*/
changeSetting: function (key, value) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_CHANGE_SETTING,
key,
value
@@ -457,7 +457,7 @@ const appActions = {
* @param {boolean} skipSync - Set true if a site isn't eligible for Sync (e.g. if addSite was triggered by Sync)
*/
changeSiteSetting: function (hostPattern, key, value, temp, skipSync) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_CHANGE_SITE_SETTING,
hostPattern,
key,
@@ -476,7 +476,7 @@ const appActions = {
* @param {boolean} skipSync - Set true if a site isn't eligible for Sync (e.g. if addSite was triggered by Sync)
*/
removeSiteSetting: function (hostPattern, key, temp, skipSync) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_REMOVE_SITE_SETTING,
hostPattern,
key,
@@ -490,7 +490,7 @@ const appActions = {
* @param {object} ledgerInfo - the current ledger state
*/
updateLedgerInfo: function (ledgerInfo) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_UPDATE_LEDGER_INFO,
ledgerInfo
})
@@ -501,7 +501,7 @@ const appActions = {
* @param {object} locationInfo - the current location synopsis
*/
updateLocationInfo: function (locationInfo) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_UPDATE_LOCATION_INFO,
locationInfo
})
@@ -512,7 +512,7 @@ const appActions = {
* @param {object} publisherInfo - the current publisher synopsis
*/
updatePublisherInfo: function (publisherInfo) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_UPDATE_PUBLISHER_INFO,
publisherInfo
})
@@ -523,7 +523,7 @@ const appActions = {
* @param {{message: string, buttons: Array., frameOrigin: string, options: Object}} detail
*/
showNotification: function (detail) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_SHOW_NOTIFICATION,
detail
})
@@ -534,7 +534,7 @@ const appActions = {
* @param {string} message
*/
hideNotification: function (message) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_HIDE_NOTIFICATION,
message
})
@@ -546,7 +546,7 @@ const appActions = {
* @param {boolean} learn - true if the word should be learned, false if ignored
*/
addWord: function (word, learn) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_ADD_WORD,
word,
learn
@@ -558,7 +558,7 @@ const appActions = {
* @param {string} locale - The locale to set for the dictionary
*/
setDictionary: function (locale) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_SET_DICTIONARY,
locale
})
@@ -570,7 +570,7 @@ const appActions = {
* @param {string} detail - login request info
*/
setLoginRequiredDetail: function (tabId, detail) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_SET_LOGIN_REQUIRED_DETAIL,
tabId,
detail
@@ -578,7 +578,7 @@ const appActions = {
},
setLoginResponseDetail: function (tabId, detail) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_SET_LOGIN_RESPONSE_DETAIL,
tabId,
detail
@@ -590,7 +590,7 @@ const appActions = {
* @param {object} clearDataDetail - the app data to clear as per doc/state.md's clearBrowsingDataDefaults
*/
onClearBrowsingData: function (clearDataDetail) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_ON_CLEAR_BROWSING_DATA,
clearDataDetail
})
@@ -601,7 +601,7 @@ const appActions = {
* @param {object} selected - the browser data to import as per doc/state.md's importBrowserDataSelected
*/
importBrowserData: function (selected) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_IMPORT_BROWSER_DATA,
selected
})
@@ -613,7 +613,7 @@ const appActions = {
* @param {object} originalDetail - the original address before editing
*/
addAutofillAddress: function (detail, originalDetail) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_ADD_AUTOFILL_ADDRESS,
detail,
originalDetail
@@ -625,7 +625,7 @@ const appActions = {
* @param {object} detail - the address to remove as per doc/state.md's autofillAddressDetail
*/
removeAutofillAddress: function (detail) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_REMOVE_AUTOFILL_ADDRESS,
detail
})
@@ -637,7 +637,7 @@ const appActions = {
* @param {object} originalDetail - the original credit card before editing
*/
addAutofillCreditCard: function (detail, originalDetail) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_ADD_AUTOFILL_CREDIT_CARD,
detail,
originalDetail
@@ -649,7 +649,7 @@ const appActions = {
* @param {object} detail - the credit card to remove as per doc/state.md's autofillCreditCardDetail
*/
removeAutofillCreditCard: function (detail) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_REMOVE_AUTOFILL_CREDIT_CARD,
detail
})
@@ -661,7 +661,7 @@ const appActions = {
* @param {Array} creditCardGuids - the guid array to access credit card entries in autofill DB
*/
autofillDataChanged: function (addressGuids, creditCardGuids) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_AUTOFILL_DATA_CHANGED,
addressGuids,
creditCardGuids
@@ -674,7 +674,7 @@ const appActions = {
* @param {Number} windowId - the unique id of the window
*/
windowBlurred: function (windowId) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_WINDOW_BLURRED,
windowId: windowId
})
@@ -686,7 +686,7 @@ const appActions = {
* @param {Number} windowId - the unique id of the window
*/
windowFocused: function (windowId) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_WINDOW_FOCUSED,
windowId: windowId
})
@@ -697,7 +697,7 @@ const appActions = {
* @param {Object} menubarTemplate - JSON used to build the menu
*/
setMenubarTemplate: function (menubarTemplate) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_SET_MENUBAR_TEMPLATE,
menubarTemplate
})
@@ -708,7 +708,7 @@ const appActions = {
* after being disconnected
*/
networkConnected: function () {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_NETWORK_CONNECTED
})
},
@@ -717,7 +717,7 @@ const appActions = {
* Dispatches a message when the network is disconnected
*/
networkDisconnected: function () {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_NETWORK_DISCONNECTED
})
},
@@ -728,7 +728,7 @@ const appActions = {
* @param {boolean} useBrave - whether set Brave as default browser
*/
defaultBrowserUpdated: function (useBrave) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_DEFAULT_BROWSER_UPDATED,
useBrave
})
@@ -738,7 +738,7 @@ const appActions = {
* Dispatch a message to indicate default browser check is complete
*/
defaultBrowserCheckComplete: function () {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_DEFAULT_BROWSER_CHECK_COMPLETE
})
},
@@ -747,13 +747,13 @@ const appActions = {
* Notify the AppStore to provide default history values.
*/
populateHistory: function () {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_POPULATE_HISTORY
})
},
allowFlashOnce: function (tabId, url, isPrivate) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_ALLOW_FLASH_ONCE,
tabId,
url,
@@ -762,7 +762,7 @@ const appActions = {
},
allowFlashAlways: function (tabId, url) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_ALLOW_FLASH_ALWAYS,
tabId,
url
@@ -773,7 +773,7 @@ const appActions = {
* Dispatch a message to copy data URL to clipboard
**/
dataURLCopied: function (dataURL, html, text) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_DATA_URL_COPIED,
dataURL,
html,
@@ -785,7 +785,7 @@ const appActions = {
* Dispatches a message when the app is shutting down.
*/
shuttingDown: function () {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_SHUTTING_DOWN
})
},
@@ -796,7 +796,7 @@ const appActions = {
* @param {string} downloadId - ID of the download being revealed
*/
downloadRevealed: function (downloadId) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_DOWNLOAD_REVEALED,
downloadId
})
@@ -807,7 +807,7 @@ const appActions = {
* @param {string} downloadId - ID of the download being opened
*/
downloadOpened: function (downloadId) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_DOWNLOAD_OPENED,
downloadId
})
@@ -819,7 +819,7 @@ const appActions = {
* @param {string} downloadAction - the action to perform from constants/electronDownloadItemActions.js
*/
downloadActionPerformed: function (downloadId, downloadAction) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_DOWNLOAD_ACTION_PERFORMED,
downloadId,
downloadAction
@@ -831,7 +831,7 @@ const appActions = {
* @param {string} downloadId - ID of the download item being copied to the clipboard
*/
downloadCopiedToClipboard: function (downloadId) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_DOWNLOAD_COPIED_TO_CLIPBOARD,
downloadId
})
@@ -842,7 +842,7 @@ const appActions = {
* @param {string} downloadId - ID of the download item being deleted
*/
downloadDeleted: function (downloadId) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_DOWNLOAD_DELETED,
downloadId
})
@@ -853,7 +853,7 @@ const appActions = {
* @param {string} downloadId - ID of the download item being cleared
*/
downloadCleared: function (downloadId) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_DOWNLOAD_CLEARED,
downloadId
})
@@ -864,7 +864,7 @@ const appActions = {
* @param {string} downloadId - ID of the download item being redownloaded
*/
downloadRedownloaded: function (downloadId) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_DOWNLOAD_REDOWNLOADED,
downloadId
})
@@ -874,7 +874,7 @@ const appActions = {
* Shows delete confirmation bar in download item panel
*/
showDownloadDeleteConfirmation: function () {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_SHOW_DOWNLOAD_DELETE_CONFIRMATION
})
},
@@ -883,7 +883,7 @@ const appActions = {
* Hides delete confirmation bar in download item panel
*/
hideDownloadDeleteConfirmation: function () {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_HIDE_DOWNLOAD_DELETE_CONFIRMATION
})
},
@@ -893,7 +893,7 @@ const appActions = {
* @param {string} text - clipboard text which is copied
*/
clipboardTextCopied: function (text) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_CLIPBOARD_TEXT_UPDATED,
text
})
@@ -904,7 +904,7 @@ const appActions = {
* @param {number} tabId - The tabId
*/
toggleDevTools: function (tabId) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_TAB_TOGGLE_DEV_TOOLS,
tabId
})
@@ -916,7 +916,7 @@ const appActions = {
* @param {object} options - object containing options such as acive, back, and forward booleans
*/
tabCloned: function (tabId, options) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_TAB_CLONED,
tabId,
options
@@ -930,7 +930,7 @@ const appActions = {
* @param {boolean} temporary
*/
noScriptExceptionsAdded: function (hostPattern, origins, temporary) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_ADD_NOSCRIPT_EXCEPTIONS,
hostPattern,
origins,
@@ -944,7 +944,7 @@ const appActions = {
* @param {Array.} objectPath
*/
setObjectId: function (objectId, objectPath) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_SET_OBJECT_ID,
objectId,
objectPath
@@ -958,7 +958,7 @@ const appActions = {
* @param {Object} devices {[deviceId]: {lastRecordTimestamp=, name=}}
*/
saveSyncDevices: function (devices) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_SAVE_SYNC_DEVICES,
devices
})
@@ -972,7 +972,7 @@ const appActions = {
* @param {string=} seedQr
*/
saveSyncInitData: function (seed, deviceId, lastFetchTimestamp, seedQr) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_SAVE_SYNC_INIT_DATA,
seed,
deviceId,
@@ -986,7 +986,7 @@ const appActions = {
* @param {string|null} error
*/
setSyncSetupError: function (error) {
- AppDispatcher.dispatch({
+ dispatch({
actionType: appConstants.APP_SET_SYNC_SETUP_ERROR,
error
})
@@ -998,7 +998,7 @@ const appActions = {
* @param {Array.