1 // Copyright 2014 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.
5 // Custom binding for the runtime API.
7 var binding
= require('binding').Binding
.create('runtime');
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
;
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
24 backgroundPage
= runtimeNatives
.GetExtensionViews(-1, 'BACKGROUND')[0];
26 var GetModuleSystem
= requireNative('v8_context').GetModuleSystem
;
27 backgroundRequire
= GetModuleSystem(backgroundPage
).require
;
29 backgroundPage
= window
;
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
, callback
, response
) {
50 var fileSystemId
= response
.fileSystemId
;
51 var baseName
= response
.baseName
;
52 var fs
= GetIsolatedFileSystem(fileSystemId
);
55 fs
.root
.getDirectory(baseName
, {}, callback
, function(fileError
) {
56 lastError
.run('runtime.' + functionName
,
57 'Error getting Entry, code: ' + fileError
.code
,
62 lastError
.run('runtime.' + functionName
,
71 // Force the runtime API to be loaded in the background page. Using
72 // backgroundPageModuleSystem.require('runtime') is insufficient as
73 // requireNative is only allowed while lazily loading an API.
74 backgroundPage
.chrome
.runtime
;
75 var bindDirectoryEntryCallback
= backgroundRequire(
76 'runtime').bindDirectoryEntryCallback
;
79 binding
.registerCustomHook(function(binding
, id
, contextType
) {
80 var apiFunctions
= binding
.apiFunctions
;
81 var runtime
= binding
.compiledApi
;
90 apiFunctions
.setHandleRequest('getManifest', function() {
91 return runtimeNatives
.GetManifest();
94 apiFunctions
.setHandleRequest('getURL', function(path
) {
96 if (!path
.length
|| path
[0] != '/')
98 return 'chrome-extension://' + id
+ path
;
101 var sendMessageUpdateArguments
= messaging
.sendMessageUpdateArguments
;
102 apiFunctions
.setUpdateArgumentsPreValidate('sendMessage',
103 $Function
.bind(sendMessageUpdateArguments
, null, 'sendMessage',
104 true /* hasOptionsArgument */));
105 apiFunctions
.setUpdateArgumentsPreValidate('sendNativeMessage',
106 $Function
.bind(sendMessageUpdateArguments
, null, 'sendNativeMessage',
107 false /* hasOptionsArgument */));
109 apiFunctions
.setHandleRequest('sendMessage',
110 function(targetId
, message
, options
, responseCallback
) {
111 var connectOptions
= {name
: messaging
.kMessageChannel
};
112 forEach(options
, function(k
, v
) {
113 connectOptions
[k
] = v
;
115 var port
= runtime
.connect(targetId
|| runtime
.id
, connectOptions
);
116 messaging
.sendMessageImpl(port
, message
, responseCallback
);
119 apiFunctions
.setHandleRequest('sendNativeMessage',
120 function(targetId
, message
, responseCallback
) {
121 var port
= runtime
.connectNative(targetId
);
122 messaging
.sendMessageImpl(port
, message
, responseCallback
);
125 apiFunctions
.setUpdateArgumentsPreValidate('connect', function() {
126 // Align missing (optional) function arguments with the arguments that
127 // schema validation is expecting, e.g.
128 // runtime.connect() -> runtime.connect(null, null)
129 // runtime.connect({}) -> runtime.connect(null, {})
132 // targetId (first argument) is optional.
134 if (typeof(arguments
[nextArg
]) == 'string')
135 targetId
= arguments
[nextArg
++];
137 // connectInfo (second argument) is optional.
138 var connectInfo
= null;
139 if (typeof(arguments
[nextArg
]) == 'object')
140 connectInfo
= arguments
[nextArg
++];
142 if (nextArg
!= arguments
.length
)
143 throw new Error('Invalid arguments to connect.');
144 return [targetId
, connectInfo
];
147 apiFunctions
.setUpdateArgumentsPreValidate('connectNative',
149 if (typeof(appName
) !== 'string') {
150 throw new Error('Invalid arguments to connectNative.');
155 apiFunctions
.setHandleRequest('connect', function(targetId
, connectInfo
) {
156 // Don't let orphaned content scripts communicate with their extension.
157 // http://crbug.com/168263
158 if (unloadEvent
.wasDispatched
)
159 throw new Error('Error connecting to extension ' + targetId
);
162 // runtime.id is only defined inside extensions. If we're in a webpage,
163 // the best we can do at this point is to fail.
165 throw new Error('chrome.runtime.connect() called from a webpage must ' +
166 'specify an Extension ID (string) for its first ' +
169 targetId
= runtime
.id
;
173 if (connectInfo
&& connectInfo
.name
)
174 name
= connectInfo
.name
;
176 var includeTlsChannelId
=
177 !!(connectInfo
&& connectInfo
.includeTlsChannelId
);
179 var portId
= runtimeNatives
.OpenChannelToExtension(targetId
, name
,
180 includeTlsChannelId
);
182 return messaging
.createPort(portId
, name
);
188 if (contextType
!= 'BLESSED_EXTENSION')
191 apiFunctions
.setHandleRequest('connectNative',
192 function(nativeAppName
) {
193 if (!unloadEvent
.wasDispatched
) {
194 var portId
= runtimeNatives
.OpenChannelToNativeApp(runtime
.id
,
197 return messaging
.createPort(portId
, '');
199 throw new Error('Error connecting to native app: ' + nativeAppName
);
202 apiFunctions
.setCustomCallback('getBackgroundPage',
203 function(name
, request
, callback
, response
) {
205 var bg
= runtimeNatives
.GetExtensionViews(-1, 'BACKGROUND')[0] || null;
210 bindDirectoryEntryCallback('getPackageDirectoryEntry', apiFunctions
);
213 exports
.bindDirectoryEntryCallback
= bindDirectoryEntryCallback
;
214 exports
.binding
= binding
.generate();