Commit 0ade0386 authored by Stephen McGruer's avatar Stephen McGruer Committed by Commit Bot

Web Animations: Fix bugs in procedure to process a keyframes argument

There were three minor bugs left in the implementation:

  - We threw on lists-in-custom-iterators instead of just ignoring them.
  - We returned all properties on the keyframe rather than just those
    defined on the keyframe itself (e.g. we would include prototype
    properties, against spec).
  - We didn't access the properties in ascending unicode order.

Bug: 827573
Change-Id: I213ae5b24e1f35d7f28d16625025122950a6ba88
Reviewed-on: https://chromium-review.googlesource.com/989261Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Reviewed-by: default avatarYuki Shiino <yukishiino@chromium.org>
Reviewed-by: default avatarRobert Flack <flackr@chromium.org>
Commit-Queue: Stephen McGruer <smcgruer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#550641}
parent d014653a
<!DOCTYPE html>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<div id="target"></div>
<script>
test(() => {
assert_throws(null, () => {
target.animate({
[Symbol.iterator]() {
return { next() { throw 'error'; } };
}
});
});
}, 'Do not crash when exceptions are thrown during keyframe iteration.');
</script>
...@@ -134,18 +134,6 @@ test(() => { ...@@ -134,18 +134,6 @@ test(() => {
}); });
}, 'Custom iterator with non object keyframe should throw.'); }, 'Custom iterator with non object keyframe should throw.');
test(() => {
assert_throws({name: 'TypeError'}, () => {
assertAnimationEffect({
keyframes: createIterable([
{done: false, value: {left: ['100px', '200px']}},
{done: true},
]),
expect: [],
});
});
}, 'Custom iterator with value list in keyframe should throw.');
test(() => { test(() => {
assert_throws({name: 'TypeError'}, () => { assert_throws({name: 'TypeError'}, () => {
assertAnimationEffect({ assertAnimationEffect({
......
This is a testharness.js-based test. This is a testharness.js-based test.
Found 167 tests; 154 PASS, 13 FAIL, 0 TIMEOUT, 0 NOTRUN. Found 167 tests; 155 PASS, 12 FAIL, 0 TIMEOUT, 0 NOTRUN.
PASS A KeyframeEffect can be constructed with no frames PASS A KeyframeEffect can be constructed with no frames
PASS easing values are parsed correctly when passed to the KeyframeEffect constructor in KeyframeEffectOptions PASS easing values are parsed correctly when passed to the KeyframeEffect constructor in KeyframeEffectOptions
PASS Invalid easing values are correctly rejected when passed to the KeyframeEffect constructor in KeyframeEffectOptions PASS Invalid easing values are correctly rejected when passed to the KeyframeEffect constructor in KeyframeEffectOptions
...@@ -166,8 +166,6 @@ PASS Invalid KeyframeEffect option by an 'inherit' easing ...@@ -166,8 +166,6 @@ PASS Invalid KeyframeEffect option by an 'inherit' easing
PASS Invalid KeyframeEffect option by a variable easing PASS Invalid KeyframeEffect option by a variable easing
PASS Invalid KeyframeEffect option by a multi-value easing PASS Invalid KeyframeEffect option by a multi-value easing
PASS A KeyframeEffect constructed with null target PASS A KeyframeEffect constructed with null target
FAIL KeyframeEffect constructor propagates exceptions generated by accessing the options object assert_throws: function "() => { PASS KeyframeEffect constructor propagates exceptions generated by accessing the options object
new KeyframeEffect(target, { get left() { throw test_error }})
}" did not throw
Harness: the test ran to completion. Harness: the test ran to completion.
This is a testharness.js-based test.
Found 50 tests; 46 PASS, 4 FAIL, 0 TIMEOUT, 0 NOTRUN.
PASS non-animatable property 'animation' is not accessed when using a property-indexed keyframe object
PASS non-animatable property 'animationDelay' is not accessed when using a property-indexed keyframe object
PASS non-animatable property 'animationDirection' is not accessed when using a property-indexed keyframe object
PASS non-animatable property 'animationDuration' is not accessed when using a property-indexed keyframe object
PASS non-animatable property 'animationFillMode' is not accessed when using a property-indexed keyframe object
PASS non-animatable property 'animationIterationCount' is not accessed when using a property-indexed keyframe object
PASS non-animatable property 'animationName' is not accessed when using a property-indexed keyframe object
PASS non-animatable property 'animationPlayState' is not accessed when using a property-indexed keyframe object
PASS non-animatable property 'animationTimingFunction' is not accessed when using a property-indexed keyframe object
PASS non-animatable property 'transition' is not accessed when using a property-indexed keyframe object
PASS non-animatable property 'transitionDelay' is not accessed when using a property-indexed keyframe object
PASS non-animatable property 'transitionDuration' is not accessed when using a property-indexed keyframe object
PASS non-animatable property 'transitionProperty' is not accessed when using a property-indexed keyframe object
PASS non-animatable property 'transitionTimingFunction' is not accessed when using a property-indexed keyframe object
PASS non-animatable property 'display' is not accessed when using a property-indexed keyframe object
PASS non-animatable property 'unsupportedProperty' is not accessed when using a property-indexed keyframe object
PASS non-animatable property 'font-size' is not accessed when using a property-indexed keyframe object
PASS non-animatable property 'animation' is not accessed when using a keyframe sequence
PASS non-animatable property 'animationDelay' is not accessed when using a keyframe sequence
PASS non-animatable property 'animationDirection' is not accessed when using a keyframe sequence
PASS non-animatable property 'animationDuration' is not accessed when using a keyframe sequence
PASS non-animatable property 'animationFillMode' is not accessed when using a keyframe sequence
PASS non-animatable property 'animationIterationCount' is not accessed when using a keyframe sequence
PASS non-animatable property 'animationName' is not accessed when using a keyframe sequence
PASS non-animatable property 'animationPlayState' is not accessed when using a keyframe sequence
PASS non-animatable property 'animationTimingFunction' is not accessed when using a keyframe sequence
PASS non-animatable property 'transition' is not accessed when using a keyframe sequence
PASS non-animatable property 'transitionDelay' is not accessed when using a keyframe sequence
PASS non-animatable property 'transitionDuration' is not accessed when using a keyframe sequence
PASS non-animatable property 'transitionProperty' is not accessed when using a keyframe sequence
PASS non-animatable property 'transitionTimingFunction' is not accessed when using a keyframe sequence
PASS non-animatable property 'display' is not accessed when using a keyframe sequence
PASS non-animatable property 'unsupportedProperty' is not accessed when using a keyframe sequence
PASS non-animatable property 'font-size' is not accessed when using a keyframe sequence
PASS Equivalent property-indexed and sequenced keyframes: two properties with one value
PASS Equivalent property-indexed and sequenced keyframes: two properties with three values
PASS Equivalent property-indexed and sequenced keyframes: two properties with different numbers of values
PASS Equivalent property-indexed and sequenced keyframes: same easing applied to all keyframes
PASS Equivalent property-indexed and sequenced keyframes: same composite applied to all keyframes
PASS Keyframes are read from a custom iterator
PASS 'easing' and 'offset' are ignored on iterable objects
PASS Keyframes are read from a custom iterator with multiple properties specified
PASS Keyframes are read from a custom iterator with where an offset is specified
PASS Reading from a custom iterator that returns a non-object keyframe should throw
FAIL A list of values returned from a custom iterator should be ignored Failed to construct 'KeyframeEffect': Lists of values not permitted in array-form list of keyframes
PASS Only enumerable properties on keyframes are read
FAIL Only properties defined directly on keyframes are read assert_equals: properties on ComputedKeyframe #0 should match expected "composite,computedOffset,easing,offset,top" but got "composite,computedOffset,easing,height,left,offset,top"
PASS Only enumerable properties on property-indexed keyframes are read
FAIL Only properties defined directly on property-indexed keyframes are read assert_equals: properties on ComputedKeyframe #0 should match expected "composite,computedOffset,easing,offset,top" but got "composite,computedOffset,easing,left,offset,top"
FAIL Properties are read in ascending order by Unicode codepoint assert_array_equals: property access order property 3, expected "left" but got "marginLeft"
Harness: the test ran to completion.
...@@ -307,6 +307,20 @@ test(() => { ...@@ -307,6 +307,20 @@ test(() => {
}, 'Keyframes are read from a custom iterator with where an offset is' }, 'Keyframes are read from a custom iterator with where an offset is'
+ ' specified'); + ' specified');
test(() => {
const test_error = { name: 'test' };
const bad_keyframe = { get left() { throw test_error; } };
assert_throws(test_error, () => {
new KeyframeEffect(null, createIterable([
{ done: false, value: { left: '100px' } },
{ done: false, value: bad_keyframe },
{ done: false, value: { left: '200px' } },
{ done: true },
]));
});
}, 'If a keyframe throws for an animatable property, that exception should be'
+ ' propagated');
test(() => { test(() => {
assert_throws({ name: 'TypeError' }, () => { assert_throws({ name: 'TypeError' }, () => {
new KeyframeEffect(null, createIterable([ new KeyframeEffect(null, createIterable([
...@@ -319,6 +333,36 @@ test(() => { ...@@ -319,6 +333,36 @@ test(() => {
}, 'Reading from a custom iterator that returns a non-object keyframe' }, 'Reading from a custom iterator that returns a non-object keyframe'
+ ' should throw'); + ' should throw');
test(() => {
const effect = new KeyframeEffect(null, createIterable([
{ done: false, value: { left: '100px' } },
{ done: false }, // No value member; keyframe is undefined.
{ done: false, value: { left: '200px' } },
{ done: true },
]));
assert_frame_lists_equal(effect.getKeyframes(), [
{ left: '100px', offset: null, computedOffset: 0, easing: 'linear', composite: null },
{ offset: null, computedOffset: 0.5, easing: 'linear', composite: null },
{ left: '200px', offset: null, computedOffset: 1, easing: 'linear', composite: null },
]);
}, 'An undefined keyframe returned from a custom iterator should be treated as a'
+ ' default keyframe');
test(() => {
const effect = new KeyframeEffect(null, createIterable([
{ done: false, value: { left: '100px' } },
{ done: false, value: null },
{ done: false, value: { left: '200px' } },
{ done: true },
]));
assert_frame_lists_equal(effect.getKeyframes(), [
{ left: '100px', offset: null, computedOffset: 0, easing: 'linear', composite: null },
{ offset: null, computedOffset: 0.5, easing: 'linear', composite: null },
{ left: '200px', offset: null, computedOffset: 1, easing: 'linear', composite: null },
]);
}, 'A null keyframe returned from a custom iterator should be treated as a'
+ ' default keyframe');
test(() => { test(() => {
const effect = new KeyframeEffect(null, createIterable([ const effect = new KeyframeEffect(null, createIterable([
{ done: false, value: { left: ['100px', '200px'] } }, { done: false, value: { left: ['100px', '200px'] } },
...@@ -329,6 +373,44 @@ test(() => { ...@@ -329,6 +373,44 @@ test(() => {
]); ]);
}, 'A list of values returned from a custom iterator should be ignored'); }, 'A list of values returned from a custom iterator should be ignored');
test(() => {
const test_error = { name: 'test' };
const keyframe_obj = {
[Symbol.iterator]() {
return { next() { throw test_error; } };
},
};
assert_throws(test_error, () => {
new KeyframeEffect(null, keyframe_obj);
});
}, 'If a custom iterator throws from next(), the exception should be rethrown');
// Test handling of invalid Symbol.iterator
test(() => {
const test_error = { name: 'test' };
const keyframe_obj = {
[Symbol.iterator]() {
throw test_error;
},
};
assert_throws(test_error, () => {
new KeyframeEffect(null, keyframe_obj);
});
}, 'Accessing a Symbol.iterator property that throws should rethrow');
test(() => {
const keyframe_obj = {
[Symbol.iterator]() {
return 42; // Not an object.
},
};
assert_throws({ name: 'TypeError' }, () => {
new KeyframeEffect(null, keyframe_obj);
});
}, 'A non-object returned from the Symbol.iterator property should cause a'
+ ' TypeError to be thrown');
test(() => { test(() => {
const keyframe = {}; const keyframe = {};
Object.defineProperty(keyframe, 'width', { value: '200px' }); Object.defineProperty(keyframe, 'width', { value: '200px' });
......
<!DOCTYPE html>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script>
// http://crbug.com/644237
test(function() {
// Use Element.animate() that takes a dictionary.
let element = document.createElement('span');
let keyFrames = {};
Object.defineProperty(keyFrames, 'color', {
enumerable: true,
get: function() {
throw "This exception should be swallowed.";
}
});
element.animate(keyFrames);
}, "Dictionary member's [[Get]] may throw.");
done();
</script>
...@@ -34,8 +34,6 @@ bindings_core_v8_files = ...@@ -34,8 +34,6 @@ bindings_core_v8_files =
"core/v8/dictionary.cc", "core/v8/dictionary.cc",
"core/v8/dictionary.h", "core/v8/dictionary.h",
"core/v8/dictionary_helper_for_core.cc", "core/v8/dictionary_helper_for_core.cc",
"core/v8/dictionary_iterator.cc",
"core/v8/dictionary_iterator.h",
"core/v8/exception_messages.cc", "core/v8/exception_messages.cc",
"core/v8/exception_messages.h", "core/v8/exception_messages.h",
"core/v8/exception_state.cc", "core/v8/exception_state.cc",
...@@ -73,6 +71,8 @@ bindings_core_v8_files = ...@@ -73,6 +71,8 @@ bindings_core_v8_files =
"core/v8/script_event_listener.h", "core/v8/script_event_listener.h",
"core/v8/script_function.cc", "core/v8/script_function.cc",
"core/v8/script_function.h", "core/v8/script_function.h",
"core/v8/script_iterator.cc",
"core/v8/script_iterator.h",
"core/v8/script_module.cc", "core/v8/script_module.cc",
"core/v8/script_module.h", "core/v8/script_module.h",
"core/v8/script_promise.cc", "core/v8/script_promise.cc",
......
...@@ -73,23 +73,6 @@ bool Dictionary::HasProperty(const StringView& key, ...@@ -73,23 +73,6 @@ bool Dictionary::HasProperty(const StringView& key,
return has_key; return has_key;
} }
DictionaryIterator Dictionary::GetIterator(
ExecutionContext* execution_context) const {
v8::Local<v8::Value> iterator_getter;
if (!GetInternal(v8::Symbol::GetIterator(isolate_), iterator_getter) ||
!iterator_getter->IsFunction())
return nullptr;
v8::Local<v8::Value> iterator;
if (!V8ScriptRunner::CallFunction(
v8::Local<v8::Function>::Cast(iterator_getter), execution_context,
dictionary_object_, 0, nullptr, isolate_)
.ToLocal(&iterator))
return nullptr;
if (!iterator->IsObject())
return nullptr;
return DictionaryIterator(v8::Local<v8::Object>::Cast(iterator), isolate_);
}
bool Dictionary::Get(const StringView& key, Dictionary& value) const { bool Dictionary::Get(const StringView& key, Dictionary& value) const {
v8::Local<v8::Value> v8_value; v8::Local<v8::Value> v8_value;
if (!Get(key, v8_value)) if (!Get(key, v8_value))
...@@ -107,8 +90,8 @@ bool Dictionary::Get(const StringView& key, Dictionary& value) const { ...@@ -107,8 +90,8 @@ bool Dictionary::Get(const StringView& key, Dictionary& value) const {
return true; return true;
} }
bool Dictionary::GetInternal(const v8::Local<v8::Value>& key, bool Dictionary::Get(v8::Local<v8::Value> key,
v8::Local<v8::Value>& result) const { v8::Local<v8::Value>& result) const {
if (dictionary_object_.IsEmpty()) if (dictionary_object_.IsEmpty())
return false; return false;
......
...@@ -26,7 +26,6 @@ ...@@ -26,7 +26,6 @@
#ifndef THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_DICTIONARY_H_ #ifndef THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_DICTIONARY_H_
#define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_DICTIONARY_H_ #define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_DICTIONARY_H_
#include "third_party/blink/renderer/bindings/core/v8/dictionary_iterator.h"
#include "third_party/blink/renderer/bindings/core/v8/exception_state.h" #include "third_party/blink/renderer/bindings/core/v8/exception_state.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/core_export.h"
...@@ -37,8 +36,6 @@ ...@@ -37,8 +36,6 @@
namespace blink { namespace blink {
class ExecutionContext;
// Dictionary class provides ways to retrieve property values as C++ objects // Dictionary class provides ways to retrieve property values as C++ objects
// from a V8 object. Instances of this class must not outlive V8's handle scope // from a V8 object. Instances of this class must not outlive V8's handle scope
// because they hold a V8 value without putting it on persistent handles. // because they hold a V8 value without putting it on persistent handles.
...@@ -73,7 +70,7 @@ class CORE_EXPORT Dictionary final { ...@@ -73,7 +70,7 @@ class CORE_EXPORT Dictionary final {
} }
bool Get(const StringView& key, v8::Local<v8::Value>& value) const { bool Get(const StringView& key, v8::Local<v8::Value>& value) const {
return isolate_ && GetInternal(V8String(isolate_, key), value); return isolate_ && Get(V8String(isolate_, key), value);
} }
bool Get(const StringView& key, bool Get(const StringView& key,
v8::Local<v8::Value>& value, v8::Local<v8::Value>& value,
...@@ -112,6 +109,8 @@ class CORE_EXPORT Dictionary final { ...@@ -112,6 +109,8 @@ class CORE_EXPORT Dictionary final {
return value; return value;
} }
bool Get(v8::Local<v8::Value> key, v8::Local<v8::Value>& result) const;
HashMap<String, String> GetOwnPropertiesAsStringHashMap( HashMap<String, String> GetOwnPropertiesAsStringHashMap(
ExceptionState&) const; ExceptionState&) const;
Vector<String> GetPropertyNames(ExceptionState&) const; Vector<String> GetPropertyNames(ExceptionState&) const;
...@@ -124,11 +123,7 @@ class CORE_EXPORT Dictionary final { ...@@ -124,11 +123,7 @@ class CORE_EXPORT Dictionary final {
return isolate_->GetCurrentContext(); return isolate_->GetCurrentContext();
} }
DictionaryIterator GetIterator(ExecutionContext*) const;
private: private:
bool GetInternal(const v8::Local<v8::Value>& key,
v8::Local<v8::Value>& result) const;
bool GetInternal(const v8::Local<v8::Value>& key, bool GetInternal(const v8::Local<v8::Value>& key,
v8::Local<v8::Value>& result, v8::Local<v8::Value>& result,
ExceptionState&) const; ExceptionState&) const;
......
...@@ -114,6 +114,34 @@ TEST_F(V8DictionaryTest, Get_ExceptionOnAccess) { ...@@ -114,6 +114,34 @@ TEST_F(V8DictionaryTest, Get_ExceptionOnAccess) {
ASSERT_FALSE(r.has_value()); ASSERT_FALSE(r.has_value());
} }
// TODO(bashi,yukishiino): Should rethrow the exception.
// http://crbug.com/666661
TEST_F(V8DictionaryTest, Get_ExceptionOnAccess2) {
V8TestingScope scope;
Dictionary dictionary = CreateDictionary(scope.GetScriptState(),
"({get foo() { throw Error(2); }})");
v8::Local<v8::Value> value;
v8::TryCatch try_catch(scope.GetIsolate());
ASSERT_FALSE(dictionary.Get("foo", value));
ASSERT_FALSE(try_catch.HasCaught());
}
// TODO(bashi,yukishiino): Should rethrow the exception.
// http://crbug.com/666661
TEST_F(V8DictionaryTest, Get_InvalidInnerDictionary) {
V8TestingScope scope;
Dictionary dictionary =
CreateDictionary(scope.GetScriptState(), "({foo: 4})");
v8::TryCatch try_catch(scope.GetIsolate());
Dictionary inner_dictionary;
ASSERT_TRUE(dictionary.Get("foo", inner_dictionary));
ASSERT_FALSE(try_catch.HasCaught());
EXPECT_TRUE(inner_dictionary.IsUndefinedOrNull());
}
TEST_F(V8DictionaryTest, Get_TypeConversion) { TEST_F(V8DictionaryTest, Get_TypeConversion) {
V8TestingScope scope; V8TestingScope scope;
Dictionary dictionary = CreateDictionary( Dictionary dictionary = CreateDictionary(
......
...@@ -2,19 +2,17 @@ ...@@ -2,19 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "third_party/blink/renderer/bindings/core/v8/dictionary_iterator.h" #include "third_party/blink/renderer/bindings/core/v8/script_iterator.h"
#include "third_party/blink/renderer/bindings/core/v8/dictionary.h"
#include "third_party/blink/renderer/bindings/core/v8/exception_state.h" #include "third_party/blink/renderer/bindings/core/v8/exception_state.h"
#include "third_party/blink/renderer/bindings/core/v8/script_controller.h" #include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_string_resource.h" #include "third_party/blink/renderer/bindings/core/v8/v8_string_resource.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink { namespace blink {
DictionaryIterator::DictionaryIterator(v8::Local<v8::Object> iterator, ScriptIterator::ScriptIterator(v8::Local<v8::Object> iterator,
v8::Isolate* isolate) v8::Isolate* isolate)
: isolate_(isolate), : isolate_(isolate),
iterator_(iterator), iterator_(iterator),
next_key_(V8AtomicString(isolate, "next")), next_key_(V8AtomicString(isolate, "next")),
...@@ -24,9 +22,9 @@ DictionaryIterator::DictionaryIterator(v8::Local<v8::Object> iterator, ...@@ -24,9 +22,9 @@ DictionaryIterator::DictionaryIterator(v8::Local<v8::Object> iterator,
DCHECK(!iterator.IsEmpty()); DCHECK(!iterator.IsEmpty());
} }
bool DictionaryIterator::Next(ExecutionContext* execution_context, bool ScriptIterator::Next(ExecutionContext* execution_context,
ExceptionState& exception_state, ExceptionState& exception_state,
v8::Local<v8::Value> next_value) { v8::Local<v8::Value> next_value) {
DCHECK(!IsNull()); DCHECK(!IsNull());
v8::TryCatch try_catch(isolate_); v8::TryCatch try_catch(isolate_);
...@@ -87,32 +85,4 @@ bool DictionaryIterator::Next(ExecutionContext* execution_context, ...@@ -87,32 +85,4 @@ bool DictionaryIterator::Next(ExecutionContext* execution_context,
return !done_; return !done_;
} }
bool DictionaryIterator::ValueAsDictionary(Dictionary& result,
ExceptionState& exception_state) {
DCHECK(!IsNull());
DCHECK(!done_);
v8::Local<v8::Value> value;
if (!value_.ToLocal(&value) || !value->IsObject())
return false;
result = Dictionary(isolate_, value, exception_state);
return true;
}
bool DictionaryIterator::ValueAsString(String& result) {
DCHECK(!IsNull());
DCHECK(!done_);
v8::Local<v8::Value> value;
if (!value_.ToLocal(&value))
return false;
V8StringResource<> string_value(value);
if (!string_value.Prepare())
return false;
result = string_value;
return true;
}
} // namespace blink } // namespace blink
...@@ -2,32 +2,25 @@ ...@@ -2,32 +2,25 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_DICTIONARY_ITERATOR_H_ #ifndef THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SCRIPT_ITERATOR_H_
#define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_DICTIONARY_ITERATOR_H_ #define THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SCRIPT_ITERATOR_H_
#include "third_party/blink/renderer/core/core_export.h" #include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/platform/wtf/allocator.h" #include "third_party/blink/renderer/platform/wtf/allocator.h"
#include "v8/include/v8.h" #include "v8/include/v8.h"
namespace WTF {
class String;
} // namespace WTF
namespace blink { namespace blink {
class Dictionary;
class ExceptionState; class ExceptionState;
class ExecutionContext; class ExecutionContext;
class CORE_EXPORT DictionaryIterator { class CORE_EXPORT ScriptIterator {
STACK_ALLOCATED(); STACK_ALLOCATED();
public: public:
DictionaryIterator(std::nullptr_t) : isolate_(nullptr), done_(true) {} ScriptIterator(std::nullptr_t) : isolate_(nullptr), done_(true) {}
DictionaryIterator(v8::Local<v8::Object> iterator, v8::Isolate*); ScriptIterator(v8::Local<v8::Object> iterator, v8::Isolate*);
bool IsNull() const { return iterator_.IsEmpty(); } bool IsNull() const { return iterator_.IsEmpty(); }
...@@ -37,8 +30,6 @@ class CORE_EXPORT DictionaryIterator { ...@@ -37,8 +30,6 @@ class CORE_EXPORT DictionaryIterator {
v8::Local<v8::Value> next_value = v8::Local<v8::Value>()); v8::Local<v8::Value> next_value = v8::Local<v8::Value>());
v8::MaybeLocal<v8::Value> GetValue() { return value_; } v8::MaybeLocal<v8::Value> GetValue() { return value_; }
bool ValueAsDictionary(Dictionary& result, ExceptionState&);
bool ValueAsString(WTF::String& result);
private: private:
v8::Isolate* isolate_; v8::Isolate* isolate_;
...@@ -52,4 +43,4 @@ class CORE_EXPORT DictionaryIterator { ...@@ -52,4 +43,4 @@ class CORE_EXPORT DictionaryIterator {
} // namespace blink } // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_DICTIONARY_ITERATOR_H_ #endif // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_SCRIPT_ITERATOR_H_
...@@ -31,6 +31,8 @@ ...@@ -31,6 +31,8 @@
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/bindings/core/v8/custom/v8_custom_xpath_ns_resolver.h" #include "third_party/blink/renderer/bindings/core/v8/custom/v8_custom_xpath_ns_resolver.h"
#include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
#include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
#include "third_party/blink/renderer/bindings/core/v8/script_controller.h" #include "third_party/blink/renderer/bindings/core/v8/script_controller.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_abstract_event_listener.h" #include "third_party/blink/renderer/bindings/core/v8/v8_abstract_event_listener.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_array_buffer_view.h" #include "third_party/blink/renderer/bindings/core/v8/v8_array_buffer_view.h"
...@@ -788,27 +790,38 @@ bool IsValidEnum(const Vector<String>& values, ...@@ -788,27 +790,38 @@ bool IsValidEnum(const Vector<String>& values,
return true; return true;
} }
v8::Local<v8::Object> GetEsIterator(v8::Isolate* isolate, v8::Local<v8::Function> GetEsIteratorMethod(v8::Isolate* isolate,
v8::Local<v8::Object> object, v8::Local<v8::Object> object,
ExceptionState& exception_state) { ExceptionState& exception_state) {
v8::TryCatch block(isolate); const v8::Local<v8::Value> key = v8::Symbol::GetIterator(isolate);
v8::Local<v8::Context> context = isolate->GetCurrentContext();
v8::Local<v8::Value> iterator_getter; v8::TryCatch try_catch(isolate);
if (!object->Get(context, v8::Symbol::GetIterator(isolate)) v8::Local<v8::Value> iterator_method;
.ToLocal(&iterator_getter)) { if (!object->Get(isolate->GetCurrentContext(), key)
exception_state.RethrowV8Exception(block.Exception()); .ToLocal(&iterator_method)) {
return v8::Local<v8::Object>(); exception_state.RethrowV8Exception(try_catch.Exception());
} return v8::Local<v8::Function>();
if (!iterator_getter->IsFunction()) {
exception_state.ThrowTypeError("Iterator getter is not callable.");
return v8::Local<v8::Object>();
} }
v8::Local<v8::Function> getter_function = iterator_getter.As<v8::Function>(); if (iterator_method->IsNullOrUndefined())
return v8::Local<v8::Function>();
if (!iterator_method->IsFunction())
exception_state.ThrowTypeError("Iterator must be callable function");
return iterator_method.As<v8::Function>();
}
v8::Local<v8::Object> GetEsIteratorWithMethod(
v8::Isolate* isolate,
v8::Local<v8::Function> getter_function,
v8::Local<v8::Object> object,
ExceptionState& exception_state) {
v8::TryCatch block(isolate);
v8::Local<v8::Value> iterator; v8::Local<v8::Value> iterator;
if (!V8ScriptRunner::CallFunction(getter_function, if (!V8ScriptRunner::CallFunction(
ToExecutionContext(context), object, 0, getter_function, ToExecutionContext(isolate->GetCurrentContext()),
nullptr, isolate) object, 0, nullptr, isolate)
.ToLocal(&iterator)) { .ToLocal(&iterator)) {
exception_state.RethrowV8Exception(block.Exception()); exception_state.RethrowV8Exception(block.Exception());
return v8::Local<v8::Object>(); return v8::Local<v8::Object>();
...@@ -820,6 +833,23 @@ v8::Local<v8::Object> GetEsIterator(v8::Isolate* isolate, ...@@ -820,6 +833,23 @@ v8::Local<v8::Object> GetEsIterator(v8::Isolate* isolate,
return iterator.As<v8::Object>(); return iterator.As<v8::Object>();
} }
v8::Local<v8::Object> GetEsIterator(v8::Isolate* isolate,
v8::Local<v8::Object> object,
ExceptionState& exception_state) {
v8::Local<v8::Function> iterator_getter =
GetEsIteratorMethod(isolate, object, exception_state);
if (exception_state.HadException())
return v8::Local<v8::Object>();
if (iterator_getter.IsEmpty()) {
exception_state.ThrowTypeError("Iterator getter is not callable.");
return v8::Local<v8::Object>();
}
return GetEsIteratorWithMethod(isolate, iterator_getter, object,
exception_state);
}
bool HasCallableIteratorSymbol(v8::Isolate* isolate, bool HasCallableIteratorSymbol(v8::Isolate* isolate,
v8::Local<v8::Value> value, v8::Local<v8::Value> value,
ExceptionState& exception_state) { ExceptionState& exception_state) {
...@@ -863,4 +893,22 @@ v8::Local<v8::Value> FromJSONString(v8::Isolate* isolate, ...@@ -863,4 +893,22 @@ v8::Local<v8::Value> FromJSONString(v8::Isolate* isolate,
return parsed; return parsed;
} }
Vector<String> GetOwnPropertyNames(v8::Isolate* isolate,
const v8::Local<v8::Object>& object,
ExceptionState& exception_state) {
if (object.IsEmpty())
return Vector<String>();
v8::TryCatch try_catch(isolate);
v8::Local<v8::Array> property_names;
if (!object->GetOwnPropertyNames(isolate->GetCurrentContext())
.ToLocal(&property_names)) {
exception_state.RethrowV8Exception(try_catch.Exception());
return Vector<String>();
}
return NativeValueTraits<IDLSequence<IDLString>>::NativeValue(
isolate, property_names, exception_state);
}
} // namespace blink } // namespace blink
...@@ -418,6 +418,19 @@ VectorOf<typename NativeValueTraits<IDLType>::ImplType> ToImplArguments( ...@@ -418,6 +418,19 @@ VectorOf<typename NativeValueTraits<IDLType>::ImplType> ToImplArguments(
return result; return result;
} }
// Returns the iterator method for an object, or an empty v8::Local if the
// method is null or undefined.
CORE_EXPORT v8::Local<v8::Function> GetEsIteratorMethod(v8::Isolate*,
v8::Local<v8::Object>,
ExceptionState&);
// Gets an iterator for an object, given the iterator method for that object.
CORE_EXPORT v8::Local<v8::Object> GetEsIteratorWithMethod(
v8::Isolate*,
v8::Local<v8::Function>,
v8::Local<v8::Object>,
ExceptionState&);
// Gets an iterator from an Object. // Gets an iterator from an Object.
CORE_EXPORT v8::Local<v8::Object> GetEsIterator(v8::Isolate*, CORE_EXPORT v8::Local<v8::Object> GetEsIterator(v8::Isolate*,
v8::Local<v8::Object>, v8::Local<v8::Object>,
...@@ -524,6 +537,10 @@ MaybeSharedType ToMaybeShared(v8::Isolate* isolate, ...@@ -524,6 +537,10 @@ MaybeSharedType ToMaybeShared(v8::Isolate* isolate,
return MaybeSharedType(dom_typed_array); return MaybeSharedType(dom_typed_array);
} }
CORE_EXPORT Vector<String> GetOwnPropertyNames(v8::Isolate*,
const v8::Local<v8::Object>&,
ExceptionState&);
} // namespace blink } // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_V8_BINDING_FOR_CORE_H_ #endif // THIRD_PARTY_BLINK_RENDERER_BINDINGS_CORE_V8_V8_BINDING_FOR_CORE_H_
...@@ -5,9 +5,9 @@ ...@@ -5,9 +5,9 @@
#include "third_party/blink/renderer/core/layout/custom/css_layout_definition.h" #include "third_party/blink/renderer/core/layout/custom/css_layout_definition.h"
#include <memory> #include <memory>
#include "third_party/blink/renderer/bindings/core/v8/dictionary_iterator.h"
#include "third_party/blink/renderer/bindings/core/v8/idl_types.h" #include "third_party/blink/renderer/bindings/core/v8/idl_types.h"
#include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h" #include "third_party/blink/renderer/bindings/core/v8/native_value_traits_impl.h"
#include "third_party/blink/renderer/bindings/core/v8/script_iterator.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_fragment_result_options.h" #include "third_party/blink/renderer/bindings/core/v8/v8_fragment_result_options.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_layout_fragment_request.h" #include "third_party/blink/renderer/bindings/core/v8/v8_layout_fragment_request.h"
...@@ -121,7 +121,7 @@ bool CSSLayoutDefinition::Instance::Layout( ...@@ -121,7 +121,7 @@ bool CSSLayoutDefinition::Instance::Layout(
v8::Local<v8::Object> generator = v8::Local<v8::Object> generator =
v8::Local<v8::Object>::Cast(generator_value); v8::Local<v8::Object>::Cast(generator_value);
DictionaryIterator iterator(generator, isolate); ScriptIterator iterator(generator, isolate);
v8::Local<v8::Value> next_value; v8::Local<v8::Value> next_value;
// We run the generator until it's exhausted. // We run the generator until it's exhausted.
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "third_party/blink/renderer/core/testing/dictionary_test.h" #include "third_party/blink/renderer/core/testing/dictionary_test.h"
#include "third_party/blink/renderer/bindings/core/v8/script_iterator.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h" #include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h" #include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/testing/internal_dictionary.h" #include "third_party/blink/renderer/core/testing/internal_dictionary.h"
...@@ -12,6 +13,26 @@ ...@@ -12,6 +13,26 @@
#include "third_party/blink/renderer/platform/bindings/script_state.h" #include "third_party/blink/renderer/platform/bindings/script_state.h"
namespace blink { namespace blink {
namespace {
ScriptIterator GetIterator(const Dictionary& iterable,
ExecutionContext* execution_context) {
v8::Local<v8::Value> iterator_getter;
v8::Isolate* isolate = iterable.GetIsolate();
if (!iterable.Get(v8::Symbol::GetIterator(isolate), iterator_getter) ||
!iterator_getter->IsFunction()) {
return nullptr;
}
v8::Local<v8::Value> iterator;
if (!V8ScriptRunner::CallFunction(
v8::Local<v8::Function>::Cast(iterator_getter), execution_context,
iterable.V8Value(), 0, nullptr, isolate)
.ToLocal(&iterator))
return nullptr;
if (!iterator->IsObject())
return nullptr;
return ScriptIterator(v8::Local<v8::Object>::Cast(iterator), isolate);
}
} // namespace
DictionaryTest::DictionaryTest() : required_boolean_member_(false) {} DictionaryTest::DictionaryTest() : required_boolean_member_(false) {}
...@@ -183,7 +204,7 @@ String DictionaryTest::stringFromIterable( ...@@ -183,7 +204,7 @@ String DictionaryTest::stringFromIterable(
ExceptionState& exception_state) const { ExceptionState& exception_state) const {
StringBuilder result; StringBuilder result;
ExecutionContext* execution_context = ExecutionContext::From(script_state); ExecutionContext* execution_context = ExecutionContext::From(script_state);
DictionaryIterator iterator = iterable.GetIterator(execution_context); ScriptIterator iterator = GetIterator(iterable, execution_context);
if (iterator.IsNull()) if (iterator.IsNull())
return g_empty_string; return g_empty_string;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment