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