]> git.gir.st - VimFx.git/blob - extension/bootstrap.coffee
explicitly import Services
[VimFx.git] / extension / bootstrap.coffee
1 # This file boots the main VimFx process, as well as each frame script. It tries
2 # to do the minimum amount of things to run main.coffee, or main-frame.coffee
3 # for frame scripts. It defines a few global variables, and sets up a
4 # Node.js-style `require` module loader.
5
6 # `bootstrap.js` files of different add-ons do _not_ share scope. However, frame
7 # scripts for the same `<browser>` but from different add-ons _do_ share scope.
8 # In order not to pollute that global scope in frame scripts, everything is done
9 # in an IIFE here, and the `global` variable is handled with care.
10
11 do (global = this) ->
12
13 {classes: Cc, interfaces: Ci, utils: Cu} = Components
14 ADDON_PATH = do -> # @echo ADDON_PATH
15 IS_FRAME_SCRIPT = (typeof content != 'undefined')
16 BUILD_TIME = do -> # @echo BUILD_TIME
17 REQUIRE_DATA = do -> # @echo REQUIRE_DATA
18
19 unless IS_FRAME_SCRIPT
20 # Make `Services` and `console` available globally, just like they are in
21 # frame scripts by default.
22 Cu.import('resource://gre/modules/Services.jsm')
23 Cu.import('resource://gre/modules/Console.jsm')
24
25 shutdownHandlers = []
26
27 dirname = (uri) -> uri.split('/')[...-1].join('/') or '.'
28
29 require = (path, moduleRoot = '.', dir = '.') ->
30 unless path[0] == '.'
31 # Allow `require('module/lib/foo')` in additon to `require('module')`.
32 [match, name, subPath] = path.match(///^ ([^/]+) (?: /(.+) )? ///)
33 base = REQUIRE_DATA[moduleRoot]?[name] ? moduleRoot
34 dir = "#{base}/node_modules/#{name}"
35 main = REQUIRE_DATA[dir]?['']
36 path = subPath ? main ? 'index'
37 moduleRoot = dir
38
39 prefix = "#{ADDON_PATH}/content"
40 uri = "#{prefix}/#{dir}/#{path}.js"
41 normalizedUri = Services.io.newURI(uri, null, null).spec
42 currentDir = dirname(".#{normalizedUri[prefix.length..]}")
43
44 unless require.scopes[normalizedUri]?
45 module = {
46 exports: {}
47 onShutdown: (fn) -> shutdownHandlers.push(fn)
48 }
49 require.scopes[normalizedUri] = scope = {
50 require: (path) -> require.call(null, path, moduleRoot, currentDir)
51 module, exports: module.exports
52 Cc, Ci, Cu, Services
53 ADDON_PATH, BUILD_TIME
54 IS_FRAME_SCRIPT
55 FRAME_SCRIPT_ENVIRONMENT: if IS_FRAME_SCRIPT then global else null
56 }
57 Services.scriptloader.loadSubScript(normalizedUri, scope, 'UTF-8')
58
59 return require.scopes[normalizedUri].module.exports
60
61 require.scopes = {}
62
63 startup = (args...) ->
64 main = if IS_FRAME_SCRIPT then './lib/main-frame' else './lib/main'
65 require(main)(args...)
66
67 shutdown = ->
68 for shutdownHandler in shutdownHandlers
69 try
70 shutdownHandler()
71 catch error
72 Cu.reportError(error)
73 shutdownHandlers = []
74
75 # Release everything in `require`d modules. This must be done _after_ all
76 # shutdownHandlers, since they use variables in these scopes.
77 for path, scope of require.scopes
78 for name of scope
79 scope[name] = null
80 require.scopes = {}
81
82 if IS_FRAME_SCRIPT
83 messageManager = require('./lib/message-manager')
84
85 # Tell the main process that this frame script was created, and ask if
86 # anything should be done in this frame.
87 messageManager.send('tabCreated', null, (ok) ->
88 # After dragging a tab from one window to another, `content` might have
89 # been set to `null` by Firefox when this runs. If so, simply return.
90 return unless ok and content?
91
92 startup()
93
94 messageManager.listenOnce('shutdown', shutdown)
95 )
96 else
97 global.startup = startup
98 global.shutdown = shutdown
99 global.install = ->
100 global.uninstall = ->
Imprint / Impressum