Revision: 4499
Author: metaweta
Date: Fri Jun 10 15:03:42 2011
Log: Fixes javascript URLs and inline event handlers
http://codereview.appspot.com/4530099
Modifies caja.js to copy the taming frame's plugin_dispatchToHandler___
and plugin_dispatchEvent___ functions into the guest frame (which is
necessary for inline event handlers like onclick="...") and into
the window containing the dom elements (which is necessary for
'javascript:' URLs). Since we always use a single taming frame
even when there are multiple guest frames, this fix should work in
the presence of multiple cajoled pages.
R=mikesamuel, jasvir
http://code.google.com/p/google-caja/source/detail?r=4499
Modified:
/trunk/src/com/google/caja/plugin/caja.js
=======================================
--- /trunk/src/com/google/caja/plugin/caja.js Mon Jun 6 15:55:09 2011
+++ /trunk/src/com/google/caja/plugin/caja.js Fri Jun 10 15:03:42 2011
@@ -80,6 +80,95 @@
}
}
}
+
+ /**
+ * Enforces {@code typeof specimen === typename}, in which case
+ * specimen is returned.
+ * <p>
+ * If not, throws an informative TypeError
+ * <p>
+ * opt_name, if provided, should be a name or description of the
+ * specimen used only to generate friendlier error messages.
+ */
+ function enforceType(specimen, typename, opt_name) {
+ if (typeof specimen !== typename) {
+ throw new TypeError('expected ' + typename + ' instead of ' +
+ typeof specimen + ': ' + (opt_name || specimen));
+ }
+ return specimen;
+ }
+
+ var registeredImports = [];
+
+ /**
+ * Gets or assigns the id associated with this (assumed to be)
+ * imports object, registering it so that
+ * <tt>getImports(getId(imports)) === imports</tt>.
+ * <p>
+ * This system of registration and identification allows us to
+ * cajole html such as
+ * <pre><a onmouseover="alert(1)">Mouse here</a></pre>
+ * into html-writing JavaScript such as<pre>
+ * IMPORTS___.document.innerHTML = "
+ * <a onmouseover=\"
+ * (function(IMPORTS___) {
+ * IMPORTS___.alert(1);
+ * })(___.getImports(" + ___.getId(IMPORTS___) + "))
+ * \">Mouse here</a>
+ * ";
+ * </pre>
+ * If this is executed by a plugin whose imports is assigned id 42,
+ * it generates html with the same meaning as<pre>
+ * <a onmouseover="___.getImports(42).alert(1)">Mouse
here</a>
+ * </pre>
+ * <p>
+ * An imports is not registered and no id is assigned to it until the
+ * first call to <tt>getId</tt>. This way, an imports that is never
+ * registered, or that has been <tt>unregister</tt>ed since the last
+ * time it was registered, will still be garbage collectable.
+ */
+ function getId(imports) {
+ enforceType(imports, 'object', 'imports');
+ var id;
+ if ('id___' in imports) {
+ id = enforceType(imports.id___, 'number', 'id');
+ } else {
+ id = imports.id___ = registeredImports.length;
+ }
+ registeredImports[id] = imports;
+ return id;
+ }
+
+ /**
+ * Gets the imports object registered under this id.
+ * <p>
+ * If it has been <tt>unregistered</tt> since the last
+ * <tt>getId</tt> on it, then <tt>getImports</tt> will fail.
+ */
+ function getImports(id) {
+ var result = registeredImports[enforceType(id, 'number', 'id')];
+ if (result === void 0) {
+ throw new Error('Internal: imports#', id, ' unregistered');
+ }
+ return result;
+ }
+
+ /**
+ * If you know that this <tt>imports</tt> no longer needs to be
+ * accessed by <tt>getImports</tt>, then you should
+ * <tt>unregister</tt> it so it can be garbage collected.
+ * <p>
+ * After unregister()ing, the id is not reassigned, and the imports
+ * remembers its id. If asked for another <tt>getId</tt>, it
+ * reregisters itself at its old id.
+ */
+ function unregister(imports) {
+ enforceType(imports, 'object', 'imports');
+ if ('id___' in imports) {
+ var id = enforceType(imports.id___, 'number', 'id');
+ registeredImports[id] = void 0;
+ }
+ }
var guestDocumentIdIndex = 0;
@@ -289,10 +378,29 @@
innerContainer);
imports.htmlEmitter___ =
new tamingWindow.HtmlEmitter(innerContainer,
imports.document);
+ var divWindow = div.ownerDocument.defaultView ||
+ div.ownerDocument.parentWindow;
+ divWindow.___.getId =
+ tamingWindow.___.getId =
+ guestWindow.___.getId = getId;
+ divWindow.___.getImports =
+ tamingWindow.___.getImports =
+ guestWindow.___.getImports = getImports;
+ divWindow.___.unregister =
+ tamingWindow.___.unregister =
+ guestWindow.___.unregister = unregister;
+ getId(imports);
+ if (!divWindow.___.tamingFrames) {
+ divWindow.___.tamingFrames = {};
+ }
+ divWindow.___.tamingFrames[imports.id___] = tamingWindow;
guestWindow.plugin_dispatchEvent___ =
tamingWindow.plugin_dispatchEvent___;
- guestWindow.plugin_dispatchToHandler___ =
- tamingWindow.plugin_dispatchToHandler___;
+ divWindow.plugin_dispatchToHandler___ =
+ function (pluginId, handler, args) {
+ return divWindow.___.tamingFrames[pluginId].
+ plugin_dispatchToHandler___(pluginId, handler, args);
+ };
}
function runMaker(func) {
@@ -449,7 +557,7 @@
'}';
aWindow.document.getElementsByTagName('head')[0].appendChild(style);
// Attach safety marker to 'window' object
- aWindow.___ = aWindow;
+ aWindow.___ = {};
// Attach recognition marker to 'Object' constructor
aWindow.Object.FERAL_FRAME_OBJECT___ = aWindow.Object;
}