diff --git a/.npmrc b/.npmrc index c9565661da..e949adde62 100644 --- a/.npmrc +++ b/.npmrc @@ -1,7 +1,7 @@ runtime = electron target_arch = x64 -brave_electron_version = 3.0.100 +brave_electron_version = 3.0.102 chromedriver_version = 2.27 -target = v3.0.1 +target = v3.0.102 disturl=https://brave-laptop-binaries.s3.amazonaws.com/atom-shell/dist/ build_from_source = true diff --git a/app/browser/menu.js b/app/browser/menu.js index 91d93489df..431970e681 100644 --- a/app/browser/menu.js +++ b/app/browser/menu.js @@ -261,7 +261,7 @@ const createViewSubmenu = () => { accelerator: isDarwin ? 'Cmd+Alt+I' : 'Ctrl+Shift+I', click: function (item) { const win = BrowserWindow.getActiveWindow() - const activeTab = tabState.getActiveTabValue(appStore.getState(), win.id) + const activeTab = tabState.getActiveTab(appStore.getState(), win.id) appActions.toggleDevTools(activeTab.get('tabId')) } }, diff --git a/app/browser/reducers/tabsReducer.js b/app/browser/reducers/tabsReducer.js index 03d2318412..b4b8682ce2 100644 --- a/app/browser/reducers/tabsReducer.js +++ b/app/browser/reducers/tabsReducer.js @@ -127,6 +127,11 @@ const tabsReducer = (state, action, immutableAction) => { tabs.setActive(action.get('tabId')) }) break + case appConstants.APP_TAB_INDEX_CHANGED: + setImmediate(() => { + tabs.setTabIndex(action.get('tabId'), action.get('index')) + }) + break case appConstants.APP_TAB_TOGGLE_DEV_TOOLS: setImmediate(() => { tabs.toggleDevTools(action.get('tabId')) diff --git a/app/browser/share.js b/app/browser/share.js index ac04bcd68a..ff25a2482a 100644 --- a/app/browser/share.js +++ b/app/browser/share.js @@ -29,7 +29,7 @@ const validateShareType = (shareType) => * @param {string} shareType - The template to use, see the property key names in templateUrls above. */ const simpleShareActiveTab = (state, windowId, shareType) => { - const tabValue = tabState.getActiveTabValue(state, windowId) + const tabValue = tabState.getActiveTab(state, windowId) const encodedTitle = encodeURIComponent(tabValue.get('title') || '') const encodedUrl = encodeURIComponent(tabValue.get('url')) diff --git a/app/browser/tabs.js b/app/browser/tabs.js index 74e1b4f46a..ca7f77bd46 100644 --- a/app/browser/tabs.js +++ b/app/browser/tabs.js @@ -3,6 +3,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ const appActions = require('../../js/actions/appActions') +const windowActions = require('../../js/actions/windowActions') const config = require('../../js/constants/config') const Immutable = require('immutable') const tabState = require('../common/state/tabState') @@ -363,6 +364,12 @@ const api = { appActions.updatePassword(username, origin, tabId) }) + tab.on('did-get-response-details', (evt, status, newURL, originalURL, httpResponseCode, requestMethod, referrer, headers, resourceType) => { + if (resourceType === 'mainFrame') { + windowActions.gotResponseDetails(tabId, {status, newURL, originalURL, httpResponseCode, requestMethod, referrer, headers, resourceType}) + } + }) + updateWebContents(tabId, tab) let tabValue = getTabValue(tabId) @@ -413,12 +420,17 @@ const api = { }, setActive: (tabId) => { - setImmediate(() => { - let tab = getWebContents(tabId) - if (tab && !tab.isDestroyed()) { - tab.setActive(true) - } - }) + let tab = getWebContents(tabId) + if (tab && !tab.isDestroyed()) { + tab.setActive(true) + } + }, + + setTabIndex: (tabId, index) => { + let tab = getWebContents(tabId) + if (tab && !tab.isDestroyed()) { + tab.setTabIndex(index) + } }, loadURL: (action) => { @@ -443,7 +455,7 @@ const api = { }, loadURLInActiveTab: (state, windowId, url) => { - const tabValue = tabState.getActiveTabValue(state, windowId) + const tabValue = tabState.getActiveTab(state, windowId) if (tabValue) { api.loadURLInTab(state, tabValue.get('tabId'), url) } @@ -611,7 +623,7 @@ const api = { if (windowId == null || windowId === -1) { appActions.newWindow(makeImmutable(frameOpts), browserOpts) } else { - appActions.newWebContentsAdded(windowId, frameOpts) + appActions.newWebContentsAdded(windowId, frameOpts, tabValue) } }) } diff --git a/app/common/state/tabState.js b/app/common/state/tabState.js index 708817a499..802aa5843b 100644 --- a/app/common/state/tabState.js +++ b/app/common/state/tabState.js @@ -3,6 +3,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ const { makeImmutable, isMap, isList } = require('./immutableUtil') +const Immutable = require('immutable') const assert = require('assert') const frameState = require('./frameState') const windowState = require('./windowState') @@ -55,6 +56,36 @@ const matchTab = function (queryInfo, tab) { return !Object.keys(queryInfo).map((queryKey) => (tab.get(queryKey) === queryInfo[queryKey])).includes(false) } +const deleteTabsInternalIndex = (state, tabValue) => { + const tabId = validateId('tabId', tabValue.get('tabId')) + if (tabId === tabState.TAB_ID_NONE) { + return state + } + state = state.deleteIn(['tabsInternal', tabId]) + return state.deleteIn(['tabsInternal', tabId.toString()]) +} + +const updateTabsInternalIndex = (state, fromIndex) => { + let tabsInternal = state.get('tabsInternal') || Immutable.Map() + state.get('tabs').slice(fromIndex).forEach((tab, idx) => { + if (tab.get('tabId') !== tabState.TAB_ID_NONE) { + tabsInternal = tabsInternal.setIn(['index', tab.get('tabId')], idx + fromIndex) + } + }) + return state.set('tabsInternal', tabsInternal) +} + +const getTabInternalIndexByTabId = (state, tabId) => { + tabId = validateId('tabId', tabId) + state = validateState(state) + + let index = state.getIn(['tabsInternal', 'index', tabId]) + if (index == null) { + index = state.getIn(['tabsInternal', 'index', tabId.toString()]) + } + return index == null ? -1 : index +} + const tabState = { queryTab: (state, queryInfo) => { state = validateState(state) @@ -68,32 +99,12 @@ const tabState = { validateId('tabId', tabId) }, - getTabIndex: (state, tabValue) => { - state = validateState(state) - tabValue = validateTabValue(tabValue) - let tabId = validateId('tabId', tabValue.get('tabId')) - return tabState.getTabIndexByTabId(state, tabId) - }, - - getTabIndexByTabId: (state, tabId) => { - tabId = validateId('tabId', tabId) - state = validateState(state) - - return state.get('tabs').findIndex((tab) => tab.get('tabId') === tabId) - }, - - getActiveTabValue: (state, windowId) => { - windowId = validateId('windowId', windowId) - state = validateState(state) - return state.get('tabs').find((tab) => tab.get('windowId') === windowId && tab.get('active')) - }, - removeTabByTabId: (state, tabId) => { tabId = validateId('tabId', tabId) state = validateState(state) - let index = tabState.getTabIndexByTabId(state, tabId) - if (index === -1) { + let index = getTabInternalIndexByTabId(state, tabId) + if (index === tabState.TAB_ID_NONE) { return state } return tabState.removeTabByIndex(state, index) @@ -103,7 +114,13 @@ const tabState = { index = parseInt(index) assert.ok(index >= 0, 'index must be positive') state = validateState(state) - return state.set('tabs', state.get('tabs').delete(index)) + const tabValue = state.getIn(['tabs', index]) + if (!tabValue) { + return state + } + state = deleteTabsInternalIndex(state, tabValue) + state = state.set('tabs', state.get('tabs').delete(index)) + return updateTabsInternalIndex(state, index) }, removeTab: (state, tabValue) => { @@ -119,7 +136,8 @@ const tabState = { state = validateState(state) let tabValue = validateTabValue(action.get('tabValue')) assert.ok(!tabState.getTab(state, tabValue), 'Tab already exists') - return state.set('tabs', state.get('tabs').push(tabValue)) + state = state.set('tabs', state.get('tabs').push(tabValue)) + return updateTabsInternalIndex(state, state.get('tabs').size - 1) }, maybeCreateTab: (state, action) => { @@ -171,7 +189,7 @@ const tabState = { tabId = tabState.resolveTabId(state, tabId) state = validateState(state) - const index = state.get('tabs').findIndex((tab) => tab.get('tabId') === tabId) + const index = getTabInternalIndexByTabId(state, tabId) if (index === tabState.TAB_ID_NONE) { return null } @@ -217,8 +235,8 @@ const tabState = { updateTabValue: (state, tabValue, replace = false) => { tabValue = validateTabValue(tabValue) const tabs = state.get('tabs') - const index = tabState.getTabIndex(state, tabValue) - if (index === -1) { + const index = getTabInternalIndexByTabId(state, tabValue.get('tabId')) + if (index === tabState.TAB_ID_NONE) { return state } @@ -269,43 +287,63 @@ const tabState = { return tabState.updateTabValue(state, tabValue) }, - getTabPropertyByTabId: (state, tabId, property) => { + getTabPropertyByTabId: (state, tabId, property, defaultValue = null) => { state = validateState(state) tabId = validateId('tabId', tabId) + if (tabId === tabState.TAB_ID_NONE) { + return defaultValue + } const tab = tabState.getByTabId(state, tabId) assert.ok(tab, `Could not find tab for ${tabId}`) - return tab.get(property) + const val = tab.get(property) + return val == null ? defaultValue : val }, windowId: (state, tabId) => { - return tabState.getTabPropertyByTabId(state, tabId, 'windowId') || windowState.WINDOW_ID_NONE + return tabState.getTabPropertyByTabId(state, tabId, 'windowId', windowState.WINDOW_ID_NONE) }, canGoForward: (state, tabId) => { - return tabState.getTabPropertyByTabId(state, tabId, 'canGoForward') || false + try { + return tabState.getTabPropertyByTabId(state, tabId, 'canGoForward', false) + } catch (e) { + return false + } }, canGoBack: (state, tabId) => { - return tabState.getTabPropertyByTabId(state, tabId, 'canGoBack') || false + try { + return tabState.getTabPropertyByTabId(state, tabId, 'canGoBack', false) + } catch (e) { + return false + } }, isShowingMessageBox: (state, tabId) => { - if (tabId === tabState.TAB_ID_NONE) { + try { + return tabState.getTabPropertyByTabId(state, tabId, 'messageBoxDetail', false) + } catch (e) { return false } - return tabState.getTabPropertyByTabId(state, tabId, 'messageBoxDetail') || false + }, + + isPinned: (state, tabId) => { + return tabState.getTabPropertyByTabId(state, tabId, 'pinned', false) }, getTitle: (state, tabId) => { - return tabState.getTabPropertyByTabId(state, tabId, 'title') || '' + return tabState.getTabPropertyByTabId(state, tabId, 'title', '') }, isActive: (state, tabId) => { - return tabState.getTabPropertyByTabId(state, tabId, 'active') || false + return tabState.getTabPropertyByTabId(state, tabId, 'active', false) }, // TOOD(bridiver) - make everything work with TAB_ID_ACTIVE resolveTabId: (state, tabId) => { + if (tabId == null) { + tabId = tabState.TAB_ID_NONE + } if (tabId === tabState.TAB_ID_ACTIVE) { tabId = tabState.getActiveTabId(state) } @@ -385,6 +423,7 @@ const tabState = { state = tabState.removeTabField(state, 'messageBoxDetail') state = tabState.removeTabField(state, 'frame') + state = state.delete('tabsInternal') return state.delete('tabs') } } diff --git a/app/renderer/components/navigation/urlBar.js b/app/renderer/components/navigation/urlBar.js index cdaf97b764..3541c8fa46 100644 --- a/app/renderer/components/navigation/urlBar.js +++ b/app/renderer/components/navigation/urlBar.js @@ -317,19 +317,19 @@ class UrlBar extends React.Component { } select () { - if (this.urlInput) { - setImmediate(() => { + setImmediate(() => { + if (this.urlInput) { this.urlInput.select() - }) - } + } + }) } focus () { - if (this.urlInput) { - setImmediate(() => { + setImmediate(() => { + if (this.urlInput) { this.urlInput.focus() - }) - } + } + }) } onFocus (e) { diff --git a/app/renderer/components/reduxComponent.js b/app/renderer/components/reduxComponent.js index 538570fbed..c20f516919 100644 --- a/app/renderer/components/reduxComponent.js +++ b/app/renderer/components/reduxComponent.js @@ -32,6 +32,7 @@ class ReduxComponent extends ImmutableComponent { } componentDidMount () { + this.dontCheck = false appStore.addChangeListener(this.checkForUpdates) windowStore.addChangeListener(this.checkForUpdates) } diff --git a/app/renderer/lib/tabUtil.js b/app/renderer/lib/tabUtil.js index 6116869b13..b32191c150 100644 --- a/app/renderer/lib/tabUtil.js +++ b/app/renderer/lib/tabUtil.js @@ -103,13 +103,7 @@ module.exports.getTabIconColor = (props) => { * @param frameProps Any frame belonging to the page */ module.exports.updateTabPageIndex = (state, frameProps) => { - // No need to update tab page index if we are given a pinned frame - if (!frameProps || frameProps.get('pinnedLocation')) { - return state - } - - const index = frameStateUtil.getFrameTabPageIndex(state.get('frames') - .filter((frame) => !frame.get('pinnedLocation')), frameProps, getSetting(settings.TABS_PER_PAGE)) + const index = frameStateUtil.getFrameTabPageIndex(state, frameProps, getSetting(settings.TABS_PER_PAGE)) if (index === -1) { return state diff --git a/app/renderer/reducers/frameReducer.js b/app/renderer/reducers/frameReducer.js index 23789485aa..6392a1345d 100644 --- a/app/renderer/reducers/frameReducer.js +++ b/app/renderer/reducers/frameReducer.js @@ -5,6 +5,7 @@ 'use strict' const Immutable = require('immutable') +const appConstants = require('../../../js/constants/appConstants') const windowConstants = require('../../../js/constants/windowConstants') const frameStateUtil = require('../../../js/state/frameStateUtil') const appActions = require('../../../js/actions/appActions') @@ -16,29 +17,32 @@ const {updateTabPageIndex} = require('../lib/tabUtil') const {getCurrentWindowId} = require('../currentWindow') const setFullScreen = (state, action) => { - return state.mergeIn(['frames', frameStateUtil.getFramePropsIndex(state.get('frames'), action.frameProps)], { - isFullScreen: action.isFullScreen !== undefined ? action.isFullScreen : state.getIn(['frames', frameStateUtil.getFramePropsIndex(state.get('frames'), action.frameProps)].concat('isFullScreen')), + const index = frameStateUtil.getFrameIndex(state, action.frameProps.get('key')) + return state.mergeIn(['frames', index], { + isFullScreen: action.isFullScreen !== undefined ? action.isFullScreen : state.getIn(['frames', index].concat('isFullScreen')), showFullScreenWarning: action.showFullScreenWarning }) } const closeFrame = (state, action) => { // Use the frameProps we passed in, or default to the active frame - const frameProps = frameStateUtil.getFrameByKey(state, action.frameKey) - const index = frameStateUtil.getFramePropsIndex(state.get('frames'), frameProps) + const frameProps = action.frameProps + const index = frameStateUtil.getFrameIndex(state, frameProps.get('key')) if (index === -1) { return state } const hoverState = state.getIn(['frames', index, 'hoverState']) const activeFrameKey = frameStateUtil.getActiveFrame(state).get('key') state = state.merge(frameStateUtil.removeFrame( - state.get('frames'), - state.get('closedFrames'), + state, frameProps.set('closedAtIndex', index), activeFrameKey, index, getSetting(settings.TAB_CLOSE_ACTION) )) + state = frameStateUtil.deleteFrameInternalIndex(state, frameProps) + state = frameStateUtil.updateFramesInternalIndex(state, index) + // If we reach the limit of opened tabs per page while closing tabs, switch to // the active tab's page otherwise the user will hang on empty page if (frameStateUtil.getNonPinnedFrameCount(state) % getSetting(settings.TABS_PER_PAGE) === 0) { @@ -57,8 +61,55 @@ const closeFrame = (state, action) => { return state } -const frameReducer = (state, action) => { +const frameReducer = (state, action, immutableAction) => { switch (action.actionType) { + case appConstants.APP_TAB_UPDATED: + const tab = immutableAction.get('tabValue') + if (!tab) { + break + } + const tabId = tab.get('tabId') + const frame = frameStateUtil.getFrameByTabId(state, tabId) + if (!frame) { + break + } + + const index = frameStateUtil.getIndexByTabId(state, tabId) + const pinned = immutableAction.getIn(['changeInfo', 'pinned']) + if (pinned != null) { + if (pinned) { + state = state.setIn(['frames', index, 'pinnedLocation'], tab.get('url')) + } else { + state = state.deleteIn(['frames', index, 'pinnedLocation']) + } + } + // handle pinned tabs that are created as pinned + const url = immutableAction.getIn(['changeInfo', 'url']) + if (url != null && tab.get('pinned') === true) { + const pinnedLocation = state.getIn(['frames', index, 'pinnedLocation']) + if (!pinnedLocation || pinnedLocation === 'about:blank' || pinnedLocation === '') { + state = state.setIn(['frames', index, 'pinnedLocation'], tab.get('url')) + } + } + + const title = immutableAction.getIn(['changeInfo', 'title']) + if (title != null) { + state = state.setIn(['frames', index, 'title'], title) + } + + const active = immutableAction.getIn(['changeInfo', 'active']) + if (active != null) { + if (active) { + state = state.merge({ + activeFrameKey: frame.get('key'), + previewFrameKey: null + }) + state = state.setIn(['frames', index, 'lastAccessedTime'], new Date().getTime()) + state = state.deleteIn(['ui', 'tabs', 'previewTabPageIndex']) + state = updateTabPageIndex(state, frame) + } + } + break case windowConstants.WINDOW_CLOSE_FRAMES: let closedFrames = new Immutable.List() action.framePropsList.forEach((frameProps) => { @@ -80,9 +131,8 @@ const frameReducer = (state, action) => { } // Unless a caller explicitly specifies to close a pinned frame, then // ignore the call. - const frames = frameStateUtil.getFrames(state) - const nonPinnedFrames = frames.filter((frame) => !frame.get('pinnedLocation')) - const pinnedFrames = frames.filter((frame) => frame.get('pinnedLocation')) + const nonPinnedFrames = frameStateUtil.getNonPinnedFrames(state) + const pinnedFrames = frameStateUtil.getPinnedFrames(state) // If there is at least 1 pinned frame don't close the window until subsequent // close attempts if (nonPinnedFrames.size > 1 || pinnedFrames.size > 0) { diff --git a/app/renderer/reducers/urlBarReducer.js b/app/renderer/reducers/urlBarReducer.js index 1b08ebcae2..1d5103d6cb 100644 --- a/app/renderer/reducers/urlBarReducer.js +++ b/app/renderer/reducers/urlBarReducer.js @@ -7,7 +7,7 @@ const windowConstants = require('../../../js/constants/windowConstants') const {aboutUrls, isNavigatableAboutPage, isSourceAboutUrl, isUrl, getSourceAboutUrl, getSourceMagnetUrl} = require('../../../js/lib/appUrlUtil') const {isURL, isPotentialPhishingUrl, getUrlFromInput} = require('../../../js/lib/urlutil') -const {getFrameByKey, getFrameKeyByTabId, activeFrameStatePath, frameStatePath, frameStatePathForFrame, getActiveFrame, getFrameByTabId} = require('../../../js/state/frameStateUtil') +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') @@ -348,10 +348,10 @@ const navigationAborted = (state, action) => { if (frame) { let location = action.location || frame.get('provisionalLocation') if (location) { - const frameStatePath = frameStatePathForFrame(state, frame) + const framePath = frameStatePath(state, frame.get('key')) location = getLocation(location) - state = updateNavBarInput(state, location, frameStatePath) - state = state.mergeIn(frameStatePath, { + state = updateNavBarInput(state, location, framePath) + state = state.mergeIn(framePath, { location }) } diff --git a/app/sessionStore.js b/app/sessionStore.js index fd2e3ac089..71f6f8360a 100644 --- a/app/sessionStore.js +++ b/app/sessionStore.js @@ -111,6 +111,8 @@ module.exports.cleanPerWindowData = (perWindowData, isShutdown) => { if (!perWindowData) { perWindowData = {} } + // delete the frame index because tabId is per-session + delete perWindowData.framesInternal // Hide the context menu when we restore. delete perWindowData.contextMenuDetail // Don't save preview frame since they are only related to hovering on a tab diff --git a/docs/appActions.md b/docs/appActions.md index 8c833ff79b..fbe64eda39 100644 --- a/docs/appActions.md +++ b/docs/appActions.md @@ -137,6 +137,18 @@ Dispatches a message to the store to set a new frame as the active frame. +### tabIndexChanged(tabId, index) + +Dispatches a message to the store to change the tab index + +**Parameters** + +**tabId**: `Number`, the tabId + +**index**: `Number`, the new index + + + ### tabCloseRequested(tabId, forceClosePinned) Dispatches a message to close the tabId diff --git a/docs/windowActions.md b/docs/windowActions.md index 73bbec4489..b7dc3227e4 100644 --- a/docs/windowActions.md +++ b/docs/windowActions.md @@ -85,16 +85,6 @@ Dispatches a message when the guestInstanceId changes for a frame -### tabDataChanged(tabs) - -Dispatches a message when tab data changes - -**Parameters** - -**tabs**: `Object`, the tab properties - - - ### setFrameError(frameProps, errorDetails) Dispatches a message to set the frame error state diff --git a/js/actions/appActions.js b/js/actions/appActions.js index 2ce6494b80..702047dc02 100644 --- a/js/actions/appActions.js +++ b/js/actions/appActions.js @@ -172,7 +172,10 @@ const appActions = { AppDispatcher.dispatch({ actionType: appConstants.APP_TAB_UPDATED, tabValue, - changeInfo + changeInfo, + queryInfo: { + windowId: tabValue.get('windowId') + } }) }, @@ -188,6 +191,20 @@ const appActions = { }) }, + /** + * Dispatches a message to the store to change the tab index + * + * @param {Number} tabId - the tabId + * @param {Number} index - the new index + */ + tabIndexChanged: function (tabId, index) { + AppDispatcher.dispatch({ + actionType: appConstants.APP_TAB_INDEX_CHANGED, + tabId, + index + }) + }, + /** * Dispatches a message to close the tabId * diff --git a/js/actions/windowActions.js b/js/actions/windowActions.js index 3d1563ed6a..ad3413cad4 100644 --- a/js/actions/windowActions.js +++ b/js/actions/windowActions.js @@ -101,17 +101,6 @@ const windowActions = { }) }, - /** - * Dispatches a message when tab data changes - * @param {Object} tabs - the tab properties - */ - tabDataChanged: function (tabs) { - dispatch({ - actionType: windowConstants.WINDOW_TAB_DATA_CHANGED, - tabs - }) - }, - /** * Dispatches a message to set the frame error state * @param {Object} frameProps - The frame properties diff --git a/js/components/frame.js b/js/components/frame.js index 635e80b46a..169293f0c6 100644 --- a/js/components/frame.js +++ b/js/components/frame.js @@ -197,7 +197,7 @@ class Frame extends React.Component { if (!this.props.guestInstanceId || !this.webview.attachGuest(this.props.guestInstanceId)) { // The partition is guaranteed to be initialized by now by the browser process this.webview.setAttribute('partition', frameStateUtil.getPartition(this.frame)) - this.webview.setAttribute('src', this.frame.get('location')) + this.webview.setAttribute('src', this.props.location) } domUtil.appendChild(this.webviewContainer, this.webview) } else { @@ -206,9 +206,6 @@ class Frame extends React.Component { } onPropsChanged (prevProps = {}) { - if (this.props.tabIndex !== prevProps.tabIndex) { - this.webview.setTabIndex(this.props.tabIndex) - } if (this.props.isActive && isFocused()) { windowActions.setFocusedFrame(this.frame) } @@ -348,9 +345,9 @@ class Frame extends React.Component { if (sourceLocation !== null) { appActions.createTabRequested({ url: sourceLocation, - isPrivate: this.frame.get('isPrivate'), - partitionNumber: this.frame.get('partitionNumber'), - openerTabId: this.frame.get('tabId'), + isPrivate: this.props.isPrivate, + partitionNumber: this.props.partitionNumber, + openerTabId: this.props.tabId, active: true }) } @@ -486,7 +483,7 @@ class Frame extends React.Component { windowActions.setBlockedBy(this.frame, 'noScript', e.details[1]) } if (e.details[0] === 'autoplay') { - appActions.autoplayBlocked(this.frame.get('tabId')) + appActions.autoplayBlocked(this.props.tabId) } }) this.webview.addEventListener('did-block-run-insecure-content', (e) => { @@ -650,7 +647,7 @@ class Frame extends React.Component { if (!this.allowRunningWidevinePlugin()) { this.showWidevineNotification(this.props.location, this.origin, () => { }, () => { - appActions.loadURLRequested(this.frame.get('tabId'), this.props.provisionalLocation) + appActions.loadURLRequested(this.props.tabId, this.props.provisionalLocation) }) } @@ -705,13 +702,13 @@ class Frame extends React.Component { errorCode: e.errorCode, url: e.validatedURL }) - appActions.loadURLRequested(this.frame.get('tabId'), 'about:error') + appActions.loadURLRequested(this.props.tabId, 'about:error') appActions.removeSite(siteUtil.getDetailFromFrame(this.frame)) } else if (isAborted(e.errorCode)) { // just stay put - windowActions.navigationAborted(this.frame.get('tabId'), url) + windowActions.navigationAborted(this.props.tabId, url) } else if (provisionLoadFailure) { - windowActions.setNavigated(url, this.props.frameKey, true, this.frame.get('tabId')) + windowActions.setNavigated(url, this.props.frameKey, true, this.props.tabId) } } this.webview.addEventListener('security-style-changed', (e) => { @@ -759,7 +756,7 @@ class Frame extends React.Component { this.webview.focus() } if (!this.frame.isEmpty()) { - windowActions.setNavigated(e.url, this.props.frameKey, false, this.frame.get('tabId')) + windowActions.setNavigated(e.url, this.props.frameKey, false, this.props.tabId) } // force temporary url display for tabnapping protection windowActions.setMouseInTitlebar(true) @@ -773,7 +770,7 @@ class Frame extends React.Component { title: 'unexpectedError', url: this.props.location }) - appActions.loadURLRequested(this.frame.get('tabId'), 'about:error') + appActions.loadURLRequested(this.props.tabId, 'about:error') this.webview = false }) this.webview.addEventListener('did-fail-provisional-load', (e) => { @@ -799,7 +796,7 @@ class Frame extends React.Component { return } if (e.isMainFrame) { - windowActions.setNavigated(e.url, this.props.frameKey, true, this.frame.get('tabId')) + windowActions.setNavigated(e.url, this.props.frameKey, true, this.props.tabId) loadEnd(true, e.url) } }) @@ -857,12 +854,6 @@ class Frame extends React.Component { })) } }) - this.webview.addEventListener('did-get-response-details', (details) => { - if (this.frame.isEmpty()) { - return - } - windowActions.gotResponseDetails(this.frame.get('tabId'), details) - }) // Handle zoom using Ctrl/Cmd and the mouse wheel. this.webview.addEventListener('mousewheel', this.onMouseWheel.bind(this)) } @@ -874,7 +865,7 @@ class Frame extends React.Component { onFocus () { if (!this.frame.isEmpty()) { windowActions.setTabPageIndexByFrame(this.frame) - windowActions.tabOnFocus(this.frame.get('tabId')) + windowActions.tabOnFocus(this.props.tabId) } windowActions.setContextMenuDetail() @@ -922,6 +913,7 @@ class Frame extends React.Component { mergeProps (state, dispatchProps, ownProps) { const currentWindow = state.get('currentWindow') const frame = frameStateUtil.getFrameByKey(currentWindow, ownProps.frameKey) || Immutable.Map() + const location = frame.get('location') const allSiteSettings = siteSettingsState.getAllSiteSettings(state, frame.get('isPrivate')) const frameSiteSettings = frame.get('location') ? siteSettings.getSiteSettingsForURL(allSiteSettings, frame.get('location')) @@ -937,14 +929,13 @@ class Frame extends React.Component { props.isPreview = frame.get('key') === currentWindow.get('previewFrameKey') props.isActive = frameStateUtil.isFrameKeyActive(currentWindow, frame.get('key')) props.showFullScreenWarning = frame.get('showFullScreenWarning') - props.location = frame.get('location') + props.location = location props.hrefPreview = frame.get('hrefPreview') props.showOnRight = frame.get('showOnRight') props.tabId = tabId props.showMessageBox = tab && tab.get('messageBoxDetail') // used in other functions - props.tabIndex = frameStateUtil.getFrameIndex(currentWindow, frame.get('key')) props.urlBarFocused = frame && frame.getIn(['navbar', 'urlbar', 'focused']) props.isAutFillContextMenu = contextMenu && contextMenu.get('type') === 'autofill' props.isSecure = frame.getIn(['security', 'isSecure']) @@ -961,7 +952,6 @@ class Frame extends React.Component { props.shortcutDetailsOrigin = frame.getIn(['activeShortcutDetails', 'origin']) props.shortcutDetailsAction = frame.getIn(['activeShortcutDetails', 'action']) props.provisionalLocation = frame.get('provisionalLocation') - props.pinnedLocation = frame.get('pinnedLocation') props.src = frame.get('src') props.guestInstanceId = frame.get('guestInstanceId') props.aboutDetailsUrl = frame.getIn(['aboutDetails', 'url']) @@ -972,6 +962,7 @@ class Frame extends React.Component { props.siteZoomLevel = frameSiteSettings && frameSiteSettings.get('zoomLevel') props.allSiteSettings = allSiteSettings // TODO (nejc) can be improved even more props.tabUrl = tab && tab.get('url') + props.partitionNumber = frame.get('partitionNumber') return Object.assign({}, ownProps, props) } diff --git a/js/components/window.js b/js/components/window.js index 1315341f7f..e4c0748f2f 100644 --- a/js/components/window.js +++ b/js/components/window.js @@ -97,22 +97,26 @@ class Window extends React.Component { } onChange () { - this.windowState = windowStore.getState() - this.setState({ - immutableData: { - windowState: this.windowState, - appState: this.appState - } + setImmediate(() => { + this.windowState = windowStore.getState() + this.setState({ + immutableData: { + windowState: this.windowState, + appState: this.appState + } + }) }) } onAppStateChange () { - this.appState = appStoreRenderer.state - this.setState({ - immutableData: { - windowState: this.windowState, - appState: this.appState - } + setImmediate(() => { + this.appState = appStoreRenderer.state + this.setState({ + immutableData: { + windowState: this.windowState, + appState: this.appState + } + }) }) } } diff --git a/js/constants/appConstants.js b/js/constants/appConstants.js index 6983797ab3..d103b4b858 100644 --- a/js/constants/appConstants.js +++ b/js/constants/appConstants.js @@ -62,6 +62,7 @@ const appConstants = { APP_TAB_CREATED: _, APP_TAB_MOVED: _, APP_TAB_ACTIVATE_REQUESTED: _, + APP_TAB_INDEX_CHANGED: _, APP_TAB_CLOSE_REQUESTED: _, APP_CREATE_TAB_REQUESTED: _, APP_LOAD_URL_REQUESTED: _, diff --git a/js/constants/windowConstants.js b/js/constants/windowConstants.js index c7af80c32b..094c82bd44 100644 --- a/js/constants/windowConstants.js +++ b/js/constants/windowConstants.js @@ -33,7 +33,6 @@ const windowConstants = { WINDOW_SET_FRAME_ERROR: _, WINDOW_FRAME_TAB_ID_CHANGED: _, WINDOW_FRAME_GUEST_INSTANCE_ID_CHANGED: _, - WINDOW_TAB_DATA_CHANGED: _, WINDOW_SET_NAVIGATED: _, WINDOW_SET_NAVIGATION_ABORTED: _, WINDOW_SET_URL_BAR_ACTIVE: _, // whether the URL bar is being typed in diff --git a/js/state/frameStateUtil.js b/js/state/frameStateUtil.js index a934b183c2..f1725dab4f 100644 --- a/js/state/frameStateUtil.js +++ b/js/state/frameStateUtil.js @@ -10,6 +10,7 @@ const {isIntermediateAboutPage} = require('../lib/appUrlUtil') const urlParse = require('../../app/common/urlParse') const windowActions = require('../actions/windowActions.js') const webviewActions = require('../actions/webviewActions.js') +const appActions = require('../actions/appActions') const comparatorByKeyAsc = (a, b) => a.get('key') > b.get('key') ? 1 : b.get('key') > a.get('key') ? -1 : 0 @@ -19,106 +20,66 @@ const matchFrame = (queryInfo, frame) => { return !Object.keys(queryInfo).map((queryKey) => (frame.get(queryKey) === queryInfo[queryKey])).includes(false) } -function query (windowState, queryInfo) { - return windowState.get('frames').filter(matchFrame.bind(null, queryInfo)) +function query (state, queryInfo) { + return state.get('frames').filter(matchFrame.bind(null, queryInfo)) } -function find (windowState, queryInfo) { - return windowState.get('frames').find(matchFrame.bind(null, queryInfo)) +function find (state, queryInfo) { + return state.get('frames').find(matchFrame.bind(null, queryInfo)) } -function isFrameKeyActive (windowState, frameKey) { - return windowState.get('activeFrameKey') === frameKey +function isFrameKeyActive (state, frameKey) { + return getActiveFrameKey(state) === frameKey } -function getFrameDisplayIndex (windowState, frame) { - return findDisplayIndexForFrameKey(windowState.get('frames'), frame) +function getFrames (state) { + return state.get('frames') } -function getFrames (windowState) { - return windowState.get('frames') +function getSortedFrames (state) { + return state.get('frames').sort(comparatorByKeyAsc) } -function getSortedFrames (windowState) { - return windowState.get('frames').sort(comparatorByKeyAsc) +function getPinnedFrames (state) { + return state.get('frames').filter((frame) => frame.get('pinnedLocation')) } -function getPinnedFrames (windowState) { - return windowState.get('frames').filter((frame) => frame.get('pinnedLocation')) +function getNonPinnedFrames (state) { + return state.get('frames').filter((frame) => !frame.get('pinnedLocation')) } -function getNonPinnedFrames (windowState) { - return windowState.get('frames').filter((frame) => !frame.get('pinnedLocation')) +function getFrameIndex (state, key) { + return getFramesInternalIndex(state, key) } -function getFrameIndex (windowState, frame) { - return findIndexForFrameKey(windowState.get('frames'), frame) +function getActiveFrameIndex (state) { + return getFrameIndex(state, getActiveFrameKey(state)) } -function getActiveFrameDisplayIndex (windowState) { - return getFrameDisplayIndex(windowState, windowState.get('activeFrameKey')) -} - -function getActiveFrameIndex (windowState) { - return getFrameIndex(windowState, windowState.get('activeFrameKey')) -} - -// TODO(bridiver) - will be used for pinned tab transfer -function restoreFramePropsFromTab (tab) { - let frame = (tab && tab.get('frame')) || new Immutable.Map() - return frame.delete('activeShortcut') - .delete('activeShortcutDetails') - .delete('guestInstanceId') - .delete('key') - .delete('tabId') - .delete('parentFrameKey') -} - -function getFramePropsFromTab (tab) { - let frame = new Immutable.Map() - if (tab) { - const isTabPinned = tab.get('pinned') - const url = tab.get('url') - let pinnedLocation = 'about:blank' - - if (isTabPinned && url !== 'about:blank' && url !== '') { - pinnedLocation = url - } - - if (!isTabPinned) { - pinnedLocation = null - } - - frame = frame.set('pinnedLocation', pinnedLocation) - frame = frame.set('title', tab.get('title')) - } - return frame -} - -function getActiveFrameTabId (windowState) { - const activeFrame = getActiveFrame(windowState) +function getActiveFrameTabId (state) { + const activeFrame = getActiveFrame(state) return activeFrame && activeFrame.get('tabId') } -function getFrameByIndex (windowState, i) { - return windowState.getIn(['frames', i]) -} - -function findFrameInList (frames, key) { - return frames.find(matchFrame.bind(null, {key})) +function getFrameByIndex (state, i) { + if (i === -1) { + return null + } + return state.getIn(['frames', i]) } // This will eventually go away fully when we replace frameKey by tabId -function getFrameKeyByTabId (windowState, tabId) { - let parentFrameKey - const openerFrame = getFrameByTabId(windowState, tabId) +function getFrameKeyByTabId (state, tabId) { + let parentFrameKey = null + const openerFrame = getFrameByTabId(state, tabId) if (openerFrame) { parentFrameKey = openerFrame.get('key') } return parentFrameKey } -function getFrameKeysByDisplayIndex (frames) { +function getFrameKeysByDisplayIndex (state) { + const frames = state.get('frames') let framesByDisplayIndex = [[], []] frames.forEach((frame) => { let key = frame.get('key') @@ -133,14 +94,15 @@ function getFrameKeysByDisplayIndex (frames) { }, []) } -function getFrameByDisplayIndex (windowState, i) { - let frames = getFrameKeysByDisplayIndex(windowState.get('frames')) +function getFrameByDisplayIndex (state, i) { + let frames = getFrameKeysByDisplayIndex(state) let key = frames[i] - return getFrameByKey(windowState, key) + return getFrameByKey(state, key) } -function getFrameByKey (windowState, key) { - return find(windowState, {key}) +function getFrameByKey (state, key) { + const index = getFrameIndex(state, key) + return state.getIn(['frames', index]) } function isFrameSecure (frame) { @@ -172,25 +134,29 @@ function getHistory (frame) { return (frame && frame.get('history')) || Immutable.fromJS([]) } -function isFrameKeyPinned (frames, key) { +function isFrameKeyPinned (state, key) { if (typeof key !== 'number') { return false } - const frame = frames.find(matchFrame.bind(null, {key})) + const frame = getFrameByKey(state, key) return frame ? frame.get('pinnedLocation') : false } -function getNonPinnedFrameCount (windowState) { - return windowState.get('frames').filter((frame) => !frame.get('pinnedLocation')).size +function getNonPinnedFrameCount (state) { + return state.get('frames').filter((frame) => !frame.get('pinnedLocation')).size +} + +function getFrameByTabId (state, tabId) { + return getFrameByIndex(state, getFramesInternalIndexByTabId(state, tabId)) } -function getFrameByTabId (windowState, tabId) { - return find(windowState, {tabId}) +function getIndexByTabId (state, tabId) { + return getFramesInternalIndexByTabId(state, tabId) } -function getActiveFrame (windowState) { - const activeFrameIndex = getActiveFrameIndex(windowState) - return windowState.get('frames').get(activeFrameIndex) +function getActiveFrame (state) { + const activeFrameIndex = getActiveFrameIndex(state) + return state.get('frames').get(activeFrameIndex) } // Returns the same as the active frame's location, but returns the requested @@ -214,103 +180,41 @@ function getLastCommittedURL (frame) { return location } -function setActiveFrameDisplayIndex (windowState, i) { - const frame = getFrameByDisplayIndex(windowState, i) - if (!frame) { - return windowState - } - - return setActiveFrameKey(windowState, frame.get('key')) -} - -function setActiveFrameIndex (windowState, i) { - const frame = getFrameByIndex(windowState, i) - if (!frame) { - return windowState - } - - return setActiveFrameKey(windowState, frame.get('key')) +function getActiveFrameKey (state) { + return state.get('activeFrameKey') } -function setActiveFrameKey (windowState, activeFrameKey) { - return windowState.merge({ +function setActiveFrameKey (state, activeFrameKey) { + return state.merge({ activeFrameKey: activeFrameKey, previewFrameKey: null }) } -function getNextFrame (windowState) { - const activeFrameIndex = getActiveFrameDisplayIndex(windowState) - const index = (activeFrameIndex + 1) % windowState.get('frames').size - return getFrameByDisplayIndex(windowState, index) +function getNextFrame (state) { + const activeFrameIndex = findDisplayIndexForFrameKey(state, getActiveFrameKey(state)) + const index = (activeFrameIndex + 1) % state.get('frames').size + return getFrameByDisplayIndex(state, index) } -function getPreviousFrame (windowState) { - const activeFrameIndex = getActiveFrameDisplayIndex(windowState) - const index = (windowState.get('frames').size + activeFrameIndex - 1) % windowState.get('frames').size - return getFrameByDisplayIndex(windowState, index) -} - -/** - * @param {Object} windowState - * @param {Object} frameProps - * @param {String} propName - * @return {Object} the value of the propName associated with frameProps - */ -function getFramePropValue (windowState, frameProps, propName) { - return windowState.getIn(getFramePropPath(windowState, frameProps, propName)) -} - -/** - * @param {Object} windowState - * @param {Object} frameProps - * @param {String} propName - * @return {Object} the path of the propName in windowState - */ -function getFramePropPath (windowState, frameProps, propName) { - return ['frames', getFramePropsIndex(windowState.get('frames'), frameProps), propName] -} - -/** - * Obtains the index for the specified frame key - */ -function findIndexForFrameKey (frames, key) { - return frames.findIndex(matchFrame.bind(null, {key})) +function getPreviousFrame (state) { + const activeFrameIndex = findDisplayIndexForFrameKey(state, getActiveFrameKey(state)) + const index = (state.get('frames').size + activeFrameIndex - 1) % state.get('frames').size + return getFrameByDisplayIndex(state, index) } /** * Obtains the display index for the specified frame key */ -function findDisplayIndexForFrameKey (frames, key) { - return getFrameKeysByDisplayIndex(frames).findIndex((displayKey) => displayKey === key) -} - -/** - * Obtains the frameProps index in the frames - */ -function getFramePropsIndex (frames, frameProps) { - if (!frameProps) { - return -1 - } - let queryInfo = frameProps.toJS ? frameProps.toJS() : frameProps - if (queryInfo.tabId) { - queryInfo = { - tabId: queryInfo.tabId - } - } - if (queryInfo.key) { - queryInfo = { - key: queryInfo.key - } - } - return frames.findIndex(matchFrame.bind(null, queryInfo)) +function findDisplayIndexForFrameKey (state, key) { + return getFrameKeysByDisplayIndex(state).findIndex((displayKey) => displayKey === key) } /** * Find frame that was accessed last */ -function getFrameByLastAccessedTime (frames) { - const frameProps = frames.toJS() +function getFrameByLastAccessedTime (state) { + const frameProps = state.get('frames').toJS() .reduce((pre, cur) => { return ([undefined, null].includes(cur.pinnedLocation) && cur.lastAccessedTime && @@ -320,7 +224,7 @@ function getFrameByLastAccessedTime (frames) { lastAccessedTime: 0 }) - return (frameProps.lastAccessedTime === 0) ? -1 : getFramePropsIndex(frames, frameProps) + return (frameProps.lastAccessedTime === 0) ? -1 : getFrameIndex(state, frameProps.key) } /** @@ -335,7 +239,7 @@ function getFrameByLastAccessedTime (frames) { * It should go like so: * G g1 g1.1 g2 g3 g4 */ -function isAncestorFrameKey (frames, frame, parentFrameKey) { +function isAncestorFrameKey (state, frame, parentFrameKey) { if (!frame || !frame.get('parentFrameKey')) { return false } @@ -347,12 +251,12 @@ function isAncestorFrameKey (frames, frame, parentFrameKey) { // So there is a parentFrameKey but it isn't the specified one. // Check recursively for each of the parentFrame's ancestors to see // if we have a match. - const parentFrameIndex = findIndexForFrameKey(frames, frame.get('parentFrameKey')) - const parentFrame = frames.get(parentFrameIndex) + const parentFrameIndex = getFrameIndex(state, frame.get('parentFrameKey')) + const parentFrame = state.getIn(['frames', parentFrameIndex]) if (parentFrameIndex === -1 || !parentFrame.get('parentFrameKey')) { return false } - return isAncestorFrameKey(frames, parentFrame, parentFrameKey) + return isAncestorFrameKey(state, parentFrame, parentFrameKey) } function getPartitionNumber (partition) { @@ -395,8 +299,8 @@ const frameOptsFromFrame = (frame) => { * Adds a frame specified by frameOpts and newKey and sets the activeFrameKey * @return Immutable top level application state ready to merge back in */ -function addFrame (windowState, frameOpts, newKey, partitionNumber, activeFrameKey, insertionIndex) { - const frames = windowState.get('frames') +function addFrame (state, frameOpts, newKey, partitionNumber, activeFrameKey, insertionIndex) { + const frames = state.get('frames') const url = frameOpts.location || config.defaultUrl // delayedLoadUrl is used as a placeholder when the new frame is created @@ -414,7 +318,7 @@ function addFrame (windowState, frameOpts, newKey, partitionNumber, activeFrameK // calculating it here. let parentFrameKey = frameOpts.parentFrameKey if (frameOpts.openerTabId) { - parentFrameKey = getFrameKeyByTabId(windowState, frameOpts.openerTabId) + parentFrameKey = getFrameKeyByTabId(state, frameOpts.openerTabId) } const frame = Immutable.fromJS(Object.assign({ @@ -470,7 +374,7 @@ function addFrame (windowState, frameOpts, newKey, partitionNumber, activeFrameK * Removes a frame specified by frameProps * @return Immutable top level application state ready to merge back in */ -function removeFrame (frames, closedFrames, frameProps, activeFrameKey, framePropsIndex, closeAction) { +function removeFrame (state, frameProps, activeFrameKey, framePropsIndex, closeAction) { function getNewActiveFrame (activeFrameIndex) { // Go to the next frame if it exists. let index = activeFrameIndex @@ -491,14 +395,23 @@ function removeFrame (frames, closedFrames, frameProps, activeFrameKey, framePro return prevFrame.get('key') } } + // Fall back on the original logic. - return Math.max( - newFrames.get(activeFrameIndex) - ? newFrames.get(activeFrameIndex).get('key') - : newFrames.get(activeFrameIndex - 1).get('key'), - 0) + nextFrame = newFrames.get(activeFrameIndex) + if (!nextFrame) { + nextFrame = newFrames.get(activeFrameIndex - 1) + } + if (!nextFrame) { + nextFrame = newFrames.get(0) + } + return nextFrame.get('key') } + const frames = state.get('frames') + let closedFrames = state.get('closedFrames') + const newFrames = frames.splice(framePropsIndex, 1) + let newActiveFrameKey = activeFrameKey + if (!frameProps.get('isPrivate') && frameProps.get('location') !== 'about:newtab') { frameProps = frameProps.set('isFullScreen', false) closedFrames = closedFrames.push(frameProps) @@ -510,9 +423,6 @@ function removeFrame (frames, closedFrames, frameProps, activeFrameKey, framePro } } - const newFrames = frames.splice(framePropsIndex, 1) - let newActiveFrameKey = activeFrameKey - // If the frame being removed IS ACTIVE let isActiveFrameBeingRemoved = frameProps.get('key') === activeFrameKey if (isActiveFrameBeingRemoved && newFrames.size > 0) { @@ -520,7 +430,7 @@ function removeFrame (frames, closedFrames, frameProps, activeFrameKey, framePro switch (closeAction) { case tabCloseAction.LAST_ACTIVE: - const lastActive = getFrameByLastAccessedTime(newFrames) + const lastActive = getFrameByLastAccessedTime(state) activeFrameIndex = (lastActive > -1) ? lastActive : frames.count() - 1 break case tabCloseAction.NEXT: @@ -528,8 +438,8 @@ function removeFrame (frames, closedFrames, frameProps, activeFrameKey, framePro break // Default is a parent tab default: - let parentFrameIndex = findIndexForFrameKey(frames, frameProps.get('parentFrameKey')) - activeFrameIndex = (parentFrameIndex === -1) ? findIndexForFrameKey(frames, activeFrameKey) : parentFrameIndex + let parentFrameIndex = getFrameIndex(state, frameProps.get('parentFrameKey')) + activeFrameIndex = (parentFrameIndex === -1) ? getFrameIndex(state, activeFrameKey) : parentFrameIndex break } @@ -545,8 +455,9 @@ function removeFrame (frames, closedFrames, frameProps, activeFrameKey, framePro } } -function getFrameTabPageIndex (frames, frameProps, tabsPerTabPage) { - const index = getFramePropsIndex(frames, frameProps) +function getFrameTabPageIndex (state, frameProps, tabsPerTabPage) { + frameProps = makeImmutable(frameProps) + const index = getFrameIndex(state, frameProps.get('key')) if (index === -1) { return -1 } @@ -584,15 +495,61 @@ function getTotalBlocks (frame) { : ((blocked > 99) ? '99+' : blocked) } -const frameStatePath = (windowState, key) => - ['frames', findIndexForFrameKey(windowState.get('frames'), key)] +const frameStatePath = (state, frameKey) => + ['frames', getFrameIndex(state, frameKey)] + +const activeFrameStatePath = (state) => frameStatePath(state, getActiveFrameKey(state)) + +const getFramesInternalIndex = (state, frameKey) => { + if (frameKey == null) return -1 + + let index = state.getIn(['framesInternal', 'index', frameKey]) + if (index == null) { + index = state.getIn(['framesInternal', 'index', frameKey.toString()]) + } + return index == null ? -1 : index +} + +const getFramesInternalIndexByTabId = (state, tabId) => { + if (tabId == null) return -1 + + let index = state.getIn(['framesInternal', 'tabIndex', tabId]) + if (index == null) { + index = state.getIn(['framesInternal', 'tabIndex', tabId.toString()]) + } + return index == null ? -1 : index +} + +const deleteFrameInternalIndex = (state, frame) => { + if (!frame) { + return state + } + state = state.deleteIn(['framesInternal', 'index', frame.get('key').toString()]) + state = state.deleteIn(['framesInternal', 'index', frame.get('key')]) + state = state.deleteIn(['framesInternal', 'tabIndex', frame.get('tabId').toString()]) + return state.deleteIn(['framesInternal', 'tabIndex', frame.get('tabId')]) +} + +const updateFramesInternalIndex = (state, fromIndex) => { + let framesInternal = state.get('framesInternal') || Immutable.Map() + state.get('frames').slice(fromIndex).forEach((frame, idx) => { + const realIndex = idx + fromIndex + if (frame.get('key')) { + framesInternal = framesInternal.setIn(['index', frame.get('key')], realIndex) + } + if (frame.get('tabId') !== -1) { + framesInternal = framesInternal.setIn(['tabIndex', frame.get('tabId')], realIndex) + } -const activeFrameStatePath = (windowState) => frameStatePath(windowState, windowState.get('activeFrameKey')) + appActions.tabIndexChanged(frame.get('tabId'), realIndex) + }) -const frameStatePathForFrame = (windowState, frameProps) => - ['frames', getFramePropsIndex(windowState.get('frames'), frameProps)] + return state.set('framesInternal', framesInternal) +} module.exports = { + deleteFrameInternalIndex, + updateFramesInternalIndex, query, find, isAncestorFrameKey, @@ -611,29 +568,19 @@ module.exports = { getPinnedFrames, getNonPinnedFrames, getFrameIndex, - getFrameDisplayIndex, - restoreFramePropsFromTab, - getFramePropsFromTab, getActiveFrameIndex, - getActiveFrameDisplayIndex, getActiveFrameTabId, getFrameByIndex, getFrameByDisplayIndex, getFrameByKey, getFrameByTabId, + getIndexByTabId, getPartitionNumber, getActiveFrame, - setActiveFrameDisplayIndex, - setActiveFrameIndex, setActiveFrameKey, getNextFrame, getPreviousFrame, - getFramePropValue, - getFramePropPath, - findIndexForFrameKey, findDisplayIndexForFrameKey, - findFrameInList, - getFramePropsIndex, getFrameKeysByDisplayIndex, getPartition, getPartitionFromNumber, @@ -644,7 +591,6 @@ module.exports = { getFrameTabPageIndex, frameStatePath, activeFrameStatePath, - frameStatePathForFrame, getLastCommittedURL, getFrameByLastAccessedTime, onFindBarHide, diff --git a/js/stores/eventStore.js b/js/stores/eventStore.js index b4f84dde27..c03edf74da 100644 --- a/js/stores/eventStore.js +++ b/js/stores/eventStore.js @@ -67,7 +67,7 @@ const addPageView = (url, tabId) => { url, tabId }) - eventState = eventState.set('page_view', eventState.get('page_view').push(pageViewEvent)) + eventState = eventState.set('page_view', eventState.get('page_view').slice(-100).push(pageViewEvent)) lastActivePageUrl = url } @@ -117,19 +117,19 @@ const doAction = (action) => { break case 'event-set-page-info': // retains all past pages, not really sure that's needed... [MTR] - eventState = eventState.set('page_info', eventState.get('page_info').push(action.pageInfo)) + eventState = eventState.set('page_info', eventState.get('page_info').slice(-100).push(action.pageInfo)) break case windowConstants.WINDOW_GOT_RESPONSE_DETAILS: // Only capture response for the page (not subresources, like images, JavaScript, etc) - if (action.details && action.details.get('resourceType') === 'mainFrame') { - const pageUrl = action.details.get('newURL') + if (action.details && action.details.resourceType === 'mainFrame') { + const pageUrl = action.details.newURL // create a page view event if this is a page load on the active tabId if (!lastActiveTabId || action.tabId === lastActiveTabId) { addPageView(pageUrl, action.tabId) } - const responseCode = action.details.get('httpResponseCode') + const responseCode = action.details.httpResponseCode if (isSourceAboutUrl(pageUrl) || !responseHasContent(responseCode)) { break } @@ -140,7 +140,7 @@ const doAction = (action) => { tabId: action.tabId, details: action.details }) - eventState = eventState.set('page_load', eventState.get('page_load').push(pageLoadEvent)) + eventState = eventState.set('page_load', eventState.get('page_load').slice(-100).push(pageLoadEvent)) } break default: diff --git a/js/stores/windowStore.js b/js/stores/windowStore.js index 654f740d9a..27d2b37225 100644 --- a/js/stores/windowStore.js +++ b/js/stores/windowStore.js @@ -6,13 +6,11 @@ const AppDispatcher = require('../dispatcher/appDispatcher') const EventEmitter = require('events').EventEmitter const appActions = require('../actions/appActions') const appConstants = require('../constants/appConstants') -const windowActions = require('../actions/windowActions') const windowConstants = require('../constants/windowConstants') const config = require('../constants/config') const settings = require('../constants/settings') const Immutable = require('immutable') const frameStateUtil = require('../state/frameStateUtil') -const {activeFrameStatePath, frameStatePathForFrame} = frameStateUtil const ipc = require('electron').ipcRenderer const messages = require('../constants/messages') const debounce = require('../lib/debounce') @@ -26,10 +24,6 @@ const Serializer = require('../dispatcher/serializer') const {updateTabPageIndex} = require('../../app/renderer/lib/tabUtil') const assert = require('assert') const contextMenuState = require('../../app/common/state/contextMenuState') -const tabState = require('../../app/common/state/tabState') -const appStoreRenderer = require('./appStoreRenderer') - -let previousTabs = new Immutable.List() let windowState = Immutable.fromJS({ activeFrameKey: null, @@ -103,7 +97,7 @@ const addToHistory = (frameProps) => { return history.slice(-10) } -const newFrame = (frameOpts, openInForeground, insertionIndex, nextKey) => { +const newFrame = (state, frameOpts, openInForeground, insertionIndex, nextKey) => { if (frameOpts === undefined) { frameOpts = {} } @@ -150,14 +144,14 @@ const newFrame = (frameOpts, openInForeground, insertionIndex, nextKey) => { // calculating it here. let parentFrameKey = frameOpts.parentFrameKey if (frameOpts.openerTabId) { - parentFrameKey = frameStateUtil.getFrameKeyByTabId(windowState, frameOpts.openerTabId) + parentFrameKey = frameStateUtil.getFrameKeyByTabId(state, frameOpts.openerTabId) } // Find the closest index to the current frame's index which has // a different ancestor frame key. - const frames = frameStateUtil.getFrames(windowState) + const frames = frameStateUtil.getFrames(state) if (insertionIndex === undefined) { - insertionIndex = frameStateUtil.findIndexForFrameKey(frames, frameOpts.indexByFrameKey || parentFrameKey) + insertionIndex = frameStateUtil.getFrameIndex(state, frameOpts.indexByFrameKey || parentFrameKey) if (frameOpts.prependIndexByFrameKey === false) { insertionIndex++ } @@ -167,13 +161,13 @@ const newFrame = (frameOpts, openInForeground, insertionIndex, nextKey) => { } else if (!frameOpts.indexByFrameKey) { while (insertionIndex < frames.size) { ++insertionIndex - if (!frameStateUtil.isAncestorFrameKey(frames, frames.get(insertionIndex), parentFrameKey)) { + if (!frameStateUtil.isAncestorFrameKey(state, frames.get(insertionIndex), parentFrameKey)) { break } } } } - if (frameStateUtil.isFrameKeyPinned(frames, parentFrameKey)) { + if (frameStateUtil.isFrameKeyPinned(state, parentFrameKey)) { insertionIndex = 0 } @@ -181,15 +175,19 @@ const newFrame = (frameOpts, openInForeground, insertionIndex, nextKey) => { nextKey = incrementNextKey() } - windowState = windowState.merge( + state = state.merge( frameStateUtil.addFrame( - windowState, frameOpts, - nextKey, frameOpts.partitionNumber, openInForeground || typeof windowState.get('activeFrameKey') !== 'number' ? nextKey : windowState.get('activeFrameKey'), insertionIndex)) + state, frameOpts, + nextKey, frameOpts.partitionNumber, openInForeground || typeof state.get('activeFrameKey') !== 'number' ? nextKey : state.get('activeFrameKey'), insertionIndex)) + + state = frameStateUtil.updateFramesInternalIndex(state, insertionIndex) if (openInForeground) { - const activeFrame = frameStateUtil.getActiveFrame(windowState) - windowState = updateTabPageIndex(windowState, activeFrame) + const activeFrame = frameStateUtil.getActiveFrame(state) + state = updateTabPageIndex(state, activeFrame) } + + return state } const frameTabIdChanged = (state, action) => { @@ -201,15 +199,10 @@ const frameTabIdChanged = (state, action) => { } let newFrameProps = new Immutable.Map() - // TODO(bridiver) - use for pinned tab transfer - // if (oldTabId != null && oldTabId !== -1) { - // let tab = appStoreRenderer.state.get('tabs').find((tab) => tab.get('tabId') === newTabId) - // if (tab.get('pinned')) { - // newFrameProps = frameStateUtil.restoreFramePropsFromTab(tab) - // } - // } newFrameProps = newFrameProps.set('tabId', newTabId) - return state.mergeIn(['frames', frameStateUtil.getFramePropsIndex(frameStateUtil.getFrames(state), action.get('frameProps'))], newFrameProps) + const index = frameStateUtil.getFrameIndex(state, action.getIn(['frameProps', 'key'])) + state = state.mergeIn(['frames', index], newFrameProps) + return frameStateUtil.updateFramesInternalIndex(state, index) } const frameGuestInstanceIdChanged = (state, action) => { @@ -221,45 +214,21 @@ const frameGuestInstanceIdChanged = (state, action) => { return state } - return state.mergeIn(['frames', frameStateUtil.getFramePropsIndex(frameStateUtil.getFrames(state), action.get('frameProps'))], { + return state.mergeIn(['frames', frameStateUtil.getFrameIndex(state, action.getIn(['frameProps', 'key']))], { guestInstanceId: newGuestInstanceId }) } -const tabDataChanged = (state, action) => { - action = makeImmutable(action) - const tabs = action.get('tabs') - - tabs.forEach((tab) => { - const newProps = frameStateUtil.getFramePropsFromTab(tab) - const frameProps = frameStateUtil.getFrameByTabId(state, tab.get('tabId')) - if (frameProps) { - state = state.mergeIn(['frames', frameStateUtil.getFramePropsIndex(frameStateUtil.getFrames(state), frameProps)], newProps) - if (tab.get('active') === true && tab.get('windowId') === getCurrentWindowId()) { - state = state.merge({ - activeFrameKey: frameProps.get('key'), - previewFrameKey: null - }) - state = state.mergeIn(['frames', frameStateUtil.getFramePropsIndex(frameStateUtil.getFrames(state), action.frameProps)], { - lastAccessedTime: new Date().getTime() }) - state = state.deleteIn(['ui', 'tabs', 'previewTabPageIndex']) - state = updateTabPageIndex(state, frameProps) - } - } - }) - return state -} - const windowStore = new WindowStore() const emitChanges = debounce(windowStore.emitChanges.bind(windowStore), 5) -const applyReducers = (state, action) => [ +const applyReducers = (state, action, immutableAction) => [ require('../../app/renderer/reducers/urlBarReducer'), require('../../app/renderer/reducers/frameReducer'), require('../../app/renderer/reducers/contextMenuReducer') ].reduce( (windowState, reducer) => { - const newState = reducer(windowState, action) + const newState = reducer(windowState, action, immutableAction) assert.ok(Immutable.Map.isMap(newState), `Oops! action ${action.actionType} didn't return valid state for reducer:\n\n${reducer}`) return newState @@ -280,14 +249,14 @@ const immediatelyEmittedActions = [ // Register callback to handle all updates const doAction = (action) => { // console.log(action.actionType, action, windowState.toJS()) - windowState = applyReducers(windowState, action) + windowState = applyReducers(windowState, action, makeImmutable(action)) switch (action.actionType) { case windowConstants.WINDOW_SET_STATE: windowState = action.windowState currentKey = frameStateUtil.getFrames(windowState).reduce((previousVal, frame) => Math.max(previousVal, frame.get('key')), 0) const activeFrame = frameStateUtil.getActiveFrame(windowState) if (activeFrame && activeFrame.get('location') !== 'about:newtab') { - focusWebview(activeFrameStatePath(windowState)) + focusWebview(frameStateUtil.activeFrameStatePath(windowState)) } // We should not emit here because the Window already know about the change on startup. return @@ -302,7 +271,7 @@ const doAction = (action) => { // set the previous location to the most recent history item or the default url let previousLocation = action.frameProps.get('history').unshift(config.defaultUrl).findLast((url) => url !== action.errorDetails.url) - windowState = windowState.mergeIn(['frames', frameStateUtil.getFramePropsIndex(frameStateUtil.getFrames(windowState), action.frameProps)], { + windowState = windowState.mergeIn(['frames', frameStateUtil.getFrameIndex(windowState, action.frameProps.get('key'))], { aboutDetails: Object.assign({ title: action.errorDetails.title || l10nErrorText(action.errorDetails.errorCode), message: action.errorDetails.message, @@ -312,21 +281,21 @@ const doAction = (action) => { }) break case windowConstants.WINDOW_SET_FINDBAR_SHOWN: - const frameIndex = frameStateUtil.findIndexForFrameKey(windowState.get('frames'), action.frameKey) + const frameIndex = frameStateUtil.getFrameIndex(windowState, action.frameKey) windowState = windowState.mergeIn(['frames', frameIndex], { findbarShown: action.shown, findbarSelected: action.shown }) break case windowConstants.WINDOW_SET_FINDBAR_SELECTED: - windowState = windowState.mergeIn(['frames', frameStateUtil.getFramePropsIndex(frameStateUtil.getFrames(windowState), action.frameProps)], { + windowState = windowState.mergeIn(['frames', frameStateUtil.getFrameIndex(windowState, action.frameProps.get('key'))], { findbarSelected: action.selected }) break case windowConstants.WINDOW_WEBVIEW_LOAD_START: { const statePath = path => - [path, frameStateUtil.getFramePropsIndex(frameStateUtil.getFrames(windowState), action.frameProps)] + [path, frameStateUtil.getFrameIndex(windowState, action.frameProps.get('key'))] // Reset security state windowState = @@ -345,12 +314,12 @@ const doAction = (action) => { // For about:newtab we want to have the urlbar focused, not the new frame. // Otherwise we want to focus the new tab when it is a new frame in the foreground. if (action.location !== getTargetAboutUrl('about:newtab')) { - focusWebview(activeFrameStatePath(windowState)) + focusWebview(frameStateUtil.activeFrameStatePath(windowState)) } break } case windowConstants.WINDOW_WEBVIEW_LOAD_END: - windowState = windowState.mergeIn(['frames', frameStateUtil.getFramePropsIndex(frameStateUtil.getFrames(windowState), action.frameProps)], { + windowState = windowState.mergeIn(['frames', frameStateUtil.getFrameIndex(windowState, action.frameProps.get('key'))], { loading: false, endLoadTime: new Date().getTime(), history: addToHistory(action.frameProps) @@ -398,21 +367,24 @@ const doAction = (action) => { break case windowConstants.WINDOW_SET_TAB_BREAKPOINT: { - const frameIndex = frameStateUtil.findIndexForFrameKey(frameStateUtil.getFrames(windowState), action.frameKey) + if (!action.frameKey) { + break + } + const frameIndex = frameStateUtil.getFrameIndex(frameStateUtil.getFrames(windowState), action.frameKey) windowState = windowState.setIn(['frames', frameIndex, 'breakpoint'], action.breakpoint) break } case windowConstants.WINDOW_SET_TAB_HOVER_STATE: { - const frameIndex = frameStateUtil.findIndexForFrameKey(frameStateUtil.getFrames(windowState), action.frameKey) + const frameIndex = frameStateUtil.getFrameIndex(frameStateUtil.getFrames(windowState), action.frameKey) windowState = windowState.setIn(['frames', frameIndex, 'hoverState'], action.hoverState) break } case windowConstants.WINDOW_TAB_MOVE: { const sourceFrameProps = frameStateUtil.getFrameByKey(windowState, action.sourceFrameKey) - const sourceFrameIndex = frameStateUtil.findIndexForFrameKey(frameStateUtil.getFrames(windowState), action.sourceFrameKey) - let newIndex = frameStateUtil.findIndexForFrameKey(frameStateUtil.getFrames(windowState), action.destinationFrameKey) + (action.prepend ? 0 : 1) + const sourceFrameIndex = frameStateUtil.getFrameIndex(frameStateUtil.getFrames(windowState), action.sourceFrameKey) + let newIndex = frameStateUtil.getFrameIndex(windowState, action.destinationFrameProps.get('key')) + (action.prepend ? 0 : 1) let frames = frameStateUtil.getFrames(windowState).splice(sourceFrameIndex, 1) if (newIndex > sourceFrameIndex) { newIndex-- @@ -420,25 +392,29 @@ const doAction = (action) => { frames = frames.splice(newIndex, 0, sourceFrameProps) windowState = windowState.set('frames', frames) // Since the tab could have changed pages, update the tab page as well + windowState = frameStateUtil.updateFramesInternalIndex(windowState, Math.min(sourceFrameIndex, newIndex)) windowState = updateTabPageIndex(windowState, frameStateUtil.getActiveFrame(windowState)) break } case windowConstants.WINDOW_SET_LINK_HOVER_PREVIEW: - windowState = windowState.mergeIn(activeFrameStatePath(windowState), { + windowState = windowState.mergeIn(frameStateUtil.activeFrameStatePath(windowState), { hrefPreview: action.href, showOnRight: action.showOnRight }) break case windowConstants.WINDOW_SET_THEME_COLOR: - if (action.themeColor !== undefined) { - windowState = windowState.setIn(frameStatePathForFrame(windowState, action.frameProps).concat(['themeColor']), action.themeColor) - } - if (action.computedThemeColor !== undefined) { - windowState = windowState.setIn(frameStatePathForFrame(windowState, action.frameProps).concat(['computedThemeColor']), action.computedThemeColor) + { + const frameKey = action.frameProps.get('key') + if (action.themeColor !== undefined) { + windowState = windowState.setIn(frameStateUtil.frameStatePath(windowState, frameKey).concat(['themeColor']), action.themeColor) + } + if (action.computedThemeColor !== undefined) { + windowState = windowState.setIn(frameStateUtil.frameStatePath(windowState, frameKey).concat(['computedThemeColor']), action.computedThemeColor) + } + break } - break case windowConstants.WINDOW_FRAME_SHORTCUT_CHANGED: - const framePath = action.frameProps ? ['frames', frameStateUtil.getFramePropsIndex(frameStateUtil.getFrames(windowState), action.frameProps)] : activeFrameStatePath(windowState) + const framePath = action.frameProps ? ['frames', frameStateUtil.getFrameIndex(windowState, action.frameProps.get('key'))] : frameStateUtil.activeFrameStatePath(windowState) windowState = windowState.mergeIn(framePath, { activeShortcut: action.activeShortcut, activeShortcutDetails: action.activeShortcutDetails @@ -451,7 +427,7 @@ const doAction = (action) => { break case windowConstants.WINDOW_SET_FIND_DETAIL: { - const frameIndex = frameStateUtil.findIndexForFrameKey(windowState.get('frames'), action.frameKey) + const frameIndex = frameStateUtil.getFrameIndex(windowState, action.frameKey) windowState = windowState.mergeIn(['frames', frameIndex, 'findDetail'], action.findDetail) break } @@ -505,13 +481,13 @@ const doAction = (action) => { }) break case windowConstants.WINDOW_SET_AUDIO_PLAYBACK_ACTIVE: - windowState = windowState.setIn(['frames', frameStateUtil.getFramePropsIndex(frameStateUtil.getFrames(windowState), action.frameProps), 'audioPlaybackActive'], action.audioPlaybackActive) + windowState = windowState.setIn(['frames', frameStateUtil.getFrameIndex(windowState, action.frameProps.get('key')), 'audioPlaybackActive'], action.audioPlaybackActive) break case windowConstants.WINDOW_SET_FAVICON: - windowState = windowState.setIn(['frames', frameStateUtil.getFramePropsIndex(frameStateUtil.getFrames(windowState), action.frameProps), 'icon'], action.favicon) + windowState = windowState.setIn(['frames', frameStateUtil.getFrameIndex(windowState, action.frameProps.get('key')), 'icon'], action.favicon) break case windowConstants.WINDOW_SET_LAST_ZOOM_PERCENTAGE: - windowState = windowState.setIn(['frames', frameStateUtil.getFramePropsIndex(frameStateUtil.getFrames(windowState), action.frameProps), 'lastZoomPercentage'], action.percentage) + windowState = windowState.setIn(['frames', frameStateUtil.getFrameIndex(windowState, action.frameProps.get('key')), 'lastZoomPercentage'], action.percentage) break case windowConstants.WINDOW_SET_MOUSE_IN_TITLEBAR: windowState = windowState.setIn(['ui', 'mouseInTitlebar'], action.mouseInTitlebar) @@ -562,7 +538,7 @@ const doAction = (action) => { } break case windowConstants.WINDOW_WIDEVINE_SITE_ACCESSED_WITHOUT_INSTALL: - const activeLocation = windowState.getIn(activeFrameStatePath(windowState).concat(['location'])) + const activeLocation = windowState.getIn(frameStateUtil.activeFrameStatePath(windowState).concat(['location'])) windowState = windowState.set('widevinePanelDetail', Immutable.Map({ alsoAddRememberSiteSetting: true, location: activeLocation, @@ -596,35 +572,37 @@ const doAction = (action) => { windowState = windowState.setIn(['ui', 'releaseNotes', 'isVisible'], action.isVisible) break case windowConstants.WINDOW_SET_SECURITY_STATE: - let path = frameStatePathForFrame(windowState, action.frameProps) - if (action.securityState.secure !== undefined) { - windowState = windowState.setIn(path.concat(['security', 'isSecure']), - action.securityState.secure) - } - if (action.securityState.runInsecureContent !== undefined) { - windowState = windowState.setIn(path.concat(['security', 'runInsecureContent']), - action.securityState.runInsecureContent) + { + const path = frameStateUtil.frameStatePath(windowState, action.frameProps.get('key')) + if (action.securityState.secure !== undefined) { + windowState = windowState.setIn(path.concat(['security', 'isSecure']), + action.securityState.secure) + } + if (action.securityState.runInsecureContent !== undefined) { + windowState = windowState.setIn(path.concat(['security', 'runInsecureContent']), + action.securityState.runInsecureContent) + } + break } - break case windowConstants.WINDOW_SET_BLOCKED_BY: - const blockedByPath = ['frames', frameStateUtil.getFramePropsIndex(frameStateUtil.getFrames(windowState), action.frameProps), action.blockType, 'blocked'] + const blockedByPath = ['frames', frameStateUtil.getFrameIndex(windowState, action.frameProps.get('key')), action.blockType, 'blocked'] let blockedBy = windowState.getIn(blockedByPath) || new Immutable.List() blockedBy = blockedBy.toSet().add(action.location).toList() windowState = windowState.setIn(blockedByPath, blockedBy) break case windowConstants.WINDOW_SET_REDIRECTED_BY: - const redirectedByPath = ['frames', frameStateUtil.getFramePropsIndex(frameStateUtil.getFrames(windowState), action.frameProps), 'httpsEverywhere', action.ruleset] + const redirectedByPath = ['frames', frameStateUtil.getFrameIndex(windowState, action.frameProps.get('key')), 'httpsEverywhere', action.ruleset] let redirectedBy = windowState.getIn(redirectedByPath) || new Immutable.List() windowState = windowState.setIn(redirectedByPath, redirectedBy.push(action.location)) break case windowConstants.WINDOW_ADD_HISTORY: - windowState = windowState.mergeIn(['frames', frameStateUtil.getFramePropsIndex(frameStateUtil.getFrames(windowState), action.frameProps)], { + windowState = windowState.mergeIn(['frames', frameStateUtil.getFrameIndex(windowState, action.frameProps.get('key'))], { history: addToHistory(action.frameProps) }) break case windowConstants.WINDOW_SET_BLOCKED_RUN_INSECURE_CONTENT: const blockedRunInsecureContentPath = - ['frames', frameStateUtil.getFramePropsIndex(frameStateUtil.getFrames(windowState), action.frameProps)] + ['frames', frameStateUtil.getFrameIndex(windowState, action.frameProps.get('key'))] if (action.source) { let blockedList = windowState.getIn( blockedRunInsecureContentPath.concat(['security', 'blockedRunInsecureContent'])) || new Immutable.List() @@ -700,9 +678,6 @@ const doAction = (action) => { case windowConstants.WINDOW_TAB_MOUSE_LEAVE: windowState = windowState.deleteIn(['ui', 'tabs', 'fixTabWidth']) break - case windowConstants.WINDOW_TAB_DATA_CHANGED: - windowState = tabDataChanged(windowState, action) - break case appConstants.APP_NEW_WEB_CONTENTS_ADDED: if (!action.frameOpts) { break @@ -715,8 +690,7 @@ const doAction = (action) => { action.frameOpts.tabId = tabValue.get('tabId') action.frameOpts.icon = action.frameOpts.icon || tabValue.get('favIconUrl') } - newFrame(action.frameOpts, action.frameOpts.openInForeground) - updateTabPageIndex(frameStateUtil.getActiveFrame(windowState)) + windowState = newFrame(windowState, action.frameOpts, action.frameOpts.openInForeground) break case windowConstants.WINDOW_FRAME_MOUSE_ENTER: windowState = windowState.setIn(['ui', 'mouseInFrame'], true) @@ -796,7 +770,7 @@ frameShortcuts.forEach((shortcut) => { if (shortcut === 'toggle-dev-tools') { appActions.toggleDevTools(frameStateUtil.getActiveFrameTabId(windowState)) } else { - windowState = windowState.mergeIn(activeFrameStatePath(windowState), { + windowState = windowState.mergeIn(frameStateUtil.activeFrameStatePath(windowState), { activeShortcut: shortcut, activeShortcutDetails: args }) @@ -806,7 +780,7 @@ frameShortcuts.forEach((shortcut) => { // Listen for actions on frame N if (['reload', 'mute'].includes(shortcut)) { ipc.on(`shortcut-frame-${shortcut}`, (e, i, args) => { - const path = ['frames', frameStateUtil.findIndexForFrameKey(frameStateUtil.getFrames(windowState), i)] + const path = ['frames', frameStateUtil.getFrameIndex(windowState, i)] windowState = windowState.mergeIn(path, { activeShortcut: shortcut, activeShortcutDetails: args @@ -836,20 +810,6 @@ ipc.on(messages.DISPATCH_ACTION, (e, serializedPayload) => { } }) -const onAppStateChange = () => { - setImmediate(() => { - const tabs = tabState.getTabs(appStoreRenderer.state).map((tab) => { - return tab.delete('frame') - }) - if (tabs.hashCode() !== previousTabs.hashCode()) { - previousTabs = tabs - windowActions.tabDataChanged(tabs) - } - }) -} - -appStoreRenderer.addChangeListener(onAppStateChange) - AppDispatcher.register(doAction) module.exports = windowStore diff --git a/test/tab-components/tabTest.js b/test/tab-components/tabTest.js index cb3739ee8e..343513d1f9 100644 --- a/test/tab-components/tabTest.js +++ b/test/tab-components/tabTest.js @@ -3,7 +3,7 @@ const Brave = require('../lib/brave') const messages = require('../../js/constants/messages') const settings = require('../../js/constants/settings') -const {urlInput, backButton, forwardButton, activeTab, activeTabTitle, activeTabFavicon, newFrameButton, notificationBar, contextMenu} = require('../lib/selectors') +const {urlInput, backButton, forwardButton, activeTab, activeTabTitle, activeTabFavicon, newFrameButton, notificationBar, contextMenu, pinnedTabsTabs, tabsTabs} = require('../lib/selectors') describe('tab tests', function () { function * setup (client) { @@ -14,8 +14,8 @@ describe('tab tests', function () { } describe('back forward actions', function () { - Brave.beforeAll(this) - before(function * () { + Brave.beforeEach(this) + beforeEach(function * () { yield setup(this.app.client) this.page1 = Brave.server.url('page1.html') this.page2 = Brave.server.url('page2.html') @@ -117,6 +117,30 @@ describe('tab tests', function () { }) describe('tab order', function () { + describe('with pinned tabs', function () { + Brave.beforeAll(this) + before(function * () { + this.page1 = Brave.server.url('page1.html') + this.page2 = Brave.server.url('page2.html') + yield setup(this.app.client) + yield this.app.client + .waitForExist('.tabArea:nth-of-type(1) [data-frame-key="1"]') // original newtab + .newTab({ url: this.page1, pinned: true }) + .waitForExist(pinnedTabsTabs + '[data-frame-key="2"]') + .newTab({ url: this.page1 }) + .waitForExist(tabsTabs + '[data-frame-key="3"]') + .newTab({ url: this.page2 }) + .waitForExist(tabsTabs + '[data-frame-key="4"]') + }) + + it('sequentially by default', function * () { + yield this.app.client + .waitForExist('.tabArea:nth-of-type(2) [data-frame-key="3"]') + .waitForExist('webview[data-frame-key="3"][src="' + this.page1 + '"]') + .waitForExist('.tabArea:nth-of-type(3) [data-frame-key="4"]') + .waitForExist('.frameWrapper.isActive webview[data-frame-key="4"][src="' + this.page2 + '"]') + }) + }) describe('sequentially by default', function () { Brave.beforeAll(this) before(function * () { diff --git a/test/unit/app/common/state/basicAuthStateTest.js b/test/unit/app/common/state/basicAuthStateTest.js index f8bfc258da..fdc3cf691e 100644 --- a/test/unit/app/common/state/basicAuthStateTest.js +++ b/test/unit/app/common/state/basicAuthStateTest.js @@ -4,16 +4,24 @@ const tabState = require('../../../../../app/common/state/tabState') const Immutable = require('immutable') const assert = require('assert') -const defaultAppState = Immutable.fromJS({ +const defaultTabId = 1 + +const sampleAppState = { windows: [{ windowId: 1, windowUUID: 'uuid' }], - tabs: [] -}) + tabs: [], + tabsInternal: { + index: {} + } +} +sampleAppState.tabsInternal.index[defaultTabId] = 0 + +const defaultAppState = Immutable.fromJS(sampleAppState) const defaultTab = Immutable.fromJS({ - tabId: 1, + tabId: defaultTabId, windowId: 1, windowUUID: 'uuid', loginRequiredDetail: { @@ -22,12 +30,12 @@ const defaultTab = Immutable.fromJS({ } }) -describe('basicAuthState', function () { +describe('basicAuthState unit tests', function () { describe('setLoginResponseDetail', function () { describe('`tabId` exists in appState with loginRequiredDetail', function () { before(function () { this.appState = defaultAppState.set('tabs', Immutable.fromJS([defaultTab])) - this.appState = basicAuthState.setLoginResponseDetail(this.appState, {tabId: 1, + this.appState = basicAuthState.setLoginResponseDetail(this.appState, {tabId: defaultTabId, detail: { username: 'username', password: 'password' @@ -35,7 +43,7 @@ describe('basicAuthState', function () { }) it('removes the login detail', function () { - let tab = tabState.getByTabId(this.appState, 1) + let tab = tabState.getByTabId(this.appState, defaultTabId) assert(tab) assert.equal(undefined, tab.get('loginRequiredDetail')) }) @@ -43,8 +51,8 @@ describe('basicAuthState', function () { describe('`tabId` exists in appState with no loginRequiredDetail', function () { before(function () { - this.appState = defaultAppState.set('tabs', Immutable.fromJS([{ tabId: 1 }])) - this.appState = basicAuthState.setLoginResponseDetail(this.appState, {tabId: 1, + this.appState = defaultAppState.set('tabs', Immutable.fromJS([{ tabId: defaultTabId }])) + this.appState = basicAuthState.setLoginResponseDetail(this.appState, {tabId: defaultTabId, detail: { username: 'username', password: 'password' @@ -52,7 +60,7 @@ describe('basicAuthState', function () { }) it('returns the unmodified appState', function () { - let tab = tabState.getByTabId(this.appState, 1) + let tab = tabState.getByTabId(this.appState, defaultTabId) assert(tab) assert.equal(undefined, tab.get('loginRequiredDetail')) }) @@ -60,7 +68,7 @@ describe('basicAuthState', function () { describe('`tabId` does not exist in appState', function () { before(function () { - this.appState = basicAuthState.setLoginResponseDetail(defaultAppState, {tabId: 1, + this.appState = basicAuthState.setLoginResponseDetail(defaultAppState, {tabId: defaultTabId, detail: { username: 'username', password: 'password' @@ -68,7 +76,7 @@ describe('basicAuthState', function () { }) it('returns the unmodified appState', function () { - let tab = tabState.getByTabId(this.appState, 1) + let tab = tabState.getByTabId(this.appState, defaultTabId) assert.equal(null, tab) }) }) @@ -80,11 +88,11 @@ describe('basicAuthState', function () { describe('with null detail', function () { before(function () { this.appState = defaultAppState.set('tabs', Immutable.fromJS([defaultTab])) - this.appState = basicAuthState.setLoginRequiredDetail(this.appState, {tabId: 1}) + this.appState = basicAuthState.setLoginRequiredDetail(this.appState, {tabId: defaultTabId}) }) it('removes the login detail', function () { - let tab = tabState.getByTabId(this.appState, 1) + let tab = tabState.getByTabId(this.appState, defaultTabId) assert(tab) assert.equal(undefined, tab.get('loginRequiredDetail')) }) @@ -93,11 +101,11 @@ describe('basicAuthState', function () { describe('with empty detail', function () { before(function () { this.appState = defaultAppState.set('tabs', Immutable.fromJS([defaultTab])) - this.appState = basicAuthState.setLoginRequiredDetail(this.appState, {tabId: 1, detail: {}}) + this.appState = basicAuthState.setLoginRequiredDetail(this.appState, {tabId: defaultTabId, detail: {}}) }) it('removes the login detail', function () { - let tab = tabState.getByTabId(this.appState, 1) + let tab = tabState.getByTabId(this.appState, defaultTabId) assert(tab) assert.equal(undefined, tab.get('loginRequiredDetail')) }) @@ -107,10 +115,10 @@ describe('basicAuthState', function () { before(function () { this.appState = defaultAppState.set('tabs', Immutable.fromJS([ { - tabId: 1 + tabId: defaultTabId } ])) - this.appState = basicAuthState.setLoginRequiredDetail(this.appState, {tabId: 1, + this.appState = basicAuthState.setLoginRequiredDetail(this.appState, {tabId: defaultTabId, detail: { request: { url: 'someurl' }, authInfo: { authInfoProp: 'value' } @@ -118,7 +126,7 @@ describe('basicAuthState', function () { }) it('sets the login detail for `tabId` in the appState', function () { - let tab = tabState.getByTabId(this.appState, 1) + let tab = tabState.getByTabId(this.appState, defaultTabId) assert(tab) let loginRequiredDetail = tab.get('loginRequiredDetail') assert.equal('someurl', loginRequiredDetail.getIn(['request', 'url'])) @@ -128,7 +136,7 @@ describe('basicAuthState', function () { describe('`tabId` does not exist in appState', function () { before(function () { - this.appState = basicAuthState.setLoginRequiredDetail(defaultAppState, {tabId: 1, + this.appState = basicAuthState.setLoginRequiredDetail(defaultAppState, {tabId: defaultTabId, detail: { request: { url: 'someurl' }, authInfo: { authInfoProp: 'value' } @@ -145,7 +153,7 @@ describe('basicAuthState', function () { describe('`tabId` exists in appState with loginRequiredDetail', function () { before(function () { this.appState = defaultAppState.set('tabs', Immutable.fromJS([defaultTab])) - this.loginRequiredDetail = basicAuthState.getLoginRequiredDetail(this.appState, 1) + this.loginRequiredDetail = basicAuthState.getLoginRequiredDetail(this.appState, defaultTabId) }) it('returns the login detail for `tabId`', function () { @@ -158,10 +166,10 @@ describe('basicAuthState', function () { before(function () { this.appState = defaultAppState.set('tabs', Immutable.fromJS([ { - tabId: 1 + tabId: defaultTabId } ])) - this.loginRequiredDetail = basicAuthState.getLoginRequiredDetail(this.appState, 1) + this.loginRequiredDetail = basicAuthState.getLoginRequiredDetail(this.appState, defaultTabId) }) it('returns null', function () { @@ -171,7 +179,7 @@ describe('basicAuthState', function () { describe('`tabId` does not exist in appState', function () { before(function () { - this.loginRequiredDetail = basicAuthState.getLoginRequiredDetail(defaultAppState, 1) + this.loginRequiredDetail = basicAuthState.getLoginRequiredDetail(defaultAppState, defaultTabId) }) it('returns null', function () { diff --git a/test/unit/app/common/state/tabMessageBoxStateTest.js b/test/unit/app/common/state/tabMessageBoxStateTest.js index be097a2fad..9eaab60986 100644 --- a/test/unit/app/common/state/tabMessageBoxStateTest.js +++ b/test/unit/app/common/state/tabMessageBoxStateTest.js @@ -6,13 +6,21 @@ const sinon = require('sinon') const Immutable = require('immutable') const assert = require('assert') -const defaultAppState = Immutable.fromJS({ +const defaultTabId = 1 + +const sampleAppState = { windows: [{ windowId: 1, windowUUID: 'uuid' }], - tabs: [] -}) + tabs: [], + tabsInternal: { + index: {} + } +} +sampleAppState.tabsInternal.index[defaultTabId] = 0 + +const defaultAppState = Immutable.fromJS(sampleAppState) const exampleMessageBox = Immutable.fromJS({ message: 'example message', @@ -22,8 +30,6 @@ const exampleMessageBox = Immutable.fromJS({ showSuppress: true }) -const defaultTabId = 1 - const defaultTab = Immutable.fromJS({ tabId: defaultTabId, windowId: 1, diff --git a/test/unit/app/common/state/tabStateTest.js b/test/unit/app/common/state/tabStateTest.js index 08da619a21..03c3f32f9e 100644 --- a/test/unit/app/common/state/tabStateTest.js +++ b/test/unit/app/common/state/tabStateTest.js @@ -11,14 +11,29 @@ const defaultAppState = Immutable.fromJS({ otherProp: true }) -const shouldValidateId = function (cb) { +const twoTabsAppState = defaultAppState + .set('tabs', Immutable.fromJS([ + { tabId: 1, windowId: 1, frameKey: 2 }, + { tabId: 2, windowId: 1, frameKey: 1 } + ])) + .set('tabsInternal', Immutable.fromJS({ + index: { + 1: 0, + 2: 1 + } + })) + +// NOTE: null check can be optional since resolveTabId sets a default if null +const shouldValidateId = function (cb, skipNullCheck) { it('throws an AssertionError if tabId is not a number', function () { - assert.throws( - () => { - cb(null) - }, - AssertionError - ) + if (!skipNullCheck) { + assert.throws( + () => { + cb(null) + }, + AssertionError + ) + } assert.throws( () => { cb('b') @@ -180,48 +195,9 @@ const shouldValidateAction = function (cb) { } describe('tabState unit tests', function () { - describe('getTabIndexByTabId', function () { - before(function () { - this.appState = defaultAppState.set('tabs', Immutable.fromJS([ - { tabId: 2 }, - { tabId: 3 }, - { tabId: 1 } - ])) - }) - - it('returns the index of the tab for the tabId', function () { - assert.equal(tabState.getTabIndexByTabId(this.appState, 1), 2) - assert.equal(tabState.getTabIndexByTabId(this.appState, 2), 0) - assert.equal(tabState.getTabIndexByTabId(this.appState, 3), 1) - }) - - it('returns -1 if the tabId does not exist', function () { - assert.equal(tabState.getTabIndexByTabId(this.appState, 4), -1) - }) - - shouldValidateId((tabId) => { - tabState.getTabIndexByTabId(defaultAppState, tabId) - }) - - shouldValidateTabState((state) => { - tabState.getTabIndexByTabId(state, 1) - }) - }) - describe('getByTabId', function () { before(function () { - this.appState = defaultAppState.set('tabs', Immutable.fromJS([ - { - windowId: 1, - frameKey: 1, - tabId: 2 - }, - { - windowId: 1, - frameKey: 2, - tabId: 1 - } - ])) + this.appState = twoTabsAppState }) it('returns the tab for `tabId` if it exists', function () { @@ -239,7 +215,7 @@ describe('tabState unit tests', function () { shouldValidateId((tabId) => { tabState.getByTabId(defaultAppState, tabId) - }) + }, true) shouldValidateTabState((state) => { tabState.getByTabId(state, 1) @@ -248,14 +224,11 @@ describe('tabState unit tests', function () { describe('removeTabByTabId', function () { before(function () { - this.appState = defaultAppState.set('tabs', Immutable.fromJS([ - { tabId: 1 }, - { tabId: 2 } - ])) + this.appState = twoTabsAppState }) it('returns a new immutable state with the tab for `tabId` removed if it exists', function () { - assert.deepEqual(tabState.removeTabByTabId(this.appState, 2).get('tabs').toJS(), [ {tabId: 1} ]) + assert.deepEqual(tabState.removeTabByTabId(this.appState, 2).get('tabs').toJS(), [twoTabsAppState.getIn(['tabs', 0]).toJS()]) }) it('returns the state unmodified if the tab for `tabId` does not exist', function () { @@ -273,14 +246,11 @@ describe('tabState unit tests', function () { describe('removeTabByIndex', function () { before(function () { - this.appState = defaultAppState.set('tabs', Immutable.fromJS([ - { tabId: 1 }, - { tabId: 2 } - ])) + this.appState = twoTabsAppState }) it('returns a new immutable state with the tab at `index` removed if it exists', function () { - assert.deepEqual(tabState.removeTabByIndex(this.appState, 1).get('tabs').toJS(), [ {tabId: 1} ]) + assert.deepEqual(tabState.removeTabByIndex(this.appState, 1).get('tabs').toJS(), [ twoTabsAppState.getIn(['tabs', 0]).toJS() ]) }) it('returns the state unmodified if `index` is out of bounds', function () { @@ -324,9 +294,9 @@ describe('tabState unit tests', function () { describe('insertTab', function () { before(function () { - this.appState = defaultAppState.set('tabs', Immutable.fromJS([ - { tabId: 1 } - ])) + this.appState = defaultAppState + .set('tabs', Immutable.fromJS([ { tabId: 1 } ])) + .set('tabsInternal', Immutable.fromJS({ index: { 1: 0 } })) }) it('returns a new immutable state with the tabValue appended to the end of the list', function () { @@ -359,22 +329,26 @@ describe('tabState unit tests', function () { describe('updateTab', function () { before(function () { - this.appState = defaultAppState.set('tabs', Immutable.fromJS([ - { - windowId: 1, - frameKey: 1, - tabId: 1, - myProp: 'test1', - myProp2: 'blah' - }, - { - windowId: 1, - frameKey: 1, - tabId: 2, - myProp: 'test2', - myProp2: 'blah' - } - ])) + this.appState = defaultAppState + .set('tabs', Immutable.fromJS([ + { + windowId: 1, + frameKey: 1, + tabId: 1, + myProp: 'test1', + myProp2: 'blah' + }, + { + windowId: 1, + frameKey: 1, + tabId: 2, + myProp: 'test2', + myProp2: 'blah' + } + ])) + .set('tabsInternal', Immutable.fromJS({ + index: { 1: 0, 2: 1 } + })) }) it('returns a new immutable state with the tabValue updated if it already exists', function () { @@ -495,9 +469,13 @@ describe('tabState unit tests', function () { describe('maybeCreateTab', function () { before(function () { - this.appState = defaultAppState.set('tabs', Immutable.fromJS([ - { tabId: 1 } - ])) + this.appState = defaultAppState + .set('tabs', Immutable.fromJS([ + { tabId: 1 } + ])) + .set('tabsInternal', Immutable.fromJS({ + index: { 1: 0 } + })) }) it('returns a new immutable state with the tabValue appended to the end of the list if it does not already exist', function () { @@ -673,11 +651,15 @@ describe('tabState unit tests', function () { describe('getTabPropertyByTabId', function () { before(function () { - this.appState = defaultAppState.set('tabs', Immutable.fromJS([ - { tabId: 1, prop1: 'test1', prop2: 'test2' }, - { tabId: 2, prop1: 'test3' }, - { tabId: 3, prop2: 'test4' } - ])) + this.appState = defaultAppState + .set('tabs', Immutable.fromJS([ + { tabId: 1, prop1: 'test1', prop2: 'test2' }, + { tabId: 2, prop1: 'test3' }, + { tabId: 3, prop2: 'test4' } + ])) + .set('tabsInternal', Immutable.fromJS({ + index: { 1: 0, 2: 1, 3: 2 } + })) }) it('returns the value for `tabId`.`key`', function () { diff --git a/test/unit/app/renderer/components/messageBoxTest.js b/test/unit/app/renderer/components/messageBoxTest.js index 3254e66ba2..8775c2e1c5 100644 --- a/test/unit/app/renderer/components/messageBoxTest.js +++ b/test/unit/app/renderer/components/messageBoxTest.js @@ -33,7 +33,12 @@ let appState = Immutable.fromJS({ windowUUID: 'uuid', url: 'https://brave.com', messageBoxDetail: detail1 - }] + }], + tabsInternal: { + index: { + 1: 0 + } + } }) describe('MessageBox component unit tests', function () { diff --git a/test/unit/app/renderer/components/navigation/navigatorTest.js b/test/unit/app/renderer/components/navigation/navigatorTest.js index d4b128c95f..7f007e79ad 100644 --- a/test/unit/app/renderer/components/navigation/navigatorTest.js +++ b/test/unit/app/renderer/components/navigation/navigatorTest.js @@ -39,6 +39,11 @@ const appStoreRenderer = Immutable.fromJS({ canGoForward: true, windowId: 1 }], + tabsInternal: { + index: { + 1: 0 + } + }, windows: [] }) diff --git a/test/unit/app/renderer/reducers/urlBarReducerTest.js b/test/unit/app/renderer/reducers/urlBarReducerTest.js index 1a5d7ec787..1baa6dd982 100644 --- a/test/unit/app/renderer/reducers/urlBarReducerTest.js +++ b/test/unit/app/renderer/reducers/urlBarReducerTest.js @@ -40,7 +40,18 @@ const windowState = Immutable.fromJS({ }, { key: 3, location: 'about:newtab' - }] + }], + framesInternal: { + index: { + 1: 0, + 2: 1, + 3: 2 + }, + tabIndex: { + 1: 0, + 2: 1 + } + } }) const fakeAppStoreRenderer = { @@ -210,7 +221,15 @@ describe('urlBarReducer', function () { }, { key: 3, location: 'about:newtab' - }] + }], + framesInternal: { + index: { + 2: 1 + }, + tabIndex: { + 2: 1 + } + } }) before(function () { diff --git a/test/unit/js/stores/windowStoreTest.js b/test/unit/js/stores/windowStoreTest.js index 0704ae46eb..0e8cde0dfc 100644 --- a/test/unit/js/stores/windowStoreTest.js +++ b/test/unit/js/stores/windowStoreTest.js @@ -76,10 +76,10 @@ describe('Window store unit tests', function () { // call doAction for WINDOW_WEBVIEW_LOAD_START doAction({ actionType: windowConstants.WINDOW_WEBVIEW_LOAD_START, - frameProps: { + frameProps: Immutable.fromJS({ tabId: 0, key: 0 - } + }) }) // get the updated windowState (AFTER doAction runs) diff --git a/test/unit/state/frameStateUtilTest.js b/test/unit/state/frameStateUtilTest.js index c18b098f2e..5ff48fee26 100644 --- a/test/unit/state/frameStateUtilTest.js +++ b/test/unit/state/frameStateUtilTest.js @@ -94,45 +94,68 @@ describe('frameStateUtil', function () { }) describe('removeFrame', function () { - let frames, closedFrames, frameProps, activeFrameKey, framePropsIndex + let state, frameProps, activeFrameKey, framePropsIndex beforeEach(function () { - frames = Immutable.fromJS([ - { key: 2 }, - { key: 3, parentFrameKey: 2 }, - { key: 4 }, - { key: 4, pinnedLocation: 'https://www.facebook.com/' }, - { key: 5, pinnedLocation: 'https://twitter.com/' } - ]) - closedFrames = Immutable.fromJS([{ key: 1 }]) frameProps = Immutable.fromJS({ key: 2 }) activeFrameKey = 2 framePropsIndex = 0 + + state = Immutable.fromJS({ + activeFrameKey: activeFrameKey, + frames: [ + { key: 2 }, + { key: 3, parentFrameKey: 2 }, + { key: 4, pinnedLocation: 'https://www.facebook.com/' }, + { key: 5, pinnedLocation: 'https://twitter.com/' } + ], + closedFrames: [ + { key: 1 } + ], + framesInternal: { + index: { + 2: 0, + 3: 1, + 4: 2, + 5: 3 + } + } + }) }) it('removed frame is added to `closedFrames`', function () { - const result = frameStateUtil.removeFrame(frames, closedFrames, frameProps, activeFrameKey, framePropsIndex) + const result = frameStateUtil.removeFrame(state, frameProps, activeFrameKey, framePropsIndex) const inClosedFrames = result.closedFrames.find((frame) => frame.get('key') === frameProps.get('key')) assert.equal(false, inClosedFrames === undefined) }) it('sets isFullScreen=false for the removed frame', function () { frameProps = Immutable.fromJS({ key: 2, isFullScreen: true }) - const result = frameStateUtil.removeFrame(frames, closedFrames, frameProps, activeFrameKey, framePropsIndex) + const result = frameStateUtil.removeFrame(state, frameProps, activeFrameKey, framePropsIndex) const inClosedFrames = result.closedFrames.find((frame) => frame.get('key') === frameProps.get('key')) assert.equal(false, inClosedFrames.get('isFullScreen')) }) it('removed frame is NOT added to `closedFrames` if private', function () { - frames = Immutable.fromJS([{ key: 2 }]) + let data = Immutable.fromJS({ + frames: [ + { key: 2 } + ], + framesInternal: { + index: { + 2: 0 + } + } + }) + const newState = state.merge(data) frameProps = Immutable.fromJS({ isPrivate: true, key: 2 }) - const result = frameStateUtil.removeFrame(frames, closedFrames, frameProps, activeFrameKey, framePropsIndex) + const result = frameStateUtil.removeFrame(newState, frameProps, activeFrameKey, framePropsIndex) const inClosedFrames = result.closedFrames.find((frame) => frame.get('key') === frameProps.get('key')) assert.equal(true, inClosedFrames === undefined) }) it('removes the frame from `frames`', function () { - const result = frameStateUtil.removeFrame(frames, closedFrames, frameProps, activeFrameKey, framePropsIndex) + const result = frameStateUtil.removeFrame(state, frameProps, activeFrameKey, framePropsIndex) const inFrames = result.frames.find((frame) => frame.get('key') === frameProps.get('key')) assert.equal(true, inFrames === undefined) }) @@ -140,19 +163,29 @@ describe('frameStateUtil', function () { describe('does not change `activeFrameKey`', function () { it('if frame removed is not active and has parentFrameKey set', function () { frameProps = Immutable.fromJS({ key: 3 }) - const result = frameStateUtil.removeFrame(frames, closedFrames, frameProps, activeFrameKey, framePropsIndex) + const result = frameStateUtil.removeFrame(state, frameProps, activeFrameKey, framePropsIndex) assert.equal(activeFrameKey, result.activeFrameKey) }) it('if frame removed is not active and does NOT have parentFrameKey set', function () { frameProps = Immutable.fromJS({ key: 4 }) - const result = frameStateUtil.removeFrame(frames, closedFrames, frameProps, activeFrameKey, framePropsIndex) + const result = frameStateUtil.removeFrame(state, frameProps, activeFrameKey, framePropsIndex) assert.equal(activeFrameKey, result.activeFrameKey) }) it('if there are no frames left', function () { - frames = Immutable.fromJS([{ key: 2 }]) - const result = frameStateUtil.removeFrame(frames, closedFrames, frameProps, activeFrameKey, framePropsIndex) + let data = Immutable.fromJS({ + frames: [ + { key: 2 } + ], + framesInternal: { + index: { + 2: 0 + } + } + }) + const newState = state.merge(data) + const result = frameStateUtil.removeFrame(newState, frameProps, activeFrameKey, framePropsIndex) assert.equal(activeFrameKey, result.activeFrameKey) }) }) @@ -160,22 +193,34 @@ describe('frameStateUtil', function () { describe('when active frame is removed', function () { describe('returns the next *non-pinned* active frame key', function () { beforeEach(function () { - frames = Immutable.fromJS([ - {key: 2, lastAccessedTime: 1484075990}, - {key: 3, pinnedLocation: 'https://www.facebook.com/', lastAccessedTime: 1484075999}, - {key: 4, parentFrameKey: 3, lastAccessedTime: 148407595}, - {key: 5, lastAccessedTime: 1484075960}, - {key: 6, lastAccessedTime: 1484075950} - ]) activeFrameKey = 4 frameProps = Immutable.fromJS({key: 4}) framePropsIndex = 2 + + state = Immutable.fromJS({ + activeFrameKey: activeFrameKey, + frames: [ + {key: 2, lastAccessedTime: 1484075990}, + {key: 3, pinnedLocation: 'https://www.facebook.com/', lastAccessedTime: 1484075999}, + {key: 4, parentFrameKey: 3, lastAccessedTime: 148407595}, + {key: 5, lastAccessedTime: 1484075960}, + {key: 6, lastAccessedTime: 1484075950} + ], + closedFrames: [], + framesInternal: { + index: { + 2: 0, + 3: 1, + 4: 2, + 5: 3, + 6: 4 + } + } + }) }) it('parent tab action', function () { - const result = frameStateUtil.removeFrame( - frames, - closedFrames, + const result = frameStateUtil.removeFrame(state, frameProps, activeFrameKey, framePropsIndex @@ -184,9 +229,7 @@ describe('frameStateUtil', function () { }) it('next tab action', function () { - const result = frameStateUtil.removeFrame( - frames, - closedFrames, + const result = frameStateUtil.removeFrame(state, frameProps, activeFrameKey, framePropsIndex, @@ -196,9 +239,7 @@ describe('frameStateUtil', function () { }) it('last active tab action', function () { - const result = frameStateUtil.removeFrame( - frames, - closedFrames, + const result = frameStateUtil.removeFrame(state, frameProps, activeFrameKey, framePropsIndex, @@ -209,12 +250,6 @@ describe('frameStateUtil', function () { }) it('returns the previous *non-pinned* frame key (if none found for next and no parent association)', function () { - frames = Immutable.fromJS([ - { key: 2 }, - { key: 3 }, - { key: 4, pinnedLocation: 'https://www.facebook.com/' }, - { key: 5, pinnedLocation: 'https://twitter.com/' } - ]) frameProps = Immutable.fromJS({ isFullScreen: false, isPrivate: false, @@ -222,28 +257,70 @@ describe('frameStateUtil', function () { }) activeFrameKey = 3 framePropsIndex = 1 - const result = frameStateUtil.removeFrame(frames, closedFrames, frameProps, activeFrameKey, framePropsIndex) + + state = Immutable.fromJS({ + activeFrameKey: activeFrameKey, + frames: [ + { key: 2 }, + { key: 3 }, + { key: 4, pinnedLocation: 'https://www.facebook.com/' }, + { key: 5, pinnedLocation: 'https://twitter.com/' } + ], + closedFrames: [], + framesInternal: { + index: { + 2: 0, + 3: 1, + 4: 2, + 5: 3 + } + } + }) + + const result = frameStateUtil.removeFrame(state, frameProps, activeFrameKey, framePropsIndex) assert.equal(2, result.activeFrameKey) }) describe('when only pinned tabs remaining', function () { it('defaults to next index if there are tabs to right', function () { - frames = Immutable.fromJS([ - { key: 2 }, - { pinnedLocation: 'https://www.facebook.com/', key: 4 } - ]) framePropsIndex = 0 - const result = frameStateUtil.removeFrame(frames, closedFrames, frameProps, activeFrameKey, framePropsIndex) + state = Immutable.fromJS({ + activeFrameKey: activeFrameKey, + frames: [ + { key: 2 }, + { key: 4, pinnedLocation: 'https://www.facebook.com/' } + ], + closedFrames: [], + framesInternal: { + index: { + 2: 0, + 4: 1 + } + } + }) + + const result = frameStateUtil.removeFrame(state, frameProps, activeFrameKey, framePropsIndex) assert.equal(4, result.activeFrameKey) }) it('defaults to previous if no tabs to right', function () { - frames = Immutable.fromJS([ - { pinnedLocation: 'https://www.facebook.com/', key: 6 }, - { key: 2 } - ]) framePropsIndex = 1 - const result = frameStateUtil.removeFrame(frames, closedFrames, frameProps, activeFrameKey, framePropsIndex) + state = Immutable.fromJS({ + activeFrameKey: activeFrameKey, + frames: [ + { key: 6, pinnedLocation: 'https://www.facebook.com/' }, + { key: 2 } + ], + closedFrames: [], + framesInternal: { + index: { + 6: 0, + 2: 1 + } + } + }) + + const result = frameStateUtil.removeFrame(state, frameProps, activeFrameKey, framePropsIndex) assert.equal(6, result.activeFrameKey) }) }) @@ -266,39 +343,67 @@ describe('frameStateUtil', function () { }) describe('getFrameByLastAccessedTime', function () { - let framesWithLastAccessedTime, framesWithoutLastAccessedTime, framesWithNullifiedLastAccessedTime + let stateWithLastAccessedTime, stateWithoutLastAccessedTime, stateWithNullifiedLastAccessedTime beforeEach(function () { - framesWithLastAccessedTime = Immutable.fromJS([ - { key: 2, lastAccessedTime: null }, - { key: 3, lastAccessedTime: 1488184050731 }, - { key: 4, lastAccessedTime: 1488184050711 }, - { key: 5 } - ]) - framesWithoutLastAccessedTime = Immutable.fromJS([ - { key: 2 }, - { key: 3 }, - { key: 4 } - ]) - framesWithNullifiedLastAccessedTime = Immutable.fromJS([ - { key: 2, lastAccessedTime: null }, - { key: 3, lastAccessedTime: null }, - { key: 4, lastAccessedTime: null } - ]) + stateWithLastAccessedTime = Immutable.fromJS({ + frames: [ + { key: 2, lastAccessedTime: null }, + { key: 3, lastAccessedTime: 1488184050731 }, + { key: 4, lastAccessedTime: 1488184050711 }, + { key: 5 } + ], + framesInternal: { + index: { + 2: 0, + 3: 1, + 4: 2, + 5: 3 + } + } + }) + stateWithoutLastAccessedTime = Immutable.fromJS({ + frames: [ + { key: 2 }, + { key: 3 }, + { key: 4 } + ], + framesInternal: { + index: { + 2: 0, + 3: 1, + 4: 2 + } + } + }) + stateWithNullifiedLastAccessedTime = Immutable.fromJS({ + frames: [ + { key: 2, lastAccessedTime: null }, + { key: 3, lastAccessedTime: null }, + { key: 4, lastAccessedTime: null } + ], + framesInternal: { + index: { + 2: 0, + 3: 1, + 4: 2 + } + } + }) }) it('gets correct frame by last accessed time', function () { - const result = frameStateUtil.getFrameByLastAccessedTime(framesWithLastAccessedTime) + const result = frameStateUtil.getFrameByLastAccessedTime(stateWithLastAccessedTime) assert.equal(1, result) }) it('returns -1 for frames without last accessed time', function () { - const result = frameStateUtil.getFrameByLastAccessedTime(framesWithoutLastAccessedTime) + const result = frameStateUtil.getFrameByLastAccessedTime(stateWithoutLastAccessedTime) assert.equal(-1, result) }) it('returns -1 for frames with nullified last accessed time', function () { - const result = frameStateUtil.getFrameByLastAccessedTime(framesWithNullifiedLastAccessedTime) + const result = frameStateUtil.getFrameByLastAccessedTime(stateWithNullifiedLastAccessedTime) assert.equal(-1, result) }) }) diff --git a/tools/cibuild.py b/tools/cibuild.py index 7e37a4db18..4ed1deda17 100755 --- a/tools/cibuild.py +++ b/tools/cibuild.py @@ -4,7 +4,7 @@ import subprocess import sys import os.path -MUON_VERSION = '3.0.100' +MUON_VERSION = '3.0.102' CHROMEDRIVER_VERSION = '2.27' SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) TARGET_ARCH= os.environ['TARGET_ARCH'] if os.environ.has_key('TARGET_ARCH') else 'x64'