OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ppapi/tests/test_instance_deprecated.h" | 5 #include "ppapi/tests/test_instance_deprecated.h" |
6 | 6 |
7 #include <assert.h> | 7 #include <assert.h> |
8 | 8 |
9 #include "ppapi/c/ppb_var.h" | 9 #include "ppapi/c/ppb_var.h" |
10 #include "ppapi/cpp/module.h" | 10 #include "ppapi/cpp/module.h" |
11 #include "ppapi/cpp/dev/scriptable_object_deprecated.h" | 11 #include "ppapi/cpp/dev/scriptable_object_deprecated.h" |
12 #include "ppapi/tests/testing_instance.h" | 12 #include "ppapi/tests/testing_instance.h" |
13 | 13 |
14 namespace { | 14 namespace { |
15 | 15 |
16 static const char kSetValueFunction[] = "SetValue"; | 16 static const char kSetValueFunction[] = "SetValue"; |
17 static const char kSetExceptionFunction[] = "SetException"; | 17 static const char kSetExceptionFunction[] = "SetException"; |
18 static const char kReturnValueFunction[] = "ReturnValue"; | 18 static const char kReturnValueFunction[] = "ReturnValue"; |
19 | 19 |
20 // ScriptableObject used by instance. | |
21 class InstanceSO : public pp::deprecated::ScriptableObject { | |
22 public: | |
23 explicit InstanceSO(TestInstance* i); | |
24 virtual ~InstanceSO(); | |
25 | |
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, | |
30 pp::Var* exception); | |
31 | |
32 private: | |
33 TestInstance* test_instance_; | |
34 // For out-of-process, the InstanceSO might be deleted after the instance was | |
35 // already destroyed, so we can't rely on test_instance_->testing_interface() | |
36 // being valid. Therefore we store our own. | |
37 const PPB_Testing_Private* testing_interface_; | |
38 }; | |
39 | |
40 InstanceSO::InstanceSO(TestInstance* i) | 20 InstanceSO::InstanceSO(TestInstance* i) |
41 : test_instance_(i), | 21 : test_instance_(i), |
42 testing_interface_(i->testing_interface()) { | 22 testing_interface_(i->testing_interface()) { |
43 // Set up a post-condition for the test so that we can ensure our destructor | 23 if (testing_interface_->IsOutOfProcess() == PP_FALSE) |
44 // is called. This only works reliably in-process. Out-of-process, it only | 24 test_instance_->set_instance_object_destroyed(false); |
45 // can work when the renderer stays alive a short while after the plugin | |
46 // instance is destroyed. If the renderer is being shut down, too much happens | |
47 // asynchronously for the out-of-process case to work reliably. In | |
48 // particular: | |
49 // - The Var ReleaseObject message is asynchronous. | |
50 // - The PPB_Var_Deprecated host-side proxy posts a task to actually release | |
51 // the object when the ReleaseObject message is received. | |
52 // - The PPP_Class Deallocate message is asynchronous. | |
53 // At time of writing this comment, if you modify the code so that the above | |
54 // happens synchronously, and you remove the restriction that the plugin can't | |
55 // be unblocked by a sync message, then this check actually passes reliably | |
56 // for out-of-process. But we don't want to make any of those changes, so we | |
57 // just skip the check. | |
58 if (testing_interface_->IsOutOfProcess() == PP_FALSE) { | |
59 i->instance()->AddPostCondition( | |
60 "window.document.getElementById('container').instance_object_destroyed" | |
61 ); | |
62 } | |
63 } | 25 } |
64 | 26 |
65 InstanceSO::~InstanceSO() { | 27 InstanceSO::~InstanceSO() { |
66 if (testing_interface_->IsOutOfProcess() == PP_FALSE) { | 28 if (test_instance_) |
67 // TODO(dmichael): It would probably be best to make in-process consistent | 29 test_instance_->set_instance_object_destroyed(true); |
68 // with out-of-process. That would mean that the instance | |
69 // would already be destroyed at this point. | |
70 pp::Var ret = test_instance_->instance()->ExecuteScript( | |
71 "document.getElementById('container').instance_object_destroyed=true;"); | |
72 } else { | |
73 // Out-of-process, this destructor might not actually get invoked. See the | |
74 // comment in InstanceSO's constructor for an explanation. Also, instance() | |
75 // has already been destroyed :-(. So we can't really do anything here. | |
76 } | |
77 } | 30 } |
78 | 31 |
79 bool InstanceSO::HasMethod(const pp::Var& name, pp::Var* exception) { | 32 bool InstanceSO::HasMethod(const pp::Var& name, pp::Var* exception) { |
80 if (!name.is_string()) | 33 if (!name.is_string()) |
81 return false; | 34 return false; |
82 return name.AsString() == kSetValueFunction || | 35 return name.AsString() == kSetValueFunction || |
83 name.AsString() == kSetExceptionFunction || | 36 name.AsString() == kSetExceptionFunction || |
84 name.AsString() == kReturnValueFunction; | 37 name.AsString() == kReturnValueFunction; |
85 } | 38 } |
86 | 39 |
87 pp::Var InstanceSO::Call(const pp::Var& method_name, | 40 pp::Var InstanceSO::Call(const pp::Var& method_name, |
88 const std::vector<pp::Var>& args, | 41 const std::vector<pp::Var>& args, |
89 pp::Var* exception) { | 42 pp::Var* exception) { |
90 if (!method_name.is_string()) | 43 if (!method_name.is_string()) |
91 return false; | 44 return false; |
92 std::string name = method_name.AsString(); | 45 std::string name = method_name.AsString(); |
93 | 46 |
94 if (name == kSetValueFunction) { | 47 if (name == kSetValueFunction) { |
95 if (args.size() != 1 || !args[0].is_string()) | 48 if (args.size() != 1 || !args[0].is_string()) |
96 *exception = pp::Var("Bad argument to SetValue(<string>)"); | 49 *exception = pp::Var("Bad argument to SetValue(<string>)"); |
97 else | 50 else if (test_instance_) |
98 test_instance_->set_string(args[0].AsString()); | 51 test_instance_->set_string(args[0].AsString()); |
99 } else if (name == kSetExceptionFunction) { | 52 } else if (name == kSetExceptionFunction) { |
100 if (args.size() != 1 || !args[0].is_string()) | 53 if (args.size() != 1 || !args[0].is_string()) |
101 *exception = pp::Var("Bad argument to SetException(<string>)"); | 54 *exception = pp::Var("Bad argument to SetException(<string>)"); |
102 else | 55 else |
103 *exception = args[0]; | 56 *exception = args[0]; |
104 } else if (name == kReturnValueFunction) { | 57 } else if (name == kReturnValueFunction) { |
105 if (args.size() != 1) | 58 if (args.size() != 1) |
106 *exception = pp::Var("Need single arg to call ReturnValue"); | 59 *exception = pp::Var("Need single arg to call ReturnValue"); |
107 else | 60 else |
108 return args[0]; | 61 return args[0]; |
109 } else { | 62 } else { |
110 *exception = pp::Var("Bad function call"); | 63 *exception = pp::Var("Bad function call"); |
111 } | 64 } |
112 | 65 |
113 return pp::Var(); | 66 return pp::Var(); |
114 } | 67 } |
115 | 68 |
116 } // namespace | 69 } // namespace |
117 | 70 |
118 REGISTER_TEST_CASE(Instance); | 71 REGISTER_TEST_CASE(Instance); |
119 | 72 |
120 TestInstance::TestInstance(TestingInstance* instance) : TestCase(instance) { | 73 TestInstance::TestInstance(TestingInstance* instance) |
121 } | 74 : TestCase(instance), |
75 instance_so_(NULL) {} | |
122 | 76 |
123 bool TestInstance::Init() { | 77 bool TestInstance::Init() { |
124 return true; | 78 return true; |
125 } | 79 } |
126 | 80 |
127 TestInstance::~TestInstance() { | 81 TestInstance::~TestInstance() { |
128 ResetTestObject(); | 82 ResetTestObject(); |
129 // When running tests in process, some post conditions check that teardown | 83 if (testing_interface_->IsOutOfProcess() == PP_FALSE) { |
130 // happened successfully. We need to run the garbage collector to ensure that | 84 // This should cause the instance objects descructor to be called. |
Sam McNally
2014/08/27 04:45:51
objects descructor -> object's destructor
| |
131 // vars get released. | |
132 if (testing_interface_->IsOutOfProcess() == PP_FALSE) | |
133 testing_interface_->RunV8GC(instance_->pp_instance()); | 85 testing_interface_->RunV8GC(instance_->pp_instance()); |
86 | |
87 // Test a post-condition which ensures the instance objects destructor is | |
88 // called. This only works reliably in-process. Out-of-process, it only | |
89 // can work when the renderer stays alive a short while after the plugin | |
90 // instance is destroyed. If the renderer is being shut down, too much | |
91 // happens asynchronously for the out-of-process case to work reliably. In | |
92 // particular: | |
93 // - The Var ReleaseObject message is asynchronous. | |
94 // - The PPB_Var_Deprecated host-side proxy posts a task to actually | |
95 // release the object when the ReleaseObject message is received. | |
96 // - The PPP_Class Deallocate message is asynchronous. | |
97 // At time of writing this comment, if you modify the code so that the above | |
98 // happens synchronously, and you remove the restriction that the plugin | |
99 // can't be unblocked by a sync message, then this check actually passes | |
100 // reliably for out-of-process. But we don't want to make any of those | |
101 // changes so we just skip the check. | |
102 PP_DCHECK(!instance_so_); | |
103 } else { | |
104 // Out-of-process, this destructor might not actually get invoked. Clear | |
105 // the InstanceSOs reference to the instance so there is no UAF. | |
106 if (instance_so_) | |
107 instance_so_->clear_test_instance(); | |
108 } | |
109 | |
134 // Save the fact that we were destroyed in sessionStorage. This tests that | 110 // Save the fact that we were destroyed in sessionStorage. This tests that |
135 // we can ExecuteScript at instance destruction without crashing. It also | 111 // we can ExecuteScript at instance destruction without crashing. It also |
136 // allows us to check that ExecuteScript will run and succeed in certain | 112 // allows us to check that ExecuteScript will run and succeed in certain |
137 // cases. In particular, when the instance is destroyed by normal DOM | 113 // cases. In particular, when the instance is destroyed by normal DOM |
138 // deletion, ExecuteScript will actually work. See | 114 // deletion, ExecuteScript will actually work. See |
139 // TestExecuteScriptInInstanceShutdown for that test. Note, however, that | 115 // TestExecuteScriptInInstanceShutdown for that test. Note, however, that |
140 // ExecuteScript will *not* have an effect when the instance is destroyed | 116 // ExecuteScript will *not* have an effect when the instance is destroyed |
141 // because the renderer was shut down. | 117 // because the renderer was shut down. |
142 pp::Var ret = instance()->ExecuteScript( | 118 pp::Var ret = instance()->ExecuteScript( |
143 "sessionStorage.setItem('instance_destroyed', 'true');"); | 119 "sessionStorage.setItem('instance_destroyed', 'true');"); |
144 } | 120 } |
145 | 121 |
146 void TestInstance::RunTests(const std::string& filter) { | 122 void TestInstance::RunTests(const std::string& filter) { |
147 RUN_TEST(ExecuteScript, filter); | 123 RUN_TEST(ExecuteScript, filter); |
148 RUN_TEST(RecursiveObjects, filter); | 124 RUN_TEST(RecursiveObjects, filter); |
149 RUN_TEST(LeakedObjectDestructors, filter); | 125 RUN_TEST(LeakedObjectDestructors, filter); |
150 RUN_TEST(SetupExecuteScriptAtInstanceShutdown, filter); | 126 RUN_TEST(SetupExecuteScriptAtInstanceShutdown, filter); |
151 RUN_TEST(ExecuteScriptAtInstanceShutdown, filter); | 127 RUN_TEST(ExecuteScriptAtInstanceShutdown, filter); |
152 } | 128 } |
153 | 129 |
154 void TestInstance::LeakReferenceAndIgnore(const pp::Var& leaked) { | 130 void TestInstance::LeakReferenceAndIgnore(const pp::Var& leaked) { |
155 static const PPB_Var* var_interface = static_cast<const PPB_Var*>( | 131 static const PPB_Var* var_interface = static_cast<const PPB_Var*>( |
156 pp::Module::Get()->GetBrowserInterface(PPB_VAR_INTERFACE)); | 132 pp::Module::Get()->GetBrowserInterface(PPB_VAR_INTERFACE)); |
157 var_interface->AddRef(leaked.pp_var()); | 133 var_interface->AddRef(leaked.pp_var()); |
158 IgnoreLeakedVar(leaked.pp_var().value.as_id); | 134 IgnoreLeakedVar(leaked.pp_var().value.as_id); |
159 } | 135 } |
160 | 136 |
161 pp::deprecated::ScriptableObject* TestInstance::CreateTestObject() { | 137 pp::deprecated::ScriptableObject* TestInstance::CreateTestObject() { |
162 return new InstanceSO(this); | 138 if (!instance_so_) |
139 instance_so_ = new InstanceSO(this); | |
140 return instance_so_; | |
163 } | 141 } |
164 | 142 |
165 std::string TestInstance::TestExecuteScript() { | 143 std::string TestInstance::TestExecuteScript() { |
166 // Simple call back into the plugin. | 144 // Simple call back into the plugin. |
167 pp::Var exception; | 145 pp::Var exception; |
168 pp::Var ret = instance_->ExecuteScript( | 146 pp::Var ret = instance_->ExecuteScript( |
169 "document.getElementById('plugin').SetValue('hello, world');", | 147 "document.getElementById('plugin').SetValue('hello, world');", |
170 &exception); | 148 &exception); |
171 ASSERT_TRUE(ret.is_undefined()); | 149 ASSERT_TRUE(ret.is_undefined()); |
172 ASSERT_TRUE(exception.is_undefined()); | 150 ASSERT_TRUE(exception.is_undefined()); |
(...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
292 // that it was set as expected. | 270 // that it was set as expected. |
293 pp::Var result = instance()->ExecuteScript( | 271 pp::Var result = instance()->ExecuteScript( |
294 "sessionStorage.getItem('instance_destroyed');"); | 272 "sessionStorage.getItem('instance_destroyed');"); |
295 ASSERT_TRUE(result.is_string()); | 273 ASSERT_TRUE(result.is_string()); |
296 ASSERT_EQ(std::string("true"), result.AsString()); | 274 ASSERT_EQ(std::string("true"), result.AsString()); |
297 instance()->ExecuteScript("sessionStorage.removeItem('instance_destroyed');"); | 275 instance()->ExecuteScript("sessionStorage.removeItem('instance_destroyed');"); |
298 | 276 |
299 PASS(); | 277 PASS(); |
300 } | 278 } |
301 | 279 |
OLD | NEW |