Commit 28b5175c authored by ap@webkit.org's avatar ap@webkit.org

Reviewed by Sam Weinig, Anders Carlsson, and (unofficially) Adam Barth.

        https://bugs.webkit.org/show_bug.cgi?id=20879
        Implement HTML5 channel messaging

        Tests: fast/events/message-channel-gc.html
               fast/events/message-port-deleted-document.html
               fast/events/message-port-deleted-frame.html
               fast/events/message-port-inactive-document.html
               fast/events/message-port.html
               http/tests/security/MessagePort/event-listener-context.html

        * Configurations/WebCore.xcconfig:
        Removed unused  ENABLE_CROSS_DOCUMENT_MESSAGING macro.

        * DerivedSources.make:
        Added MessageChannel and MessagePort.

        * WebCore.pro: Made MessageEvent compilation unconditional, as it could not possibly be
        turmed off anyway. Added new files.

        * GNUmakefile.am:
        * WebCore.pro:
        * WebCore.vcproj/WebCore.vcproj:
        * WebCore.xcodeproj/project.pbxproj:
        * WebCoreSources.bkl:
        Added new files.

        * bindings/js/JSDOMWindowBase.h:
        * bindings/js/JSDOMWindowBase.cpp: (WebCore::JSDOMWindowBase::getValueProperty):
        Added suport for window.MessageChannel constructor.

        * bindings/js/JSDOMWindowCustom.cpp: (WebCore::JSDOMWindow::postMessage):
        * page/DOMWindow.cpp:
        (WebCore::DOMWindow::postMessage):
        * page/DOMWindow.h:
        * page/DOMWindow.idl:
        Added support for three-argument postMessage (that posts a MessagePort).

        * dom/EventTarget.cpp:
        (WebCore::EventTarget::toMessagePort):
        * dom/EventTarget.h:
        * bindings/js/JSEventTargetBase.cpp: (WebCore::toJS): Added MessagePort as yet another
        EventTarget variant.

        * bindings/js/JSMessageChannelConstructor.h:
        * bindings/js/JSMessageChannelConstructor.cpp: Added a custom constructor, so that it could
        take a browsing context (document) parameter.

        * bindings/js/JSMessageChannelCustom.cpp: Added.
        (WebCore::JSMessageChannel::mark): JSMessageChannel uses a custom mark function to mark
        port1 and port2 that it owns.

        * bindings/js/JSMessagePortCustom.cpp: Added.
        (WebCore::JSMessagePort::startConversation):
        (WebCore::JSMessagePort::addEventListener):
        (WebCore::JSMessagePort::removeEventListener):
        (WebCore::JSMessagePort::dispatchEvent):
        (WebCore::JSMessagePort::setOnmessage):
        (WebCore::JSMessagePort::onmessage):
        (WebCore::JSMessagePort::setOnclose):
        (WebCore::JSMessagePort::onclose):
        (WebCore::JSMessagePort::mark):
        * dom/MessagePort.cpp: Added.
        * dom/MessagePort.h: Added.
        * dom/MessagePort.idl: Added.
        Added a MessagePort implementation. Currently, it is not thread-safe at all, and only works
        with Documents as contexts, but in the future, it will be used for communication with worker
        threads.

        * bindings/objc/DOMInternal.h: Include "DOMMessagePortInternal.h". The new APIs do not
        really have Obj-C bindings, as they are far from being final, but a MessagePort stub is
        needed for MessageEvent.

        * bindings/scripts/CodeGeneratorJS.pm: Include PlatformString.h for MessagePort happiness.

        * dom/Document.cpp:
        (WebCore::MessagePortTimer::MessagePortTimer):
        (WebCore::MessagePortTimer::fired):
        (WebCore::Document::processMessagePortMessagesSoon):
        (WebCore::Document::~Document):
        (WebCore::Document::dispatchMessagePortEvents):
        (WebCore::Document::createdMessagePort):
        (WebCore::Document::destroyedMessagePort):
        * dom/Document.h:
        Document keeps track of all MessagePort objects that were created when it was fully active
        in its context.

        * dom/EventNames.h: Added closeEvent.

        * dom/MessageChannel.cpp: Added.
        (WebCore::MessageChannel::MessageChannel):
        (WebCore::MessageChannel::~MessageChannel):
        * dom/MessageChannel.h: Added.
        (WebCore::MessageChannel::create):
        (WebCore::MessageChannel::port1):
        (WebCore::MessageChannel::port2):
        * dom/MessageChannel.idl: Added.
        Addded JSMessageChannel implementation.

        * dom/MessageEvent.cpp:
        (WebCore::MessageEvent::MessageEvent):
        (WebCore::MessageEvent::initMessageEvent):
        * dom/MessageEvent.h:
        (WebCore::MessageEvent::create):
        (WebCore::MessageEvent::messagePort):
        * dom/MessageEvent.idl:
        MessageEvent has a MessagePort member now, making it possible to pass ports across
        documents.



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@36891 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 3e32f42b
2008-09-24 Alexey Proskuryakov <ap@webkit.org>
Reviewed by Sam Weinig, Anders Carlsson, and (unofficially) Adam Barth.
https://bugs.webkit.org/show_bug.cgi?id=20879
Implement HTML5 channel messaging
* fast/dom/Window/window-properties-expected.txt:
* fast/events/message-channel-gc-expected.txt: Added.
* fast/events/message-channel-gc.html: Added.
* fast/events/message-port-deleted-document-expected.txt: Added.
* fast/events/message-port-deleted-document.html: Added.
* fast/events/message-port-deleted-frame-expected.txt: Added.
* fast/events/message-port-deleted-frame.html: Added.
* fast/events/message-port-expected.txt: Added.
* fast/events/message-port-inactive-document-expected.txt: Added.
* fast/events/message-port-inactive-document.html: Added.
* fast/events/message-port.html: Added.
* fast/events/resources/message-port-iframe.html: Added.
* http/tests/security/MessagePort: Added.
* http/tests/security/MessagePort/event-listener-context-expected.txt: Added.
* http/tests/security/MessagePort/event-listener-context.html: Added.
* http/tests/security/MessagePort/resources: Added.
* http/tests/security/MessagePort/resources/attacker-frame.html: Added.
* http/tests/security/MessagePort/resources/banking-site.html: Added.
2008-09-25 David Smith <catfish.man@gmail.com>
Reviewed by Eric Seidel
......
......@@ -901,6 +901,8 @@ window.MediaList.prototype [object MediaListPrototype]
window.MediaList.prototype.appendMedium [function]
window.MediaList.prototype.deleteMedium [function]
window.MediaList.prototype.item [function]
window.MessageChannel [object MessageChannelConstructor]
window.MessageChannel.prototype [object MessageChannelPrototype]
window.MessageEvent [object MessageEventConstructor]
window.MessageEvent.prototype [printed above as window.Event.prototype]
window.MouseEvent [object MouseEventConstructor]
......
Test some cases of MessagePort garbage collection.
Should be 'bar': bar
onmessage
message listener
onclose
close listener
<body>
<p>Test some cases of MessagePort garbage collection.</p>
<pre id=log></pre>
<script>
function gc()
{
if (window.GCController)
return GCController.collect();
for (var i = 0; i < 10000; i++) { // > force garbage collection (FF requires about 9K allocations before a collect)
var s = new String("abc");
}
}
function log(message)
{
document.getElementById("log").innerHTML += message + "<br>";
}
if (window.layoutTestController) {
layoutTestController.dumpAsText();
layoutTestController.waitUntilDone();
}
var channel = new MessageChannel;
channel.port1.foo = "bar";
gc();
log("Should be 'bar': " + channel.port1.foo);
channel.port1.onmessage = function() { log("onmessage"); }
channel.port1.onclose = function() { log("onclose"); setTimeout(function() { if (window.layoutTestController) layoutTestController.notifyDone() }, 0) }
channel.port1.addEventListener("close", function() { log("close listener"); }, false);
channel.port1.addEventListener("message", function() { log("message listener"); }, false);
gc();
channel.port1.start();
channel.port2.postMessage("msg");
gc();
channel.port1.close();
channel.port2.close();
gc();
</script>
</body>
Test that destroying a document doesn't cause a crash when posting a message to a MessagePort it owned.
Didn't crash: SUCCESS
<body>
<p>Test that destroying a document doesn't cause a crash when posting a message to a MessagePort it owned.</p>
<pre id=log></pre>
<script>
function gc()
{
if (window.GCController)
return GCController.collect();
for (var i = 0; i < 10000; i++) { // > force garbage collection (FF requires about 9K allocations before a collect)
var s = new String("abc");
}
}
function log(message)
{
document.getElementById("log").innerHTML += message + "<br>";
}
if (window.layoutTestController) {
layoutTestController.dumpAsText();
layoutTestController.waitUntilDone();
}
var mainPort;
function test()
{
var channel = new MessageChannel;
window.frames[0].postMessage("msg", channel.port2, "*");
mainPort = channel.port1;
mainPort.start();
mainPort.postMessage("ping");
mainPort.onmessage = test2;
}
function test2()
{
var frameElement = document.getElementsByTagName("iframe")[0];
frameElement.parentNode.removeChild(frameElement);
frameElement = null;
gc();
setTimeout(test3, 10);
}
function test3()
{
gc();
mainPort.postMessage("ping");
mainPort.onmessage = function(evt) {
if (evt.data == "pong")
log("Unexpected response: FAIL");
};
setTimeout(test4, 10);
}
function test4()
{
mainPort.close();
log("Didn't crash: SUCCESS");
if (window.layoutTestController)
layoutTestController.notifyDone();
}
</script>
<iframe src="resources/message-port-iframe.html" onload="test()"></iframe>
</body>
Test that destroying a frame doesn't cause a crash when posting a message to a MessagePort it owned.
Didn't crash: SUCCESS
<body>
<p>Test that destroying a frame doesn't cause a crash when posting a message to a MessagePort it owned.</p>
<pre id=log></pre>
<script>
function gc()
{
if (window.GCController)
return GCController.collect();
for (var i = 0; i < 10000; i++) { // > force garbage collection (FF requires about 9K allocations before a collect)
var s = new String("abc");
}
}
function log(message)
{
document.getElementById("log").innerHTML += message + "<br>";
}
if (window.layoutTestController) {
layoutTestController.dumpAsText();
layoutTestController.waitUntilDone();
}
var mainPort;
var frameDoc;
function test()
{
frameDoc = window.frames[0].document;
var channel = new MessageChannel;
window.frames[0].postMessage("msg", channel.port2, "*");
mainPort = channel.port1;
mainPort.start();
mainPort.postMessage("ping");
mainPort.onmessage = test2;
}
function test2()
{
var frameElement = document.getElementsByTagName("iframe")[0];
frameElement.parentNode.removeChild(frameElement);
frameElement = null;
gc();
setTimeout(test3, 10);
}
function test3()
{
gc();
mainPort.postMessage("ping");
mainPort.onmessage = function(evt) {
if (evt.data == "pong")
log("Unexpected response: FAIL");
};
setTimeout(test4, 10);
}
function test4()
{
log("Didn't crash: SUCCESS");
if (window.layoutTestController)
layoutTestController.notifyDone();
}
</script>
<iframe src="resources/message-port-iframe.html" onload="test()"></iframe>
</body>
Test cross-frame MessagePort communication.
Simple exchange: SUCCESS
Posted port: SUCCESS
Conversation: SUCCESS
A port is active: SUCCESS
A closed port is inactive: SUCCESS
Test that messaging an inactive frameless document works as expected.
Didn't crash, freeze or respond: SUCCESS
<body>
<p>Test that messaging an inactive frameless document works as expected.</p>
<pre id=log></pre>
<script>
function gc()
{
if (window.GCController)
return GCController.collect();
for (var i = 0; i < 10000; i++) { // > force garbage collection (FF requires about 9K allocations before a collect)
var s = new String("abc");
}
}
function log(message)
{
document.getElementById("log").innerHTML += message + "<br>";
}
if (window.layoutTestController) {
layoutTestController.dumpAsText();
layoutTestController.waitUntilDone();
}
var document;
var mainPort;
function test()
{
document = window.frames[0].document;
var channel = new MessageChannel;
window.frames[0].postMessage("msg", channel.port2, "*");
mainPort = channel.port1;
mainPort.start();
mainPort.postMessage("ping");
mainPort.onmessage = test2;
}
function test2()
{
var frameElement = document.getElementsByTagName("iframe")[0];
frameElement.src = "resources/window-opened.html";
frameElement.onload = test3;
gc();
}
function test3()
{
gc();
mainPort.postMessage("alert FAIL: the document is inactive.");
mainPort.postMessage("freeze");
mainPort.postMessage("ping");
mainPort.onmessage = function(evt) {
log("Unexpected response: FAIL");
};
setTimeout(test4, 10);
}
function test4()
{
mainPort.close();
log("Didn't crash, freeze or respond: SUCCESS");
if (window.layoutTestController)
layoutTestController.notifyDone();
}
</script>
<iframe src="resources/message-port-iframe.html" onload="test()" width=0 height=0 frameborder=0></iframe>
</body>
<body>
<p>Test cross-frame MessagePort communication.</p>
<pre id=log></pre>
<script>
function log(message)
{
document.getElementById("log").innerHTML += message + "<br>";
}
if (window.layoutTestController) {
layoutTestController.dumpAsText();
layoutTestController.waitUntilDone();
}
var mainPort;
function test()
{
var channel = new MessageChannel;
window.frames[0].postMessage("msg", channel.port2, "*");
mainPort = channel.port1;
mainPort.postMessage("ping");
mainPort.onmessage = function(evt) {
if (evt.data == "pong" && evt.origin == "")
log("Simple exchange: SUCCESS");
else
log("Simple exchange: FAIL. Got message '" + evt.data + "' from '" + evt.origin + "'.");
};
mainPort.start();
setTimeout(test2, 50);
}
function test2()
{
var channel = new MessageChannel;
mainPort.postMessage("newPort", channel.port2);
channel.port1.onmessage = function(evt) {
if (evt.data == "yo" && evt.origin == "")
log("Posted port: SUCCESS");
else
log("Posted port: FAIL. Got message '" + evt.data + "' from '" + evt.origin + "'.");
};
channel.port1.start();
setTimeout(test3, 50);
}
function test3()
{
var port = mainPort.startConversation("newConversation");
port.onmessage = function(evt) {
if (evt.data == "hey" && evt.origin == "")
log("Conversation: SUCCESS");
else
log("Conversation: FAIL. Got message '" + evt.data + "' from '" + evt.origin + "'.");
};
port.start();
setTimeout(test4, 50);
}
function test4()
{
log("A port is active: " + (mainPort.active ? "SUCCESS" : "FAIL"));
mainPort.close();
log("A closed port is inactive: " + (mainPort.active ? "FAIL" : "SUCCESS"));
if (window.layoutTestController)
layoutTestController.notifyDone();
}
</script>
<iframe src="resources/message-port-iframe.html" onload="test()"></iframe>
</body>
<script>
var port;
function onMessage(evt) {
if (evt.data == "ping")
port.postMessage("pong");
else if (evt.data == "newPort")
evt.messagePort.postMessage("yo");
else if (evt.data == "newConversation")
if (evt.origin == "")
evt.messagePort.postMessage("hey");
else
alert("Incorrect security origin in conversation: '" + evt.origin + "'.");
else if (/alert.+/.test(evt.data))
alert(evt.data.substr(6));
else if (evt.data == "freeze")
while (1) {}
}
window.addEventListener("message", function(evt) {
port = evt.messagePort;
port.onmessage = onMessage;
port.start();
}, false);
</script>
Test that JS code cannot be planted into an unsuspecting subframe via MessagePort and navigation.
PASS
<body>
<p>Test that JS code cannot be planted into an unsuspecting subframe via MessagePort and navigation.</p>
<pre id=log></pre>
<script>
function log(message)
{
document.getElementById("log").innerHTML += message + "<br>";
}
if (window.layoutTestController) {
layoutTestController.dumpAsText();
layoutTestController.waitUntilDone();
}
var closure;
var mainPort;
var failed;
function test()
{
failed = false;
closure = window.frames[0].createClosure();
document.getElementsByTagName("iframe")[0].onload = test2;
window.frames[0].location = "resources/banking-site.html";
}
function test2()
{
mainPort = closure();
mainPort.postMessage("ping");
mainPort.onmessage = function(evt) { if (/sensitive/.test(evt.data)) failed = true; }
mainPort.start();
setTimeout(test3, 100);
}
function test3()
{
log(failed ? "FAIL" : "PASS");
if (window.layoutTestController)
layoutTestController.notifyDone();
}
</script>
<iframe src="resources/attacker-frame.html" onload="test()"></iframe>
</body>
<script>
window.createClosure = function() {
var MessageChannelConstructor = window.MessageChannel;
var channel;
return function() {
channel = new MessageChannelConstructor;
channel.port1.onmessage = function() { channel.port1.postMessage(document.body.innerHTML) }
channel.port1.addEventListener("message", function() { channel.port1.postMessage(document.body.innerHTML) }, false);
channel.port1.start();
return channel.port2;
}
}
</script>
<p>Attacker iframe.</p>
2008-09-24 Alexey Proskuryakov <ap@webkit.org>
Reviewed by Sam Weinig, Anders Carlsson, and (unofficially) Adam Barth.
https://bugs.webkit.org/show_bug.cgi?id=20879
Implement HTML5 channel messaging
Tests: fast/events/message-channel-gc.html
fast/events/message-port-deleted-document.html
fast/events/message-port-deleted-frame.html
fast/events/message-port-inactive-document.html
fast/events/message-port.html
http/tests/security/MessagePort/event-listener-context.html
* Configurations/WebCore.xcconfig:
Removed unused ENABLE_CROSS_DOCUMENT_MESSAGING macro.
* DerivedSources.make:
Added MessageChannel and MessagePort.
* WebCore.pro: Made MessageEvent compilation unconditional, as it could not possibly be
turmed off anyway. Added new files.
* GNUmakefile.am:
* WebCore.pro:
* WebCore.vcproj/WebCore.vcproj:
* WebCore.xcodeproj/project.pbxproj:
* WebCoreSources.bkl:
Added new files.
* bindings/js/JSDOMWindowBase.h:
* bindings/js/JSDOMWindowBase.cpp: (WebCore::JSDOMWindowBase::getValueProperty):
Added suport for window.MessageChannel constructor.
* bindings/js/JSDOMWindowCustom.cpp: (WebCore::JSDOMWindow::postMessage):
* page/DOMWindow.cpp:
(WebCore::DOMWindow::postMessage):
* page/DOMWindow.h:
* page/DOMWindow.idl:
Added support for three-argument postMessage (that posts a MessagePort).
* dom/EventTarget.cpp:
(WebCore::EventTarget::toMessagePort):
* dom/EventTarget.h:
* bindings/js/JSEventTargetBase.cpp: (WebCore::toJS): Added MessagePort as yet another
EventTarget variant.
* bindings/js/JSMessageChannelConstructor.h:
* bindings/js/JSMessageChannelConstructor.cpp: Added a custom constructor, so that it could
take a browsing context (document) parameter.
* bindings/js/JSMessageChannelCustom.cpp: Added.
(WebCore::JSMessageChannel::mark): JSMessageChannel uses a custom mark function to mark
port1 and port2 that it owns.
* bindings/js/JSMessagePortCustom.cpp: Added.
(WebCore::JSMessagePort::startConversation):
(WebCore::JSMessagePort::addEventListener):
(WebCore::JSMessagePort::removeEventListener):
(WebCore::JSMessagePort::dispatchEvent):
(WebCore::JSMessagePort::setOnmessage):
(WebCore::JSMessagePort::onmessage):
(WebCore::JSMessagePort::setOnclose):
(WebCore::JSMessagePort::onclose):
(WebCore::JSMessagePort::mark):
* dom/MessagePort.cpp: Added.
* dom/MessagePort.h: Added.
* dom/MessagePort.idl: Added.
Added a MessagePort implementation. Currently, it is not thread-safe at all, and only works
with Documents as contexts, but in the future, it will be used for communication with worker
threads.
* bindings/objc/DOMInternal.h: Include "DOMMessagePortInternal.h". The new APIs do not
really have Obj-C bindings, as they are far from being final, but a MessagePort stub is
needed for MessageEvent.
* bindings/scripts/CodeGeneratorJS.pm: Include PlatformString.h for MessagePort happiness.
* dom/Document.cpp:
(WebCore::MessagePortTimer::MessagePortTimer):
(WebCore::MessagePortTimer::fired):
(WebCore::Document::processMessagePortMessagesSoon):
(WebCore::Document::~Document):
(WebCore::Document::dispatchMessagePortEvents):
(WebCore::Document::createdMessagePort):
(WebCore::Document::destroyedMessagePort):
* dom/Document.h:
Document keeps track of all MessagePort objects that were created when it was fully active
in its context.
* dom/EventNames.h: Added closeEvent.
* dom/MessageChannel.cpp: Added.
(WebCore::MessageChannel::MessageChannel):
(WebCore::MessageChannel::~MessageChannel):
* dom/MessageChannel.h: Added.
(WebCore::MessageChannel::create):
(WebCore::MessageChannel::port1):
(WebCore::MessageChannel::port2):
* dom/MessageChannel.idl: Added.
Addded JSMessageChannel implementation.
* dom/MessageEvent.cpp:
(WebCore::MessageEvent::MessageEvent):
(WebCore::MessageEvent::initMessageEvent):
* dom/MessageEvent.h:
(WebCore::MessageEvent::create):
(WebCore::MessageEvent::messagePort):
* dom/MessageEvent.idl:
MessageEvent has a MessagePort member now, making it possible to pass ports across
documents.
2008-09-25 David Smith <catfish.man@gmail.com>
Reviewed by Eric Seidel
......
......@@ -15,7 +15,7 @@ PRODUCT_NAME = WebCore;
OTHER_LDFLAGS = -l$(SQLITE3_LIBRARY) -lobjc -sub_library libobjc -umbrella WebKit;
// This needs to be kept sorted, and in sync with FEATURE_DEFINES in JavaScriptCore.xcconfig, WebKit.xcconfig and the default settings of build-webkit.
FEATURE_DEFINES = ENABLE_CROSS_DOCUMENT_MESSAGING ENABLE_DATABASE ENABLE_DOM_STORAGE ENABLE_ICONDATABASE ENABLE_OFFLINE_WEB_APPLICATIONS ENABLE_SVG ENABLE_SVG_ANIMATION ENABLE_SVG_AS_IMAGE ENABLE_SVG_FONTS ENABLE_SVG_FOREIGN_OBJECT ENABLE_SVG_USE ENABLE_VIDEO ENABLE_XPATH ENABLE_XSLT;
FEATURE_DEFINES = ENABLE_DATABASE ENABLE_DOM_STORAGE ENABLE_ICONDATABASE ENABLE_OFFLINE_WEB_APPLICATIONS ENABLE_SVG ENABLE_SVG_ANIMATION ENABLE_SVG_AS_IMAGE ENABLE_SVG_FONTS ENABLE_SVG_FOREIGN_OBJECT ENABLE_SVG_USE ENABLE_VIDEO ENABLE_XPATH ENABLE_XSLT;
SQLITE3_LIBRARY = $(SQLITE3_LIBRARY_$(MAC_OS_X_VERSION_MAJOR));
SQLITE3_LIBRARY_ = WebCoreSQLite3;
......
......@@ -141,7 +141,9 @@
#include "JSLocation.cpp"
#include "JSMediaError.cpp"
#include "JSMediaList.cpp"
#include "JSMessageChannel.cpp"
#include "JSMessageEvent.cpp"
#include "JSMessagePort.cpp"
#include "JSMimeType.cpp"
#include "JSMimeTypeArray.cpp"
#include "JSMouseEvent.cpp"
......