Title: [186955] trunk
Revision
186955
Author
[email protected]
Date
2015-07-17 11:11:40 -0700 (Fri, 17 Jul 2015)

Log Message

Remove leak of objects between isolated worlds on custom events, message events, and pop state events.
https://bugs.webkit.org/show_bug.cgi?id=118884

Reviewed by Filip Pizlo and Mark Lam.
Patch by Keith Miller  <[email protected]>.

Source/WebCore:

Tests: fast/events/event-leak-objects.html
       fast/events/event-properties-gc.html

Fixes an issue where objects passed as certain properties of events could cross isolated worlds. This
was fixed by checking that any object passed by an event must be serializable or originate from the same
isolated world as the one it is currently being accessed in. In the case of MessageEvents and PopStateEvents we
cache the values of the data and state properties, respectively, as they may be a deserialized object. In case
an object was deserialized in a world with elevated privileges we also check the cached value is from the same
world, if it is from a different world we recompute it. For testing purposes, I added a new function to Internals
that determines whether a JSObject originated in the current world.

* CMakeLists.txt:
* WebCore.xcodeproj/project.pbxproj:
* bindings/js/DOMWrapperWorld.h:
(WebCore::worldForDOMObject):
* bindings/js/JSBindingsAllInOne.cpp:
* bindings/js/JSCustomEventCustom.cpp: Copied from Source/WebCore/dom/CustomEvent.cpp.
(WebCore::JSCustomEvent::detail):
* bindings/js/JSMessageEventCustom.cpp:
(WebCore::JSMessageEvent::data):
* bindings/js/JSPopStateEventCustom.cpp:
(WebCore::JSPopStateEvent::state):
* dom/CustomEvent.cpp:
(WebCore::CustomEvent::initCustomEvent):
(WebCore::CustomEvent::trySerializeDetail):
* dom/CustomEvent.h:
* dom/CustomEvent.idl:
* dom/MessageEvent.cpp:
(WebCore::MessageEvent::initMessageEvent):
(WebCore::MessageEvent::trySerializeData):
* dom/MessageEvent.h:
* dom/PopStateEvent.cpp:
(WebCore::PopStateEvent::trySerializeState):
* dom/PopStateEvent.h:
* testing/Internals.cpp:
(WebCore::Internals::isFromCurrentWorld):
* testing/Internals.h:
* testing/Internals.idl:

LayoutTests:

These tests ensure ensure objects are not leaked across isolated worlds and that those properties are not prematurely
garbage collected.

* fast/events/constructors/custom-event-constructor-expected.txt:
* fast/events/constructors/custom-event-constructor.html:
* fast/events/event-leak-objects-expected.txt: Added.
* fast/events/event-leak-objects.html: Added.
* fast/events/event-properties-gc-expected.txt: Added.
* fast/events/event-properties-gc.html: Added.

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (186954 => 186955)


--- trunk/LayoutTests/ChangeLog	2015-07-17 09:23:28 UTC (rev 186954)
+++ trunk/LayoutTests/ChangeLog	2015-07-17 18:11:40 UTC (rev 186955)
@@ -1,3 +1,21 @@
+2015-07-16  Mark Lam  <[email protected]>
+
+        Remove leak of objects between isolated worlds on custom events, message events, and pop state events.
+        https://bugs.webkit.org/show_bug.cgi?id=118884
+
+        Reviewed by Filip Pizlo and Mark Lam.
+        Patch by Keith Miller  <[email protected]>.
+
+        These tests ensure ensure objects are not leaked across isolated worlds and that those properties are not prematurely
+        garbage collected.
+
+        * fast/events/constructors/custom-event-constructor-expected.txt:
+        * fast/events/constructors/custom-event-constructor.html:
+        * fast/events/event-leak-objects-expected.txt: Added.
+        * fast/events/event-leak-objects.html: Added.
+        * fast/events/event-properties-gc-expected.txt: Added.
+        * fast/events/event-properties-gc.html: Added.
+
 2015-07-16  Simon Fraser  <[email protected]>
 
         Fix disappearing position:fixed elements in fixed layout mode

Modified: trunk/LayoutTests/fast/events/constructors/custom-event-constructor-expected.txt (186954 => 186955)


--- trunk/LayoutTests/fast/events/constructors/custom-event-constructor-expected.txt	2015-07-17 09:23:28 UTC (rev 186954)
+++ trunk/LayoutTests/fast/events/constructors/custom-event-constructor-expected.txt	2015-07-17 18:11:40 UTC (rev 186955)
@@ -13,8 +13,11 @@
 PASS new CustomEvent('eventType', { detail: 'string' }).detail is 'string'
 PASS new CustomEvent('eventType', { detail: detailObject }).detail is detailObject
 PASS new CustomEvent('eventType', { detail: document }).detail is document
+PASS new CustomEvent('eventType', { detail: undefined }).detail is undefined
+PASS new CustomEvent('eventType', { detail: null }).detail is null
 PASS new CustomEvent('eventType', { get detail() { return true; } }).detail is true
 PASS new CustomEvent('eventType', { get detail() { throw 'Custom Error'; } }) threw exception Custom Error.
+PASS event.detail is detailObject
 PASS successfullyParsed is true
 
 TEST COMPLETE

Modified: trunk/LayoutTests/fast/events/constructors/custom-event-constructor.html (186954 => 186955)


--- trunk/LayoutTests/fast/events/constructors/custom-event-constructor.html	2015-07-17 09:23:28 UTC (rev 186954)
+++ trunk/LayoutTests/fast/events/constructors/custom-event-constructor.html	2015-07-17 18:11:40 UTC (rev 186955)
@@ -31,11 +31,23 @@
 // Detail is a DOM object
 shouldBe("new CustomEvent('eventType', { detail: document }).detail", "document");
 
+// Detail is undefined.
+shouldBe("new CustomEvent('eventType', { detail: undefined }).detail", "undefined");
+
+// Detail is null.
+shouldBe("new CustomEvent('eventType', { detail: null }).detail", "null");
+
 // Detail is a getter.
 shouldBe("new CustomEvent('eventType', { get detail() { return true; } }).detail", "true");
 
 // Detail throws an exeception.
 shouldThrow("new CustomEvent('eventType', { get detail() { throw 'Custom Error'; } })");
+
+// try initCustomEvent
+var event = document.createEvent('CustomEvent');
+event.initCustomEvent('eventType', true, false, detailObject);
+shouldBe("event.detail", "detailObject");
+
 </script>
 <script src=""
 </body>

Modified: trunk/LayoutTests/fast/events/event-leak-objects-expected.txt (186954 => 186955)


--- trunk/LayoutTests/fast/events/event-leak-objects-expected.txt	2015-07-17 09:23:28 UTC (rev 186954)
+++ trunk/LayoutTests/fast/events/event-leak-objects-expected.txt	2015-07-17 18:11:40 UTC (rev 186955)
@@ -0,0 +1,58 @@
+PASS window.internals.isFromCurrentWorld(event) is true
+PASS checkAllPropertiesFromCurrentWorld(resultValue) is true
+PASS Object.pageDefinedVar is undefined.
+PASS window.internals.isFromCurrentWorld(event) is true
+PASS checkAllPropertiesFromCurrentWorld(resultValue) is true
+PASS Object.pageDefinedVar is undefined.
+PASS window.internals.isFromCurrentWorld(event) is true
+PASS checkAllPropertiesFromCurrentWorld(resultValue) is true
+PASS Object.pageDefinedVar is undefined.
+PASS window.internals.isFromCurrentWorld(event) is true
+PASS checkAllPropertiesFromCurrentWorld(resultValue) is true
+PASS Object.pageDefinedVar is undefined.
+PASS window.internals.isFromCurrentWorld(event) is true
+PASS checkAllPropertiesFromCurrentWorld(resultValue) is true
+PASS Object.pageDefinedVar is undefined.
+PASS window.internals.isFromCurrentWorld(event) is true
+PASS checkAllPropertiesFromCurrentWorld(resultValue) is true
+PASS Object.pageDefinedVar is undefined.
+PASS window.internals.isFromCurrentWorld(event) is true
+PASS checkAllPropertiesFromCurrentWorld(resultValue) is true
+PASS Object.pageDefinedVar is undefined.
+PASS window.internals.isFromCurrentWorld(event) is true
+PASS checkAllPropertiesFromCurrentWorld(resultValue) is true
+PASS Object.pageDefinedVar is undefined.
+PASS window.internals.isFromCurrentWorld(event) is true
+PASS checkAllPropertiesFromCurrentWorld(resultValue) is true
+PASS Object.pageDefinedVar is undefined.
+PASS window.internals.isFromCurrentWorld(event) is true
+PASS checkAllPropertiesFromCurrentWorld(resultValue) is true
+PASS Object.pageDefinedVar is undefined.
+PASS window.internals.isFromCurrentWorld(event) is true
+PASS checkAllPropertiesFromCurrentWorld(resultValue) is true
+PASS Object.pageDefinedVar is undefined.
+PASS window.internals.isFromCurrentWorld(event) is true
+PASS checkAllPropertiesFromCurrentWorld(resultValue) is true
+PASS Object.pageDefinedVar is undefined.
+PASS window.internals.isFromCurrentWorld(event) is true
+PASS checkAllPropertiesFromCurrentWorld(resultValue) is true
+PASS Object.pageDefinedVar is undefined.
+PASS window.internals.isFromCurrentWorld(event) is true
+PASS checkAllPropertiesFromCurrentWorld(resultValue) is true
+PASS Object.pageDefinedVar is undefined.
+PASS window.internals.isFromCurrentWorld(event) is true
+PASS checkAllPropertiesFromCurrentWorld(resultValue) is true
+PASS Object.pageDefinedVar is undefined.
+PASS window.internals.isFromCurrentWorld(event) is true
+PASS checkAllPropertiesFromCurrentWorld(resultValue) is true
+PASS Object.pageDefinedVar is undefined.
+PASS window.internals.isFromCurrentWorld(event) is true
+PASS checkAllPropertiesFromCurrentWorld(resultValue) is true
+PASS Object.pageDefinedVar is undefined.
+PASS window.internals.isFromCurrentWorld(event) is true
+PASS checkAllPropertiesFromCurrentWorld(resultValue) is true
+PASS Object.pageDefinedVar is undefined.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Modified: trunk/LayoutTests/fast/events/event-leak-objects.html (186954 => 186955)


--- trunk/LayoutTests/fast/events/event-leak-objects.html	2015-07-17 09:23:28 UTC (rev 186954)
+++ trunk/LayoutTests/fast/events/event-leak-objects.html	2015-07-17 18:11:40 UTC (rev 186955)
@@ -0,0 +1,122 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script>
+window.jsTestIsAsync = true;
+
+// The events that we want to test, with the properties that each one uses.
+var events = [
+    { eventKind: "CustomEvent", propName: "detail" },
+    { eventKind: "MessageEvent", propName: "data"  },
+    { eventKind: "PopStateEvent", propName: "state" }
+];
+
+// Types we can put in the property of the Event
+var values = [
+    { propValue: "document" },
+    { propValue: '{"bar":1}' },
+    { propValue: "5" },
+    { propValue: '"hello"' },
+    { propValue: "true" },
+    { propValue: "[1,2,3]" }
+];
+
+function merge(obj1, obj2) {
+    var newObj = { };
+    for (var attrname in obj1) { newObj[attrname] = obj1[attrname]; }
+    for (var attrname in obj2) { newObj[attrname] = obj2[attrname]; }
+    return newObj;
+}
+
+// Format the tests so they look like [ { eventKind: ..., propValue: ... }, ... { eventKind: ..., propValue: ... } ]
+var tests = events.map(function(event) {
+    return values.map(function(value) {
+        return merge(event, value);
+    })
+});
+tests = tests.reduce(function(a, b) {
+    return a.concat(b);
+});
+
+// We need to scan all the properies of value to ensure they all came from the current world.
+// Assumes window.internals exists.
+function checkAllPropertiesFromCurrentWorld(value) {
+    var allFromCurrentWorld = true;
+    while (allFromCurrentWorld && value && typeof value === "object") {
+        allFromCurrentWorld = allFromCurrentWorld && window.internals.isFromCurrentWorld(value);
+        for (var prop in value)
+            allFromCurrentWorld = allFromCurrentWorld && checkAllPropertiesFromCurrentWorld(value[prop], seenValues);
+        value = Object.getPrototypeOf(value);
+    }
+
+    return allFromCurrentWorld;
+}
+
+function addListener(eventKind, eventString, prop) {
+    document.addEventListener(eventString, function(event) {
+        eventValue = event
+        resultValue = event[prop]
+
+        if (window.internals) {
+            shouldBeTrue("window.internals.isFromCurrentWorld(event)");
+            shouldBeTrue("checkAllPropertiesFromCurrentWorld(resultValue)");
+        }
+
+        // The property defined in the isolated world should be undefined.
+        shouldBeUndefined("Object.pageDefinedVar");
+        window.postMessage("done", "*");
+    });
+}
+
+function sendDocumentEvent(eventKind, eventString, prop, value) {
+    var constructor = eval(eventKind);
+    var initializer = { };
+    initializer[prop] = value;
+    var newEvent = new constructor(eventString, initializer);
+    // Try to access the property in a different world to make sure caching issues do not occur
+    newEvent[prop];
+    document.dispatchEvent(newEvent);
+}
+
+function runScript(eventKind, propName, propValue, number) {
+    // Final string should have the form:
+    //     document.pageDefinedVar = 1; (function sendDocumentObject(eventKind, propName, result) {...})(...);
+    // When evaluated in the isolated world, should initiate the event with the
+    // document object as the specificed property value.
+    var eventString = eventKind + number;
+
+    var script = "Object.pageDefinedVar = 1; "
+        + "(" + sendDocumentEvent.toString() + ")('"
+        + eventKind + "', '" + eventString + "', '" + propName + "', " + propValue + ");";
+    addListener(eventKind, eventString);
+    testRunner.evaluateScriptInIsolatedWorld(0, script);
+}
+
+// Run the tests whenever a notification arrives, which indicates that the
+// previous test has finished.
+window.addEventListener("message", function(message) {
+    runNextTest();
+}, false);
+
+// Keep a count to make a unique string
+var count = 1;
+function runNextTest () {
+    var test = tests.pop();
+    if (!test) {
+        finishJSTest();
+        return;
+    }
+    runScript(test.eventKind, test.propName, test.propValue, count++);
+};
+
+// This test is meaningless without testRunner.
+if (window.testRunner) {
+    runNextTest();
+}
+</script>
+</body>
+<script src=""
+</html>

Modified: trunk/LayoutTests/fast/events/event-properties-gc-expected.txt (186954 => 186955)


--- trunk/LayoutTests/fast/events/event-properties-gc-expected.txt	2015-07-17 09:23:28 UTC (rev 186954)
+++ trunk/LayoutTests/fast/events/event-properties-gc-expected.txt	2015-07-17 18:11:40 UTC (rev 186955)
@@ -0,0 +1,7 @@
+PASS event[prop] is "foo"
+PASS event[prop] is "foo"
+PASS event[prop] is "foo"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Modified: trunk/LayoutTests/fast/events/event-properties-gc.html (186954 => 186955)


--- trunk/LayoutTests/fast/events/event-properties-gc.html	2015-07-17 09:23:28 UTC (rev 186954)
+++ trunk/LayoutTests/fast/events/event-properties-gc.html	2015-07-17 18:11:40 UTC (rev 186955)
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script>
+window.jsTestIsAsync = true;
+
+function addListener(eventType, prop) {
+    document.addEventListener(eventType, function(event) {
+        window.prop = prop;
+        // Despite the earlier assignement of the local variable to null and
+        // the following garabage collection, the property should still be
+        // present here.
+        shouldBeEqualToString("event[prop]", "foo");
+        window.prop = undefined;
+        window.postMessage("done", "*");
+    });
+}
+
+// Run the tests whenever a notification arrives, which indicates that the
+// previous test has finished.
+window.addEventListener("message", function(message) {
+    runNextTest();
+}, false);
+
+function newEvent(eventType, prop, value) {
+   return eval("new " + eventType + "('" + eventType + "', { " + prop + ": value })");
+}
+
+// The events that we want to test, with the properties that each one uses.
+var events = [
+    { eventType: "CustomEvent", prop: "detail" },
+    { eventType: "MessageEvent", prop: "data" },
+    { eventType: "PopStateEvent", prop: "state" }
+];
+
+function runNextTest () {
+    var evt = events.pop();
+    if (!evt) {
+        finishJSTest();
+        return;
+    }
+    var value = "foo";
+    var eventToDispatch = newEvent(evt.eventType, evt.prop, value);
+    value = null;
+    gc();
+    addListener(evt.eventType, evt.prop);
+    document.dispatchEvent(eventToDispatch);
+};
+
+// This test is meaningless without testRunner.
+if (window.testRunner) {
+    runNextTest();
+}
+</script>
+</body>
+<script src=""
+</html>

Modified: trunk/Source/WebCore/CMakeLists.txt (186954 => 186955)


--- trunk/Source/WebCore/CMakeLists.txt	2015-07-17 09:23:28 UTC (rev 186954)
+++ trunk/Source/WebCore/CMakeLists.txt	2015-07-17 18:11:40 UTC (rev 186955)
@@ -1097,6 +1097,7 @@
     bindings/js/JSCryptoKeyPairCustom.cpp
     bindings/js/JSCryptoKeySerializationJWK.cpp
     bindings/js/JSCryptoOperationData.cpp
+    bindings/js/JSCustomEventCustom.cpp
     bindings/js/JSCustomSQLStatementErrorCallback.cpp
     bindings/js/JSCustomXPathNSResolver.cpp
     bindings/js/JSDOMBinding.cpp

Modified: trunk/Source/WebCore/ChangeLog (186954 => 186955)


--- trunk/Source/WebCore/ChangeLog	2015-07-17 09:23:28 UTC (rev 186954)
+++ trunk/Source/WebCore/ChangeLog	2015-07-17 18:11:40 UTC (rev 186955)
@@ -1,3 +1,50 @@
+2015-07-17  Mark Lam  <[email protected]>
+
+        Remove leak of objects between isolated worlds on custom events, message events, and pop state events.
+        https://bugs.webkit.org/show_bug.cgi?id=118884
+
+        Reviewed by Filip Pizlo and Mark Lam.
+        Patch by Keith Miller  <[email protected]>.
+
+        Tests: fast/events/event-leak-objects.html
+               fast/events/event-properties-gc.html
+
+        Fixes an issue where objects passed as certain properties of events could cross isolated worlds. This
+        was fixed by checking that any object passed by an event must be serializable or originate from the same
+        isolated world as the one it is currently being accessed in. In the case of MessageEvents and PopStateEvents we
+        cache the values of the data and state properties, respectively, as they may be a deserialized object. In case
+        an object was deserialized in a world with elevated privileges we also check the cached value is from the same
+        world, if it is from a different world we recompute it. For testing purposes, I added a new function to Internals
+        that determines whether a JSObject originated in the current world.
+
+        * CMakeLists.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * bindings/js/DOMWrapperWorld.h:
+        (WebCore::worldForDOMObject):
+        * bindings/js/JSBindingsAllInOne.cpp:
+        * bindings/js/JSCustomEventCustom.cpp: Copied from Source/WebCore/dom/CustomEvent.cpp.
+        (WebCore::JSCustomEvent::detail):
+        * bindings/js/JSMessageEventCustom.cpp:
+        (WebCore::JSMessageEvent::data):
+        * bindings/js/JSPopStateEventCustom.cpp:
+        (WebCore::JSPopStateEvent::state):
+        * dom/CustomEvent.cpp:
+        (WebCore::CustomEvent::initCustomEvent):
+        (WebCore::CustomEvent::trySerializeDetail):
+        * dom/CustomEvent.h:
+        * dom/CustomEvent.idl:
+        * dom/MessageEvent.cpp:
+        (WebCore::MessageEvent::initMessageEvent):
+        (WebCore::MessageEvent::trySerializeData):
+        * dom/MessageEvent.h:
+        * dom/PopStateEvent.cpp:
+        (WebCore::PopStateEvent::trySerializeState):
+        * dom/PopStateEvent.h:
+        * testing/Internals.cpp:
+        (WebCore::Internals::isFromCurrentWorld):
+        * testing/Internals.h:
+        * testing/Internals.idl:
+
 2015-07-17  Carlos Garcia Campos  <[email protected]>
 
         [GTK] Cleanup PasteboardHelper

Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (186954 => 186955)


--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2015-07-17 09:23:28 UTC (rev 186954)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2015-07-17 18:11:40 UTC (rev 186955)
@@ -6042,6 +6042,7 @@
 		DEBCCDD216646E8200A452E1 /* RenderMediaControlElements.h in Headers */ = {isa = PBXBuildFile; fileRef = DE49B308165F2FE10010338D /* RenderMediaControlElements.h */; };
 		DEBCCDD416646EAF00A452E1 /* MediaControlElementTypes.h in Headers */ = {isa = PBXBuildFile; fileRef = DE49B300165F2FC60010338D /* MediaControlElementTypes.h */; };
 		DEBCCDD516646EB200A452E1 /* MediaControlElementTypes.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DE49B2FF165F2FC60010338D /* MediaControlElementTypes.cpp */; };
+		DEC297611B4F2F8D005F5945 /* JSCustomEventCustom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = DEC2975D1B4DEB2A005F5945 /* JSCustomEventCustom.cpp */; };
 		DF9AFD7213FC31D80015FEB7 /* MediaPlayerPrivateAVFoundationObjC.h in Headers */ = {isa = PBXBuildFile; fileRef = DF9AFD7013FC31D80015FEB7 /* MediaPlayerPrivateAVFoundationObjC.h */; };
 		DF9AFD7313FC31D80015FEB7 /* MediaPlayerPrivateAVFoundationObjC.mm in Sources */ = {isa = PBXBuildFile; fileRef = DF9AFD7113FC31D80015FEB7 /* MediaPlayerPrivateAVFoundationObjC.mm */; };
 		E0FEF372B17C53EAC1C1FBEE /* EventSource.h in Headers */ = {isa = PBXBuildFile; fileRef = E0FEF371B17C53EAC1C1FBEE /* EventSource.h */; };
@@ -13715,6 +13716,7 @@
 		DE49B300165F2FC60010338D /* MediaControlElementTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaControlElementTypes.h; sourceTree = "<group>"; };
 		DE49B307165F2FE10010338D /* RenderMediaControlElements.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RenderMediaControlElements.cpp; sourceTree = "<group>"; };
 		DE49B308165F2FE10010338D /* RenderMediaControlElements.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RenderMediaControlElements.h; sourceTree = "<group>"; };
+		DEC2975D1B4DEB2A005F5945 /* JSCustomEventCustom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSCustomEventCustom.cpp; sourceTree = "<group>"; };
 		DF9AFD7013FC31D80015FEB7 /* MediaPlayerPrivateAVFoundationObjC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaPlayerPrivateAVFoundationObjC.h; sourceTree = "<group>"; };
 		DF9AFD7113FC31D80015FEB7 /* MediaPlayerPrivateAVFoundationObjC.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MediaPlayerPrivateAVFoundationObjC.mm; sourceTree = "<group>"; };
 		E0FEF371B07C53EAC1C1FBEE /* EventSource.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = EventSource.idl; sourceTree = "<group>"; };
@@ -21414,6 +21416,7 @@
 		BC4EDEF70C08F414007EDD49 /* Custom */ = {
 			isa = PBXGroup;
 			children = (
+				DEC2975D1B4DEB2A005F5945 /* JSCustomEventCustom.cpp */,
 				BC2ED6BB0C6BD2F000920BFF /* JSAttrCustom.cpp */,
 				FDEAAAEF12B02EE400DCF33B /* JSAudioBufferSourceNodeCustom.cpp */,
 				FDEAAAF012B02EE400DCF33B /* JSAudioContextCustom.cpp */,
@@ -30400,6 +30403,7 @@
 				B2227AD90D00BF220071B782 /* SVGTransformable.cpp in Sources */,
 				B2227ADC0D00BF220071B782 /* SVGTransformDistance.cpp in Sources */,
 				B2227ADE0D00BF220071B782 /* SVGTransformList.cpp in Sources */,
+				DEC297611B4F2F8D005F5945 /* JSCustomEventCustom.cpp in Sources */,
 				B2227AE10D00BF220071B782 /* SVGTRefElement.cpp in Sources */,
 				B2227AE40D00BF220071B782 /* SVGTSpanElement.cpp in Sources */,
 				B2227AE90D00BF220071B782 /* SVGURIReference.cpp in Sources */,

Modified: trunk/Source/WebCore/bindings/js/DOMWrapperWorld.h (186954 => 186955)


--- trunk/Source/WebCore/bindings/js/DOMWrapperWorld.h	2015-07-17 09:23:28 UTC (rev 186954)
+++ trunk/Source/WebCore/bindings/js/DOMWrapperWorld.h	2015-07-17 18:11:40 UTC (rev 186955)
@@ -74,6 +74,11 @@
     return JSC::jsCast<JSDOMGlobalObject*>(exec->lexicalGlobalObject())->world();
 }
 
+inline DOMWrapperWorld& worldForDOMObject(JSC::JSObject* object)
+{
+    return JSC::jsCast<JSDOMGlobalObject*>(object->globalObject())->world();
+}
+    
 } // namespace WebCore
 
 #endif // DOMWrapperWorld_h

Modified: trunk/Source/WebCore/bindings/js/JSBindingsAllInOne.cpp (186954 => 186955)


--- trunk/Source/WebCore/bindings/js/JSBindingsAllInOne.cpp	2015-07-17 09:23:28 UTC (rev 186954)
+++ trunk/Source/WebCore/bindings/js/JSBindingsAllInOne.cpp	2015-07-17 18:11:40 UTC (rev 186955)
@@ -45,6 +45,7 @@
 #include "JSCharacterDataCustom.cpp"
 #include "JSCommandLineAPIHostCustom.cpp"
 #include "JSCryptoCustom.cpp"
+#include "JSCustomEventCustom.cpp"
 #include "JSCustomSQLStatementErrorCallback.cpp"
 #include "JSCustomXPathNSResolver.cpp"
 #include "JSDOMBinding.cpp"

Modified: trunk/Source/WebCore/bindings/js/JSCustomEventCustom.cpp (186954 => 186955)


--- trunk/Source/WebCore/bindings/js/JSCustomEventCustom.cpp	2015-07-17 09:23:28 UTC (rev 186954)
+++ trunk/Source/WebCore/bindings/js/JSCustomEventCustom.cpp	2015-07-17 18:11:40 UTC (rev 186955)
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "JSCustomEvent.h"
+
+#include "CustomEvent.h"
+#include "DOMWrapperWorld.h"
+#include <runtime/JSCInlines.h>
+#include <runtime/JSCJSValue.h>
+#include <runtime/Structure.h>
+
+using namespace JSC;
+
+namespace WebCore {
+    
+JSValue JSCustomEvent::detail(ExecState* exec) const
+{
+    CustomEvent& event = impl();
+    
+    if (event.detail().hasNoValue())
+        return jsNull();
+
+    JSValue detail = event.detail().jsValue();
+    
+    if (detail.isObject() && &worldForDOMObject(detail.getObject()) != &currentWorld(exec)) {
+        // We need to make sure CustomEvents do not leak their detail property across isolated DOM worlds.
+        // Ideally, we would check that the worlds have different privileges but that's not possible yet.
+        RefPtr<SerializedScriptValue> serializedDetail = event.trySerializeDetail(exec);
+        
+        if (!serializedDetail)
+            return jsNull();
+        
+        return serializedDetail->deserialize(exec, globalObject(), nullptr);
+    }
+    
+    return detail;
+}
+
+} // namespace WebCore
+

Modified: trunk/Source/WebCore/bindings/js/JSMessageEventCustom.cpp (186954 => 186955)


--- trunk/Source/WebCore/bindings/js/JSMessageEventCustom.cpp	2015-07-17 09:23:28 UTC (rev 186954)
+++ trunk/Source/WebCore/bindings/js/JSMessageEventCustom.cpp	2015-07-17 18:11:40 UTC (rev 186955)
@@ -46,8 +46,12 @@
 
 JSValue JSMessageEvent::data(ExecState* exec) const
 {
-    if (JSValue cachedValue = m_data.get())
-        return cachedValue;
+    if (JSValue cachedValue = m_data.get()) {
+        // We cannot use a cached object if we are in a different world than the one it was created in.
+        if (!cachedValue.isObject() || &worldForDOMObject(cachedValue.getObject()) == &currentWorld(exec))
+            return cachedValue;
+        ASSERT_NOT_REACHED();
+    }
 
     MessageEvent& event = impl();
     JSValue result;
@@ -56,8 +60,19 @@
         Deprecated::ScriptValue scriptValue = event.dataAsScriptValue();
         if (scriptValue.hasNoValue())
             result = jsNull();
-        else
-            result = scriptValue.jsValue();
+        else {
+            JSValue dataValue = scriptValue.jsValue();
+            // We need to make sure MessageEvents do not leak objects in their state property across isolated DOM worlds.
+            // Ideally, we would check that the worlds have different privileges but that's not possible yet.
+            if (dataValue.isObject() && &worldForDOMObject(dataValue.getObject()) != &currentWorld(exec)) {
+                RefPtr<SerializedScriptValue> serializedValue = event.trySerializeData(exec);
+                if (serializedValue)
+                    result = serializedValue->deserialize(exec, globalObject(), nullptr);
+                else
+                    result = jsNull();
+            } else
+                result = dataValue;
+        }
         break;
     }
 

Modified: trunk/Source/WebCore/bindings/js/JSPopStateEventCustom.cpp (186954 => 186955)


--- trunk/Source/WebCore/bindings/js/JSPopStateEventCustom.cpp	2015-07-17 09:23:28 UTC (rev 186954)
+++ trunk/Source/WebCore/bindings/js/JSPopStateEventCustom.cpp	2015-07-17 18:11:40 UTC (rev 186955)
@@ -49,14 +49,29 @@
 JSValue JSPopStateEvent::state(ExecState* exec) const
 {
     JSValue cachedValue = m_state.get();
-    if (!cachedValue.isEmpty())
-        return cachedValue;
+    if (!cachedValue.isEmpty()) {
+        // We cannot use a cached object if we are in a different world than the one it was created in.
+        if (!cachedValue.isObject() || &worldForDOMObject(cachedValue.getObject()) == &currentWorld(exec))
+            return cachedValue;
+        ASSERT_NOT_REACHED();
+    }
 
     PopStateEvent& event = impl();
 
-    if (!event.state().hasNoValue())
-        return cacheState(exec, const_cast<JSPopStateEvent*>(this), event.state().jsValue());
-
+    if (!event.state().hasNoValue()) {
+        // We need to make sure a PopStateEvent does not leak objects in its state property across isolated DOM worlds.
+        // Ideally, we would check that the worlds have different privileges but that's not possible yet.
+        JSValue state = event.state().jsValue();
+        if (state.isObject() && &worldForDOMObject(state.getObject()) != &currentWorld(exec)) {
+            if (RefPtr<SerializedScriptValue> serializedValue = event.trySerializeState(exec))
+                state = serializedValue->deserialize(exec, globalObject(), nullptr);
+            else
+                state = jsNull();
+        }
+        
+        return cacheState(exec, const_cast<JSPopStateEvent*>(this), state);
+    }
+    
     History* history = event.history();
     if (!history || !event.serializedState())
         return cacheState(exec, const_cast<JSPopStateEvent*>(this), jsNull());

Modified: trunk/Source/WebCore/dom/CustomEvent.cpp (186954 => 186955)


--- trunk/Source/WebCore/dom/CustomEvent.cpp	2015-07-17 09:23:28 UTC (rev 186954)
+++ trunk/Source/WebCore/dom/CustomEvent.cpp	2015-07-17 18:11:40 UTC (rev 186955)
@@ -51,15 +51,26 @@
 
 void CustomEvent::initCustomEvent(const AtomicString& type, bool canBubble, bool cancelable, const Deprecated::ScriptValue& detail)
 {
-    ASSERT(!m_serializedScriptValue.get());
     if (dispatched())
         return;
 
     initEvent(type, canBubble, cancelable);
 
     m_detail = detail;
+    m_serializedDetail = nullptr;
+    m_triedToSerialize = false;
 }
 
+RefPtr<SerializedScriptValue> CustomEvent::trySerializeDetail(JSC::ExecState* exec)
+{
+    if (!m_serializedDetail && !m_triedToSerialize) {
+        m_serializedDetail = SerializedScriptValue::create(exec, m_detail.jsValue(), nullptr, nullptr, NonThrowing);
+        m_triedToSerialize = true;
+    }
+    
+    return m_serializedDetail;
+}
+
 EventInterface CustomEvent::eventInterface() const
 {
     return CustomEventInterfaceType;

Modified: trunk/Source/WebCore/dom/CustomEvent.h (186954 => 186955)


--- trunk/Source/WebCore/dom/CustomEvent.h	2015-07-17 09:23:28 UTC (rev 186954)
+++ trunk/Source/WebCore/dom/CustomEvent.h	2015-07-17 18:11:40 UTC (rev 186955)
@@ -57,14 +57,16 @@
     virtual EventInterface eventInterface() const override;
 
     const Deprecated::ScriptValue& detail() const { return m_detail; }
-    PassRefPtr<SerializedScriptValue> serializedScriptValue() { return m_serializedScriptValue; }
+    
+    RefPtr<SerializedScriptValue> trySerializeDetail(JSC::ExecState*);
 
 private:
     CustomEvent();
     CustomEvent(const AtomicString& type, const CustomEventInit& initializer);
 
     Deprecated::ScriptValue m_detail;
-    RefPtr<SerializedScriptValue> m_serializedScriptValue;
+    RefPtr<SerializedScriptValue> m_serializedDetail;
+    bool m_triedToSerialize { false };
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/dom/CustomEvent.idl (186954 => 186955)


--- trunk/Source/WebCore/dom/CustomEvent.idl	2015-07-17 09:23:28 UTC (rev 186954)
+++ trunk/Source/WebCore/dom/CustomEvent.idl	2015-07-17 18:11:40 UTC (rev 186955)
@@ -27,11 +27,11 @@
 [
     ConstructorTemplate=Event,
 ] interface CustomEvent : Event {
-    [InitializedByEventConstructor] readonly attribute any detail;
+    [InitializedByEventConstructor, CustomGetter] readonly attribute any detail;
 
-    void initCustomEvent([Default=Undefined] optional DOMString typeArg, 
-                         [Default=Undefined] optional boolean canBubbleArg, 
-                         [Default=Undefined] optional boolean cancelableArg, 
+    void initCustomEvent([Default=Undefined] optional DOMString typeArg,
+                         [Default=Undefined] optional boolean canBubbleArg,
+                         [Default=Undefined] optional boolean cancelableArg,
                          [Default=Undefined] optional any detailArg);
 };
 

Modified: trunk/Source/WebCore/dom/MessageEvent.cpp (186954 => 186955)


--- trunk/Source/WebCore/dom/MessageEvent.cpp	2015-07-17 09:23:28 UTC (rev 186954)
+++ trunk/Source/WebCore/dom/MessageEvent.cpp	2015-07-17 18:11:40 UTC (rev 186955)
@@ -118,6 +118,8 @@
 
     m_dataType = DataTypeScriptValue;
     m_dataAsScriptValue = data;
+    m_dataAsSerializedScriptValue = nullptr;
+    m_triedToSerialize = false;
     m_origin = origin;
     m_lastEventId = lastEventId;
     m_source = source;
@@ -138,6 +140,18 @@
     m_source = source;
     m_ports = WTF::move(ports);
 }
+    
+RefPtr<SerializedScriptValue> MessageEvent::trySerializeData(JSC::ExecState* exec)
+{
+    ASSERT(!m_dataAsScriptValue.hasNoValue());
+    
+    if (!m_dataAsSerializedScriptValue && !m_triedToSerialize) {
+        m_dataAsSerializedScriptValue = SerializedScriptValue::create(exec, m_dataAsScriptValue.jsValue(), nullptr, nullptr, NonThrowing);
+        m_triedToSerialize = true;
+    }
+    
+    return m_dataAsSerializedScriptValue;
+}
 
 // FIXME: Remove this when we have custom ObjC binding support.
 SerializedScriptValue* MessageEvent::data() const

Modified: trunk/Source/WebCore/dom/MessageEvent.h (186954 => 186955)


--- trunk/Source/WebCore/dom/MessageEvent.h	2015-07-17 09:23:28 UTC (rev 186954)
+++ trunk/Source/WebCore/dom/MessageEvent.h	2015-07-17 18:11:40 UTC (rev 186955)
@@ -114,6 +114,8 @@
     Blob* dataAsBlob() const { ASSERT(m_dataType == DataTypeBlob); return m_dataAsBlob.get(); }
     ArrayBuffer* dataAsArrayBuffer() const { ASSERT(m_dataType == DataTypeArrayBuffer); return m_dataAsArrayBuffer.get(); }
 
+    RefPtr<SerializedScriptValue> trySerializeData(JSC::ExecState*);
+    
 private:
     MessageEvent();
     MessageEvent(const AtomicString&, const MessageEventInit&);
@@ -127,6 +129,7 @@
     DataType m_dataType;
     Deprecated::ScriptValue m_dataAsScriptValue;
     RefPtr<SerializedScriptValue> m_dataAsSerializedScriptValue;
+    bool m_triedToSerialize { false };
     String m_dataAsString;
     RefPtr<Blob> m_dataAsBlob;
     RefPtr<ArrayBuffer> m_dataAsArrayBuffer;

Modified: trunk/Source/WebCore/dom/PopStateEvent.cpp (186954 => 186955)


--- trunk/Source/WebCore/dom/PopStateEvent.cpp	2015-07-17 09:23:28 UTC (rev 186954)
+++ trunk/Source/WebCore/dom/PopStateEvent.cpp	2015-07-17 18:11:40 UTC (rev 186955)
@@ -29,7 +29,6 @@
 
 #include "EventNames.h"
 #include "History.h"
-#include "SerializedScriptValue.h"
 #include <runtime/JSCInlines.h>
 
 namespace WebCore {
@@ -79,6 +78,18 @@
     return adoptRef(*new PopStateEvent(type, initializer));
 }
 
+RefPtr<SerializedScriptValue> PopStateEvent::trySerializeState(JSC::ExecState* exec)
+{
+    ASSERT(!m_state.hasNoValue());
+    
+    if (!m_serializedState && !m_triedToSerialize) {
+        m_serializedState = SerializedScriptValue::create(exec, m_state.jsValue(), nullptr, nullptr, NonThrowing);
+        m_triedToSerialize = true;
+    }
+    
+    return m_serializedState;
+}
+
 EventInterface PopStateEvent::eventInterface() const
 {
     return PopStateEventInterfaceType;

Modified: trunk/Source/WebCore/dom/PopStateEvent.h (186954 => 186955)


--- trunk/Source/WebCore/dom/PopStateEvent.h	2015-07-17 09:23:28 UTC (rev 186954)
+++ trunk/Source/WebCore/dom/PopStateEvent.h	2015-07-17 18:11:40 UTC (rev 186955)
@@ -28,6 +28,7 @@
 #define PopStateEvent_h
 
 #include "Event.h"
+#include "SerializedScriptValue.h"
 #include <bindings/ScriptValue.h>
 
 namespace WebCore {
@@ -48,7 +49,10 @@
     static Ref<PopStateEvent> create(PassRefPtr<SerializedScriptValue>, PassRefPtr<History>);
     static Ref<PopStateEvent> create(const AtomicString&, const PopStateEventInit&);
 
-    PassRefPtr<SerializedScriptValue> serializedState() const { return m_serializedState; }
+    PassRefPtr<SerializedScriptValue> serializedState() const { ASSERT(m_serializedState); return m_serializedState; }
+    
+    RefPtr<SerializedScriptValue> trySerializeState(JSC::ExecState*);
+    
     const Deprecated::ScriptValue& state() const { return m_state; }
     History* history() const { return m_history.get(); }
 
@@ -61,6 +65,7 @@
 
     Deprecated::ScriptValue m_state;
     RefPtr<SerializedScriptValue> m_serializedState;
+    bool m_triedToSerialize { false };
     RefPtr<History> m_history;
 };
 

Modified: trunk/Source/WebCore/testing/Internals.cpp (186954 => 186955)


--- trunk/Source/WebCore/testing/Internals.cpp	2015-07-17 09:23:28 UTC (rev 186954)
+++ trunk/Source/WebCore/testing/Internals.cpp	2015-07-17 18:11:40 UTC (rev 186955)
@@ -2357,6 +2357,16 @@
     return SerializedScriptValue::adopt(bytes);
 }
 
+bool Internals::isFromCurrentWorld(Deprecated::ScriptValue value) const
+{
+    ASSERT(!value.hasNoValue());
+    
+    JSC::ExecState* exec = contextDocument()->vm().topCallFrame;
+    if (!value.isObject() || &worldForDOMObject(value.jsValue().getObject()) == &currentWorld(exec))
+        return true;
+    return false;
+}
+
 void Internals::setUsesOverlayScrollbars(bool enabled)
 {
     WebCore::Settings::setUsesOverlayScrollbars(enabled);

Modified: trunk/Source/WebCore/testing/Internals.h (186954 => 186955)


--- trunk/Source/WebCore/testing/Internals.h	2015-07-17 09:23:28 UTC (rev 186954)
+++ trunk/Source/WebCore/testing/Internals.h	2015-07-17 18:11:40 UTC (rev 186955)
@@ -324,6 +324,8 @@
     PassRefPtr<ArrayBuffer> serializeObject(PassRefPtr<SerializedScriptValue>) const;
     PassRefPtr<SerializedScriptValue> deserializeBuffer(PassRefPtr<ArrayBuffer>) const;
 
+    bool isFromCurrentWorld(Deprecated::ScriptValue) const;
+
     void setUsesOverlayScrollbars(bool enabled);
 
     String getCurrentCursorInfo(ExceptionCode&);

Modified: trunk/Source/WebCore/testing/Internals.idl (186954 => 186955)


--- trunk/Source/WebCore/testing/Internals.idl	2015-07-17 09:23:28 UTC (rev 186954)
+++ trunk/Source/WebCore/testing/Internals.idl	2015-07-17 18:11:40 UTC (rev 186955)
@@ -311,6 +311,8 @@
     SerializedScriptValue deserializeBuffer(ArrayBuffer buffer);
     ArrayBuffer serializeObject(SerializedScriptValue obj);
 
+    boolean isFromCurrentWorld(any obj);
+
     void setUsesOverlayScrollbars(boolean enabled);
 
     void forceReload(boolean endToEnd);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to