1 // Copyright 2015 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.
6 #include "base/memory/weak_ptr.h"
7 #include "base/message_loop/message_loop.h"
8 #include "extensions/common/extension.h"
9 #include "extensions/common/extension_set.h"
10 #include "extensions/common/features/feature.h"
11 #include "extensions/renderer/gc_callback.h"
12 #include "extensions/renderer/scoped_web_frame.h"
13 #include "extensions/renderer/script_context.h"
14 #include "extensions/renderer/script_context_set.h"
15 #include "gin/function_template.h"
16 #include "gin/public/context_holder.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "third_party/WebKit/public/web/WebFrame.h"
19 #include "v8/include/v8.h"
21 namespace extensions
{
24 void SetToTrue(bool* value
) {
26 ADD_FAILURE() << "Value is already true";
30 class GCCallbackTest
: public testing::Test
{
32 GCCallbackTest() : script_context_set_(&extensions_
, &active_extensions_
) {}
35 base::MessageLoop
& message_loop() { return message_loop_
; }
37 ScriptContextSet
& script_context_set() { return script_context_set_
; }
39 v8::Local
<v8::Context
> v8_context() {
40 return v8::Local
<v8::Context
>::New(v8::Isolate::GetCurrent(), v8_context_
);
43 ScriptContext
* RegisterScriptContext() {
44 // No extension group or world ID.
45 return script_context_set_
.Register(
47 v8::Local
<v8::Context
>::New(v8::Isolate::GetCurrent(), v8_context_
), 0,
51 void RequestGarbageCollection() {
52 v8::Isolate::GetCurrent()->RequestGarbageCollectionForTesting(
53 v8::Isolate::kFullGarbageCollection
);
57 void SetUp() override
{
58 v8::Isolate
* isolate
= v8::Isolate::GetCurrent();
59 v8::HandleScope
handle_scope(isolate
);
60 v8::Local
<v8::Context
> local_v8_context
= v8::Context::New(isolate
);
61 v8_context_
.Reset(isolate
, local_v8_context
);
62 // ScriptContexts rely on gin.
63 gin_context_holder_
.reset(new gin::ContextHolder(isolate
));
64 gin_context_holder_
->SetContext(local_v8_context
);
67 void TearDown() override
{
68 gin_context_holder_
.reset();
70 RequestGarbageCollection();
73 base::MessageLoop message_loop_
;
74 ScopedWebFrame web_frame_
; // (this will construct the v8::Isolate)
75 ExtensionSet extensions_
;
76 ExtensionIdSet active_extensions_
;
77 ScriptContextSet script_context_set_
;
78 v8::Global
<v8::Context
> v8_context_
;
79 scoped_ptr
<gin::ContextHolder
> gin_context_holder_
;
81 DISALLOW_COPY_AND_ASSIGN(GCCallbackTest
);
84 TEST_F(GCCallbackTest
, GCBeforeContextInvalidated
) {
85 v8::Isolate
* isolate
= v8::Isolate::GetCurrent();
86 v8::HandleScope
handle_scope(isolate
);
87 v8::Context::Scope
context_scope(v8_context());
89 ScriptContext
* script_context
= RegisterScriptContext();
91 bool callback_invoked
= false;
92 bool fallback_invoked
= false;
95 // Nest another HandleScope so that |object| and |unreachable_function|'s
96 // handles will be garbage collected.
97 v8::HandleScope
handle_scope(isolate
);
98 v8::Local
<v8::Object
> object
= v8::Object::New(isolate
);
99 v8::Local
<v8::FunctionTemplate
> unreachable_function
=
100 gin::CreateFunctionTemplate(isolate
,
101 base::Bind(SetToTrue
, &callback_invoked
));
102 // The GCCallback will delete itself, or memory tests will complain.
103 new GCCallback(script_context
, object
, unreachable_function
->GetFunction(),
104 base::Bind(SetToTrue
, &fallback_invoked
));
107 // Trigger a GC. Only the callback should be invoked.
108 RequestGarbageCollection();
109 message_loop().RunUntilIdle();
111 EXPECT_TRUE(callback_invoked
);
112 EXPECT_FALSE(fallback_invoked
);
114 // Invalidate the context. The fallback should not be invoked because the
115 // callback was already invoked.
116 script_context_set().Remove(script_context
);
117 message_loop().RunUntilIdle();
119 EXPECT_FALSE(fallback_invoked
);
122 TEST_F(GCCallbackTest
, ContextInvalidatedBeforeGC
) {
123 v8::Isolate
* isolate
= v8::Isolate::GetCurrent();
124 v8::HandleScope
handle_scope(isolate
);
125 v8::Context::Scope
context_scope(v8_context());
127 ScriptContext
* script_context
= RegisterScriptContext();
129 bool callback_invoked
= false;
130 bool fallback_invoked
= false;
133 // Nest another HandleScope so that |object| and |unreachable_function|'s
134 // handles will be garbage collected.
135 v8::HandleScope
handle_scope(isolate
);
136 v8::Local
<v8::Object
> object
= v8::Object::New(isolate
);
137 v8::Local
<v8::FunctionTemplate
> unreachable_function
=
138 gin::CreateFunctionTemplate(isolate
,
139 base::Bind(SetToTrue
, &callback_invoked
));
140 // The GCCallback will delete itself, or memory tests will complain.
141 new GCCallback(script_context
, object
, unreachable_function
->GetFunction(),
142 base::Bind(SetToTrue
, &fallback_invoked
));
145 // Invalidate the context. Only the fallback should be invoked.
146 script_context_set().Remove(script_context
);
147 message_loop().RunUntilIdle();
149 EXPECT_FALSE(callback_invoked
);
150 EXPECT_TRUE(fallback_invoked
);
152 // Trigger a GC. The callback should not be invoked because the fallback was
154 RequestGarbageCollection();
155 message_loop().RunUntilIdle();
157 EXPECT_FALSE(callback_invoked
);
161 } // namespace extensions