diff --git a/spec/url-handler-registry-spec.js b/spec/url-handler-registry-spec.js index 0a5042262..3488a94fc 100644 --- a/spec/url-handler-registry-spec.js +++ b/spec/url-handler-registry-spec.js @@ -7,7 +7,11 @@ import {it} from './async-spec-helpers' import UrlHandlerRegistry from '../src/url-handler-registry' describe('UrlHandlerRegistry', () => { - let registry = new UrlHandlerRegistry() + let registry + + beforeEach(() => { + registry = new UrlHandlerRegistry(5) + }) it('handles URLs on a per-host basis', () => { const testPackageSpy = jasmine.createSpy() @@ -27,6 +31,37 @@ describe('UrlHandlerRegistry', () => { expect(otherPackageSpy).toHaveBeenCalledWith(url.parse('atom://other-package/path', true), 'atom://other-package/path') }) + it('keeps track of the most recent URIs', () => { + const spy1 = jasmine.createSpy() + const spy2 = jasmine.createSpy() + const changeSpy = jasmine.createSpy() + registry.registerHostHandler('one', spy1) + registry.registerHostHandler('two', spy2) + registry.onHistoryChange(changeSpy) + + const urls = [ + 'atom://one/something?asdf=1', + 'atom://fake/nothing', + 'atom://two/other/stuff', + 'atom://one/more/thing', + 'atom://two/more/stuff' + ] + + urls.forEach(u => registry.handleUrl(u)) + + expect(changeSpy.callCount).toBe(5) + expect(registry.getRecentlyHandledUrls()).toEqual(urls.map((u, idx) => { + return {id: idx + 1, url: u, handled: !u.match(/fake/), host: url.parse(u).host} + }).reverse()) + + registry.handleUrl('atom://another/url') + expect(changeSpy.callCount).toBe(6) + const history = registry.getRecentlyHandledUrls() + expect(history.length).toBe(5) + expect(history[0].url).toBe('atom://another/url') + expect(history[4].url).toBe(urls[1]) + }) + it('refuses to handle bad URLs', () => { [ 'atom:package/path', diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 0036e35b3..a7178aac7 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -357,6 +357,7 @@ class AtomEnvironment extends Model @config.unobserveUserConfig() @autoUpdater.destroy() @protocolHandlerInstaller.destroy() + @urlHandlerRegistry.destroy() @uninstallWindowEventHandler() diff --git a/src/url-handler-registry.js b/src/url-handler-registry.js index 3115506e7..608ce2810 100644 --- a/src/url-handler-registry.js +++ b/src/url-handler-registry.js @@ -1,5 +1,5 @@ const url = require('url') -const {Disposable} = require('event-kit') +const {Emitter, Disposable} = require('event-kit') // Private: Associates listener functions with URLs from outside the application. // @@ -64,8 +64,13 @@ const {Disposable} = require('event-kit') // ``` module.exports = class UrlHandlerRegistry { - constructor () { + constructor (maxHistoryLength = 50) { this.registrations = new Map() + this.history = [] + this.maxHistoryLength = maxHistoryLength + this._id = 0 + + this.emitter = new Emitter() } registerHostHandler (host, callback) { @@ -92,8 +97,32 @@ class UrlHandlerRegistry { } const registration = this.registrations.get(host) - if (registration) { - registration(parsed, uri) + const historyEntry = {id: ++this._id, url: uri, handled: false, host} + try { + if (registration) { + historyEntry.handled = true + registration(parsed, uri) + } + } finally { + this.history.unshift(historyEntry) + if (this.history.length > this.maxHistoryLength) { + this.history.length = this.maxHistoryLength + } + this.emitter.emit('history-change') } } + + getRecentlyHandledUrls () { + return this.history + } + + onHistoryChange (cb) { + return this.emitter.on('history-change', cb) + } + + destroy () { + this.emitter.dispose() + this.registrations = new Map() + this._id = 0 + } }