| OLD | NEW |
| (Empty) |
| 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. | |
| 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 |