1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <stdint.h>
#include <tuple>
#include "ipc/ipc_test_sink.h"
#include "ppapi/c/dev/ppp_class_deprecated.h"
#include "ppapi/proxy/plugin_var_tracker.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "ppapi/proxy/ppapi_proxy_test.h"
#include "ppapi/proxy/proxy_object_var.h"
#include "ppapi/shared_impl/proxy_lock.h"
namespace ppapi {
namespace proxy {
namespace {
PP_Var MakeObject(int32_t object_id) {
PP_Var ret;
ret.type = PP_VARTYPE_OBJECT;
ret.value.as_id = object_id;
return ret;
}
// A Deallocate() function for PPP_Class that just increments the integer
// referenced by the pointer so we know how often Deallocate was called.
void MarkOnDeallocate(void* object) {
(*static_cast<int*>(object))++;
}
// A class that just implements MarkOnDeallocate on destruction.
PPP_Class_Deprecated mark_on_deallocate_class = {
NULL, // HasProperty,
NULL, // HasMethod,
NULL, // GetProperty,
NULL, // GetAllPropertyNames,
NULL, // SetProperty,
NULL, // RemoveProperty,
NULL, // Call,
NULL, // Construct,
&MarkOnDeallocate
};
} // namespace
class PluginVarTrackerTest : public PluginProxyTest {
public:
PluginVarTrackerTest() {}
protected:
// Asserts that there is a unique "release object" IPC message in the test
// sink. This will return the var ID from the message or -1 if none found.
int32_t GetObjectIDForUniqueReleaseObject() {
const IPC::Message* release_msg = sink().GetUniqueMessageMatching(
PpapiHostMsg_PPBVar_ReleaseObject::ID);
if (!release_msg)
return -1;
std::tuple<int64_t> id;
PpapiHostMsg_PPBVar_ReleaseObject::Read(release_msg, &id);
return std::get<0>(id);
}
};
TEST_F(PluginVarTrackerTest, GetHostObject) {
ProxyAutoLock lock;
PP_Var host_object = MakeObject(12345);
// Round-trip through the tracker to make sure the host object comes out the
// other end.
PP_Var plugin_object = var_tracker().ReceiveObjectPassRef(
host_object, plugin_dispatcher());
PP_Var host_object2 = var_tracker().GetHostObject(plugin_object);
EXPECT_EQ(PP_VARTYPE_OBJECT, host_object2.type);
EXPECT_EQ(host_object.value.as_id, host_object2.value.as_id);
var_tracker().ReleaseVar(plugin_object);
}
TEST_F(PluginVarTrackerTest, ReceiveObjectPassRef) {
ProxyAutoLock lock;
PP_Var host_object = MakeObject(12345);
// Receive the object, we should have one ref and no messages.
PP_Var plugin_object = var_tracker().ReceiveObjectPassRef(
host_object, plugin_dispatcher());
EXPECT_EQ(0u, sink().message_count());
EXPECT_EQ(1, var_tracker().GetRefCountForObject(plugin_object));
EXPECT_EQ(0,
var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_object));
// Receive the same object again, we should get the same plugin ID out.
PP_Var plugin_object2 = var_tracker().ReceiveObjectPassRef(
host_object, plugin_dispatcher());
EXPECT_EQ(plugin_object.value.as_id, plugin_object2.value.as_id);
EXPECT_EQ(2, var_tracker().GetRefCountForObject(plugin_object));
EXPECT_EQ(0,
var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_object));
// It should have sent one message to decerment the refcount in the host.
// This is because it only maintains one host refcount for all references
// in the plugin, but the host just sent the second one.
EXPECT_EQ(host_object.value.as_id, GetObjectIDForUniqueReleaseObject());
sink().ClearMessages();
// Release the object, one ref at a time. The second release should free
// the tracking data and send a release message to the browser.
var_tracker().ReleaseVar(plugin_object);
EXPECT_EQ(1, var_tracker().GetRefCountForObject(plugin_object));
var_tracker().ReleaseVar(plugin_object);
EXPECT_EQ(-1, var_tracker().GetRefCountForObject(plugin_object));
EXPECT_EQ(host_object.value.as_id, GetObjectIDForUniqueReleaseObject());
}
// Tests freeing objects that have both refcounts and "tracked with no ref".
TEST_F(PluginVarTrackerTest, FreeTrackedAndReferencedObject) {
ProxyAutoLock lock;
PP_Var host_object = MakeObject(12345);
// Phase one: First receive via a "pass ref", then a tracked with no ref.
PP_Var plugin_var = var_tracker().ReceiveObjectPassRef(
host_object, plugin_dispatcher());
PP_Var plugin_var2 = var_tracker().TrackObjectWithNoReference(
host_object, plugin_dispatcher());
EXPECT_EQ(plugin_var.value.as_id, plugin_var2.value.as_id);
EXPECT_EQ(1, var_tracker().GetRefCountForObject(plugin_var));
EXPECT_EQ(1,
var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var));
// Free via the refcount, this should release the object to the browser but
// maintain the tracked object.
var_tracker().ReleaseVar(plugin_var);
EXPECT_EQ(0, var_tracker().GetRefCountForObject(plugin_var));
EXPECT_EQ(1u, sink().message_count());
EXPECT_EQ(host_object.value.as_id, GetObjectIDForUniqueReleaseObject());
// Now free via the tracked object, this should free it.
var_tracker().StopTrackingObjectWithNoReference(plugin_var);
EXPECT_EQ(-1, var_tracker().GetRefCountForObject(plugin_var));
// Phase two: Receive via a tracked, then get an addref.
sink().ClearMessages();
plugin_var = var_tracker().TrackObjectWithNoReference(
host_object, plugin_dispatcher());
plugin_var2 = var_tracker().ReceiveObjectPassRef(
host_object, plugin_dispatcher());
EXPECT_EQ(plugin_var.value.as_id, plugin_var2.value.as_id);
EXPECT_EQ(1, var_tracker().GetRefCountForObject(plugin_var));
EXPECT_EQ(1,
var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var));
// Free via the tracked object, this should have no effect.
var_tracker().StopTrackingObjectWithNoReference(plugin_var);
EXPECT_EQ(0,
var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var));
EXPECT_EQ(0u, sink().message_count());
// Now free via the refcount, this should delete it.
var_tracker().ReleaseVar(plugin_var);
EXPECT_EQ(-1, var_tracker().GetRefCountForObject(plugin_var));
EXPECT_EQ(host_object.value.as_id, GetObjectIDForUniqueReleaseObject());
}
TEST_F(PluginVarTrackerTest, RecursiveTrackWithNoRef) {
ProxyAutoLock lock;
PP_Var host_object = MakeObject(12345);
// Receive a tracked object twice.
PP_Var plugin_var = var_tracker().TrackObjectWithNoReference(
host_object, plugin_dispatcher());
EXPECT_EQ(1,
var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var));
PP_Var plugin_var2 = var_tracker().TrackObjectWithNoReference(
host_object, plugin_dispatcher());
EXPECT_EQ(plugin_var.value.as_id, plugin_var2.value.as_id);
EXPECT_EQ(0, var_tracker().GetRefCountForObject(plugin_var));
EXPECT_EQ(2,
var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var));
// Now release those tracked items, the reference should be freed.
var_tracker().StopTrackingObjectWithNoReference(plugin_var);
EXPECT_EQ(1,
var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var));
var_tracker().StopTrackingObjectWithNoReference(plugin_var);
EXPECT_EQ(-1,
var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var));
}
// Tests that objects implemented by the plugin that have no references by
// the plugin get their Deallocate function called on destruction.
TEST_F(PluginVarTrackerTest, PluginObjectInstanceDeleted) {
ProxyAutoLock lock;
PP_Var host_object = MakeObject(12345);
PP_Instance pp_instance = 0x12345;
int deallocate_called = 0;
void* user_data = &deallocate_called;
// Make a var with one reference.
scoped_refptr<ProxyObjectVar> object(
new ProxyObjectVar(plugin_dispatcher(), host_object.value.as_id));
PP_Var plugin_var = MakeObject(var_tracker().AddVar(object.get()));
var_tracker().PluginImplementedObjectCreated(
pp_instance, plugin_var, &mark_on_deallocate_class, user_data);
// Release the plugin ref to the var. WebKit hasn't called destroy so
// we won't get a destroy call.
object.reset();
var_tracker().ReleaseVar(plugin_var);
EXPECT_EQ(0, deallocate_called);
// Synthesize an instance destuction, this should call Deallocate.
var_tracker().DidDeleteInstance(pp_instance);
EXPECT_EQ(1, deallocate_called);
}
// Tests what happens when a plugin keeps a ref to a plugin-implemented
// object var longer than the instance. We should not call the destructor until
// the plugin releases its last ref.
TEST_F(PluginVarTrackerTest, PluginObjectLeaked) {
ProxyAutoLock lock;
PP_Var host_object = MakeObject(12345);
PP_Instance pp_instance = 0x12345;
int deallocate_called = 0;
void* user_data = &deallocate_called;
// Make a var with one reference.
scoped_refptr<ProxyObjectVar> object(
new ProxyObjectVar(plugin_dispatcher(), host_object.value.as_id));
PP_Var plugin_var = MakeObject(var_tracker().AddVar(object.get()));
var_tracker().PluginImplementedObjectCreated(
pp_instance, plugin_var, &mark_on_deallocate_class, user_data);
// Destroy the instance. This should not call deallocate since the plugin
// still has a ref.
var_tracker().DidDeleteInstance(pp_instance);
EXPECT_EQ(0, deallocate_called);
// Release the plugin ref to the var. Since the instance is gone this should
// call deallocate.
object.reset();
var_tracker().ReleaseVar(plugin_var);
EXPECT_EQ(1, deallocate_called);
}
} // namespace proxy
} // namespace ppapi
|