Add the ability to code generated prepopulated static nested structs
[chromium-blink-merge.git] / extensions / renderer / resources / runtime_custom_bindings.js
blob4e4a71d1e0a2c7b313c4d29d7123ac6b68bbc3ce
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
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   }
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) {
45       if (callback) {
46         if (!response) {
47           callback();
48           return;
49         }
50         var fileSystemId = response.fileSystemId;
51         var baseName = response.baseName;
52         var fs = GetIsolatedFileSystem(fileSystemId);
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;
79 binding.registerCustomHook(function(binding, id, contextType) {
80   var apiFunctions = binding.apiFunctions;
81   var runtime = binding.compiledApi;
83   //
84   // Unprivileged APIs.
85   //
87   if (id != '')
88     runtime.id = id;
90   apiFunctions.setHandleRequest('getManifest', function() {
91     return runtimeNatives.GetManifest();
92   });
94   apiFunctions.setHandleRequest('getURL', function(path) {
95     path = String(path);
96     if (!path.length || path[0] != '/')
97       path = '/' + path;
98     return 'chrome-extension://' + id + path;
99   });
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;
114     });
115     var port = runtime.connect(targetId || runtime.id, connectOptions);
116     messaging.sendMessageImpl(port, message, responseCallback);
117   });
119   apiFunctions.setHandleRequest('sendNativeMessage',
120                                 function(targetId, message, responseCallback) {
121     var port = runtime.connectNative(targetId);
122     messaging.sendMessageImpl(port, message, responseCallback);
123   });
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, {})
130     var nextArg = 0;
132     // targetId (first argument) is optional.
133     var targetId = null;
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];
145   });
147   apiFunctions.setUpdateArgumentsPreValidate('connectNative',
148                                              function(appName) {
149     if (typeof(appName) !== 'string') {
150       throw new Error('Invalid arguments to connectNative.');
151     }
152     return [appName];
153   });
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);
161     if (!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.
164       if (!runtime.id) {
165         throw new Error('chrome.runtime.connect() called from a webpage must ' +
166                         'specify an Extension ID (string) for its first ' +
167                         'argument');
168       }
169       targetId = runtime.id;
170     }
172     var name = '';
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);
181     if (portId >= 0)
182       return messaging.createPort(portId, name);
183   });
185   //
186   // Privileged APIs.
187   //
188   if (contextType != 'BLESSED_EXTENSION')
189     return;
191   apiFunctions.setHandleRequest('connectNative',
192                                 function(nativeAppName) {
193     if (!unloadEvent.wasDispatched) {
194       var portId = runtimeNatives.OpenChannelToNativeApp(runtime.id,
195                                                          nativeAppName);
196       if (portId >= 0)
197         return messaging.createPort(portId, '');
198     }
199     throw new Error('Error connecting to native app: ' + nativeAppName);
200   });
202   apiFunctions.setCustomCallback('getBackgroundPage',
203                                  function(name, request, callback, response) {
204     if (callback) {
205       var bg = runtimeNatives.GetExtensionViews(-1, 'BACKGROUND')[0] || null;
206       callback(bg);
207     }
208   });
210   bindDirectoryEntryCallback('getPackageDirectoryEntry', apiFunctions);
213 exports.bindDirectoryEntryCallback = bindDirectoryEntryCallback;
214 exports.binding = binding.generate();