| 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 // This script contains privileged chrome extension related javascript APIs. | 5 // This script contains privileged chrome extension related javascript APIs. |
| 6 // It is loaded by pages whose URL has the chrome-extension protocol. | 6 // It is loaded by pages whose URL has the chrome-extension protocol. |
| 7 | 7 |
| 8 var chrome = chrome || {}; | 8 var chrome = chrome || {}; |
| 9 (function() { | 9 (function() { |
| 10 native function GetChromeHidden(); | 10 native function GetChromeHidden(); |
| (...skipping 386 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 397 if (!isSchemaNodeSupported(t, platform, manifestVersion)) | 397 if (!isSchemaNodeSupported(t, platform, manifestVersion)) |
| 398 return; | 398 return; |
| 399 | 399 |
| 400 chromeHidden.validationTypes.push(t); | 400 chromeHidden.validationTypes.push(t); |
| 401 if (t.type == 'object' && customTypes[t.id]) { | 401 if (t.type == 'object' && customTypes[t.id]) { |
| 402 customTypes[t.id].prototype.setSchema(t); | 402 customTypes[t.id].prototype.setSchema(t); |
| 403 } | 403 } |
| 404 }); | 404 }); |
| 405 } | 405 } |
| 406 | 406 |
| 407 // Returns whether access to the content of a schema should be denied, |
| 408 // based on the presence of "unprivileged" and whether this is an |
| 409 // extension process (versus e.g. a content script). |
| 410 function isSchemaAccessAllowed(itemSchema) { |
| 411 return isExtensionProcess || |
| 412 apiDef.unprivileged || |
| 413 itemSchema.unprivileged; |
| 414 } |
| 415 |
| 407 // Adds a getter that throws an access denied error to object |module| | 416 // Adds a getter that throws an access denied error to object |module| |
| 408 // for property |name|. | 417 // for property |name|. |
| 409 // | 418 function addUnprivilegedAccessGetter(module, name) { |
| 410 // Returns true if the getter was necessary (access is disallowed), or | |
| 411 // false otherwise. | |
| 412 function addUnprivilegedAccessGetter(module, name, allowUnprivileged) { | |
| 413 if (allowUnprivileged || isExtensionProcess) | |
| 414 return false; | |
| 415 | |
| 416 module.__defineGetter__(name, function() { | 419 module.__defineGetter__(name, function() { |
| 417 throw new Error( | 420 throw new Error( |
| 418 '"' + name + '" can only be used in extension processes. See ' + | 421 '"' + name + '" can only be used in extension processes. See ' + |
| 419 'the content scripts documentation for more details.'); | 422 'the content scripts documentation for more details.'); |
| 420 }); | 423 }); |
| 421 return true; | |
| 422 } | 424 } |
| 423 | 425 |
| 424 // Setup Functions. | 426 // Setup Functions. |
| 425 if (apiDef.functions) { | 427 if (apiDef.functions) { |
| 426 apiDef.functions.forEach(function(functionDef) { | 428 apiDef.functions.forEach(function(functionDef) { |
| 429 if (functionDef.name in module) |
| 430 return; |
| 427 if (!isSchemaNodeSupported(functionDef, platform, manifestVersion)) | 431 if (!isSchemaNodeSupported(functionDef, platform, manifestVersion)) |
| 428 return; | 432 return; |
| 429 | 433 if (!isSchemaAccessAllowed(functionDef)) { |
| 430 if (functionDef.name in module || | 434 addUnprivilegedAccessGetter(module, functionDef.name); |
| 431 addUnprivilegedAccessGetter(module, functionDef.name, | |
| 432 functionDef.unprivileged)) { | |
| 433 return; | 435 return; |
| 434 } | 436 } |
| 435 | 437 |
| 436 var apiFunction = {}; | 438 var apiFunction = {}; |
| 437 apiFunction.definition = functionDef; | 439 apiFunction.definition = functionDef; |
| 438 apiFunction.name = apiDef.namespace + "." + functionDef.name; | 440 apiFunction.name = apiDef.namespace + "." + functionDef.name; |
| 439 apiFunctions.register(apiFunction.name, apiFunction); | 441 apiFunctions.register(apiFunction.name, apiFunction); |
| 440 | 442 |
| 441 module[functionDef.name] = (function() { | 443 module[functionDef.name] = (function() { |
| 442 var args = arguments; | 444 var args = arguments; |
| (...skipping 19 matching lines...) Expand all Loading... |
| 462 chromeHidden.validate([retval], [this.definition.returns]); | 464 chromeHidden.validate([retval], [this.definition.returns]); |
| 463 } | 465 } |
| 464 return retval; | 466 return retval; |
| 465 }).bind(apiFunction); | 467 }).bind(apiFunction); |
| 466 }); | 468 }); |
| 467 } | 469 } |
| 468 | 470 |
| 469 // Setup Events | 471 // Setup Events |
| 470 if (apiDef.events) { | 472 if (apiDef.events) { |
| 471 apiDef.events.forEach(function(eventDef) { | 473 apiDef.events.forEach(function(eventDef) { |
| 474 if (eventDef.name in module) |
| 475 return; |
| 472 if (!isSchemaNodeSupported(eventDef, platform, manifestVersion)) | 476 if (!isSchemaNodeSupported(eventDef, platform, manifestVersion)) |
| 473 return; | 477 return; |
| 474 | 478 if (!isSchemaAccessAllowed(eventDef)) { |
| 475 if (eventDef.name in module || | 479 addUnprivilegedAccessGetter(module, eventDef.name); |
| 476 addUnprivilegedAccessGetter(module, eventDef.name, | |
| 477 eventDef.unprivileged)) { | |
| 478 return; | 480 return; |
| 479 } | 481 } |
| 480 | 482 |
| 481 var eventName = apiDef.namespace + "." + eventDef.name; | 483 var eventName = apiDef.namespace + "." + eventDef.name; |
| 482 var customEvent = customEvents[apiDef.namespace]; | 484 var customEvent = customEvents[apiDef.namespace]; |
| 483 if (customEvent) { | 485 if (customEvent) { |
| 484 module[eventDef.name] = new customEvent( | 486 module[eventDef.name] = new customEvent( |
| 485 eventName, eventDef.parameters, eventDef.extraParameters, | 487 eventName, eventDef.parameters, eventDef.extraParameters, |
| 486 eventDef.options); | 488 eventDef.options); |
| 487 } else { | 489 } else { |
| 488 module[eventDef.name] = new chrome.Event( | 490 module[eventDef.name] = new chrome.Event( |
| 489 eventName, eventDef.parameters, eventDef.options); | 491 eventName, eventDef.parameters, eventDef.options); |
| 490 } | 492 } |
| 491 }); | 493 }); |
| 492 } | 494 } |
| 493 | 495 |
| 494 function addProperties(m, def) { | 496 function addProperties(m, parentDef) { |
| 495 // Parse any values defined for properties. | 497 var properties = parentDef.properties; |
| 496 if (def.properties) { | 498 if (!properties) |
| 497 forEach(def.properties, function(prop, property) { | 499 return; |
| 498 if (!isSchemaNodeSupported(property, platform, manifestVersion)) | |
| 499 return; | |
| 500 | 500 |
| 501 if (prop in m || | 501 forEach(properties, function(propertyName, propertyDef) { |
| 502 addUnprivilegedAccessGetter(m, prop, property.unprivileged)) { | 502 if (propertyName in m) |
| 503 return; | 503 return; |
| 504 if (!isSchemaNodeSupported(propertyDef, platform, manifestVersion)) |
| 505 return; |
| 506 if (!isSchemaAccessAllowed(propertyDef)) { |
| 507 addUnprivilegedAccessGetter(m, propertyName); |
| 508 return; |
| 509 } |
| 510 |
| 511 var value = propertyDef.value; |
| 512 if (value) { |
| 513 if (propertyDef.type === 'integer') { |
| 514 value = parseInt(value); |
| 515 } else if (propertyDef.type === 'boolean') { |
| 516 value = value === "true"; |
| 517 } else if (propertyDef["$ref"]) { |
| 518 var constructor = customTypes[propertyDef["$ref"]]; |
| 519 if (!constructor) |
| 520 throw new Error("No custom binding for " + propertyDef["$ref"]); |
| 521 var args = value; |
| 522 // For an object propertyDef, |value| is an array of constructor |
| 523 // arguments, but we want to pass the arguments directly |
| 524 // (i.e. not as an array), so we have to fake calling |new| on |
| 525 // the constructor. |
| 526 value = { __proto__: constructor.prototype }; |
| 527 constructor.apply(value, args); |
| 528 // Recursively add properties. |
| 529 addProperties(value, propertyDef); |
| 530 } else if (propertyDef.type === 'object') { |
| 531 // Recursively add properties. |
| 532 addProperties(value, propertyDef); |
| 533 } else if (propertyDef.type !== 'string') { |
| 534 throw "NOT IMPLEMENTED (extension_api.json error): Cannot " + |
| 535 "parse values for type \"" + propertyDef.type + "\""; |
| 504 } | 536 } |
| 505 | 537 } |
| 506 var value = property.value; | 538 if (value) { |
| 507 if (value) { | 539 m[propertyName] = value; |
| 508 if (property.type === 'integer') { | 540 } |
| 509 value = parseInt(value); | 541 }); |
| 510 } else if (property.type === 'boolean') { | |
| 511 value = value === "true"; | |
| 512 } else if (property["$ref"]) { | |
| 513 var constructor = customTypes[property["$ref"]]; | |
| 514 if (!constructor) | |
| 515 throw new Error("No custom binding for " + property["$ref"]); | |
| 516 var args = value; | |
| 517 // For an object property, |value| is an array of constructor | |
| 518 // arguments, but we want to pass the arguments directly | |
| 519 // (i.e. not as an array), so we have to fake calling |new| on | |
| 520 // the constructor. | |
| 521 value = { __proto__: constructor.prototype }; | |
| 522 constructor.apply(value, args); | |
| 523 // Recursively add properties. | |
| 524 addProperties(value, property); | |
| 525 } else if (property.type === 'object') { | |
| 526 // Recursively add properties. | |
| 527 addProperties(value, property); | |
| 528 } else if (property.type !== 'string') { | |
| 529 throw "NOT IMPLEMENTED (extension_api.json error): Cannot " + | |
| 530 "parse values for type \"" + property.type + "\""; | |
| 531 } | |
| 532 } | |
| 533 if (value) { | |
| 534 m[prop] = value; | |
| 535 } | |
| 536 }); | |
| 537 } | |
| 538 } | 542 } |
| 539 | 543 |
| 540 addProperties(module, apiDef); | 544 addProperties(module, apiDef); |
| 541 }); | 545 }); |
| 542 | 546 |
| 543 // TODO(aa): The rest of the crap below this really needs to be factored out | 547 // Run the non-declarative custom hooks after all the schemas have been |
| 544 // with a clean API boundary. Right now it is too soupy for me to feel | 548 // generated, in case hooks depend on other APIs being available. |
| 545 // comfortable running in content scripts. What if people are just | |
| 546 // overwriting random APIs? That would bypass our content script access | |
| 547 // checks. | |
| 548 if (!isExtensionProcess) | |
| 549 return; | |
| 550 | |
| 551 // TODO(kalman/aa): "The rest of this crap..." comment above. Only run the | |
| 552 // custom hooks in extension processes, to maintain current behaviour. We | |
| 553 // should fix this this with a smaller hammer. | |
| 554 apiDefinitions.forEach(function(apiDef) { | 549 apiDefinitions.forEach(function(apiDef) { |
| 555 if (!isSchemaNodeSupported(apiDef, platform, manifestVersion)) | 550 if (!isSchemaNodeSupported(apiDef, platform, manifestVersion)) |
| 556 return; | 551 return; |
| 557 | 552 |
| 558 var hook = customHooks[apiDef.namespace]; | 553 var hook = customHooks[apiDef.namespace]; |
| 559 if (!hook) | 554 if (!hook) |
| 560 return; | 555 return; |
| 561 | 556 |
| 562 // Pass through the public API of schema_generated_bindings, to be used | 557 // Pass through the public API of schema_generated_bindings, to be used |
| 563 // by custom bindings JS files. Create a new one so that bindings can't | 558 // by custom bindings JS files. Create a new one so that bindings can't |
| 564 // interfere with each other. | 559 // interfere with each other. |
| 565 hook({ | 560 hook({ |
| 566 apiFunctions: apiFunctions, | 561 apiFunctions: apiFunctions, |
| 567 sendRequest: sendRequest, | 562 sendRequest: sendRequest, |
| 568 setIcon: setIcon, | 563 setIcon: setIcon, |
| 569 apiDefinitions: apiDefinitions, | 564 apiDefinitions: apiDefinitions, |
| 570 }, extensionId); | 565 }, extensionId); |
| 571 }); | 566 }); |
| 572 | 567 |
| 573 // TOOD(mihaip): remove this alias once the webstore stops calling | 568 // TODO(mihaip): remove this alias once the webstore stops calling |
| 574 // beginInstallWithManifest2. | 569 // beginInstallWithManifest2. |
| 575 // See http://crbug.com/100242 | 570 // See http://crbug.com/100242 |
| 576 if (chrome.webstorePrivate) { | 571 if (chrome.webstorePrivate) { |
| 577 chrome.webstorePrivate.beginInstallWithManifest2 = | 572 chrome.webstorePrivate.beginInstallWithManifest2 = |
| 578 chrome.webstorePrivate.beginInstallWithManifest3; | 573 chrome.webstorePrivate.beginInstallWithManifest3; |
| 579 } | 574 } |
| 580 | 575 |
| 581 if (chrome.test) | 576 if (chrome.test) |
| 582 chrome.test.getApiDefinitions = GetExtensionAPIDefinition; | 577 chrome.test.getApiDefinitions = GetExtensionAPIDefinition; |
| 583 }); | 578 }); |
| 584 })(); | 579 })(); |
| OLD | NEW |