Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(303)

Side by Side Diff: chrome/renderer/resources/extensions/schema_generated_bindings.js

Issue 9403006: Extensions: run "custom bindings" v8-extensions in content scripts. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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 })();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698