| // Copyright (c) 2009 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 "gtest/gtest.h" |
| #include "gmock/gmock.h" |
| #include "chrome_frame/chrome_frame_automation.h" |
| #include "chrome_frame/chrome_frame_npapi.h" |
| #include "chrome_frame/ff_privilege_check.h" |
| |
| |
| TEST(ChromeFrameNPAPI, DoesNotCrashOnConstruction) { |
| ChromeFrameNPAPI* api = new ChromeFrameNPAPI(); |
| delete api; |
| } |
| |
| |
| // All mocks in the anonymous namespace. |
| namespace { |
| |
| using ::testing::_; |
| using ::testing::Eq; |
| using ::testing::Invoke; |
| using ::testing::Return; |
| using ::testing::StrEq; |
| |
| // Make mocking privilege test easy. |
| class MockPrivilegeTest { |
| public: |
| MockPrivilegeTest() { |
| CHECK(current_ == NULL); |
| current_ = this; |
| } |
| ~MockPrivilegeTest() { |
| CHECK(current_ == this); |
| current_ = NULL; |
| } |
| |
| MOCK_METHOD1(IsFireFoxPrivilegedInvocation, bool(NPP)); |
| |
| static MockPrivilegeTest* current() { return current_; } |
| |
| private: |
| static MockPrivilegeTest* current_; |
| }; |
| |
| MockPrivilegeTest* MockPrivilegeTest::current_ = NULL; |
| |
| const char* kMimeType = "application/chromeframe"; |
| // The default profile name is by default derived from the currently |
| // running executable's name. |
| const wchar_t* kDefaultProfileName = L"chrome_frame_unittests"; |
| |
| |
| class MockNPAPI: public ChromeFrameNPAPI { |
| public: |
| MockNPAPI() : mock_automation_client_(NULL) {} |
| |
| MOCK_METHOD0(CreatePrefService, NpProxyService*()); |
| |
| MOCK_METHOD0(GetLocation, std::string()); |
| MOCK_METHOD0(GetBrowserIncognitoMode, bool()); |
| |
| MOCK_METHOD1(JavascriptToNPObject, virtual NPObject*(const std::string&)); |
| |
| // Make public for test purposes |
| void OnAutomationServerReady() { |
| ChromeFrameNPAPI::OnAutomationServerReady(); |
| } |
| |
| // Neuter this (or it dchecks during testing). |
| void SetReadyState(READYSTATE new_state) {} |
| |
| ChromeFrameAutomationClient* CreateAutomationClient() { |
| return mock_automation_client_; |
| } |
| |
| ChromeFrameAutomationClient* mock_automation_client_; |
| }; |
| |
| class MockAutomationClient: public ChromeFrameAutomationClient { |
| public: |
| MOCK_METHOD6(Initialize, bool(ChromeFrameDelegate*, int, bool, |
| const std::wstring&, const std::wstring&, |
| bool)); |
| MOCK_METHOD1(SetEnableExtensionAutomation, |
| void(const std::vector<std::string>&)); // NOLINT |
| }; |
| |
| class MockProxyService: public NpProxyService { |
| public: |
| MOCK_METHOD2(Initialize, bool(NPP instance, ChromeFrameAutomationClient*)); |
| }; |
| |
| |
| // Test fixture to allow testing the privileged NPAPI APIs |
| class TestNPAPIPrivilegedApi: public ::testing::Test { |
| public: |
| virtual void SetUp() { |
| memset(&instance, 0, sizeof(instance)); |
| |
| // Gets owned & destroyed by mock_api (in the |
| // ChromeFramePlugin<T>::Uninitialize() function). |
| mock_automation = new MockAutomationClient; |
| |
| mock_api.mock_automation_client_ = mock_automation; |
| mock_proxy = new MockProxyService; |
| mock_proxy->AddRef(); |
| mock_proxy_holder.Attach(mock_proxy); |
| } |
| |
| virtual void TearDown() { |
| } |
| |
| void SetupPrivilegeTest(bool is_incognito, |
| bool expect_privilege_check, |
| bool is_privileged, |
| const std::wstring& profile_name, |
| const std::wstring& extra_args) { |
| EXPECT_CALL(mock_api, GetLocation()) |
| .WillOnce(Return(std::string("http://www.google.com"))); |
| EXPECT_CALL(mock_api, CreatePrefService()) |
| .WillOnce(Return(mock_proxy)); |
| EXPECT_CALL(mock_api, GetBrowserIncognitoMode()) |
| .WillOnce(Return(is_incognito)); |
| |
| EXPECT_CALL(*mock_proxy, Initialize(_, _)).WillRepeatedly(Return(false)); |
| |
| EXPECT_CALL(*mock_automation, |
| Initialize(_, _, true, StrEq(profile_name), StrEq(extra_args), false)) |
| .WillOnce(Return(true)); |
| |
| if (expect_privilege_check) { |
| EXPECT_CALL(mock_priv, IsFireFoxPrivilegedInvocation(_)) |
| .WillOnce(Return(is_privileged)); |
| } else { |
| EXPECT_CALL(mock_priv, IsFireFoxPrivilegedInvocation(_)) |
| .Times(0); // Fail if privilege check invoked. |
| } |
| } |
| |
| public: |
| MockNPAPI mock_api; |
| MockAutomationClient* mock_automation; |
| MockProxyService* mock_proxy; |
| ScopedNsPtr<nsISupports> mock_proxy_holder; |
| MockPrivilegeTest mock_priv; |
| NPP_t instance; |
| }; |
| |
| } // namespace |
| |
| // Stub for unittesting. |
| bool IsFireFoxPrivilegedInvocation(NPP npp) { |
| MockPrivilegeTest* mock = MockPrivilegeTest::current(); |
| if (!mock) |
| return false; |
| |
| return mock->IsFireFoxPrivilegedInvocation(npp); |
| } |
| |
| TEST_F(TestNPAPIPrivilegedApi, NoPrivilegeCheckWhenNoArguments) { |
| SetupPrivilegeTest(false, // Not incognito |
| false, // Fail if privilege check is invoked. |
| false, |
| kDefaultProfileName, |
| L""); // No extra args to initialize. |
| |
| // No arguments, no privilege requested. |
| EXPECT_TRUE(mock_api.Initialize(const_cast<NPMIMEType>(kMimeType), |
| &instance, |
| NP_EMBED, |
| 0, 0, 0)); |
| } |
| |
| TEST_F(TestNPAPIPrivilegedApi, NoPrivilegeCheckWhenZeroArgument) { |
| SetupPrivilegeTest(false, // Not incognito |
| false, // Fail if privilege check is invoked. |
| false, |
| kDefaultProfileName, |
| L""); // No extra args to initialize. |
| |
| // Privileged mode explicitly zero. |
| char* argn = "is_privileged"; |
| char* argv = "0"; |
| EXPECT_TRUE(mock_api.Initialize(const_cast<NPMIMEType>(kMimeType), |
| &instance, |
| NP_EMBED, |
| 1, &argn, &argv)); |
| } |
| |
| TEST_F(TestNPAPIPrivilegedApi, NotPrivilegedDoesNotAllowArgsOrProfile) { |
| SetupPrivilegeTest(false, // Not incognito. |
| true, // Fail unless privilege check is invoked. |
| false, // Not privileged. |
| kDefaultProfileName, |
| L""); // No extra arguments allowed. |
| |
| char* argn[] = { |
| "privileged_mode", |
| "chrome_extra_arguments", |
| "chrome_profile_name", |
| }; |
| char *argv[] = { |
| "1", |
| "foo", |
| "bar", |
| }; |
| EXPECT_TRUE(mock_api.Initialize(const_cast<NPMIMEType>(kMimeType), |
| &instance, |
| NP_EMBED, |
| arraysize(argn), argn, argv)); |
| } |
| |
| TEST_F(TestNPAPIPrivilegedApi, PrivilegedAllowsArgsAndProfile) { |
| SetupPrivilegeTest(false, // Not incognito. |
| true, // Fail unless privilege check is invoked. |
| true, // Privileged mode. |
| L"custom_profile_name", // Custom profile expected. |
| L"-bar=far"); // Extra arguments expected |
| |
| // With privileged mode we expect automation to be enabled. |
| EXPECT_CALL(*mock_automation, SetEnableExtensionAutomation(_)) |
| .Times(1); |
| |
| char* argn[] = { |
| "privileged_mode", |
| "chrome_extra_arguments", |
| "chrome_profile_name", |
| }; |
| char *argv[] = { |
| "1", |
| "-bar=far", |
| "custom_profile_name", |
| }; |
| EXPECT_TRUE(mock_api.Initialize(const_cast<NPMIMEType>(kMimeType), |
| &instance, |
| NP_EMBED, |
| arraysize(argn), argn, argv)); |
| |
| // Since we're mocking out ChromeFrameAutomationClient::Initialize, we need |
| // to tickle this explicitly. |
| mock_api.OnAutomationServerReady(); |
| } |
| |
| |
| namespace { |
| |
| static const NPIdentifier kOnPrivateMessageId = |
| reinterpret_cast<NPIdentifier>(0x100); |
| static const NPIdentifier kPostPrivateMessageId = |
| reinterpret_cast<NPIdentifier>(0x100); |
| |
| |
| class MockNetscapeFuncs { |
| public: |
| MockNetscapeFuncs() { |
| CHECK(NULL == current_); |
| current_ = this; |
| } |
| |
| ~MockNetscapeFuncs() { |
| CHECK(this == current_); |
| current_ = NULL; |
| } |
| |
| MOCK_METHOD3(GetValue, NPError(NPP, NPNVariable, void *)); |
| MOCK_METHOD3(GetStringIdentifiers, void(const NPUTF8 **, |
| int32_t, |
| NPIdentifier *)); // NOLINT |
| MOCK_METHOD1(RetainObject, NPObject*(NPObject*)); // NOLINT |
| MOCK_METHOD1(ReleaseObject, void(NPObject*)); // NOLINT |
| |
| |
| void GetPrivilegedStringIdentifiers(const NPUTF8 **names, |
| int32_t name_count, |
| NPIdentifier *identifiers) { |
| for (int32_t i = 0; i < name_count; ++i) { |
| if (0 == strcmp(names[i], "onprivatemessage")) { |
| identifiers[i] = kOnPrivateMessageId; |
| } else if (0 == strcmp(names[i], "postPrivateMessage")) { |
| identifiers[i] = kPostPrivateMessageId; |
| } else { |
| identifiers[i] = 0; |
| } |
| } |
| } |
| |
| static const NPNetscapeFuncs* netscape_funcs() { |
| return &netscape_funcs_; |
| } |
| |
| private: |
| static NPError MockGetValue(NPP instance, |
| NPNVariable variable, |
| void *ret_value) { |
| DCHECK(current_); |
| return current_->GetValue(instance, variable, ret_value); |
| } |
| |
| static void MockGetStringIdentifiers(const NPUTF8 **names, |
| int32_t name_count, |
| NPIdentifier *identifiers) { |
| DCHECK(current_); |
| return current_->GetStringIdentifiers(names, name_count, identifiers); |
| } |
| |
| static NPObject* MockRetainObject(NPObject* obj) { |
| DCHECK(current_); |
| return current_->RetainObject(obj); |
| } |
| |
| static void MockReleaseObject(NPObject* obj) { |
| DCHECK(current_); |
| current_->ReleaseObject(obj); |
| } |
| |
| static MockNetscapeFuncs* current_; |
| static NPNetscapeFuncs netscape_funcs_; |
| }; |
| |
| MockNetscapeFuncs* MockNetscapeFuncs::current_ = NULL; |
| NPNetscapeFuncs MockNetscapeFuncs::netscape_funcs_ = { |
| 0, // size |
| 0, // version |
| NULL, // geturl |
| NULL, // posturl |
| NULL, // requestread |
| NULL, // newstream |
| NULL, // write |
| NULL, // destroystream |
| NULL, // status |
| NULL, // uagent |
| NULL, // memalloc |
| NULL, // memfree |
| NULL, // memflush |
| NULL, // reloadplugins |
| NULL, // getJavaEnv |
| NULL, // getJavaPeer |
| NULL, // geturlnotify |
| NULL, // posturlnotify |
| MockGetValue, // getvalue |
| NULL, // setvalue |
| NULL, // invalidaterect |
| NULL, // invalidateregion |
| NULL, // forceredraw |
| NULL, // getstringidentifier |
| MockGetStringIdentifiers, // getstringidentifiers |
| NULL, // getintidentifier |
| NULL, // identifierisstring |
| NULL, // utf8fromidentifier |
| NULL, // intfromidentifier |
| NULL, // createobject |
| MockRetainObject, // retainobject |
| MockReleaseObject, // releaseobject |
| NULL, // invoke |
| NULL, // invokeDefault |
| NULL, // evaluate |
| NULL, // getproperty |
| NULL, // setproperty |
| NULL, // removeproperty |
| NULL, // hasproperty |
| NULL, // hasmethod |
| NULL, // releasevariantvalue |
| NULL, // setexception |
| NULL, // pushpopupsenabledstate |
| NULL, // poppopupsenabledstate |
| NULL, // enumerate |
| NULL, // pluginthreadasynccall |
| NULL, // construct |
| }; |
| |
| NPObject* const kMockNPObject = reinterpret_cast<NPObject*>(0xCafeBabe); |
| |
| class TestNPAPIPrivilegedProperty: public TestNPAPIPrivilegedApi { |
| public: |
| virtual void SetUp() { |
| TestNPAPIPrivilegedApi::SetUp(); |
| npapi::InitializeBrowserFunctions( |
| const_cast<NPNetscapeFuncs*>(mock_funcs.netscape_funcs())); |
| |
| // Expect calls to release and retain objects. |
| EXPECT_CALL(mock_funcs, RetainObject(kMockNPObject)) |
| .WillRepeatedly(Return(kMockNPObject)); |
| EXPECT_CALL(mock_funcs, ReleaseObject(kMockNPObject)) |
| .WillRepeatedly(Return()); |
| |
| // And we should expect SetEnableExtensionAutomation to be called |
| // for privileged tests. |
| EXPECT_CALL(*mock_automation, SetEnableExtensionAutomation(_)) |
| .WillRepeatedly(Return()); |
| |
| // Initializes identifiers. |
| EXPECT_CALL(mock_funcs, GetStringIdentifiers(_, _, _)) |
| .WillRepeatedly( |
| Invoke(&mock_funcs, |
| &MockNetscapeFuncs::GetPrivilegedStringIdentifiers)); |
| MockNPAPI::InitializeIdentifiers(); |
| } |
| |
| virtual void TearDown() { |
| npapi::UninitializeBrowserFunctions(); |
| TestNPAPIPrivilegedApi::TearDown(); |
| } |
| |
| public: |
| MockNetscapeFuncs mock_funcs; |
| }; |
| |
| |
| } // namespace |
| |
| TEST_F(TestNPAPIPrivilegedProperty, |
| NonPrivilegedOnPrivateMessageInitializationFails) { |
| // Attempt setting onprivatemessage when not privileged. |
| SetupPrivilegeTest(false, // not incognito. |
| true, // expect privilege check. |
| false, // not privileged. |
| kDefaultProfileName, |
| L""); |
| |
| char* on_private_message_str = "onprivatemessage()"; |
| EXPECT_CALL(mock_api, JavascriptToNPObject(StrEq(on_private_message_str))) |
| .Times(0); // this should not be called. |
| |
| char* argn[] = { |
| "privileged_mode", |
| "onprivatemessage", |
| }; |
| char* argv[] = { |
| "1", |
| on_private_message_str, |
| }; |
| EXPECT_TRUE(mock_api.Initialize(const_cast<NPMIMEType>(kMimeType), |
| &instance, |
| NP_EMBED, |
| arraysize(argn), argn, argv)); |
| // Shouldn't be able to retrieve it. |
| NPVariant var; |
| VOID_TO_NPVARIANT(var); |
| EXPECT_FALSE(mock_api.GetProperty(kOnPrivateMessageId, &var)); |
| EXPECT_TRUE(NPVARIANT_IS_VOID(var)); |
| |
| mock_api.Uninitialize(); |
| } |
| |
| TEST_F(TestNPAPIPrivilegedProperty, |
| PrivilegedOnPrivateMessageInitializationSucceeds) { |
| // Set onprivatemessage argument when privileged. |
| SetupPrivilegeTest(false, // not incognito. |
| true, // expect privilege check. |
| true, // privileged. |
| kDefaultProfileName, |
| L""); |
| |
| char* on_private_message_str = "onprivatemessage()"; |
| NPObject* on_private_object = kMockNPObject; |
| EXPECT_CALL(mock_api, JavascriptToNPObject(StrEq(on_private_message_str))) |
| .WillOnce(Return(on_private_object)); |
| |
| char* argn[] = { |
| "privileged_mode", |
| "onprivatemessage", |
| }; |
| char* argv[] = { |
| "1", |
| on_private_message_str, |
| }; |
| EXPECT_TRUE(mock_api.Initialize(const_cast<NPMIMEType>(kMimeType), |
| &instance, |
| NP_EMBED, |
| arraysize(argn), argn, argv)); |
| // The property should have been set, verify that |
| // we can retrieve it and test it for correct value. |
| NPVariant var; |
| VOID_TO_NPVARIANT(var); |
| EXPECT_TRUE(mock_api.GetProperty(kOnPrivateMessageId, &var)); |
| EXPECT_TRUE(NPVARIANT_IS_OBJECT(var)); |
| EXPECT_EQ(kMockNPObject, NPVARIANT_TO_OBJECT(var)); |
| |
| mock_api.Uninitialize(); |
| } |
| |
| TEST_F(TestNPAPIPrivilegedProperty, |
| NonPrivilegedOnPrivateMessageAssignmentFails) { |
| // Assigning to onprivatemessage when not privileged should fail. |
| SetupPrivilegeTest(false, // not incognito. |
| true, // expect privilege check. |
| false, // not privileged. |
| kDefaultProfileName, |
| L""); |
| |
| char* argn = "privileged_mode"; |
| char* argv = "1"; |
| EXPECT_TRUE(mock_api.Initialize(const_cast<NPMIMEType>(kMimeType), |
| &instance, |
| NP_EMBED, |
| 1, &argn, &argv)); |
| |
| NPVariant var = {}; |
| OBJECT_TO_NPVARIANT(kMockNPObject, var); |
| // Setting should fail. |
| EXPECT_FALSE(mock_api.SetProperty(kOnPrivateMessageId, &var)); |
| |
| // And so should getting. |
| NULL_TO_NPVARIANT(var); |
| EXPECT_FALSE(mock_api.GetProperty(kOnPrivateMessageId, &var)); |
| |
| mock_api.Uninitialize(); |
| } |
| |
| TEST_F(TestNPAPIPrivilegedProperty, |
| PrivilegedOnPrivateMessageAssignmentSucceeds) { |
| // Assigning to onprivatemessage when privileged should succeed. |
| SetupPrivilegeTest(false, // not incognito. |
| true, // expect privilege check. |
| true, // privileged. |
| kDefaultProfileName, |
| L""); |
| |
| char* argn = "privileged_mode"; |
| char* argv = "1"; |
| EXPECT_TRUE(mock_api.Initialize(const_cast<NPMIMEType>(kMimeType), |
| &instance, |
| NP_EMBED, |
| 1, &argn, &argv)); |
| |
| NPVariant var = {}; |
| VOID_TO_NPVARIANT(var); |
| // Getting the property when NULL fails under current implementation. |
| // I shouldn't have thought this is correct behavior, e.g. I should |
| // have thought retrieving the NULL should succeed, but this is consistent |
| // with how other properties behave. |
| // TODO(robertshield): investigate and/or fix. |
| EXPECT_FALSE(mock_api.GetProperty(kOnPrivateMessageId, &var)); |
| // EXPECT_TRUE(NPVARIANT_IS_OBJECT(var)); |
| // EXPECT_EQ(NULL, NPVARIANT_TO_OBJECT(var)); |
| |
| // Setting the property should succeed. |
| OBJECT_TO_NPVARIANT(kMockNPObject, var); |
| EXPECT_TRUE(mock_api.SetProperty(kOnPrivateMessageId, &var)); |
| |
| // And fething it should return the value we just set. |
| VOID_TO_NPVARIANT(var); |
| EXPECT_TRUE(mock_api.GetProperty(kOnPrivateMessageId, &var)); |
| EXPECT_TRUE(NPVARIANT_IS_OBJECT(var)); |
| EXPECT_EQ(kMockNPObject, NPVARIANT_TO_OBJECT(var)); |
| |
| mock_api.Uninitialize(); |
| } |
| |
| // TODO(siggi): test invoking postPrivateMessage. |