From 525e7725a2cf27aefef2aaff492678fe09c7ab77 Mon Sep 17 00:00:00 2001 From: NejcZdovc Date: Fri, 23 Jun 2017 15:15:03 +0200 Subject: [PATCH] Converts Main into redux component Resolves #9452 Auditors: @bbondy @bridiver @bsclifton Test Plan: --- app/browser/menu.js | 2 +- app/renderer/components/frame/frame.js | 20 +- app/renderer/components/main/main.js | 329 +++++++++++-------------- app/renderer/components/window.js | 3 +- app/renderer/reducers/frameReducer.js | 18 +- docs/windowActions.md | 36 ++- js/actions/windowActions.js | 60 +++-- js/constants/windowConstants.js | 4 +- js/contextMenus.js | 2 +- js/state/frameStateUtil.js | 9 +- js/stores/eventStore.js | 4 +- js/stores/windowStore.js | 20 +- 12 files changed, 271 insertions(+), 236 deletions(-) diff --git a/app/browser/menu.js b/app/browser/menu.js index 86db4f6e1e6..3cd129fd3be 100644 --- a/app/browser/menu.js +++ b/app/browser/menu.js @@ -619,7 +619,7 @@ const doAction = (action) => { switch (action.actionType) { case windowConstants.WINDOW_SET_FOCUSED_FRAME: // Update the checkbox next to "Bookmark Page" (Bookmarks menu) - currentLocation = action.frameProps.get('location') + currentLocation = action.location setMenuItemChecked(locale.translation('bookmarkPage'), isCurrentLocationBookmarked()) break case appConstants.APP_CHANGE_SETTING: diff --git a/app/renderer/components/frame/frame.js b/app/renderer/components/frame/frame.js index 4c2c4cbb480..22cad8d2e67 100644 --- a/app/renderer/components/frame/frame.js +++ b/app/renderer/components/frame/frame.js @@ -203,9 +203,9 @@ class Frame extends React.Component { } } - onPropsChanged (prevProps = {}) { + onPropsChanged () { if (this.props.isActive && isFocused()) { - windowActions.setFocusedFrame(this.frame) + windowActions.setFocusedFrame(this.props.location, this.props.tabId) } } @@ -270,7 +270,7 @@ class Frame extends React.Component { this.lastFrame = this.frame.delete('lastAccessedTime') const cb = (prevProps = {}) => { - this.onPropsChanged(prevProps) + this.onPropsChanged() if (this.props.isActive && !prevProps.isActive && !this.props.urlBarFocused) { this.webview.focus() } @@ -469,7 +469,7 @@ class Frame extends React.Component { return } if (e.details[0] === 'javascript' && e.details[1]) { - windowActions.setBlockedBy(this.frame, 'noScript', e.details[1]) + windowActions.setBlockedBy(this.props.frameKey, 'noScript', e.details[1]) } if (e.details[0] === 'autoplay') { appActions.autoplayBlocked(this.props.tabId) @@ -550,7 +550,7 @@ class Frame extends React.Component { } method = (detail) => { const description = [detail.type, detail.scriptUrl || this.props.provisionalLocation].join(': ') - windowActions.setBlockedBy(this.frame, 'fingerprintingProtection', description) + windowActions.setBlockedBy(this.props.frameKey, 'fingerprintingProtection', description) } break case messages.THEME_COLOR_COMPUTED: @@ -648,7 +648,7 @@ class Frame extends React.Component { if (url.startsWith(pdfjsOrigin)) { let displayLocation = UrlUtil.getLocationIfPDF(url) - windowActions.setSecurityState(this.frame, { + windowActions.setSecurityState(this.props.frameKey, { secure: urlParse(displayLocation).protocol === 'https:', runInsecureContent: false }) @@ -713,7 +713,7 @@ class Frame extends React.Component { // connection. isSecure = this.props.isSecure !== false ? 1 : false } - windowActions.setSecurityState(this.frame, { + windowActions.setSecurityState(this.props.frameKey, { secure: runInsecureContent ? false : isSecure, runInsecureContent }) @@ -783,15 +783,15 @@ class Frame extends React.Component { if (this.frame.isEmpty()) { return } - windowActions.setFullScreen(this.frame, true, true) + windowActions.setFullScreen(this.props.frameKey, true, true) // disable the fullscreen warning after 5 seconds - setTimeout(windowActions.setFullScreen.bind(this, this.frame, undefined, false), 5000) + setTimeout(windowActions.setFullScreen.bind(this, this.props.frameKey, undefined, false), 5000) }) this.webview.addEventListener('leave-html-full-screen', () => { if (this.frame.isEmpty()) { return } - windowActions.setFullScreen(this.frame, false) + windowActions.setFullScreen(this.props.frameKey, false) }) this.webview.addEventListener('media-started-playing', ({title}) => { if (this.frame.isEmpty()) { diff --git a/app/renderer/components/main/main.js b/app/renderer/components/main/main.js index 5760814cc99..688eac42f20 100644 --- a/app/renderer/components/main/main.js +++ b/app/renderer/components/main/main.js @@ -3,7 +3,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ const React = require('react') -const ImmutableComponent = require('../immutableComponent') +const ReduxComponent = require('../reduxComponent') const Immutable = require('immutable') const electron = require('electron') const urlResolve = require('url').resolve @@ -55,7 +55,6 @@ const siteUtil = require('../../../../js/state/siteUtil') const searchProviders = require('../../../../js/data/searchProviders') const defaultBrowserState = require('../../../common/state/defaultBrowserState') const shieldState = require('../../../common/state/shieldState') -const siteSettingsState = require('../../../common/state/siteSettingsState') const menuBarState = require('../../../common/state/menuBarState') const windowState = require('../../../common/state/windowState') const updateState = require('../../../common/state/updateState') @@ -64,14 +63,13 @@ const updateState = require('../../../common/state/updateState') const _ = require('underscore') const cx = require('../../../../js/lib/classSet') const eventUtil = require('../../../../js/lib/eventUtil') -const siteSettings = require('../../../../js/state/siteSettings') const {isSourceAboutUrl} = require('../../../../js/lib/appUrlUtil') const {getCurrentWindowId, isMaximized, isFocused, isFullScreen} = require('../../currentWindow') const {isDarwin, isWindows, isLinux} = require('../../../common/lib/platformUtil') -class Main extends ImmutableComponent { - constructor () { - super() +class Main extends React.Component { + constructor (props) { + super(props) this.onMouseDown = this.onMouseDown.bind(this) this.onClickWindow = this.onClickWindow.bind(this) this.onHideSiteInfo = this.onHideSiteInfo.bind(this) @@ -112,10 +110,9 @@ class Main extends ImmutableComponent { this.keydownHistory = [] } - registerCustomTitlebarHandlers () { - if (this.customTitlebar.enabled) { + registerCustomTitleBarHandlers () { + if (this.props.showCustomTitleBar) { document.addEventListener('keyup', (e) => { - const customTitlebar = this.customTitlebar switch (e.which) { case keyCodes.LEFT: case keyCodes.RIGHT: @@ -143,7 +140,7 @@ class Main extends ImmutableComponent { if (getSetting(settings.AUTO_HIDE_MENU)) { windowActions.toggleMenubarVisible(null) } else { - if (customTitlebar.menubarSelectedIndex) { + if (this.props.menubarSelectedIndex) { windowActions.setMenuBarSelectedIndex() windowActions.setContextMenuDetail() } else { @@ -152,12 +149,12 @@ class Main extends ImmutableComponent { } break case keyCodes.ESC: - if (getSetting(settings.AUTO_HIDE_MENU) && customTitlebar.menubarVisible && !customTitlebar.menubarSelectedIndex) { + if (getSetting(settings.AUTO_HIDE_MENU) && this.props.menubarVisible && !this.props.menubarSelectedIndex) { e.preventDefault() windowActions.toggleMenubarVisible(false) break } - if (customTitlebar.menubarSelectedIndex) { + if (this.props.menubarSelectedIndex) { e.preventDefault() windowActions.setMenuBarSelectedIndex() windowActions.setContextMenuDetail() @@ -174,7 +171,7 @@ class Main extends ImmutableComponent { delete this.keydown[e.which] }) - document.addEventListener('focus', (e) => { + document.addEventListener('focus', () => { let selector = document.activeElement.id ? '#' + document.activeElement.id : null @@ -192,15 +189,13 @@ class Main extends ImmutableComponent { } exitFullScreen () { - const activeFrame = frameStateUtil.getActiveFrame(this.props.windowState) - if (activeFrame && activeFrame.get('isFullScreen')) { - windowActions.setFullScreen(activeFrame, false) + if (this.props.isFullScreen) { + windowActions.setFullScreen(this.props.activeFrameKey, false) } } registerSwipeListener () { // Navigates back/forward on macOS two- and or three-finger swipe - let mouseInFrame = false let trackingFingers = false let startTime = 0 let isSwipeOnLeftEdge = false @@ -211,8 +206,7 @@ class Main extends ImmutableComponent { // isSwipeTrackingFromScrollEventsEnabled is only true if "two finger scroll to swipe" is enabled ipc.on('scroll-touch-begin', () => { - mouseInFrame = this.props.windowState.getIn(['ui', 'mouseInFrame']) - if (mouseInFrame) { + if (this.props.mouseInTitlebar) { trackingFingers = true startTime = (new Date()).getTime() } @@ -257,7 +251,7 @@ class Main extends ImmutableComponent { }) const throttledSwipe = _.throttle(direction => { - if (mouseInFrame) { + if (this.props.mouseInTitlebar) { if (direction === 'left') { ipc.emit(messages.SHORTCUT_ACTIVE_FRAME_BACK) } else if (direction === 'right') { @@ -288,41 +282,38 @@ class Main extends ImmutableComponent { } componentWillUpdate (nextProps) { - if (!this.props.appState.getIn([appConfig.resourceNames.WIDEVINE, 'ready']) && - nextProps.appState.getIn([appConfig.resourceNames.WIDEVINE, 'ready'])) { - const widevinePanelDetail = this.props.windowState.get('widevinePanelDetail') + if (!this.props.isWidevineReady && nextProps.isWidevineReady) { // User may have enabled from preferences and no details are present - if (!widevinePanelDetail) { + if (this.props.widevineLocation) { return } - const origin = siteUtil.getOrigin(widevinePanelDetail.get('location')) + // This automatically handles reloading the frame as well - appActions.changeSiteSetting(origin, appConfig.resourceNames.WIDEVINE, widevinePanelDetail.get('alsoAddRememberSiteSetting') ? 1 : 0) + appActions.changeSiteSetting( + this.props.widevineLocation, + appConfig.resourceNames.WIDEVINE, + this.props.widevineRememberSettings + ) } } componentDidUpdate (prevProps) { this.loadSearchProviders() - const activeFrame = frameStateUtil.getActiveFrame(this.props.windowState) - const activeFramePrev = frameStateUtil.getActiveFrame(prevProps.windowState) - const activeFrameTitle = (activeFrame && (activeFrame.get('title') || activeFrame.get('location'))) || '' - const activeFramePrevTitle = (activeFramePrev && (activeFramePrev.get('title') || activeFramePrev.get('location'))) || '' - if (activeFrameTitle !== activeFramePrevTitle) { - windowActions.shouldSetTitle(getCurrentWindowId(), activeFrameTitle) + if (prevProps.title !== this.props.title) { + windowActions.shouldSetTitle(getCurrentWindowId(), this.props.title) } // If the tab changes or was closed, exit out of full screen to give a better // picture of what's happening. - if (activeFramePrev && activeFrame && - activeFrame.get('key') !== activeFramePrev.get('key') && activeFramePrev.get('isFullScreen')) { - windowActions.setFullScreen(activeFramePrev, false) + if (prevProps.activeFrameKey !== this.props.activeFrameKey && this.props.isFullScreen) { + windowActions.setFullScreen(this.props.activeFrameKey, false) } } componentDidMount () { this.registerSwipeListener() this.registerWindowLevelShortcuts() - this.registerCustomTitlebarHandlers() + this.registerCustomTitleBarHandlers() // DO NOT ADD TO THIS LIST // ipc.on is deprecated and should be replaced by actions/reducers @@ -346,9 +337,8 @@ class Main extends ImmutableComponent { }) ipc.on(messages.OPEN_BRAVERY_PANEL, () => { - const activeFrame = frameStateUtil.getActiveFrame(self.props.windowState) - if (shieldState.braveShieldsEnabled(activeFrame)) { - this.onBraveMenu() + if (this.props.braveShieldEnabled) { + windowActions.setBraveryPanelDetail({}) } else { appActions.maybeCreateTabRequested({ url: 'about:preferences#shields' @@ -366,30 +356,20 @@ class Main extends ImmutableComponent { })) }) - ipc.on(messages.SHORTCUT_CLOSE_FRAME, (e, i) => { - const frame = i == null - ? frameStateUtil.getActiveFrame(this.props.windowState) - : frameStateUtil.getFrameByKey(self.props.windowState, i) - if (frame) { - appActions.tabCloseRequested(frame.get('tabId')) + ipc.on(messages.SHORTCUT_CLOSE_FRAME, (e, tabId) => { + if (tabId == null) { + tabId = this.props.tabId + } + + if (tabId) { + appActions.tabCloseRequested(tabId) } }) + ipc.on(messages.SHORTCUT_UNDO_CLOSED_FRAME, () => windowActions.undoClosedFrame()) ipc.on(messages.SHORTCUT_CLOSE_OTHER_FRAMES, (e, key, isCloseRight, isCloseLeft) => { - const currentIndex = frameStateUtil.getFrameIndex(self.props.windowState, key) - if (currentIndex === -1) { - return - } - - frameStateUtil.getFrames(self.props.windowState).forEach((frame, i) => { - if (!frame.get('pinnedLocation') && - ((i < currentIndex && isCloseLeft) || (i > currentIndex && isCloseRight))) { - if (frame) { - appActions.tabCloseRequested(frame.get('tabId')) - } - } - }) + windowActions.closeOtherFrames(key, isCloseRight, isCloseLeft) }) ipc.on(messages.SHOW_DOWNLOADS_TOOLBAR, () => { @@ -400,39 +380,20 @@ class Main extends ImmutableComponent { windowActions.setDownloadsToolbarVisible(false) }) - const self = this ipc.on(messages.BLOCKED_RESOURCE, (e, blockType, details) => { - const frameProps = frameStateUtil.getFrameByTabId(self.props.windowState, details.tabId) - frameProps && windowActions.setBlockedBy(frameProps, blockType, details.url) - }) - - ipc.on(messages.BLOCKED_PAGE, (e, blockType, details) => { - // const frameProps = frameStateUtil.getFrameByTabId(self.props.windowState, details.tabId) - // if (!frameProps) { - // return - // } + windowActions.setBlockedBy(this.props.activeFrameKey, blockType, details.url) }) ipc.on(messages.HTTPSE_RULE_APPLIED, (e, ruleset, details) => { - const frameProps = frameStateUtil.getFrameByTabId(self.props.windowState, details.tabId) - frameProps && windowActions.setRedirectedBy(frameProps, ruleset, details.url) + windowActions.setRedirectedBy(details.tabId, ruleset, details.url) }) ipc.on(messages.CERT_ERROR, (e, details) => { - const frame = frameStateUtil.getFrameByTabId(self.props.windowState, details.tabId) - if (frame && (frame.get('location') === details.url || - frame.get('provisionalLocation') === details.url)) { - windowActions.setFrameError(frame, { - url: details.url, - error: details.error - }) - appActions.loadURLRequested(frame.get('tabId'), 'about:certerror') - } + windowActions.onCertError(details.tabId, details.url, details.error) }) ipc.on(messages.SET_SECURITY_STATE, (e, frameKey, securityState) => { - windowActions.setSecurityState(frameStateUtil.getFrameByKey(self.props.windowState, frameKey), - securityState) + windowActions.setSecurityState(frameKey, securityState) }) ipc.on(messages.HIDE_CONTEXT_MENU, () => { @@ -448,14 +409,14 @@ class Main extends ImmutableComponent { this.loadSearchProviders() window.addEventListener('focus', () => { - const activeFrame = frameStateUtil.getActiveFrame(self.props.windowState) - windowActions.setFocusedFrame(activeFrame) + windowActions.setFocusedFrame(this.props.location, this.props.tabId) windowActions.onFocus(getCurrentWindowId()) // For whatever reason other elements are preserved but webviews are not. if (document.activeElement && document.activeElement.tagName === 'BODY') { webviewActions.setWebviewFocused() } }, { passive: true }) + windowActions.onFocus(getCurrentWindowId()) // disable dnd by default @@ -478,24 +439,17 @@ class Main extends ImmutableComponent { return false }, true) - const activeFrame = frameStateUtil.getActiveFrame(self.props.windowState) - if (activeFrame && activeFrame.get('title')) { - windowActions.shouldSetTitle(getCurrentWindowId(), activeFrame.get('title')) + if (this.props.title) { + windowActions.shouldSetTitle(getCurrentWindowId(), this.props.title) } + const self = this window.onblur = () => { self.resetAltMenuProcessing() windowActions.onBlur(getCurrentWindowId()) } } - onBraveMenu () { - const activeFrame = frameStateUtil.getActiveFrame(this.props.windowState) - if (shieldState.braveShieldsEnabled(activeFrame)) { - windowActions.setBraveryPanelDetail({}) - } - } - onHideSiteInfo () { windowActions.setSiteInfoVisible(false) } @@ -520,6 +474,7 @@ class Main extends ImmutableComponent { } node = node.parentNode } + // Hide context menus, popup menus, and menu selections windowActions.resetMenuState() } @@ -538,90 +493,91 @@ class Main extends ImmutableComponent { } onTabContextMenu (e) { - const activeFrame = frameStateUtil.getActiveFrame(this.props.windowState) - contextMenus.onTabsToolbarContextMenu(activeFrame.get('title'), activeFrame.get('location'), undefined, undefined, e) - } - - get allSiteSettings () { - const activeFrame = frameStateUtil.getActiveFrame(this.props.windowState) || Immutable.Map() - return siteSettingsState.getAllSiteSettings(this.props.appState, activeFrame.get('isPrivate')) - } - - frameSiteSettings (location) { - if (!location) { - return undefined - } - return siteSettings.getSiteSettingsForURL(this.allSiteSettings, location) + contextMenus.onTabsToolbarContextMenu(this.props.title, this.props.location, undefined, undefined, e) } - get customTitlebar () { - const menubarVisible = menuBarState.isMenuBarVisible(this.props.windowState) - const selectedIndex = this.props.windowState.getIn(['ui', 'contextMenu', 'selectedIndex']) - return { - enabled: isWindows(), - captionButtonsVisible: isWindows(), - menubarVisible: menubarVisible, - menubarSelectedIndex: this.props.windowState.getIn(['ui', 'menubar', 'selectedIndex']), - contextMenuSelectedIndex: typeof selectedIndex === 'object' && Array.isArray(selectedIndex) && selectedIndex.length > 0 - ? selectedIndex - : null, - isMaximized: isMaximized() || isFullScreen() - } - } - - render () { - // Sort frames by key so that the order of the frames do not change which could - // cause unexpected reloading when a user moves tabs. - // All frame operations work off of frame keys and not index though so unsorted frames - // can be passed everywhere other than the Frame elements. - const sortedFrames = frameStateUtil.getSortedFrames(this.props.windowState) - const activeFrame = frameStateUtil.getActiveFrame(this.props.windowState) - const nonPinnedFrames = frameStateUtil.getNonPinnedFrames(this.props.windowState) + mergeProps (state, ownProps) { + const currentWindow = state.get('currentWindow') + const activeFrame = frameStateUtil.getActiveFrame(currentWindow) || Immutable.Map() + const nonPinnedFrames = frameStateUtil.getNonPinnedFrames(currentWindow) const tabsPerPage = Number(getSetting(settings.TABS_PER_PAGE)) - const showBookmarksToolbar = getSetting(settings.SHOW_BOOKMARKS_TOOLBAR) - const siteInfoIsVisible = this.props.windowState.getIn(['ui', 'siteInfo', 'isVisible']) && !isSourceAboutUrl(activeFrame.get('location')) - const braveryPanelIsVisible = shieldState.braveShieldsEnabled(activeFrame) && - this.props.windowState.get('braveryPanelDetail') - const clearBrowsingDataPanelIsVisible = this.props.windowState.getIn(['ui', 'isClearBrowsingDataPanelVisible']) - const importBrowserDataPanelIsVisible = this.props.windowState.get('importBrowserDataDetail') - const widevinePanelIsVisible = this.props.windowState.getIn(['widevinePanelDetail', 'shown']) && !isLinux() - const autofillAddressPanelIsVisible = this.props.windowState.get('autofillAddressDetail') - const autofillCreditCardPanelIsVisible = this.props.windowState.get('autofillCreditCardDetail') - const noScriptIsVisible = this.props.windowState.getIn(['ui', 'noScriptInfo', 'isVisible']) && + const activeOrigin = !activeFrame.isEmpty() ? siteUtil.getOrigin(activeFrame.get('location')) : null + const widevinePanelDetail = currentWindow.get('widevinePanelDetail', Immutable.Map()) + const loginRequiredDetails = basicAuthState.getLoginRequiredDetail(state, activeFrame.get('tabId')) + + const props = {} + // used in renderer + props.isFullScreen = activeFrame.get('isFullScreen', false) + props.isMaximized = isMaximized() || isFullScreen() + props.captionButtonsVisible = isWindows() + props.showContextMenu = !!currentWindow.get('contextMenuDetail') + props.showPopupWindow = !!currentWindow.get('popupWindowDetail') + props.showSiteInfo = currentWindow.getIn(['ui', 'siteInfo', 'isVisible']) && + !isSourceAboutUrl(activeFrame.get('location')) + props.showBravery = shieldState.braveShieldsEnabled(activeFrame) && + !!currentWindow.get('braveryPanelDetail') + props.showClearData = !!currentWindow.getIn(['ui', 'isClearBrowsingDataPanelVisible']) + props.showImportData = !!currentWindow.get('importBrowserDataDetail') + props.showWidevine = currentWindow.getIn(['widevinePanelDetail', 'shown']) && !isLinux() + props.showAutoFillAddress = !!currentWindow.get('autofillAddressDetail') + props.showAutoFillCC = !!currentWindow.get('autofillCreditCardDetail') + props.showLogin = !!loginRequiredDetails + props.showBookmarkHanger = currentWindow.get('bookmarkDetail') && + !currentWindow.getIn(['bookmarkDetail', 'isBookmarkHanger']) + props.showNoScript = currentWindow.getIn(['ui', 'noScriptInfo', 'isVisible']) && siteUtil.getOrigin(activeFrame.get('location')) - const releaseNotesIsVisible = this.props.windowState.getIn(['ui', 'releaseNotes', 'isVisible']) - const checkDefaultBrowserDialogIsVisible = - isFocused() && defaultBrowserState.shouldDisplayDialog(this.props.appState) - const loginRequiredDetails = activeFrame - ? basicAuthState.getLoginRequiredDetail(this.props.appState, activeFrame.get('tabId')) - : null - const loginRequiredUrl = loginRequiredDetails + props.showReleaseNotes = currentWindow.getIn(['ui', 'releaseNotes', 'isVisible']) + props.showCheckDefault = isFocused() && defaultBrowserState.shouldDisplayDialog(state) + props.showUpdate = updateState.isUpdateVisible(state) + props.showBookmarksToolbar = getSetting(settings.SHOW_BOOKMARKS_TOOLBAR) + props.shouldAllowWindowDrag = windowState.shouldAllowWindowDrag(state, currentWindow, activeFrame, isFocused()) + props.isSinglePage = nonPinnedFrames.size <= tabsPerPage + props.showTabPages = nonPinnedFrames.size > tabsPerPage + props.showNotificationBar = activeOrigin && state.get('notifications').filter((item) => + item.get('frameOrigin') ? activeOrigin === item.get('frameOrigin') : true).size > 0 + props.showFindBar = activeFrame.get('findbarShown') && !activeFrame.get('isFullScreen') + props.sortedFrames = frameStateUtil.getSortedFrameKeys(currentWindow) + props.showDownloadBar = currentWindow.getIn(['ui', 'downloadsToolbar', 'isVisible']) && + state.get('downloads') && state.get('downloads').size > 0 + props.title = activeFrame.get('title') + props.location = activeFrame.get('location') + props.loginRequiredUrl = loginRequiredDetails ? urlResolve(loginRequiredDetails.getIn(['request', 'url']), '/') : null - const customTitlebar = this.customTitlebar - const contextMenuDetail = this.props.windowState.get('contextMenuDetail') - const shouldAllowWindowDrag = windowState.shouldAllowWindowDrag(this.props.appState, this.props.windowState, activeFrame, isFocused()) - const activeOrigin = activeFrame ? siteUtil.getOrigin(activeFrame.get('location')) : null - const notificationBarIsVisible = activeOrigin && this.props.appState.get('notifications').filter((item) => - item.get('frameOrigin') ? activeOrigin === item.get('frameOrigin') : true).size > 0 - const updateIsVisible = updateState.isUpdateVisible(this.props.appState) + // used in other functions + props.menubarSelectedIndex = currentWindow.getIn(['ui', 'menubar', 'selectedIndex']) + props.showCustomTitleBar = isWindows() + props.menubarVisible = menuBarState.isMenuBarVisible(currentWindow) + props.mouseInTitlebar = currentWindow.getIn(['ui', 'mouseInTitlebar']) + props.braveShieldEnabled = shieldState.braveShieldsEnabled(activeFrame) + props.tabId = activeFrame.get('tabId') + props.location = activeFrame.get('location') + props.activeFrameKey = activeFrame.get('key') + props.isWidevineReady = state.getIn([appConfig.resourceNames.WIDEVINE, 'ready']) + props.widevineLocation = siteUtil.getOrigin(widevinePanelDetail.get('location')) + props.widevineRememberSettings = widevinePanelDetail.get('alsoAddRememberSiteSetting') ? 1 : 0 + + return props + } + + render () { return
{ this.mainWindow = node }} onMouseDown={this.onMouseDown} onClick={this.onClickWindow}> { - contextMenuDetail + this.props.showContextMenu ? : null } { - this.props.windowState.get('popupWindowDetail') + this.props.showPopupWindow ? : null } @@ -631,116 +587,115 @@ class Main extends ImmutableComponent { > { - siteInfoIsVisible + this.props.showSiteInfo ? : null } { - braveryPanelIsVisible + this.props.showBravery ? : null } { - clearBrowsingDataPanelIsVisible + this.props.showClearData ? : null } { - importBrowserDataPanelIsVisible + this.props.showImportData ? : null } { - widevinePanelIsVisible + this.props.showWidevine ? : null } { - autofillAddressPanelIsVisible + this.props.showAutoFillAddress ? : null } { - autofillCreditCardPanelIsVisible + this.props.showAutoFillCC ? : null } { - loginRequiredUrl + this.props.showLogin ? : null } { - this.props.windowState.get('bookmarkDetail') && !this.props.windowState.getIn(['bookmarkDetail', 'isBookmarkHanger']) + this.props.showBookmarkHanger ? : null } { - noScriptIsVisible + this.props.showNoScript ? : null } { - releaseNotesIsVisible + this.props.showReleaseNotes ? : null } { - checkDefaultBrowserDialogIsVisible + this.props.showCheckDefault ? : null } { - updateIsVisible + this.props.showUpdate ? : null } { - showBookmarksToolbar + this.props.showBookmarksToolbar ? : null }
{ - nonPinnedFrames.size > tabsPerPage + this.props.showTabPages ? : null }
{ - notificationBarIsVisible + this.props.showNotificationBar ? : null } { - activeFrame && activeFrame.get('findbarShown') && !activeFrame.get('isFullScreen') + this.props.showFindBar ? : null }
-
{ this.tabContainer = node }}> +
{ - sortedFrames.map((frame) => + this.props.sortedFrames.map((frameKey) => ) }
{ - this.props.windowState.getIn(['ui', 'downloadsToolbar', 'isVisible']) && this.props.appState.get('downloads') && this.props.appState.get('downloads').size > 0 + this.props.showDownloadBar ? : null } @@ -748,4 +703,4 @@ class Main extends ImmutableComponent { } } -module.exports = Main +module.exports = ReduxComponent.connect(Main) diff --git a/app/renderer/components/window.js b/app/renderer/components/window.js index 07a1b65e340..d156aa7beb0 100644 --- a/app/renderer/components/window.js +++ b/app/renderer/components/window.js @@ -86,8 +86,7 @@ class Window extends React.Component { } return
-
+
} diff --git a/app/renderer/reducers/frameReducer.js b/app/renderer/reducers/frameReducer.js index c6ab89f7517..42975e161f1 100644 --- a/app/renderer/reducers/frameReducer.js +++ b/app/renderer/reducers/frameReducer.js @@ -24,7 +24,7 @@ const settings = require('../../../js/constants/settings') const {getSetting} = require('../../../js/settings') const setFullScreen = (state, action) => { - const index = frameStateUtil.getFrameIndex(state, action.frameProps.get('key')) + const index = frameStateUtil.getFrameIndex(state, action.frameKey) return state.mergeIn(['frames', index], { isFullScreen: action.isFullScreen !== undefined ? action.isFullScreen : state.getIn(['frames', index].concat('isFullScreen')), showFullScreenWarning: action.showFullScreenWarning @@ -183,6 +183,22 @@ const frameReducer = (state, action, immutableAction) => { closedFrames.forEach((frame) => { appActions.tabCloseRequested(frame.get('tabId')) }) + break + case windowConstants.WINDOW_CLOSE_OTHER_FRAMES: + const currentIndex = frameStateUtil.getFrameIndex(state, action.frameKey) + if (currentIndex === -1) { + return + } + + state.get('frames').forEach((frame, i) => { + if (!frame.get('pinnedLocation') && + ((i < currentIndex && action.isCloseLeft) || (i > currentIndex && action.isCloseRight))) { + if (frame) { + appActions.tabCloseRequested(frame.get('tabId')) + } + } + }) + break case windowConstants.WINDOW_CLOSE_FRAME: state = closeFrame(state, action) diff --git a/docs/windowActions.md b/docs/windowActions.md index 60643043108..92c8b50cc30 100644 --- a/docs/windowActions.md +++ b/docs/windowActions.md @@ -32,13 +32,13 @@ Dispatches a message to the store to let it know a page has been navigated. -### setSecurityState(frameProps, securityState) +### setSecurityState(frameKey, securityState) Dispatches a message to set the security state. **Parameters** -**frameProps**: `Object`, The frame properties to modify. +**frameKey**: `Object`, Frame key of the frame properties to modify. **securityState**: `Object`, The security state properties that have changed. @@ -132,13 +132,13 @@ Dispatches a message to the store to indicate that the webview is done loading. -### setFullScreen(frameProps, isFullScreen, showFullScreenWarning) +### setFullScreen(frameKey, isFullScreen, showFullScreenWarning) Dispatches a message to the store to indicate that the webview entered full screen mode. **Parameters** -**frameProps**: `Object`, The frame properties to put in full screen +**frameKey**: `Object`, frame key of the frame to put in full screen **isFullScreen**: `boolean`, true if the webview is entering full screen mode. @@ -166,6 +166,20 @@ Dispatches a message to close multiple frames +### closeOtherFrames(frameKey, isCloseRight, isCloseLeft) + +Dispatches a message to close multiple frames + +**Parameters** + +**frameKey**: `string`, Frame that we want to ignore when closing all tabs + +**isCloseRight**: `boolean`, Close frames to the right of the frame provided + +**isCloseLeft**: `boolean`, Close frames to the left of the frame provided + + + ### undoClosedFrame() Dispatches a message to the store to undo a closed frame @@ -179,13 +193,15 @@ Dispatches a message to the store to clear closed frames -### setFocusedFrame(frameProps) +### setFocusedFrame(location, tabId) Dispatches a message to the store when the frame is active and the window is focused **Parameters** -**frameProps**: `Object`, the frame properties for the webview in question. +**location**: `Object`, location for the webview in question. + +**tabId**: `Object`, tabId for the webview in question. @@ -548,13 +564,13 @@ for a hovered link -### setBlockedBy(frameProps, blockType, location) +### setBlockedBy(frameKey, blockType, location) Dispatches a message to indicate the site info, such as # of blocked ads, should be shown **Parameters** -**frameProps**: `object`, The frame to set blocked info on +**frameKey**: `object`, Frame key for the frame to set blocked info on **blockType**: `string`, type of the block @@ -562,13 +578,13 @@ Dispatches a message to indicate the site info, such as # of blocked ads, should -### setRedirectedBy(frameProps, ruleset, location) +### setRedirectedBy(tabId, ruleset, location) Similar to setBlockedBy but for httpse redirects **Parameters** -**frameProps**: `Object`, The frame to set blocked info on +**tabId**: `Object`, Tab id of the frame to set blocked info on **ruleset**: `string`, Name of the HTTPS Everywhere ruleset XML file diff --git a/js/actions/windowActions.js b/js/actions/windowActions.js index e6a4258155f..156355f99ba 100644 --- a/js/actions/windowActions.js +++ b/js/actions/windowActions.js @@ -40,15 +40,15 @@ const windowActions = { /** * Dispatches a message to set the security state. - * @param {Object} frameProps - The frame properties to modify. + * @param {Object} frameKey - Frame key of the frame properties to modify. * @param {Object} securityState - The security state properties that have * changed. */ - setSecurityState: function (frameProps, securityState) { + setSecurityState: function (frameKey, securityState) { dispatch({ actionType: windowConstants.WINDOW_SET_SECURITY_STATE, - securityState, - frameProps + frameKey, + securityState }) }, @@ -151,14 +151,14 @@ const windowActions = { /** * Dispatches a message to the store to indicate that the webview entered full screen mode. * - * @param {Object} frameProps - The frame properties to put in full screen + * @param {Object} frameKey - frame key of the frame to put in full screen * @param {boolean} isFullScreen - true if the webview is entering full screen mode. * @param {boolean} showFullScreenWarning - true if a warning about entering full screen should be shown. */ - setFullScreen: function (frameProps, isFullScreen, showFullScreenWarning) { + setFullScreen: function (frameKey, isFullScreen, showFullScreenWarning) { dispatch({ actionType: windowConstants.WINDOW_SET_FULL_SCREEN, - frameProps, + frameKey, isFullScreen, showFullScreenWarning }) @@ -187,6 +187,21 @@ const windowActions = { }) }, + /** + * Dispatches a message to close multiple frames + * @param {string} frameKey - Frame that we want to ignore when closing all tabs + * @param {boolean} isCloseRight - Close frames to the right of the frame provided + * @param {boolean} isCloseLeft - Close frames to the left of the frame provided + */ + closeOtherFrames: function (frameKey, isCloseRight, isCloseLeft) { + dispatch({ + actionType: windowConstants.WINDOW_CLOSE_OTHER_FRAMES, + frameKey, + isCloseRight, + isCloseLeft + }) + }, + /** * Dispatches a message to the store to undo a closed frame * The new frame is expected to appear at the index it was last closed at @@ -216,13 +231,15 @@ const windowActions = { /** * Dispatches a message to the store when the frame is active and the window is focused * - * @param {Object} frameProps - the frame properties for the webview in question. + * @param {Object} location - location for the webview in question. + * @param {Object} tabId - tabId for the webview in question. */ - setFocusedFrame: function (frameProps) { - if (frameProps) { + setFocusedFrame: function (location, tabId) { + if (location) { dispatch({ actionType: windowConstants.WINDOW_SET_FOCUSED_FRAME, - frameProps: frameProps + location, + tabId }) } }, @@ -682,14 +699,14 @@ const windowActions = { /** * Dispatches a message to indicate the site info, such as # of blocked ads, should be shown * - * @param {object} frameProps - The frame to set blocked info on + * @param {object} frameKey - Frame key for the frame to set blocked info on * @param {string} blockType - type of the block * @param {string} location - URL that was blocked */ - setBlockedBy: function (frameProps, blockType, location) { + setBlockedBy: function (frameKey, blockType, location) { dispatch({ actionType: windowConstants.WINDOW_SET_BLOCKED_BY, - frameProps, + frameKey, blockType, location }) @@ -697,14 +714,14 @@ const windowActions = { /** * Similar to setBlockedBy but for httpse redirects - * @param {Object} frameProps - The frame to set blocked info on + * @param {Object} tabId - Tab id of the frame to set blocked info on * @param {string} ruleset - Name of the HTTPS Everywhere ruleset XML file * @param {string} location - URL that was redirected */ - setRedirectedBy: function (frameProps, ruleset, location) { + setRedirectedBy: function (tabId, ruleset, location) { dispatch({ actionType: windowConstants.WINDOW_SET_REDIRECTED_BY, - frameProps, + tabId, ruleset, location }) @@ -1103,6 +1120,15 @@ const windowActions = { partition, tabId }) + }, + + onCertError: function (tabId, url, error) { + dispatch({ + actionType: windowConstants.WINDOW_ON_CERT_ERROR, + tabId, + url, + error + }) } } diff --git a/js/constants/windowConstants.js b/js/constants/windowConstants.js index aae5ad7a779..95d3519602a 100644 --- a/js/constants/windowConstants.js +++ b/js/constants/windowConstants.js @@ -98,7 +98,9 @@ const windowConstants = { WINDOW_SHOULD_OPEN_DEV_TOOLS: _, WINDOW_SET_ALL_AUDIO_MUTED: _, WINDOW_ON_GO_BACK_LONG: _, - WINDOW_ON_GO_FORWARD_LONG: _ + WINDOW_ON_GO_FORWARD_LONG: _, + WINDOW_CLOSE_OTHER_FRAMES: _, + WINDOW_ON_CERT_ERROR: _ } module.exports = mapValuesByKeys(windowConstants) diff --git a/js/contextMenus.js b/js/contextMenus.js index 22ecef52604..cc00bd019da 100644 --- a/js/contextMenus.js +++ b/js/contextMenus.js @@ -610,7 +610,7 @@ function tabTemplateInit (frameProps) { click: (item, focusedWindow) => { if (focusedWindow) { // TODO: Don't switch active tabs when this is called - focusedWindow.webContents.send(messages.SHORTCUT_CLOSE_FRAME, frameKey) + focusedWindow.webContents.send(messages.SHORTCUT_CLOSE_FRAME, tabId) } } }) diff --git a/js/state/frameStateUtil.js b/js/state/frameStateUtil.js index 04bdc92f5bb..0f66a0eaf94 100644 --- a/js/state/frameStateUtil.js +++ b/js/state/frameStateUtil.js @@ -49,6 +49,12 @@ function getSortedFrames (state) { return state.get('frames').sort(comparatorByKeyAsc) } +function getSortedFrameKeys (state) { + return state.get('frames') + .sort(comparatorByKeyAsc) + .map(frame => frame.get('key')) +} + function getPinnedFrames (state) { return state.get('frames').filter((frame) => frame.get('pinnedLocation')) } @@ -582,5 +588,6 @@ module.exports = { isPinned, updateTabPageIndex, isValidClosedFrame, - getTabPageCount + getTabPageCount, + getSortedFrameKeys } diff --git a/js/stores/eventStore.js b/js/stores/eventStore.js index c03edf74dac..620cf227fa7 100644 --- a/js/stores/eventStore.js +++ b/js/stores/eventStore.js @@ -97,8 +97,8 @@ const windowClosed = (windowId) => { const doAction = (action) => { switch (action.actionType) { case windowConstants.WINDOW_SET_FOCUSED_FRAME: - lastActiveTabId = action.frameProps.get('tabId') - addPageView(action.frameProps.get('location'), lastActiveTabId) + lastActiveTabId = action.tabId + addPageView(action.location, lastActiveTabId) break case appConstants.APP_WINDOW_BLURRED: windowBlurred(action.windowId) diff --git a/js/stores/windowStore.js b/js/stores/windowStore.js index f90d6ebd3f2..46992882912 100644 --- a/js/stores/windowStore.js +++ b/js/stores/windowStore.js @@ -22,6 +22,7 @@ const {aboutUrls, getTargetAboutUrl, newFrameUrl} = require('../lib/appUrlUtil') const assert = require('assert') const contextMenuState = require('../../app/common/state/contextMenuState') const appStoreRenderer = require('./appStoreRenderer') +const windowActions = require('../actions/windowActions') let windowState = Immutable.fromJS({ activeFrameKey: null, @@ -599,7 +600,7 @@ const doAction = (action) => { break case windowConstants.WINDOW_SET_SECURITY_STATE: { - const path = frameStateUtil.frameStatePath(windowState, action.frameProps.get('key')) + const path = frameStateUtil.frameStatePath(windowState, action.frameKey) if (action.securityState.secure !== undefined) { windowState = windowState.setIn(path.concat(['security', 'isSecure']), action.securityState.secure) @@ -611,13 +612,13 @@ const doAction = (action) => { break } case windowConstants.WINDOW_SET_BLOCKED_BY: - const blockedByPath = ['frames', frameStateUtil.getFrameIndex(windowState, action.frameProps.get('key')), action.blockType, 'blocked'] + const blockedByPath = ['frames', frameStateUtil.getFrameIndex(windowState, action.frameKey), 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.getFrameIndex(windowState, action.frameProps.get('key')), 'httpsEverywhere', action.ruleset] + const redirectedByPath = ['frames', frameStateUtil.getIndexByTabId(windowState, action.tabId), 'httpsEverywhere', action.ruleset] let redirectedBy = windowState.getIn(redirectedByPath) || new Immutable.List() windowState = windowState.setIn(redirectedByPath, redirectedBy.push(action.location)) break @@ -743,6 +744,19 @@ const doAction = (action) => { case windowConstants.WINDOW_ON_EXIT_FULL_SCREEN: windowState = windowState.setIn(['ui', 'isFullScreen'], false) break + case windowConstants.WINDOW_ON_CERT_ERROR: + { + const frame = frameStateUtil.getFrameByTabId(windowState, action.tabId) || Immutable.Map() + if (frame.get('location') === action.url || + frame.get('provisionalLocation') === action.url) { + windowActions.setFrameError(frame, { + url: action.url, + error: action.error + }) + appActions.loadURLRequested(action.tabId, 'about:certerror') + } + break + } default: break }