1 // Copyright (c) 2012 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.
5 #include "ppapi/tests/test_instance_deprecated.h"
9 #include "ppapi/c/ppb_var.h"
10 #include "ppapi/cpp/module.h"
11 #include "ppapi/cpp/dev/scriptable_object_deprecated.h"
12 #include "ppapi/tests/testing_instance.h"
16 static const char kSetValueFunction
[] = "SetValue";
17 static const char kSetExceptionFunction
[] = "SetException";
18 static const char kReturnValueFunction
[] = "ReturnValue";
20 // ScriptableObject used by instance.
21 class InstanceSO
: public pp::deprecated::ScriptableObject
{
23 InstanceSO(TestInstance
* i
);
24 virtual ~InstanceSO();
26 // pp::deprecated::ScriptableObject overrides.
27 bool HasMethod(const pp::Var
& name
, pp::Var
* exception
);
28 pp::Var
Call(const pp::Var
& name
,
29 const std::vector
<pp::Var
>& args
,
33 TestInstance
* test_instance_
;
36 InstanceSO::InstanceSO(TestInstance
* i
) : test_instance_(i
) {
37 // Set up a post-condition for the test so that we can ensure our destructor
38 // is called. This only works in-process right now. Rather than disable the
39 // whole test, we only do this check when running in-process.
40 // TODO(dmichael): Figure out if we want this to work out-of-process, and if
41 // so, fix it. Note that it might just be failing because the
42 // ReleaseObject and Deallocate messages are asynchronous.
43 if (i
->testing_interface() &&
44 i
->testing_interface()->IsOutOfProcess() == PP_FALSE
) {
45 i
->instance()->AddPostCondition(
46 "window.document.getElementById('container').instance_object_destroyed"
51 InstanceSO::~InstanceSO() {
53 pp::Var ret
= test_instance_
->instance()->ExecuteScript(
54 "document.getElementById('container').instance_object_destroyed=true;");
57 bool InstanceSO::HasMethod(const pp::Var
& name
, pp::Var
* exception
) {
58 if (!name
.is_string())
60 return name
.AsString() == kSetValueFunction
||
61 name
.AsString() == kSetExceptionFunction
||
62 name
.AsString() == kReturnValueFunction
;
65 pp::Var
InstanceSO::Call(const pp::Var
& method_name
,
66 const std::vector
<pp::Var
>& args
,
68 if (!method_name
.is_string())
70 std::string name
= method_name
.AsString();
72 if (name
== kSetValueFunction
) {
73 if (args
.size() != 1 || !args
[0].is_string())
74 *exception
= pp::Var("Bad argument to SetValue(<string>)");
76 test_instance_
->set_string(args
[0].AsString());
77 } else if (name
== kSetExceptionFunction
) {
78 if (args
.size() != 1 || !args
[0].is_string())
79 *exception
= pp::Var("Bad argument to SetException(<string>)");
82 } else if (name
== kReturnValueFunction
) {
84 *exception
= pp::Var("Need single arg to call ReturnValue");
88 *exception
= pp::Var("Bad function call");
96 REGISTER_TEST_CASE(Instance
);
98 TestInstance::TestInstance(TestingInstance
* instance
) : TestCase(instance
) {
101 bool TestInstance::Init() {
105 void TestInstance::RunTests(const std::string
& filter
) {
106 RUN_TEST(ExecuteScript
, filter
);
107 RUN_TEST(RecursiveObjects
, filter
);
108 RUN_TEST(LeakedObjectDestructors
, filter
);
111 void TestInstance::LeakReferenceAndIgnore(const pp::Var
& leaked
) {
112 static const PPB_Var
* var_interface
= static_cast<const PPB_Var
*>(
113 pp::Module::Get()->GetBrowserInterface(PPB_VAR_INTERFACE
));
114 var_interface
->AddRef(leaked
.pp_var());
115 IgnoreLeakedVar(leaked
.pp_var().value
.as_id
);
118 pp::deprecated::ScriptableObject
* TestInstance::CreateTestObject() {
119 return new InstanceSO(this);
122 std::string
TestInstance::TestExecuteScript() {
123 // Simple call back into the plugin.
125 pp::Var ret
= instance_
->ExecuteScript(
126 "document.getElementById('plugin').SetValue('hello, world');",
128 ASSERT_TRUE(ret
.is_undefined());
129 ASSERT_TRUE(exception
.is_undefined());
130 ASSERT_TRUE(string_
== "hello, world");
132 // Return values from the plugin should be returned.
133 ret
= instance_
->ExecuteScript(
134 "document.getElementById('plugin').ReturnValue('return value');",
136 ASSERT_TRUE(ret
.is_string() && ret
.AsString() == "return value");
137 ASSERT_TRUE(exception
.is_undefined());
139 // Exception thrown by the plugin should be caught.
140 ret
= instance_
->ExecuteScript(
141 "document.getElementById('plugin').SetException('plugin exception');",
143 ASSERT_TRUE(ret
.is_undefined());
144 ASSERT_TRUE(exception
.is_string());
145 // Due to a limitation in the implementation of TryCatch, it doesn't actually
146 // pass the strings up. Since this is a trusted only interface, we've decided
147 // not to bother fixing this for now.
149 // Exception caused by string evaluation should be caught.
150 exception
= pp::Var();
151 ret
= instance_
->ExecuteScript("document.doesntExist()", &exception
);
152 ASSERT_TRUE(ret
.is_undefined());
153 ASSERT_TRUE(exception
.is_string()); // Don't know exactly what it will say.
158 // A scriptable object that contains other scriptable objects recursively. This
159 // is used to help verify that our scriptable object clean-up code works
161 class ObjectWithChildren
: public pp::deprecated::ScriptableObject
{
163 ObjectWithChildren(TestInstance
* i
, int num_descendents
) {
164 if (num_descendents
> 0) {
165 child_
= pp::VarPrivate(i
->instance(),
166 new ObjectWithChildren(i
, num_descendents
- 1));
169 struct IgnoreLeaks
{};
170 ObjectWithChildren(TestInstance
* i
, int num_descendents
, IgnoreLeaks
) {
171 if (num_descendents
> 0) {
172 child_
= pp::VarPrivate(i
->instance(),
173 new ObjectWithChildren(i
, num_descendents
- 1,
175 i
->IgnoreLeakedVar(child_
.pp_var().value
.as_id
);
179 pp::VarPrivate child_
;
182 std::string
TestInstance::TestRecursiveObjects() {
183 // These should be deleted when we exit scope, so should not leak.
184 pp::VarPrivate
not_leaked(instance(), new ObjectWithChildren(this, 50));
186 // Leak some, but tell TestCase to ignore the leaks. This test is run and then
187 // reloaded (see ppapi_uitest.cc). If these aren't cleaned up when the first
188 // run is torn down, they will show up as leaks in the second run.
189 // NOTE: The ScriptableObjects are actually leaked, but they should be removed
190 // from the tracker. See below for a test that verifies that the
191 // destructor is not run.
192 pp::VarPrivate
leaked(
194 new ObjectWithChildren(this, 50, ObjectWithChildren::IgnoreLeaks()));
195 // Now leak a reference to the root object. This should force the root and
196 // all its descendents to stay in the tracker.
197 LeakReferenceAndIgnore(leaked
);
202 // A scriptable object that should cause a crash if its destructor is run. We
203 // don't run the destructor for objects which the plugin leaks. This is to
204 // prevent them doing dangerous things at cleanup time, such as executing script
205 // or creating new objects.
206 class BadDestructorObject
: public pp::deprecated::ScriptableObject
{
208 BadDestructorObject() {}
209 ~BadDestructorObject() {
214 std::string
TestInstance::TestLeakedObjectDestructors() {
215 pp::VarPrivate
leaked(instance(), new BadDestructorObject());
216 // Leak a reference so it gets deleted on instance shutdown.
217 LeakReferenceAndIgnore(leaked
);