Changeset 205354 in webkit


Ignore:
Timestamp:
Sep 2, 2016 11:04:25 AM (8 years ago)
Author:
Chris Dumez
Message:

Align proto getter / setter behavior with other browsers
https://bugs.webkit.org/show_bug.cgi?id=161455

Reviewed by Mark Lam.

Source/JavaScriptCore:

Drop allowsAccessFrom from the methodTable and delegate cross-origin
checking to the DOM bindings for SetPrototypeOf? / GetPrototypeOf?.
This is more consistent with other operations (e.g. GetOwnProperty?).

  • jsc.cpp:
  • runtime/JSGlobalObject.cpp:
  • runtime/JSGlobalObject.h:
  • runtime/JSGlobalObjectFunctions.cpp:

(JSC::globalFuncProtoGetter):
(JSC::globalFuncProtoSetter):
(JSC::globalFuncBuiltinLog): Deleted.

  • runtime/JSGlobalObjectFunctions.h:
  • runtime/JSObject.h:

(JSC::JSObject::getArrayLength): Deleted.

  • runtime/JSProxy.cpp:

(JSC::JSProxy::setPrototype):
(JSC::JSProxy::getPrototype):

  • runtime/JSProxy.h:
  • runtime/ObjectConstructor.cpp:

(JSC::objectConstructorGetPrototypeOf):
(JSC::objectConstructorSetPrototypeOf):
(JSC::objectConstructorGetOwnPropertyDescriptor): Deleted.
(JSC::objectConstructorGetOwnPropertyDescriptors): Deleted.

  • runtime/ObjectConstructor.h:
  • runtime/ReflectObject.cpp:

(JSC::reflectObjectGetPrototypeOf):
(JSC::reflectObjectSetPrototypeOf):

  • runtime/JSObject.cpp:

(JSC::JSObject::setPrototypeWithCycleCheck):
Comment out check added in r197648. This check was added to match
the latest EcmaScript spec:

This check allowed for Prototype? chain cycles if the prototype
chain includes objects that do not use the ordinary object definitions
for GetPrototypeOf? and SetPrototypeOf?.
The issue is that the rest of our code base does not properly handle
such cycles and we can end up in infinite loops. This became obvious
because this patch updates Window / Location so that they no longer
use the default GetPrototypeOf? / SetPrototypeOf?. If I do not
comment out this check, I get an infinite loop in
Structure::anyObjectInChainMayInterceptIndexedAccesses(), which is
called from JSObject::setPrototypeDirect(), when running the following
layout test:

  • html/browsers/history/the-location-interface/allow_prototype_cycle_through_location.sub.html

I filed https://bugs.webkit.org/show_bug.cgi?id=161534 to track this
issue.

Source/WebCore:

Align cross-origin proto getter / setter behavior with other
browsers and the specification:

SetPrototypeOf? should throw a TypeError:

GetPrototypeOf? should return null cross-origin:

Test: js/dom/setPrototypeOf-location-window.html

  • bindings/js/JSDOMWindowBase.cpp:

(WebCore::JSDOMWindowBase::JSDOMWindowBase): Deleted.

  • bindings/js/JSDOMWindowCustom.cpp:

(WebCore::JSDOMWindow::setPrototype):
(WebCore::JSDOMWindow::getPrototype):

  • bindings/js/JSLocationCustom.cpp:

(WebCore::JSLocation::setPrototype):
(WebCore::JSLocation::getPrototype):

  • bindings/js/JSWorkerGlobalScopeBase.cpp:

(WebCore::JSWorkerGlobalScopeBase::supportsRichSourceInfo): Deleted.

  • bindings/js/JSWorkerGlobalScopeBase.h:
  • bindings/scripts/CodeGeneratorJS.pm:

(GenerateHeader):

  • bindings/scripts/IDLAttributes.txt:
  • page/DOMWindow.idl:
  • page/Location.idl:

LayoutTests:

Add layout test coverage and update a few existing test to reflect
behavior change.

  • http/tests/security/cross-frame-access-object-getPrototypeOf-expected.txt:
  • http/tests/security/cross-frame-access-object-getPrototypeOf.html:
  • http/tests/security/cross-frame-access-object-setPrototypeOf-expected.txt:
  • http/tests/security/cross-frame-access-object-setPrototypeOf.html:
  • http/tests/security/xss-DENIED-htmlelelment-with-iframe-proto-expected.txt:
  • http/tests/security/xss-DENIED-htmlelelment-with-iframe-proto.html:
  • http/tests/security/xss-DENIED-method-with-iframe-proto-expected.txt:
  • http/tests/security/xss-DENIED-method-with-iframe-proto.html:
  • http/tests/security/xss-DENIED-non-shadowable-propterty-with-iframe-proto-expected.txt:
  • http/tests/security/xss-DENIED-non-shadowable-propterty-with-iframe-proto.html:
  • http/tests/security/xss-DENIED-regular-propterty-with-iframe-proto-expected.txt:
  • http/tests/security/xss-DENIED-regular-propterty-with-iframe-proto.html:
  • js/dom/setPrototypeOf-location-window-expected.txt: Added.
  • js/dom/setPrototypeOf-location-window.html: Added.
Location:
trunk
Files:
2 added
3 deleted
41 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r205348 r205354  
     12016-09-02  Chris Dumez  <cdumez@apple.com>
     2
     3        Align proto getter / setter behavior with other browsers
     4        https://bugs.webkit.org/show_bug.cgi?id=161455
     5
     6        Reviewed by Mark Lam.
     7
     8        Add layout test coverage and update a few existing test to reflect
     9        behavior change.
     10
     11        * http/tests/security/cross-frame-access-object-getPrototypeOf-expected.txt:
     12        * http/tests/security/cross-frame-access-object-getPrototypeOf.html:
     13        * http/tests/security/cross-frame-access-object-setPrototypeOf-expected.txt:
     14        * http/tests/security/cross-frame-access-object-setPrototypeOf.html:
     15        * http/tests/security/xss-DENIED-htmlelelment-with-iframe-proto-expected.txt:
     16        * http/tests/security/xss-DENIED-htmlelelment-with-iframe-proto.html:
     17        * http/tests/security/xss-DENIED-method-with-iframe-proto-expected.txt:
     18        * http/tests/security/xss-DENIED-method-with-iframe-proto.html:
     19        * http/tests/security/xss-DENIED-non-shadowable-propterty-with-iframe-proto-expected.txt:
     20        * http/tests/security/xss-DENIED-non-shadowable-propterty-with-iframe-proto.html:
     21        * http/tests/security/xss-DENIED-regular-propterty-with-iframe-proto-expected.txt:
     22        * http/tests/security/xss-DENIED-regular-propterty-with-iframe-proto.html:
     23        * js/dom/setPrototypeOf-location-window-expected.txt: Added.
     24        * js/dom/setPrototypeOf-location-window.html: Added.
     25
    1262016-09-02  Eric Carlson  <eric.carlson@apple.com>
    227
  • trunk/LayoutTests/fast/dom/Window/window-custom-prototype-crash-expected.txt

    r30157 r205354  
     1CONSOLE MESSAGE: line 7: TypeError: Cannot set prototype of this object
    12If this did not crash the test has succeeded.
  • trunk/LayoutTests/http/tests/security/cross-frame-access-object-getPrototypeOf-expected.txt

    r205301 r205354  
    1 CONSOLE MESSAGE: line 1: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
    2 CONSOLE MESSAGE: line 1: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
    31This tests that you can't get the prototype of the window or history objects cross-origin using Object.getPrototypeOf().
    42
    53PASS: Object.getPrototypeOf(targetWindow) should be 'null' and is.
    64PASS: Object.getPrototypeOf(targetWindow.location) should be 'null' and is.
     5PASS: protoGetter.call(targetWindow) should be 'null' and is.
     6PASS: protoGetter.call(targetWindow.location) should be 'null' and is.
    77PASS targetWindow.history threw exception SecurityError (DOM Exception 18): Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match..
    88PASS: successfullyParsed should be 'true' and is.
  • trunk/LayoutTests/http/tests/security/cross-frame-access-object-getPrototypeOf.html

    r205301 r205354  
    1919            shouldBeNull("Object.getPrototypeOf(targetWindow)");
    2020            shouldBeNull("Object.getPrototypeOf(targetWindow.location)");
     21            protoGetter = Object.getOwnPropertyDescriptor(Object.prototype, '__proto__').get;
     22            shouldBeNull("protoGetter.call(targetWindow)");
     23            shouldBeNull("protoGetter.call(targetWindow.location)");
     24
    2125            shouldThrowErrorName("targetWindow.history", "SecurityError");
    2226
  • trunk/LayoutTests/http/tests/security/cross-frame-access-object-setPrototypeOf-expected.txt

    r205301 r205354  
    1 CONSOLE MESSAGE: line 1: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
    2 CONSOLE MESSAGE: line 1: Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
    31This tests that you can't set the prototype of the window or location objects cross-origin using Object.setPrototypeOf()
    42
     
    75
    86PASS: targetWindow instanceof Array should be 'false' and is.
    9 PASS Object.setPrototypeOf(targetWindow, Array.prototype) threw exception TypeError: Permission denied.
     7PASS Object.setPrototypeOf(targetWindow, Array.prototype) threw exception TypeError: Cannot set prototype of this object.
    108PASS: targetWindow instanceof Array should be 'false' and is.
    119PASS: targetWindow.location instanceof Array should be 'false' and is.
    12 PASS Object.setPrototypeOf(targetWindow.location, Array.prototype) threw exception TypeError: Permission denied.
     10PASS Object.setPrototypeOf(targetWindow.location, Array.prototype) threw exception TypeError: Cannot set prototype of this object.
     11PASS: targetWindow.location instanceof Array should be 'false' and is.
     12PASS: targetWindow instanceof Array should be 'false' and is.
     13PASS protoSetter.call(targetWindow, Array.prototype) threw exception TypeError: Cannot set prototype of this object.
     14PASS: targetWindow instanceof Array should be 'false' and is.
     15PASS: targetWindow.location instanceof Array should be 'false' and is.
     16PASS protoSetter.call(targetWindow.location, Array.prototype) threw exception TypeError: Cannot set prototype of this object.
    1317PASS: targetWindow.location instanceof Array should be 'false' and is.
    1418PASS: successfullyParsed should be 'true' and is.
  • trunk/LayoutTests/http/tests/security/cross-frame-access-object-setPrototypeOf.html

    r205301 r205354  
    2525            shouldBeFalse("targetWindow.location instanceof Array");
    2626
     27            protoSetter = Object.getOwnPropertyDescriptor(Object.prototype, '__proto__').set;
     28            shouldBeFalse("targetWindow instanceof Array");
     29            shouldThrowErrorName("protoSetter.call(targetWindow, Array.prototype)", "TypeError");
     30            shouldBeFalse("targetWindow instanceof Array");
     31
     32            shouldBeFalse("targetWindow.location instanceof Array");
     33            shouldThrowErrorName("protoSetter.call(targetWindow.location, Array.prototype)", "TypeError");
     34            shouldBeFalse("targetWindow.location instanceof Array");
     35
    2736            finishJSTest();
    2837        }
  • trunk/LayoutTests/http/tests/security/xss-DENIED-htmlelelment-with-iframe-proto-expected.txt

    r205136 r205354  
    55
    66
     7PASS __proto__ = targetWindow threw exception TypeError: Cannot set prototype of this object.
    78PASS targetWindow.myinput threw exception SecurityError (DOM Exception 18): Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match..
    89PASS: successfullyParsed should be 'true' and is.
  • trunk/LayoutTests/http/tests/security/xss-DENIED-htmlelelment-with-iframe-proto.html

    r205136 r205354  
    1515
    1616window.onload = function() {
    17   __proto__ = targetWindow;
     17  shouldThrowErrorName("__proto__ = targetWindow", "TypeError");
    1818  shouldThrowErrorName('targetWindow.myinput', 'SecurityError');
    1919  finishJSTest();
  • trunk/LayoutTests/http/tests/security/xss-DENIED-method-with-iframe-proto-expected.txt

    r205136 r205354  
    1 CONSOLE MESSAGE: line 40: SecurityError (DOM Exception 18): Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
    2 CONSOLE MESSAGE: line 47: SecurityError (DOM Exception 18): Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
    3 CONSOLE MESSAGE: line 54: SecurityError (DOM Exception 18): Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
     1CONSOLE MESSAGE: line 36: SecurityError (DOM Exception 18): Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
     2CONSOLE MESSAGE: line 43: SecurityError (DOM Exception 18): Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
     3CONSOLE MESSAGE: line 50: SecurityError (DOM Exception 18): Blocked a frame with origin "http://127.0.0.1:8000" from accessing a frame with origin "http://localhost:8000". Protocols, domains, and ports must match.
    44
    55Tests that making other frame window a prototype doesn't expose that window methods
    66
     7On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
     8
     9
     10PASS __proto__ = targetWindow threw exception TypeError: Cannot set prototype of this object.
     11PASS: successfullyParsed should be 'true' and is.
     12
     13TEST COMPLETE
     14
  • trunk/LayoutTests/http/tests/security/xss-DENIED-method-with-iframe-proto.html

    r205096 r205354  
    11<html>
    22<head>
     3<script src="/js-test-resources/js-test-pre.js"></script>
    34<script src="resources/cross-frame-access.js"></script>
    45</head>
     
    78<pre id="console"></pre>
    89<script>
    9 if (window.testRunner) {
    10     testRunner.dumpAsText();
    11     testRunner.waitUntilDone();
    12 }
    13 
    14 log("Tests that making other frame window a prototype doesn't expose that window methods");
     10description("Tests that making other frame window a prototype doesn't expose that window methods");
     11jsTestIsAsync = true;
    1512
    1613targetWindow = frames[0];
     
    2421function check() {
    2522  shouldBeFalse('this.wasInvoked');
    26   if (window.testRunner)
    27       testRunner.notifyDone();
     23  finishJSTest();
    2824}
    2925
     
    3127  originalSetTimeout = setTimeout;
    3228
    33   __proto__ = targetWindow;
     29  shouldThrowErrorName("__proto__ = targetWindow", "TypeError");
    3430
    3531  var needsCheck = false;
     
    5854    originalSetTimeout(check, 10);
    5955  } else {
    60     if (window.testRunner)
    61       testRunner.notifyDone();
     56    finishJSTest();
    6257  }
    6358}
    6459</script>
     60<script src="/js-test-resources/js-test-post.js"></script>
    6561</body>
    6662</html>
  • trunk/LayoutTests/http/tests/security/xss-DENIED-non-shadowable-propterty-with-iframe-proto-expected.txt

    r48484 r205354  
    11
    22Tests that making other frame window a prototype doesn't expose that window properties
     3
     4On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
     5
     6
     7PASS __proto__ = targetWindow threw exception TypeError: Cannot set prototype of this object.
    38PASS: location === originalLocation should be 'true' and is.
    49PASS: this.location === originalLocation should be 'true' and is.
     10PASS: successfullyParsed should be 'true' and is.
    511
     12TEST COMPLETE
     13
  • trunk/LayoutTests/http/tests/security/xss-DENIED-non-shadowable-propterty-with-iframe-proto.html

    r120174 r205354  
    11<html>
    22<head>
     3<script src="/js-test-resources/js-test-pre.js"></script>
    34<script src="resources/cross-frame-access.js"></script>
    45</head>
     
    78<pre id="console"></pre>
    89<script>
    9 if (window.testRunner)
    10     testRunner.dumpAsText();
    11 
    12 log("Tests that making other frame window a prototype doesn't expose that window properties");
     10description("Tests that making other frame window a prototype doesn't expose that window properties");
     11jsTestIsAsync = true;
    1312
    1413targetWindow = frames[0];
     
    1716  originalLocation = location;
    1817
    19   __proto__ = targetWindow;
     18  shouldThrowErrorName("__proto__ = targetWindow", "TypeError");
    2019
    2120  shouldBeTrue('location === originalLocation');
    2221  shouldBeTrue('this.location === originalLocation');
     22  finishJSTest();
    2323}
    2424</script>
     25<script src="/js-test-resources/js-test-post.js"></script>
    2526</body>
    2627</html>
  • trunk/LayoutTests/http/tests/security/xss-DENIED-regular-propterty-with-iframe-proto-expected.txt

    r48484 r205354  
    11
    22Tests that making other frame window a prototype doesn't expose that window properties
     3
     4On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
     5
     6
     7PASS __proto__ = targetWindow threw exception TypeError: Cannot set prototype of this object.
    38PASS: innerHeight === originalInnerHeight should be 'true' and is.
    49PASS: this.innerHeight === originalInnerHeight should be 'true' and is.
     10PASS: successfullyParsed should be 'true' and is.
    511
     12TEST COMPLETE
     13
  • trunk/LayoutTests/http/tests/security/xss-DENIED-regular-propterty-with-iframe-proto.html

    r120174 r205354  
    11<html>
    22<head>
     3<script src="/js-test-resources/js-test-pre.js"></script>
    34<script src="resources/cross-frame-access.js"></script>
    45</head>
     
    78<pre id="console"></pre>
    89<script>
    9 if (window.testRunner)
    10     testRunner.dumpAsText();
    11 
    12 log("Tests that making other frame window a prototype doesn't expose that window properties");
     10description("Tests that making other frame window a prototype doesn't expose that window properties");
     11jsTestIsAsync = true;
    1312
    1413targetWindow = frames[0];
     
    1716  originalInnerHeight = innerHeight;
    1817
    19   __proto__ = targetWindow;
     18  shouldThrowErrorName("__proto__ = targetWindow", "TypeError");
    2019
    2120  shouldBeTrue('innerHeight === originalInnerHeight');
    2221  shouldBeTrue('this.innerHeight === originalInnerHeight');
     22  finishJSTest();
    2323}
    2424</script>
     25<script src="/js-test-resources/js-test-post.js"></script>
    2526</body>
    2627</html>
  • trunk/LayoutTests/js/object-literal-shorthand-construction-expected.txt

    r181179 r205354  
    6262PASS !!Object.getOwnPropertyDescriptor({set 'x'(value){}}, 'x').set is true
    6363PASS !!Object.getOwnPropertyDescriptor({set 42(value){}}, '42').set is true
    64 PASS __proto__ = []; ({__proto__: __proto__}) instanceof Array is true
    65 PASS __proto__ = []; ({__proto__}) instanceof Array is false
    66 PASS __proto__ = []; ({__proto__}).__proto__ instanceof Array is true
     64PASS __proto__ = [] threw exception TypeError: Cannot set prototype of this object.
    6765PASS successfullyParsed is true
    6866
  • trunk/LayoutTests/js/script-tests/object-literal-shorthand-construction.js

    r181179 r205354  
    110110shouldBeTrue("!!Object.getOwnPropertyDescriptor({set 42(value){}}, '42').set");
    111111
    112 // __proto__ shorthand should be not modify the prototype.
    113 shouldBeTrue("__proto__ = []; ({__proto__: __proto__}) instanceof Array");
    114 shouldBeFalse("__proto__ = []; ({__proto__}) instanceof Array");
    115 shouldBeTrue("__proto__ = []; ({__proto__}).__proto__ instanceof Array");
     112shouldThrowErrorName("__proto__ = []", "TypeError");
  • trunk/LayoutTests/js/script-tests/sloppy-getter-setter-global-object.js

    r183275 r205354  
    3434
    3535var top_level_sloppy_setter = Object.getOwnPropertyDescriptor(Object.prototype,'__proto__').set;
    36 shouldNotThrow("top_level_sloppy_setter(['foo']);");
     36shouldThrowErrorName("top_level_sloppy_setter(['foo']);", "TypeError");
  • trunk/LayoutTests/js/sloppy-getter-setter-global-object-expected.txt

    r183275 r205354  
    1212PASS (0,Object.getOwnPropertyDescriptor(Object.prototype,'__proto__').set)(['foo']) threw exception TypeError: Can't convert undefined or null to object.
    1313PASS top_level_sloppy_getter(); did not throw exception.
    14 PASS top_level_sloppy_setter(['foo']); did not throw exception.
     14PASS top_level_sloppy_setter(['foo']); threw exception TypeError: Cannot set prototype of this object.
    1515PASS successfullyParsed is true
    1616
  • trunk/Source/JavaScriptCore/ChangeLog

    r205335 r205354  
     12016-09-02  Chris Dumez  <cdumez@apple.com>
     2
     3        Align proto getter / setter behavior with other browsers
     4        https://bugs.webkit.org/show_bug.cgi?id=161455
     5
     6        Reviewed by Mark Lam.
     7
     8        Drop allowsAccessFrom from the methodTable and delegate cross-origin
     9        checking to the DOM bindings for [[SetPrototypeOf]] / [[GetPrototypeOf]].
     10        This is more consistent with other operations (e.g. [[GetOwnProperty]]).
     11
     12        * jsc.cpp:
     13        * runtime/JSGlobalObject.cpp:
     14        * runtime/JSGlobalObject.h:
     15        * runtime/JSGlobalObjectFunctions.cpp:
     16        (JSC::globalFuncProtoGetter):
     17        (JSC::globalFuncProtoSetter):
     18        (JSC::globalFuncBuiltinLog): Deleted.
     19        * runtime/JSGlobalObjectFunctions.h:
     20        * runtime/JSObject.h:
     21        (JSC::JSObject::getArrayLength): Deleted.
     22        * runtime/JSProxy.cpp:
     23        (JSC::JSProxy::setPrototype):
     24        (JSC::JSProxy::getPrototype):
     25        * runtime/JSProxy.h:
     26        * runtime/ObjectConstructor.cpp:
     27        (JSC::objectConstructorGetPrototypeOf):
     28        (JSC::objectConstructorSetPrototypeOf):
     29        (JSC::objectConstructorGetOwnPropertyDescriptor): Deleted.
     30        (JSC::objectConstructorGetOwnPropertyDescriptors): Deleted.
     31        * runtime/ObjectConstructor.h:
     32        * runtime/ReflectObject.cpp:
     33        (JSC::reflectObjectGetPrototypeOf):
     34        (JSC::reflectObjectSetPrototypeOf):
     35
     36        * runtime/JSObject.cpp:
     37        (JSC::JSObject::setPrototypeWithCycleCheck):
     38        Comment out check added in r197648. This check was added to match
     39        the latest EcmaScript spec:
     40        - https://tc39.github.io/ecma262/#sec-ordinarysetprototypeof (step 8)
     41        This check allowed for [[Prototype]] chain cycles if the prototype
     42        chain includes objects that do not use the ordinary object definitions
     43        for [[GetPrototypeOf]] and [[SetPrototypeOf]].
     44        The issue is that the rest of our code base does not properly handle
     45        such cycles and we can end up in infinite loops. This became obvious
     46        because this patch updates Window / Location so that they no longer
     47        use the default [[GetPrototypeOf]] / [[SetPrototypeOf]]. If I do not
     48        comment out this check, I get an infinite loop in
     49        Structure::anyObjectInChainMayInterceptIndexedAccesses(), which is
     50        called from JSObject::setPrototypeDirect(), when running the following
     51        layout test:
     52        - html/browsers/history/the-location-interface/allow_prototype_cycle_through_location.sub.html
     53        I filed https://bugs.webkit.org/show_bug.cgi?id=161534 to track this
     54        issue.
     55
    1562016-09-01  Yusuke Suzuki  <utatane.tea@gmail.com>
    257
  • trunk/Source/JavaScriptCore/jsc.cpp

    r205328 r205354  
    904904
    905905const ClassInfo GlobalObject::s_info = { "global", &JSGlobalObject::s_info, nullptr, CREATE_METHOD_TABLE(GlobalObject) };
    906 const GlobalObjectMethodTable GlobalObject::s_globalObjectMethodTable = { &allowsAccessFrom, &supportsRichSourceInfo, &shouldInterruptScript, &javaScriptRuntimeFlags, 0, &shouldInterruptScriptBeforeTimeout, &moduleLoaderResolve, &moduleLoaderFetch, nullptr, nullptr, nullptr, nullptr };
     906const GlobalObjectMethodTable GlobalObject::s_globalObjectMethodTable = { &supportsRichSourceInfo, &shouldInterruptScript, &javaScriptRuntimeFlags, 0, &shouldInterruptScriptBeforeTimeout, &moduleLoaderResolve, &moduleLoaderFetch, nullptr, nullptr, nullptr, nullptr };
    907907
    908908
  • trunk/Source/JavaScriptCore/runtime/JSGlobalObject.cpp

    r205198 r205354  
    208208const ClassInfo JSGlobalObject::s_info = { "GlobalObject", &Base::s_info, &globalObjectTable, CREATE_METHOD_TABLE(JSGlobalObject) };
    209209
    210 const GlobalObjectMethodTable JSGlobalObject::s_globalObjectMethodTable = { &allowsAccessFrom, &supportsRichSourceInfo, &shouldInterruptScript, &javaScriptRuntimeFlags, nullptr, &shouldInterruptScriptBeforeTimeout, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
     210const GlobalObjectMethodTable JSGlobalObject::s_globalObjectMethodTable = { &supportsRichSourceInfo, &shouldInterruptScript, &javaScriptRuntimeFlags, nullptr, &shouldInterruptScriptBeforeTimeout, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
    211211
    212212/* Source for JSGlobalObject.lut.h
  • trunk/Source/JavaScriptCore/runtime/JSGlobalObject.h

    r205278 r205354  
    149149
    150150struct GlobalObjectMethodTable {
    151     typedef bool (*AllowsAccessFromFunctionPtr)(const JSGlobalObject*, ExecState*);
    152     AllowsAccessFromFunctionPtr allowsAccessFrom;
    153 
    154151    typedef bool (*SupportsRichSourceInfoFunctionPtr)(const JSGlobalObject*);
    155152    SupportsRichSourceInfoFunctionPtr supportsRichSourceInfo;
     
    713710    const GlobalObjectMethodTable* globalObjectMethodTable() const { return m_globalObjectMethodTable; }
    714711
    715     static bool allowsAccessFrom(const JSGlobalObject*, ExecState*) { return true; }
    716712    static bool supportsRichSourceInfo(const JSGlobalObject*) { return true; }
    717713
  • trunk/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp

    r205301 r205354  
    867867}
    868868
    869 class GlobalFuncProtoGetterFunctor {
    870 public:
    871     GlobalFuncProtoGetterFunctor(ExecState* exec, JSObject* thisObject)
    872         : m_exec(exec)
    873         , m_hasSkippedFirstFrame(false)
    874         , m_thisObject(thisObject)
    875         , m_result(JSValue::encode(jsUndefined()))
    876     {
    877     }
    878 
    879     EncodedJSValue result() { return m_result; }
    880 
    881     StackVisitor::Status operator()(StackVisitor& visitor) const
    882     {
    883         if (!m_hasSkippedFirstFrame) {
    884             m_hasSkippedFirstFrame = true;
    885             return StackVisitor::Continue;
    886         }
    887 
    888         if (m_thisObject->allowsAccessFrom(visitor->callFrame()))
    889             m_result = JSValue::encode(m_thisObject->getPrototype(m_exec->vm(), m_exec));
    890 
    891         return StackVisitor::Done;
    892     }
    893 
    894 private:
    895     ExecState* m_exec;
    896     mutable bool m_hasSkippedFirstFrame;
    897     JSObject* m_thisObject;
    898     mutable EncodedJSValue m_result;
    899 };
    900 
    901869EncodedJSValue JSC_HOST_CALL globalFuncProtoGetter(ExecState* exec)
    902870{
     
    916884    }
    917885
    918     GlobalFuncProtoGetterFunctor functor(exec, thisObject);
    919     // This can throw but it's just unneeded extra work to check for it. The return
    920     // value from this function is only used as the return value from a host call.
    921     // Therefore, the return value is only used if there wasn't an exception.
    922     exec->iterate(functor);
    923     return functor.result();
    924 }
    925 
    926 class GlobalFuncProtoSetterFunctor {
    927 public:
    928     GlobalFuncProtoSetterFunctor(JSObject* thisObject)
    929         : m_hasSkippedFirstFrame(false)
    930         , m_allowsAccess(false)
    931         , m_thisObject(thisObject)
    932     {
    933     }
    934 
    935     bool allowsAccess() const { return m_allowsAccess; }
    936 
    937     StackVisitor::Status operator()(StackVisitor& visitor) const
    938     {
    939         if (!m_hasSkippedFirstFrame) {
    940             m_hasSkippedFirstFrame = true;
    941             return StackVisitor::Continue;
    942         }
    943 
    944         m_allowsAccess = m_thisObject->allowsAccessFrom(visitor->callFrame());
    945         return StackVisitor::Done;
    946     }
    947 
    948 private:
    949     mutable bool m_hasSkippedFirstFrame;
    950     mutable bool m_allowsAccess;
    951     JSObject* m_thisObject;
    952 };
    953 
    954 bool checkProtoSetterAccessAllowed(ExecState* exec, JSObject* object)
    955 {
    956     GlobalFuncProtoSetterFunctor functor(object);
    957     exec->iterate(functor);
    958     return functor.allowsAccess();
     886    return JSValue::encode(thisObject->getPrototype(vm, exec));
    959887}
    960888
     
    975903        return JSValue::encode(jsUndefined());
    976904
    977     if (!checkProtoSetterAccessAllowed(exec, thisObject))
    978         return JSValue::encode(jsUndefined());
    979 
    980905    // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla.
    981906    if (!value.isObject() && !value.isNull())
  • trunk/Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.h

    r202680 r205354  
    5353EncodedJSValue JSC_HOST_CALL globalFuncBuiltinLog(ExecState*);
    5454
    55 bool checkProtoSetterAccessAllowed(ExecState*, JSObject*);
    56 
    5755static const double mantissaOverflowLowerBound = 9007199254740992.0;
    5856double parseIntOverflow(const LChar*, unsigned length, int radix);
  • trunk/Source/JavaScriptCore/runtime/JSObject.cpp

    r205198 r205354  
    13721372
    13731373    JSValue nextPrototype = prototype;
    1374     MethodTable::GetPrototypeFunctionPtr defaultGetPrototype = JSObject::getPrototype;
    13751374    while (nextPrototype && nextPrototype.isObject()) {
    13761375        if (nextPrototype == this) {
     
    13791378            return false;
    13801379        }
    1381         if (UNLIKELY(asObject(nextPrototype)->methodTable(vm)->getPrototype != defaultGetPrototype))
    1382             break; // We're done. Set the prototype.
     1380        // FIXME: The specification says we should do this but this allows for cycles and our
     1381        // code base currently does not deal properly with such cycles.
     1382        // https://bugs.webkit.org/show_bug.cgi?id=161534
     1383        // if (UNLIKELY(asObject(nextPrototype)->methodTable(vm)->getPrototype != JSObject::getPrototype))
     1384        //    break; // We're done. Set the prototype.
    13831385        nextPrototype = asObject(nextPrototype)->getPrototypeDirect();
    13841386    }
     
    14001402{
    14011403    return methodTable(vm)->setPrototype(this, exec, prototype, shouldThrowIfCantSet);
    1402 }
    1403 
    1404 bool JSObject::allowsAccessFrom(ExecState* exec)
    1405 {
    1406     JSGlobalObject* globalObject = this->globalObject();
    1407     return globalObject->globalObjectMethodTable()->allowsAccessFrom(globalObject, exec);
    14081404}
    14091405
  • trunk/Source/JavaScriptCore/runtime/JSObject.h

    r205324 r205354  
    169169    JS_EXPORT_PRIVATE bool getOwnPropertyDescriptor(ExecState*, PropertyName, PropertyDescriptor&);
    170170
    171     JS_EXPORT_PRIVATE bool allowsAccessFrom(ExecState*);
    172 
    173171    unsigned getArrayLength() const
    174172    {
  • trunk/Source/JavaScriptCore/runtime/JSProxy.cpp

    r198023 r205354  
    140140}
    141141
     142bool JSProxy::setPrototype(JSObject* object, ExecState* exec, JSValue value, bool shouldThrowIfCantSet)
     143{
     144    JSProxy* thisObject = jsCast<JSProxy*>(object);
     145    return thisObject->target()->methodTable(exec->vm())->setPrototype(thisObject->target(), exec, value, shouldThrowIfCantSet);
     146}
     147
     148JSValue JSProxy::getPrototype(JSObject* object, ExecState* exec)
     149{
     150    JSProxy* thisObject = jsCast<JSProxy*>(object);
     151    return thisObject->target()->methodTable(exec->vm())->getPrototype(thisObject->target(), exec);
     152}
     153
    142154} // namespace JSC
  • trunk/Source/JavaScriptCore/runtime/JSProxy.h

    r201808 r205354  
    9595    JS_EXPORT_PRIVATE static void getGenericPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
    9696    JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow);
     97    JS_EXPORT_PRIVATE static bool setPrototype(JSObject*, ExecState*, JSValue, bool shouldThrowIfCantSet);
     98    JS_EXPORT_PRIVATE static JSValue getPrototype(JSObject*, ExecState*);
    9799
    98100private:
  • trunk/Source/JavaScriptCore/runtime/ObjectConstructor.cpp

    r205258 r205354  
    167167}
    168168
    169 class ObjectConstructorGetPrototypeOfFunctor {
    170 public:
    171     ObjectConstructorGetPrototypeOfFunctor(ExecState* exec, JSObject* object)
    172         : m_exec(exec)
    173         , m_hasSkippedFirstFrame(false)
    174         , m_object(object)
    175         , m_result(jsUndefined())
    176     {
    177     }
    178 
    179     JSValue result() const { return m_result; }
    180 
    181     StackVisitor::Status operator()(StackVisitor& visitor) const
    182     {
    183         if (!m_hasSkippedFirstFrame) {
    184             m_hasSkippedFirstFrame = true;
    185             return StackVisitor::Continue;
    186         }
    187 
    188         if (m_object->allowsAccessFrom(visitor->callFrame()))
    189             m_result = m_object->getPrototype(m_exec->vm(), m_exec);
    190         else
    191             m_result = jsNull();
    192         return StackVisitor::Done;
    193     }
    194 
    195 private:
    196     ExecState* m_exec;
    197     mutable bool m_hasSkippedFirstFrame;
    198     JSObject* m_object;
    199     mutable JSValue m_result;
    200 };
    201 
    202 JSValue objectConstructorGetPrototypeOf(ExecState* exec, JSObject* object)
    203 {
    204     ObjectConstructorGetPrototypeOfFunctor functor(exec, object);
    205     // This can throw but it's just unneeded extra work to check for it. The return
    206     // value from this function is only used as the return value from a host call.
    207     // Therefore, the return value is only used if there wasn't an exception.
    208     exec->iterate(functor);
    209     return functor.result();
    210 }
    211 
    212169EncodedJSValue JSC_HOST_CALL objectConstructorGetPrototypeOf(ExecState* exec)
    213170{
     
    215172    if (exec->hadException())
    216173        return JSValue::encode(jsUndefined());
    217     return JSValue::encode(objectConstructorGetPrototypeOf(exec, object));
     174    return JSValue::encode(object->getPrototype(exec->vm(), exec));
    218175}
    219176
     
    234191    if (exec->hadException())
    235192        return JSValue::encode(objectValue);
    236 
    237     if (!checkProtoSetterAccessAllowed(exec, object)) {
    238         throwTypeError(exec, scope, ASCIILiteral("Permission denied"));
    239         return JSValue::encode(objectValue);
    240     }
    241193
    242194    bool shouldThrowIfCantSet = true;
  • trunk/Source/JavaScriptCore/runtime/ObjectConstructor.h

    r204679 r205354  
    125125
    126126JS_EXPORT_PRIVATE JSObject* objectConstructorFreeze(ExecState*, JSObject*);
    127 JSValue objectConstructorGetPrototypeOf(ExecState*, JSObject*);
    128127JSValue objectConstructorGetOwnPropertyDescriptor(ExecState*, JSObject*, const Identifier&);
    129128JSValue objectConstructorGetOwnPropertyDescriptors(ExecState*, JSObject*);
  • trunk/Source/JavaScriptCore/runtime/ReflectObject.cpp

    r205198 r205354  
    216216    if (!target.isObject())
    217217        return JSValue::encode(throwTypeError(exec, scope, ASCIILiteral("Reflect.getPrototypeOf requires the first argument be an object")));
    218     return JSValue::encode(objectConstructorGetPrototypeOf(exec, asObject(target)));
     218    return JSValue::encode(asObject(target)->getPrototype(exec->vm(), exec));
    219219}
    220220
     
    302302
    303303    JSObject* object = asObject(target);
    304 
    305     if (!checkProtoSetterAccessAllowed(exec, object))
    306         return JSValue::encode(jsBoolean(false));
    307304
    308305    bool shouldThrowIfCantSet = false;
  • trunk/Source/WebCore/ChangeLog

    r205348 r205354  
     12016-09-02  Chris Dumez  <cdumez@apple.com>
     2
     3        Align proto getter / setter behavior with other browsers
     4        https://bugs.webkit.org/show_bug.cgi?id=161455
     5
     6        Reviewed by Mark Lam.
     7
     8        Align cross-origin __proto__ getter / setter behavior with other
     9        browsers and the specification:
     10
     11        [[SetPrototypeOf]] should throw a TypeError:
     12        - https://html.spec.whatwg.org/#windowproxy-setprototypeof
     13        - https://html.spec.whatwg.org/#location-setprototypeof
     14        - https://tc39.github.io/ecma262/#sec-object.setprototypeof (step 5)
     15
     16        [[GetPrototypeOf]] should return null cross-origin:
     17        - https://html.spec.whatwg.org/#windowproxy-getprototypeof
     18        - https://html.spec.whatwg.org/#location-getprototypeof
     19
     20        Test: js/dom/setPrototypeOf-location-window.html
     21
     22        * bindings/js/JSDOMWindowBase.cpp:
     23        (WebCore::JSDOMWindowBase::JSDOMWindowBase): Deleted.
     24        * bindings/js/JSDOMWindowCustom.cpp:
     25        (WebCore::JSDOMWindow::setPrototype):
     26        (WebCore::JSDOMWindow::getPrototype):
     27        * bindings/js/JSLocationCustom.cpp:
     28        (WebCore::JSLocation::setPrototype):
     29        (WebCore::JSLocation::getPrototype):
     30        * bindings/js/JSWorkerGlobalScopeBase.cpp:
     31        (WebCore::JSWorkerGlobalScopeBase::supportsRichSourceInfo): Deleted.
     32        * bindings/js/JSWorkerGlobalScopeBase.h:
     33        * bindings/scripts/CodeGeneratorJS.pm:
     34        (GenerateHeader):
     35        * bindings/scripts/IDLAttributes.txt:
     36        * page/DOMWindow.idl:
     37        * page/Location.idl:
     38
    1392016-09-02  Eric Carlson  <eric.carlson@apple.com>
    240
  • trunk/Source/WebCore/bindings/js/JSDOMWindowBase.cpp

    r205278 r205354  
    5858namespace WebCore {
    5959
    60 static bool shouldAllowAccessFrom(const JSGlobalObject* thisObject, ExecState* exec)
    61 {
    62     return BindingSecurity::shouldAllowAccessToDOMWindow(exec, asJSDOMWindow(thisObject)->wrapped());
    63 }
    64 
    6560const ClassInfo JSDOMWindowBase::s_info = { "Window", &JSDOMGlobalObject::s_info, 0, CREATE_METHOD_TABLE(JSDOMWindowBase) };
    6661
    67 const GlobalObjectMethodTable JSDOMWindowBase::s_globalObjectMethodTable = { &shouldAllowAccessFrom, &supportsRichSourceInfo, &shouldInterruptScript, &javaScriptRuntimeFlags, &queueTaskToEventLoop, &shouldInterruptScriptBeforeTimeout, &moduleLoaderResolve, &moduleLoaderFetch, nullptr, nullptr, &moduleLoaderEvaluate, &defaultLanguage };
     62const GlobalObjectMethodTable JSDOMWindowBase::s_globalObjectMethodTable = { &supportsRichSourceInfo, &shouldInterruptScript, &javaScriptRuntimeFlags, &queueTaskToEventLoop, &shouldInterruptScriptBeforeTimeout, &moduleLoaderResolve, &moduleLoaderFetch, nullptr, nullptr, &moduleLoaderEvaluate, &defaultLanguage };
    6863
    6964JSDOMWindowBase::JSDOMWindowBase(VM& vm, Structure* structure, RefPtr<DOMWindow>&& window, JSDOMWindowShell* shell)
  • trunk/Source/WebCore/bindings/js/JSDOMWindowCustom.cpp

    r205200 r205354  
    343343}
    344344
     345bool JSDOMWindow::setPrototype(JSObject*, ExecState* exec, JSValue, bool shouldThrowIfCantSet)
     346{
     347    auto scope = DECLARE_THROW_SCOPE(exec->vm());
     348
     349    if (shouldThrowIfCantSet)
     350        throwTypeError(exec, scope, ASCIILiteral("Cannot set prototype of this object"));
     351
     352    return false;
     353}
     354
     355JSValue JSDOMWindow::getPrototype(JSObject* object, ExecState* exec)
     356{
     357    JSDOMWindow* thisObject = jsCast<JSDOMWindow*>(object);
     358    if (!BindingSecurity::shouldAllowAccessToDOMWindow(exec, thisObject->wrapped(), DoNotReportSecurityError))
     359        return jsNull();
     360
     361    return Base::getPrototype(object, exec);
     362}
     363
    345364// Custom Attributes
    346365
  • trunk/Source/WebCore/bindings/js/JSLocationCustom.cpp

    r205200 r205354  
    125125}
    126126
     127bool JSLocation::setPrototype(JSObject*, ExecState* exec, JSValue, bool shouldThrowIfCantSet)
     128{
     129    auto scope = DECLARE_THROW_SCOPE(exec->vm());
     130
     131    if (shouldThrowIfCantSet)
     132        throwTypeError(exec, scope, ASCIILiteral("Cannot set prototype of this object"));
     133
     134    return false;
     135}
     136
     137JSValue JSLocation::getPrototype(JSObject* object, ExecState* exec)
     138{
     139    JSLocation* thisObject = jsCast<JSLocation*>(object);
     140    if (!BindingSecurity::shouldAllowAccessToFrame(exec, thisObject->wrapped().frame(), DoNotReportSecurityError))
     141        return jsNull();
     142
     143    return Base::getPrototype(object, exec);
     144}
     145
    127146bool JSLocationPrototype::putDelegate(ExecState* exec, PropertyName propertyName, JSValue, PutPropertySlot&, bool& putResult)
    128147{
  • trunk/Source/WebCore/bindings/js/JSWorkerGlobalScopeBase.cpp

    r201808 r205354  
    4646const ClassInfo JSWorkerGlobalScopeBase::s_info = { "WorkerGlobalScope", &JSDOMGlobalObject::s_info, 0, CREATE_METHOD_TABLE(JSWorkerGlobalScopeBase) };
    4747
    48 const GlobalObjectMethodTable JSWorkerGlobalScopeBase::s_globalObjectMethodTable = { &allowsAccessFrom, &supportsRichSourceInfo, &shouldInterruptScript, &javaScriptRuntimeFlags, &queueTaskToEventLoop, &shouldInterruptScriptBeforeTimeout, nullptr, nullptr, nullptr, nullptr, nullptr, &defaultLanguage };
     48const GlobalObjectMethodTable JSWorkerGlobalScopeBase::s_globalObjectMethodTable = { &supportsRichSourceInfo, &shouldInterruptScript, &javaScriptRuntimeFlags, &queueTaskToEventLoop, &shouldInterruptScriptBeforeTimeout, nullptr, nullptr, nullptr, nullptr, nullptr, &defaultLanguage };
    4949
    5050JSWorkerGlobalScopeBase::JSWorkerGlobalScopeBase(JSC::VM& vm, JSC::Structure* structure, RefPtr<WorkerGlobalScope>&& impl)
     
    7878{
    7979    return m_wrapped.get();
    80 }
    81 
    82 bool JSWorkerGlobalScopeBase::allowsAccessFrom(const JSGlobalObject* object, ExecState* exec)
    83 {
    84     return JSGlobalObject::allowsAccessFrom(object, exec);
    8580}
    8681
  • trunk/Source/WebCore/bindings/js/JSWorkerGlobalScopeBase.h

    r201808 r205354  
    5454        static const JSC::GlobalObjectMethodTable s_globalObjectMethodTable;
    5555
    56         static bool allowsAccessFrom(const JSC::JSGlobalObject*, JSC::ExecState*);
    5756        static bool supportsRichSourceInfo(const JSC::JSGlobalObject*);
    5857        static bool shouldInterruptScript(const JSC::JSGlobalObject*);
  • trunk/Source/WebCore/bindings/scripts/CodeGeneratorJS.pm

    r205257 r205354  
    13221322    # Custom defineOwnProperty function
    13231323    push(@headerContent, "    static bool defineOwnProperty(JSC::JSObject*, JSC::ExecState*, JSC::PropertyName, const JSC::PropertyDescriptor&, bool shouldThrow);\n") if $interface->extendedAttributes->{"JSCustomDefineOwnProperty"};
     1324
     1325    # Custom getPrototype / setPrototype functions.
     1326    push (@headerContent, "    static JSC::JSValue getPrototype(JSC::JSObject*, JSC::ExecState*);\n") if $interface->extendedAttributes->{"CustomGetPrototype"};
     1327    push (@headerContent, "    static bool setPrototype(JSC::JSObject*, JSC::ExecState*, JSC::JSValue, bool shouldThrowIfCantSet);\n") if $interface->extendedAttributes->{"CustomSetPrototype"};
    13241328
    13251329    # Override toBoolean to return false for objects that want to 'MasqueradesAsUndefined'.
  • trunk/Source/WebCore/bindings/scripts/IDLAttributes.txt

    r204717 r205354  
    4242CustomEnumerateProperty
    4343CustomGetOwnPropertySlot
     44CustomGetPrototype
    4445CustomGetter
    4546CustomIndexedSetter
     
    5051CustomPutFunction
    5152CustomReturn
     53CustomSetPrototype
    5254CustomSetter
    5355CustomToJSObject
  • trunk/Source/WebCore/page/DOMWindow.idl

    r205280 r205354  
    3030    CustomEnumerateProperty,
    3131    CustomGetOwnPropertySlot,
     32    CustomGetPrototype,
    3233    CustomProxyToJSObject,
    3334    CustomPutFunction,
     35    CustomSetPrototype,
    3436    ExportMacro=WEBCORE_EXPORT,
    3537    ImplicitThis,
  • trunk/Source/WebCore/page/Location.idl

    r205154 r205354  
    3131    CustomDeleteProperty,
    3232    CustomEnumerateProperty,
     33    CustomGetPrototype,
    3334    CustomNamedSetter,
     35    CustomSetPrototype,
    3436    GenerateIsReachable=ImplFrame,
    3537    JSCustomDefineOwnProperty,
Note: See TracChangeset for help on using the changeset viewer.