Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / bindings / core / v8 / ScriptPromisePropertyTest.cpp
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "config.h"
6 #include "bindings/core/v8/ScriptPromiseProperty.h"
7
8 #include "bindings/core/v8/DOMWrapperWorld.h"
9 #include "bindings/core/v8/ScriptFunction.h"
10 #include "bindings/core/v8/ScriptPromise.h"
11 #include "bindings/core/v8/ScriptState.h"
12 #include "bindings/core/v8/ScriptValue.h"
13 #include "bindings/core/v8/V8Binding.h"
14 #include "bindings/core/v8/V8GCController.h"
15 #include "core/dom/Document.h"
16 #include "core/testing/DummyPageHolder.h"
17 #include "core/testing/GCObservation.h"
18 #include "core/testing/GarbageCollectedScriptWrappable.h"
19 #include "core/testing/RefCountedScriptWrappable.h"
20 #include "platform/heap/Handle.h"
21 #include "wtf/OwnPtr.h"
22 #include "wtf/PassOwnPtr.h"
23 #include "wtf/PassRefPtr.h"
24 #include "wtf/RefPtr.h"
25 #include <gtest/gtest.h>
26 #include <v8.h>
27
28 using namespace blink;
29
30 namespace {
31
32 class NotReached : public ScriptFunction {
33 public:
34     static v8::Handle<v8::Function> createFunction(ScriptState* scriptState)
35     {
36         NotReached* self = new NotReached(scriptState);
37         return self->bindToV8Function();
38     }
39
40 private:
41     explicit NotReached(ScriptState* scriptState)
42         : ScriptFunction(scriptState)
43     {
44     }
45
46     virtual ScriptValue call(ScriptValue) OVERRIDE;
47 };
48
49 ScriptValue NotReached::call(ScriptValue)
50 {
51     EXPECT_TRUE(false) << "'Unreachable' code was reached";
52     return ScriptValue();
53 }
54
55 class StubFunction : public ScriptFunction {
56 public:
57     static v8::Handle<v8::Function> createFunction(ScriptState* scriptState, ScriptValue& value, size_t& callCount)
58     {
59         StubFunction* self = new StubFunction(scriptState, value, callCount);
60         return self->bindToV8Function();
61     }
62
63 private:
64     StubFunction(ScriptState* scriptState, ScriptValue& value, size_t& callCount)
65         : ScriptFunction(scriptState)
66         , m_value(value)
67         , m_callCount(callCount)
68     {
69     }
70
71     virtual ScriptValue call(ScriptValue arg) OVERRIDE
72     {
73         m_value = arg;
74         m_callCount++;
75         return ScriptValue();
76     }
77
78     ScriptValue& m_value;
79     size_t& m_callCount;
80 };
81
82 class GarbageCollectedHolder : public GarbageCollectedScriptWrappable {
83 public:
84     typedef ScriptPromiseProperty<Member<GarbageCollectedScriptWrappable>, Member<GarbageCollectedScriptWrappable>, Member<GarbageCollectedScriptWrappable> > Property;
85     GarbageCollectedHolder(ExecutionContext* executionContext)
86         : GarbageCollectedScriptWrappable("holder")
87         , m_property(new Property(executionContext, toGarbageCollectedScriptWrappable(), Property::Ready)) { }
88
89     Property* property() { return m_property; }
90     GarbageCollectedScriptWrappable* toGarbageCollectedScriptWrappable() { return this; }
91
92     virtual void trace(Visitor *visitor) OVERRIDE
93     {
94         GarbageCollectedScriptWrappable::trace(visitor);
95         visitor->trace(m_property);
96     }
97
98 private:
99     Member<Property> m_property;
100 };
101
102 class RefCountedHolder : public RefCountedScriptWrappable {
103 public:
104     // Do not resolve or reject the property with the holder itself. It leads
105     // to a leak.
106     typedef ScriptPromiseProperty<RefCountedScriptWrappable*, RefPtr<RefCountedScriptWrappable>, RefPtr<RefCountedScriptWrappable> > Property;
107     static PassRefPtr<RefCountedHolder> create(ExecutionContext* executionContext)
108     {
109         return adoptRef(new RefCountedHolder(executionContext));
110     }
111     Property* property() { return m_property; }
112     RefCountedScriptWrappable* toRefCountedScriptWrappable() { return this; }
113
114 private:
115     RefCountedHolder(ExecutionContext* executionContext)
116         : RefCountedScriptWrappable("holder")
117         , m_property(new Property(executionContext, toRefCountedScriptWrappable(), Property::Ready)) { }
118
119     Persistent<Property> m_property;
120 };
121
122 class ScriptPromisePropertyTestBase {
123 public:
124     ScriptPromisePropertyTestBase()
125         : m_page(DummyPageHolder::create(IntSize(1, 1)))
126     {
127         v8::HandleScope handleScope(isolate());
128         m_otherScriptState = ScriptStateForTesting::create(v8::Context::New(isolate()), DOMWrapperWorld::create(1));
129     }
130
131     virtual ~ScriptPromisePropertyTestBase()
132     {
133         m_page.clear();
134         gc();
135         Heap::collectAllGarbage();
136     }
137
138     Document& document() { return m_page->document(); }
139     v8::Isolate* isolate() { return toIsolate(&document()); }
140     ScriptState* mainScriptState() { return ScriptState::forMainWorld(document().frame()); }
141     DOMWrapperWorld& mainWorld() { return mainScriptState()->world(); }
142     ScriptState* otherScriptState() { return m_otherScriptState.get(); }
143     DOMWrapperWorld& otherWorld() { return m_otherScriptState->world(); }
144     ScriptState* currentScriptState() { return ScriptState::current(isolate()); }
145
146     virtual void destroyContext()
147     {
148         m_page.clear();
149         m_otherScriptState.clear();
150         gc();
151         Heap::collectGarbage(ThreadState::HeapPointersOnStack);
152     }
153
154     void gc() { V8GCController::collectGarbage(v8::Isolate::GetCurrent()); }
155
156     v8::Handle<v8::Function> notReached(ScriptState* scriptState) { return NotReached::createFunction(scriptState); }
157     v8::Handle<v8::Function> stub(ScriptState* scriptState, ScriptValue& value, size_t& callCount) { return StubFunction::createFunction(scriptState, value, callCount); }
158
159     template <typename T>
160     ScriptValue wrap(DOMWrapperWorld& world, const T& value)
161     {
162         v8::HandleScope handleScope(isolate());
163         ScriptState* scriptState = ScriptState::from(toV8Context(&document(), world));
164         ScriptState::Scope scope(scriptState);
165         return ScriptValue(scriptState, V8ValueTraits<T>::toV8Value(value, scriptState->context()->Global(), isolate()));
166     }
167
168 private:
169     OwnPtr<DummyPageHolder> m_page;
170     RefPtr<ScriptState> m_otherScriptState;
171 };
172
173 // This is the main test class.
174 // If you want to examine a testcase independent of holder types, place the
175 // test on this class.
176 class ScriptPromisePropertyGarbageCollectedTest : public ScriptPromisePropertyTestBase, public ::testing::Test {
177 public:
178     typedef GarbageCollectedHolder::Property Property;
179
180     ScriptPromisePropertyGarbageCollectedTest()
181         : m_holder(new GarbageCollectedHolder(&document()))
182     {
183     }
184
185     GarbageCollectedHolder* holder() { return m_holder; }
186     Property* property() { return m_holder->property(); }
187     ScriptPromise promise(DOMWrapperWorld& world) { return property()->promise(world); }
188
189     virtual void destroyContext() OVERRIDE
190     {
191         m_holder = nullptr;
192         ScriptPromisePropertyTestBase::destroyContext();
193     }
194
195 private:
196     Persistent<GarbageCollectedHolder> m_holder;
197 };
198
199 TEST_F(ScriptPromisePropertyGarbageCollectedTest, Promise_IsStableObjectInMainWorld)
200 {
201     ScriptPromise v = property()->promise(DOMWrapperWorld::mainWorld());
202     ScriptPromise w = property()->promise(DOMWrapperWorld::mainWorld());
203     EXPECT_EQ(v, w);
204     ASSERT_FALSE(v.isEmpty());
205     {
206         ScriptState::Scope scope(mainScriptState());
207         EXPECT_EQ(v.v8Value().As<v8::Object>()->CreationContext(), toV8Context(&document(), mainWorld()));
208     }
209     EXPECT_EQ(Property::Pending, property()->state());
210 }
211
212 TEST_F(ScriptPromisePropertyGarbageCollectedTest, Promise_IsStableObjectInVariousWorlds)
213 {
214     ScriptPromise u = property()->promise(otherWorld());
215     ScriptPromise v = property()->promise(DOMWrapperWorld::mainWorld());
216     ScriptPromise w = property()->promise(DOMWrapperWorld::mainWorld());
217     EXPECT_NE(mainScriptState(), otherScriptState());
218     EXPECT_NE(&mainWorld(), &otherWorld());
219     EXPECT_NE(u, v);
220     EXPECT_EQ(v, w);
221     ASSERT_FALSE(u.isEmpty());
222     ASSERT_FALSE(v.isEmpty());
223     {
224         ScriptState::Scope scope(otherScriptState());
225         EXPECT_EQ(u.v8Value().As<v8::Object>()->CreationContext(), toV8Context(&document(), otherWorld()));
226     }
227     {
228         ScriptState::Scope scope(mainScriptState());
229         EXPECT_EQ(v.v8Value().As<v8::Object>()->CreationContext(), toV8Context(&document(), mainWorld()));
230     }
231     EXPECT_EQ(Property::Pending, property()->state());
232 }
233
234 TEST_F(ScriptPromisePropertyGarbageCollectedTest, Promise_IsStableObjectAfterSettling)
235 {
236     ScriptPromise v = promise(DOMWrapperWorld::mainWorld());
237     GarbageCollectedScriptWrappable* value = new GarbageCollectedScriptWrappable("value");
238
239     property()->resolve(value);
240     EXPECT_EQ(Property::Resolved, property()->state());
241
242     ScriptPromise w = promise(DOMWrapperWorld::mainWorld());
243     EXPECT_EQ(v, w);
244     EXPECT_FALSE(v.isEmpty());
245 }
246
247 TEST_F(ScriptPromisePropertyGarbageCollectedTest, Promise_DoesNotImpedeGarbageCollection)
248 {
249     ScriptValue holderWrapper = wrap(mainWorld(), holder()->toGarbageCollectedScriptWrappable());
250
251     Persistent<GCObservation> observation;
252     {
253         ScriptState::Scope scope(mainScriptState());
254         observation = GCObservation::create(promise(DOMWrapperWorld::mainWorld()).v8Value());
255     }
256
257     gc();
258     EXPECT_FALSE(observation->wasCollected());
259
260     holderWrapper.clear();
261     gc();
262     EXPECT_TRUE(observation->wasCollected());
263
264     EXPECT_EQ(Property::Pending, property()->state());
265 }
266
267 TEST_F(ScriptPromisePropertyGarbageCollectedTest, Resolve_ResolvesScriptPromise)
268 {
269     ScriptPromise promise = property()->promise(DOMWrapperWorld::mainWorld());
270     ScriptPromise otherPromise = property()->promise(otherWorld());
271     ScriptValue actual, otherActual;
272     size_t nResolveCalls = 0;
273     size_t nOtherResolveCalls = 0;
274
275     {
276         ScriptState::Scope scope(mainScriptState());
277         promise.then(stub(currentScriptState(), actual, nResolveCalls), notReached(currentScriptState()));
278     }
279
280     {
281         ScriptState::Scope scope(otherScriptState());
282         otherPromise.then(stub(currentScriptState(), otherActual, nOtherResolveCalls), notReached(currentScriptState()));
283     }
284
285     EXPECT_NE(promise, otherPromise);
286
287     GarbageCollectedScriptWrappable* value = new GarbageCollectedScriptWrappable("value");
288     property()->resolve(value);
289     EXPECT_EQ(Property::Resolved, property()->state());
290
291     isolate()->RunMicrotasks();
292     EXPECT_EQ(1u, nResolveCalls);
293     EXPECT_EQ(1u, nOtherResolveCalls);
294     EXPECT_EQ(wrap(mainWorld(), value), actual);
295     EXPECT_NE(actual, otherActual);
296     EXPECT_EQ(wrap(otherWorld(), value), otherActual);
297 }
298
299 TEST_F(ScriptPromisePropertyGarbageCollectedTest, ResolveAndGetPromiseOnOtherWorld)
300 {
301     ScriptPromise promise = property()->promise(DOMWrapperWorld::mainWorld());
302     ScriptPromise otherPromise = property()->promise(otherWorld());
303     ScriptValue actual, otherActual;
304     size_t nResolveCalls = 0;
305     size_t nOtherResolveCalls = 0;
306
307     {
308         ScriptState::Scope scope(mainScriptState());
309         promise.then(stub(currentScriptState(), actual, nResolveCalls), notReached(currentScriptState()));
310     }
311
312     EXPECT_NE(promise, otherPromise);
313     GarbageCollectedScriptWrappable* value = new GarbageCollectedScriptWrappable("value");
314     property()->resolve(value);
315     EXPECT_EQ(Property::Resolved, property()->state());
316
317     isolate()->RunMicrotasks();
318     EXPECT_EQ(1u, nResolveCalls);
319     EXPECT_EQ(0u, nOtherResolveCalls);
320
321     {
322         ScriptState::Scope scope(otherScriptState());
323         otherPromise.then(stub(currentScriptState(), otherActual, nOtherResolveCalls), notReached(currentScriptState()));
324     }
325
326     isolate()->RunMicrotasks();
327     EXPECT_EQ(1u, nResolveCalls);
328     EXPECT_EQ(1u, nOtherResolveCalls);
329     EXPECT_EQ(wrap(mainWorld(), value), actual);
330     EXPECT_NE(actual, otherActual);
331     EXPECT_EQ(wrap(otherWorld(), value), otherActual);
332 }
333
334 TEST_F(ScriptPromisePropertyGarbageCollectedTest, Reject_RejectsScriptPromise)
335 {
336     GarbageCollectedScriptWrappable* reason = new GarbageCollectedScriptWrappable("reason");
337     property()->reject(reason);
338     EXPECT_EQ(Property::Rejected, property()->state());
339
340     ScriptValue actual, otherActual;
341     size_t nRejectCalls = 0;
342     size_t nOtherRejectCalls = 0;
343     {
344         ScriptState::Scope scope(mainScriptState());
345         property()->promise(DOMWrapperWorld::mainWorld()).then(notReached(currentScriptState()), stub(currentScriptState(), actual, nRejectCalls));
346     }
347
348     {
349         ScriptState::Scope scope(otherScriptState());
350         property()->promise(otherWorld()).then(notReached(currentScriptState()), stub(currentScriptState(), otherActual, nOtherRejectCalls));
351     }
352
353     isolate()->RunMicrotasks();
354     EXPECT_EQ(1u, nRejectCalls);
355     EXPECT_EQ(wrap(mainWorld(), reason), actual);
356     EXPECT_EQ(1u, nOtherRejectCalls);
357     EXPECT_NE(actual, otherActual);
358     EXPECT_EQ(wrap(otherWorld(), reason), otherActual);
359 }
360
361 TEST_F(ScriptPromisePropertyGarbageCollectedTest, Promise_DeadContext)
362 {
363     Property* property = this->property();
364     property->resolve(new GarbageCollectedScriptWrappable("value"));
365     EXPECT_EQ(Property::Resolved, property->state());
366
367     destroyContext();
368
369     EXPECT_TRUE(property->promise(DOMWrapperWorld::mainWorld()).isEmpty());
370 }
371
372 TEST_F(ScriptPromisePropertyGarbageCollectedTest, Resolve_DeadContext)
373 {
374     Property* property = this->property();
375
376     {
377         ScriptState::Scope scope(mainScriptState());
378         property->promise(DOMWrapperWorld::mainWorld()).then(notReached(currentScriptState()), notReached(currentScriptState()));
379     }
380
381     destroyContext();
382     EXPECT_TRUE(!property->executionContext() || property->executionContext()->activeDOMObjectsAreStopped());
383
384     property->resolve(new GarbageCollectedScriptWrappable("value"));
385     EXPECT_EQ(Property::Pending, property->state());
386
387     v8::Isolate::GetCurrent()->RunMicrotasks();
388 }
389
390 TEST_F(ScriptPromisePropertyGarbageCollectedTest, Reset)
391 {
392     ScriptPromise oldPromise, newPromise;
393     ScriptValue oldActual, newActual;
394     GarbageCollectedScriptWrappable* oldValue = new GarbageCollectedScriptWrappable("old");
395     GarbageCollectedScriptWrappable* newValue = new GarbageCollectedScriptWrappable("new");
396     size_t nOldResolveCalls = 0;
397     size_t nNewRejectCalls = 0;
398
399     {
400         ScriptState::Scope scope(mainScriptState());
401         property()->resolve(oldValue);
402         oldPromise = property()->promise(mainWorld());
403         oldPromise.then(stub(currentScriptState(), oldActual, nOldResolveCalls), notReached(currentScriptState()));
404     }
405
406     property()->reset();
407
408     {
409         ScriptState::Scope scope(mainScriptState());
410         newPromise = property()->promise(mainWorld());
411         newPromise.then(notReached(currentScriptState()), stub(currentScriptState(), newActual, nNewRejectCalls));
412         property()->reject(newValue);
413     }
414
415     EXPECT_EQ(0u, nOldResolveCalls);
416     EXPECT_EQ(0u, nNewRejectCalls);
417
418     isolate()->RunMicrotasks();
419     EXPECT_EQ(1u, nOldResolveCalls);
420     EXPECT_EQ(1u, nNewRejectCalls);
421     EXPECT_NE(oldPromise, newPromise);
422     EXPECT_EQ(wrap(mainWorld(), oldValue), oldActual);
423     EXPECT_EQ(wrap(mainWorld(), newValue), newActual);
424     EXPECT_NE(oldActual, newActual);
425 }
426
427 // Tests that ScriptPromiseProperty works with a ref-counted holder.
428 class ScriptPromisePropertyRefCountedTest : public ScriptPromisePropertyTestBase, public ::testing::Test {
429 public:
430     typedef RefCountedHolder::Property Property;
431
432     ScriptPromisePropertyRefCountedTest()
433         : m_holder(RefCountedHolder::create(&document()))
434     {
435     }
436
437     RefCountedHolder* holder() { return m_holder.get(); }
438     Property* property() { return m_holder->property(); }
439
440 private:
441     RefPtr<RefCountedHolder> m_holder;
442 };
443
444 TEST_F(ScriptPromisePropertyRefCountedTest, Resolve)
445 {
446     ScriptValue actual;
447     size_t nResolveCalls = 0;
448
449     {
450         ScriptState::Scope scope(mainScriptState());
451         property()->promise(DOMWrapperWorld::mainWorld()).then(stub(currentScriptState(), actual, nResolveCalls), notReached(currentScriptState()));
452     }
453
454     RefPtr<RefCountedScriptWrappable> value = RefCountedScriptWrappable::create("value");
455     property()->resolve(value.get());
456     EXPECT_EQ(Property::Resolved, property()->state());
457
458     isolate()->RunMicrotasks();
459     EXPECT_EQ(1u, nResolveCalls);
460     EXPECT_EQ(wrap(mainWorld(), value), actual);
461 }
462
463 TEST_F(ScriptPromisePropertyRefCountedTest, Reject)
464 {
465     ScriptValue actual;
466     size_t nRejectCalls = 0;
467
468     {
469         ScriptState::Scope scope(mainScriptState());
470         property()->promise(DOMWrapperWorld::mainWorld()).then(notReached(currentScriptState()), stub(currentScriptState(), actual, nRejectCalls));
471     }
472
473     RefPtr<RefCountedScriptWrappable> reason = RefCountedScriptWrappable::create("reason");
474     property()->reject(reason);
475     EXPECT_EQ(Property::Rejected, property()->state());
476
477     isolate()->RunMicrotasks();
478     EXPECT_EQ(1u, nRejectCalls);
479     EXPECT_EQ(wrap(mainWorld(), reason), actual);
480 }
481
482 TEST_F(ScriptPromisePropertyRefCountedTest, ReSolveAndReset)
483 {
484     RefPtr<RefCountedScriptWrappable> value = RefCountedScriptWrappable::create("value");
485
486     {
487         ScriptState::Scope scope(mainScriptState());
488         property()->resolve(value);
489     }
490
491     EXPECT_EQ(2, value->refCount());
492     property()->reset();
493     EXPECT_EQ(1, value->refCount());
494 }
495
496 TEST_F(ScriptPromisePropertyRefCountedTest, RejectAndReset)
497 {
498     RefPtr<RefCountedScriptWrappable> value = RefCountedScriptWrappable::create("value");
499
500     {
501         ScriptState::Scope scope(mainScriptState());
502         property()->reject(value.get());
503     }
504
505     EXPECT_EQ(2, value->refCount());
506     property()->reset();
507     EXPECT_EQ(1, value->refCount());
508 }
509
510 // Tests that ScriptPromiseProperty works with a non ScriptWrappable resolution
511 // target.
512 class ScriptPromisePropertyNonScriptWrappableResolutionTargetTest : public ScriptPromisePropertyTestBase, public ::testing::Test {
513 public:
514     template <typename T>
515     void test(const T& value, const char* expected, const char* file, size_t line)
516     {
517         typedef ScriptPromiseProperty<Member<GarbageCollectedScriptWrappable>, T, V8UndefinedType> Property;
518         Property* property = new Property(&document(), new GarbageCollectedScriptWrappable("holder"), Property::Ready);
519         size_t nResolveCalls = 0;
520         ScriptValue actualValue;
521         String actual;
522         {
523             ScriptState::Scope scope(mainScriptState());
524             property->promise(DOMWrapperWorld::mainWorld()).then(stub(currentScriptState(), actualValue, nResolveCalls), notReached(currentScriptState()));
525         }
526         property->resolve(value);
527         isolate()->RunMicrotasks();
528         {
529             ScriptState::Scope scope(mainScriptState());
530             actual = toCoreString(actualValue.v8Value()->ToString());
531         }
532         if (expected != actual) {
533             ADD_FAILURE_AT(file, line) << "toV8Value returns an incorrect value.\n  Actual: " << actual.utf8().data() << "\nExpected: " << expected;
534             return;
535         }
536     }
537 };
538
539 TEST_F(ScriptPromisePropertyNonScriptWrappableResolutionTargetTest, ResolveWithUndefined)
540 {
541     test(V8UndefinedType(), "undefined", __FILE__, __LINE__);
542 }
543
544 TEST_F(ScriptPromisePropertyNonScriptWrappableResolutionTargetTest, ResolveWithString)
545 {
546     test(String("hello"), "hello", __FILE__, __LINE__);
547 }
548
549 TEST_F(ScriptPromisePropertyNonScriptWrappableResolutionTargetTest, ResolveWithInteger)
550 {
551     test<int>(-1, "-1", __FILE__, __LINE__);
552 }
553
554 } // namespace