OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 // Custom binding for the runtime API. | |
6 | |
7 var binding = require('binding').Binding.create('runtime'); | |
8 | |
9 var messaging = require('messaging'); | |
10 var runtimeNatives = requireNative('runtime'); | |
11 var unloadEvent = require('unload_event'); | |
12 var process = requireNative('process'); | |
13 var forEach = require('utils').forEach; | |
14 | |
15 var backgroundPage = window; | |
16 var backgroundRequire = require; | |
17 var contextType = process.GetContextType(); | |
18 if (contextType == 'BLESSED_EXTENSION' || | |
19 contextType == 'UNBLESSED_EXTENSION') { | |
20 var manifest = runtimeNatives.GetManifest(); | |
21 if (manifest.app && manifest.app.background) { | |
22 // Get the background page if one exists. Otherwise, default to the current | |
23 // window. | |
24 backgroundPage = runtimeNatives.GetExtensionViews(-1, 'BACKGROUND')[0]; | |
25 if (backgroundPage) { | |
26 var GetModuleSystem = requireNative('v8_context').GetModuleSystem; | |
27 backgroundRequire = GetModuleSystem(backgroundPage).require; | |
28 } else { | |
29 backgroundPage = window; | |
30 } | |
31 } | |
32 } | |
33 | |
34 // For packaged apps, all windows use the bindFileEntryCallback from the | |
35 // background page so their FileEntry objects have the background page's context | |
36 // as their own. This allows them to be used from other windows (including the | |
37 // background page) after the original window is closed. | |
38 if (window == backgroundPage) { | |
39 var lastError = require('lastError'); | |
40 var fileSystemNatives = requireNative('file_system_natives'); | |
41 var GetIsolatedFileSystem = fileSystemNatives.GetIsolatedFileSystem; | |
42 var bindDirectoryEntryCallback = function(functionName, apiFunctions) { | |
43 apiFunctions.setCustomCallback(functionName, | |
44 function(name, request, response) { | |
45 if (request.callback && response) { | |
46 var callback = request.callback; | |
47 request.callback = null; | |
48 | |
49 var fileSystemId = response.fileSystemId; | |
50 var baseName = response.baseName; | |
51 var fs = GetIsolatedFileSystem(fileSystemId); | |
52 | |
53 try { | |
54 fs.root.getDirectory(baseName, {}, callback, function(fileError) { | |
55 lastError.run('runtime.' + functionName, | |
56 'Error getting Entry, code: ' + fileError.code, | |
57 request.stack, | |
58 callback); | |
59 }); | |
60 } catch (e) { | |
61 lastError.run('runtime.' + functionName, | |
62 'Error: ' + e.stack, | |
63 request.stack, | |
64 callback); | |
65 } | |
66 } | |
67 }); | |
68 }; | |
69 } else { | |
70 // Force the runtime API to be loaded in the background page. Using | |
71 // backgroundPageModuleSystem.require('runtime') is insufficient as | |
72 // requireNative is only allowed while lazily loading an API. | |
73 backgroundPage.chrome.runtime; | |
74 var bindDirectoryEntryCallback = backgroundRequire( | |
75 'runtime').bindDirectoryEntryCallback; | |
76 } | |
77 | |
78 binding.registerCustomHook(function(binding, id, contextType) { | |
79 var apiFunctions = binding.apiFunctions; | |
80 var runtime = binding.compiledApi; | |
81 | |
82 // | |
83 // Unprivileged APIs. | |
84 // | |
85 | |
86 runtime.id = id; | |
87 | |
88 apiFunctions.setHandleRequest('getManifest', function() { | |
89 return runtimeNatives.GetManifest(); | |
90 }); | |
91 | |
92 apiFunctions.setHandleRequest('getURL', function(path) { | |
93 path = String(path); | |
94 if (!path.length || path[0] != '/') | |
95 path = '/' + path; | |
96 return 'chrome-extension://' + id + path; | |
97 }); | |
98 | |
99 var sendMessageUpdateArguments = messaging.sendMessageUpdateArguments; | |
100 apiFunctions.setUpdateArgumentsPreValidate('sendMessage', | |
101 $Function.bind(sendMessageUpdateArguments, null, 'sendMessage', | |
102 true /* hasOptionsArgument */)); | |
103 apiFunctions.setUpdateArgumentsPreValidate('sendNativeMessage', | |
104 $Function.bind(sendMessageUpdateArguments, null, 'sendNativeMessage', | |
105 false /* hasOptionsArgument */)); | |
106 | |
107 apiFunctions.setHandleRequest('sendMessage', | |
108 function(targetId, message, options, responseCallback) { | |
109 var connectOptions = {name: messaging.kMessageChannel}; | |
110 forEach(options, function(k, v) { | |
111 connectOptions[k] = v; | |
112 }); | |
113 var port = runtime.connect(targetId || runtime.id, connectOptions); | |
114 messaging.sendMessageImpl(port, message, responseCallback); | |
115 }); | |
116 | |
117 apiFunctions.setHandleRequest('sendNativeMessage', | |
118 function(targetId, message, responseCallback) { | |
119 var port = runtime.connectNative(targetId); | |
120 messaging.sendMessageImpl(port, message, responseCallback); | |
121 }); | |
122 | |
123 apiFunctions.setUpdateArgumentsPreValidate('connect', function() { | |
124 // Align missing (optional) function arguments with the arguments that | |
125 // schema validation is expecting, e.g. | |
126 // runtime.connect() -> runtime.connect(null, null) | |
127 // runtime.connect({}) -> runtime.connect(null, {}) | |
128 var nextArg = 0; | |
129 | |
130 // targetId (first argument) is optional. | |
131 var targetId = null; | |
132 if (typeof(arguments[nextArg]) == 'string') | |
133 targetId = arguments[nextArg++]; | |
134 | |
135 // connectInfo (second argument) is optional. | |
136 var connectInfo = null; | |
137 if (typeof(arguments[nextArg]) == 'object') | |
138 connectInfo = arguments[nextArg++]; | |
139 | |
140 if (nextArg != arguments.length) | |
141 throw new Error('Invalid arguments to connect.'); | |
142 return [targetId, connectInfo]; | |
143 }); | |
144 | |
145 apiFunctions.setUpdateArgumentsPreValidate('connectNative', | |
146 function(appName) { | |
147 if (typeof(appName) !== 'string') { | |
148 throw new Error('Invalid arguments to connectNative.'); | |
149 } | |
150 return [appName]; | |
151 }); | |
152 | |
153 apiFunctions.setHandleRequest('connect', function(targetId, connectInfo) { | |
154 // Don't let orphaned content scripts communicate with their extension. | |
155 // http://crbug.com/168263 | |
156 if (unloadEvent.wasDispatched) | |
157 throw new Error('Error connecting to extension ' + targetId); | |
158 | |
159 if (!targetId) | |
160 targetId = runtime.id; | |
161 | |
162 var name = ''; | |
163 if (connectInfo && connectInfo.name) | |
164 name = connectInfo.name; | |
165 | |
166 var includeTlsChannelId = | |
167 !!(connectInfo && connectInfo.includeTlsChannelId); | |
168 | |
169 var portId = runtimeNatives.OpenChannelToExtension(targetId, name, | |
170 includeTlsChannelId); | |
171 if (portId >= 0) | |
172 return messaging.createPort(portId, name); | |
173 }); | |
174 | |
175 // | |
176 // Privileged APIs. | |
177 // | |
178 if (contextType != 'BLESSED_EXTENSION') | |
179 return; | |
180 | |
181 apiFunctions.setHandleRequest('connectNative', | |
182 function(nativeAppName) { | |
183 if (!unloadEvent.wasDispatched) { | |
184 var portId = runtimeNatives.OpenChannelToNativeApp(runtime.id, | |
185 nativeAppName); | |
186 if (portId >= 0) | |
187 return messaging.createPort(portId, ''); | |
188 } | |
189 throw new Error('Error connecting to native app: ' + nativeAppName); | |
190 }); | |
191 | |
192 apiFunctions.setCustomCallback('getBackgroundPage', | |
193 function(name, request, response) { | |
194 if (request.callback) { | |
195 var bg = runtimeNatives.GetExtensionViews(-1, 'BACKGROUND')[0] || null; | |
196 request.callback(bg); | |
197 } | |
198 request.callback = null; | |
199 }); | |
200 | |
201 bindDirectoryEntryCallback('getPackageDirectoryEntry', apiFunctions); | |
202 }); | |
203 | |
204 exports.bindDirectoryEntryCallback = bindDirectoryEntryCallback; | |
205 exports.binding = binding.generate(); | |
OLD | NEW |