Revision: 5579
Author: [email protected]
Date: Mon Aug 26 20:59:09 2013 UTC
Log: Add early support for using a complete document as vdoc container.
https://codereview.appspot.com/12775044
This support is documented as experimental, as we do not have complete
tests for functionality in the document case. Furthermore, we do not yet
use an un-virtualized <html> and <body>, which prevents some of the
desirable consequences of using a document.
* Modified caja.js to permit documents.
* Modified HtmlEmitter to permit documents and implement emitStatic
without requiring the node to have an .innerHTML.
* Documented functionality in cajajs.xml.
* If in a document, emitCss puts stylesheets inside the guest tree,
lacking anywhere else to put them. Note that in ES5 mode emitCss
is being eliminated.
* Add event dispatch hooks to feral documents.
(The above last two changes I believe to eliminate the reasons why
caja.js previously had a restriction that the <div> container must be
in the same document as caja.js was loaded in.)
[email protected]
http://code.google.com/p/google-caja/source/detail?r=5579
Modified:
/trunk/doc/cajajs/cajajs.xml
/trunk/src/com/google/caja/plugin/caja.js
/trunk/src/com/google/caja/plugin/domado.js
/trunk/src/com/google/caja/plugin/html-emitter.js
/trunk/src/com/google/caja/plugin/ses-frame-group.js
/trunk/tests/com/google/caja/plugin/test-cajajs-invocation.js
=======================================
--- /trunk/doc/cajajs/cajajs.xml Thu Oct 18 05:01:30 2012 UTC
+++ /trunk/doc/cajajs/cajajs.xml Mon Aug 26 20:59:09 2013 UTC
@@ -115,7 +115,15 @@
the guest code's virtual HTML <code>document</code></p> <p>The
caller may pass <code>undefined</code>, which causes the guest
code to run with no virtual document (e.g., for running plain
- JavaScript without a UI).</p></doc>
+ JavaScript without a UI).</p>
+
+ <p><em>Experimental:</em> It is also possible to provide a
+ HTML document node, such as an <code><iframe></code>'s
+ <code>contentDocument</code>, instead of an element. This mode of
+ operation has not been fully tested and may have missing
+ functionality. Note that the provided document <em>cannot</em> be
+ the same as the one in which <code>caja.js</code> was loaded, as
+ this would overwrite Caja's internal iframes.</p></doc>
</arg>
<arg>
<name>uriPolicy</name>
=======================================
--- /trunk/src/com/google/caja/plugin/caja.js Tue Aug 13 17:46:59 2013 UTC
+++ /trunk/src/com/google/caja/plugin/caja.js Mon Aug 26 20:59:09 2013 UTC
@@ -589,16 +589,23 @@
}
}
- function prepareContainerDiv(opt_div, feralWin, domOpts) {
- if (opt_div && feralWin['document'] !== opt_div.ownerDocument) {
- throw '<div> provided for ES5 frame must be in main document';
- }
+ /**
+ * opt_container may be absent, an element, or a Document. If absent
+ * then no DOM or related APIs are given to the guest.
+ */
+ function prepareContainerDiv(opt_container, feralWin, domOpts) {
domOpts = domOpts || {};
var opt_idClass = domOpts ? domOpts['idClass'] : void 0;
var idClass = opt_idClass || ('caja-guest-' + nextId++ + '___');
+ if (opt_container && opt_container.nodeType === 9 /* Document */) {
+ caja['console']['warn']('Warning: Using a document, rather than an '
+
+ 'element, as a Caja virtual document container is an
experimental ' +
+ 'feature and may not operate correctly or support all
features.');
+ initFeralFrame(opt_container.defaultView);
+ }
return {
'idClass': idClass,
- 'opt_div': opt_div
+ 'opt_div': opt_container
};
}
=======================================
--- /trunk/src/com/google/caja/plugin/domado.js Thu Aug 22 21:49:41 2013 UTC
+++ /trunk/src/com/google/caja/plugin/domado.js Mon Aug 26 20:59:09 2013 UTC
@@ -6858,17 +6858,34 @@
});
+ var cssIdClassPrefixRE =
+ new RegExp('(^|[},]\)\s*\\.' + idClass + ' ', 'g');
/**
* Create a CSS stylesheet with the given text and append it to the
DOM.
* @param {string} cssText a well-formed stylesheet production.
*/
domicile.emitCss = cajaVM.constFunc(function(cssText) {
+ if (outerContainerNode === document) {
+ // Kludge to strip out container class markers from the cajoler
if
+ // we're using a frame. TODO(kpreid): Modify the cajoler so that
its
+ // generated emitCss invocations are parameterized to handle this
+ // difference. TODO(kpreid): Validate this regexp strategy.
+ cssText = cssText.replace(cssIdClassPrefixRE, '');
+ }
this.getCssContainer().appendChild(
bridal.createStylesheet(document, cssText));
});
/** The node to which gadget stylesheets should be added. */
domicile.getCssContainer = cajaVM.constFunc(function() {
- var e = document.getElementsByTagName('head')[0];
+ var e = document.getElementsByTagName('head')[0] ||
+ // iframe doc
+ outerContainerNode.getElementsByTagName('caja-v-head')[0];
+ if (!e) {
+ if (typeof console !== 'undefined') {
+ console.warn('Domado: Unable to find location to stash
stylesheet');
+ }
+ e = document.createElement('head');
+ }
e = makeDOMAccessible(e);
return e;
});
=======================================
--- /trunk/src/com/google/caja/plugin/html-emitter.js Fri Aug 2 06:16:42
2013 UTC
+++ /trunk/src/com/google/caja/plugin/html-emitter.js Mon Aug 26 20:59:09
2013 UTC
@@ -60,11 +60,16 @@
'Host page error: Virtual document element was not provided');
}
base = makeDOMAccessible(base);
+
+ var targetDocument = base.nodeType === 9 // Document node
+ ? base
+ : base.ownerDocument;
+
// TODO(kpreid): Fix our terminology: HTML5 spec contains something
called the
// 'insertion point', which is not this; this is the 'current node' and
// implicitly the 'stack of open elements' via parents.
var insertionPoint = base;
- var bridal = bridalMaker(makeDOMAccessible, base);
+ var bridal = bridalMaker(makeDOMAccessible, targetDocument);
// TODO: Take into account <base> elements.
@@ -104,7 +109,7 @@
if (!idMap) { buildIdMap(); }
var node = idMap[id + " map entry"];
if (node) { return node; }
- for (; (node = base.ownerDocument.getElementById(id));) {
+ for (; (node = targetDocument.getElementById(id));) {
if (base.contains
? base.contains(node)
: (base.compareDocumentPosition(node) & 0x10)) {
@@ -126,7 +131,21 @@
throw new Error('Host page error: HtmlEmitter.emitStatic called
after' +
' document finish()ed');
}
- base.innerHTML += htmlString;
+ if (base.nodeType === 1 /* Element */) {
+ base.innerHTML += htmlString;
+ } else {
+ // We need to handle Document nodes, which don't provide .innerHTML.
+
+ // Additionally, we currently don't use real <html> and <head>
elements
+ // and so just doing base.write(htmlString), which would otherwise be
+ // sufficient, would insert unwanted structure around our HTML.
+ // TODO(kpreid): Fix that.
+ var dummy = makeDOMAccessible(targetDocument.createElement('div'));
+ dummy.innerHTML = htmlString;
+ while (dummy.firstChild) {
+ base.appendChild(dummy.firstChild);
+ }
+ }
updateInsertionMode();
}
@@ -723,7 +742,7 @@
throw new Error('HtmlEmitter internal: attempted to add text to ' +
'unsafe element ' + realTagName + '!');
}
-
insertionPoint.appendChild(insertionPoint.ownerDocument.createTextNode(
+ insertionPoint.appendChild(targetDocument.createTextNode(
html.unescapeEntities(text)));
}
=======================================
--- /trunk/src/com/google/caja/plugin/ses-frame-group.js Wed Aug 14
17:51:03 2013 UTC
+++ /trunk/src/com/google/caja/plugin/ses-frame-group.js Mon Aug 26
20:59:09 2013 UTC
@@ -65,6 +65,7 @@
return lazyDomado || (lazyDomado = Domado(null));
}
+ // TODO(kpreid): Only used for XHR; dependency on feralWin is bogus
var bridal = bridalMaker(identity, feralWin.document);
var unsafe = false;
@@ -294,15 +295,13 @@
identity, domicile.htmlEmitterTarget, uriPolicy.mitigate, domicile,
window);
- if (!feralWin.___.tamingWindows) {
- feralWin.___.tamingWindows = {};
- }
- feralWin.___.tamingWindows[imports.id___] = tamingWin;
-
// Invoked by textual event handlers emitted by Domado.
// TODO(kpreid): Use a name other than ___ for this purpose; perhaps
some
// property of the 'caja' object.
- feralWin.___.plugin_dispatchEvent___ =
getDomado().plugin_dispatchEvent;
+ var containerFeralWin =
+ (divInfo.opt_div.ownerDocument || divInfo.opt_div).defaultView;
+ containerFeralWin.___.plugin_dispatchEvent___ =
+ getDomado().plugin_dispatchEvent;
return [domicile, htmlEmitter];
}
=======================================
--- /trunk/tests/com/google/caja/plugin/test-cajajs-invocation.js Wed Aug
14 04:56:12 2013 UTC
+++ /trunk/tests/com/google/caja/plugin/test-cajajs-invocation.js Mon Aug
26 20:59:09 2013 UTC
@@ -42,7 +42,8 @@
* Assert that a cajoled and loaded fixture-guest.html has the right
* results.
*/
- function assertGuestHtmlCorrect(frame, div) {
+ function assertGuestHtmlCorrect(frame, div, opt_containerDoc) {
+ var containerDoc = opt_containerDoc || document;
assertStringContains('static html', div.innerHTML);
assertStringContains('edited html', div.innerHTML);
assertStringContains('dynamic html', div.innerHTML);
@@ -50,13 +51,13 @@
assertStringContains('external script', div.innerHTML);
}
assertEquals('small-caps',
- document.defaultView.getComputedStyle(
- document.getElementById('foo-' + frame.idSuffix),
+ containerDoc.defaultView.getComputedStyle(
+ containerDoc.getElementById('foo-' + frame.idSuffix),
null).fontVariant);
if (inES5Mode) {
assertEquals('inline',
- document.defaultView.getComputedStyle(
- document.getElementById('hello-' + frame.idSuffix),
+ containerDoc.defaultView.getComputedStyle(
+ containerDoc.getElementById('hello-' + frame.idSuffix),
null).display);
}
}
@@ -583,7 +584,8 @@
});
});
- jsunitRegister('testUrlHtmlWithMimeType', function testUrlHtml() {
+ jsunitRegister('testUrlHtmlWithMimeType',
+ function testUrlHtmlWithMimeType() {
var div = createDiv();
frameGroup.makeES5Frame(div, uriPolicy, function (frame) {
frame.url('fixture-guest.html', 'text/html').run({},
@@ -593,8 +595,26 @@
});
});
});
+
+ jsunitRegister('testContainerIsIframe', function
testContainerIsIframe() {
+ var container = document.createElement('iframe');
+ document.body.appendChild(container);
+ setTimeout(function() { // delay required on Firefox
+ var frameDoc = container.contentDocument;
+ frameDoc.removeChild(frameDoc.documentElement);
+ frameGroup.makeES5Frame(frameDoc, uriPolicy,
+ function(frame) {
+ frame.url('fixture-guest.html', 'text/html').run({},
+ function (result) {
+ // TODO(kpreid): Ensure no container
+ assertGuestHtmlCorrect(frame, frameDoc.documentElement,
frameDoc);
+ jsunitPass('testContainerIsIframe');
+ });
+ });
+ }, 0);
+ });
+
readyToTest();
jsunitRun();
});
-
})();
--
---
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.