| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 require('json_schema'); | 5 require('json_schema'); |
| 6 require('event_bindings'); | 6 require('event_bindings'); |
| 7 var chrome = requireNative('chrome').GetChrome(); | 7 var chrome = requireNative('chrome').GetChrome(); |
| 8 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); | 8 var chromeHidden = requireNative('chrome_hidden').GetChromeHidden(); |
| 9 var forEach = require('utils').forEach; | 9 var forEach = require('utils').forEach; |
| 10 var GetAvailability = requireNative('v8_context').GetAvailability; | 10 var GetAvailability = requireNative('v8_context').GetAvailability; |
| (...skipping 185 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 196 compiledApi: api | 196 compiledApi: api |
| 197 }, extensionId, contextType); | 197 }, extensionId, contextType); |
| 198 }, this); | 198 }, this); |
| 199 }, | 199 }, |
| 200 | 200 |
| 201 // Generates the bindings from |this.schema_| and integrates any custom | 201 // Generates the bindings from |this.schema_| and integrates any custom |
| 202 // bindings that might be present. | 202 // bindings that might be present. |
| 203 generate: function() { | 203 generate: function() { |
| 204 var schema = this.schema_; | 204 var schema = this.schema_; |
| 205 | 205 |
| 206 function shouldCheckUnprivileged() { |
| 207 var shouldCheck = 'unprivileged' in schema; |
| 208 if (shouldCheck) |
| 209 return shouldCheck; |
| 210 |
| 211 ['functions', 'events'].forEach(function(type) { |
| 212 if (schema.hasOwnProperty(type)) { |
| 213 schema[type].forEach(function(node) { |
| 214 if ('unprivileged' in node) |
| 215 shouldCheck = true; |
| 216 }); |
| 217 } |
| 218 }); |
| 219 if (shouldCheck) |
| 220 return shouldCheck; |
| 221 |
| 222 for (var property in schema.properties) { |
| 223 if (schema.hasOwnProperty(property) && |
| 224 'unprivileged' in schema.properties[property]) { |
| 225 shouldCheck = true; |
| 226 break; |
| 227 } |
| 228 } |
| 229 return shouldCheck; |
| 230 } |
| 231 var checkUnprivileged = shouldCheckUnprivileged(); |
| 232 |
| 206 // TODO(kalman/cduvall): Make GetAvailability handle this, then delete the | 233 // TODO(kalman/cduvall): Make GetAvailability handle this, then delete the |
| 207 // supporting code. | 234 // supporting code. |
| 208 if (!isSchemaNodeSupported(schema, platform, manifestVersion)) { | 235 if (!isSchemaNodeSupported(schema, platform, manifestVersion)) { |
| 209 console.error('chrome.' + schema.namespace + ' is not supported on ' + | 236 console.error('chrome.' + schema.namespace + ' is not supported on ' + |
| 210 'this platform or manifest version'); | 237 'this platform or manifest version'); |
| 211 return undefined; | 238 return undefined; |
| 212 } | 239 } |
| 213 | 240 |
| 214 var availability = GetAvailability(schema.namespace); | |
| 215 if (!availability.is_available) { | |
| 216 console.error('chrome.' + schema.namespace + ' is not available: ' + | |
| 217 availability.message); | |
| 218 return undefined; | |
| 219 } | |
| 220 | |
| 221 // See comment on internalAPIs at the top. | |
| 222 var mod = {}; | 241 var mod = {}; |
| 223 | 242 |
| 224 var namespaces = schema.namespace.split('.'); | 243 var namespaces = schema.namespace.split('.'); |
| 225 for (var index = 0, name; name = namespaces[index]; index++) { | 244 for (var index = 0, name; name = namespaces[index]; index++) { |
| 226 mod[name] = mod[name] || {}; | 245 mod[name] = mod[name] || {}; |
| 227 mod = mod[name]; | 246 mod = mod[name]; |
| 228 } | 247 } |
| 229 | 248 |
| 230 // Add types to global schemaValidator, the types we depend on from other | 249 // Add types to global schemaValidator, the types we depend on from other |
| 231 // namespaces will be added as needed. | 250 // namespaces will be added as needed. |
| 232 if (schema.types) { | 251 if (schema.types) { |
| 233 forEach(schema.types, function(i, t) { | 252 forEach(schema.types, function(i, t) { |
| 234 if (!isSchemaNodeSupported(t, platform, manifestVersion)) | 253 if (!isSchemaNodeSupported(t, platform, manifestVersion)) |
| 235 return; | 254 return; |
| 236 | 255 |
| 237 schemaUtils.schemaValidator.addTypes(t); | 256 schemaUtils.schemaValidator.addTypes(t); |
| 238 }, this); | 257 }, this); |
| 239 } | 258 } |
| 240 | 259 |
| 260 // TODO(cduvall): Take out when all APIs have been converted to features. |
| 241 // Returns whether access to the content of a schema should be denied, | 261 // Returns whether access to the content of a schema should be denied, |
| 242 // based on the presence of "unprivileged" and whether this is an | 262 // based on the presence of "unprivileged" and whether this is an |
| 243 // extension process (versus e.g. a content script). | 263 // extension process (versus e.g. a content script). |
| 244 function isSchemaAccessAllowed(itemSchema) { | 264 function isSchemaAccessAllowed(itemSchema) { |
| 245 return (contextType == 'BLESSED_EXTENSION') || | 265 return (contextType == 'BLESSED_EXTENSION') || |
| 246 schema.unprivileged || | 266 schema.unprivileged || |
| 247 itemSchema.unprivileged; | 267 itemSchema.unprivileged; |
| 248 }; | 268 }; |
| 249 | 269 |
| 250 // Adds a getter that throws an access denied error to object |mod| | |
| 251 // for property |name|. | |
| 252 function addUnprivilegedAccessGetter(mod, name) { | |
| 253 mod.__defineGetter__(name, function() { | |
| 254 throw new Error( | |
| 255 '"' + name + '" can only be used in extension processes. See ' + | |
| 256 'the content scripts documentation for more details.'); | |
| 257 }); | |
| 258 } | |
| 259 | |
| 260 // Setup Functions. | 270 // Setup Functions. |
| 261 if (schema.functions) { | 271 if (schema.functions) { |
| 262 forEach(schema.functions, function(i, functionDef) { | 272 forEach(schema.functions, function(i, functionDef) { |
| 263 if (functionDef.name in mod) { | 273 if (functionDef.name in mod) { |
| 264 throw new Error('Function ' + functionDef.name + | 274 throw new Error('Function ' + functionDef.name + |
| 265 ' already defined in ' + schema.namespace); | 275 ' already defined in ' + schema.namespace); |
| 266 } | 276 } |
| 267 | 277 |
| 268 if (!isSchemaNodeSupported(functionDef, platform, manifestVersion)) { | 278 if (!isSchemaNodeSupported(functionDef, platform, manifestVersion)) { |
| 269 this.apiFunctions_.registerUnavailable(functionDef.name); | 279 this.apiFunctions_.registerUnavailable(functionDef.name); |
| 270 return; | 280 return; |
| 271 } | 281 } |
| 272 if (!isSchemaAccessAllowed(functionDef)) { | |
| 273 this.apiFunctions_.registerUnavailable(functionDef.name); | |
| 274 addUnprivilegedAccessGetter(mod, functionDef.name); | |
| 275 return; | |
| 276 } | |
| 277 | 282 |
| 278 var apiFunction = {}; | 283 var apiFunction = {}; |
| 279 apiFunction.definition = functionDef; | 284 apiFunction.definition = functionDef; |
| 280 apiFunction.name = schema.namespace + '.' + functionDef.name; | 285 apiFunction.name = schema.namespace + '.' + functionDef.name; |
| 281 | 286 |
| 287 if (!GetAvailability(apiFunction.name).is_available || |
| 288 (checkUnprivileged && !isSchemaAccessAllowed(functionDef))) { |
| 289 this.apiFunctions_.registerUnavailable(functionDef.name); |
| 290 return; |
| 291 } |
| 292 |
| 282 // TODO(aa): It would be best to run this in a unit test, but in order | 293 // TODO(aa): It would be best to run this in a unit test, but in order |
| 283 // to do that we would need to better factor this code so that it | 294 // to do that we would need to better factor this code so that it |
| 284 // doesn't depend on so much v8::Extension machinery. | 295 // doesn't depend on so much v8::Extension machinery. |
| 285 if (chromeHidden.validateAPI && | 296 if (chromeHidden.validateAPI && |
| 286 schemaUtils.isFunctionSignatureAmbiguous( | 297 schemaUtils.isFunctionSignatureAmbiguous( |
| 287 apiFunction.definition)) { | 298 apiFunction.definition)) { |
| 288 throw new Error( | 299 throw new Error( |
| 289 apiFunction.name + ' has ambiguous optional arguments. ' + | 300 apiFunction.name + ' has ambiguous optional arguments. ' + |
| 290 'To implement custom disambiguation logic, add ' + | 301 'To implement custom disambiguation logic, add ' + |
| 291 '"allowAmbiguousOptionalArguments" to the function\'s schema.'); | 302 '"allowAmbiguousOptionalArguments" to the function\'s schema.'); |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 329 | 340 |
| 330 // Setup Events | 341 // Setup Events |
| 331 if (schema.events) { | 342 if (schema.events) { |
| 332 forEach(schema.events, function(i, eventDef) { | 343 forEach(schema.events, function(i, eventDef) { |
| 333 if (eventDef.name in mod) { | 344 if (eventDef.name in mod) { |
| 334 throw new Error('Event ' + eventDef.name + | 345 throw new Error('Event ' + eventDef.name + |
| 335 ' already defined in ' + schema.namespace); | 346 ' already defined in ' + schema.namespace); |
| 336 } | 347 } |
| 337 if (!isSchemaNodeSupported(eventDef, platform, manifestVersion)) | 348 if (!isSchemaNodeSupported(eventDef, platform, manifestVersion)) |
| 338 return; | 349 return; |
| 339 if (!isSchemaAccessAllowed(eventDef)) { | 350 |
| 340 addUnprivilegedAccessGetter(mod, eventDef.name); | 351 var eventName = schema.namespace + "." + eventDef.name; |
| 352 if (!GetAvailability(eventName).is_available || |
| 353 (checkUnprivileged && !isSchemaAccessAllowed(eventDef))) { |
| 341 return; | 354 return; |
| 342 } | 355 } |
| 343 | 356 |
| 344 var eventName = schema.namespace + "." + eventDef.name; | |
| 345 var options = eventDef.options || {}; | 357 var options = eventDef.options || {}; |
| 346 | |
| 347 if (eventDef.filters && eventDef.filters.length > 0) | 358 if (eventDef.filters && eventDef.filters.length > 0) |
| 348 options.supportsFilters = true; | 359 options.supportsFilters = true; |
| 349 | 360 |
| 350 if (this.customEvent_) { | 361 if (this.customEvent_) { |
| 351 mod[eventDef.name] = new this.customEvent_( | 362 mod[eventDef.name] = new this.customEvent_( |
| 352 eventName, eventDef.parameters, eventDef.extraParameters, | 363 eventName, eventDef.parameters, eventDef.extraParameters, |
| 353 options); | 364 options); |
| 354 } else if (eventDef.anonymous) { | 365 } else if (eventDef.anonymous) { |
| 355 mod[eventDef.name] = new chrome.Event(); | 366 mod[eventDef.name] = new chrome.Event(); |
| 356 } else { | 367 } else { |
| 357 mod[eventDef.name] = new chrome.Event( | 368 mod[eventDef.name] = new chrome.Event( |
| 358 eventName, eventDef.parameters, options); | 369 eventName, eventDef.parameters, options); |
| 359 } | 370 } |
| 360 }, this); | 371 }, this); |
| 361 } | 372 } |
| 362 | 373 |
| 363 function addProperties(m, parentDef) { | 374 function addProperties(m, parentDef) { |
| 364 var properties = parentDef.properties; | 375 var properties = parentDef.properties; |
| 365 if (!properties) | 376 if (!properties) |
| 366 return; | 377 return; |
| 367 | 378 |
| 368 forEach(properties, function(propertyName, propertyDef) { | 379 forEach(properties, function(propertyName, propertyDef) { |
| 369 if (propertyName in m) | 380 if (propertyName in m) |
| 370 return; // TODO(kalman): be strict like functions/events somehow. | 381 return; // TODO(kalman): be strict like functions/events somehow. |
| 371 if (!isSchemaNodeSupported(propertyDef, platform, manifestVersion)) | 382 if (!isSchemaNodeSupported(propertyDef, platform, manifestVersion)) |
| 372 return; | 383 return; |
| 373 if (!isSchemaAccessAllowed(propertyDef)) { | 384 if (!GetAvailability(schema.namespace + "." + |
| 374 addUnprivilegedAccessGetter(m, propertyName); | 385 propertyName).is_available || |
| 386 (checkUnprivileged && !isSchemaAccessAllowed(propertyDef))) { |
| 375 return; | 387 return; |
| 376 } | 388 } |
| 377 | 389 |
| 378 var value = propertyDef.value; | 390 var value = propertyDef.value; |
| 379 if (value) { | 391 if (value) { |
| 380 // Values may just have raw types as defined in the JSON, such | 392 // Values may just have raw types as defined in the JSON, such |
| 381 // as "WINDOW_ID_NONE": { "value": -1 }. We handle this here. | 393 // as "WINDOW_ID_NONE": { "value": -1 }. We handle this here. |
| 382 // TODO(kalman): enforce that things with a "value" property can't | 394 // TODO(kalman): enforce that things with a "value" property can't |
| 383 // define their own types. | 395 // define their own types. |
| 384 var type = propertyDef.type || typeof(value); | 396 var type = propertyDef.type || typeof(value); |
| (...skipping 21 matching lines...) Expand all Loading... |
| 406 } else if (type !== 'string') { | 418 } else if (type !== 'string') { |
| 407 throw new Error('NOT IMPLEMENTED (extension_api.json error): ' + | 419 throw new Error('NOT IMPLEMENTED (extension_api.json error): ' + |
| 408 'Cannot parse values for type "' + type + '"'); | 420 'Cannot parse values for type "' + type + '"'); |
| 409 } | 421 } |
| 410 m[propertyName] = value; | 422 m[propertyName] = value; |
| 411 } | 423 } |
| 412 }); | 424 }); |
| 413 }; | 425 }; |
| 414 | 426 |
| 415 addProperties(mod, schema); | 427 addProperties(mod, schema); |
| 428 |
| 429 var availability = GetAvailability(schema.namespace); |
| 430 if (!availability.is_available && Object.keys(mod).length == 0) { |
| 431 console.error('chrome.' + schema.namespace + ' is not available: ' + |
| 432 availability.message); |
| 433 return; |
| 434 } |
| 435 |
| 416 this.runHooks_(mod); | 436 this.runHooks_(mod); |
| 417 return mod; | 437 return mod; |
| 418 } | 438 } |
| 419 }; | 439 }; |
| 420 | 440 |
| 421 exports.Binding = Binding; | 441 exports.Binding = Binding; |
| OLD | NEW |