You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by do...@apache.org on 2008/03/11 15:21:19 UTC

svn commit: r635936 [1/2] - in /incubator/shindig/trunk/features: caja/caja.js caja/domita.js caja/feature.xml caja/html-sanitizer.js caja/html4-defs.js caja/unicode.js opensocial-reference/container.js

Author: doll
Date: Tue Mar 11 07:21:16 2008
New Revision: 635936

URL: http://svn.apache.org/viewvc?rev=635936&view=rev
Log:
patch from Mike Samuel

Updates the caja files in the features/caja dir and adds basic domita usage to container.js.


Added:
    incubator/shindig/trunk/features/caja/domita.js
    incubator/shindig/trunk/features/caja/html4-defs.js
    incubator/shindig/trunk/features/caja/unicode.js
Modified:
    incubator/shindig/trunk/features/caja/caja.js
    incubator/shindig/trunk/features/caja/feature.xml
    incubator/shindig/trunk/features/caja/html-sanitizer.js
    incubator/shindig/trunk/features/opensocial-reference/container.js

Modified: incubator/shindig/trunk/features/caja/caja.js
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/caja/caja.js?rev=635936&r1=635935&r2=635936&view=diff
==============================================================================
--- incubator/shindig/trunk/features/caja/caja.js (original)
+++ incubator/shindig/trunk/features/caja/caja.js Tue Mar 11 07:21:16 2008
@@ -1,25 +1,21 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
+// Copyright (C) 2007 Google Inc.
+//      
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// .............................................................................
 
 // This module is the Caja runtime library. It is written in
 // Javascript, not Caja, and would be rejected by the Caja
-// translator. This module exports two globals:
+// translator. This module exports two globals: 
 // * "___" for use by the output of the Caja translator and by some
 //   other untranslated Javascript code.
 // * "caja" providing some common services to the Caja programmer.
@@ -36,9 +32,9 @@
 ////////////////////////////////////////////////////////////////////////
 
 if (Array.prototype.indexOf === undefined) {
-  /**
+  /** 
    * Returns the first index at which the specimen is found (by
-   * "===") or -1 if none.
+   * "===") or -1 if none.  
    */
   Array.prototype.indexOf = function(specimen) {
     var len = this.length;
@@ -52,9 +48,9 @@
 }
 
 if (Array.prototype.lastIndexOf === undefined) {
-  /**
+  /** 
    * Returns the last index at which the specimen is found (by
-   * "===") or -1 if none.
+   * "===") or -1 if none.  
    */
   Array.prototype.lastIndexOf = function(specimen) {
     for (var i = this.length; --i >= 0; ) {
@@ -105,16 +101,16 @@
 // like HTMLDivElement.
 
 (function(global) {
-
+  
   ////////////////////////////////////////////////////////////////////////
   // Diagnostics and condition enforcement
   ////////////////////////////////////////////////////////////////////////
-
+  
   /**
-   * The initial default logging function does nothing.
+   * The initial default logging function does nothing. 
    * <p>
    * Note: JavaScript has no macros, so even in the "does nothing"
-   * case, remember that the arguments are still evaluated.
+   * case, remember that the arguments are still evaluated. 
    */
   var myLogFunc_ = function(str, opt_stop) {};
 
@@ -125,9 +121,9 @@
 
   /**
    * Register newLogFunc as the current logging function, to be called
-   * by <tt>___.log(str)</tt> and <tt>___.fail(...)</tt>.
+   * by <tt>___.log(str)</tt> and <tt>___.fail(...)</tt>. 
    * <p>
-   * A logging function is assumed to have the signature
+   * A logging function is assumed to have the signature 
    * <tt>(str, opt_stop)</tt>, where<ul>
    * <li><tt>str</tt> is the diagnostic string to be logged, and
    * <li><tt>opt_stop</tt>, if present and <tt>true</tt>, indicates
@@ -135,7 +131,7 @@
    *     throw. This provides the logging function the opportunity to
    *     terminate normal control flow in its own way, such as by
    *     invoking an undefined method, in order to trigger a Firebug
-   *     stacktrace.
+   *     stacktrace. 
    * </ul>
    */
   function setLogFunc(newLogFunc) { myLogFunc_ = newLogFunc; }
@@ -146,7 +142,7 @@
   function log(str) { myLogFunc_(String(str)); }
 
 
-  /**
+  /** 
    * Throw, and optionally log, an error whose message is the
    * concatentation of the arguments.
    * <p>
@@ -155,12 +151,13 @@
    * the message of the Error that's thrown.
    */
   function fail(var_args) {
+    (typeof console !== 'undefined') && console.trace();
     var message = Array.prototype.slice.call(arguments, 0).join('');
     myLogFunc_(message, true);
     throw new Error(message);
   }
-
-  /**
+  
+  /** 
    * Like an assert that can't be turned off.
    * <p>
    * Either returns true (on success) or throws (on failure). The
@@ -175,10 +172,10 @@
    * </pre>
    */
   function enforce(test, var_args) {
-    return test || fail.apply({},
+    return test || fail.apply({}, 
                               Array.prototype.slice.call(arguments, 1));
   }
-
+  
   /**
    * Enforces <tt>typeof specimen === typename</tt>, in which case
    * specimen is returned.
@@ -195,7 +192,7 @@
     }
     return specimen;
   }
-
+  
   /**
    * Enforces that specimen is a non-negative integer within the range
    * of exactly representable consecutive integers, in which case
@@ -221,13 +218,13 @@
     }
     return specimen;
   }
-
+  
   ////////////////////////////////////////////////////////////////////////
   // Privileged fault handlers
   ////////////////////////////////////////////////////////////////////////
-
+  
   /**
-   *
+   * 
    */
   var myKeeper_ = {
 
@@ -237,29 +234,29 @@
     toString: function() { return '<Logging Keeper>'; },
 
     /**
-     *
+     * 
      */
     handleRead: function(obj, name) {
       log('Not readable: (' + obj + ').' + name);
-      return undefined;
+      return undefined; 
     },
 
     /**
-     *
+     * 
      */
     handleCall: function(obj, name, args) {
       fail('Not callable: (', obj, ').', name);
     },
 
     /**
-     *
+     * 
      */
     handleSet: function(obj, name, val) {
       fail('Not settable: (', obj, ').', name);
     },
 
     /**
-     *
+     * 
      */
     handleDelete: function(obj, name) {
       fail('Not deletable: (', obj, ').', name);
@@ -267,17 +264,17 @@
   };
 
   /**
-   *
+   * 
    */
   function getKeeper() { return myKeeper_; }
 
   /**
-   *
+   * 
    */
   function setKeeper(newKeeper) { myKeeper_ = newKeeper; }
 
   /**
-   *
+   * 
    */
   Object.prototype.handleRead___ = function(name) {
     var handlerName = name + '_getter___';
@@ -288,7 +285,7 @@
   };
 
   /**
-   *
+   * 
    */
   Object.prototype.handleCall___ = function(name, args) {
     var handlerName = name + '_handler___';
@@ -299,7 +296,7 @@
   };
 
   /**
-   *
+   * 
    */
   Object.prototype.handleSet___ = function(name, val) {
     var handlerName = name + '_setter___';
@@ -310,7 +307,7 @@
   };
 
   /**
-   *
+   * 
    */
   Object.prototype.handleDelete___ = function(name) {
     var handlerName = name + '_deleter___';
@@ -319,15 +316,15 @@
     }
     return myKeeper_.handleDelete(this, name);
   };
-
+  
   ////////////////////////////////////////////////////////////////////////
   // Overriding some very basic primordial methods
   ////////////////////////////////////////////////////////////////////////
-
+  
   /**
    * Returns true only if we can call
    * Object.prototype.hasOwnProperty on this object without
-   * exploding.
+   * exploding. 
    * <p>
    * On Firefox, it seems that calling hasOwnProperty on an
    * HTMLDivElement sometimes causes an
@@ -336,7 +333,7 @@
    * SECURITY BUG STOPGAP TODO(erights)
    */
   var canCallHasOwnProperty = function(obj) { return true; };
-
+  
   // When we're in a non-browser environment, such that there isn't
   // a global HTMLDivElement, then we don't need to worry about
   // this bug.
@@ -345,45 +342,45 @@
       return !(obj instanceof global.HTMLDivElement);
     };
   }
-
+  
   var originalHOP_ = Object.prototype.hasOwnProperty;
-
+  
   /**
    * <tt>hasOwnProp(obj.prop)</tt> means what
    * <tt>obj.hasOwnProperty(prop)</tt> would normally mean in an
    * unmodified Javascript system.
    */
-  function hasOwnProp(obj, name) {
+  function hasOwnProp(obj, name) { 
     var t = typeof obj;
-    if (t !== 'object' && t !== 'function') {
-      return false;
+    if (t !== 'object' && t !== 'function') { 
+      return false; 
     }
     if (canCallHasOwnProperty(obj)) {
-      // Fails in Firefox for some DOM objects intermittently(?!)
+      // Fails in Firefox for some DOM objects intermittently(?!) 
       // with "Illegal operation on WrappedNative prototype object".
       // For these, canCallHasOwnProperty must say false.
-      return originalHOP_.call(obj, name);
+      return originalHOP_.call(obj, name); 
     } else {
       return false;
     }
   }
-
+  
   ////////////////////////////////////////////////////////////////////////
   // walking prototype chain, checking JSON containers
   ////////////////////////////////////////////////////////////////////////
-
+  
   /**
-   * Does str end with suffix?
+   * Does str end with suffix? 
    */
   function endsWith(str, suffix) {
     enforceType(str, 'string');
     enforceType(suffix, 'string');
     var strLen = str.length;
     var sufLen = suffix.length;
-    return strLen >= sufLen &&
+    return strLen >= sufLen && 
       (str.substring(strLen-sufLen, strLen) === suffix);
   }
-
+  
   /**
    * Returns the 'constructor' property of obj's prototype.
    * <p>
@@ -398,33 +395,37 @@
    */
   function directConstructor(obj) {
     if (obj === null) { return undefined; }
-    if (typeof obj !== 'object') {
-      // Note that functions thereby return undefined,
-      // so directConstructor() doesn't provide access to the
-      // forbidden Function constructor.
-      return undefined;
-    }
-    // The following test will initially return false in IE
-    if (hasOwnProp(obj, '__proto__')) {
-      if (obj.__proto__ === null) { return undefined; }
-      return obj.__proto__.constructor;
-    }
-    var result;
-    if (!hasOwnProp(obj, 'constructor')) {
-      result = obj.constructor;
-    } else {
-      var oldConstr = obj.constructor;
-      if (!(delete obj.constructor)) { return undefined; }
-      result = obj.constructor;
-      obj.constructor = oldConstr;
-    }
-    if (result.prototype.constructor === result) {
-      // Memoize, so it'll be faster next time.
-      obj.__proto__ = result.prototype;
+    try {
+      if (typeof obj !== 'object') {
+        // Note that functions thereby return undefined,
+        // so directConstructor() doesn't provide access to the
+        // forbidden Function constructor.
+        return undefined;
+      }
+      // The following test will initially return false in IE
+      if (hasOwnProp(obj, '__proto__')) { 
+        if (obj.__proto__ === null) { return undefined; }
+        return obj.__proto__.constructor; 
+      }
+      var result;
+      if (!hasOwnProp(obj, 'constructor')) { 
+        result = obj.constructor;
+      } else {
+        var oldConstr = obj.constructor;
+        if (!(delete obj.constructor)) { return undefined; }
+        result = obj.constructor;
+        obj.constructor = oldConstr;
+      }
+      if (result.prototype.constructor === result) {
+        // Memoize, so it'll be faster next time.
+        obj.__proto__ = result.prototype;
+      }
+      return result;
+    } catch (ex) {
+      return null;
     }
-    return result;
   }
-
+  
   /**
    * A JSON container is an object whose direct constructor is
    * Object or Array.
@@ -436,7 +437,7 @@
     var constr = directConstructor(obj);
     return constr === Object || constr === Array;
   }
-
+  
   /**
    * If obj is frozen, Caja code cannot directly assign to
    * properties of obj, nor directly add or delete properties to
@@ -449,14 +450,14 @@
    * If typeof <tt>obj</tt> is neither 'object' nor 'function', then
    * it's currently considered frozen.
    */
-  function isFrozen(obj) {
+  function isFrozen(obj) { 
     var t = typeof obj;
-    if (t !== 'object' && t !== 'function') {
-      return true;
+    if (t !== 'object' && t !== 'function') { 
+      return true; 
     }
-    return hasOwnProp(obj, '___FROZEN___');
+    return hasOwnProp(obj, '___FROZEN___'); 
   }
-
+  
   /**
    * Mark obj as frozen so that Caja code cannot directly assign to its
    * properties.
@@ -475,9 +476,9 @@
     // badFlags are names of properties we need to turn off.
     // We accumulate these first, so that we're not in the midst of a
     // for/in loop on obj while we're deleting properties from obj.
-    var badFlags = [];
+    var badFlags = []; 
     for (var k in obj) {
-      if (endsWith(k, '_canSet___') || endsWith(k, '_canDelete___')) {
+      if (endsWith(k, '_canSet___') || endsWith(k, '_canDelete___')) { 
         if (obj[k]) {
           badFlags.push(k);
         }
@@ -499,7 +500,7 @@
         // for a future optimization, where the
         // prototype can record as canSet those
         // properties that appear in instances that
-        // inherit from this prototype.
+        // inherit from this prototype. 
         obj[flag] = false;
       }
     }
@@ -510,7 +511,7 @@
     }
     return obj;
   }
-
+  
   /**
    * Like primFreeze(obj), but applicable only to JSON containers.
    */
@@ -520,7 +521,7 @@
     }
     return primFreeze(obj);
   }
-
+  
   /**
    * Makes a mutable copy of a JSON container.
    * <p>
@@ -536,19 +537,19 @@
     }));
     return result;
   }
-
+  
   /**
    * A snapshot of a JSON container is a frozen copy of that
-   * container.
+   * container. 
    */
   function snapshot(obj) {
     return primFreeze(copy(obj));
   }
-
+  
   ////////////////////////////////////////////////////////////////////////
   // Accessing property attributes
   ////////////////////////////////////////////////////////////////////////
-
+  
   /** Tests whether the fast-path canRead flag is set. */
   function canRead(obj, name)   { return !!obj[name + '_canRead___']; }
   /** Tests whether the fast-path canEnum flag is set. */
@@ -559,33 +560,33 @@
   function canSet(obj, name)    { return !!obj[name + '_canSet___']; }
   /** Tests whether the fast-path canDelete flag is set. */
   function canDelete(obj, name) { return !!obj[name + '_canDelete___']; }
-
-  /**
+  
+  /** 
    * Sets the fast-path canRead flag.
    * <p>
    * The various <tt>allow*</tt> functions are called externally by
    * Javascript code to express whitelisting taming decisions. And
    * they are called internally to memoize decisions arrived at by
-   * other means.
+   * other means. 
    */
-  function allowRead(obj, name) {
-    obj[name + '_canRead___'] = true;
+  function allowRead(obj, name) { 
+    obj[name + '_canRead___'] = true; 
   }
-
+  
   /** allowEnum implies allowRead */
-  function allowEnum(obj, name) {
+  function allowEnum(obj, name) { 
     allowRead(obj, name);
     obj[name + '_canEnum___'] = true;
   }
-
-  /**
+  
+  /** 
    * Simple functions should callable and readable, but methods
    * should only be callable.
    */
-  function allowCall(obj, name) {
-    obj[name + '_canCall___'] = true;
+  function allowCall(obj, name) { 
+    obj[name + '_canCall___'] = true; 
   }
-
+  
   /**
    * allowSet implies allowEnum and allowRead.
    */
@@ -596,10 +597,10 @@
     allowEnum(obj, name);
     obj[name + '_canSet___'] = true;
   }
-
+  
   /**
    * BUG TODO(erights): allowDelete is not yet specified or
-   * implemented.
+   * implemented. 
    */
   function allowDelete(obj, name) {
     if (isFrozen(obj)) {
@@ -608,16 +609,16 @@
     fail('TODO(erights): allowDelete() not yet implemented');
     obj[name + '_canDelete___'] = true;
   }
-
+  
   ////////////////////////////////////////////////////////////////////////
   // Classifying functions
   ////////////////////////////////////////////////////////////////////////
-
+  
   function isCtor(constr)    { return !!constr.___CONSTRUCTOR___; }
   function isMethod(meth)    { return '___METHOD_OF___' in meth; }
   function isSimpleFunc(fun) { return !!fun.___SIMPLE_FUNC___; }
-
-  /**
+  
+  /** 
    * Mark constr as a constructor.
    * <p>
    * If opt_Sup is provided, set constr.Super = opt_Sup.
@@ -626,7 +627,7 @@
    * method(), or simpleFunc(). Each of these checks that the
    * function hasn't already been classified by any of the others. A
    * function which has not been so classified is an <i>untamed
-   * function</i>.
+   * function</i>. 
    * <p>
    * opt_name, if provided, should be the name of the constructor
    * function. Currently, this is used only to generate friendlier
@@ -656,9 +657,9 @@
     }
     return constr;  // translator freezes constructor later
   }
-
-  /**
-   * Mark meth as a method of instances of constr.
+  
+  /** 
+   * Mark meth as a method of instances of constr. 
    * <p>
    * opt_name, if provided, should be the message name associated
    * with the method. Currently, this is used only to generate
@@ -675,11 +676,11 @@
     meth.___METHOD_OF___ = asCtorOnly(constr);
     return primFreeze(meth);
   }
-
-  /**
+  
+  /** 
    * Mark fun as a simple function.
    * <p>
-   * opt_name, if provided, should be the name of the
+   * opt_name, if provided, should be the name of the 
    * function. Currently, this is used only to generate friendlier
    * error messages.
    */
@@ -696,47 +697,47 @@
     fun.call_canCall___ = true;
     return fun;  // translator freezes fun later
   }
-
+  
   /** This "Only" form doesn't freeze */
   function asCtorOnly(constr) {
-    if (isCtor(constr) || isSimpleFunc(constr)) {
-      return constr;
+    if (isCtor(constr) || isSimpleFunc(constr)) { 
+      return constr; 
     }
-
+    
     enforceType(constr, 'function');
     if (isMethod(constr)) {
       fail("Methods can't be called as constructors: ", constr);
     }
     fail("Untamed functions can't be called as constructors: ", constr);
   }
-
+  
   /** Only constructors and simple functions can be called as constructors */
   function asCtor(constr) {
-    return primFreeze(asCtorOnly(constr));
+    return primFreeze(asCtorOnly(constr)); 
   }
-
+  
   /** Only methods and simple functions can be called as methods */
   function asMethod(meth) {
-    if (isSimpleFunc(meth) || isMethod(meth)) {
+    if (isSimpleFunc(meth) || isMethod(meth)) { 
       if (!isFrozen(meth)) {
         fail('internal: non-frozen func stored as method: ', meth);
       }
-      return meth;
+      return meth; 
     }
-
+    
     enforceType(meth, 'function');
     if (isCtor(meth)) {
       fail("Constructors can't be called as methods: ", meth);
     }
     fail("Untamed functions can't be called as methods: ", meth);
   }
-
+  
   /** Only simple functions can be called as simple functions */
   function asSimpleFunc(fun) {
-    if (isSimpleFunc(fun)) {
-      return primFreeze(fun);
+    if (isSimpleFunc(fun)) { 
+      return primFreeze(fun); 
     }
-
+    
     enforceType(fun, 'function');
     if (isCtor(fun)) {
       if (fun === Number || fun === String || fun === Boolean) {
@@ -770,8 +771,8 @@
     }
     fail("Untamed functions can't be called as simple functions: ", fun);
   }
-
-  /**
+  
+  /** 
    * Sets constr.prototype[name] = member.
    * <p>
    * If member is a method of constr, make it callable.
@@ -798,13 +799,13 @@
     }
     proto[name] = member;
   }
-
+  
   ////////////////////////////////////////////////////////////////////////
   // Accessing properties
   ////////////////////////////////////////////////////////////////////////
-
-  /**
-   * Can a constructed Caja object read this property on itself?
+  
+  /** 
+   * Can a constructed Caja object read this property on itself? 
    * <p>
    * Can a Caja method whose <tt>this</tt> is bound to <tt>that</tt>
    * read its own <tt>name</tt> property? For properties added to
@@ -818,8 +819,8 @@
     if (endsWith(name, '__')) { return false; }
     return canRead(that, name);
   }
-
-  /**
+  
+  /** 
    * A constructed Caja object's attempt to read this property on
    * itself.
    * <p>
@@ -829,9 +830,9 @@
     name = String(name);
     return canReadProp(that, name) ? that[name] : that.handleRead___(name);
   }
-
-  /**
-   * Can a Caja client of <tt>obj</tt> read its <name> property?
+  
+  /** 
+   * Can a Caja client of <tt>obj</tt> read its <name> property? 
    * <p>
    * If the property is Internal (i.e. ends in an '_'), then no.
    * If the property was defined by Caja code, then yes. If it was
@@ -847,7 +848,7 @@
     allowRead(obj, name);  // memoize
     return true;
   }
-
+  
   /**
    * Caja code attempting to read a property on something besides
    * <tt>this</tt>.
@@ -858,7 +859,7 @@
     name = String(name);
     return canReadPub(obj, name) ? obj[name] : obj.handleRead___(name);
   }
-
+  
   /**
    * Can "innocent" code enumerate the named property on this object?
    * <p>
@@ -884,9 +885,9 @@
     if (endsWith(name, '___')) { return false; }
     return true;
   }
-
-  /**
-   * Would a Caja for/in loop on <tt>this</tt> see this name?
+  
+  /** 
+   * Would a Caja for/in loop on <tt>this</tt> see this name? 
    * <p>
    * For properties defined in Caja, this is generally the same as
    * canReadProp. Otherwise according to whitelisting.
@@ -896,9 +897,9 @@
     if (endsWith(name, '__')) { return false; }
     return canEnum(that, name);
   }
-
-  /**
-   * Would a Caja for/in loop by a client of obj see this name?
+  
+  /** 
+   * Would a Caja for/in loop by a client of obj see this name? 
    * <p>
    * For properties defined in Caja, this is generally the same as
    * canReadProp. Otherwise according to whitelisting.
@@ -912,7 +913,7 @@
     allowEnum(obj, name);  // memoize
     return true;
   }
-
+  
   /**
    * Like canEnumPub, but allows only non-inherited properties.
    */
@@ -920,14 +921,14 @@
     name = String(name);
     return hasOwnProp(obj, name) && canEnumPub(obj, name);
   }
-
+  
   /**
    * Inside a <tt>caja.each()</tt>, the body function can terminate
    * early, as if with a conventional <tt>break;</tt>, by doing a
    * <pre>return caja.BREAK;</pre>
    */
   var BREAK = {};
-
+  
   /**
    * For each sensible key/value pair in obj, call fn with that
    * pair.
@@ -954,7 +955,7 @@
       }
     }
   }
-
+  
   /**
    * Can this be called as an internal method?
    * <p>
@@ -964,7 +965,7 @@
    * which we can memoize.
    * <p>
    * SECURITY HAZARD TODO(erights): If a settable property is
-   * first set to a
+   * first set to a 
    * simple function, which is then called, memoizing canCall, and
    * then set to some other kind of function which leaked (such as
    * an untamed function), then that other function can be
@@ -983,7 +984,7 @@
    * after a set will re-check the value to be called.
    * <p>
    * This plan will need to be thought through again when we
-   * implement property deletion.
+   * implement property deletion. 
    */
   function canCallProp(that, name) {
     name = String(name);
@@ -995,7 +996,7 @@
     allowCall(that, name);  // memoize
     return true;
   }
-
+  
   /**
    * A Caja method tries to call one of its Internal methods.
    */
@@ -1008,7 +1009,7 @@
       return that.handleCall___(name, args);
     }
   }
-
+  
   /**
    * Like canCallProp(), with differences that parallel the
    * differences between canReadProp vs canReadPub.
@@ -1023,7 +1024,7 @@
     allowCall(obj, name);  // memoize
     return true;
   }
-
+  
   /**
    * A client of obj tries to call one of its methods.
    */
@@ -1032,12 +1033,14 @@
     if (canCallPub(obj, name)) {
       var meth = obj[name];
       return meth.apply(obj, args);
-    } else {
+    } else if (obj.handleCall___) {
       return obj.handleCall___(name, args);
+    } else {
+      fail('not callable %o %s', obj, name);
     }
   }
-
-  /**
+  
+  /** 
    * Can a method of a Caja constructed object directly assign to
    * this property of its object?
    * <p>
@@ -1049,7 +1052,7 @@
     if (canSet(that, name)) { return true; }
     return !isFrozen(that);
   }
-
+  
   /**
    * A Caja method tries to assign to this property of its object.
    */
@@ -1063,12 +1066,12 @@
       return that.handleSet___(name, val);
     }
   }
-
+  
   /**
    * Can a client of obj directly assign to its name property?
    * <p>
    * If this property is Internal (i.e., ends with a '_') or if this
-   * object is frozen, then no.
+   * object is frozen, then no. 
    * If this property is not Internal and was defined by Caja code,
    * then yes. If the object is a JSON container, then
    * yes. Otherwise according to whitelisting decisions.
@@ -1085,7 +1088,7 @@
     if (canSet(obj, name)) { return true; }
     return !isFrozen(obj) && isJSONContainer(obj);
   }
-
+  
   /** A client of obj attempts to assign to one of its properties. */
   function setPub(obj, name, val) {
     name = String(name);
@@ -1097,22 +1100,22 @@
       return obj.handleSet___(name, val);
     }
   }
-
+  
   /**
    * Can a Caja constructed object delete the named property?
    * <p>
    * BUG TODO(erights): This is not yet supported. The precise
    * enabling conditions are not yet determined, as is the implied
-   * bookkeeping.
+   * bookkeeping. 
    */
   function canDeleteProp(that, name) {
     fail('TODO(erights): deletion not yet supported');
     return false;
   }
-
+  
   /**
    * A Caja constructed object attempts to delete one of its own
-   * properties.
+   * properties. 
    * <p>
    * BUG TODO(erights): This is not yet supported. The precise
    * enabling conditions are not yet determined, as is the implied
@@ -1128,26 +1131,26 @@
       return that.handleDelete___(name);
     }
   }
-
+  
   /**
    * Can a client of obj delete the named property?
    * <p>
    * BUG TODO(erights): This is not yet supported. The precise
    * enabling conditions are not yet determined, as is the implied
-   * bookkeeping.
+   * bookkeeping. 
    */
   function canDeletePub(obj, name) {
     fail('TODO(erights): deletion not yet supported');
     return false;
   }
-
+  
   /**
    * A client of obj can only delete a property of obj if obj is a
    * non-frozen JSON container.
    * <p>
    * BUG TODO(erights): This is not yet supported. The precise
    * enabling conditions are not yet determined, as is the implied
-   * bookkeeping.
+   * bookkeeping. 
    */
   function deletePub(obj, name) {
     name = String(name);
@@ -1162,11 +1165,11 @@
       return obj.handleDelete___(name);
     }
   }
-
+  
   ////////////////////////////////////////////////////////////////////////
   // Other
   ////////////////////////////////////////////////////////////////////////
-
+  
   /**
    * This returns a frozen array copy of the original array or
    * array-like object.
@@ -1182,7 +1185,7 @@
   function args(original) {
     return primFreeze(Array.prototype.slice.call(original, 0));
   }
-
+  
   /**
    *
    */
@@ -1191,7 +1194,7 @@
       setMember(sub, mname, member);
     }));
   }
-
+  
   /**
    * Provides a shorthand for a class-like declaration of a fresh
    * Caja constructor.
@@ -1203,7 +1206,7 @@
    * opt_statics added as members to sub.
    * <p>
    * TODO(erights): return a builder object that allows further
-   * initialization.
+   * initialization. 
    */
   function def(sub, opt_Sup, opt_members, opt_statics) {
     var sup = opt_Sup || Object;
@@ -1213,21 +1216,21 @@
       fail('The static name "Super" is reserved ',
            'for the super-constructor');
     }
-
+    
     ctor(sub, sup);
     function PseudoSuper() {}
     PseudoSuper.prototype = sup.prototype;
     sub.prototype = new PseudoSuper();
     sub.prototype.constructor = sub;
-
+    
     setMemberMap(sub, members);
     each(statics, simpleFunc(function(sname, staticMember) {
       setPub(sub, sname, staticMember);
     }));
-
+    
     // translator freezes sub and sub.prototype later.
   }
-
+  
   ////////////////////////////////////////////////////////////////////////
   // Taming mechanism
   ////////////////////////////////////////////////////////////////////////
@@ -1235,7 +1238,7 @@
   /**
    * Arrange to handle read-faults on <tt>obj[name]</tt>
    * by calling <tt>getHandler()</tt> as a method on the faulted
-   * object.
+   * object. 
    * <p>
    * In order for this fault-handler to get control, it's important
    * that no one does a conflicting allowRead().
@@ -1247,7 +1250,7 @@
   /**
    * Arrange to handle call-faults on <tt>obj[name](args...)</tt> by
    * calling <tt>applyHandler(args)</tt> as a method on the faulted
-   * object.
+   * object. 
    * <p>
    * Note that <tt>applyHandler</tt> is called with a single argument,
    * which is the list of arguments in the original call.
@@ -1263,10 +1266,10 @@
   /**
    * Arrange to handle call-faults on <tt>obj[name](args...)</tt> by
    * calling <tt>callHandler(args...)</tt> as a method on the faulted
-   * object.
+   * object. 
    * <p>
    * Note that <tt>callHandler</tt> is called with the same arguments
-   * as the original call.
+   * as the original call. 
    * <p>
    * In order for this fault-handler to get control, it's important
    * that no one does a conflicting allowCall(), allowSimpleFunc(), or
@@ -1281,7 +1284,7 @@
   /**
    * Arrange to handle set-faults on <tt>obj[name] = newValue</tt> by
    * calling <tt>setHandler(newValue)</tt> as a method on the faulted
-   * object.
+   * object.  
    * <p>
    * In order for this fault-handler to get control, it's important
    * that no one does a conflicting allowSet().
@@ -1292,7 +1295,7 @@
 
   /**
    * Arrange to handle delete-faults on <tt>delete obj[name]</tt> by
-   * calling <tt>deleteHandler()</tt> as a method on the faulted object.
+   * calling <tt>deleteHandler()</tt> as a method on the faulted object. 
    * <p>
    * In order for this fault-handler to get control, it's important
    * that no one does a conflicting allowDelete().
@@ -1310,7 +1313,7 @@
     allowCall(obj, name);
     allowRead(obj, name);
   }
-
+  
   /**
    * Whitelist constr.prototype[name] as a method that can be called
    * on instances of constr.
@@ -1319,7 +1322,7 @@
     method(constr, constr.prototype[name], name);
     allowCall(constr.prototype, name);
   }
-
+  
   /**
    * Virtually replace constr.prototype[name] with a fault-handler
    * wrapper that first verifies that <tt>this</tt> isn't frozen.
@@ -1329,7 +1332,7 @@
    * mutation from violating Caja semantics. In order for this fault
    * handler to get control, it's important that no one does an
    * allowCall(), allowSimpleFunc(), or allowMethod() on the
-   * original method.
+   * original method. 
    */
   function allowMutator(constr, name) {
     var original = constr.prototype[name];
@@ -1340,7 +1343,7 @@
       return original.apply(this, args);
     });
   }
-
+  
   /**
    * Verifies that regexp is something that can appear as a
    * parameter to a Javascript method that would use it in a match.
@@ -1355,7 +1358,7 @@
       }
     }
   }
-
+  
   /**
    * A shorthand that happens to be useful here.
    * <p>
@@ -1367,7 +1370,7 @@
       func2(arg1, arg2s[i]);
     }
   }
-
+  
   ////////////////////////////////////////////////////////////////////////
   // Taming decisions
   ////////////////////////////////////////////////////////////////////////
@@ -1380,8 +1383,8 @@
     'abs', 'acos', 'asin', 'atan', 'atan2', 'ceil', 'cos', 'exp', 'floor',
     'log', 'max', 'min', 'pow', 'random', 'round', 'sin', 'sqrt', 'tan'
   ]);
-
-
+  
+  
   ctor(Object, undefined, 'Object');
   all2(allowMethod, Object, [
     'toString', 'toLocaleString', 'valueOf', 'isPrototypeOf'
@@ -1409,13 +1412,13 @@
   useCallHandler(Object.prototype, 'freeze_', function() {
     return primFreeze(this);
   });
-
-
+  
+  
   // SECURITY HAZARD TODO(erights): Seems dangerous, but doesn't add
-  // risk. Or does it?
+  // risk. Or does it? 
   ctor(Function, Object, 'Function');
   // SECURITY HAZARD TODO(erights): Seems dangerous, but doesn't add
-  // risk. Or does it?
+  // risk. Or does it? 
   allowRead(Function.prototype, 'prototype');
 
   useCallHandler(Function.prototype, 'apply', function(that, realArgs) {
@@ -1424,8 +1427,8 @@
   useCallHandler(Function.prototype, 'call', function(that, realArgs) {
     return asSimpleFunc(this).apply(that, realArgs);
   });
-
-
+  
+  
   ctor(Array, Object, 'Array');
   all2(allowMethod, Array, [
     'concat', 'join', 'slice', 'indexOf', 'lastIndexOf'
@@ -1433,8 +1436,8 @@
   all2(allowMutator, Array, [
     'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'
   ]);
-
-
+  
+  
   ctor(String, Object, 'String');
   allowSimpleFunc(String, 'fromCharCode');
   all2(allowMethod, String, [
@@ -1446,7 +1449,7 @@
     enforceMatchable(regexp);
     return this.match(regexp);
   });
-  useCallHandler(String.prototype, 'replace', function(searchValue,
+  useCallHandler(String.prototype, 'replace', function(searchValue, 
                                                        replaceValue) {
     enforceMatchable(searchValue);
     return this.replace(searchValue, replaceValue);
@@ -1459,11 +1462,11 @@
     enforceMatchable(separator);
     return this.split(separator, limit);
   });
-
-
+  
+  
   ctor(Boolean, Object, 'Boolean');
-
-
+  
+  
   ctor(Number, Object, 'Number');
   all2(allowRead, Number, [
     'MAX_VALUE', 'MIN_VALUE', 'NaN',
@@ -1472,18 +1475,18 @@
   all2(allowMethod, Number, [
     'toFixed', 'toExponential', 'toPrecision'
   ]);
-
-
+  
+  
   ctor(Date, Object, 'Date');
   allowSimpleFunc(Date, 'parse');
   allowSimpleFunc(Date, 'UTC');
-
+  
   all2(allowMethod, Date, [
     'toDateString', 'toTimeString', 'toUTCString',
     'toLocaleString', 'toLocaleDateString', 'toLocaleTimeString',
     'toISOString',
     'getDay', 'getUTCDay', 'getTimezoneOffset',
-
+    
     'getTime', 'getFullYear', 'getUTCFullYear', 'getMonth', 'getUTCMonth',
     'getDate', 'getUTCDate', 'getHours', 'getUTCHours',
     'getMinutes', 'getUTCMinutes', 'getSeconds', 'getUTCSeconds',
@@ -1495,17 +1498,17 @@
     'setMinutes', 'setUTCMinutes', 'setSeconds', 'setUTCSeconds',
     'setMilliseconds', 'setUTCMilliseconds'
   ]);
-
-
+  
+  
   ctor(RegExp, Object, 'RegExp');
   allowMutator(RegExp, 'exec');
   allowMutator(RegExp, 'test');
-
+  
   all2(allowRead, RegExp, [
     'source', 'global', 'ignoreCase', 'multiline', 'lastIndex'
   ]);
-
-
+  
+  
   ctor(Error, Object, 'Error');
   allowRead(Error, 'name');
   allowRead(Error, 'message');
@@ -1515,23 +1518,23 @@
   ctor(SyntaxError, Error, 'SyntaxError');
   ctor(TypeError, Error, 'TypeError');
   ctor(URIError, Error, 'URIError');
-
-
+  
+  
   var sharedOuters;
-
+  
   ////////////////////////////////////////////////////////////////////////
   // Module loading
   ////////////////////////////////////////////////////////////////////////
-
+  
   var myNewModuleHandler;
-
+  
   /**
    * Gets the current module handler.
    */
   function getNewModuleHandler() {
     return myNewModuleHandler;
   }
-
+  
   /**
    * Registers a new-module-handler, to be called back when a new
    * module is loaded.
@@ -1545,14 +1548,14 @@
   function setNewModuleHandler(newModuleHandler) {
     myNewModuleHandler = newModuleHandler;
   }
-
+  
   /**
    * A new-module-handler which does nothing.
    */
   var ignoreNewModule = freeze({
     handle: simpleFunc(function(newModule){})
   });
-
+  
   /**
    * Makes and returns a fresh "normal" module handler whose outers
    * are initialized to a copy of the sharedOuters.
@@ -1572,13 +1575,13 @@
       })
     });
   }
-
+  
   /**
    * A module is a plugin-maker function.
    * <p>
    * loadModule(module) marks module as a simpleFunc, freezes it,
    * asks the current new-module-handler to handle it (thereby
-   * notifying the handler), and returns the new module.
+   * notifying the handler), and returns the new module.  
    */
   function loadModule(module) {
     callPub(myNewModuleHandler, 'handle',
@@ -1590,7 +1593,7 @@
 
   /**
    * Gets or assigns the id associated with this (assumed to be)
-   * outers object, registering it so that
+   * outers object, registering it so that 
    * <tt>getOuters(getId(outers)) ==== outers</tt>.
    * <p>
    * This system of registration and identification allows us to
@@ -1651,21 +1654,21 @@
    * reregisters itself at its old id.
    */
   function unregister(outers) {
-    enforceType(outers, 'object', 'outers');
+    enforceType(outers, 'object', 'outers');      
     if ('id___' in outers) {
       var id = enforceType(outers.id___, 'number', 'id');
       registeredOuters[id] = undefined;
     }
   }
-
+  
   ////////////////////////////////////////////////////////////////////////
   // Exports
   ////////////////////////////////////////////////////////////////////////
-
+  
   caja = {
 
     // Diagnostics and condition enforcement
-    getLogFunc: getLogFunc,
+    getLogFunc: getLogFunc, 
     setLogFunc: setLogFunc,
     log: log,
 
@@ -1673,29 +1676,29 @@
     enforce: enforce,
     enforceType: enforceType,
     enforceNat: enforceNat,
-
+    
     // walking prototype chain, checking JSON containers
     isJSONContainer: isJSONContainer,
     freeze: freeze,
     copy: copy,
     snapshot: snapshot,
-
+    
     // Accessing properties
     canReadPub: canReadPub,       readPub: readPub,
     canEnumPub: canEnumPub,
-    canEnumOwn: canEnumOwn,
-    BREAK: BREAK,                 each: each,
+    canEnumOwn: canEnumOwn,       
+    BREAK: BREAK,                 each: each,                   
     canCallPub: canCallPub,       callPub: callPub,
     canSetPub: canSetPub,         setPub: setPub,
     canDeletePub: canDeletePub,   deletePub: deletePub,
-
+    
     // Other
     def: def
   };
-
+  
   sharedOuters = {
     caja: caja,
-
+    
     'null': null,
     'false': false,
     'true': true,
@@ -1711,7 +1714,7 @@
     encodeURI: encodeURI,
     encodeURIComponent: encodeURIComponent,
     Math: Math,
-
+    
     Object: Object,
     Array: Array,
     String: String,
@@ -1719,7 +1722,7 @@
     Number: Number,
     Date: Date,
     RegExp: RegExp,
-
+    
     Error: Error,
     EvalError: EvalError,
     RangeError: RangeError,
@@ -1728,7 +1731,7 @@
     TypeError: TypeError,
     URIError: URIError
   };
-
+  
   each(sharedOuters, simpleFunc(function(k, v) {
     switch (typeof v) {
     case 'object':
@@ -1740,7 +1743,7 @@
     }
   }));
   primFreeze(sharedOuters);
-
+  
   ___ = {
 
     // Privileged fault handlers
@@ -1751,14 +1754,14 @@
     directConstructor: directConstructor,
     isFrozen: isFrozen,
     primFreeze: primFreeze,
-
+    
     // Accessing property attributes
     canRead: canRead,             allowRead: allowRead,
     canEnum: canEnum,             allowEnum: allowEnum,
     canCall: canCall,             allowCall: allowCall,
     canSet: canSet,               allowSet: allowSet,
     canDelete: canDelete,         allowDelete: allowDelete,
-
+    
     // Classifying functions
     isCtor: isCtor,
     isMethod: isMethod,
@@ -1769,7 +1772,7 @@
     simpleFunc: simpleFunc,       asSimpleFunc: asSimpleFunc,
     setMember: setMember,
     setMemberMap: setMemberMap,
-
+    
     // Accessing properties
     canReadProp: canReadProp,     readProp: readProp,
     canInnocentEnum: canInnocentEnum,
@@ -1777,13 +1780,13 @@
     canCallProp: canCallProp,     callProp: callProp,
     canSetProp: canSetProp,       setProp: setProp,
     canDeleteProp: canDeleteProp, deleteProp: deleteProp,
-
+    
     // Other
     hasOwnProp: hasOwnProp,
     args: args,
-
+    
     // Taming mechanism
-    useGetHandler: useGetHandler,
+    useGetHandler: useGetHandler, 
     useApplyHandler: useApplyHandler,
     useCallHandler: useCallHandler,
     useSetHandler: useSetHandler,
@@ -1794,10 +1797,10 @@
     allowMutator: allowMutator,
     enforceMatchable: enforceMatchable,
     all2: all2,
-
+    
     // Taming decisions
     sharedOuters: sharedOuters,
-
+    
     // Module loading
     getNewModuleHandler: getNewModuleHandler,
     setNewModuleHandler: setNewModuleHandler,
@@ -1809,7 +1812,7 @@
     getOuters: getOuters,
     unregister: unregister
   };
-
+  
   each(caja, simpleFunc(function(k, v) {
     if (k in ___) {
       fail('internal: initialization conflict: ', k);
@@ -1821,7 +1824,7 @@
     ___[k] = v;
   }));
   primFreeze(caja);
-
+  
   setNewModuleHandler(makeNormalNewModuleHandler());
-
+  
 })(this);

Added: incubator/shindig/trunk/features/caja/domita.js
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/caja/domita.js?rev=635936&view=auto
==============================================================================
--- incubator/shindig/trunk/features/caja/domita.js (added)
+++ incubator/shindig/trunk/features/caja/domita.js Tue Mar 11 07:21:16 2008
@@ -0,0 +1,719 @@
+// Copyright (C) 2008 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+/**
+ * @fileoverview
+ * A partially tamed browser object model based on
+ * <a href="http://www.w3.org/TR/DOM-Level-2-HTML/Overview.html"
+ * >DOM-Level-2-HTML</a> and specifically, the
+ * <a href="http://www.w3.org/TR/DOM-Level-2-HTML/ecma-script-binding.html"
+ * >ECMAScript Language Bindings</a>.
+ *
+ * Requires caja.js, html-defs.js, html-sanitizer.js, unicode.js.
+ *
+ * Caveats:
+ * - This is not a full implementation.
+ * - Security Review is pending.
+ * - <code>===</code> and <code>!==</code> on tamed DOM nodes will not behave
+ *   the same as with untamed nodes.  Specifically, it is not always true that
+ *   {@code document.getElementById('foo') === document.getElementById('foo')}.
+ *
+ * @author mikesamuel@gmail.com
+ */
+
+
+/**
+ * Add a tamed document implementation to a Gadget's global scope.
+ *
+ * @param {string} idPrefix a string prefix prepended to all node IDs.
+ * @param {Object} uriCallback an object like <pre>{
+ *       rewrite: function (uri, mimeType) { return safeUri }
+ *     }</pre>.
+ *     The rewrite function should be idempotent to allow rewritten HTML
+ *     to be reinjected.
+ * @param {Object} outers the gadget's global scope.
+ */
+attachDocumentStub = (function () {
+  var tameNodeSecret = {};
+
+  var XML_SPACE = '\t\n\r ';
+
+  var XML_NAME_PATTERN = new RegExp(
+      '^[' + unicode.LETTER + '_:][' + unicode.LETTER + unicode.DIGIT + '.\\-_:'
+      + unicode.COMBINING_CHAR + unicode.EXTENDER + ']*$');
+
+  var XML_NMTOKEN_PATTERN = new RegExp(
+      '^[' + unicode.LETTER + unicode.DIGIT + '.\\-_:'
+      + unicode.COMBINING_CHAR + unicode.EXTENDER + ']+$');
+
+  var XML_NMTOKENS_PATTERN = new RegExp(
+      '^(?:[' + XML_SPACE + ']*[' + unicode.LETTER + unicode.DIGIT + '.\\-_:'
+      + unicode.COMBINING_CHAR + unicode.EXTENDER + ']+)+[' + XML_SPACE + ']*$'
+      );
+
+  var JS_SPACE = '\t\n\r ';
+  // An identifier that does not end with __.
+  var JS_IDENT = '(?:[a-zA-Z_][a-zA-Z0-9$_]*[a-zA-Z0-9$]|[a-zA-Z])_?';
+  var SIMPLE_HANDLER_PATTERN = new RegExp(
+      '^[' + JS_SPACE + ']*'
+      + '(return[' + JS_SPACE + ']+)?'  // Group 1 is present if it returns.
+      + '(' + JS_IDENT + ')[' + JS_SPACE + ']*'  // Group 2 is a function name.
+      // Which can be passed optionally this node, and optionally the event.
+      + '\\((?:this'
+        + '(?:[' + JS_SPACE + ']*,[' + JS_SPACE + ']*event)?'
+        + '[' + JS_SPACE + ']*)?\\)'
+      // And it can end with a semicolon.
+      + '[' + JS_SPACE + ']*(?:;?[' + JS_SPACE + ']*)$');
+
+  /**
+   * Coerces the string to a valid XML Name.
+   * @see http://www.w3.org/TR/2000/REC-xml-20001006#NT-Name
+   */
+  function isXmlName(s) {
+    return XML_NAME_PATTERN.test(s);
+  }
+
+  /**
+   * Coerces the string to valid XML Nmtokens
+   * @see http://www.w3.org/TR/2000/REC-xml-20001006#NT-Nmtokens
+   */
+  function isXmlNmTokens(s) {
+    return XML_NMTOKENS_PATTERN.test(s);
+  }
+
+  function assert(cond) {
+    if (!cond) {
+      console && (console.log('domita assertion failed'), console.trace());
+      throw new Error();
+    }
+  }
+
+  /**
+   * Add setter and getter hooks so that the caja {@code node.innerHTML = '...'}
+   * works as expected.
+   */
+  function exportFields(ctor, fields) {
+    for (var i = fields.length; --i >= 0;) {
+      var field = fields[i];
+      var fieldUCamel = field.charAt(0).toUpperCase() + field.substring(1);
+      var getterName = 'get' + fieldUCamel;
+      var setterName = 'set' + fieldUCamel;
+      var count = 0;
+      if (ctor.prototype.hasOwnProperty(getterName)) {
+        ++count;
+        ___.useGetHandler(
+           ctor.prototype, field, ctor.prototype[getterName]);
+      }
+      if (ctor.prototype.hasOwnProperty(setterName)) {
+        ++count;
+        ___.useSetHandler(
+           ctor.prototype, field, ctor.prototype[setterName]);
+      }
+      if (!count) {
+        throw new Error('Failed to export field ' + field + ' on ' + ctor);
+      }
+    }
+  }
+
+  /**
+   * Makes the first a subclass of the second.
+   */
+  function extend(subClass, baseClass) {
+    var noop = function () {};
+    noop.prototype = baseClass.prototype;
+    subClass.prototype = new noop();
+    subClass.prototype.constructor = subClass;
+  }
+
+  // See above for a description of this function.
+  function attachDocumentStub(idPrefix, uriCallback, outers) {
+
+    var elementPolicies = {};
+    elementPolicies.FORM = function (attribs) {
+      // Forms must have a gated onsubmit handler or they must have an
+      // external target.
+      var sawHandler = false;
+      for (var i = 0, n = attribs.length; i < n; i += 2) {
+        if (attribs[i] === 'ONSUBMIT') {
+          sawHandler = true;
+        }
+      }
+      if (!sawHandler) {
+        attribs.push('ONSUBMIT', 'return false');
+      }
+      return attribs;
+    };
+    elementPolicies.A = elementPolicies.AREA = function (attribs) {
+      // Anchor tags must have a target.
+      attribs.push('TARGET', '_blank');
+      return attribs;
+    };
+    
+
+    /** Sanitize HTML applying the appropriate transformations. */
+    function sanitizeHtml(htmlText) {
+      var out = [];
+      htmlSanitizer(htmlText, out);
+      return out.join('');
+    }
+    var htmlSanitizer = html.makeHtmlSanitizer(
+        function sanitizeAttributes(tagName, attribs) {
+          for (var i = 0; i < attribs.length; i += 2) {
+            var attribName = attribs[i];
+            var value = attribs[i + 1];
+            if (html4.ATTRIBS.hasOwnProperty(attribName)) {
+              var atype = html4.ATTRIBS[attribName];
+              value = rewriteAttribute(tagName, attribName, atype, value);
+            } else {
+              value = null;
+            }
+            if (value != null) {  // intentionally matches undefined
+              attribs[i + 1] = value;
+            } else {
+              attribs.splice(i, 2);
+              i -= 2;
+            }
+          }
+          var policy = elementPolicies[tagName];
+          if (policy && elementPolicies.hasOwnProperty(tagName)) {
+            return policy(attribs);
+          }
+          return attribs;
+        });
+
+
+    /**
+     * Undoes some of the changes made by sanitizeHtml, e.g. stripping ID
+     * prefixes.
+     */
+    function tameInnerHtml(htmlText) {
+      var out = [];
+      innerHtmlTamer(htmlText, out);
+      return out.join('');
+    }
+    var innerHtmlTamer = html.makeSaxParser({
+        startTag: function (tagName, attribs, out) {
+          out.push('<', tagName);
+          for (var i = 0; i < attribs.length; i += 2) {
+            var attribName = attribs[i];
+            if (attribName === 'TARGET') { continue; }
+            var atype = html4.ATTRIBS[attribName];
+            var value = attribs[i + 1];
+            if (atype === html4.atype.IDREF) {
+              if (value.length <= idPrefix.length
+                  || idPrefix !== value.substring(0, idPrefix.length)) {
+                continue;
+              }
+              value = value.substring(idPrefix.length);
+            }
+            if (value != null) {
+              out.push(' ', attribName, '="', html.escapeAttrib(value), '"');
+            }
+          }
+          out.push('>');
+        },
+        endTag: function (name, out) { out.push('</', name, '>'); },
+        pcdata: function (text, out) { out.push(text); },
+        rcdata: function (text, out) { out.push(text); },
+        cdata: function (text, out) { out.push(text); }
+      });
+
+    function rewriteAttribute(tagName, attribName, type, value) {
+      value = String(value);
+      switch (type) {
+        case html4.atype.IDREF:
+          if (!(value && isXmlName(value))) { return null; }
+          return idPrefix + value;
+        case html4.atype.NAME:
+          if (!(value && isXmlName(value))) { return null; }
+          return value;
+        case html4.atype.NMTOKENS:
+          if (!(value && isXmlNmTokens(value))) { return null; }
+          return value;
+        case html4.atype.SCRIPT:
+          // Translate a handler that calls a simple function like
+          //   return foo(this, event)
+
+          // TODO(mikesamuel): integrate cajita compiler to allow arbitrary
+          // cajita in event handlers.
+          var match = value.match(SIMPLE_HANDLER_PATTERN);
+          if (!match) { return null; }
+          var doesReturn = match[1];
+          var fnName = match[2];
+          var pluginId = ___.getId(outers);
+          value = (doesReturn ? 'return ' : '') + 'plugin_dispatchEvent___('
+              + 'this, event || window.event, ' + pluginId + ', "'
+              + fnName + '");';
+          if (attribName === 'ONSUBMIT') {
+            value = 'try { ' + value + ' } finally { return false; }';
+          }
+          return value;
+        case html4.atype.URI:
+          if (!uriCallback) { return null; }
+          // TODO(mikesamuel): determine mime type properly.
+          return uriCallback.rewrite(value, '*/*') || null;
+        case html4.atype.STYLE:
+          // TODO(mikesamuel): sanitize css
+          return null;
+        case html4.atype.FRAME:
+          // Frames are ambient, so disallow reference.
+          return null;
+        default:
+          return value;
+      }
+    }
+
+    /**
+     * returns a tame DOM node.
+     * @param {Node} node
+     * @param {boolean} editable
+     * @see <a href="http://www.w3.org/TR/DOM-Level-2-HTML/html.html"
+     *       >DOM Level 2</a>
+     */
+    function tameNode(node, editable) {
+      if (node == null) { return null; }  // Returns null for undefined.
+      var tamed;
+      switch (node.nodeType) {
+        case 1:  // Element
+          switch (node.tagName.toUpperCase()) {
+            case 'FORM':
+              tamed = new TameFormElement(node, editable);
+              break;
+            case 'INPUT':
+              tamed = new TameInputElement(node, editable);
+              break;
+            default:
+              tamed = new TameElement(node, editable);
+              break;
+          }
+          break;
+        case 3:  // Text
+          tamed = new TameTextNode(node, editable);
+          break;
+        default:
+          return null;
+      }
+      return tamed;
+    }
+
+    /**
+     * Returns a NodeList like object.
+     */
+    function tameNodeList(nodeList, editable, opt_keyAttrib) {
+      var tamed = [];
+      for (var i = nodeList.length; --i >= 0;) {
+        var node = tameNode(nodeList.item(i), editable);
+        tamed[i] = node;
+        // Make the node available via its name if doing so would not mask
+        // any properties of tamed.
+        var key = opt_keyAttrib && node.getAttribute(opt_keyAttrib);
+        if (key && !(/_$/.test(key) || (key in tamed)
+                     || key === String(key & 0x7fffffff))) {
+          tamed[key] = node;
+        }
+      }
+      tamed.item = function (k) {
+        k &= 0x7fffffff;
+        if (isNaN(k)) { throw new Error(); }
+        return this[k] || null;
+      };
+      // TODO(mikesamuel): if opt_keyAttrib, could implement getNamedItem
+      return tamed;
+    }
+
+    /**
+     * Base class for a Node wrapper.  Do not create directly -- use the
+     * tameNode factory instead.
+     * @constructor
+     */
+    function TameNode(node, editable) {
+      this.node___ = node;
+      this.editable___ = editable;
+    }
+    TameNode.prototype.secret___ = tameNodeSecret;
+    TameNode.prototype.getNodeType = function () {
+      return this.node___.nodeType;
+    };
+    TameNode.prototype.getNodeName = function () {
+      return this.node___.nodeName;
+    };
+    TameNode.prototype.getNodeValue = function () {
+      return this.node___.nodeValue;
+    };
+    TameNode.prototype.appendChild = function (child) {
+      // Child must be editable since appendChild can remove it from its parent.
+      if (child.secret___ !== tameNodeSecret
+          || !this.editable___ || !child.editable___) {
+        throw new Error();
+      }
+      this.node___.appendChild(child.node___);
+    };
+    TameNode.prototype.insertBefore = function (child) {
+      if (child.secret___ !== tameNodeSecret
+          || !this.editable___ || !child.editable___) {
+        throw new Error();
+      }
+      this.node___.insertBefore(child.node___);
+    };
+    TameNode.prototype.removeChild = function (child) {
+      if (child.secret___ !== tameNodeSecret || !this.editable___) {
+        throw new Error();
+      }
+      this.node___.removeChild(child.node___);
+    };
+    TameNode.prototype.replaceChild = function (child, replacement) {
+      if (child.secret___ !== tameNodeSecret
+          || replacement.secret___ !== tameNodeSecret
+          || !this.editable___
+          || !replacement.editable___) {
+        throw new Error();
+      }
+      this.node___.replaceChild(child.node___, replacement.node___);
+    };
+    TameNode.prototype.getFirstChild = function () {
+      return tameNode(this.node___.firstChild);
+    };
+    TameNode.prototype.getLastChild = function () {
+      return tameNode(this.node___.lastChild);
+    };
+    TameNode.prototype.getNextSibling = function () {
+      return tameNode(this.node___.nextSibling);
+    };
+    TameNode.prototype.getPrevSibling = function () {
+      return tameNode(this.node___.prevSibling);
+    };
+    TameNode.prototype.getElementsByTagName = function (tagName) {
+      return tameNodeList(
+          this.node___.getElementsByTagName(String(tagName)), this.editable___);
+    };
+    ___.ctor(TameNode, undefined, 'TameNode');
+    ___.all2(
+       ___.allowMethod, TameNode,
+       ['getNodeType', 'getNodeValue', 'getNodeName',
+        'appendChild', 'insertBefore', 'removeChild', 'replaceChild',
+        'getFirstChild', 'getLastChild', 'getNextSibling', 'getPrevSibling',
+        'getElementsByTagName']);
+    exportFields(TameNode, ['nodeType', 'nodeValue', 'nodeName', 'firstChild',
+                            'lastChild', 'nextSibling', 'prevSibling']);
+
+    function TameTextNode(node, editable) {
+      assert(node.nodeType === 3);
+      TameNode.call(this, node, editable);
+    }
+    extend(TameTextNode, TameNode);
+    TameTextNode.prototype.setNodeValue = function (value) {
+      if (!this.editable___) { throw new Error(); }
+      this.node___.nodeValue = String(value || '');
+    };
+    TameTextNode.prototype.getData = TameTextNode.prototype.getNodeValue;
+    TameTextNode.prototype.setData = TameTextNode.prototype.setNodeValue;
+    TameTextNode.prototype.toString = function () {
+      return '#text';
+    };
+    ___.ctor(TameTextNode, undefined, 'TameNode');
+    ___.all2(___.allowMethod, TameTextNode,
+             ['setNodeValue', 'getData', 'setData']);
+    exportFields(TameTextNode, ['nodeValue', 'data']);
+
+
+    function TameElement(node, editable) {
+      assert(node.nodeType === 1);
+      TameNode.call(this, node, editable);
+    }
+    extend(TameElement, TameNode);
+    TameElement.prototype.getId = function () {
+      return this.getAttribute('ID') || '';
+    };
+    TameElement.prototype.setId = function (newId) {
+      return this.setAttribute('ID', newId);
+    };
+    TameElement.prototype.getAttribute = function (name) {
+      name = String(name).toUpperCase();
+      var type = html4.ATTRIBS[name];
+      if (type === undefined || !html4.ATTRIBS.hasOwnProperty(name)) {
+        return null;
+      }
+      var value = this.node___.getAttribute(name);
+      if ('string' !== typeof value) { return value; }
+      switch (type) {
+        case html4.atype.IDREF:
+          var n = idPrefix.length;
+          if (value && value.length >= n
+              && idPrefix === value.substring(0, n)) {
+            return value.substring(n);
+          }
+          return '';
+        default:
+          return value;
+      }
+    };
+    TameElement.prototype.setAttribute = function (name, value) {
+      if (!this.editable___) { throw new Error(); }
+      name = String(name).toUpperCase();
+      var type = html4.ATTRIBS[name];
+      if (type === undefined || !html4.ATTRIBS.hasOwnProperty(name)) {
+        throw new Error();
+      }
+      var sanitizedValue = rewriteAttribute(
+          this.node___.tagName, name, type, value);
+      if (sanitizedValue !== null) {
+        this.node___.setAttribute(name, sanitizedValue);
+      }
+    };
+    TameElement.prototype.getClassName = function () {
+      return getAttribute('class');
+    };
+    TameElement.prototype.setClassName = function (classes) {
+      if (!this.editable___) { throw new Error(); }
+      return this.getAttribute('class', String(classes));
+    };
+    TameElement.prototype.getTagName = TameNode.prototype.getNodeName;
+    TameElement.prototype.getInnerHTML = function () {
+      var tagName = this.node___.tagName.toUpperCase();
+      if (!html4.ELEMENTS.hasOwnProperty(tagName)) {
+        return '';  // unknown node
+      }
+      var flags = html4.ELEMENTS[tagName];
+      var innerHtml = this.node___.innerHTML;
+      if (flags & html4.eflags.CDATA) {
+        innerHtml = html.escapeAttrib(innerHtml);
+      } else if (flags & html4.eflags.RCDATA) {
+        // Make sure we return PCDATA.
+        // TODO(mikesamuel): for RCDATA we only need to escape & if they're not
+        // part of an entity.
+        innerHtml = html.normalizeRCData(innerHtml);
+      } else {
+        innerHtml = tameInnerHtml(innerHtml);
+      }
+      // TODO(mikesamuel): rewrite ids
+      return innerHtml;
+    };
+    TameElement.prototype.setInnerHTML = function (html) {
+      if (!this.editable___) { throw new Error(); }
+      var tagName = this.node___.tagName.toUpperCase();
+      if (!html4.ELEMENTS.hasOwnProperty(tagName)) { throw new Error(); }
+      var flags = html4.ELEMENTS[tagName];
+      if (flags & html4.eflags.UNSAFE) { throw new Error(); }
+      html = String(html || '');
+      if (flags & html4.eflags.RCDATA) {
+        html = html.normalizeRCData(html);
+      } else {
+        html = sanitizeHtml(html);
+      }
+      this.node___.innerHTML = html;
+    };
+    TameElement.prototype.addEventListener = function (name, listener, bubble) {
+      if (!this.editable___) { throw new Error(); }
+      if ('function' !== typeof listener) {
+        throw new Error();
+      }
+      name = String(name);
+      if (this.node___.addEventListener) {
+        this.node___.addEventListener(
+            name,
+            function (event) {
+              return plugin_dispatchEvent___(
+                  this, event || window.event, ___.getId(outers),
+                  listener);
+            }, bubble === undefined ? undefined : Boolean(bubble));
+      } else {
+        var thisNode = this;
+        this.node___.attachEvent('on' + name, dispatcher);
+      }
+    };
+    TameElement.prototype.toString = function () {
+      return '<' + this.node___.tagName + '>';
+    };
+    ___.ctor(TameElement, TameNode, 'TameElement');
+    ___.all2(
+       ___.allowMethod, TameElement,
+       ['addEventListener', 'getAttribute', 'setAttribute',
+        'getClassName', 'setClassName', 'getId', 'setId',
+        'getInnerHTML', 'setInnerHTML', 'getTagName']);
+    exportFields(TameElement, ['className', 'id', 'innerHTML', 'tagName']);
+
+
+    function TameFormElement(node, editable) {
+      TameElement.call(this, node, editable);
+    }
+    extend(TameFormElement, TameElement);
+    TameFormElement.prototype.getElements = function () {
+      return tameNodeList(this.node___.elements, this.editable___, 'name');
+    };
+    ___.ctor(TameFormElement, TameElement, 'TameFormElement');
+    ___.all2(___.allowMethod, TameFormElement, ['getElements']);
+    exportFields(TameFormElement, ['elements']);
+
+
+    function TameInputElement(node, editable) {
+      TameElement.call(this, node, editable);
+    }
+    extend(TameInputElement, TameElement);
+    TameInputElement.prototype.getValue = function () {
+      var value = this.node___.value;
+      return value == null ? null : String(value); // Return null for undefined.
+    };
+    TameInputElement.prototype.setValue = function (newValue) {
+      if (!this.editable___) { throw new Error(); }
+      // == matches undefined
+      this.node___.value = (newValue == null ? '' : '' + value);
+    };
+    TameInputElement.prototype.focus = function () {
+      if (!this.editable___) { throw new Error(); }
+      this.node___.focus();
+    };
+    TameInputElement.prototype.blur = function () {
+      if (!this.editable___) { throw new Error(); }
+      this.node___.blur();
+    };
+    TameInputElement.prototype.getForm = function () {
+      return tameNode(this.node___.form);
+    };
+    ___.ctor(TameInputElement, TameElement, 'TameInputElement');
+    ___.all2(___.allowMethod, TameInputElement,
+             ['getValue', 'setValue', 'focus', 'getForm']);
+    exportFields(TameInputElement, ['value', 'form']);
+
+
+    function TameEvent(event) {
+      this.event___ = event;
+    }
+    TameEvent.prototype.getType = function () {
+      return String(this.event___.type);
+    };
+    TameEvent.prototype.getTarget = function () {
+      return tameNode(this.event___.target, true);
+    };
+    TameEvent.prototype.getPageX = function () {
+      return Number(this.event___.pageX);
+    };
+    TameEvent.prototype.getPageY = function () {
+      return Number(this.event___.pageY);
+    };
+    TameEvent.prototype.stopPropagation = function () {
+      // TODO(mikesamuel): make sure event doesn't propagate to dispatched
+      // events for this gadget only.
+      // But don't allow it to stop propagation to the container.
+      return this.event___.stopPropagation();
+    };
+    TameEvent.prototype.getAltKey = function () {
+      return Boolean(this.event___.altKey);
+    };
+    TameEvent.prototype.getCtrlKey = function () {
+      return Boolean(this.event___.ctrlKey);
+    };
+    TameEvent.prototype.getMetaKey = function () {
+      return Boolean(this.event___.metaKey);
+    };
+    TameEvent.prototype.getShiftKey = function () {
+      return Boolean(this.event___.shiftKey);
+    };
+    TameEvent.prototype.getButton = function () {
+      var e = this.event___;
+      return e.button && Number(e.button);
+    };
+    TameEvent.prototype.getClientX = function () {
+      return Number(this.event___.clientX);
+    };
+    TameEvent.prototype.getClientY = function () {
+      return Number(this.event___.clientY);
+    };
+    TameEvent.prototype.getWhich = function () {
+      var w = this.event___.which;
+      return w && Number(w);
+    };
+    TameEvent.prototype.getKeyCode = function () {
+      var kc = this.event___.keyCode;
+      return kc && Number(kc);
+    };
+    TameEvent.prototype.toString = function () { return 'Not a real event'; };
+    TameEvent.prototype.current
+    ___.ctor(TameEvent, undefined, 'TameEvent');
+    ___.all2(___.allowMethod, TameEvent,
+             ['getType', 'getTarget', 'getPageX', 'getPageY', 'stopPropagation',
+              'getAltKey', 'getCtrlKey', 'getMetaKey', 'getShiftKey',
+              'getButton', 'getClientX', 'getClientY',
+              'getKeyCode', 'getWhich']);
+    exportFields(TameEvent, ['type', 'target', 'pageX', 'pageY', 'altKey',
+                             'ctrlKey', 'metaKey', 'shiftKey', 'button',
+                             'clientX', 'clientY', 'keyCode', 'which']);
+
+
+    function TameDocument(doc, editable) {
+      this.doc___ = doc;
+      this.editable___ = editable;
+    }
+    TameDocument.prototype.createElement = function (tagName) {
+      if (!this.editable___) { throw new Error(); }
+      tagName = String(tagName).toUpperCase();
+      if (!html4.ELEMENTS.hasOwnProperty(tagName)) { throw new Error(); }
+      var flags = html4.ELEMENTS[tagName];
+      if (flags & html4.eflags.UNSAFE) { throw new Error(); }
+      var newEl = this.doc___.createElement(tagName);
+      if (elementPolicies.hasOwnProperty(tagName)) {
+        var attribs = elementPolicies[tagName]([]);
+        if (attribs) {
+          for (var i = 0; i < attribs.length; i += 2) {
+            newEl.setAttribute(attribs[i], attribs[i + 1]);
+          }
+        }
+      }
+      return tameNode(newEl, true);
+    };
+    TameDocument.prototype.createTextNode = function (text) {
+      if (!this.editable___) { throw new Error(); }
+      return tameNode(  // == matches undefined
+          this.doc___.createTextNode(text != null ? '' + text : ''), true);
+    };
+    TameDocument.prototype.getElementById = function (id) {
+      id = idPrefix + id;
+      var node = this.doc___.getElementById(id);
+      return tameNode(node, this.editable___);
+    };
+    TameDocument.prototype.toString = function () { return '[Fake Document]'; };
+    ___.ctor(TameDocument, undefined, 'TameDocument');
+    ___.all2(___.allowMethod, TameDocument,
+             ['createElement', 'createTextNode', 'getElementById']);
+
+
+    outers.tameNode___ = tameNode;
+    outers.tameEvent___ = function (event) { return new TameEvent(event); };
+    outers.document = new TameDocument(document, true);
+  }
+
+  return attachDocumentStub;
+})();
+
+/**
+ * Function called from rewritten event handlers to dispatch an event safely.
+ */
+function plugin_dispatchEvent___(thisNode, event, pluginId, handler) {
+  (typeof console !== 'undefined' && console.log) &&
+  console.log(
+      'Dispatch %s event thisNode=%o, event=%o, pluginId=%o, handler=%o',
+      event.type, thisNode, event, pluginId, handler);
+  var outers = ___.getOuters(pluginId);
+  switch (typeof handler) {
+    case 'string':
+      handler = outers[handler];
+      break;
+    case 'function':
+      break;
+    default:
+      throw new Error(
+          'Expected function as event handler, not ' + typeof handler);
+  }
+  return (___.asSimpleFunc(handler))(
+      outers.tameNode___(thisNode), outers.tameEvent___(event));
+}

Modified: incubator/shindig/trunk/features/caja/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/caja/feature.xml?rev=635936&r1=635935&r2=635936&view=diff
==============================================================================
--- incubator/shindig/trunk/features/caja/feature.xml (original)
+++ incubator/shindig/trunk/features/caja/feature.xml Tue Mar 11 07:21:16 2008
@@ -23,7 +23,10 @@
   <name>caja</name>
   <gadget>
     <script src="caja.js"></script>
+    <script src="html4-defs.js"></script>
     <script src="html-sanitizer.js"></script>
+    <script src="unicode.js"></script>
+    <script src="domita.js"></script>
     <script src="log-to-console.js"></script>
     <script src="permissive.js"></script>
   </gadget>