// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ppapi/c/dev/ppb_var_deprecated.h" #include "ppapi/c/dev/ppp_class_deprecated.h" #include "ppapi/c/pp_var.h" #include "ppapi/c/ppb_var.h" #include "ppapi/c/ppp_instance.h" #include "ppapi/c/private/ppp_instance_private.h" #include "ppapi/proxy/host_dispatcher.h" #include "ppapi/proxy/ppapi_proxy_test.h" namespace ppapi { namespace proxy { namespace { const PP_Instance kInstance = 0xdeadbeef; PP_Var MakeObjectVar(int64_t object_id) { PP_Var ret; ret.type = PP_VARTYPE_OBJECT; ret.value.as_id = object_id; return ret; } PluginDispatcher* plugin_dispatcher = NULL; // Return the plugin-side proxy for PPB_Var_Deprecated. const PPB_Var_Deprecated* plugin_var_deprecated_if() { // The test code must set the plugin dispatcher. CHECK(plugin_dispatcher); // Grab the plugin-side proxy for PPB_Var_Deprecated (for CreateObject). return static_cast( plugin_dispatcher->GetBrowserInterface( PPB_VAR_DEPRECATED_INTERFACE)); } // Mock PPP_Instance_Private. PP_Var instance_obj; PP_Var GetInstanceObject(PP_Instance /*instance*/) { // The 1 ref we got from CreateObject will be passed to the host. We want to // have a ref of our own. plugin_var_deprecated_if()->AddRef(instance_obj); return instance_obj; } PPP_Instance_Private ppp_instance_private_mock = { &GetInstanceObject }; // We need to mock PPP_Instance, so that we can create and destroy the pretend // instance that PPP_Instance_Private uses. PP_Bool DidCreate(PP_Instance /*instance*/, uint32_t /*argc*/, const char* /*argn*/[], const char* /*argv*/[]) { // Create an object var. This should exercise the typical path for creating // instance objects. instance_obj = plugin_var_deprecated_if()->CreateObject(kInstance, NULL, NULL); return PP_TRUE; } void DidDestroy(PP_Instance /*instance*/) { // Decrement the reference count for our instance object. It should be // deleted. plugin_var_deprecated_if()->Release(instance_obj); } PPP_Instance_1_0 ppp_instance_mock = { &DidCreate, &DidDestroy }; } // namespace // Mock PPB_Var_Deprecated, so that we can emulate creating an Object Var. std::map id_refcount_map; void AddRefVar(PP_Var var) { CHECK(var.type >= PP_VARTYPE_STRING); // Must be a ref-counted type. CHECK(id_refcount_map.find(var.value.as_id) != id_refcount_map.end()); ++id_refcount_map[var.value.as_id]; } void ReleaseVar(PP_Var var) { CHECK(var.type >= PP_VARTYPE_STRING); // Must be a ref-counted type. std::map::iterator iter = id_refcount_map.find(var.value.as_id); CHECK(iter != id_refcount_map.end()); CHECK(iter->second > 0); if (--(iter->second) == 0) id_refcount_map.erase(iter); } PP_Var CreateObject(PP_Instance instance, const PPP_Class_Deprecated* /*ppp_class*/, void* /*ppp_class_data*/) { static int64_t last_id = 0; ++last_id; // Set the refcount to 0. It should really be 1, but that ref gets passed to // the plugin immediately (and we don't emulate all of that behavior here). id_refcount_map[last_id] = 0; return MakeObjectVar(last_id); } const PPB_Var_Deprecated ppb_var_deprecated_mock = { &AddRefVar, &ReleaseVar, NULL, // VarFromUtf8 NULL, // VarToUtf8 NULL, // HasProperty NULL, // HasMethod NULL, // GetProperty NULL, // EnumerateProperties NULL, // SetProperty NULL, // RemoveProperty NULL, // Call NULL, // Construct NULL, // IsInstanceOf &CreateObject }; const PPB_Var ppb_var_mock = { &AddRefVar, &ReleaseVar }; class PPP_Instance_Private_ProxyTest : public TwoWayTest { public: PPP_Instance_Private_ProxyTest() : TwoWayTest(TwoWayTest::TEST_PPP_INTERFACE) { plugin().RegisterTestInterface(PPP_INSTANCE_PRIVATE_INTERFACE, &ppp_instance_private_mock); plugin().RegisterTestInterface(PPP_INSTANCE_INTERFACE_1_0, &ppp_instance_mock); host().RegisterTestInterface(PPB_VAR_DEPRECATED_INTERFACE, &ppb_var_deprecated_mock); host().RegisterTestInterface(PPB_VAR_INTERFACE, &ppb_var_mock); } }; TEST_F(PPP_Instance_Private_ProxyTest, PPPInstancePrivate) { // This test controls its own instance; we can't use the one that // PluginProxyTestHarness provides. ASSERT_NE(kInstance, pp_instance()); HostDispatcher::SetForInstance(kInstance, host().host_dispatcher()); // This file-local global is used by the PPP_Instance mock above in order to // access PPB_Var_Deprecated. plugin_dispatcher = plugin().plugin_dispatcher(); // Grab the host-side proxy for PPP_Instance and PPP_Instance_Private. const PPP_Instance_Private* ppp_instance_private = static_cast( host().host_dispatcher()->GetProxiedInterface( PPP_INSTANCE_PRIVATE_INTERFACE)); const PPP_Instance_1_0* ppp_instance = static_cast( host().host_dispatcher()->GetProxiedInterface( PPP_INSTANCE_INTERFACE_1_0)); // Initialize an Instance, so that the plugin-side machinery will work // properly. EXPECT_EQ(PP_TRUE, ppp_instance->DidCreate(kInstance, 0, NULL, NULL)); // Now instance_obj is valid and should have a ref-count of 1. PluginVarTracker& plugin_var_tracker = *PluginGlobals::Get()->plugin_var_tracker(); // Check the plugin-side reference count. EXPECT_EQ(1, plugin_var_tracker.GetRefCountForObject(instance_obj)); // Check the host-side var and reference count. ASSERT_EQ(1u, id_refcount_map.size()); EXPECT_EQ(plugin_var_tracker.GetHostObject(instance_obj).value.as_id, id_refcount_map.begin()->first); EXPECT_EQ(0, id_refcount_map.begin()->second); // Call from the browser side to get the instance object. PP_Var host_obj = ppp_instance_private->GetInstanceObject(kInstance); EXPECT_EQ(instance_obj.type, host_obj.type); EXPECT_EQ(host_obj.value.as_id, plugin_var_tracker.GetHostObject(instance_obj).value.as_id); EXPECT_EQ(1, plugin_var_tracker.GetRefCountForObject(instance_obj)); ASSERT_EQ(1u, id_refcount_map.size()); // The browser should be passed a reference. EXPECT_EQ(1, id_refcount_map.begin()->second); // The plugin is going away; generally, so will all references to its instance // object. ReleaseVar(host_obj); // Destroy the instance. DidDestroy above decrements the reference count for // instance_obj, so it should also be destroyed. ppp_instance->DidDestroy(kInstance); EXPECT_EQ(-1, plugin_var_tracker.GetRefCountForObject(instance_obj)); // Check the host-side reference count. EXPECT_EQ(0u, id_refcount_map.size()); } } // namespace proxy } // namespace ppapi