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_(&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 ExtensionIdSet active_extensions_
;
76 ScriptContextSet script_context_set_
;
77 v8::Global
<v8::Context
> v8_context_
;
78 scoped_ptr
<gin::ContextHolder
> gin_context_holder_
;
80 DISALLOW_COPY_AND_ASSIGN(GCCallbackTest
);
83 TEST_F(GCCallbackTest
, GCBeforeContextInvalidated
) {
84 v8::Isolate
* isolate
= v8::Isolate::GetCurrent();
85 v8::HandleScope
handle_scope(isolate
);
86 v8::Context::Scope
context_scope(v8_context());
88 ScriptContext
* script_context
= RegisterScriptContext();
90 bool callback_invoked
= false;
91 bool fallback_invoked
= false;
94 // Nest another HandleScope so that |object| and |unreachable_function|'s
95 // handles will be garbage collected.
96 v8::HandleScope
handle_scope(isolate
);
97 v8::Local
<v8::Object
> object
= v8::Object::New(isolate
);
98 v8::Local
<v8::FunctionTemplate
> unreachable_function
=
99 gin::CreateFunctionTemplate(isolate
,
100 base::Bind(SetToTrue
, &callback_invoked
));
101 // The GCCallback will delete itself, or memory tests will complain.
102 new GCCallback(script_context
, object
, unreachable_function
->GetFunction(),
103 base::Bind(SetToTrue
, &fallback_invoked
));
106 // Trigger a GC. Only the callback should be invoked.
107 RequestGarbageCollection();
108 message_loop().RunUntilIdle();
110 EXPECT_TRUE(callback_invoked
);
111 EXPECT_FALSE(fallback_invoked
);
113 // Invalidate the context. The fallback should not be invoked because the
114 // callback was already invoked.
115 script_context_set().Remove(script_context
);
116 message_loop().RunUntilIdle();
118 EXPECT_FALSE(fallback_invoked
);
121 TEST_F(GCCallbackTest
, ContextInvalidatedBeforeGC
) {
122 v8::Isolate
* isolate
= v8::Isolate::GetCurrent();
123 v8::HandleScope
handle_scope(isolate
);
124 v8::Context::Scope
context_scope(v8_context());
126 ScriptContext
* script_context
= RegisterScriptContext();
128 bool callback_invoked
= false;
129 bool fallback_invoked
= false;
132 // Nest another HandleScope so that |object| and |unreachable_function|'s
133 // handles will be garbage collected.
134 v8::HandleScope
handle_scope(isolate
);
135 v8::Local
<v8::Object
> object
= v8::Object::New(isolate
);
136 v8::Local
<v8::FunctionTemplate
> unreachable_function
=
137 gin::CreateFunctionTemplate(isolate
,
138 base::Bind(SetToTrue
, &callback_invoked
));
139 // The GCCallback will delete itself, or memory tests will complain.
140 new GCCallback(script_context
, object
, unreachable_function
->GetFunction(),
141 base::Bind(SetToTrue
, &fallback_invoked
));
144 // Invalidate the context. Only the fallback should be invoked.
145 script_context_set().Remove(script_context
);
146 message_loop().RunUntilIdle();
148 EXPECT_FALSE(callback_invoked
);
149 EXPECT_TRUE(fallback_invoked
);
151 // Trigger a GC. The callback should not be invoked because the fallback was
153 RequestGarbageCollection();
154 message_loop().RunUntilIdle();
156 EXPECT_FALSE(callback_invoked
);
160 } // namespace extensions