[google-caja] r5712 committed - Backport typed array whitelisting (5621,5650,5684,5685,5695,5710) into...

0 views
Skip to first unread message

googl...@googlecode.com

unread,
Feb 25, 2015, 4:24:27 PM2/25/15
to caja....@gmail.com
Revision: 5712
Author: kpr...@switchb.org
Date: Wed Feb 25 21:23:55 2015 UTC
Log: Backport typed array whitelisting (5621,5650,5684,5685,5695,5710)
into es53 branch.
https://codereview.appspot.com/202280043

Typed array support in SES (r5621) had previously been excluded for the
simple reason that r5621 also includes changes to domado.js to use
typed arrays, but ES5/3 does not support typed arrays. In this change
we add the SES and taming membrane parts of typed array support only.

This change started as a merge
svn merge -c 5621,5684 ^/trunk
but contains additional edits, as well as manual backports of the
remainders of r5650, r5685, r5695, r5710 which did not previously
apply.

R=eri...@gmail.com


https://code.google.com/p/google-caja/source/detail?r=5712

Modified:
/branches/es53
/branches/es53/src/com/google/caja/plugin/taming-membrane.js
/branches/es53/src/com/google/caja/ses/repairES5.js
/branches/es53/src/com/google/caja/ses/startSES.js
/branches/es53/src/com/google/caja/ses/whitelist.js
/branches/es53/tests/com/google/caja/plugin/test-compare-modes.js
/branches/es53/tests/com/google/caja/plugin/test-scan-guest.js
/branches/es53/tests/com/google/caja/plugin/test-taming-inout-guest.js

=======================================
--- /branches/es53/src/com/google/caja/plugin/taming-membrane.js Tue Feb 24
19:34:47 2015 UTC
+++ /branches/es53/src/com/google/caja/plugin/taming-membrane.js Wed Feb 25
21:23:55 2015 UTC
@@ -15,7 +15,9 @@
/**
* Generic taming membrane implementation.
*
- * @requires WeakMap
+ * @requires WeakMap, ArrayBuffer, Int8Array, Uint8Array,
Uint8ClampedArray,
+ * Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array,
+ * Float64Array, DataView
* @overrides window
* @provides TamingMembrane
*/
@@ -96,11 +98,18 @@
}
}

- // Given a builtin object "o" provided by either a guest or host frame,
- // return a copy constructed in the taming frame. Return undefined if
- // "o" is not a builtin object. Note that we only call this function if
we
- // know that "o" is *not* a primitive.
- function copyBuiltin(o) {
+ /**
+ * Given a builtin object "o" from either side of the membrane, return a
copy
+ * constructed in the taming frame. Return undefined if "o" is not of a
type
+ * handled here. Note that we only call this function if we know
that "o" is
+ * *not* a primitive.
+ *
+ * This function handles only objects which we copy exactly once and
reuse
+ * the copy (via tamesTo()) if the same object is met again. For objects
which
+ * we copy every time they are passed across the membrane, see
+ * copyTreatedMutable below.
+ */
+ function copyTreatedImmutable(o) {
var t = void 0;
// Object.prototype.toString is spoofable (as of ES6). Therefore, each
// branch of this switch must assume that o is not necessarily of the
type
@@ -161,9 +170,78 @@
t.name = '' + name;
break;
}
+ break;
}
return t;
}
+
+ /**
+ * Helper function; return a copy of a typed array object without
depending on
+ * the typed array constructor to do it.
+ *
+ * This is needed, or at least reasonable caution, because typed array
+ * constructors have overloads that will share an ArrayBuffer with the
+ * provided value, rather than copying. In current specification and
+ * implementation it does not appear to be possible to create an object
which
+ * exploits this, but we don't wish to rely on that invariant.
+ */
+ function copyTArray(ctor, o) {
+ // Get a copy of the relevant portion of the underlying buffer in a way
+ // which has no overloads and guarantees a copy.
+ var byteOffset = +o.byteOffset;
+ var byteLength = +o.byteLength;
+ var buffer = ArrayBuffer.prototype.slice.call(
+ o.buffer, o.byteOffset, o.byteOffset + o.byteLength);
+
+ return new ctor(buffer);
+ }
+
+ /**
+ * Given a builtin object "o" from either side of the membrane, return a
copy
+ * constructed in the taming frame. Return undefined if "o" is not of a
type
+ * handled here. Note that we only call this function if we know
that "o" is
+ * *not* a primitive.
+ *
+ * This function handles only objects which should be copied every time
they
+ * are passed across the membrane. For objects which we wish to copy at
most
+ * once, see copyTreatedImmutable above.
+ */
+ function copyTreatedMutable(o, recursor) {
+ { // dummy block for minimum difference with trunk
+ var t = undefined;
+ // Object.prototype.toString is spoofable (as of ES6). Therefore,
each
+ // branch of this switch must assume that o is not necessarily of
the type
+ // and defend against that. However, we consider it acceptable for a
+ // spoofing object to be copied as one of what it was spoofing, or to
+ // cause an error.
+ switch (Object.prototype.toString.call(o)) {
+ // Note that these typed array tamings break any buffer sharing,
but
+ // that's in line with our general policy of copying.
+ //
+ // Note that we do not use privilegedAccess operations here, but
that
+ // is okay because ES5/3 does not support typed arrays and so this
code
+ // will never be reached.
+ case '[object ArrayBuffer]':
+ // ArrayBuffer.prototype.slice will always copy or throw
TypeError
+ t = ArrayBuffer.prototype.slice.call(o, 0);
+ break;
+ case '[object Int8Array]': t = copyTArray(Int8Array, o); break;
+ case '[object Uint8Array]': t = copyTArray(Uint8Array, o); break;
+ case '[object Uint8ClampedArray]':
+ t = copyTArray(Uint8ClampedArray, o); break;
+ case '[object Int16Array]': t = copyTArray(Int16Array, o); break;
+ case '[object Uint16Array]': t = copyTArray(Uint16Array, o);
break;
+ case '[object Int32Array]': t = copyTArray(Int32Array, o); break;
+ case '[object Uint32Array]': t = copyTArray(Uint32Array, o);
break;
+ case '[object Float32Array]': t = copyTArray(Float32Array, o);
break;
+ case '[object Float64Array]': t = copyTArray(Float64Array, o);
break;
+ case '[object DataView]':
+ t = new DataView(recursor(o.buffer), o.byteOffset, o.byteLength);
+ break;
+ }
+ return t;
+ }
+ }

// This is a last resort for passing a safe "demilitarized zone"
exception
// across the taming membrane in cases where passing the actual thrown
@@ -204,7 +282,7 @@
if ((f && tameByFeral.has(f)) || (t && feralByTame.has(t))) {
var et = tameByFeral.get(f);
var ef = feralByTame.get(t);
- throw new TypeError('Attempt to multiply tame: ' + f +
+ throw new TypeError('Attempt to multiply tame: ' + f +
(ef ? ' (already ' + (ef === f ? 'same' : ef) + ')' : '') +
' <-> ' + t +
(et ? ' (already ' + (et === t ? 'same' : et) + ')' : ''));
@@ -266,23 +344,25 @@
// Primitive value; tames to self
return f;
}
- var ftype = typeof f;
+ var t = tameByFeral.get(f);
+ if (t) { return t; }
if (Array.isArray(f)) {
// No tamesTo(...) for arrays; we copy across the membrane
return tameArray(f);
}
- var t = tameByFeral.get(f);
- if (t) { return t; }
if (feralByTame.has(f)) {
throw new TypeError('Tame object found on feral side of taming
membrane: '
+ f + '. The membrane has previously been compromised.');
}
+ var ftype = typeof f;
if (ftype === 'object') {
+ t = copyTreatedMutable(f, tame); // after type test to avoid toxic
fns
+ if (t) { return t; }
var ctor = privilegedAccess.directConstructor(f);
if (ctor === privilegedAccess.BASE_OBJECT_CONSTRUCTOR) {
t = preventExtensions(tameRecord(f));
} else {
- t = copyBuiltin(f);
+ t = copyTreatedImmutable(f);
if (t === void 0) {
if (ctor === void 0) {
throw new TypeError('Cannot determine ctor of: ' + f);
@@ -565,13 +645,10 @@
// Primitive value; untames to self
return t;
}
- var ttype = typeof t;
- if (Array.isArray(t)) {
- // No tamesTo(...) for arrays; we copy across the membrane
- return untameArray(t);
- }
var f = feralByTame.get(t);
if (f) { return f; }
+ f = copyTreatedMutable(t, untame);
+ if (f) { return f; }
if (tameByFeral.has(t)) {
throw new TypeError('Feral object found on tame side of taming
membrane: '
+ t + '. The membrane has previously been compromised.');
@@ -579,12 +656,17 @@
if (!privilegedAccess.isDefinedInCajaFrame(t)) {
throw new TypeError('Host object leaked without being tamed');
}
+ if (Array.isArray(t)) {
+ // No tamesTo(...) for arrays; we copy across the membrane
+ return untameArray(t);
+ }
+ var ttype = typeof t;
if (ttype === 'object') {
var ctor = privilegedAccess.directConstructor(t);
if (ctor === privilegedAccess.BASE_OBJECT_CONSTRUCTOR) {
f = untameCajaRecord(t);
} else {
- f = copyBuiltin(t);
+ f = copyTreatedImmutable(t);
if (f === void 0) {
throw new TypeError(
'Untaming of guest constructed objects unsupported: ' + t);
@@ -660,7 +742,7 @@
reTamesTo: reTamesTo,
hasTameTwin: hasTameTwin,
hasFeralTwin: hasFeralTwin,
-
+
// Any code which bypasses the membrane (e.g. in order to provide its
own
// tame twins, as Domado does) must also filter exceptions resulting
from
// control flow crossing the membrane.
=======================================
--- /branches/es53/src/com/google/caja/ses/repairES5.js Tue Feb 24 19:34:47
2015 UTC
+++ /branches/es53/src/com/google/caja/ses/repairES5.js Wed Feb 25 21:23:55
2015 UTC
@@ -36,8 +36,10 @@
*
* @author Mark S. Miller
* @requires ___global_test_function___, ___global_valueOf_function___
- * @requires JSON, navigator, this, eval, document
- * @overrides ses, RegExp, WeakMap, Object, parseInt, repairES5Module
+ * @requires JSON, eval, this
+ * @requires navigator, document, DOMException
+ * @overrides ses, repairES5Module
+ * @overrides RegExp, WeakMap, Object, parseInt
*/
var RegExp;
var ses;
@@ -818,6 +820,19 @@
// tests are rerun to see how they were effected by these repair
// attempts. Finally, we report what happened.

+ // Certain tests cannot operate without freezing primordial objects;
+ // they must therefore be run in separate frames with fresh
+ // primordials. Since the repairs will not have been performed in
+ // those frames, we use these flags to have the tests explicitly
+ // perform those repairs.
+ //
+ // TODO(kpreid): Figure out a better design for solving this problem.
+ // For example, it would be good to generically run the relevant tests
+ // after startSES has frozen everything and abort otherwise (this is
+ // done as a special case for FREEZING_BREAKS_PROTOTYPES only).
+ var repair_FREEZING_BREAKS_PROTOTYPES_wasApplied = false;
+ var repair_TYPED_ARRAY_PROTOS_LOOK_UNFROZEN_wasApplied = false;
+
/**
* If {@code Object.getOwnPropertyNames} is missing, we consider
* this to be an ES3 browser which is unsuitable for attempting to
@@ -2811,6 +2826,95 @@
delete ses.CANT_SAFELY_VERIFY_SYNTAX_canary;
}
}
+
+ var typedArrayNames = [
+ 'Int8Array',
+ 'Uint8Array',
+ 'Uint8ClampedArray',
+ 'Int16Array',
+ 'Uint16Array',
+ 'Int32Array',
+ 'Uint32Array',
+ 'Float32Array',
+ 'Float64Array'
+ ];
+
+ function test_TYPED_ARRAYS_THROW_DOMEXCEPTION() {
+ if (global.DataView === undefined) { return false; }
+ if (global.DOMException === undefined) { return false; }
+ function subtest(f) {
+ try {
+ f();
+ } catch (e) {
+ if (e instanceof DOMException) {
+ return true;
+ } else if (e instanceof Error && !(e instanceof DOMException)) {
+ return false;
+ } else {
+ return 'Exception from ' + f + ' of unexpected type: ' + e;
+ }
+ }
+ return f + ' did not throw';
+ };
+ return [
+ function() { new global.Int8Array(1).set(new global.Int8Array(),
10); },
+ function() { new global.DataView(new
global.ArrayBuffer(1)).getInt8(-1); }
+ ].some(subtest);
+ }
+
+ /**
+ * Observed on Safari 6.0.5 (8536.30.1): frozen typed array prototypes
report
+ * their properties as writable.
+ */
+ function test_TYPED_ARRAY_PROTOS_LOOK_UNFROZEN() {
+ // note: cannot test without frames
+ return inTestFrame(function(window) {
+ // Apply the repair which should fix the problem to the testing
frame.
+ // TODO(kpreid): Design a better architecture to handle cases like
this
+ // than one-off state flags.
+ if (repair_TYPED_ARRAY_PROTOS_LOOK_UNFROZEN_wasApplied) {
+ // optional argument not supplied by normal repair process
+ repair_TYPED_ARRAY_PROTOS_LOOK_UNFROZEN(window);
+ }
+
+ var fail = false;
+ typedArrayNames.forEach(function(ctorName) {
+ var ctor = window[ctorName];
+ if (!ctor) { return; }
+ var proto = ctor.prototype;
+
+ window.Object.freeze(proto);
+ if (!window.Object.isFrozen(proto)) {
+ fail = true;
+ return;
+ }
+
+ window.Object.getOwnPropertyNames(proto, function(prop) {
+ if (typeof fail === 'string') { return; }
+
+ // check attributes
+ var desc = window.Object.getOwnPropertyDescriptor(proto, prop);
+ if (!desc.configurable && desc.writable) {
+ fail = true;
+ } else if (!desc.configurable && !desc.writable) {
+ // correct result
+ } else {
+ fail = 'Unexpected property attributes for ' + ctorName + '.' +
+ prop;
+ return;
+ }
+
+ // check actual writability
+ try { proto[prop] = 9; } catch (e) {}
+ if (proto[prop] !== desc.value) {
+ fail = 'Unexpected actual writability of ' + ctorName + '.' +
prop;
+ return;
+ }
+ });
+ });
+ return fail;
+ });
+ }

/**
* Detects
@@ -3307,8 +3411,6 @@
// error message is matched elsewhere (for tighter bounds on catch)
var NO_CREATE_NULL =
'Repaired Object.create can not support Object.create(null)';
- // flag used for the test-of-repair which cannot operate normally.
- var repair_FREEZING_BREAKS_PROTOTYPES_wasApplied = false;
// optional argument is used for the test-of-repair
function repair_FREEZING_BREAKS_PROTOTYPES(opt_Object) {
var baseObject = opt_Object || Object;
@@ -3421,6 +3523,69 @@
// No known repairs under these conditions
}
}
+
+ function repair_TYPED_ARRAYS_THROW_DOMEXCEPTION() {
+ var protos = typedArrayNames.map(
+ function(ctorName) { return global[ctorName].prototype; });
+ protos.push(global.DataView.prototype);
+ protos.forEach(function(proto) {
+ Object.getOwnPropertyNames(proto).forEach(function(prop) {
+ if (/^[gs]et/.test(prop)) {
+ var origMethod = proto[prop];
+ proto[prop] = function exceptionAdapterWrapper(var_args) {
+ try {
+ origMethod.apply(this, arguments);
+ } catch (e) {
+ if (e instanceof DOMException) {
+ throw new RangeError(e.message);
+ }
+ }
+ };
+ }
+ });
+ });
+ }
+
+ function repair_TYPED_ARRAY_PROTOS_LOOK_UNFROZEN(opt_global) {
+ var targetGlobal = opt_global || global;
+ var typedArrayProtos = targetGlobal.Object.freeze(typedArrayNames.map(
+ function(ctorName) { return targetGlobal[ctorName].prototype; }));
+
+ var isFrozen = targetGlobal.Object.isFrozen;
+ var getOwnPropertyDescriptor =
targetGlobal.Object.getOwnPropertyDescriptor;
+
+ Object.defineProperty(targetGlobal.Object, 'getOwnPropertyDescriptor',
{
+ configurable: true,
+ writable: true, // allow other repairs to stack on
+ value: function getOwnPropertyDescriptor_typedArrayPatch(object,
prop) {
+ var desc = getOwnPropertyDescriptor(object, prop);
+ if (desc && typedArrayProtos.indexOf(object) !== -1 &&
+ 'value' in desc && ses._primordialsHaveBeenFrozen) {
+ // If it is one of the typed array prototypes then it will have
been
+ // frozen by startSES.
+ desc.writable = false;
+ }
+ return desc;
+ }
+ });
+
+ Object.defineProperty(targetGlobal.Object, 'isFrozen', {
+ configurable: true,
+ writable: true, // allow other repairs to stack on
+ value: function isFrozen_typedArrayPatch(object) {
+ // If it is one of the typed array prototypes then it will have
been
+ // frozen by startSES.
+ var v = typedArrayProtos.indexOf(object) !== -1;
+ return isFrozen(object) || (v && ses._primordialsHaveBeenFrozen);
+ }
+ });
+
+ // isSealed does not need repair as it already gives the correct
answer.
+
+ if (targetGlobal === global) {
+ repair_TYPED_ARRAY_PROTOS_LOOK_UNFROZEN_wasApplied = true;
+ }
+ }

function repair_GLOBAL_LEAKS_FROM_ARRAY_METHODS() {
var object = Array.prototype;
@@ -4608,6 +4773,32 @@
sections: ['15.3.2.1'],
tests: []
},
+ {
+ id: 'TYPED_ARRAYS_THROW_DOMEXCEPTION',
+ description: 'Typed Array operations throw DOMException',
+ test: test_TYPED_ARRAYS_THROW_DOMEXCEPTION,
+ repair: repair_TYPED_ARRAYS_THROW_DOMEXCEPTION,
+ // indirectly unsafe: DOMException is poisonous to WeakMaps on FF,
so we
+ // choose not to expose it, and un-whitelisted types do not get
frozen by
+ // startSES and are therefore global mutable state.
+ preSeverity: severities.NOT_OCAP_SAFE,
+ canRepair: true,
+ urls: [], // TODO(kpreid): file bugs if appropriate
+ sections: ['13.2.3'],
+ tests: [] // hopefully will be in ES6 tests
+ },
+ {
+ id: 'TYPED_ARRAY_PROTOS_LOOK_UNFROZEN',
+ description: 'Typed Array prototypes look unfrozen',
+ test: test_TYPED_ARRAY_PROTOS_LOOK_UNFROZEN,
+ repair: repair_TYPED_ARRAY_PROTOS_LOOK_UNFROZEN,
+ preSeverity: severities.SAFE_SPEC_VIOLATION,
+ canRepair: true,
+ urls: [], // TODO(kpreid): file bugs if appropriate
+ // appears on Safari only
+ sections: ['15.2.3.9', '15.2.3.12'],
+ tests: [] // hopefully will be in ES6 tests
+ },
{
id: 'NESTED_STRICT_FUNCTIONS_LEAK',
description: 'Strict nested functions leak from block scope',
=======================================
--- /branches/es53/src/com/google/caja/ses/startSES.js Thu Dec 18 19:31:03
2014 UTC
+++ /branches/es53/src/com/google/caja/ses/startSES.js Wed Feb 25 21:23:55
2015 UTC
@@ -1707,6 +1707,11 @@
// can skip it for non-defensive frames that must only be confined.
cajaVM.def(sharedImports);

+ // Internal communication back to repairES5 repairs that need to know if
+ // things have been frozen. TODO(kpreid): Consider making this more
specific
+ // (identifying the actually frozen objects) if that doesn't cost too
much.
+ ses._primordialsHaveBeenFrozen = true;
+
// The following objects are ambiently available via language
constructs, and
// therefore if we did not clean and defend them we have a problem. This
is
// defense against mistakes in modifying the whitelist, not against
browser
=======================================
--- /branches/es53/src/com/google/caja/ses/whitelist.js Tue Feb 24 19:34:47
2015 UTC
+++ /branches/es53/src/com/google/caja/ses/whitelist.js Wed Feb 25 21:23:55
2015 UTC
@@ -102,6 +102,7 @@
if (!ses) { ses = {}; }

var t = true;
+ var TypedArrayWhitelist; // defined and used below
ses.whitelist = {
cajaVM: { // Caja support
// This object is present here only to make it itself processed by
the
@@ -449,6 +450,62 @@
JSON: {
parse: t,
stringify: t
+ },
+ ArrayBuffer: { // Khronos Typed Arrays spec; ops are
safe
+ length: t, // does not inherit from Function.prototype on Chrome
+ name: t, // ditto
+ isView: t,
+ prototype: {
+ byteLength: 'maybeAccessor',
+ slice: t
+ }
+ },
+ Int8Array: TypedArrayWhitelist = { // Typed Arrays spec
+ length: t, // does not inherit from Function.prototype on Chrome
+ name: t, // ditto
+ BYTES_PER_ELEMENT: t,
+ prototype: {
+ buffer: 'maybeAccessor',
+ byteOffset: 'maybeAccessor',
+ byteLength: 'maybeAccessor',
+ length: 'maybeAccessor',
+ BYTES_PER_ELEMENT: t,
+ set: t,
+ subarray: t
+ }
+ },
+ Uint8Array: TypedArrayWhitelist,
+ Uint8ClampedArray: TypedArrayWhitelist,
+ Int16Array: TypedArrayWhitelist,
+ Uint16Array: TypedArrayWhitelist,
+ Int32Array: TypedArrayWhitelist,
+ Uint32Array: TypedArrayWhitelist,
+ Float32Array: TypedArrayWhitelist,
+ Float64Array: TypedArrayWhitelist,
+ DataView: { // Typed Arrays spec
+ length: t, // does not inherit from Function.prototype on Chrome
+ name: t, // ditto
+ prototype: {
+ buffer: 'maybeAccessor',
+ byteOffset: 'maybeAccessor',
+ byteLength: 'maybeAccessor',
+ getInt8: t,
+ getUint8: t,
+ getInt16: t,
+ getUint16: t,
+ getInt32: t,
+ getUint32: t,
+ getFloat32: t,
+ getFloat64: t,
+ setInt8: t,
+ setUint8: t,
+ setInt16: t,
+ setUint16: t,
+ setInt32: t,
+ setUint32: t,
+ setFloat32: t,
+ setFloat64: t
+ }
}
};
})();
=======================================
--- /branches/es53/tests/com/google/caja/plugin/test-compare-modes.js Wed
Aug 14 04:56:12 2013 UTC
+++ /branches/es53/tests/com/google/caja/plugin/test-compare-modes.js Wed
Feb 25 21:23:55 2015 UTC
@@ -225,6 +225,19 @@
parse: ON_ALL,
stringify: ON_ALL
},
+
+ // Features only supported on SES
+ ArrayBuffer: ON_SES,
+ DataView: ON_SES,
+ Float32Array: ON_SES,
+ Float64Array: ON_SES,
+ Int8Array: ON_SES,
+ Int16Array: ON_SES,
+ Int32Array: ON_SES,
+ Uint8Array: ON_SES,
+ Uint8ClampedArray: ON_SES,
+ Uint16Array: ON_SES,
+ Uint32Array: ON_SES,

cajaVM: {
// evaluation operations -- not supported in ES5/3
=======================================
--- /branches/es53/tests/com/google/caja/plugin/test-scan-guest.js Thu Dec
18 19:31:03 2014 UTC
+++ /branches/es53/tests/com/google/caja/plugin/test-scan-guest.js Wed Feb
25 21:23:55 2015 UTC
@@ -29,7 +29,8 @@
* HTMLTextAreaElement, HTMLVideoElement, HTMLButtonElement,
* Audio, Image, Option, XMLHttpRequest, Window, Document, Node,
Element,
* Attr, Text, CSSStyleDeclaration, CanvasRenderingContext2D,
- * CanvasGradient, ImageData, Location, TouchList
+ * CanvasGradient, ImageData, Location, TouchList,
+ * ArrayBuffer, Int8Array, DataView
* @overrides window
*/

@@ -351,6 +352,11 @@
var genAccessorSet = genMethod(G.value(cajaVM.def({
toString: function() { return '<setter garbage>'; }
})));
+ function genInstance(ctor) {
+ return G.lazyValue(function() {
+ return obtainInstance(ctor);
+ });
+ }
/** Add third value-callback to an arguments generator */
function annotate(calls, callback) {
return G.apply(function (call) {
@@ -1322,9 +1328,105 @@
obtainInstance.define(CanvasGradient,

document.createElement('canvas').getContext('2d').createLinearGradient(
0, 1, 2, 3));
- obtainInstance.define(ImageData,
- document.createElement('canvas').getContext('2d').createImageData(
- 2, 2));
+ obtainInstance.define(ImageData, (function() {
+ var v =
document.createElement('canvas').getContext('2d').createImageData(
+ 2, 2);
+ // v.data is an unfrozen native Uint8ClampedArray
+ expectedUnfrozen.setByIdentity(v.data);
+ expectedUnfrozen.setByIdentity(v.data.buffer);
+ return v;
+ }()));
+
+ // Combined Typed Array processing
+ // TODO(kpreid): Reorder everything so that args, obtainInstance, and
+ // expectedUnfrozen are done together for all types.
+ if (inES5Mode) (function() {
+
+ argsByAnyFrame('ArrayBuffer', genNew(genSmallInteger));
+ argsByAnyFrame('ArrayBuffer.isView', genMethod(G.any(
+ genObject, genInstance(Int8Array))));
+ argsByAnyFrame('ArrayBuffer.prototype.slice',
+ freshResult(genMethod(genSmallInteger, genSmallInteger)));
+ obtainInstance.define(ArrayBuffer, new ArrayBuffer(3));
+ expectedUnfrozen.setByConstructor(ArrayBuffer, true);
+ expectedUnfrozen.setByConstructor(
+ simpleEval(tamingEnv, 'ArrayBuffer'), true);
+
+ forEachFrame('Int8Array', function(arrayCtor) {
+ // inert ctor we need to note
+ var ArrayBufferView =
+ Object.getPrototypeOf(arrayCtor.prototype).constructor;
+ if (ArrayBufferView === Object) return;
+ var refArrayBufferView = Ref.is(ArrayBufferView);
+ functionArgs.set(refArrayBufferView, genNew());
+ expectedAlwaysThrow.mark(refArrayBufferView);
+ });
+
+ // PLAIN_CALL is needed on Firefox because it allows calling these
ctors
+ var typedArrayCall = G.tuple(G.value(CONSTRUCT, PLAIN_CALL), G.any(
+ G.tuple(genSmallInteger),
+ G.tuple(genInstance(Int8Array)),
+ G.tuple(genNumbers(2)),
+ genConcat(
+ G.tuple(genInstance(ArrayBuffer)),
+ genNumbers(2))));
+ function setupTypedArray(name, doAllCalls) {
+ var ref = RefAnyFrame(name);
+ var ctor = window[name];
+
+ functionArgs.set(ref, freshResult(doAllCalls
+ ? typedArrayCall
+ : genAllCall(G.value(0))));
+ obtainInstance.define(ctor, new ctor(3));
+
+ argsByAnyFrame(name + '.prototype.set', doAllCalls
+ ? G.any(
+ genMethod(genInstance(Int8Array), genSmallInteger),
+ genMethod(G.value([1, 2, 3]), genSmallInteger))
+ : genMethod(G.value([1, 2, 3]), G.value(0)));
+ argsByAnyFrame(name + '.prototype.subarray', freshResult(
+ doAllCalls
+ ? genMethod(genSmallInteger, genSmallInteger)
+ : genMethod(G.value(1), G.value(2))));
+ }
+ // To save on scan time, we only fully exercise some of the array
types
+ // (chosen for coverage of different cases: 1-byte, clamped,
endianness,
+ // floats).
+ setupTypedArray('Int8Array', true);
+ setupTypedArray('Uint8Array', false);
+ setupTypedArray('Uint8ClampedArray', true);
+ setupTypedArray('Int16Array', false);
+ setupTypedArray('Uint16Array', false);
+ setupTypedArray('Int32Array', false);
+ setupTypedArray('Uint32Array', true);
+ setupTypedArray('Float32Array', false);
+ setupTypedArray('Float64Array', true);
+
+ argsByAnyFrame('DataView', genNew(
+ genInstance(ArrayBuffer), genSmallInteger, genSmallInteger));
+ obtainInstance.define(DataView, new DataView(new ArrayBuffer(8)));
+ expectedUnfrozen.setByConstructor(DataView, true);
+ var get8 = genMethod(genSmallInteger);
+ argsByAnyFrame('DataView.prototype.getInt8', get8);
+ argsByAnyFrame('DataView.prototype.getUint8', get8);
+ var getWide = genMethod(genSmallInteger, genBoolean);
+ argsByAnyFrame('DataView.prototype.getInt16', getWide);
+ argsByAnyFrame('DataView.prototype.getUint16', getWide);
+ argsByAnyFrame('DataView.prototype.getInt32', getWide);
+ argsByAnyFrame('DataView.prototype.getUint32', getWide);
+ argsByAnyFrame('DataView.prototype.getFloat32', getWide);
+ argsByAnyFrame('DataView.prototype.getFloat64', getWide);
+ var set8 = genMethod(genSmallInteger, genNumber);
+ argsByAnyFrame('DataView.prototype.setInt8', set8);
+ argsByAnyFrame('DataView.prototype.setUint8', set8);
+ var setWide = genMethod(genSmallInteger, genNumber, genBoolean);
+ argsByAnyFrame('DataView.prototype.setInt16', setWide);
+ argsByAnyFrame('DataView.prototype.setUint16', setWide);
+ argsByAnyFrame('DataView.prototype.setInt32', setWide);
+ argsByAnyFrame('DataView.prototype.setUint32', setWide);
+ argsByAnyFrame('DataView.prototype.setFloat32', setWide);
+ argsByAnyFrame('DataView.prototype.setFloat64', setWide);
+ })();

var tamingFeralWin = directAccess.evalInTamingFrame('window');
var guestFeralWin = directAccess.evalInGuestFrame('window');
=======================================
--- /branches/es53/tests/com/google/caja/plugin/test-taming-inout-guest.js
Tue Feb 24 19:34:47 2015 UTC
+++ /branches/es53/tests/com/google/caja/plugin/test-taming-inout-guest.js
Wed Feb 25 21:23:55 2015 UTC
@@ -175,6 +175,57 @@
pass('testGuestConstructedObjectMethods');
});

+jsunitRegisterIf(
+ typeof ArrayBuffer !== 'undefined',
+ 'testGuestTypedArrays',
+ function testGuestTypedArrays() {
+ // Note: We haven't written a corresponding guest-to-host test, because
the
+ // handling of typed arrays is fully symmetric (copyUnmemoized) and has
no
+ // reason to behave differently.
+
+ var buf = new Uint8Array([1, 2, 3]).buffer;
+ tamedApi.tamedHostPureFunction(
+ 'assertTrue("value " + a, a instanceof frame.imports.ArrayBuffer);',
buf);
+ tamedApi.tamedHostPureFunction(
+ 'assertEquals("buffer length", 3, a.byteLength);', buf);
+ tamedApi.tamedHostPureFunction(
+ 'assertEquals("buffer element", 2, new Uint8Array(a)[1]);', buf);
+
+ // Testing array type and also specifically the lack of buffer sharing.
+ var a = new Uint8Array([1, 2, 3]);
+ var b = new Uint8Array(a.buffer);
+ tamedApi.tamedHostPureFunction(
+ 'assertTrue("array " + a, a instanceof frame.imports.Uint8Array);',
a, b);
+ tamedApi.tamedHostPureFunction(
+ 'assertNotEquals("array buffer", a.buffer, b.buffer);', a, b);
+ tamedApi.tamedHostPureFunction('a[0] = 99;', a);
+ // no persistent mutation and sharing
+ tamedApi.tamedHostPureFunction(
+ 'assertEquals("not mutated", 2, a[0] + b[0]);', a, b);
+ assertEquals('guest not mutated', 1, a[0]);
+
+ // DataView
+ var array = new Uint8Array([0, 0, 0, 1, 0, 1]);
+ var vbuf = array.buffer;
+ var view = new DataView(vbuf, 1, 4);
+ tamedApi.tamedHostPureFunction(
+ 'assertTrue("view: " + a, a instanceof frame.imports.DataView);',
+ view, vbuf);
+ tamedApi.tamedHostPureFunction(
+ 'assertNotEquals("equality", a.buffer, b);', view, vbuf);
+ tamedApi.tamedHostPureFunction('assertEquals("bo", 1, a.byteOffset);',
view);
+ tamedApi.tamedHostPureFunction('assertEquals("bl", 4, a.byteLength);',
view);
+ tamedApi.tamedHostPureFunction(
+ 'assertEquals("value", 256, a.getUint32(0));', view);
+ tamedApi.tamedHostPureFunction(
+ 'a.setUint32(0, 10);', view);
+ assertEquals('mutation check 1', 1, array[3]);
+ assertEquals('mutation check 2', 0, array[4]);
+
+ pass('testGuestTypedArrays');
+});
+
+
jsunitRegister('testMembraneViolation',
function testMembraneViolation() {
expectFailure(function() {
Reply all
Reply to author
Forward
0 new messages