Chromium Code Reviews| 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 |