From a11efa7376a564245c1cf51b955ebfa245d8c71d Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Wed, 18 Oct 2017 16:00:37 -0700 Subject: [PATCH 01/15] Add core URI handlers --- src/atom-environment.coffee | 2 ++ src/core-uri-handlers.js | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 src/core-uri-handlers.js diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index a32c4424b..7bbc513d6 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -32,6 +32,7 @@ ThemeManager = require './theme-manager' MenuManager = require './menu-manager' ContextMenuManager = require './context-menu-manager' CommandInstaller = require './command-installer' +CoreURIHandlers = require './core-uri-handlers' ProtocolHandlerInstaller = require './protocol-handler-installer' Project = require './project' TitleBar = require './title-bar' @@ -238,6 +239,7 @@ class AtomEnvironment extends Model @commandInstaller.initialize(@getVersion()) @protocolHandlerInstaller.initialize(@config, @notifications) + @uriHandlerRegistry.registerHostHandler('core', CoreURIHandlers.create(@)) @autoUpdater.initialize() @config.load() diff --git a/src/core-uri-handlers.js b/src/core-uri-handlers.js new file mode 100644 index 000000000..c575b3f40 --- /dev/null +++ b/src/core-uri-handlers.js @@ -0,0 +1,24 @@ +function openFile (atom, {query}) { + const {filename, line, column} = query + + atom.workspace.open(filename, { + initialLine: parseInt(line || 0, 10), + initialColumn: parseInt(column || 0, 10), + searchAllPanes: true + }) +} + +const ROUTER = { + '/open/file': openFile +} + +module.exports = { + create (atomEnv) { + return function coreURIHandler (parsed) { + const handler = ROUTER[parsed.pathname] + if (handler) { + handler(atomEnv, parsed) + } + } + } +} From 2901a484c2c796664abf10f75186b87ad5eeef4b Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Wed, 18 Oct 2017 17:34:24 -0700 Subject: [PATCH 02/15] :shirt: --- src/atom-environment.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 7bbc513d6..b9c6306ab 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -239,7 +239,7 @@ class AtomEnvironment extends Model @commandInstaller.initialize(@getVersion()) @protocolHandlerInstaller.initialize(@config, @notifications) - @uriHandlerRegistry.registerHostHandler('core', CoreURIHandlers.create(@)) + @uriHandlerRegistry.registerHostHandler('core', CoreURIHandlers.create(this)) @autoUpdater.initialize() @config.load() From 158622ce48f02fbace7a7617acef619ebe9a0c24 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Thu, 19 Oct 2017 14:19:24 -0700 Subject: [PATCH 03/15] Convert array of windows in AtomApplication to a WindowStack --- spec/main-process/atom-application.test.js | 10 +-- src/main-process/atom-application.coffee | 75 +++++++++++++++------- 2 files changed, 57 insertions(+), 28 deletions(-) diff --git a/spec/main-process/atom-application.test.js b/spec/main-process/atom-application.test.js index 62fae82b3..6434710ce 100644 --- a/spec/main-process/atom-application.test.js +++ b/spec/main-process/atom-application.test.js @@ -137,7 +137,7 @@ describe('AtomApplication', function () { // Does not change the project paths when doing so. const reusedWindow = atomApplication.launch(parseCommandLine([existingDirCFilePath])) assert.equal(reusedWindow, window1) - assert.deepEqual(atomApplication.windows, [window1]) + assert.deepEqual(atomApplication.getAllWindows(), [window1]) activeEditorPath = await evalInWebContents(window1.browserWindow.webContents, function (sendBackToMainProcess) { const subscription = atom.workspace.onDidChangeActivePaneItem(function (textEditor) { sendBackToMainProcess(textEditor.getPath()) @@ -177,7 +177,7 @@ describe('AtomApplication', function () { // parent directory to the project let reusedWindow = atomApplication.launch(parseCommandLine([existingDirCFilePath, '--add'])) assert.equal(reusedWindow, window1) - assert.deepEqual(atomApplication.windows, [window1]) + assert.deepEqual(atomApplication.getAllWindows(), [window1]) activeEditorPath = await evalInWebContents(window1.browserWindow.webContents, function (sendBackToMainProcess) { const subscription = atom.workspace.onDidChangeActivePaneItem(function (textEditor) { sendBackToMainProcess(textEditor.getPath()) @@ -191,7 +191,7 @@ describe('AtomApplication', function () { // the directory to the project reusedWindow = atomApplication.launch(parseCommandLine([dirBPath, '-a'])) assert.equal(reusedWindow, window1) - assert.deepEqual(atomApplication.windows, [window1]) + assert.deepEqual(atomApplication.getAllWindows(), [window1]) await conditionPromise(async () => (await getTreeViewRootDirectories(reusedWindow)).length === 3) assert.deepEqual(await getTreeViewRootDirectories(window1), [dirAPath, dirCPath, dirBPath]) @@ -276,7 +276,7 @@ describe('AtomApplication', function () { }) assert.equal(window2EditorTitle, 'untitled') - assert.deepEqual(atomApplication.windows, [window1, window2]) + assert.deepEqual(atomApplication.getAllWindows(), [window2, window1]) }) it('does not open an empty editor when opened with no path if the core.openEmptyEditorOnStart config setting is false', async function () { @@ -514,7 +514,7 @@ describe('AtomApplication', function () { async function focusWindow (window) { window.focus() await window.loadedPromise - await conditionPromise(() => window.atomApplication.lastFocusedWindow === window) + await conditionPromise(() => window.atomApplication.getLastFocusedWindow() === window) } function mockElectronAppQuit () { diff --git a/src/main-process/atom-application.coffee b/src/main-process/atom-application.coffee index 0c587020e..f17aef902 100644 --- a/src/main-process/atom-application.coffee +++ b/src/main-process/atom-application.coffee @@ -67,7 +67,7 @@ class AtomApplication {@resourcePath, @devResourcePath, @version, @devMode, @safeMode, @socketPath, @logFile, @userDataDir} = options @socketPath = null if options.test or options.benchmark or options.benchmarkTest @pidsToOpenWindows = {} - @windows = [] + @windows = new WindowStack() @config = new Config({enablePersistence: true}) @config.setSchema null, {type: 'object', properties: _.clone(ConfigSchema)} @@ -114,7 +114,7 @@ class AtomApplication @launch(options) destroy: -> - windowsClosePromises = @windows.map (window) -> + windowsClosePromises = @getAllWindows().map (window) -> window.close() window.closedPromise Promise.all(windowsClosePromises).then(=> @disposable.dispose()) @@ -162,8 +162,8 @@ class AtomApplication # Public: Removes the {AtomWindow} from the global window list. removeWindow: (window) -> - @windows.splice(@windows.indexOf(window), 1) - if @windows.length is 0 + @windows.removeWindow(window) + if @getAllWindows().length is 0 @applicationMenu?.enableWindowSpecificItems(false) if process.platform in ['win32', 'linux'] app.quit() @@ -172,22 +172,28 @@ class AtomApplication # Public: Adds the {AtomWindow} to the global window list. addWindow: (window) -> - @windows.push window + @windows.addWindow(window) @applicationMenu?.addWindow(window.browserWindow) window.once 'window:loaded', => @autoUpdateManager?.emitUpdateAvailableEvent(window) unless window.isSpec - focusHandler = => @lastFocusedWindow = window + focusHandler = => @windows.touch(window) blurHandler = => @saveState(false) window.browserWindow.on 'focus', focusHandler window.browserWindow.on 'blur', blurHandler window.browserWindow.once 'closed', => - @lastFocusedWindow = null if window is @lastFocusedWindow + @windows.removeWindow(window) window.browserWindow.removeListener 'focus', focusHandler window.browserWindow.removeListener 'blur', blurHandler window.browserWindow.webContents.once 'did-finish-load', => @saveState(false) + getAllWindows: () => + @windows.all().slice() + + getLastFocusedWindow: () => + @windows.getLastFocusedWindow() + # Creates server to listen for additional atom application launches. # # You can run the atom command multiple times, but after the first launch @@ -276,7 +282,7 @@ class AtomApplication else event.preventDefault() @quitting = true - windowUnloadPromises = @windows.map((window) -> window.prepareToUnload()) + windowUnloadPromises = @getAllWindows().map((window) -> window.prepareToUnload()) Promise.all(windowUnloadPromises).then((windowUnloadedResults) -> didUnloadAllWindows = windowUnloadedResults.every((didUnloadWindow) -> didUnloadWindow) app.quit() if didUnloadAllWindows @@ -309,7 +315,7 @@ class AtomApplication event.sender.send('did-resolve-proxy', requestId, proxy) @disposable.add ipcHelpers.on ipcMain, 'did-change-history-manager', (event) => - for atomWindow in @windows + for atomWindow in @getAllWindows() webContents = atomWindow.browserWindow.webContents if webContents isnt event.sender webContents.send('did-change-history-manager') @@ -483,7 +489,7 @@ class AtomApplication # Returns the {AtomWindow} for the given paths. windowForPaths: (pathsToOpen, devMode) -> - _.find @windows, (atomWindow) -> + _.find @getAllWindows(), (atomWindow) -> atomWindow.devMode is devMode and atomWindow.containsPaths(pathsToOpen) # Returns the {AtomWindow} for the given ipcMain event. @@ -491,11 +497,11 @@ class AtomApplication @atomWindowForBrowserWindow(BrowserWindow.fromWebContents(sender)) atomWindowForBrowserWindow: (browserWindow) -> - @windows.find((atomWindow) -> atomWindow.browserWindow is browserWindow) + @getAllWindows().find((atomWindow) -> atomWindow.browserWindow is browserWindow) # Public: Returns the currently focused {AtomWindow} or undefined if none. focusedWindow: -> - _.find @windows, (atomWindow) -> atomWindow.isFocused() + _.find @getAllWindows(), (atomWindow) -> atomWindow.isFocused() # Get the platform-specific window offset for new windows. getWindowOffsetForCurrentPlatform: -> @@ -507,8 +513,8 @@ class AtomApplication # Get the dimensions for opening a new window by cascading as appropriate to # the platform. getDimensionsForNewWindow: -> - return if (@focusedWindow() ? @lastFocusedWindow)?.isMaximized() - dimensions = (@focusedWindow() ? @lastFocusedWindow)?.getDimensions() + return if (@focusedWindow() ? @getLastFocusedWindow())?.isMaximized() + dimensions = (@focusedWindow() ? @getLastFocusedWindow())?.getDimensions() offset = @getWindowOffsetForCurrentPlatform() if dimensions? and offset? dimensions.x += offset @@ -554,7 +560,7 @@ class AtomApplication existingWindow = @windowForPaths(pathsToOpen, devMode) stats = (fs.statSyncNoException(pathToOpen) for pathToOpen in pathsToOpen) unless existingWindow? - if currentWindow = window ? @lastFocusedWindow + if currentWindow = window ? @getLastFocusedWindow() existingWindow = currentWindow if ( addToLastWindow or currentWindow.devMode is devMode and @@ -583,7 +589,7 @@ class AtomApplication windowDimensions ?= @getDimensionsForNewWindow() openedWindow = new AtomWindow(this, @fileRecoveryService, {initialPaths, locationsToOpen, windowInitializationScript, resourcePath, devMode, safeMode, windowDimensions, profileStartup, clearWindowState, env}) openedWindow.focus() - @lastFocusedWindow = openedWindow + @windows.addWindow(openedWindow) if pidToKillWhenClosed? @pidsToOpenWindows[pidToKillWhenClosed] = openedWindow @@ -617,9 +623,10 @@ class AtomApplication saveState: (allowEmpty=false) -> return if @quitting states = [] - for window in @windows + for window in @getAllWindows() unless window.isSpec states.push({initialPaths: window.representedDirectoryPaths}) + states.reverse() if states.length > 0 or allowEmpty @storageFolder.storeSync('application.json', states) @emit('application:did-save-state') @@ -665,13 +672,14 @@ class AtomApplication resourcePath = @devResourcePath windowInitializationScript ?= require.resolve('../initialize-application-window') - if @lastFocusedWindow? - @lastFocusedWindow.sendURIMessage url + if @getLastFocusedWindow()? + @getLastFocusedWindow().sendURIMessage url else windowDimensions = @getDimensionsForNewWindow() - @lastFocusedWindow = new AtomWindow(this, @fileRecoveryService, {resourcePath, windowInitializationScript, devMode, safeMode, windowDimensions, env}) - @lastFocusedWindow.on 'window:loaded', => - @lastFocusedWindow.sendURIMessage url + win = new AtomWindow(this, @fileRecoveryService, {resourcePath, windowInitializationScript, devMode, safeMode, windowDimensions, env}) + @windows.addWindow(win) + win.on 'window:loaded', => + win.sendURIMessage url findPackageWithName: (packageName, devMode) -> _.find @getPackageManager(devMode).getAvailablePackageMetadata(), ({name}) -> name is packageName @@ -867,7 +875,7 @@ class AtomApplication disableZoomOnDisplayChange: -> outerCallback = => - for window in @windows + for window in @getAllWindows() window.disableZoom() # Set the limits every time a display is added or removed, otherwise the @@ -878,3 +886,24 @@ class AtomApplication new Disposable -> screen.removeListener('display-added', outerCallback) screen.removeListener('display-removed', outerCallback) + +class WindowStack + constructor: (@windows = []) -> + + addWindow: (window) => + @removeWindow(window) + @windows.unshift(window) + + touch: (window) => + @addWindow(window) + + removeWindow: (window) => + currentIndex = @windows.indexOf(window) + @windows.splice(currentIndex, 1) if currentIndex > -1 + + getLastFocusedWindow: (predicate) => + predicate ?= (win) => true + @windows.find(predicate) + + all: () => + @windows From e1bc9b593b01e062e800d71a945ab5898f255d4e Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Thu, 19 Oct 2017 14:37:08 -0700 Subject: [PATCH 04/15] Run URI handlers in last non-spec window --- src/main-process/atom-application.coffee | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main-process/atom-application.coffee b/src/main-process/atom-application.coffee index f17aef902..76b0d2bed 100644 --- a/src/main-process/atom-application.coffee +++ b/src/main-process/atom-application.coffee @@ -191,8 +191,8 @@ class AtomApplication getAllWindows: () => @windows.all().slice() - getLastFocusedWindow: () => - @windows.getLastFocusedWindow() + getLastFocusedWindow: (predicate) => + @windows.getLastFocusedWindow(predicate) # Creates server to listen for additional atom application launches. # @@ -672,8 +672,10 @@ class AtomApplication resourcePath = @devResourcePath windowInitializationScript ?= require.resolve('../initialize-application-window') - if @getLastFocusedWindow()? - @getLastFocusedWindow().sendURIMessage url + lastNonSpecWindow = @getLastFocusedWindow (win) -> !win.isSpecWindow() + if lastNonSpecWindow? + lastNonSpecWindow.sendURIMessage url + lastNonSpecWindow.focus() else windowDimensions = @getDimensionsForNewWindow() win = new AtomWindow(this, @fileRecoveryService, {resourcePath, windowInitializationScript, devMode, safeMode, windowDimensions, env}) From 8111ba6c1ede4674ce0f52603e7f321531eca911 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Thu, 19 Oct 2017 15:47:57 -0700 Subject: [PATCH 05/15] Allow core URI handlers to determine which window to trigger the URI on --- src/core-uri-handlers.js | 22 +++++++++++++--- src/main-process/atom-application.coffee | 32 ++++++++++++++---------- 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/core-uri-handlers.js b/src/core-uri-handlers.js index c575b3f40..2af00f610 100644 --- a/src/core-uri-handlers.js +++ b/src/core-uri-handlers.js @@ -8,17 +8,31 @@ function openFile (atom, {query}) { }) } +function windowShouldOpenFile ({query}) { + const {filename} = query + return (win) => win.containsPath(filename) +} + const ROUTER = { - '/open/file': openFile + '/open/file': { handler: openFile, getWindowPredicate: windowShouldOpenFile } } module.exports = { create (atomEnv) { return function coreURIHandler (parsed) { - const handler = ROUTER[parsed.pathname] - if (handler) { - handler(atomEnv, parsed) + const config = ROUTER[parsed.pathname] + if (config) { + config.handler(atomEnv, parsed) } } + }, + + windowPredicate (parsed) { + const config = ROUTER[parsed.pathname] + if (config && config.getWindowPredicate) { + return config.getWindowPredicate(parsed) + } else { + return (win) => true + } } } diff --git a/src/main-process/atom-application.coffee b/src/main-process/atom-application.coffee index 76b0d2bed..1f4d7214f 100644 --- a/src/main-process/atom-application.coffee +++ b/src/main-process/atom-application.coffee @@ -655,28 +655,34 @@ class AtomApplication # :devMode - Boolean to control the opened window's dev mode. # :safeMode - Boolean to control the opened window's safe mode. openUrl: ({urlToOpen, devMode, safeMode, env}) -> - parsedUrl = url.parse(urlToOpen) + parsedUrl = url.parse(urlToOpen, true) return unless parsedUrl.protocol is "atom:" pack = @findPackageWithName(parsedUrl.host, devMode) if pack?.urlMain @openPackageUrlMain(parsedUrl.host, pack.urlMain, urlToOpen, devMode, safeMode, env) else - @openPackageUriHandler(urlToOpen, devMode, safeMode, env) + @openPackageUriHandler(urlToOpen, parsedUrl, devMode, safeMode, env) - openPackageUriHandler: (url, devMode, safeMode, env) -> - resourcePath = @resourcePath - if devMode - try - windowInitializationScript = require.resolve(path.join(@devResourcePath, 'src', 'initialize-application-window')) - resourcePath = @devResourcePath + openPackageUriHandler: (url, parsedUrl, devMode, safeMode, env) -> + bestWindow = null + if parsedUrl.host is 'core' + predicate = require('../core-uri-handlers').windowPredicate(parsedUrl) + bestWindow = @getLastFocusedWindow (win) -> + !win.isSpecWindow() && predicate(win) - windowInitializationScript ?= require.resolve('../initialize-application-window') - lastNonSpecWindow = @getLastFocusedWindow (win) -> !win.isSpecWindow() - if lastNonSpecWindow? - lastNonSpecWindow.sendURIMessage url - lastNonSpecWindow.focus() + bestWindow ?= @getLastFocusedWindow (win) -> !win.isSpecWindow() + if bestWindow? + bestWindow.sendURIMessage url + bestWindow.focus() else + resourcePath = @resourcePath + if devMode + try + windowInitializationScript = require.resolve(path.join(@devResourcePath, 'src', 'initialize-application-window')) + resourcePath = @devResourcePath + + windowInitializationScript ?= require.resolve('../initialize-application-window') windowDimensions = @getDimensionsForNewWindow() win = new AtomWindow(this, @fileRecoveryService, {resourcePath, windowInitializationScript, devMode, safeMode, windowDimensions, env}) @windows.addWindow(win) From 662a3978604ebcac0f7693a6422ec87a250a36c0 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Thu, 19 Oct 2017 16:19:44 -0700 Subject: [PATCH 06/15] :shirt: --- src/main-process/atom-application.coffee | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main-process/atom-application.coffee b/src/main-process/atom-application.coffee index 1f4d7214f..fc2058dd4 100644 --- a/src/main-process/atom-application.coffee +++ b/src/main-process/atom-application.coffee @@ -188,7 +188,7 @@ class AtomApplication window.browserWindow.removeListener 'blur', blurHandler window.browserWindow.webContents.once 'did-finish-load', => @saveState(false) - getAllWindows: () => + getAllWindows: => @windows.all().slice() getLastFocusedWindow: (predicate) => @@ -669,9 +669,9 @@ class AtomApplication if parsedUrl.host is 'core' predicate = require('../core-uri-handlers').windowPredicate(parsedUrl) bestWindow = @getLastFocusedWindow (win) -> - !win.isSpecWindow() && predicate(win) + not win.isSpecWindow() and predicate(win) - bestWindow ?= @getLastFocusedWindow (win) -> !win.isSpecWindow() + bestWindow ?= @getLastFocusedWindow (win) -> not win.isSpecWindow() if bestWindow? bestWindow.sendURIMessage url bestWindow.focus() @@ -686,7 +686,7 @@ class AtomApplication windowDimensions = @getDimensionsForNewWindow() win = new AtomWindow(this, @fileRecoveryService, {resourcePath, windowInitializationScript, devMode, safeMode, windowDimensions, env}) @windows.addWindow(win) - win.on 'window:loaded', => + win.on 'window:loaded', -> win.sendURIMessage url findPackageWithName: (packageName, devMode) -> @@ -910,8 +910,8 @@ class WindowStack @windows.splice(currentIndex, 1) if currentIndex > -1 getLastFocusedWindow: (predicate) => - predicate ?= (win) => true + predicate ?= (win) -> true @windows.find(predicate) - all: () => + all: => @windows From 2d20886cfa991a1db2431d1883ddd5db65914472 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Wed, 1 Nov 2017 14:36:23 -0700 Subject: [PATCH 07/15] Rename AtomApplication#windows -> #windowStack Update references across codebase --- src/main-process/application-menu.coffee | 2 +- src/main-process/atom-application.coffee | 18 +++++++++--------- src/main-process/auto-update-manager.coffee | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main-process/application-menu.coffee b/src/main-process/application-menu.coffee index 681677603..35bc7d66c 100644 --- a/src/main-process/application-menu.coffee +++ b/src/main-process/application-menu.coffee @@ -128,7 +128,7 @@ class ApplicationMenu ] focusedWindow: -> - _.find global.atomApplication.windows, (atomWindow) -> atomWindow.isFocused() + _.find global.atomApplication.getAllWindows(), (atomWindow) -> atomWindow.isFocused() # Combines a menu template with the appropriate keystroke. # diff --git a/src/main-process/atom-application.coffee b/src/main-process/atom-application.coffee index fc2058dd4..a5e3e6b0b 100644 --- a/src/main-process/atom-application.coffee +++ b/src/main-process/atom-application.coffee @@ -67,7 +67,7 @@ class AtomApplication {@resourcePath, @devResourcePath, @version, @devMode, @safeMode, @socketPath, @logFile, @userDataDir} = options @socketPath = null if options.test or options.benchmark or options.benchmarkTest @pidsToOpenWindows = {} - @windows = new WindowStack() + @windowStack = new WindowStack() @config = new Config({enablePersistence: true}) @config.setSchema null, {type: 'object', properties: _.clone(ConfigSchema)} @@ -162,7 +162,7 @@ class AtomApplication # Public: Removes the {AtomWindow} from the global window list. removeWindow: (window) -> - @windows.removeWindow(window) + @windowStacktack.removeWindow(window) if @getAllWindows().length is 0 @applicationMenu?.enableWindowSpecificItems(false) if process.platform in ['win32', 'linux'] @@ -172,27 +172,27 @@ class AtomApplication # Public: Adds the {AtomWindow} to the global window list. addWindow: (window) -> - @windows.addWindow(window) + @windowStack.addWindow(window) @applicationMenu?.addWindow(window.browserWindow) window.once 'window:loaded', => @autoUpdateManager?.emitUpdateAvailableEvent(window) unless window.isSpec - focusHandler = => @windows.touch(window) + focusHandler = => @windowStack.touch(window) blurHandler = => @saveState(false) window.browserWindow.on 'focus', focusHandler window.browserWindow.on 'blur', blurHandler window.browserWindow.once 'closed', => - @windows.removeWindow(window) + @windowStack.removeWindow(window) window.browserWindow.removeListener 'focus', focusHandler window.browserWindow.removeListener 'blur', blurHandler window.browserWindow.webContents.once 'did-finish-load', => @saveState(false) getAllWindows: => - @windows.all().slice() + @windowStack.all().slice() getLastFocusedWindow: (predicate) => - @windows.getLastFocusedWindow(predicate) + @windowStack.getLastFocusedWindow(predicate) # Creates server to listen for additional atom application launches. # @@ -589,7 +589,7 @@ class AtomApplication windowDimensions ?= @getDimensionsForNewWindow() openedWindow = new AtomWindow(this, @fileRecoveryService, {initialPaths, locationsToOpen, windowInitializationScript, resourcePath, devMode, safeMode, windowDimensions, profileStartup, clearWindowState, env}) openedWindow.focus() - @windows.addWindow(openedWindow) + @windowStack.addWindow(openedWindow) if pidToKillWhenClosed? @pidsToOpenWindows[pidToKillWhenClosed] = openedWindow @@ -685,7 +685,7 @@ class AtomApplication windowInitializationScript ?= require.resolve('../initialize-application-window') windowDimensions = @getDimensionsForNewWindow() win = new AtomWindow(this, @fileRecoveryService, {resourcePath, windowInitializationScript, devMode, safeMode, windowDimensions, env}) - @windows.addWindow(win) + @windowStack.addWindow(win) win.on 'window:loaded', -> win.sendURIMessage url diff --git a/src/main-process/auto-update-manager.coffee b/src/main-process/auto-update-manager.coffee index 2ff2852cb..0e4144c1a 100644 --- a/src/main-process/auto-update-manager.coffee +++ b/src/main-process/auto-update-manager.coffee @@ -138,4 +138,4 @@ class AutoUpdateManager detail: message getWindows: -> - global.atomApplication.windows + global.atomApplication.getAllWindows() From cbf2d24d9ea726f1edbc3605080b0072ba69978e Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Thu, 2 Nov 2017 11:31:06 -0700 Subject: [PATCH 08/15] :keyboard: fix typo --- src/main-process/atom-application.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main-process/atom-application.coffee b/src/main-process/atom-application.coffee index a5e3e6b0b..f6802705e 100644 --- a/src/main-process/atom-application.coffee +++ b/src/main-process/atom-application.coffee @@ -162,7 +162,7 @@ class AtomApplication # Public: Removes the {AtomWindow} from the global window list. removeWindow: (window) -> - @windowStacktack.removeWindow(window) + @windowStack.removeWindow(window) if @getAllWindows().length is 0 @applicationMenu?.enableWindowSpecificItems(false) if process.platform in ['win32', 'linux'] From db0fd527cea4ff6bc203a599f772303c6e950dbc Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Thu, 2 Nov 2017 13:35:29 -0700 Subject: [PATCH 09/15] Add test for core URI handler window-selection logic --- spec/main-process/atom-application.test.js | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/spec/main-process/atom-application.test.js b/spec/main-process/atom-application.test.js index 6434710ce..a209b4301 100644 --- a/spec/main-process/atom-application.test.js +++ b/spec/main-process/atom-application.test.js @@ -461,6 +461,30 @@ describe('AtomApplication', function () { assert.equal(reached, true); windows[0].close(); }) + + it('triggers /core/open/file in the correct window', async function() { + const dirAPath = makeTempDir('a') + const dirBPath = makeTempDir('b') + + const atomApplication = buildAtomApplication() + const window1 = atomApplication.launch(parseCommandLine([path.join(dirAPath)])) + await focusWindow(window1) + const window2 = atomApplication.launch(parseCommandLine([path.join(dirBPath)])) + await focusWindow(window2) + + await new Promise(res => setTimeout(res, 2000)) + + const fileA = path.join(dirAPath, 'file-a') + const fileB = path.join(dirBPath, 'file-b') + + atomApplication.launch(parseCommandLine(['--uri-handler', `atom://core/open/file?filename=${fileA}`])) + await new Promise(res => setTimeout(res, 1000)) + assert.equal(atomApplication.getLastFocusedWindow(), window1) + + atomApplication.launch(parseCommandLine(['--uri-handler', `atom://core/open/file?filename=${fileB}`])) + await new Promise(res => setTimeout(res, 1000)) + assert.equal(atomApplication.getLastFocusedWindow(), window2) + }) }) }) From 668397c1d0cac12e61c1894cfa1d832f37cd8f59 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Thu, 2 Nov 2017 14:11:52 -0700 Subject: [PATCH 10/15] Fix flaky test --- spec/main-process/atom-application.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/main-process/atom-application.test.js b/spec/main-process/atom-application.test.js index a209b4301..c97c70746 100644 --- a/spec/main-process/atom-application.test.js +++ b/spec/main-process/atom-application.test.js @@ -479,11 +479,11 @@ describe('AtomApplication', function () { atomApplication.launch(parseCommandLine(['--uri-handler', `atom://core/open/file?filename=${fileA}`])) await new Promise(res => setTimeout(res, 1000)) - assert.equal(atomApplication.getLastFocusedWindow(), window1) + await conditionPromise(() => window.atomApplication.getLastFocusedWindow() === window1) atomApplication.launch(parseCommandLine(['--uri-handler', `atom://core/open/file?filename=${fileB}`])) await new Promise(res => setTimeout(res, 1000)) - assert.equal(atomApplication.getLastFocusedWindow(), window2) + await conditionPromise(() => window.atomApplication.getLastFocusedWindow() === window2) }) }) }) From ada75ed1dd1ee354bcf43982cd2a9ee957952727 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Thu, 2 Nov 2017 14:45:14 -0700 Subject: [PATCH 11/15] Fix bug in test --- spec/main-process/atom-application.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/main-process/atom-application.test.js b/spec/main-process/atom-application.test.js index c97c70746..70ebf686c 100644 --- a/spec/main-process/atom-application.test.js +++ b/spec/main-process/atom-application.test.js @@ -479,11 +479,11 @@ describe('AtomApplication', function () { atomApplication.launch(parseCommandLine(['--uri-handler', `atom://core/open/file?filename=${fileA}`])) await new Promise(res => setTimeout(res, 1000)) - await conditionPromise(() => window.atomApplication.getLastFocusedWindow() === window1) + await conditionPromise(() => atomApplication.getLastFocusedWindow() === window1) atomApplication.launch(parseCommandLine(['--uri-handler', `atom://core/open/file?filename=${fileB}`])) await new Promise(res => setTimeout(res, 1000)) - await conditionPromise(() => window.atomApplication.getLastFocusedWindow() === window2) + await conditionPromise(() => atomApplication.getLastFocusedWindow() === window2) }) }) }) From 178756b62ae1245745f40473e1d44a689ae1a1fc Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Thu, 2 Nov 2017 15:27:59 -0700 Subject: [PATCH 12/15] :white_check_mark: update test --- spec/main-process/atom-application.test.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/spec/main-process/atom-application.test.js b/spec/main-process/atom-application.test.js index 70ebf686c..69ddb3539 100644 --- a/spec/main-process/atom-application.test.js +++ b/spec/main-process/atom-application.test.js @@ -472,17 +472,13 @@ describe('AtomApplication', function () { const window2 = atomApplication.launch(parseCommandLine([path.join(dirBPath)])) await focusWindow(window2) - await new Promise(res => setTimeout(res, 2000)) - const fileA = path.join(dirAPath, 'file-a') const fileB = path.join(dirBPath, 'file-b') atomApplication.launch(parseCommandLine(['--uri-handler', `atom://core/open/file?filename=${fileA}`])) - await new Promise(res => setTimeout(res, 1000)) await conditionPromise(() => atomApplication.getLastFocusedWindow() === window1) atomApplication.launch(parseCommandLine(['--uri-handler', `atom://core/open/file?filename=${fileB}`])) - await new Promise(res => setTimeout(res, 1000)) await conditionPromise(() => atomApplication.getLastFocusedWindow() === window2) }) }) From 444597c8455501d8421d5dbe766adf28d4070187 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Thu, 2 Nov 2017 15:49:06 -0700 Subject: [PATCH 13/15] Let's add some debugging --- spec/async-spec-helpers.js | 4 ++-- spec/main-process/atom-application.test.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/async-spec-helpers.js b/spec/async-spec-helpers.js index 56550cd9f..73002c049 100644 --- a/spec/async-spec-helpers.js +++ b/spec/async-spec-helpers.js @@ -34,7 +34,7 @@ export function afterEach (fn) { } }) -export async function conditionPromise (condition) { +export async function conditionPromise (condition, description = 'anonymous condition') { const startTime = Date.now() while (true) { @@ -45,7 +45,7 @@ export async function conditionPromise (condition) { } if (Date.now() - startTime > 5000) { - throw new Error('Timed out waiting on condition') + throw new Error('Timed out waiting on ' + description) } } } diff --git a/spec/main-process/atom-application.test.js b/spec/main-process/atom-application.test.js index 69ddb3539..39863aa34 100644 --- a/spec/main-process/atom-application.test.js +++ b/spec/main-process/atom-application.test.js @@ -476,10 +476,10 @@ describe('AtomApplication', function () { const fileB = path.join(dirBPath, 'file-b') atomApplication.launch(parseCommandLine(['--uri-handler', `atom://core/open/file?filename=${fileA}`])) - await conditionPromise(() => atomApplication.getLastFocusedWindow() === window1) + await conditionPromise(() => atomApplication.getLastFocusedWindow() === window1, 'window1 to be focused') atomApplication.launch(parseCommandLine(['--uri-handler', `atom://core/open/file?filename=${fileB}`])) - await conditionPromise(() => atomApplication.getLastFocusedWindow() === window2) + await conditionPromise(() => atomApplication.getLastFocusedWindow() === window2, 'window2 to be focused') }) }) }) From 99bef8e7d1b2fdb1fe670a54fce4c3623b241343 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Thu, 2 Nov 2017 16:09:57 -0700 Subject: [PATCH 14/15] More debugging --- spec/main-process/atom-application.test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/main-process/atom-application.test.js b/spec/main-process/atom-application.test.js index 39863aa34..a7096e49c 100644 --- a/spec/main-process/atom-application.test.js +++ b/spec/main-process/atom-application.test.js @@ -476,10 +476,10 @@ describe('AtomApplication', function () { const fileB = path.join(dirBPath, 'file-b') atomApplication.launch(parseCommandLine(['--uri-handler', `atom://core/open/file?filename=${fileA}`])) - await conditionPromise(() => atomApplication.getLastFocusedWindow() === window1, 'window1 to be focused') + await conditionPromise(() => atomApplication.getLastFocusedWindow() === window1, `window1 to be focused from ${fileA}`) atomApplication.launch(parseCommandLine(['--uri-handler', `atom://core/open/file?filename=${fileB}`])) - await conditionPromise(() => atomApplication.getLastFocusedWindow() === window2, 'window2 to be focused') + await conditionPromise(() => atomApplication.getLastFocusedWindow() === window2, `window2 to be focused from ${fileB}`) }) }) }) From 026782921145e492bfb1660f1d5cee763fa0ca78 Mon Sep 17 00:00:00 2001 From: Michelle Tilley Date: Thu, 2 Nov 2017 16:45:52 -0700 Subject: [PATCH 15/15] Change the way we test this --- spec/main-process/atom-application.test.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/spec/main-process/atom-application.test.js b/spec/main-process/atom-application.test.js index a7096e49c..01d052b96 100644 --- a/spec/main-process/atom-application.test.js +++ b/spec/main-process/atom-application.test.js @@ -5,6 +5,7 @@ import dedent from 'dedent' import electron from 'electron' import fs from 'fs-plus' import path from 'path' +import sinon from 'sinon' import AtomApplication from '../../src/main-process/atom-application' import parseCommandLine from '../../src/main-process/parse-command-line' import {timeoutPromise, conditionPromise, emitterEventPromise} from '../async-spec-helpers' @@ -473,13 +474,18 @@ describe('AtomApplication', function () { await focusWindow(window2) const fileA = path.join(dirAPath, 'file-a') + const uriA = `atom://core/open/file?filename=${fileA}` const fileB = path.join(dirBPath, 'file-b') + const uriB = `atom://core/open/file?filename=${fileB}` - atomApplication.launch(parseCommandLine(['--uri-handler', `atom://core/open/file?filename=${fileA}`])) - await conditionPromise(() => atomApplication.getLastFocusedWindow() === window1, `window1 to be focused from ${fileA}`) + sinon.spy(window1, 'sendURIMessage') + sinon.spy(window2, 'sendURIMessage') - atomApplication.launch(parseCommandLine(['--uri-handler', `atom://core/open/file?filename=${fileB}`])) - await conditionPromise(() => atomApplication.getLastFocusedWindow() === window2, `window2 to be focused from ${fileB}`) + atomApplication.launch(parseCommandLine(['--uri-handler', uriA])) + await conditionPromise(() => window1.sendURIMessage.calledWith(uriA), `window1 to be focused from ${fileA}`) + + atomApplication.launch(parseCommandLine(['--uri-handler', uriB])) + await conditionPromise(() => window2.sendURIMessage.calledWith(uriB), `window2 to be focused from ${fileB}`) }) }) })