Revision: 5644
Author: [email protected]
Date: Tue Dec 10 23:52:12 2013 UTC
Log: Add basic support for touch events.
https://codereview.appspot.com/40570044
* Whitelist touch event types (touchstart, etc.)
* Tame event properties touches, targetTouches, changedTouches.
* Tamings for Touch and TouchList types.
Supporting changes:
* PT.TameMemoIf now uses the property name from the environment
(refactoring only).
* Removed stray whitespace in domado.js.
Note that there are unfortunately no tests added since the existing
test automation only does click events (not touches) and there does not
appear to be a portable way to create touch events or other
touch-related data types from within the browser, even.
[email protected]
http://code.google.com/p/google-caja/source/detail?r=5644
Modified:
/trunk/src/com/google/caja/lang/html/htmlext-attributes.json
/trunk/src/com/google/caja/plugin/domado.js
/trunk/tests/com/google/caja/plugin/test-domado-events-guest.html
/trunk/tests/com/google/caja/plugin/test-scan-guest.js
=======================================
--- /trunk/src/com/google/caja/lang/html/htmlext-attributes.json Mon Oct 15
18:33:58 2012 UTC
+++ /trunk/src/com/google/caja/lang/html/htmlext-attributes.json Tue Dec 10
23:52:12 2013 UTC
@@ -11,9 +11,21 @@
"a DOM method getElementsByName (not yet implemented by us)
which",
"searches all name= attributes, and which is used by jQuery,
which",
"suggests we need this. Original rationale lost."
- ] }
+ ] },
+ { "key": "*::ONTOUCHCANCEL", "type": "SCRIPT", "optional": true },
+ { "key": "*::ONTOUCHEND", "type": "SCRIPT", "optional": true },
+ { "key": "*::ONTOUCHENTER", "type": "SCRIPT", "optional": true },
+ { "key": "*::ONTOUCHLEAVE", "type": "SCRIPT", "optional": true },
+ { "key": "*::ONTOUCHMOVE", "type": "SCRIPT", "optional": true },
+ { "key": "*::ONTOUCHSTART", "type": "SCRIPT", "optional": true }
],
"allowed": [
+ "*::ONTOUCHCANCEL",
+ "*::ONTOUCHEND",
+ "*::ONTOUCHENTER",
+ "*::ONTOUCHLEAVE",
+ "*::ONTOUCHMOVE",
+ "*::ONTOUCHSTART"
]
}
=======================================
--- /trunk/src/com/google/caja/plugin/domado.js Thu Nov 28 03:55:43 2013 UTC
+++ /trunk/src/com/google/caja/plugin/domado.js Tue Dec 10 23:52:12 2013 UTC
@@ -516,7 +516,7 @@
return cajaVM.def(Confidence);
})();
-
+
/**
* Explicit marker that this is a function intended to be exported that
needs
* no other wrapping. Also, remove the function's .prototype object.
@@ -815,7 +815,7 @@
/**
* Handler for a proxy which presents value properties derived from an
* external data source.
- *
+ *
* The subclass should implement .col_lookup(name) -> internalvalue,
* .col_evaluate(internalvalue) -> value, and .col_names() -> array.
*/
@@ -869,7 +869,7 @@
if (XMLHttpRequest &&
new XMLHttpRequest().withCredentials !== undefined) {
return XMLHttpRequest;
- } else if (XDomainRequest) {
+ } else if (XDomainRequest) {
return function XDomainRequestObjectForIE() {
var xdr = new XDomainRequest();
xdr.onload = function () {
@@ -944,10 +944,10 @@
amplify(this, function(privates) {
var xhr = privates.feral = new xmlHttpRequestMaker();
taming.tamesTo(xhr, this);
-
+
privates.async = undefined;
privates.handler = undefined;
-
+
Object.preventExtensions(privates);
});
}
@@ -2509,26 +2509,30 @@
*/
var PT = cajaVM.def({
/**
- * Ensure that a taming wrapper for the given underlying property
is
+ * Ensure that a taming wrapper for the feral property's value is
* memoized via the taming membrane, but only if 'memo' is true.
*
* @param {boolean} memo Memoize if true, construct every time if
false.
- * @param {string} feralProp feral property name to use as lookup
key.
* @param {function} tamer function(privates, feralValue) ->
tamedValue
*/
- TameMemoIf: function(memo, feralProp, tamer) {
+ TameMemoIf: function(memo, tamer) {
assert(typeof memo === 'boolean'); // in case of bad data
return Props.markPropMaker(function(env) {
+ var prop = env.prop;
return {
enumerable: true,
get: memo ? env.amplifying(function(privates) {
- var feral = privates.feral[feralProp];
+ var feral = privates.feral[prop];
+ if (feral !== Object(feral)) {
+ // not an object, membrane does not apply
+ return tamer.call(this, privates, feral);
+ }
if (!taming.hasTameTwin(feral)) {
taming.tamesTo(feral, tamer.call(this, privates, feral));
}
return taming.tame(feral);
}) : env.amplifying(function(privates) {
- return tamer.call(this, privates,
privates.feral[feralProp]);
+ return tamer.call(this, privates, privates.feral[prop]);
})
};
});
@@ -2890,7 +2894,7 @@
} catch (e) {}
return null;
}
-
+
/**
* Like tameRelatedNode but includes the window (which is an
EventTarget,
* but not a Node).
@@ -3845,7 +3849,7 @@
nextSibling: PT.related,
previousSibling: PT.related,
parentNode: PT.related,
- childNodes: PT.TameMemoIf(nodeListsAreLive, 'childNodes',
+ childNodes: PT.TameMemoIf(nodeListsAreLive,
function(privates, f) {
if (privates.policy.childrenVisible) {
return new TameNodeList(f, defaultTameNode);
@@ -3853,7 +3857,7 @@
return fakeNodeList([], 'NodeList');
}
}),
- attributes: PT.TameMemoIf(namedNodeMapsAreLive, 'attributes',
+ attributes: PT.TameMemoIf(namedNodeMapsAreLive,
function(privates, feralMap) {
if (privates.policy.attributesVisible) {
var feralOwnerElement = privates.feral;
@@ -4500,7 +4504,7 @@
});
if ('classList' in elementForFeatureTests) {
Props.define(TameElement.prototype, TameNodeConf, {
- classList: PT.TameMemoIf(true, 'classList',
+ classList: PT.TameMemoIf(true,
function(privates, feralList) {
var element = this;
return new TameDOMSettableTokenList(feralList,
@@ -4589,7 +4593,7 @@
tamingClassTable.registerLazy(domClass, defineElementThunk);
}
cajaVM.def(defineElement);
-
+
/**
* For elements which have no properties at all, but we want to
define in
* in order to be explicitly complete (suppress the no-implementation
@@ -4740,7 +4744,7 @@
// only as #hhhhhh, not as names.
return colorNameTable[" " + colorString] || colorString;
}
-
+
tamingClassTable.registerLazy('ImageData', function() {
function TameImageData(imageData) {
TameImageDataConf.confide(this, taming);
@@ -5345,7 +5349,7 @@
},
properties: function() { return {
action: PT.filterAttr(defaultToEmptyStr, String),
- elements: PT.TameMemoIf(false, 'elements', function(privates, f)
{
+ elements: PT.TameMemoIf(false, function(privates, f) {
// TODO(kpreid): make tameHTMLCollection live-capable
return tameHTMLCollection(f, defaultTameNode);
}),
@@ -5512,7 +5516,7 @@
false, function (x) { return x == null ? '' : '' + x; })
}; }
});
-
+
defineElement({
superclass: 'CajaFormField',
domClass: 'HTMLInputElement',
@@ -5551,7 +5555,7 @@
domClass: 'HTMLSelectElement',
properties: function() { return {
multiple: PT.rw,
- options: PT.TameMemoIf(nodeListsAreLive, 'options',
+ options: PT.TameMemoIf(nodeListsAreLive,
function(privates, f) {
return new TameOptionsList(f, defaultTameNode, 'name');
}),
@@ -5793,7 +5797,7 @@
properties: function() { return {
// TODO(kpreid): Arrange so there are preexisting functions to
pass
// into TameMemoIf rather than repeating this inline stuff.
- cells: PT.TameMemoIf(nodeListsAreLive, 'cells',
+ cells: PT.TameMemoIf(nodeListsAreLive,
function(privates, feralList) {
return new TameNodeList(feralList, defaultTameNode);
}),
@@ -5817,7 +5821,7 @@
defineElement({
domClass: 'HTMLTableSectionElement',
properties: function() { return {
- rows: PT.TameMemoIf(nodeListsAreLive, 'rows',
+ rows: PT.TameMemoIf(nodeListsAreLive,
function(privates, feralList) {
return new TameNodeList(feralList, defaultTameNode);
}),
@@ -5839,7 +5843,7 @@
superclass: 'HTMLTableSectionElement',
domClass: 'HTMLTableElement',
properties: function() { return {
- tBodies: PT.TameMemoIf(nodeListsAreLive, 'tBodies',
+ tBodies: PT.TameMemoIf(nodeListsAreLive,
function(privates, f) {
if (privates.policy.childrenVisible) {
return new TameNodeList(f, defaultTameNode);
@@ -5911,6 +5915,73 @@
return taming.tame(event);
}
+ tamingClassTable.registerLazy('Touch', function() {
+ /**
+ * Taming of touch record objects for touch events.
+ */
+ function TameTouch(feral) {
+ // Touch objects are read-only records, so we can just copy
+ this.identifier = +feral.identifier;
+ this.screenX = +feral.screenX;
+ this.screenY = +feral.screenY;
+ this.clientX = +feral.clientX;
+ this.clientY = +feral.clientY;
+ this.pageX = +feral.pageX;
+ this.pageY = +feral.pageY;
+ this.radiusX = +feral.radiusX;
+ this.radiusY = +feral.radiusY;
+ this.rotationAngle = +feral.rotationAngle;
+ this.force = +feral.force;
+ this.target = tameRelatedNode(feral.target);
+ Object.freeze(this);
+ }
+ inertCtor(TameTouch, Object);
+ return cajaVM.def(TameTouch); // and defend its prototype
+ });
+
+ tamingClassTable.registerLazy('TouchList', function() {
+ var TameTouch = tamingClassTable.getTamingCtor('Touch');
+
+ var TameTouchListConf = new Confidence('TameTouchList');
+ /**
+ * Taming of TouchList type for touch events.
+ * These are immutable and so we do not need any NodeList-like
magic.
+ */
+ function TameTouchList(feralList) {
+ var length = feralList.length;
+ this.length = length;
+ for (var i = 0; i < length; i++) {
+ var feralTouch = feralList.item(i);
+ var tamedTouch = new TameTouch(feralTouch);
+ taming.tamesTo(feralTouch, tamedTouch);
+ this[i] = tamedTouch;
+ }
+ TameTouchListConf.confide(this, taming);
+ TameTouchListConf.amplify(this, function(privates) {
+ privates.feral = feralList;
+ });
+ Object.freeze(this);
+ }
+ inertCtor(TameTouchList, Object);
+ Props.define(TameTouchList.prototype, TameTouchListConf, {
+ // TODO(kpreid): documented in MDN, but not in linked standard;
get
+ // better reference for correct behavior.
+ identifiedTouch: Props.ampMethod(function(privates, id) {
+ id = +id;
+ var feralTouch = privates.feral.identifiedTouch(id);
+ if (!feralTouch) { return null; } // TODO(kpreid): proper
value?
+ if (!taming.hasTameTwin(feralTouch)) {
+ throw new Error('can\'t happen: untamed Touch object');
+ }
+ return taming.tame(feralTouch);
+ }),
+ item: Props.plainMethod(function(index) {
+ return this[+index];
+ })
+ });
+ return cajaVM.def(TameTouchList); // and defend its prototype
+ });
+
tamingClassTable.registerLazy('Event', function() {
function eventVirtualizingAccessor(fn) {
return Props.addOverride(Props.ampGetter(fn));
@@ -5971,6 +6042,20 @@
// property.
}
}
+
+ // Note: Per MDN the touch event properties are read-only, so we
+ // shouldn't be doing addOverride, but we also apply them to _all_
+ // events rather than having a TouchEvent subtype, so this is more
+ // compatible (if e.g. an application is constructing synthetic
events).
+ // It also avoids putting a special case in testEventMutation.
+ var touchListProp = Props.addOverride(PT.TameMemoIf(true,
+ function(privates, feralList) {
+ if (!feralList) { // applied to a non-TouchEvent
+ return undefined;
+ } else {
+ return new
(tamingClassTable.getTamingCtor('TouchList'))(feralList);
+ }
+ }));
function TameEvent(event, isSyntheticEvent) {
assert(!!event);
@@ -6029,6 +6114,9 @@
charCode: P_e_view(function(v) { return v && Number(v); }),
key: Props.cond('key' in featureTestKeyEvent, P_e_view(String)),
char: Props.cond('char' in featureTestKeyEvent,
P_e_view(String)),
+ touches: touchListProp,
+ targetTouches: touchListProp,
+ changedTouches: touchListProp,
stopPropagation: Props.ampMethod(function(privates) {
// TODO(mikesamuel): make sure event doesn't propagate to
dispatched
// events for this gadget only.
@@ -6275,7 +6363,7 @@
forms: Props.ampGetter(function(privates) {
// privates not used but we need host-exception protection and
// authority to access 'document'
-
+
// TODO(kpreid): Make this a memoized live list.
var tameForms = [];
for (var i = 0; i < document.forms.length; i++) {
@@ -7190,7 +7278,7 @@
// Install virtual UA stylesheet.
if (!document.caja_gadgetStylesheetInstalled) (function () {
document.caja_gadgetStylesheetInstalled = true;
-
+
var element = document.createElement("style");
element.setAttribute("type", "text/css");
element.textContent = (
@@ -7210,7 +7298,7 @@
"caja-v-base,caja-v-basefont,caja-v-head,caja-v-link,caja-v-meta,"
+
"caja-v-noembed,caja-v-noframes,caja-v-param,caja-v-source," +
"caja-v-track,caja-v-title{" +
- "display:none;" +
+ "display:none;" +
"}" +
"caja-v-html, caja-v-body {" +
=======================================
--- /trunk/tests/com/google/caja/plugin/test-domado-events-guest.html Tue
Oct 8 16:46:28 2013 UTC
+++ /trunk/tests/com/google/caja/plugin/test-domado-events-guest.html Tue
Dec 10 23:52:12 2013 UTC
@@ -1132,3 +1132,16 @@
}), false);
});
</script>
+
+<p id="testNonTouchEvents" class="testcontainer">testNonTouchEvents</p>
+<script type="text/javascript">
+ jsunitRegister('testNonTouchEvents', function() {
+ // Check that we don't have broken touch accessors on non-touch events
+ var e = document.createEvent('Events');
+ e.initEvent(e, 'foo', false, false);
+ assertEquals('touches', undefined, e.touches);
+ assertEquals('targetTouches', undefined, e.targetTouches);
+ assertEquals('changedTouches', undefined, e.changedTouches);
+ pass();
+ });
+</script>
=======================================
--- /trunk/tests/com/google/caja/plugin/test-scan-guest.js Mon Nov 25
20:27:00 2013 UTC
+++ /trunk/tests/com/google/caja/plugin/test-scan-guest.js Tue Dec 10
23:52:12 2013 UTC
@@ -30,7 +30,7 @@
* HTMLTextAreaElement, HTMLVideoElement, HTMLButtonElement,
* Audio, Image, Option, XMLHttpRequest, Window, Document, Node,
Element,
* Attr, Text, CSSStyleDeclaration, CanvasRenderingContext2D,
- * CanvasGradient, ImageData, Location,
+ * CanvasGradient, ImageData, Location, TouchList,
* ArrayBuffer, Int8Array, DataView
* @overrides window
*/
@@ -953,6 +953,7 @@
argsByProp('createComment', genMethod(genString));
argsByProp('createTextNode', genMethod(genString));
argsByProp('createDocumentFragment', genNoArgMethod);
+
argsByProp('createEvent', freshResult(genMethod(genEventClass)));
argsByProp('initEvent', genMethod(genEventName, genBoolean,
genBoolean));
argsByProp('initCustomEvent', genMethod(genEventName, genBoolean,
@@ -966,6 +967,17 @@
e.initCustomEvent('foo', true, true, 1);
return e;
})));
+
+ // Currently not possible to fully test touch event taming because we
can't
+ // synthesize touches to be examined.
+ argsByIdentity(TouchList.prototype.identifiedTouch,
+ genMethod(genSmallInteger));
+ argsByIdentity(TouchList.prototype.item,
+ genMethod(genSmallInteger));
+ // stubbing out because we can't get a TouchList
+ obtainInstance.define(TouchList, TouchList.prototype);
+ expectedAlwaysThrow.mark(Ref.is(TouchList.prototype.identifiedTouch));
+
argsByProp('getAttribute',
argsByProp('getAttributeNode',
argsByProp('hasAttribute', genMethod(genString))));
--
---
You received this message because you are subscribed to the Google Groups "Google Caja Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.