summaryrefslogtreecommitdiffstats
path: root/ceee/common
diff options
context:
space:
mode:
authorinitial.commit@chromium.org <initial.commit@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-02 02:14:31 +0000
committerinitial.commit@chromium.org <initial.commit@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-02 02:14:31 +0000
commit5a7bdf208c28c210b39cff63d1cf91302b02821b (patch)
tree5ff484561562f78b29d2670168a9e3b40b48dcab /ceee/common
parent26a54a5e0f4603c8fda2fe51789d331125993c9a (diff)
downloadchromium_src-5a7bdf208c28c210b39cff63d1cf91302b02821b.zip
chromium_src-5a7bdf208c28c210b39cff63d1cf91302b02821b.tar.gz
chromium_src-5a7bdf208c28c210b39cff63d1cf91302b02821b.tar.bz2
Checking in the initial version of CEEE (Chrome Extensions Execution
Environment), an optional feature of Chrome Frame that acts as an adapter layer for a subset of the Chrome Extension APIs. This enables extensions that stick to the supported subset of APIs to work in the context of Chrome Frame with minimal or sometimes no changes. See http://www.chromium.org/developers/design-documents/ceee for an overview of the design of CEEE. TEST=unit tests (run ceee/smoke_tests.bat as an administrator on Windows) BUG=none git-svn-id: svn://svn.chromium.org/chrome/trunk/src@64712 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ceee/common')
-rw-r--r--ceee/common/com_utils.cc79
-rw-r--r--ceee/common/com_utils.h93
-rw-r--r--ceee/common/com_utils_unittest.cc143
-rw-r--r--ceee/common/common.gyp106
-rw-r--r--ceee/common/common_unittest_main.cc23
-rw-r--r--ceee/common/initializing_coclass.h123
-rw-r--r--ceee/common/initializing_coclass.py107
-rw-r--r--ceee/common/initializing_coclass_unittest.cc230
-rw-r--r--ceee/common/install_utils.cc51
-rw-r--r--ceee/common/install_utils.h40
-rw-r--r--ceee/common/install_utils_unittest.cc114
-rw-r--r--ceee/common/np_browser_functions.cc435
-rw-r--r--ceee/common/np_browser_functions.h260
-rw-r--r--ceee/common/npobject_impl.cc121
-rw-r--r--ceee/common/npobject_impl.h119
-rw-r--r--ceee/common/npobject_impl_unittest.cc146
-rw-r--r--ceee/common/npplugin_impl.cc138
-rw-r--r--ceee/common/npplugin_impl.h147
-rw-r--r--ceee/common/npplugin_impl_unittest.cc167
-rw-r--r--ceee/common/process_utils_win.cc330
-rw-r--r--ceee/common/process_utils_win.h122
-rw-r--r--ceee/common/process_utils_win_unittest.cc571
-rw-r--r--ceee/common/sidestep_integration.cc20
-rw-r--r--ceee/common/sidestep_unittest.cc12
-rw-r--r--ceee/common/window_utils.cc142
-rw-r--r--ceee/common/window_utils.h76
-rw-r--r--ceee/common/window_utils_unittest.cc417
-rw-r--r--ceee/common/windows_constants.cc22
-rw-r--r--ceee/common/windows_constants.h46
29 files changed, 4400 insertions, 0 deletions
diff --git a/ceee/common/com_utils.cc b/ceee/common/com_utils.cc
new file mode 100644
index 0000000..b251ca1
--- /dev/null
+++ b/ceee/common/com_utils.cc
@@ -0,0 +1,79 @@
+// Copyright (c) 2010 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.
+//
+// Utilities for COM objects, error codes etc.
+
+#include "ceee/common/com_utils.h"
+
+#include <atlbase.h>
+#include <shlwapi.h>
+
+#include "base/string_util.h"
+
+namespace com {
+
+std::ostream& operator<<(std::ostream& os, const LogHr& hr) {
+ // Looks up the human-readable system message for the HRESULT code
+ // and since we're not passing any params to FormatMessage, we don't
+ // want inserts expanded.
+ const DWORD kFlags = FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS;
+ char error_text[4096] = { '\0' };
+ DWORD message_length = ::FormatMessageA(kFlags, 0, hr.hr_, 0, error_text,
+ arraysize(error_text), NULL);
+ std::string error(error_text);
+ TrimString(error, kWhitespaceASCII, &error);
+
+ return os << "[hr=0x" << std::hex << hr.hr_ << ", msg=" << error << "]";
+}
+
+std::ostream& operator<<(std::ostream& os, const LogWe& we) {
+ // Looks up the human-readable system message for the Windows error code
+ // and since we're not passing any params to FormatMessage, we don't
+ // want inserts expanded.
+ const DWORD kFlags = FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS;
+ char error_text[4096] = { '\0' };
+ DWORD message_length = ::FormatMessageA(kFlags, 0, we.we_, 0, error_text,
+ arraysize(error_text), NULL);
+ std::string error(error_text);
+ TrimString(error, kWhitespaceASCII, &error);
+
+ return os << "[we=" << we.we_ << ", msg=" << error << "]";
+}
+
+// Seam so unit tests can mock out the side effects of this function.
+HRESULT UpdateRegistryFromResourceImpl(int reg_id, BOOL should_register,
+ _ATL_REGMAP_ENTRY* entries) {
+ return _pAtlModule->UpdateRegistryFromResource(
+ reg_id, should_register, entries);
+}
+
+HRESULT ModuleRegistrationWithoutAppid(int reg_id, BOOL should_register) {
+ // Get our module path.
+ wchar_t module_path[MAX_PATH] = { 0 };
+ if (0 == ::GetModuleFileName(_AtlBaseModule.GetModuleInstance(),
+ module_path, arraysize(module_path))) {
+ NOTREACHED();
+ return AlwaysErrorFromLastError();
+ }
+
+ // Copy the filename.
+ std::wstring module_basename(::PathFindFileName(module_path));
+ // And strip the filename.
+ if (!::PathRemoveFileSpec(module_path)) {
+ NOTREACHED();
+ return AlwaysErrorFromLastError();
+ }
+
+ _ATL_REGMAP_ENTRY entries[] = {
+ { L"MODULE_PATH", module_path },
+ { L"MODULE_BASENAME", module_basename.c_str() },
+ { NULL, NULL}
+ };
+
+ return UpdateRegistryFromResourceImpl(reg_id, should_register, entries);
+}
+
+} // namespace com
diff --git a/ceee/common/com_utils.h b/ceee/common/com_utils.h
new file mode 100644
index 0000000..e531ba4
--- /dev/null
+++ b/ceee/common/com_utils.h
@@ -0,0 +1,93 @@
+// Copyright (c) 2010 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.
+//
+// Utilities for COM objects, error codes etc.
+
+#ifndef CEEE_COMMON_COM_UTILS_H_
+#define CEEE_COMMON_COM_UTILS_H_
+
+#include <windows.h>
+#include <wtypes.h>
+
+#include <ostream>
+
+namespace com {
+
+// Returns the provided hr if it is an error, otherwise default_error.
+inline HRESULT AlwaysError(HRESULT hr, HRESULT default_error) {
+ if (FAILED(hr)) {
+ return hr;
+ } else {
+ return default_error;
+ }
+}
+
+// Returns the provided hr if it is an error, otherwise E_FAIL.
+inline HRESULT AlwaysError(HRESULT hr) {
+ return AlwaysError(hr, E_FAIL);
+}
+
+// Converts a Win32 result code to a HRESULT. If the 'win32_code'
+// does not indicate an error, it returns 'default_error'.
+inline HRESULT AlwaysErrorFromWin32(DWORD win32_code,
+ HRESULT default_error) {
+ HRESULT hr = HRESULT_FROM_WIN32(win32_code);
+ return AlwaysError(hr, default_error);
+}
+
+// Converts a Win32 result code to a HRESULT. If the 'win32_code'
+// does not indicate an error, it returns E_FAIL
+inline HRESULT AlwaysErrorFromWin32(DWORD win32_code) {
+ return AlwaysErrorFromWin32(win32_code, E_FAIL);
+}
+
+// Returns the HRESULT equivalent of GetLastError(), unless it does not
+// represent an error in which case it returns 'default_error'.
+inline HRESULT AlwaysErrorFromLastError(HRESULT default_error) {
+ return AlwaysErrorFromWin32(::GetLastError(), default_error);
+}
+
+// Returns the HRESULT equivalent of GetLastError(), unless it does not
+// represent an error in which case it returns E_FAIL.
+inline HRESULT AlwaysErrorFromLastError() {
+ return AlwaysErrorFromLastError(E_FAIL);
+}
+
+// Return the specified string, or the empty string if it is NULL.
+inline const wchar_t* ToString(BSTR str) {
+ return str ? str : L"";
+}
+
+// Logs HRESULTs verbosely, with the error code and human-readable error
+// text if available.
+class LogHr {
+ public:
+ explicit LogHr(HRESULT hr) : hr_(hr) {}
+ private:
+ HRESULT hr_;
+ friend std::ostream& operator<<(std::ostream&, const LogHr&);
+};
+
+std::ostream& operator<<(std::ostream& os, const LogHr& hr);
+
+// Logs Windows errors verbosely, with the error code and human-readable error
+// text if available.
+class LogWe {
+ public:
+ LogWe() : we_(::GetLastError()) {}
+ explicit LogWe(DWORD we) : we_(we) {}
+ private:
+ DWORD we_;
+ friend std::ostream& operator<<(std::ostream&, const LogWe&);
+};
+
+std::ostream& operator<<(std::ostream& os, const LogWe& we);
+
+// Use to register a .reg file that needs %MODULE_PATH% and %MODULE_BASENAME%
+// replaceables, but no %APPID%.
+HRESULT ModuleRegistrationWithoutAppid(int reg_id, BOOL should_register);
+
+} // namespace com
+
+#endif // CEEE_COMMON_COM_UTILS_H_
diff --git a/ceee/common/com_utils_unittest.cc b/ceee/common/com_utils_unittest.cc
new file mode 100644
index 0000000..ea27f84
--- /dev/null
+++ b/ceee/common/com_utils_unittest.cc
@@ -0,0 +1,143 @@
+// Copyright (c) 2010 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.
+//
+// Unit tests for COM utils.
+
+#include "ceee/common/com_utils.h"
+
+#include <atlcomcli.h>
+
+#include "ceee/testing/utils/mock_win32.h"
+#include "ceee/testing/utils/mock_static.h"
+#include "ceee/testing/utils/test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace com {
+
+HRESULT UpdateRegistryFromResourceImpl(int reg_id, BOOL should_register,
+ _ATL_REGMAP_ENTRY* entries);
+
+} // namespace com;
+
+namespace {
+
+using testing::_;
+using testing::DoAll;
+using testing::CopyStringToArgument;
+using testing::Field;
+using testing::Pointee;
+using testing::Return;
+using testing::StrEq;
+
+TEST(ComUtils, AlwaysError) {
+ EXPECT_EQ(com::AlwaysError(S_OK, E_INVALIDARG), E_INVALIDARG);
+ EXPECT_EQ(com::AlwaysError(E_FAIL, E_INVALIDARG), E_FAIL);
+
+ EXPECT_EQ(com::AlwaysError(S_OK), E_FAIL);
+ EXPECT_EQ(com::AlwaysError(E_FAIL), E_FAIL);
+
+ EXPECT_EQ(com::AlwaysErrorFromWin32(NO_ERROR, E_INVALIDARG), E_INVALIDARG);
+ EXPECT_EQ(com::AlwaysErrorFromWin32(NO_ERROR), E_FAIL);
+
+ EXPECT_EQ(com::AlwaysErrorFromWin32(ERROR_ACCESS_DENIED, E_INVALIDARG),
+ HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED));
+ EXPECT_EQ(com::AlwaysErrorFromWin32(ERROR_ACCESS_DENIED),
+ HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED));
+
+ DWORD last_error = ::GetLastError();
+
+ ::SetLastError(NO_ERROR);
+ EXPECT_EQ(com::AlwaysErrorFromLastError(E_INVALIDARG), E_INVALIDARG);
+ EXPECT_EQ(com::AlwaysErrorFromLastError(), E_FAIL);
+
+ ::SetLastError(ERROR_ACCESS_DENIED);
+ EXPECT_EQ(com::AlwaysErrorFromLastError(E_INVALIDARG),
+ HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED));
+ EXPECT_EQ(com::AlwaysErrorFromLastError(),
+ HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED));
+
+ ::SetLastError(last_error);
+}
+
+TEST(ComUtils, ToString) {
+ CComBSTR filled(L"hello");
+ EXPECT_STREQ(com::ToString(filled), L"hello");
+
+ CComBSTR empty;
+ EXPECT_STREQ(com::ToString(empty), L"");
+}
+
+TEST(ComUtils, HrLog) {
+ {
+ std::ostringstream stream;
+ stream << com::LogHr(S_OK);
+ std::string str = stream.str();
+ EXPECT_NE(str.find("0x0,"), std::string::npos);
+ EXPECT_NE(str.find("msg="), std::string::npos);
+ }
+
+ {
+ std::ostringstream stream;
+ stream << com::LogHr(E_FAIL);
+ std::string str = stream.str();
+ EXPECT_NE(str.find("0x80004005,"), std::string::npos);
+ EXPECT_NE(str.find("msg=Unspecified error"), std::string::npos);
+ }
+}
+
+TEST(ComUtils, WeLog) {
+ {
+ std::ostringstream stream;
+ stream << com::LogWe(ERROR_SUCCESS);
+ std::string str = stream.str();
+ EXPECT_NE(str.find("[we=0,"), std::string::npos);
+ EXPECT_NE(str.find("msg=The operation completed successfully"),
+ std::string::npos);
+ }
+
+ {
+ std::ostringstream stream;
+ stream << com::LogWe(ERROR_INVALID_FUNCTION);
+ std::string str = stream.str();
+ EXPECT_NE(str.find("[we=1,"), std::string::npos);
+ EXPECT_NE(str.find("msg=Incorrect function"), std::string::npos);
+ }
+}
+
+HRESULT UpdateRegistryFromResourceImpl(int reg_id, BOOL should_register,
+ _ATL_REGMAP_ENTRY* entries) {
+ return _pAtlModule->UpdateRegistryFromResource(
+ reg_id, should_register, entries);
+}
+
+// Mock out UpdateRegistryFromResourceImpl.
+MOCK_STATIC_CLASS_BEGIN(MockModuleRegistrationWithoutAppid)
+ MOCK_STATIC_INIT_BEGIN(MockModuleRegistrationWithoutAppid)
+ MOCK_STATIC_INIT2(com::UpdateRegistryFromResourceImpl,
+ UpdateRegistryFromResourceImpl);
+ MOCK_STATIC_INIT_END()
+
+ MOCK_STATIC3(HRESULT, , UpdateRegistryFromResourceImpl,
+ int, BOOL, _ATL_REGMAP_ENTRY*);
+MOCK_STATIC_CLASS_END(MockModuleRegistrationWithoutAppid)
+
+MATCHER(ValidateRegmapComponents, "Checks regmap for correct values") {
+ return 0 == lstrcmpW(arg[0].szData, L"c:\\foo\\blat") &&
+ 0 == lstrcmpW(arg[1].szData, L"bingo.exe");
+}
+
+TEST_DEBUG_ONLY(ComUtils, ModuleRegistrationWithoutAppid) {
+ MockModuleRegistrationWithoutAppid mock;
+ EXPECT_CALL(mock, UpdateRegistryFromResourceImpl(
+ 42, TRUE, ValidateRegmapComponents())).WillRepeatedly(Return(S_OK));
+
+ testing::MockKernel32 kernel32;
+ EXPECT_CALL(kernel32, GetModuleFileName(_, _, _)).WillRepeatedly(DoAll(
+ CopyStringToArgument<1>(L"c:\\foo\\blat\\bingo.exe"), Return(1)));
+
+ EXPECT_EQ(S_OK, com::ModuleRegistrationWithoutAppid(42, TRUE));
+}
+
+} // namespace
diff --git a/ceee/common/common.gyp b/ceee/common/common.gyp
new file mode 100644
index 0000000..ce8cc2b
--- /dev/null
+++ b/ceee/common/common.gyp
@@ -0,0 +1,106 @@
+# Copyright (c) 2010 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.
+
+{
+ 'variables': {
+ 'chromium_code': 1,
+ },
+ 'includes': [
+ '../../build/common.gypi',
+ '../../ceee/common.gypi',
+ ],
+ 'target_defaults': {
+ 'include_dirs': [
+ '../..',
+ ],
+ },
+ 'targets': [
+ {
+ 'target_name': 'initializing_coclass',
+ 'type': 'none',
+ 'sources': [
+ 'initializing_coclass.h',
+ 'initializing_coclass.py',
+ ],
+ 'actions': [
+ {
+ 'action_name': 'make_initializing_coclass',
+ 'msvs_cygwin_shell': 0,
+ 'msvs_quote_cmd': 0,
+ 'inputs': [
+ 'initializing_coclass.py',
+ ],
+ 'outputs': [
+ '<(SHARED_INTERMEDIATE_DIR)/initializing_coclass_gen.inl',
+ ],
+ 'action': [
+ '<@(python)',
+ 'initializing_coclass.py',
+ '"<(SHARED_INTERMEDIATE_DIR)/initializing_coclass_gen.inl"',
+ ],
+ },
+ ],
+ # All who use this need to be able to find the .inl file we generate.
+ 'all_dependent_settings': {
+ 'include_dirs': ['<(SHARED_INTERMEDIATE_DIR)'],
+ },
+ },
+ {
+ 'target_name': 'ceee_common',
+ 'type': 'static_library',
+ 'dependencies': [
+ 'initializing_coclass',
+ '../../base/base.gyp:base',
+ '../../testing/gmock.gyp:gmock',
+ '../../third_party/npapi/npapi.gyp:npapi',
+ ],
+ 'msvs_guid': '4BAB6049-29A7-47C2-A754-14CA3FBF5BD9',
+ 'sources': [
+ 'com_utils.cc',
+ 'com_utils.h',
+ 'initializing_coclass.h',
+ 'install_utils.cc',
+ 'install_utils.h',
+ 'np_browser_functions.cc',
+ 'np_browser_functions.h',
+ 'npobject_impl.cc',
+ 'npobject_impl.h',
+ 'npplugin_impl.cc',
+ 'npplugin_impl.h',
+ 'process_utils_win.cc',
+ 'process_utils_win.h',
+ 'sidestep_integration.cc',
+ 'windows_constants.cc',
+ 'windows_constants.h',
+ 'window_utils.cc',
+ 'window_utils.h',
+ ],
+ },
+ {
+ 'target_name': 'ceee_common_unittests',
+ 'type': 'executable',
+ 'msvs_guid': 'A6DF7E24-63DF-47E6-BA72-423FCA8AC36D',
+ 'sources': [
+ 'com_utils_unittest.cc',
+ 'common_unittest_main.cc',
+ 'initializing_coclass_unittest.cc',
+ 'install_utils_unittest.cc',
+ 'npobject_impl_unittest.cc',
+ 'npplugin_impl_unittest.cc',
+ 'process_utils_win_unittest.cc',
+ 'sidestep_unittest.cc',
+ 'window_utils_unittest.cc',
+ ],
+ 'dependencies': [
+ 'ceee_common',
+ '<(DEPTH)/ceee/testing/sidestep/sidestep.gyp:sidestep',
+ '../testing/utils/test_utils.gyp:test_utils',
+ '../../base/base.gyp:base',
+ '../../testing/gmock.gyp:gmock',
+ '../../testing/gtest.gyp:gtest',
+ '../../third_party/npapi/npapi.gyp:npapi',
+ ],
+ }
+ ]
+}
diff --git a/ceee/common/common_unittest_main.cc b/ceee/common/common_unittest_main.cc
new file mode 100644
index 0000000..66c81a0
--- /dev/null
+++ b/ceee/common/common_unittest_main.cc
@@ -0,0 +1,23 @@
+// Copyright (c) 2010 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.
+//
+// Main function for common unittests.
+#include <atlbase.h>
+#include <atlcom.h>
+#include "base/at_exit.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+// We're testing ATL code that requires a module object.
+class ObligatoryModule: public CAtlDllModuleT<ObligatoryModule> {
+};
+
+ObligatoryModule g_obligatory_atl_module;
+
+int main(int argc, char **argv) {
+ base::AtExitManager exit_manager;
+ testing::InitGoogleMock(&argc, argv);
+ testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/ceee/common/initializing_coclass.h b/ceee/common/initializing_coclass.h
new file mode 100644
index 0000000..b29313f
--- /dev/null
+++ b/ceee/common/initializing_coclass.h
@@ -0,0 +1,123 @@
+// Copyright (c) 2010 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.
+//
+//
+// Declares a convenience coclass implementation that makes it easy to create
+// initialized object instances.
+#ifndef CEEE_COMMON_INITIALIZING_COCLASS_H_
+#define CEEE_COMMON_INITIALIZING_COCLASS_H_
+
+#include <atlbase.h>
+#include <atlcom.h>
+
+using ATL::CComObject;
+
+// A convenience mixin coclass, to allow creating and initializing
+// CComObject<T>-derived object instances, and optionally query them for
+// a given interface, all in a single CreateInstance invocation.
+//
+// Usage:
+// <code>
+// class MyObjectImpl: public CComObjectRoot<...>,
+// public InitializingCoClass<MyObjectImpl>, ... {
+// public:
+// // This function will be invoked when MyObjectImpl::CreateInstance with N
+// // arguments of the appropriate type is invoked.
+// // @note if this function returns error, the object will be destroyed
+// // @note reference args must be const
+// HRESULT Initialize(SomeType1 arg1, ..., SomeTypeN argN);
+// };
+// ...
+// CComPtr<IFoo> foo;
+// HRESULT hr = MyObjectImp::CreateInstance(arg1, ..., argN, &foo)
+// </code>
+//
+// @param T the CComObjectRootEx derivative class you want to create and
+// initalize.
+template <class T>
+class InitializingCoClass {
+ public:
+ typedef CComObject<T> TImpl;
+
+ // Creates an instance of CComObject<T> and initializes it.
+ // @param new_instance on success returns the new, initialized instance.
+ // @note this method does not return a reference to the new object.
+ static HRESULT CreateInstance(T** new_instance) {
+ TImpl* instance;
+ HRESULT hr = TImpl::CreateInstance(&instance);
+ if (FAILED(hr))
+ return hr;
+
+ instance->InternalFinalConstructAddRef();
+ hr = instance->Initialize();
+ instance->InternalFinalConstructRelease();
+ if (FAILED(hr)) {
+ delete instance;
+ instance = NULL;
+ }
+ *new_instance = instance;
+
+ return hr;
+ }
+
+ // Creates an instance of CComObject<T>, initializes it and queries it
+ // for interface I.
+ // @param instance on success returns the new, initialized instance.
+ template <class I>
+ static HRESULT CreateInitialized(I** instance) {
+ T* new_instance;
+ HRESULT hr = CreateInstance(&new_instance);
+ if (FAILED(hr))
+ return hr;
+
+ hr = new_instance->QueryInterface(__uuidof(I),
+ reinterpret_cast<void**>(instance));
+ if (FAILED(hr))
+ delete new_instance;
+
+ return hr;
+ }
+
+ // Creates an instance of CComObject<T>, initializes it and queries it
+ // for an interface.
+ // @param iid the interface to query for. Must be congruent to the type I.
+ // @param instance on success returns the new, initialized instance.
+ template <class I>
+ static HRESULT CreateInitializedIID(REFIID iid, I** instance) {
+ T* new_instance;
+ HRESULT hr = CreateInstance(&new_instance);
+ if (FAILED(hr))
+ return hr;
+
+ hr = new_instance->QueryInterface(iid, reinterpret_cast<void**>(instance));
+ if (FAILED(hr))
+ delete new_instance;
+
+ return hr;
+ }
+
+
+// This file is chock full of repetitions of the same code, and because we
+// don't yet stock infinite monkeys (nor infinite developers to review their
+// code) we substitute with the python script initializing_coclass.py
+//
+// The functions therein are of this general make:
+
+// Create a new, initialized T* instance. Note that zero references will
+// be held to *new_instance at return.
+//
+// template <class A1, ..., class An>
+// HRESULT CreateInstance(A1 a1, ..., An an, T **new_instance);
+//
+// Create a new, initialized T* instance, and QI it for I
+// template <class I, class A1, ..., class An>
+// HRESULT CreateInstance(A1 a1, ..., An an, I **new_instance);
+#include "initializing_coclass_gen.inl" // NOLINT
+};
+
+#ifndef CEEE_COMMON_INITIALIZING_COCLASS_GEN_INL_
+#error initializing_coclass_gen.inl wasn't generated correctly.
+#endif
+
+#endif // CEEE_COMMON_INITIALIZING_COCLASS_H_
diff --git a/ceee/common/initializing_coclass.py b/ceee/common/initializing_coclass.py
new file mode 100644
index 0000000..f756fd0
--- /dev/null
+++ b/ceee/common/initializing_coclass.py
@@ -0,0 +1,107 @@
+# Copyright (c) 2010 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.
+"""A generator for initializing_coclass.h, which contains a bunch of
+repeated code that can't be produced through the preprocessor."""
+
+import sys
+from string import Template
+
+HEADER_ = """\
+// This file is autogenerated by initializing_coclass.py, do not edit.
+#ifndef CEEE_COMMON_INITIALIZING_COCLASS_GEN_INL_
+#define CEEE_COMMON_INITIALIZING_COCLASS_GEN_INL_
+
+"""
+
+FOOTER_ = """\
+#endif // CEEE_COMMON_INITIALIZING_COCLASS_GEN_INL_
+"""
+
+# This template is used to generate a pair of template functions for N
+# initialization parameters.
+TEMPLATE_ = Template('''\
+ // Creates a new instance of ImplClass
+ // @returns S_OK on success, an appropriate error on failure.
+ // @param new_instance on success returns the newly created and initialized
+ // ImplClass instance.
+ // @note *new_instance has zero references, and as result the use of this
+ // function is highly discouraged as the existence of *new_instance
+ // on return of this function is quite brittle.
+ template <${templ_params}>
+ static HRESULT CreateInstance(${params}, T** new_instance) {
+ TImpl* instance;
+ HRESULT hr = TImpl::CreateInstance(&instance);
+ if (FAILED(hr))
+ return hr;
+
+ instance->InternalFinalConstructAddRef();
+ hr = instance->Initialize(${args});
+ instance->InternalFinalConstructRelease();
+ if (FAILED(hr)) {
+ delete instance;
+ instance = NULL;
+ }
+ *new_instance = instance;
+
+ return hr;
+ }
+
+ template <class I, ${templ_params}>
+ static HRESULT CreateInitialized(${params}, I** instance) {
+ T* new_instance;
+ HRESULT hr = CreateInstance(${args}, &new_instance);
+ if (FAILED(hr))
+ return hr;
+
+ hr = new_instance->QueryInterface(__uuidof(I),
+ reinterpret_cast<void**>(instance));
+ if (FAILED(hr))
+ delete new_instance;
+
+ return hr;
+ }
+
+ template <class I, ${templ_params}>
+ static HRESULT CreateInitializedIID(${params}, REFIID iid, I** instance) {
+ T* new_instance;
+ HRESULT hr = CreateInstance(${args}, &new_instance);
+ if (FAILED(hr))
+ return hr;
+
+ hr = new_instance->QueryInterface(iid, reinterpret_cast<void**>(instance));
+ if (FAILED(hr))
+ delete new_instance;
+
+ return hr;
+ }
+
+''')
+
+
+def Emit(outfile, templ, num_args):
+ """Emits a template function for num_args arguments"""
+ mapping = {
+ 'templ_params':
+ ", ".join(["class A%d" % d for d in xrange(1, num_args + 1)]),
+ 'params':
+ ", ".join(["const A%d &a%d" % (d, d) for d in xrange(1, num_args + 1)]),
+ 'args': ", ".join(["a%d" % d for d in xrange(1, num_args + 1)]),
+ }
+ outfile.write(templ.substitute(mapping))
+
+
+NUM_ARGUMENTS = 10
+
+
+def Main(outfile):
+ """Emits the contents of initializing_coclass-inl.h."""
+ outfile.write(HEADER_)
+ # do one to NUM_ARGUMENTS arguments
+ for num_args in xrange(1, 1 + NUM_ARGUMENTS):
+ Emit(outfile, TEMPLATE_, num_args)
+ outfile.write(FOOTER_)
+
+if (__name__ == "__main__"):
+ outfile = open(sys.argv[1], 'w')
+ Main(outfile)
diff --git a/ceee/common/initializing_coclass_unittest.cc b/ceee/common/initializing_coclass_unittest.cc
new file mode 100644
index 0000000..df41160
--- /dev/null
+++ b/ceee/common/initializing_coclass_unittest.cc
@@ -0,0 +1,230 @@
+// Copyright (c) 2010 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.
+//
+// Unit tests for InitializingCoClass template mixin.
+#include "ceee/common/initializing_coclass.h"
+
+#include <atlbase.h>
+#include <atlcom.h>
+#include "gtest/gtest.h"
+
+namespace {
+
+// A test class to exercise initializing coclass functionality
+// We inherit from two interfaces just to play devil's advocate
+class CoClassTesterBase: public CComObjectRootEx<CComSingleThreadModel>,
+ public IObjectWithSiteImpl<CoClassTesterBase>,
+ public IDispatchImpl<IDispatch> {
+ public:
+ CoClassTesterBase() {
+ instances_++;
+ last_initializer_called_ = 0;
+ }
+
+ ~CoClassTesterBase() {
+ instances_--;
+ }
+
+ BEGIN_COM_MAP(CoClassTesterBase)
+ COM_INTERFACE_ENTRY(IObjectWithSite)
+ COM_INTERFACE_ENTRY(IDispatch)
+ END_COM_MAP()
+
+ public:
+ static int last_initializer_called_;
+
+ // tally of our instances
+ static int instances_;
+};
+
+int CoClassTesterBase::last_initializer_called_ = 0;
+int CoClassTesterBase::instances_ = 0;
+
+#define DECLARE_INIT_FUNCTION(args, num, result) \
+HRESULT Initialize args { \
+ last_initializer_called_ = num; \
+ return result; \
+}
+
+class CoClassSuccessTester: public CoClassTesterBase,
+ public InitializingCoClass<CoClassSuccessTester> {
+ public:
+ DECLARE_INIT_FUNCTION((), 0, S_OK)
+ DECLARE_INIT_FUNCTION((int a1), 1, S_OK)
+ DECLARE_INIT_FUNCTION((int a1, int a2), 2, S_OK)
+ DECLARE_INIT_FUNCTION((int a1, int a2, int a3), 3, S_OK)
+ DECLARE_INIT_FUNCTION((int a1, int a2, int a3, int a4), 4, S_OK)
+ DECLARE_INIT_FUNCTION((int a1, int a2, int a3, int a4, int a5), 5, S_OK)
+ DECLARE_INIT_FUNCTION((int a1, int a2, int a3, int a4, int a5, int a6),
+ 6, S_OK)
+ DECLARE_INIT_FUNCTION((int a1, int a2, int a3, int a4, int a5, int a6,
+ int a7), 7, S_OK)
+ DECLARE_INIT_FUNCTION((int a1, int a2, int a3, int a4, int a5, int a6,
+ int a7, int a8), 8, S_OK)
+ DECLARE_INIT_FUNCTION((int a1, int a2, int a3, int a4, int a5, int a6,
+ int a7, int a8, int a9), 9, S_OK)
+ DECLARE_INIT_FUNCTION((int a1, int a2, int a3, int a4, int a5, int a6,
+ int a7, int a8, int a9, int a10), 10, S_OK)
+};
+
+class CoClassFailureTester: public CoClassTesterBase,
+ public InitializingCoClass<CoClassFailureTester> {
+ public:
+ DECLARE_INIT_FUNCTION((), 0, E_FAIL)
+ DECLARE_INIT_FUNCTION((int a1), 1, E_FAIL)
+ DECLARE_INIT_FUNCTION((int a1, int a2), 2, E_FAIL)
+ DECLARE_INIT_FUNCTION((int a1, int a2, int a3), 3, E_FAIL)
+ DECLARE_INIT_FUNCTION((int a1, int a2, int a3, int a4), 4, E_FAIL)
+ DECLARE_INIT_FUNCTION((int a1, int a2, int a3, int a4, int a5), 5, E_FAIL)
+ DECLARE_INIT_FUNCTION((int a1, int a2, int a3, int a4, int a5, int a6),
+ 6, E_FAIL)
+ DECLARE_INIT_FUNCTION((int a1, int a2, int a3, int a4, int a5, int a6,
+ int a7), 7, E_FAIL)
+ DECLARE_INIT_FUNCTION((int a1, int a2, int a3, int a4, int a5, int a6,
+ int a7, int a8), 8, E_FAIL)
+ DECLARE_INIT_FUNCTION((int a1, int a2, int a3, int a4, int a5, int a6,
+ int a7, int a8, int a9), 9, E_FAIL)
+ DECLARE_INIT_FUNCTION((int a1, int a2, int a3, int a4, int a5, int a6,
+ int a7, int a8, int a9, int a10), 10, E_FAIL)
+};
+} // namespace
+
+// Test for successful init
+TEST(InitializingCoClassTest, InitSuccess) {
+#define SUCCESS_TEST(num, args) \
+ { \
+ CComPtr<IDispatch> disp; \
+ ASSERT_TRUE(SUCCEEDED(CoClassSuccessTester::CreateInitialized args)); \
+ ASSERT_EQ(1, CoClassTesterBase::instances_); \
+ ASSERT_EQ(num, CoClassTesterBase::last_initializer_called_); \
+ disp.Release(); \
+ ASSERT_EQ(0, CoClassTesterBase::instances_); \
+ } \
+
+ SUCCESS_TEST(0, (&disp))
+ SUCCESS_TEST(1, (1, &disp))
+ SUCCESS_TEST(2, (1, 2, &disp))
+ SUCCESS_TEST(3, (1, 2, 3, &disp))
+ SUCCESS_TEST(4, (1, 2, 3, 4, &disp))
+ SUCCESS_TEST(5, (1, 2, 3, 4, 5, &disp))
+ SUCCESS_TEST(6, (1, 2, 3, 4, 5, 6, &disp))
+ SUCCESS_TEST(7, (1, 2, 3, 4, 5, 6, 7, &disp))
+ SUCCESS_TEST(8, (1, 2, 3, 4, 5, 6, 7, 8, &disp))
+ SUCCESS_TEST(9, (1, 2, 3, 4, 5, 6, 7, 8, 9, &disp))
+ SUCCESS_TEST(10, (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, &disp))
+}
+
+// Test for failure on initialization, proper cleanup
+TEST(InitializingCoClassTest, InitFailure) {
+#define FAILURE_TEST(num, args) \
+ { \
+ CComPtr<IDispatch> disp; \
+ ASSERT_TRUE(FAILED(CoClassFailureTester::CreateInitialized args)); \
+ ASSERT_EQ(0, CoClassTesterBase::instances_); \
+ ASSERT_EQ(num, CoClassTesterBase::last_initializer_called_); \
+ ASSERT_TRUE(NULL == disp.p); \
+ } \
+
+ FAILURE_TEST(0, (&disp))
+ FAILURE_TEST(1, (1, &disp))
+ FAILURE_TEST(2, (1, 2, &disp))
+ FAILURE_TEST(3, (1, 2, 3, &disp))
+ FAILURE_TEST(4, (1, 2, 3, 4, &disp))
+ FAILURE_TEST(5, (1, 2, 3, 4, 5, &disp))
+ FAILURE_TEST(6, (1, 2, 3, 4, 5, 6, &disp))
+ FAILURE_TEST(7, (1, 2, 3, 4, 5, 6, 7, &disp))
+ FAILURE_TEST(8, (1, 2, 3, 4, 5, 6, 7, 8, &disp))
+ FAILURE_TEST(9, (1, 2, 3, 4, 5, 6, 7, 8, 9, &disp))
+ FAILURE_TEST(10, (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, &disp))
+}
+
+// Test for failure on QueryInterface, proper cleanup
+TEST(InitializingCoClassTest, QueryInterfaceFailure) {
+#define QI_FAILURE_TEST(num, args) \
+ { \
+ CComPtr<IStream> stream; \
+ ASSERT_EQ(E_NOINTERFACE, CoClassSuccessTester::CreateInitialized args); \
+ ASSERT_EQ(0, CoClassTesterBase::instances_); \
+ ASSERT_EQ(num, CoClassTesterBase::last_initializer_called_); \
+ ASSERT_TRUE(NULL == stream.p); \
+ } \
+
+ QI_FAILURE_TEST(0, (&stream))
+ QI_FAILURE_TEST(1, (1, &stream))
+ QI_FAILURE_TEST(2, (1, 2, &stream))
+ QI_FAILURE_TEST(3, (1, 2, 3, &stream))
+ QI_FAILURE_TEST(4, (1, 2, 3, 4, &stream))
+ QI_FAILURE_TEST(5, (1, 2, 3, 4, 5, &stream))
+ QI_FAILURE_TEST(6, (1, 2, 3, 4, 5, 6, &stream))
+ QI_FAILURE_TEST(7, (1, 2, 3, 4, 5, 6, 7, &stream))
+ QI_FAILURE_TEST(8, (1, 2, 3, 4, 5, 6, 7, 8, &stream))
+ QI_FAILURE_TEST(9, (1, 2, 3, 4, 5, 6, 7, 8, 9, &stream))
+ QI_FAILURE_TEST(10, (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, &stream))
+}
+
+
+// Test for proper cleanup on Initialize failure for CreateInstance
+TEST(InitializingCoClassTest, InitFailureOnCreateInstance) {
+#define INIT_FAILURE_TEST(num, args) \
+ { \
+ CoClassSuccessTester* tester; \
+ ASSERT_EQ(E_FAIL, CoClassFailureTester::CreateInstance args); \
+ ASSERT_EQ(0, CoClassTesterBase::instances_); \
+ ASSERT_EQ(num, CoClassTesterBase::last_initializer_called_); \
+ ASSERT_TRUE(NULL == tester); \
+ } \
+
+ QI_FAILURE_TEST(0, (&stream))
+ QI_FAILURE_TEST(1, (1, &stream))
+ QI_FAILURE_TEST(2, (1, 2, &stream))
+ QI_FAILURE_TEST(3, (1, 2, 3, &stream))
+ QI_FAILURE_TEST(4, (1, 2, 3, 4, &stream))
+ QI_FAILURE_TEST(5, (1, 2, 3, 4, 5, &stream))
+ QI_FAILURE_TEST(6, (1, 2, 3, 4, 5, 6, &stream))
+ QI_FAILURE_TEST(7, (1, 2, 3, 4, 5, 6, 7, &stream))
+ QI_FAILURE_TEST(8, (1, 2, 3, 4, 5, 6, 7, 8, &stream))
+ QI_FAILURE_TEST(9, (1, 2, 3, 4, 5, 6, 7, 8, 9, &stream))
+ QI_FAILURE_TEST(10, (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, &stream))
+}
+
+namespace {
+
+// Class that does not like to be copied.
+class DoNotCopy {
+ public:
+ DoNotCopy() {
+ }
+ DoNotCopy(const DoNotCopy& copy) {
+ EXPECT_TRUE(false);
+ }
+};
+
+// Simple class we will use to test if reference parameters are being honoured
+// or if in fact they are being copied somewhere inbetween CreateInstance() and
+// Initialize().
+class CoClassCopyTester
+ : public CComObjectRootEx<CComSingleThreadModel>,
+ public IDispatchImpl<IDispatch>,
+ public InitializingCoClass<CoClassCopyTester> {
+ public:
+ BEGIN_COM_MAP(CoClassCopyTester)
+ COM_INTERFACE_ENTRY(IDispatch)
+ END_COM_MAP()
+
+ HRESULT Initialize(const DoNotCopy& data) {
+ // If we got this far it means we didn't run the copy constructor
+ return S_OK;
+ }
+};
+} // namespace
+
+// Test to make sure that passing a reference parameter doesn't trigger a copy
+// on the argument.
+TEST(InitializingCoClassTest, TestReferenceParams) {
+ DoNotCopy data;
+ CComPtr<IDispatch> tester;
+
+ HRESULT hr = CoClassCopyTester::CreateInitialized(data, &tester);
+ ASSERT_EQ(S_OK, hr);
+}
diff --git a/ceee/common/install_utils.cc b/ceee/common/install_utils.cc
new file mode 100644
index 0000000..64b23fb
--- /dev/null
+++ b/ceee/common/install_utils.cc
@@ -0,0 +1,51 @@
+// Copyright (c) 2010 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.
+//
+// Utilities related to installation of the CEEE.
+
+#include "ceee/common/install_utils.h"
+
+#include <windows.h>
+
+#include "base/command_line.h"
+#include "base/file_path.h"
+#include "base/path_service.h"
+
+namespace installer_util {
+ namespace switches {
+ // TODO(joi@chromium.org) Move to chrome/installer/util_constants.h
+ const wchar_t kEnableCeee[] = L"enable-ceee";
+
+ // TODO(joi@chromium.org) Remove from here, reuse from
+ // chrome/installer/util_constants.h
+ const wchar_t kChromeFrame[] = L"chrome-frame";
+ }
+}
+
+namespace ceee_install_utils {
+
+bool ShouldRegisterCeee() {
+ // First check if it's a developer running us explicitly.
+ FilePath exe_path;
+ if (PathService::Get(base::FILE_EXE, &exe_path)) {
+ if (exe_path.BaseName() == FilePath(L"regsvr32.exe")) {
+ return true;
+ }
+ }
+
+ // Failing that, it's some kind of install scenario, so the
+ // --enable-ceee flag must be provided. It should be ignored
+ // unless --chrome-frame is also specified, so we check for
+ // both.
+ CommandLine current_command_line(CommandLine::NO_PROGRAM);
+ current_command_line.ParseFromString(::GetCommandLine());
+ if (current_command_line.HasSwitch(installer_util::switches::kEnableCeee) &&
+ current_command_line.HasSwitch(installer_util::switches::kChromeFrame)) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+} // namespace ceee_install_utils
diff --git a/ceee/common/install_utils.h b/ceee/common/install_utils.h
new file mode 100644
index 0000000..c5ed966
--- /dev/null
+++ b/ceee/common/install_utils.h
@@ -0,0 +1,40 @@
+// Copyright (c) 2010 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.
+//
+// Utilities related to installation of the CEEE.
+
+#ifndef CEEE_COMMON_INSTALL_UTILS_H_
+#define CEEE_COMMON_INSTALL_UTILS_H_
+
+// This is the only way we define DllRegisterServer in CEEE.
+// It guarantees that we check whether registration should be
+// done before actually doing it.
+//
+// Instructions:
+//
+// a) In your module's main compilation unit, define
+// a function DllRegisterServerImpl with the same signature
+// as DllRegisterServer.
+//
+// b) Below your DllRegisterServerImpl function, put this
+// macro.
+#define CEEE_DEFINE_DLL_REGISTER_SERVER() \
+ STDAPI DllRegisterServer(void) { \
+ if (!ceee_install_utils::ShouldRegisterCeee()) { \
+ return S_OK; \
+ } else { \
+ return DllRegisterServerImpl(); \
+ } \
+ }
+
+namespace ceee_install_utils {
+
+// Returns true if the --enable-ceee flag was passed to the process
+// that loaded this DLL, or if the process loading the DLL is
+// regsvr32 (i.e. it's a developer that explicitly wants to register).
+bool ShouldRegisterCeee();
+
+} // namespace ceee_install_utils
+
+#endif // CEEE_COMMON_INSTALL_UTILS_H_
diff --git a/ceee/common/install_utils_unittest.cc b/ceee/common/install_utils_unittest.cc
new file mode 100644
index 0000000..61f3b54
--- /dev/null
+++ b/ceee/common/install_utils_unittest.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2010 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.
+//
+// Unit tests for utilities related to installation of the CEEE.
+
+#include "ceee/common/install_utils.h"
+
+#include "base/base_paths.h"
+#include "ceee/testing/utils/mock_win32.h"
+#include "ceee/testing/utils/test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+using testing::_;
+using testing::DoAll;
+using testing::CopyStringToArgument;
+using testing::Return;
+
+TEST(ShouldRegisterCeee, FalseUnlessRegsvr32OrCommandLine) {
+ // We can safely assume that the real command line and
+ // process name for the unit test process does not satisfy
+ // either condition.
+ ASSERT_FALSE(ceee_install_utils::ShouldRegisterCeee());
+}
+
+TEST(ShouldRegisterCeee, TrueIfRegsvr32) {
+ testing::PathServiceOverrider override(base::FILE_EXE,
+ FilePath(L"c:\\windows\\system32\\regsvr32.exe"));
+ ASSERT_TRUE(ceee_install_utils::ShouldRegisterCeee());
+}
+
+TEST(ShouldRegisterCeee, FalseIfRegsvr32NotLastPathComponent) {
+ testing::PathServiceOverrider override(base::FILE_EXE,
+ FilePath(L"c:\\windows\\regsvr32.exe\\foobar.exe"));
+ ASSERT_FALSE(ceee_install_utils::ShouldRegisterCeee());
+}
+
+TEST(ShouldRegisterCeee, TrueIfBothFlagsOnCommandLine) {
+ testing::MockKernel32 kernel32;
+ EXPECT_CALL(kernel32, GetCommandLine()).WillOnce(
+ Return(const_cast<wchar_t*>(
+ L"mini_installer.exe --enable-ceee --chrome-frame")));
+ ASSERT_TRUE(ceee_install_utils::ShouldRegisterCeee());
+}
+
+TEST(ShouldRegisterCeee, FalseIfOneFlagOnCommandLine) {
+ testing::MockKernel32 kernel32;
+ EXPECT_CALL(kernel32, GetCommandLine())
+ .WillOnce(Return(const_cast<wchar_t*>(
+ L"mini_installer.exe --enable-ceee")))
+ .WillOnce(Return(const_cast<wchar_t*>(
+ L"mini_installer.exe --chrome-frame")));
+ ASSERT_FALSE(ceee_install_utils::ShouldRegisterCeee());
+ ASSERT_FALSE(ceee_install_utils::ShouldRegisterCeee());
+}
+
+TEST(ShouldRegisterCeee, FalseIfInvertedFlagOnCommandLine) {
+ testing::MockKernel32 kernel32;
+ EXPECT_CALL(kernel32, GetCommandLine()).WillOnce(
+ Return(const_cast<wchar_t*>(
+ L"mini_installer.exe --noenable-ceee --chrome-frame")));
+ ASSERT_FALSE(ceee_install_utils::ShouldRegisterCeee());
+}
+
+TEST(ShouldRegisterCeee, FalseIfBogusFlagOnCommandLine) {
+ testing::MockKernel32 kernel32;
+ EXPECT_CALL(kernel32, GetCommandLine()).WillOnce(
+ Return(const_cast<wchar_t*>(
+ L"mini_installer.exe --enable-ceeez --chrome-frame")));
+ ASSERT_FALSE(ceee_install_utils::ShouldRegisterCeee());
+}
+
+namespace dll_register_server_test {
+
+// Gets set to true by DllRegisterServer.
+bool registration_was_done = false;
+
+// This is for unit testing only. Because we're in an anonymous namespace
+// it's separate from everything else.
+STDAPI DllRegisterServerImpl() {
+ registration_was_done = true;
+ return S_OK;
+}
+
+CEEE_DEFINE_DLL_REGISTER_SERVER()
+
+TEST(ConditionalDllRegisterServer, ShouldNotInstall) {
+ // We can safely assume that the real command line and
+ // process name for the unit test process does not satisfy
+ // either condition.
+ registration_was_done = false;
+ DllRegisterServer();
+ ASSERT_FALSE(registration_was_done);
+}
+
+TEST(ConditionalDllRegisterServer, ShouldInstall) {
+ // This is just one way to make ShouldRegisterCeee()
+ // return true; we've unit tested elsewhere that the other cases
+ // are handled correctly.
+ testing::PathServiceOverrider override(base::FILE_EXE,
+ FilePath(L"c:\\windows\\system32\\regsvr32.exe"));
+
+ registration_was_done = false;
+ DllRegisterServer();
+ ASSERT_TRUE(registration_was_done);
+}
+
+} // namespace dll_register_server_test
+
+
+} // namespace
diff --git a/ceee/common/np_browser_functions.cc b/ceee/common/np_browser_functions.cc
new file mode 100644
index 0000000..b421f7c
--- /dev/null
+++ b/ceee/common/np_browser_functions.cc
@@ -0,0 +1,435 @@
+// Copyright (c) 2010 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 "ceee/common/np_browser_functions.h"
+
+#include "base/logging.h"
+
+namespace npapi {
+
+// global function pointers (within this namespace) for the NPN functions.
+NPN_GetURLProcPtr g_geturl = NULL;
+NPN_PostURLProcPtr g_posturl = NULL;
+NPN_RequestReadProcPtr g_requestread = NULL;
+NPN_NewStreamProcPtr g_newstream = NULL;
+NPN_WriteProcPtr g_write = NULL;
+NPN_DestroyStreamProcPtr g_destroystream = NULL;
+NPN_StatusProcPtr g_status = NULL;
+NPN_UserAgentProcPtr g_useragent = NULL;
+NPN_MemAllocProcPtr g_memalloc = NULL;
+NPN_MemFreeProcPtr g_memfree = NULL;
+NPN_MemFlushProcPtr g_memflush = NULL;
+NPN_ReloadPluginsProcPtr g_reloadplugins = NULL;
+NPN_GetJavaEnvProcPtr g_getJavaEnv = NULL;
+NPN_GetJavaPeerProcPtr g_getJavaPeer = NULL;
+NPN_GetURLNotifyProcPtr g_geturlnotify = NULL;
+NPN_PostURLNotifyProcPtr g_posturlnotify = NULL;
+NPN_GetValueProcPtr g_getvalue = NULL;
+NPN_SetValueProcPtr g_setvalue = NULL;
+NPN_InvalidateRectProcPtr g_invalidaterect = NULL;
+NPN_InvalidateRegionProcPtr g_invalidateregion = NULL;
+NPN_ForceRedrawProcPtr g_forceredraw = NULL;
+NPN_GetStringIdentifierProcPtr g_getstringidentifier = NULL;
+NPN_GetStringIdentifiersProcPtr g_getstringidentifiers = NULL;
+NPN_GetIntIdentifierProcPtr g_getintidentifier = NULL;
+NPN_IdentifierIsStringProcPtr g_identifierisstring = NULL;
+NPN_UTF8FromIdentifierProcPtr g_utf8fromidentifier = NULL;
+NPN_IntFromIdentifierProcPtr g_intfromidentifier = NULL;
+NPN_CreateObjectProcPtr g_createobject = NULL;
+NPN_RetainObjectProcPtr g_retainobject = NULL;
+NPN_ReleaseObjectProcPtr g_releaseobject = NULL;
+NPN_InvokeProcPtr g_invoke = NULL;
+NPN_InvokeDefaultProcPtr g_invoke_default = NULL;
+NPN_EvaluateProcPtr g_evaluate = NULL;
+NPN_GetPropertyProcPtr g_getproperty = NULL;
+NPN_SetPropertyProcPtr g_setproperty = NULL;
+NPN_RemovePropertyProcPtr g_removeproperty = NULL;
+NPN_HasPropertyProcPtr g_hasproperty = NULL;
+NPN_HasMethodProcPtr g_hasmethod = NULL;
+NPN_ReleaseVariantValueProcPtr g_releasevariantvalue = NULL;
+NPN_SetExceptionProcPtr g_setexception = NULL;
+NPN_PushPopupsEnabledStateProcPtr g_pushpopupsenabledstate = NULL;
+NPN_PopPopupsEnabledStateProcPtr g_poppopupsenabledstate = NULL;
+NPN_EnumerateProcPtr g_enumerate = NULL;
+NPN_PluginThreadAsyncCallProcPtr g_pluginthreadasynccall = NULL;
+NPN_ConstructProcPtr g_construct = NULL;
+
+// Must be called prior to calling any of the browser functions below.
+void InitializeBrowserFunctions(NPNetscapeFuncs* functions) {
+ CHECK(functions);
+ DCHECK(g_geturl == NULL || g_geturl == functions->geturl);
+
+ g_geturl = functions->geturl;
+ g_posturl = functions->posturl;
+ g_requestread = functions->requestread;
+ g_newstream = functions->newstream;
+ g_write = functions->write;
+ g_destroystream = functions->destroystream;
+ g_status = functions->status;
+ g_useragent = functions->uagent;
+ g_memalloc = functions->memalloc;
+ g_memfree = functions->memfree;
+ g_memflush = functions->memflush;
+ g_reloadplugins = functions->reloadplugins;
+ g_getJavaEnv = functions->getJavaEnv;
+ g_getJavaPeer = functions->getJavaPeer;
+ g_geturlnotify = functions->geturlnotify;
+ g_posturlnotify = functions->posturlnotify;
+ g_getvalue = functions->getvalue;
+ g_setvalue = functions->setvalue;
+ g_invalidaterect = functions->invalidaterect;
+ g_invalidateregion = functions->invalidateregion;
+ g_forceredraw = functions->forceredraw;
+ g_getstringidentifier = functions->getstringidentifier;
+ g_getstringidentifiers = functions->getstringidentifiers;
+ g_getintidentifier = functions->getintidentifier;
+ g_identifierisstring = functions->identifierisstring;
+ g_utf8fromidentifier = functions->utf8fromidentifier;
+ g_intfromidentifier = functions->intfromidentifier;
+ g_createobject = functions->createobject;
+ g_retainobject = functions->retainobject;
+ g_releaseobject = functions->releaseobject;
+ g_invoke = functions->invoke;
+ g_invoke_default = functions->invokeDefault;
+ g_evaluate = functions->evaluate;
+ g_getproperty = functions->getproperty;
+ g_setproperty = functions->setproperty;
+ g_removeproperty = functions->removeproperty;
+ g_hasproperty = functions->hasproperty;
+ g_hasmethod = functions->hasmethod;
+ g_releasevariantvalue = functions->releasevariantvalue;
+ g_setexception = functions->setexception;
+ g_pushpopupsenabledstate = functions->pushpopupsenabledstate;
+ g_poppopupsenabledstate = functions->poppopupsenabledstate;
+ g_enumerate = functions->enumerate;
+ g_pluginthreadasynccall = functions->pluginthreadasynccall;
+ g_construct = functions->construct;
+}
+
+void UninitializeBrowserFunctions() {
+// We skip doing this in the official build as it doesn't serve much purpose
+// during shutdown. The reason for it being here in the other types of builds
+// is to spot potential browser bugs whereby the browser leaves living objects
+// in our DLL after shutdown has been called. In theory those objects could
+// trigger a call to the browser functions after shutdown has been called
+// and for non official builds we want that to simply crash.
+// For official builds we leave the function pointers around since they
+// continue to valid.
+ g_geturl = NULL;
+ g_posturl = NULL;
+ g_requestread = NULL;
+ g_newstream = NULL;
+ g_write = NULL;
+ g_destroystream = NULL;
+ g_status = NULL;
+ g_useragent = NULL;
+ g_memalloc = NULL;
+ g_memfree = NULL;
+ g_memflush = NULL;
+ g_reloadplugins = NULL;
+ g_getJavaEnv = NULL;
+ g_getJavaPeer = NULL;
+ g_geturlnotify = NULL;
+ g_posturlnotify = NULL;
+ g_getvalue = NULL;
+ g_setvalue = NULL;
+ g_invalidaterect = NULL;
+ g_invalidateregion = NULL;
+ g_forceredraw = NULL;
+ g_getstringidentifier = NULL;
+ g_getstringidentifiers = NULL;
+ g_getintidentifier = NULL;
+ g_identifierisstring = NULL;
+ g_utf8fromidentifier = NULL;
+ g_intfromidentifier = NULL;
+ g_createobject = NULL;
+ g_retainobject = NULL;
+ g_releaseobject = NULL;
+ g_invoke = NULL;
+ g_invoke_default = NULL;
+ g_evaluate = NULL;
+ g_getproperty = NULL;
+ g_setproperty = NULL;
+ g_removeproperty = NULL;
+ g_hasproperty = NULL;
+ g_hasmethod = NULL;
+ g_releasevariantvalue = NULL;
+ g_setexception = NULL;
+ g_pushpopupsenabledstate = NULL;
+ g_poppopupsenabledstate = NULL;
+ g_enumerate = NULL;
+ g_pluginthreadasynccall = NULL;
+ g_construct = NULL;
+}
+
+bool IsInitialized() {
+ // We only check one function for convenience.
+ return g_getvalue != NULL;
+}
+
+// Function stubs for functions that the host browser implements.
+
+NPError GetURL(NPP instance, const char* URL, const char* window) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_geturl(instance, URL, window);
+}
+
+NPError PostURL(NPP instance, const char* URL, const char* window, uint32 len,
+ const char* buf, NPBool file) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_posturl(instance, URL, window, len, buf, file);
+}
+
+NPError RequestRead(NPStream* stream, NPByteRange* rangeList) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_requestread(stream, rangeList);
+}
+
+NPError NewStream(NPP instance, NPMIMEType type, const char* window,
+ NPStream** stream) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_newstream(instance, type, window, stream);
+}
+
+int32 Write(NPP instance, NPStream* stream, int32 len, void* buffer) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_write(instance, stream, len, buffer);
+}
+
+NPError DestroyStream(NPP instance, NPStream* stream, NPReason reason) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_destroystream(instance, stream, reason);
+}
+
+void Status(NPP instance, const char* message) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_status(instance, message);
+}
+
+const char* UserAgent(NPP instance) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_useragent(instance);
+}
+
+void* MemAlloc(uint32 size) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_memalloc(size);
+}
+
+void MemFree(void* ptr) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_memfree(ptr);
+}
+
+uint32 MemFlush(uint32 size) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_memflush(size);
+}
+
+void ReloadPlugins(NPBool reloadPages) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_reloadplugins(reloadPages);
+}
+
+void* GetJavaEnv() {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_getJavaEnv();
+}
+
+void* GetJavaPeer(NPP instance) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_getJavaPeer(instance);
+}
+
+NPError GetURLNotify(NPP instance, const char* URL, const char* window,
+ void* notifyData) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_geturlnotify(instance, URL, window, notifyData);
+}
+
+NPError PostURLNotify(NPP instance, const char* URL, const char* window,
+ uint32 len, const char* buf, NPBool file,
+ void* notifyData) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_posturlnotify(instance, URL, window, len, buf, file, notifyData);
+}
+
+NPError GetValue(NPP instance, NPNVariable variable, void* ret_value) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_getvalue(instance, variable, ret_value);
+}
+
+NPError SetValue(NPP instance, NPPVariable variable, void* value) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_setvalue(instance, variable, value);
+}
+
+void InvalidateRect(NPP instance, NPRect* rect) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_invalidaterect(instance, rect);
+}
+
+void InvalidateRegion(NPP instance, NPRegion region) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_invalidateregion(instance, region);
+}
+
+void ForceRedraw(NPP instance) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_forceredraw(instance);
+}
+
+void ReleaseVariantValue(NPVariant* variant) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_releasevariantvalue(variant);
+}
+
+NPIdentifier GetStringIdentifier(const NPUTF8* name) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_getstringidentifier(name);
+}
+
+void GetStringIdentifiers(const NPUTF8** names, int32_t nameCount,
+ NPIdentifier* identifiers) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_getstringidentifiers(names, nameCount, identifiers);
+}
+
+NPIdentifier GetIntIdentifier(int32_t intid) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_getintidentifier(intid);
+}
+
+int32_t IntFromIdentifier(NPIdentifier identifier) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_intfromidentifier(identifier);
+}
+
+bool IdentifierIsString(NPIdentifier identifier) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_identifierisstring(identifier);
+}
+
+NPUTF8* UTF8FromIdentifier(NPIdentifier identifier) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_utf8fromidentifier(identifier);
+}
+
+NPObject* CreateObject(NPP instance, NPClass* aClass) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_createobject(instance, aClass);
+}
+
+NPObject* RetainObject(NPObject* obj) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_retainobject(obj);
+}
+
+void ReleaseObject(NPObject* obj) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_releaseobject(obj);
+}
+
+bool Invoke(NPP npp, NPObject* obj, NPIdentifier methodName,
+ const NPVariant* args, unsigned argCount, NPVariant* result) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_invoke(npp, obj, methodName, args, argCount, result);
+}
+
+bool InvokeDefault(NPP npp, NPObject* obj, const NPVariant* args,
+ unsigned argCount, NPVariant* result) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_invoke_default(npp, obj, args, argCount, result);
+}
+
+bool Evaluate(NPP npp, NPObject* obj, NPString* script, NPVariant* result) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_evaluate(npp, obj, script, result);
+}
+
+bool GetProperty(NPP npp, NPObject* obj, NPIdentifier propertyName,
+ NPVariant* result) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_getproperty(npp, obj, propertyName, result);
+}
+
+bool SetProperty(NPP npp, NPObject* obj, NPIdentifier propertyName,
+ const NPVariant* value) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_setproperty(npp, obj, propertyName, value);
+}
+
+bool HasProperty(NPP npp, NPObject* npobj, NPIdentifier propertyName) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_hasproperty(npp, npobj, propertyName);
+}
+
+bool HasMethod(NPP npp, NPObject* npobj, NPIdentifier methodName) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_hasmethod(npp, npobj, methodName);
+}
+
+bool RemoveProperty(NPP npp, NPObject* obj, NPIdentifier propertyName) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_removeproperty(npp, obj, propertyName);
+}
+
+void SetException(NPObject* obj, const NPUTF8* message) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_setexception(obj, message);
+}
+
+void PushPopupsEnabledState(NPP npp, NPBool enabled) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_pushpopupsenabledstate(npp, enabled);
+}
+
+void PopPopupsEnabledState(NPP npp) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_poppopupsenabledstate(npp);
+}
+
+bool Enumerate(NPP npp, NPObject* obj, NPIdentifier** identifier,
+ uint32_t* count) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_enumerate(npp, obj, identifier, count);
+}
+
+void PluginThreadAsyncCall(NPP instance,
+ void (*func)(void*), // NOLINT
+ void* userData) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_pluginthreadasynccall(instance, func, userData);
+}
+
+bool Construct(NPP npp, NPObject* obj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result) {
+ DCHECK(IsInitialized()) << __FUNCTION__;
+ return g_construct(npp, obj, args, argCount, result);
+}
+
+std::string StringFromIdentifier(NPIdentifier identifier) {
+ std::string ret;
+ NPUTF8* utf8 = UTF8FromIdentifier(identifier);
+ if (utf8) {
+ ret = utf8;
+ MemFree(utf8);
+ }
+ return ret;
+}
+
+} // namespace npapi
+
+void AllocateStringVariant(const std::string& str, NPVariant* var) {
+ DCHECK(var);
+
+ int len = str.length();
+ NPUTF8* buffer = reinterpret_cast<NPUTF8*>(npapi::MemAlloc(len + 1));
+ if (buffer) {
+ buffer[len] = '\0';
+ memcpy(buffer, str.c_str(), len);
+ STRINGN_TO_NPVARIANT(buffer, len, *var);
+ } else {
+ NULL_TO_NPVARIANT(*var);
+ }
+}
diff --git a/ceee/common/np_browser_functions.h b/ceee/common/np_browser_functions.h
new file mode 100644
index 0000000..8ab8b63
--- /dev/null
+++ b/ceee/common/np_browser_functions.h
@@ -0,0 +1,260 @@
+// Copyright (c) 2010 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.
+#ifndef CEEE_COMMON_NP_BROWSER_FUNCTIONS_H_
+#define CEEE_COMMON_NP_BROWSER_FUNCTIONS_H_
+
+#include <string>
+#include "base/logging.h"
+#include "third_party/npapi/bindings/nphostapi.h"
+
+namespace npapi {
+
+// Must be called prior to calling any of the browser functions below.
+void InitializeBrowserFunctions(NPNetscapeFuncs* functions);
+void UninitializeBrowserFunctions();
+
+// Returns true iff InitializeBrowserFunctions has been called successully.
+bool IsInitialized();
+
+// Function stubs for functions that the host browser implements.
+
+NPError GetURL(NPP instance, const char* URL, const char* window);
+
+NPError PostURL(NPP instance, const char* URL, const char* window, uint32 len,
+ const char* buf, NPBool file);
+
+NPError RequestRead(NPStream* stream, NPByteRange* rangeList);
+
+NPError NewStream(NPP instance, NPMIMEType type, const char* window,
+ NPStream** stream);
+
+int32 Write(NPP instance, NPStream* stream, int32 len, void* buffer);
+
+NPError DestroyStream(NPP instance, NPStream* stream, NPReason reason);
+
+void Status(NPP instance, const char* message);
+
+const char* UserAgent(NPP instance);
+
+void* MemAlloc(uint32 size);
+
+void MemFree(void* ptr);
+
+uint32 MemFlush(uint32 size);
+
+void ReloadPlugins(NPBool reloadPages);
+
+void* GetJavaEnv();
+
+void* GetJavaPeer(NPP instance);
+
+NPError GetURLNotify(NPP instance, const char* URL, const char* window,
+ void* notifyData);
+
+NPError PostURLNotify(NPP instance, const char* URL, const char* window,
+ uint32 len, const char* buf, NPBool file,
+ void* notifyData);
+
+NPError GetValue(NPP instance, NPNVariable variable, void* ret_value);
+
+NPError SetValue(NPP instance, NPPVariable variable, void* value);
+
+void InvalidateRect(NPP instance, NPRect* rect);
+
+void InvalidateRegion(NPP instance, NPRegion region);
+
+void ForceRedraw(NPP instance);
+
+void ReleaseVariantValue(NPVariant* variant);
+
+NPIdentifier GetStringIdentifier(const NPUTF8* name);
+
+void GetStringIdentifiers(const NPUTF8** names, int32_t nameCount,
+ NPIdentifier* identifiers);
+
+NPIdentifier GetIntIdentifier(int32_t intid);
+
+int32_t IntFromIdentifier(NPIdentifier identifier);
+
+bool IdentifierIsString(NPIdentifier identifier);
+
+NPUTF8* UTF8FromIdentifier(NPIdentifier identifier);
+
+NPObject* CreateObject(NPP, NPClass* aClass);
+
+NPObject* RetainObject(NPObject* obj);
+
+void ReleaseObject(NPObject* obj);
+
+bool Invoke(NPP npp, NPObject* obj, NPIdentifier methodName,
+ const NPVariant* args, unsigned argCount, NPVariant* result);
+
+bool InvokeDefault(NPP npp, NPObject* obj, const NPVariant* args,
+ unsigned argCount, NPVariant* result);
+
+bool Evaluate(NPP npp, NPObject* obj, NPString* script, NPVariant* result);
+
+bool GetProperty(NPP npp, NPObject* obj, NPIdentifier propertyName,
+ NPVariant* result);
+
+bool SetProperty(NPP npp, NPObject* obj, NPIdentifier propertyName,
+ const NPVariant* value);
+
+bool HasProperty(NPP npp, NPObject* npobj, NPIdentifier propertyName);
+
+bool HasMethod(NPP npp, NPObject* npobj, NPIdentifier methodName);
+
+bool RemoveProperty(NPP npp, NPObject* obj, NPIdentifier propertyName);
+
+void SetException(NPObject* obj, const NPUTF8* message);
+
+void PushPopupsEnabledState(NPP npp, NPBool enabled);
+
+void PopPopupsEnabledState(NPP npp);
+
+bool Enumerate(NPP npp, NPObject* obj, NPIdentifier** identifier,
+ uint32_t* count);
+
+void PluginThreadAsyncCall(NPP instance,
+ void (*func)(void*), // NOLINT
+ void* userData);
+
+bool Construct(NPP npp, NPObject* obj, const NPVariant* args, uint32_t argCount,
+ NPVariant* result);
+
+// Helper routine that wraps UTF8FromIdentifier to convert a string identifier
+// to an STL string. It's not super efficient since it could possibly do two
+// heap allocations (STL string has a stack based buffer for smaller strings).
+// For debugging purposes it is useful.
+std::string StringFromIdentifier(NPIdentifier identifier);
+
+} // namespace npapi
+
+// Simple helper class for freeing NPVariants at the end of a scope.
+class ScopedNpVariant : public NPVariant {
+ public:
+ ScopedNpVariant() {
+ VOID_TO_NPVARIANT(*this);
+ }
+
+ ~ScopedNpVariant() {
+ Free();
+ }
+
+ void Free() {
+ npapi::ReleaseVariantValue(this);
+ VOID_TO_NPVARIANT(*this);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ScopedNpVariant);
+};
+
+// Simple helper class for freeing NPObjects at the end of a scope.
+template <typename NpoType = NPObject>
+class ScopedNpObject {
+ public:
+ ScopedNpObject() : npo_(NULL) {
+ }
+
+ explicit ScopedNpObject(NpoType* npo) : npo_(npo) {
+ }
+
+ ~ScopedNpObject() {
+ Free();
+ }
+
+ NpoType* get() const {
+ return npo_;
+ }
+
+ operator NpoType*() const {
+ return npo_;
+ }
+
+ NpoType* operator->() const {
+ return npo_;
+ }
+
+ ScopedNpObject<NpoType>& operator=(NpoType* npo) {
+ if (npo != npo_) {
+ DCHECK(npo_ == NULL);
+ npapi::RetainObject(npo);
+ npo_ = npo;
+ }
+ return *this;
+ }
+
+ void Free() {
+ if (npo_) {
+ npapi::ReleaseObject(npo_);
+ npo_ = NULL;
+ }
+ }
+
+ NpoType** Receive() {
+ DCHECK(npo_ == NULL) << "Object leak. Pointer must be NULL";
+ return &npo_;
+ }
+
+ NpoType* Detach() {
+ NpoType* p = npo_;
+ npo_ = NULL;
+ return p;
+ }
+
+ void Attach(NpoType* p) {
+ DCHECK(npo_ == NULL);
+ npo_ = p;
+ }
+
+ NpoType* Copy() const {
+ if (npo_ != NULL)
+ npapi::RetainObject(npo_);
+ return npo_;
+ }
+
+ bool Invoke0(NPP npp, NPIdentifier id, NPVariant* result) {
+ return npapi::Invoke(npp, npo_, id, NULL, 0, result);
+ }
+ bool Invoke0(NPP npp, const NPUTF8* name, NPVariant* result) {
+ return Invoke0(npp, npapi::GetStringIdentifier(name), result);
+ }
+
+ bool Invoke1(NPP npp, NPIdentifier id, const NPVariant &arg1,
+ NPVariant* result) {
+ return npapi::Invoke(npp, npo_, id, &arg1, 1, result);
+ }
+ bool Invoke1(NPP npp, const NPUTF8* name, const NPVariant &arg1,
+ NPVariant* result) {
+ return Invoke1(npp, npapi::GetStringIdentifier(name), arg1, result);
+ }
+ bool InvokeN(NPP npp, NPIdentifier id, const NPVariant* args, unsigned argc,
+ NPVariant* result) {
+ return npapi::Invoke(npp, npo_, id, args, argc, result);
+ }
+ bool InvokeN(NPP npp, const NPUTF8* name, const NPVariant* args,
+ unsigned argc, NPVariant* result) {
+ return Invoke1(npp, npapi::GetStringIdentifier(name), args, argc, result);
+ }
+
+ bool GetProperty(NPP npp, NPIdentifier id, NPVariant* result) {
+ return npapi::GetProperty(npp, npo_, id, result);
+ }
+
+ bool GetProperty(NPP npp, const NPUTF8* name, NPVariant* result) {
+ return GetProperty(npp, npapi::GetStringIdentifier(name), result);
+ }
+
+ private:
+ NpoType* npo_;
+ DISALLOW_COPY_AND_ASSIGN(ScopedNpObject);
+};
+
+// Allocates a new NPUTF8 string and assigns it to the variant.
+// If memory allocation fails, the variant type will be set to NULL.
+// The memory allocation is done via the npapi browser functions.
+void AllocateStringVariant(const std::string& str, NPVariant* var);
+
+#endif // CEEE_COMMON_NP_BROWSER_FUNCTIONS_H_
diff --git a/ceee/common/npobject_impl.cc b/ceee/common/npobject_impl.cc
new file mode 100644
index 0000000..cf1a64c
--- /dev/null
+++ b/ceee/common/npobject_impl.cc
@@ -0,0 +1,121 @@
+// Copyright (c) 2010 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.
+//
+// Implementation of NPAPI object base class.
+#include "ceee/common/npobject_impl.h"
+
+void NpObjectBase::Invalidate() {
+}
+
+bool NpObjectBase::HasMethod(NPIdentifier name) {
+ return false;
+}
+
+bool NpObjectBase::Invoke(NPIdentifier name,
+ const NPVariant* args,
+ uint32_t arg_count,
+ NPVariant* result) {
+ return false;
+}
+
+bool NpObjectBase::InvokeDefault(const NPVariant* args,
+ uint32_t arg_count,
+ NPVariant* result) {
+ return false;
+}
+
+bool NpObjectBase::HasProperty(NPIdentifier name) {
+ return false;
+}
+
+bool NpObjectBase::GetProperty(NPIdentifier name, NPVariant* result) {
+ return false;
+}
+
+bool NpObjectBase::SetProperty(NPIdentifier name, const NPVariant* value) {
+ return false;
+}
+
+bool NpObjectBase::RemoveProperty(NPIdentifier name) {
+ return false;
+}
+
+bool NpObjectBase::Enumeration(NPIdentifier** value, uint32_t* count) {
+ return false;
+}
+
+bool NpObjectBase::Construct(const NPVariant* args, uint32_t argCount,
+ NPVariant* result) {
+ return false;
+}
+
+
+void NpObjectBase::NPDeallocate(NPObject* obj) {
+ NpObjectBase* base = static_cast<NpObjectBase*>(obj);
+ delete base;
+}
+
+void NpObjectBase::NPInvalidate(NPObject* obj) {
+ NpObjectBase* base = static_cast<NpObjectBase*>(obj);
+ base->Invalidate();
+}
+
+bool NpObjectBase::NPHasMethod(NPObject* obj, NPIdentifier name) {
+ NpObjectBase* base = static_cast<NpObjectBase*>(obj);
+ return base->HasMethod(name);
+}
+
+bool NpObjectBase::NPInvoke(NPObject* obj,
+ NPIdentifier name,
+ const NPVariant* args,
+ uint32_t arg_count,
+ NPVariant* result) {
+ NpObjectBase* base = static_cast<NpObjectBase*>(obj);
+ return base->Invoke(name, args, arg_count, result);
+}
+
+bool NpObjectBase::NPInvokeDefault(NPObject* obj,
+ const NPVariant* args,
+ uint32_t arg_count,
+ NPVariant* result) {
+ NpObjectBase* base = static_cast<NpObjectBase*>(obj);
+ return base->InvokeDefault(args, arg_count, result);
+}
+
+bool NpObjectBase::NPHasProperty(NPObject* obj, NPIdentifier name) {
+ NpObjectBase* base = static_cast<NpObjectBase*>(obj);
+ return base->HasProperty(name);
+}
+
+bool NpObjectBase::NPGetProperty(NPObject* obj,
+ NPIdentifier name,
+ NPVariant* result) {
+ NpObjectBase* base = static_cast<NpObjectBase*>(obj);
+ return base->GetProperty(name, result);
+}
+
+bool NpObjectBase::NPSetProperty(NPObject* obj,
+ NPIdentifier name,
+ const NPVariant* value) {
+ NpObjectBase* base = static_cast<NpObjectBase*>(obj);
+ return base->SetProperty(name, value);
+}
+
+bool NpObjectBase::NPRemoveProperty(NPObject* obj,
+ NPIdentifier name) {
+ NpObjectBase* base = static_cast<NpObjectBase*>(obj);
+ return base->RemoveProperty(name);
+}
+
+bool NpObjectBase::NPEnumeration(NPObject* obj, NPIdentifier** value,
+ uint32_t* count) {
+ NpObjectBase* base = static_cast<NpObjectBase*>(obj);
+ return base->Enumeration(value, count);
+}
+
+bool NpObjectBase::NPConstruct(NPObject* obj, const NPVariant* args,
+ uint32_t arg_count, NPVariant* result) {
+ NpObjectBase* base = static_cast<NpObjectBase*>(obj);
+ return base->Construct(args, arg_count, result);
+}
diff --git a/ceee/common/npobject_impl.h b/ceee/common/npobject_impl.h
new file mode 100644
index 0000000..9f0dee4
--- /dev/null
+++ b/ceee/common/npobject_impl.h
@@ -0,0 +1,119 @@
+// Copyright (c) 2010 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.
+//
+// Base classes to unclutter the writing of NPAPI objects.
+#ifndef CEEE_COMMON_NPOBJECT_IMPL_H_
+#define CEEE_COMMON_NPOBJECT_IMPL_H_
+
+#include "base/basictypes.h"
+#include "third_party/npapi/bindings/nphostapi.h"
+
+// Base class for NPAPI objects.
+class NpObjectBase: public NPObject {
+ public:
+ explicit NpObjectBase(NPP npp) : npp_(npp) {
+ }
+ virtual ~NpObjectBase() {
+ }
+
+ protected:
+ // @name NP Object interface.
+ // Override these to implement your object's functionality.
+ // For documentation on these methods, see the corresponding callback
+ // function description in http://developer.mozilla.org/en/NPObject
+ // @{
+ virtual void Invalidate();
+ virtual bool HasMethod(NPIdentifier name);
+ virtual bool Invoke(NPIdentifier name,
+ const NPVariant* args,
+ uint32_t argCount,
+ NPVariant* result);
+ virtual bool InvokeDefault(const NPVariant* args,
+ uint32_t argCount,
+ NPVariant* result);
+ virtual bool HasProperty(NPIdentifier name);
+ virtual bool GetProperty(NPIdentifier name, NPVariant* result);
+ virtual bool SetProperty(NPIdentifier name, const NPVariant* value);
+ virtual bool RemoveProperty(NPIdentifier name);
+ virtual bool Enumeration(NPIdentifier** value, uint32_t* count);
+ virtual bool Construct(const NPVariant* args, uint32_t argCount,
+ NPVariant* result);
+ // @}
+
+ // @name NP callback functions.
+ // @{
+ static void NPDeallocate(NPObject* obj);
+ static void NPInvalidate(NPObject* obj);
+ static bool NPHasMethod(NPObject* obj, NPIdentifier name);
+ static bool NPInvoke(NPObject* npobj,
+ NPIdentifier name,
+ const NPVariant* args,
+ uint32_t argCount,
+ NPVariant* result);
+ static bool NPInvokeDefault(NPObject* npobj,
+ const NPVariant* args,
+ uint32_t argCount,
+ NPVariant* result);
+ static bool NPHasProperty(NPObject* npobj, NPIdentifier name);
+ static bool NPGetProperty(NPObject* npobj,
+ NPIdentifier name,
+ NPVariant* result);
+ static bool NPSetProperty(NPObject* npobj,
+ NPIdentifier name,
+ const NPVariant* value);
+ static bool NPRemoveProperty(NPObject* npobj,
+ NPIdentifier name);
+ static bool NPEnumeration(NPObject* npobj, NPIdentifier** value,
+ uint32_t* count);
+ static bool NPConstruct(NPObject* npobj, const NPVariant* args,
+ uint32_t arg_count, NPVariant* result);
+ // @}
+
+ NPP npp() const { return npp_; }
+ private:
+ // The NPP instance we belong to.
+ NPP npp_;
+
+ DISALLOW_COPY_AND_ASSIGN(NpObjectBase);
+};
+
+
+// Derive your NPAPI object class from NpObjectImpl<MyClass>.
+template <class ImplClass> class NpObjectImpl: public NpObjectBase {
+ public:
+ explicit NpObjectImpl(NPP npp) : NpObjectBase(npp) {
+ }
+
+ static NPObject* NPAllocate(NPP npp, NPClass* np_class) {
+ NPObject* object = new ImplClass(npp);
+ object->_class = np_class;
+ object->referenceCount = 1;
+ return object;
+ }
+
+ static NPClass* object_class() { return &object_class_; }
+
+ private:
+ static NPClass object_class_;
+
+ DISALLOW_COPY_AND_ASSIGN(NpObjectImpl);
+};
+
+template <class ImplClass> NPClass NpObjectImpl<ImplClass>::object_class_ = {
+ NP_CLASS_STRUCT_VERSION,
+ ImplClass::NPAllocate, // allocate;
+ ImplClass::NPDeallocate, // deallocate;
+ ImplClass::NPInvalidate, // invalidate;
+ ImplClass::NPHasMethod, // hasMethod;
+ ImplClass::NPInvoke, // invoke;
+ ImplClass::NPInvokeDefault, // invokeDefault;
+ ImplClass::NPHasProperty, // hasProperty;
+ ImplClass::NPGetProperty, // getProperty;
+ ImplClass::NPSetProperty, // setProperty;
+ ImplClass::NPRemoveProperty, // removeProperty;
+ ImplClass::NPEnumeration, // enumerate
+ ImplClass::NPConstruct, // construct
+};
+
+#endif // CEEE_COMMON_NPOBJECT_IMPL_H_
diff --git a/ceee/common/npobject_impl_unittest.cc b/ceee/common/npobject_impl_unittest.cc
new file mode 100644
index 0000000..5b40170
--- /dev/null
+++ b/ceee/common/npobject_impl_unittest.cc
@@ -0,0 +1,146 @@
+// Copyright (c) 2010 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.
+//
+// Tests for NPAPI Object Wrapper classes.
+
+#include "ceee/common/npobject_impl.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using testing::_;
+using testing::Return;
+
+namespace {
+
+// A bare bones implementation class which really just uses the default
+// implementations of NpObjectImpl/NpObjectBase so we can test those
+// wrapper classes for correct default NPAPI functionality.
+class BareNpObject : public NpObjectImpl<BareNpObject> {
+ public:
+ explicit BareNpObject(NPP npp) : NpObjectImpl<BareNpObject>(npp) {
+ }
+ ~BareNpObject() {
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BareNpObject);
+};
+
+// A mock class which allows us to verify that the corresponding NPObject
+// functions are called when a client calls functions through the NPClass
+// struct's function pointers.
+class MockNpObject : public NpObjectImpl<MockNpObject> {
+ public:
+ explicit MockNpObject(NPP npp) : NpObjectImpl<MockNpObject>(npp) {
+ }
+ ~MockNpObject() {
+ }
+
+ MOCK_METHOD0(Invalidate, void());
+ MOCK_METHOD1(HasMethod, bool(NPIdentifier name));
+ MOCK_METHOD4(Invoke, bool(NPIdentifier name, const NPVariant* args,
+ uint32_t argCount, NPVariant* result));
+ MOCK_METHOD3(InvokeDefault, bool(const NPVariant* args, uint32_t argCount,
+ NPVariant* result));
+ MOCK_METHOD1(HasProperty, bool(NPIdentifier name));
+ MOCK_METHOD2(GetProperty, bool(NPIdentifier name, NPVariant* result));
+ MOCK_METHOD2(SetProperty, bool(NPIdentifier name, const NPVariant* value));
+ MOCK_METHOD1(RemoveProperty, bool(NPIdentifier name));
+ MOCK_METHOD2(Enumeration, bool(NPIdentifier** value, uint32_t* count));
+ MOCK_METHOD3(Construct, bool(const NPVariant* args, uint32_t argCount,
+ NPVariant* result));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockNpObject);
+};
+
+// Test the functions in the NPClass struct to ensure that the correct
+// NP*FunctionPtr function defaults come out. We use
+// https://developer.mozilla.org/en/NPClass as a rough guide for what default
+// values should be.
+TEST(NpApiWrappers, NpObjectDefaults) {
+ // Grab the NPClass structure so we can access the functions that have been
+ // hooked up by this particular implementation.
+ NPClass* np_class = BareNpObject::object_class();
+ ASSERT_TRUE(np_class != NULL);
+
+ // Create an instance of our barebones class so we can use it in the function
+ // calls from the NPClass struct. Also exercises allocation.
+ NPObject* np_object = np_class->allocate(NULL, np_class);
+ ASSERT_TRUE(np_object != NULL);
+
+ EXPECT_GE(np_class->structVersion, (uint32_t)3);
+ EXPECT_FALSE(np_class->hasMethod(np_object, NULL));
+ EXPECT_FALSE(np_class->invoke(np_object, NULL, NULL, 0, NULL));
+ EXPECT_FALSE(np_class->invokeDefault(np_object, NULL, 0, NULL));
+ EXPECT_FALSE(np_class->hasProperty(np_object, NULL));
+ EXPECT_FALSE(np_class->getProperty(np_object, NULL, NULL));
+ EXPECT_FALSE(np_class->setProperty(np_object, NULL, NULL));
+ EXPECT_FALSE(np_class->removeProperty(np_object, NULL));
+ EXPECT_FALSE(np_class->enumerate(np_object, NULL, NULL));
+ EXPECT_FALSE(np_class->construct(np_object, NULL, 0, NULL));
+
+ // Invalidation and deallocation. No return values to check here - just
+ // exercising the code. Deallocation should clean up the object for us.
+ np_class->invalidate(np_object);
+ np_class->deallocate(np_object);
+ np_object = NULL;
+}
+
+// Uses a Mock class to ensure that the correct functions are being called
+// on the NPAPI Object instance when they are called through the NPClass
+// structure that our wrapper sets up.
+TEST(NpApiWrappers, NpObjectCalls) {
+ NPClass* np_class = MockNpObject::object_class();
+ ASSERT_TRUE(np_class != NULL);
+ MockNpObject np_object(NULL);
+
+ EXPECT_CALL(np_object, HasMethod(NULL))
+ .Times(1)
+ .WillOnce(Return(false));
+ EXPECT_FALSE(np_class->hasMethod(&np_object, NULL));
+
+ EXPECT_CALL(np_object, Invoke(NULL, NULL, 0, NULL))
+ .Times(1)
+ .WillOnce(Return(false));
+ EXPECT_FALSE(np_class->invoke(&np_object, NULL, NULL, 0, NULL));
+
+ EXPECT_CALL(np_object, InvokeDefault(NULL, 0, NULL))
+ .Times(1)
+ .WillOnce(Return(false));
+ EXPECT_FALSE(np_class->invokeDefault(&np_object, NULL, 0, NULL));
+
+ EXPECT_CALL(np_object, HasProperty(NULL))
+ .Times(1)
+ .WillOnce(Return(false));
+ EXPECT_FALSE(np_class->hasProperty(&np_object, NULL));
+
+ EXPECT_CALL(np_object, GetProperty(NULL, NULL))
+ .Times(1)
+ .WillOnce(Return(false));
+ EXPECT_FALSE(np_class->getProperty(&np_object, NULL, NULL));
+
+ EXPECT_CALL(np_object, SetProperty(NULL, NULL))
+ .Times(1)
+ .WillOnce(Return(false));
+ EXPECT_FALSE(np_class->setProperty(&np_object, NULL, NULL));
+
+ EXPECT_CALL(np_object, RemoveProperty(NULL))
+ .Times(1)
+ .WillOnce(Return(false));
+ EXPECT_FALSE(np_class->removeProperty(&np_object, NULL));
+
+ EXPECT_CALL(np_object, Enumeration(NULL, NULL))
+ .Times(1)
+ .WillOnce(Return(false));
+ EXPECT_FALSE(np_class->enumerate(&np_object, NULL, NULL));
+
+ EXPECT_CALL(np_object, Construct(NULL, 0, NULL))
+ .Times(1)
+ .WillOnce(Return(false));
+ EXPECT_FALSE(np_class->construct(&np_object, NULL, 0, NULL));
+}
+
+} // namespace
diff --git a/ceee/common/npplugin_impl.cc b/ceee/common/npplugin_impl.cc
new file mode 100644
index 0000000..b0cfee7
--- /dev/null
+++ b/ceee/common/npplugin_impl.cc
@@ -0,0 +1,138 @@
+// Copyright (c) 2010 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.
+//
+// Implementation of NPAPI plugin base class.
+#include "base/logging.h"
+#include "ceee/common/npplugin_impl.h"
+
+NpPluginBase::~NpPluginBase() {
+}
+
+NPError NpPluginBase::SetWindow(NPWindow* window) {
+ return NPERR_NO_ERROR;
+}
+
+NPError NpPluginBase::NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stype) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError NpPluginBase::DestroyStream(NPStream* stream,
+ NPReason reason) {
+ DCHECK(false); // You need to override this if you override NewStream
+ return NPERR_GENERIC_ERROR;
+}
+
+int32 NpPluginBase::WriteReady(NPStream* stream) {
+ return 0;
+}
+
+int32 NpPluginBase::Write(NPStream* stream, int32 offset,
+ int32 len, void* buffer) {
+ return 0;
+}
+
+void NpPluginBase::StreamAsFile(NPStream* stream,
+ const char* fname) {
+}
+
+void NpPluginBase::Print(NPPrint* platform_print) {
+}
+
+int16 NpPluginBase::HandleEvent(void* event) {
+ return 0;
+}
+
+void NpPluginBase::URLNotify(const char* url,
+ NPReason reason, void* notify_data) {
+}
+
+NPError NpPluginBase::GetValue(NPPVariable variable, void* value) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError NpPluginBase::SetValue(NPNVariable variable,
+ void* value) {
+ return NPERR_GENERIC_ERROR;
+}
+
+NPError NpPluginBase::NPP_Destroy(NPP instance, NPSavedData** save) {
+ NpPluginBase* plugin = reinterpret_cast<NpPluginBase*>(instance->pdata);
+ delete plugin;
+ instance->pdata = NULL;
+ return NPERR_NO_ERROR;
+}
+
+NPError NpPluginBase::NPP_SetWindow(NPP instance, NPWindow* window) {
+ NpPluginBase* plugin = reinterpret_cast<NpPluginBase*>(instance->pdata);
+
+ return plugin->SetWindow(window);
+}
+
+NPError NpPluginBase::NPP_NewStream(NPP instance, NPMIMEType type,
+ NPStream* stream, NPBool seekable,
+ uint16* stype) {
+ NpPluginBase* plugin = reinterpret_cast<NpPluginBase*>(instance->pdata);
+ return plugin->NewStream(type, stream, seekable, stype);
+}
+
+NPError NpPluginBase::NPP_DestroyStream(NPP instance, NPStream* stream,
+ NPReason reason) {
+ NpPluginBase* plugin = reinterpret_cast<NpPluginBase*>(instance->pdata);
+
+ return plugin->DestroyStream(stream, reason);
+}
+
+int32 NpPluginBase::NPP_WriteReady(NPP instance, NPStream* stream) {
+ NpPluginBase* plugin = reinterpret_cast<NpPluginBase*>(instance->pdata);
+
+ return plugin->WriteReady(stream);
+}
+
+int32 NpPluginBase::NPP_Write(NPP instance, NPStream* stream, int32 offset,
+ int32 len, void* buffer) {
+ NpPluginBase* plugin = reinterpret_cast<NpPluginBase*>(instance->pdata);
+
+ return plugin->Write(stream, offset, len, buffer);
+}
+
+void NpPluginBase::NPP_StreamAsFile(NPP instance, NPStream* stream,
+ const char* fname) {
+ NpPluginBase* plugin = reinterpret_cast<NpPluginBase*>(instance->pdata);
+
+ return plugin->StreamAsFile(stream, fname);
+}
+
+void NpPluginBase::NPP_Print(NPP instance, NPPrint* platform_print) {
+ NpPluginBase* plugin = reinterpret_cast<NpPluginBase*>(instance->pdata);
+
+ return plugin->Print(platform_print);
+}
+
+int16 NpPluginBase::NPP_HandleEvent(NPP instance, void* event) {
+ NpPluginBase* plugin = reinterpret_cast<NpPluginBase*>(instance->pdata);
+
+ return plugin->HandleEvent(event);
+}
+
+void NpPluginBase::NPP_URLNotify(NPP instance, const char* url,
+ NPReason reason, void* notify_data) {
+ NpPluginBase* plugin = reinterpret_cast<NpPluginBase*>(instance->pdata);
+
+ return plugin->URLNotify(url, reason, notify_data);
+}
+
+NPError NpPluginBase::NPP_GetValue(NPP instance, NPPVariable variable,
+ void* value) {
+ NpPluginBase* plugin = reinterpret_cast<NpPluginBase*>(instance->pdata);
+
+ return plugin->GetValue(variable, value);
+}
+
+NPError NpPluginBase::NPP_SetValue(NPP instance, NPNVariable variable,
+ void* value) {
+ NpPluginBase* plugin = reinterpret_cast<NpPluginBase*>(instance->pdata);
+
+ return plugin->SetValue(variable, value);
+}
diff --git a/ceee/common/npplugin_impl.h b/ceee/common/npplugin_impl.h
new file mode 100644
index 0000000..4068b18b
--- /dev/null
+++ b/ceee/common/npplugin_impl.h
@@ -0,0 +1,147 @@
+// Copyright (c) 2010 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.
+//
+// Base classes to unclutter the writing of NPAPI plugins.
+#ifndef CEEE_COMMON_NPPLUGIN_IMPL_H_
+#define CEEE_COMMON_NPPLUGIN_IMPL_H_
+
+#include "base/basictypes.h"
+#include "third_party/npapi/bindings/nphostapi.h"
+
+// Base class for NPAPI plugins.
+class NpPluginBase {
+ public:
+ explicit NpPluginBase(NPP npp) : npp_(npp) {
+ }
+ virtual ~NpPluginBase();
+
+ protected:
+ NPError Initialize();
+
+ // @name NP Plugin interface.
+ // Override these to implement your plugin's functionality
+ // For documentation on these methods, see the corresponding callback
+ // function description in https://developer.mozilla.org/en
+ // /Gecko_Plugin_API_Reference/Plug-in_Side_Plug-in_API
+ // @{
+ virtual NPError SetWindow(NPWindow* window);
+ virtual NPError NewStream(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stype);
+ virtual NPError DestroyStream(NPStream* stream,
+ NPReason reason);
+ virtual int32 WriteReady(NPStream* stream);
+ virtual int32 Write(NPStream* stream, int32 offset,
+ int32 len, void* buffer);
+ virtual void StreamAsFile(NPStream* stream,
+ const char* fname);
+ virtual void Print(NPPrint* platform_print);
+ virtual int16 HandleEvent(void* event);
+ virtual void URLNotify(const char* url, NPReason reason, void* notify_data);
+ virtual NPError GetValue(NPPVariable variable, void* value);
+ virtual NPError SetValue(NPNVariable variable, void* value);
+ // @}
+
+ // @name NP callback functions.
+ // @{
+ static NPError NPP_Destroy(NPP instance, NPSavedData** save);
+ static NPError NPP_SetWindow(NPP instance, NPWindow* window);
+ static NPError NPP_NewStream(NPP instance, NPMIMEType type,
+ NPStream* stream, NPBool seekable,
+ uint16* stype);
+ static NPError NPP_DestroyStream(NPP instance, NPStream* stream,
+ NPReason reason);
+ static int32 NPP_WriteReady(NPP instance, NPStream* stream);
+ static int32 NPP_Write(NPP instance, NPStream* stream, int32 offset,
+ int32 len, void* buffer);
+ static void NPP_StreamAsFile(NPP instance, NPStream* stream,
+ const char* fname);
+ static void NPP_Print(NPP instance, NPPrint* platform_print);
+ static int16 NPP_HandleEvent(NPP instance, void* event);
+ static void NPP_URLNotify(NPP instance, const char* url,
+ NPReason reason, void* notifyData);
+ static NPError NPP_GetValue(NPP instance, NPPVariable variable, void* value);
+ static NPError NPP_SetValue(NPP instance, NPNVariable variable, void* value);
+ // @}
+
+ // Our instance.
+ NPP npp_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NpPluginBase);
+};
+
+// Derive your NPAPI implementation class from NpPluginImpl<MyClass>.
+template <class ImplClass> class NpPluginImpl: public NpPluginBase {
+ public:
+ explicit NpPluginImpl(NPP npp) : NpPluginBase(npp) {
+ }
+
+ // Retrieve a set of plugin entrypoints suitable for ImplClass.
+ static NPError GetEntryPoints(NPPluginFuncs* plugin_funcs);
+
+ protected:
+ static NPError NPP_New(NPMIMEType plugin_type, NPP instance,
+ uint16 mode, int16 argc, char* argn[],
+ char* argv[], NPSavedData* saved);
+ // Override this in ImplClass if you need custom creation logic.
+ static NPError Create(NPMIMEType plugin_type, NPP instance,
+ uint16 mode, int16 argc, char* argn[],
+ char* argv[], NPSavedData* saved);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(NpPluginImpl);
+};
+
+template <class ImplClass>
+NPError NpPluginImpl<ImplClass>::GetEntryPoints(NPPluginFuncs* plugin_funcs) {
+ plugin_funcs->version = 11;
+ plugin_funcs->size = sizeof(plugin_funcs);
+ plugin_funcs->newp = ImplClass::NPP_New;
+ plugin_funcs->destroy = ImplClass::NPP_Destroy;
+ plugin_funcs->setwindow = ImplClass::NPP_SetWindow;
+ plugin_funcs->newstream = ImplClass::NPP_NewStream;
+ plugin_funcs->destroystream = ImplClass::NPP_DestroyStream;
+ plugin_funcs->asfile = ImplClass::NPP_StreamAsFile;
+ plugin_funcs->writeready = ImplClass::NPP_WriteReady;
+ plugin_funcs->write = ImplClass::NPP_Write;
+ plugin_funcs->print = ImplClass::NPP_Print;
+ plugin_funcs->event = ImplClass::NPP_HandleEvent;
+ plugin_funcs->urlnotify = ImplClass::NPP_URLNotify;
+ plugin_funcs->getvalue = ImplClass::NPP_GetValue;
+ plugin_funcs->setvalue = ImplClass::NPP_SetValue;
+
+ return NPERR_NO_ERROR;
+}
+
+template <class ImplClass>
+NPError NpPluginImpl<ImplClass>::NPP_New(NPMIMEType plugin_type, NPP instance,
+ uint16 mode, int16 argc, char* argn[],
+ char* argv[], NPSavedData* saved) {
+ // This gives the implclass an opportunity to
+ // override default creation behavior.
+ return ImplClass::Create(plugin_type,
+ instance,
+ mode,
+ argc,
+ argn,
+ argv,
+ saved);
+}
+
+template <class ImplClass>
+NPError NpPluginImpl<ImplClass>::Create(NPMIMEType plugin_type, NPP instance,
+ uint16 mode, int16 argc, char* argn[],
+ char* argv[], NPSavedData* saved) {
+ ImplClass* impl = new ImplClass(instance);
+ NPError error = impl->Initialize();
+ if (NPERR_NO_ERROR == error) {
+ instance->pdata = impl;
+ } else {
+ delete impl;
+ }
+
+ return error;
+}
+
+#endif // CEEE_COMMON_NPPLUGIN_IMPL_H_
diff --git a/ceee/common/npplugin_impl_unittest.cc b/ceee/common/npplugin_impl_unittest.cc
new file mode 100644
index 0000000..3a92475
--- /dev/null
+++ b/ceee/common/npplugin_impl_unittest.cc
@@ -0,0 +1,167 @@
+// Copyright (c) 2010 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.
+//
+// Tests for NPAPI Plugin Wrapper classes.
+
+#include "ceee/common/npplugin_impl.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+using testing::_;
+using testing::Return;
+
+namespace {
+
+// A bare bones implementation class which really just uses the default
+// implementations of NpPluginImpl/NpPluginBase so we can test those
+// wrapper classes for correct default NPAPI functionality.
+class BareNpPlugin : public NpPluginImpl<BareNpPlugin> {
+ public:
+ explicit BareNpPlugin(NPP instance) : NpPluginImpl<BareNpPlugin>(instance) {
+ }
+ ~BareNpPlugin() {
+ }
+
+ // TODO(stevet@google.com): Note that we re implement this to avoid
+ // the DCHECK in the original base class. If we can find a non
+ // crashing method of reminding developers to implement
+ // DestroyStream, we can remove this implementation.
+ virtual NPError DestroyStream(NPStream* stream,
+ NPReason reason) {
+ return NPERR_GENERIC_ERROR;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BareNpPlugin);
+};
+
+// A mock class which allows us to verify that corresponding NPPlugin functions
+// are called after they are setup by a call to GetEntryPoints.
+class MockNpPlugin : public NpPluginImpl<MockNpPlugin> {
+ public:
+ explicit MockNpPlugin(NPP instance) : NpPluginImpl<MockNpPlugin>(instance) {
+ }
+ ~MockNpPlugin() {
+ }
+
+ NPError Initialize() {
+ return NPERR_NO_ERROR;
+ }
+
+ MOCK_METHOD1(SetWindow, NPError(NPWindow* window));
+ MOCK_METHOD4(NewStream, NPError(NPMIMEType type, NPStream* stream,
+ NPBool seekable, uint16* stype));
+ MOCK_METHOD2(DestroyStream, NPError(NPStream* stream, NPReason reason));
+ MOCK_METHOD1(WriteReady, int32(NPStream* stream));
+ MOCK_METHOD4(Write, int32(NPStream* stream, int32 offset,
+ int32 len, void* buffer));
+ MOCK_METHOD2(StreamAsFile, void(NPStream* stream, const char* fname));
+ MOCK_METHOD1(Print, void(NPPrint* platform_print));
+ MOCK_METHOD1(HandleEvent, int16(void* event));
+ MOCK_METHOD3(URLNotify, void(const char* url, NPReason reason,
+ void* notify_data));
+ MOCK_METHOD2(GetValue, NPError(NPPVariable variable, void* value));
+ MOCK_METHOD2(SetValue, NPError(NPNVariable variable, void* value));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockNpPlugin);
+};
+
+// Test to ensure that the NPPlugin helpers' defaults are correct. Note that
+// we use http://goo.gl/u0VT as a rough guide to what values are defaults.
+TEST(NpApiWrappers, NpPluginDefaults) {
+ NPP instance = new NPP_t;
+ BareNpPlugin np_plugin(instance);
+ instance->pdata = &np_plugin;
+
+ NPPluginFuncs plugin_funcs;
+ EXPECT_EQ(NPERR_NO_ERROR, MockNpPlugin::GetEntryPoints(&plugin_funcs));
+
+ EXPECT_EQ(NPERR_NO_ERROR, plugin_funcs.setwindow(instance, NULL));
+ EXPECT_EQ(NPERR_GENERIC_ERROR,
+ plugin_funcs.newstream(instance, NULL, NULL, '0', NULL));
+ EXPECT_EQ(NPERR_GENERIC_ERROR, plugin_funcs.destroystream(instance, NULL, 0));
+ EXPECT_EQ(0, plugin_funcs.writeready(instance, NULL));
+ EXPECT_EQ(0, plugin_funcs.write(instance, NULL, 0, 0, NULL));
+ EXPECT_EQ(0, plugin_funcs.event(instance, NULL));
+ EXPECT_EQ(NPERR_GENERIC_ERROR, plugin_funcs.getvalue(instance,
+ (NPPVariable)1, NULL));
+ EXPECT_EQ(NPERR_GENERIC_ERROR, plugin_funcs.setvalue(instance,
+ (NPNVariable)1, NULL));
+}
+
+// Initialize a NPPluginFuncs struct with a call to NpPluginImpl<ImplClass>
+// ::GetEntryPoints and ensure that the appropriate functions are attached
+// to the structure.
+TEST(NpApiWrappers, NpPluginCalls) {
+ // Create a faux NPP instance since function calls need it's pdata field to
+ // determine what plugin object should be making the call.
+ NPP instance = new NPP_t;
+ MockNpPlugin np_plugin(instance);
+ instance->pdata = &np_plugin;
+
+ NPPluginFuncs plugin_funcs;
+ EXPECT_EQ(NPERR_NO_ERROR, MockNpPlugin::GetEntryPoints(&plugin_funcs));
+
+ EXPECT_CALL(np_plugin, SetWindow(NULL))
+ .Times(1)
+ .WillOnce(Return(NPERR_NO_ERROR));
+ EXPECT_EQ(NPERR_NO_ERROR, plugin_funcs.setwindow(instance, NULL));
+
+ EXPECT_CALL(np_plugin, NewStream(NULL, NULL, '0', NULL))
+ .Times(1)
+ .WillOnce(Return(NPERR_GENERIC_ERROR));
+ EXPECT_EQ(NPERR_GENERIC_ERROR,
+ plugin_funcs.newstream(instance, NULL, NULL, '0', NULL));
+
+ EXPECT_CALL(np_plugin, DestroyStream(NULL, 0))
+ .Times(1)
+ .WillOnce(Return(NPERR_GENERIC_ERROR));
+ EXPECT_EQ(NPERR_GENERIC_ERROR, plugin_funcs.destroystream(instance, NULL, 0));
+
+ EXPECT_CALL(np_plugin, WriteReady(NULL))
+ .Times(1)
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, plugin_funcs.writeready(instance, NULL));
+
+ EXPECT_CALL(np_plugin, Write(NULL, 0, 0, NULL))
+ .Times(1)
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, plugin_funcs.write(instance, NULL, 0, 0, NULL));
+
+ EXPECT_CALL(np_plugin, StreamAsFile(NULL, NULL))
+ .Times(1);
+ plugin_funcs.asfile(instance, NULL, NULL);
+
+ EXPECT_CALL(np_plugin, Print(NULL))
+ .Times(1);
+ plugin_funcs.print(instance, NULL);
+
+ EXPECT_CALL(np_plugin, HandleEvent(NULL))
+ .Times(1)
+ .WillOnce(Return(0));
+ EXPECT_EQ(0, plugin_funcs.event(instance, NULL));
+
+ EXPECT_CALL(np_plugin, URLNotify(NULL, 0, NULL))
+ .Times(1);
+ plugin_funcs.urlnotify(instance, NULL, 0, NULL);
+
+ EXPECT_CALL(np_plugin, GetValue((NPPVariable)1, NULL))
+ .Times(1)
+ .WillOnce(Return(NPERR_GENERIC_ERROR));
+ EXPECT_EQ(NPERR_GENERIC_ERROR, plugin_funcs.getvalue(instance,
+ (NPPVariable)1, NULL));
+
+ EXPECT_CALL(np_plugin, SetValue((NPNVariable)1, NULL))
+ .Times(1)
+ .WillOnce(Return(NPERR_GENERIC_ERROR));
+ EXPECT_EQ(NPERR_GENERIC_ERROR, plugin_funcs.setvalue(instance,
+ (NPNVariable)1, NULL));
+
+ delete instance;
+ instance = NULL;
+}
+
+} // namespace
diff --git a/ceee/common/process_utils_win.cc b/ceee/common/process_utils_win.cc
new file mode 100644
index 0000000..094af1e
--- /dev/null
+++ b/ceee/common/process_utils_win.cc
@@ -0,0 +1,330 @@
+// Copyright (c) 2010 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.
+//
+// Utilities for windows process and threads stuff.
+
+#include "ceee/common/process_utils_win.h"
+
+#include <sddl.h>
+
+#include "base/logging.h"
+#include "base/scoped_handle.h"
+#include "base/win/windows_version.h"
+#include "ceee/common/com_utils.h"
+
+
+namespace process_utils_win {
+
+HRESULT SetThreadIntegrityLevel(HANDLE* thread, const std::wstring& level) {
+ HANDLE temp_handle = NULL;
+ BOOL success = ::OpenProcessToken(
+ ::GetCurrentProcess(), MAXIMUM_ALLOWED, &temp_handle);
+ ScopedHandle process_token(temp_handle);
+ temp_handle = NULL;
+ if (success) {
+ success = ::DuplicateTokenEx(
+ process_token, MAXIMUM_ALLOWED, NULL, SecurityImpersonation,
+ TokenImpersonation, &temp_handle);
+ ScopedHandle mic_token(temp_handle);
+ temp_handle = NULL;
+ if (success) {
+ PSID mic_sid = NULL;
+ success = ::ConvertStringSidToSid(level.c_str(), &mic_sid);
+ if (success) {
+ // Set Process IL to Low
+ TOKEN_MANDATORY_LABEL tml = {0};
+ tml.Label.Attributes = SE_GROUP_INTEGRITY | SE_GROUP_INTEGRITY_ENABLED;
+ tml.Label.Sid = mic_sid;
+ success = ::SetTokenInformation(
+ mic_token, TokenIntegrityLevel, &tml,
+ sizeof(tml) + ::GetLengthSid(mic_sid));
+ if (success) {
+ success = ::SetThreadToken(thread, mic_token);
+ LOG_IF(WARNING, !success) << "Failed to SetThreadToken." <<
+ com::LogWe();
+ } else {
+ LOG(WARNING) << "Failed to SetTokenInformation." << com::LogWe();
+ }
+ ::LocalFree(mic_sid);
+ } else {
+ LOG(WARNING) << "Failed to SIDConvert: " << level << com::LogWe();
+ }
+ } else {
+ LOG(WARNING) << "Failed to Duplicate the process token." << com::LogWe();
+ }
+ } else {
+ LOG(WARNING) << "Failed to Open the process token." << com::LogWe();
+ }
+
+ if (success)
+ return S_OK;
+ else
+ return HRESULT_FROM_WIN32(GetLastError());
+}
+
+HRESULT ResetThreadIntegrityLevel(HANDLE* thread) {
+ if (::SetThreadToken(thread, NULL))
+ return S_OK;
+ else
+ return HRESULT_FROM_WIN32(GetLastError());
+}
+
+HRESULT IsCurrentProcessUacElevated(bool* running_as_admin) {
+ DCHECK(NULL != running_as_admin);
+
+ if (base::win::GetVersion() < base::win::VERSION_VISTA) {
+ *running_as_admin = false;
+ return S_OK;
+ }
+
+ HANDLE temp_handle = NULL;
+ if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &temp_handle)) {
+ DWORD error_code = ::GetLastError();
+ LOG(WARNING) << "Failed to Open the process token." <<
+ com::LogWe(error_code);
+ return com::AlwaysErrorFromWin32(error_code);
+ }
+
+ // We check if the current process is running in a higher integrity mode than
+ // it would be running if it was started 'normally' by checking if its token
+ // has an associated full elevation token. This seems to do the trick and I
+ // carefully checked it against the obvious alternative of checking the
+ // integrity level of the current process. This is what I found out:
+ // UAC off, normal start: token default, high integrity
+ // UAC off, admin start: token default, high integrity
+ // UAC on, normal start: token limited, medium integrity
+ // UAC on, admin start: token full, medium integrity
+ // All that for an admin-group member, who can run in elevated mode.
+ // This logic applies to Vista/Win7. The case of earlier systems is handled
+ // at the start.
+ ScopedHandle process_token(temp_handle);
+ TOKEN_ELEVATION_TYPE elevation_type = TokenElevationTypeDefault;
+ DWORD variable_len_dummy = 0;
+ if (!::GetTokenInformation(process_token, TokenElevationType, &elevation_type,
+ sizeof(elevation_type), &variable_len_dummy)) {
+ DWORD error_code = ::GetLastError();
+ LOG(WARNING) << "Failed to retrieve token information." <<
+ com::LogWe(error_code);
+ return com::AlwaysErrorFromWin32(error_code);
+ }
+
+ *running_as_admin = elevation_type == TokenElevationTypeFull;
+
+ return S_OK;
+}
+
+ProcessCompatibilityCheck::OpenProcessFuncType
+ ProcessCompatibilityCheck::open_process_func_ = OpenProcess;
+ProcessCompatibilityCheck::CloseHandleFuncType
+ ProcessCompatibilityCheck::close_handle_func_ = CloseHandle;
+ProcessCompatibilityCheck::IsWOW64ProcessFuncType
+ ProcessCompatibilityCheck::is_wow64_process_func_ = IsWow64Process;
+
+ProcessCompatibilityCheck::ProcessCompatibilityCheck()
+ : initialization_result_(E_FAIL),
+ running_on_amd64_platform_(false),
+ running_as_wow_64_(false),
+ integrity_checks_on_(false),
+ running_integrity_(base::INTEGRITY_UNKNOWN) {
+ StandardInitialize();
+}
+
+void ProcessCompatibilityCheck::StandardInitialize() {
+ HRESULT hr = S_OK;
+
+ SYSTEM_INFO system_info;
+ ::GetNativeSystemInfo(&system_info);
+
+ bool system_with_integrity_checks =
+ base::win::GetVersion() >= base::win::VERSION_VISTA;
+
+ base::IntegrityLevel integrity_level = base::INTEGRITY_UNKNOWN;
+ if (system_with_integrity_checks &&
+ !base::GetProcessIntegrityLevel(::GetCurrentProcess(),
+ &integrity_level)) {
+ hr = com::AlwaysErrorFromLastError();
+ }
+
+ BOOL is_wow_64 = FALSE;
+ // If not running_on_amd64_platform_, all processes are OK and
+ // we wouldn't need all these tools...
+ if (system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64 &&
+ !is_wow64_process_func_(::GetCurrentProcess(), &is_wow_64)) {
+ hr = com::AlwaysErrorFromLastError();
+ }
+
+ KnownStateInitialize(system_info.wProcessorArchitecture, is_wow_64,
+ system_with_integrity_checks, integrity_level);
+ initialization_result_ = hr;
+}
+
+// Initialize with exactly this data with some checks, just in case.
+void ProcessCompatibilityCheck::KnownStateInitialize(
+ WORD system_type, bool current_process_wow64,
+ bool check_integrity, base::IntegrityLevel current_process_integrity) {
+ DCHECK(system_type == PROCESSOR_ARCHITECTURE_AMD64 ||
+ system_type == PROCESSOR_ARCHITECTURE_INTEL);
+
+ running_on_amd64_platform_ = system_type == PROCESSOR_ARCHITECTURE_AMD64;
+ running_as_wow_64_ = current_process_wow64;
+
+ // Can't be running_as_wow_64_ while not running_on_amd64_platform_.
+ DCHECK((running_on_amd64_platform_ && running_as_wow_64_) ||
+ !running_as_wow_64_);
+
+ integrity_checks_on_ = check_integrity;
+ running_integrity_ = current_process_integrity;
+
+ // Assert that running_integrity_ is given whenever integrity_checks_on_:
+ // integrity_checks_on_ => running_integrity_ != base::INTEGRITY_UNKNOWN AND
+ // !integrity_checks_on_ => running_integrity_ == base::INTEGRITY_UNKNOWN.
+ DCHECK(((integrity_checks_on_ &&
+ running_integrity_ != base::INTEGRITY_UNKNOWN) ||
+ !integrity_checks_on_) &&
+ ((!integrity_checks_on_ &&
+ running_integrity_ == base::INTEGRITY_UNKNOWN) ||
+ integrity_checks_on_));
+}
+
+HRESULT ProcessCompatibilityCheck::IsCompatible(HWND process_window,
+ bool* is_compatible) {
+ DWORD process_id = 0;
+ if (::GetWindowThreadProcessId(process_window, &process_id) != 0) {
+ return IsCompatible(process_id, is_compatible);
+ }
+
+ return E_FAIL;
+}
+
+HRESULT ProcessCompatibilityCheck::IsCompatible(DWORD process_id,
+ bool* is_compatible) {
+ ProcessCompatibilityCheck* instance
+ = Singleton<ProcessCompatibilityCheck>::get();
+
+ DCHECK(instance != NULL);
+ DCHECK(is_compatible != NULL);
+
+ if (instance != NULL && SUCCEEDED(instance->initialization_result_)) {
+ return instance->IsProcessCompatible(process_id, is_compatible);
+ }
+
+ return instance != NULL ? instance->initialization_result_ : E_FAIL;
+}
+
+HRESULT ProcessCompatibilityCheck::IsProcessCompatible(DWORD process_id,
+ bool* is_compatible) {
+ DCHECK(is_compatible != NULL);
+
+ if (!running_on_amd64_platform_ && !integrity_checks_on_) {
+ *is_compatible = true;
+ return S_OK;
+ }
+
+ HRESULT hr = E_FAIL;
+ HANDLE other_process = open_process_func_(PROCESS_QUERY_INFORMATION,
+ FALSE, process_id);
+
+ if (other_process != NULL) {
+ bool platform_compatible = false;
+ bool integrity_compatible = false;
+ hr = IsProcessPlatformCompatible(other_process, &platform_compatible);
+ if (SUCCEEDED(hr))
+ hr = IsProcessIntegrityCompatible(other_process, &integrity_compatible);
+
+ if (SUCCEEDED(hr))
+ *is_compatible = platform_compatible && integrity_compatible;
+
+ if (!close_handle_func_(other_process))
+ LOG(WARNING) << "CloseHandle failed: " << com::LogWe();
+ }
+
+ return hr;
+}
+
+// Is the process we are inquiring about of the same type
+// (native 32, native 64 or WOW64) as the current process.
+HRESULT ProcessCompatibilityCheck::IsProcessPlatformCompatible(
+ HANDLE process_handle, bool* is_compatible) {
+ if (!running_on_amd64_platform_) {
+ *is_compatible = true;
+ return S_OK;
+ }
+
+ BOOL is_other_wow_64 = FALSE;
+
+ if (is_wow64_process_func_(process_handle, &is_other_wow_64)) {
+ *is_compatible = (running_as_wow_64_ && is_other_wow_64) ||
+ !(running_as_wow_64_ || is_other_wow_64);
+ return S_OK;
+ }
+
+ return com::AlwaysErrorFromLastError();
+}
+
+// Is the process we are inquiring about at the integrity level which should
+// permit inserting an executor into that process?
+// For the purpose of security-compatibility high integrity processes are
+// considered a separate class from medium and low integrity processes because
+// in interactive scenarios they will belong to different logon sessions.
+// It appears we cannot programatically cross this boundary in COM and so we
+// want to filter these processes out. On the other hand, medium and low
+// integrity processes will exist within the same logon session and can
+// attempt communication (in the sense discussed here).
+// In practice this corresponds to running instances of IE as administrator and
+// normally. The former group of processes will function at high integrity level
+// while the latter at medium or low (IE's protected mode?). Note that each of
+// these groups of processes will have a separate ceee_broker. Inquiring about
+// process integrity appears to be a very practical way of properly routing
+// requests to IE's windows from a broker instance.
+HRESULT ProcessCompatibilityCheck::IsProcessIntegrityCompatible(
+ HANDLE process_handle, bool* is_compatible) {
+ if (!integrity_checks_on_) {
+ *is_compatible = true;
+ return S_OK;
+ }
+
+ base::IntegrityLevel integrity_level = base::INTEGRITY_UNKNOWN;
+ if (base::GetProcessIntegrityLevel(process_handle, &integrity_level)) {
+ *is_compatible = (integrity_level == base::HIGH_INTEGRITY &&
+ running_integrity_ == base::HIGH_INTEGRITY) ||
+ (integrity_level != base::HIGH_INTEGRITY &&
+ running_integrity_ != base::HIGH_INTEGRITY);
+ return S_OK;
+ }
+
+ return com::AlwaysErrorFromLastError();
+}
+
+// Patching affects both static pointers and the state of the sigleton.
+// To update the later, required variables are forwarded to its
+// KnownStateInitialize.
+void ProcessCompatibilityCheck::PatchState(
+ WORD system_type, bool current_process_wow64,
+ bool check_integrity, base::IntegrityLevel current_process_integrity,
+ OpenProcessFuncType open_process_func,
+ CloseHandleFuncType close_handle_func,
+ IsWOW64ProcessFuncType is_wow64_process_func) {
+ PatchState(open_process_func, close_handle_func, is_wow64_process_func);
+ Singleton<ProcessCompatibilityCheck>::get()->KnownStateInitialize(
+ system_type, current_process_wow64,
+ check_integrity, current_process_integrity);
+}
+
+void ProcessCompatibilityCheck::PatchState(
+ OpenProcessFuncType open_process_func,
+ CloseHandleFuncType close_handle_func,
+ IsWOW64ProcessFuncType is_wow64_process_func) {
+ open_process_func_ = open_process_func;
+ close_handle_func_ = close_handle_func;
+ is_wow64_process_func_ = is_wow64_process_func;
+ DCHECK(open_process_func_ != NULL && close_handle_func_ != NULL &&
+ is_wow64_process_func_ != NULL);
+}
+
+void ProcessCompatibilityCheck::ResetState() {
+ PatchState(OpenProcess, CloseHandle, IsWow64Process);
+ Singleton<ProcessCompatibilityCheck>::get()->StandardInitialize();
+}
+
+} // namespace com
diff --git a/ceee/common/process_utils_win.h b/ceee/common/process_utils_win.h
new file mode 100644
index 0000000..e928791
--- /dev/null
+++ b/ceee/common/process_utils_win.h
@@ -0,0 +1,122 @@
+// Copyright (c) 2010 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.
+//
+// Utilities for process/threads in Windows.
+
+#ifndef CEEE_COMMON_PROCESS_UTILS_WIN_H_
+#define CEEE_COMMON_PROCESS_UTILS_WIN_H_
+
+#include <windows.h>
+#include <wtypes.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/singleton.h"
+#include "base/process_util.h"
+
+namespace process_utils_win {
+
+// Sets the integrity level of the specified thread, or the current thread
+// if thread is NULL. The level is a string as #defined in sddl.h, e.g.,
+// SDDL_ML_LOW or any string of the proper SID Component format as described
+// here: http://msdn.microsoft.com/en-us/library/aa379597(v=VS.85).aspx
+// Will return HRESULT_FROM_WIN32(ERROR_PRIVILEGE_NOT_HELD) if the provided
+// thread (or its process) have lower priviliges then the ones requested.
+HRESULT SetThreadIntegrityLevel(HANDLE* thread, const std::wstring& level);
+
+// Reset the thread integrity level to the process level. Again, if thread
+// is NULL, set the integrity level of the current thread.
+HRESULT ResetThreadIntegrityLevel(HANDLE* thread);
+
+// The bool pointer will be set to true if the current process has been started
+// in administrator's mode ('Run As Adminstrator') while UAC was active.
+// Programs started that way run at different integrity level than these
+// invoked by the user in a regular way.
+// More detailed analysis is with the implementation.
+HRESULT IsCurrentProcessUacElevated(bool* running_as_admin);
+
+// Checks if a given process is compatible with the current process.
+// Two processes are compatible if they run on the same platform-compatible and
+// are security-compatible.
+// The former makes sense on a 64-bit platform, where 32-bit processes inhabit
+// a separate subsystem (WOW64). Thus, two processes are platform-compatible
+// when both are 32-bit or both are 64-bit. It is noop on 32-bit systems.
+// Two processes are security-compatible when their integrity levels permit COM
+// communication between them. High integrity processes are compatible only with
+// other high integrity processes. A low or a medium integrity process is
+// compatible with any process that is either low or medium integrity.
+// Some considerations of security-compatibility are expanded upon in comments
+// with implementation.
+class ProcessCompatibilityCheck {
+ public:
+ // Is the process associated with the given window compatible with the
+ // current process. If the call returns an error code, the value of
+ // *is_compatible is undefined.
+ static HRESULT IsCompatible(HWND process_window, bool* is_compatible);
+
+ // Is the process of given ID compatible with the current process. If the
+ // call returns an error code, the value of *is_compatible is undefined.
+ static HRESULT IsCompatible(DWORD process_id, bool* is_compatible);
+
+ protected:
+ typedef HANDLE (WINAPI *OpenProcessFuncType)(DWORD, BOOL, DWORD);
+ typedef BOOL (WINAPI *CloseHandleFuncType)(HANDLE);
+ typedef BOOL (WINAPI *IsWOW64ProcessFuncType)(HANDLE, PBOOL);
+
+ // Non-trivial constructor will initialize default state. Possible error code
+ // will be stored for reference.
+ ProcessCompatibilityCheck();
+
+ // Exposed for unittesting only.
+ static void PatchState(WORD system_type, bool current_process_wow64,
+ bool check_integrity,
+ base::IntegrityLevel current_process_integrity,
+ OpenProcessFuncType open_process_func,
+ CloseHandleFuncType close_handle_func,
+ IsWOW64ProcessFuncType is_wow64_process_func);
+ static void PatchState(OpenProcessFuncType open_process_func,
+ CloseHandleFuncType close_handle_func,
+ IsWOW64ProcessFuncType is_wow64_process_func);
+ // Reset to normal state.
+ static void ResetState();
+
+ // Is the process of given ID compatible with the current process?
+ HRESULT IsProcessCompatible(DWORD process_id, bool* is_compatible);
+
+ private:
+ // Initialization function taking true system state.
+ void StandardInitialize();
+
+ // Initialize call assigning given values to objects.
+ void KnownStateInitialize(WORD system_type, bool current_process_wow64,
+ bool check_integrity,
+ base::IntegrityLevel current_process_integrity);
+
+ // Functions checking aspects of 'compatibility' of processes with the
+ // current process.
+ HRESULT IsProcessPlatformCompatible(HANDLE process_handle,
+ bool* is_compatible);
+ HRESULT IsProcessIntegrityCompatible(HANDLE process_handle,
+ bool* is_compatible);
+
+ HRESULT initialization_result_;
+ bool running_on_amd64_platform_;
+ bool running_as_wow_64_;
+ bool integrity_checks_on_;
+ base::IntegrityLevel running_integrity_;
+
+ // Function pointers held to allow substituting with a test harness.
+ static OpenProcessFuncType open_process_func_;
+ static CloseHandleFuncType close_handle_func_;
+ static IsWOW64ProcessFuncType is_wow64_process_func_;
+
+ friend struct DefaultSingletonTraits<ProcessCompatibilityCheck>;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessCompatibilityCheck);
+};
+
+} // namespace process_utils_win
+
+#endif // CEEE_COMMON_PROCESS_UTILS_WIN_H_
diff --git a/ceee/common/process_utils_win_unittest.cc b/ceee/common/process_utils_win_unittest.cc
new file mode 100644
index 0000000..db6ceaf
--- /dev/null
+++ b/ceee/common/process_utils_win_unittest.cc
@@ -0,0 +1,571 @@
+// Copyright (c) 2010 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.
+//
+// Unit tests for process utils for Windows.
+
+#include "base/scoped_ptr.h"
+#include "base/win/windows_version.h"
+#include "ceee/common/process_utils_win.h"
+
+#include "ceee/testing/utils/mock_win32.h"
+#include "ceee/testing/utils/mock_static.h"
+#include "ceee/testing/utils/test_utils.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+using process_utils_win::ProcessCompatibilityCheck;
+using testing::_;
+using testing::Invoke;
+using testing::NativeSystemInfoMockTool;
+using testing::Return;
+using testing::SetArgumentPointee;
+using testing::StrictMock;
+using testing::WithArgs;
+
+MOCK_STATIC_CLASS_BEGIN(MockProcessTokenAccess)
+ MOCK_STATIC_INIT_BEGIN(MockProcessTokenAccess)
+ MOCK_STATIC_INIT2(base::GetProcessIntegrityLevel, GetProcessIntegrityLevel);
+ MOCK_STATIC_INIT(GetTokenInformation);
+ MOCK_STATIC_INIT2(base::win::GetVersion, GetVersion);
+ MOCK_STATIC_INIT_END()
+
+ MOCK_STATIC2(bool, , GetProcessIntegrityLevel, HANDLE, base::IntegrityLevel*);
+ MOCK_STATIC5(BOOL, WINAPI, GetTokenInformation, HANDLE,
+ TOKEN_INFORMATION_CLASS, void*, DWORD, DWORD*);
+ MOCK_STATIC0(base::win::Version, , GetVersion);
+MOCK_STATIC_CLASS_END(MockProcessTokenAccess)
+
+class TokenInfoExpectations {
+ public:
+ TokenInfoExpectations(BOOL return_value, TOKEN_ELEVATION_TYPE set_value)
+ : return_value_(return_value), set_value_(set_value) {
+ }
+
+ BOOL QueryInformation(void* value_carrier) {
+ TOKEN_ELEVATION_TYPE* value_carrier_coerce =
+ reinterpret_cast<TOKEN_ELEVATION_TYPE*>(value_carrier);
+ *value_carrier_coerce = set_value_;
+
+ return return_value_;
+ }
+
+ BOOL return_value_;
+ TOKEN_ELEVATION_TYPE set_value_;
+
+ private:
+ TokenInfoExpectations();
+};
+
+
+TEST(ProcessUtilsWin, SetThreadIntegrityLevel) {
+ // TODO(mad@chromium.org): Test this!
+}
+
+TEST(ProcessUtilsWin, ResetThreadIntegrityLevel) {
+ // TODO(mad@chromium.org): Test this too!
+}
+
+
+// This harness tests the implementation rather than the function and how
+// well this method meets the requirement. Unfortunate, but necessary.
+TEST(ProcessUtilsWin, IsRunningWithElevatedToken) {
+ StrictMock<MockProcessTokenAccess> token_mock;
+ bool running_admin = true;
+ TokenInfoExpectations token_set(TRUE, TokenElevationTypeDefault);
+
+ // In this function we check operation on systems with UAC
+ EXPECT_CALL(token_mock, GetVersion())
+ .WillRepeatedly(Return(base::win::VERSION_VISTA));
+
+ EXPECT_CALL(token_mock, GetTokenInformation(_, TokenElevationType, _, _, _))
+ .WillOnce(WithArgs<2>(
+ Invoke(&token_set, &TokenInfoExpectations::QueryInformation)));
+
+ ASSERT_HRESULT_SUCCEEDED(
+ process_utils_win::IsCurrentProcessUacElevated(&running_admin));
+ ASSERT_FALSE(running_admin);
+
+ token_set.set_value_ = TokenElevationTypeLimited;
+ EXPECT_CALL(token_mock, GetTokenInformation(_, TokenElevationType, _, _, _))
+ .WillOnce(WithArgs<2>(
+ Invoke(&token_set, &TokenInfoExpectations::QueryInformation)));
+ ASSERT_HRESULT_SUCCEEDED(
+ process_utils_win::IsCurrentProcessUacElevated(&running_admin));
+ ASSERT_FALSE(running_admin);
+
+ token_set.set_value_ = TokenElevationTypeFull;
+ EXPECT_CALL(token_mock, GetTokenInformation(_, TokenElevationType, _, _, _))
+ .WillOnce(WithArgs<2>(
+ Invoke(&token_set, &TokenInfoExpectations::QueryInformation)));
+ ASSERT_HRESULT_SUCCEEDED(
+ process_utils_win::IsCurrentProcessUacElevated(&running_admin));
+ ASSERT_TRUE(running_admin);
+}
+
+TEST(ProcessUtilsWin, IsRunningWithElevatedTokenOnXP) {
+ StrictMock<MockProcessTokenAccess> token_mock;
+ bool running_admin = true;
+
+ EXPECT_CALL(token_mock, GetVersion())
+ .WillOnce(Return(base::win::VERSION_XP));
+ ASSERT_HRESULT_SUCCEEDED(
+ process_utils_win::IsCurrentProcessUacElevated(&running_admin));
+ ASSERT_FALSE(running_admin); // No UAC on WinXP and thus no elevation.
+}
+
+TEST(ProcessUtilsWin, IsRunningWithElevatedTokenError) {
+ StrictMock<MockProcessTokenAccess> token_mock;
+ bool running_admin = true;
+ TokenInfoExpectations token_set(FALSE, TokenElevationTypeDefault);
+
+ EXPECT_CALL(token_mock, GetVersion())
+ .WillOnce(Return(base::win::VERSION_WIN7));
+ EXPECT_CALL(token_mock, GetTokenInformation(_, TokenElevationType, _, _, _))
+ .WillOnce(WithArgs<2>(
+ Invoke(&token_set, &TokenInfoExpectations::QueryInformation)));
+ ASSERT_HRESULT_FAILED(
+ process_utils_win::IsCurrentProcessUacElevated(&running_admin));
+}
+
+// Test harness' base for telling native 64 processes from WOW64 processes.
+class ProcessCompatibilityCheckTest : public testing::Test {
+ public:
+ ProcessCompatibilityCheckTest() {
+ }
+
+ protected:
+ // The class inherits from the utility under test to permit injecting test
+ // code without risky patching of WinAPI.
+ // This is not 'black box', since I need to presume a bit about the sequence
+ // of calls.
+ class TestingProcessCompatibility : public ProcessCompatibilityCheck {
+ public:
+ static void SetUpSingletonProcess(WORD system_type,
+ bool process_is64,
+ base::win::Version system_version,
+ base::IntegrityLevel process_level) {
+ AssertProcessDescriptionCorrectess(system_type, system_version,
+ process_is64, process_level);
+
+ testing_as_wow_ = !process_is64 &&
+ system_type == PROCESSOR_ARCHITECTURE_AMD64;
+ mocked_system_type_ = system_type;
+ windows_version_ = system_version;
+ PatchState(system_type, testing_as_wow_,
+ system_version > base::win::VERSION_XP, process_level,
+ ProcessCompatibilityCheckTest::SpoofedOpenProcess,
+ ProcessCompatibilityCheckTest::SpoofedCloseHandle,
+ ProcessCompatibilityCheckTest::SpoofIsWow64Process);
+ }
+
+ // Sets tested singleton as simple 32bit process on Win32.
+ static void CreateAs_x86() {
+ SetUpSingletonProcess(PROCESSOR_ARCHITECTURE_INTEL, false,
+ base::win::VERSION_XP, base::INTEGRITY_UNKNOWN);
+ }
+
+ // Sets tested singleton either as a simple 64bit process on x64 or as
+ // 32bit process on WOW64.
+ static void CreateAsFull_Wow64(bool current_is_32) {
+ SetUpSingletonProcess(PROCESSOR_ARCHITECTURE_AMD64, !current_is_32,
+ base::win::VERSION_XP, base::INTEGRITY_UNKNOWN);
+ }
+
+ static void Reset() {
+ ResetState();
+ }
+ };
+
+ class TestingProcessCompatibilityInstantiation
+ : public ProcessCompatibilityCheck {
+ public:
+ TestingProcessCompatibilityInstantiation()
+ : ProcessCompatibilityCheck() {
+ }
+ // New instance of what would have been a singleton (to test constructor
+ // code). Created as if on a 32bit platform.
+ static TestingProcessCompatibilityInstantiation* CreateAs_x86() {
+ return CreateInstance(PROCESSOR_ARCHITECTURE_INTEL, false,
+ base::win::VERSION_XP, base::INTEGRITY_UNKNOWN);
+ }
+
+ // Created as if on a 64bit platform.
+ static TestingProcessCompatibilityInstantiation* CreateAsFull_Wow64(
+ bool current_is_32) {
+ return CreateInstance(PROCESSOR_ARCHITECTURE_AMD64, !current_is_32,
+ base::win::VERSION_XP, base::INTEGRITY_UNKNOWN);
+ }
+
+ // Creates test instance of ProcessCompatibilityCheck to represent a
+ // 'current process' as defined by parameters.
+ static TestingProcessCompatibilityInstantiation* CreateInstance(
+ WORD system_type, bool process_is64,
+ base::win::Version system_version,
+ base::IntegrityLevel process_level) {
+ PatchState(ProcessCompatibilityCheckTest::SpoofedOpenProcess,
+ ProcessCompatibilityCheckTest::SpoofedCloseHandle,
+ ProcessCompatibilityCheckTest::SpoofIsWow64Process);
+ StrictMock<testing::MockKernel32> kernel_calls;
+ StrictMock<MockProcessTokenAccess> token_mock;
+
+ AssertProcessDescriptionCorrectess(system_type, system_version,
+ process_is64, process_level);
+ testing_as_wow_ = !process_is64 &&
+ system_type == PROCESSOR_ARCHITECTURE_AMD64;
+ mocked_system_type_ = system_type;
+ windows_version_ = system_version;
+
+ EXPECT_CALL(token_mock, GetVersion()).WillOnce(Return(system_version));
+ if (system_type == PROCESSOR_ARCHITECTURE_INTEL) {
+ EXPECT_CALL(kernel_calls, GetNativeSystemInfo(_))
+ .WillOnce(Invoke(NativeSystemInfoMockTool::SetEnvironment_x86));
+ } else {
+ EXPECT_CALL(kernel_calls, GetNativeSystemInfo(_))
+ .WillOnce(Invoke(NativeSystemInfoMockTool::SetEnvironment_x64));
+ }
+
+ // If mocked system supports integrity levels, expect a query.
+ if (system_version > base::win::VERSION_XP) {
+ EXPECT_CALL(token_mock, GetProcessIntegrityLevel(_, _)).
+ WillOnce(DoAll(SetArgumentPointee<1>(process_level), Return(true)));
+ }
+
+ return new TestingProcessCompatibilityInstantiation();
+ }
+
+ ~TestingProcessCompatibilityInstantiation() {
+ ResetState();
+ }
+
+ HRESULT IsProcessCompatible(DWORD process_id, bool* is_compatible) {
+ return ProcessCompatibilityCheck::IsProcessCompatible(process_id,
+ is_compatible);
+ }
+ };
+
+ struct MockProcessRepresentation {
+ MockProcessRepresentation()
+ : process_is_64bit(false),
+ process_integrity_level(base::INTEGRITY_UNKNOWN),
+ compatibility_check_expectation(false) {
+ }
+
+ MockProcessRepresentation(bool is_64, base::IntegrityLevel integrity,
+ bool expect_compatible)
+ : process_is_64bit(is_64), process_integrity_level(integrity),
+ compatibility_check_expectation(expect_compatible) {
+ }
+
+ bool process_is_64bit;
+ base::IntegrityLevel process_integrity_level;
+ bool compatibility_check_expectation;
+ };
+
+ virtual void SetUp() {
+ mock_processes_.clear();
+ back_to_pid_map_.clear();
+ testing_as_wow_ = false;
+ mocked_system_type_ = PROCESSOR_ARCHITECTURE_INTEL;
+ windows_version_ = base::win::VERSION_XP;
+ next_proc_handle_ = 1;
+ }
+
+ virtual void TearDown() {
+ ASSERT_TRUE(back_to_pid_map_.empty());
+ TestingProcessCompatibility::Reset();
+ }
+
+ // Own version of IsWow64Process. Parameter names and types to match
+ // kernel32 function's signature.
+ static BOOL WINAPI SpoofIsWow64Process(HANDLE hProcess, PBOOL Wow64Process) {
+ if (::GetCurrentProcess() == hProcess) {
+ *Wow64Process = testing_as_wow_;
+ return TRUE;
+ }
+
+ HandleToIdMap::const_iterator it_pid =
+ back_to_pid_map_.find(hProcess);
+
+ if (it_pid != back_to_pid_map_.end()) {
+ ProcessIdMap::const_iterator found_it =
+ mock_processes_.find(it_pid->second);
+ *Wow64Process = found_it != mock_processes_.end() &&
+ !found_it->second.process_is_64bit;
+ return TRUE;
+ } else {
+ LOG(ERROR) << "Failed to recognize test handle." << hProcess
+ << " A broken test!";
+ return FALSE;
+ }
+ }
+
+ // Own version of OpenProcess. Creates my own fake handle and counts issued
+ // handles. Parameter names and types to match.
+ static HANDLE WINAPI SpoofedOpenProcess(DWORD dwDesiredAccess,
+ BOOL bInheritHandle,
+ DWORD dwProcessId) {
+ DCHECK((dwDesiredAccess & PROCESS_QUERY_INFORMATION) ==
+ PROCESS_QUERY_INFORMATION);
+ HANDLE fake_handle = reinterpret_cast<HANDLE>(next_proc_handle_);
+ back_to_pid_map_[fake_handle] = dwProcessId;
+ next_proc_handle_++;
+
+ return fake_handle;
+ }
+
+ // Own version of CloseHandle. Must match process, counts my handles.
+ static BOOL WINAPI SpoofedCloseHandle(HANDLE hObject) {
+ return back_to_pid_map_.erase(hObject) > 0;
+ }
+
+ // Own version of GetProcessIntegrityLevel from base:, which in turn
+ // invokes queries on process tokens.
+ static bool MockProcessIntegrityLevel(HANDLE process,
+ base::IntegrityLevel* level) {
+ HandleToIdMap::const_iterator it_pid = back_to_pid_map_.find(process);
+ if (it_pid != back_to_pid_map_.end()) {
+ ProcessIdMap::const_iterator it_process =
+ mock_processes_.find(it_pid->second);
+ if (it_process != mock_processes_.end()) {
+ *level = it_process->second.process_integrity_level;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ static void MakeMockProcess(DWORD pid, bool process_64bit,
+ base::IntegrityLevel integrity,
+ bool check_will_succeeed) {
+ DCHECK(mock_processes_.find(pid) == mock_processes_.end());
+ DCHECK(pid < kWindowCounterBase);
+ AssertProcessDescriptionCorrectess(mocked_system_type_, windows_version_,
+ process_64bit, integrity);
+ mock_processes_[pid] = MockProcessRepresentation(process_64bit, integrity,
+ check_will_succeeed);
+ }
+
+ static void PerformCompatibilityCheck(
+ TestingProcessCompatibilityInstantiation* checker) {
+ StrictMock<MockProcessTokenAccess> mock_token;
+ StrictMock<testing::MockUser32> mock_user32;
+
+ if (windows_version_ > base::win::VERSION_XP) {
+ EXPECT_CALL(mock_token, GetProcessIntegrityLevel(_, _)).
+ WillRepeatedly(Invoke(
+ ProcessCompatibilityCheckTest::MockProcessIntegrityLevel));
+ }
+
+ if (checker != NULL) {
+ for (ProcessIdMap::const_iterator it = mock_processes_.begin();
+ it != mock_processes_.end(); ++it) {
+ bool process_compatible = false;
+ ASSERT_HRESULT_SUCCEEDED(checker->IsProcessCompatible(it->first,
+ &process_compatible));
+ EXPECT_EQ(process_compatible,
+ it->second.compatibility_check_expectation);
+ }
+ } else {
+ EXPECT_CALL(mock_user32, GetWindowThreadProcessId(_, _))
+ .WillRepeatedly(Invoke(
+ ProcessCompatibilityCheckTest::MockThreadProcessFromWindow));
+ for (ProcessIdMap::const_iterator it = mock_processes_.begin();
+ it != mock_processes_.end(); ++it) {
+ bool window_compatible = false;
+ bool process_compatible = false;
+
+ ASSERT_HRESULT_SUCCEEDED(
+ ProcessCompatibilityCheck::IsCompatible(it->first,
+ &process_compatible));
+ ASSERT_HRESULT_SUCCEEDED(
+ ProcessCompatibilityCheck::IsCompatible(
+ reinterpret_cast<HWND>(it->first + kWindowCounterBase),
+ &window_compatible));
+ EXPECT_EQ(window_compatible, process_compatible);
+ EXPECT_EQ(process_compatible,
+ it->second.compatibility_check_expectation);
+ }
+ }
+ }
+
+ static DWORD MockThreadProcessFromWindow(HWND window, DWORD* process_id) {
+ int process_from_window = reinterpret_cast<int>(window) -
+ kWindowCounterBase;
+ DCHECK(process_from_window >= 0 &&
+ process_from_window < kWindowCounterBase);
+ *process_id = static_cast<DWORD>(process_from_window);
+ return 42;
+ }
+
+ typedef std::map<DWORD, MockProcessRepresentation> ProcessIdMap;
+ typedef std::map<HANDLE, DWORD> HandleToIdMap;
+
+ static ProcessIdMap mock_processes_;
+ static HandleToIdMap back_to_pid_map_;
+ static unsigned int next_proc_handle_;
+
+ static bool testing_as_wow_;
+ static WORD mocked_system_type_;
+ static base::win::Version windows_version_;
+
+ static const int kWindowCounterBase = 1000;
+ private:
+ // Asserts that variables defining a process are consistent with each other.
+ // In other words: can process defined by two latter parameters exist on the
+ // platform defined by two former.
+ static void AssertProcessDescriptionCorrectess(
+ WORD system_type, base::win::Version windows_version,
+ bool current_process_is64, base::IntegrityLevel process_integrity) {
+ ASSERT_TRUE(system_type == PROCESSOR_ARCHITECTURE_INTEL ||
+ system_type == PROCESSOR_ARCHITECTURE_AMD64);
+ ASSERT_TRUE(windows_version >= base::win::VERSION_XP);
+
+ ASSERT_TRUE(system_type == PROCESSOR_ARCHITECTURE_AMD64 ||
+ !current_process_is64); // No 64 bit processes on win32.
+
+ ASSERT_TRUE(windows_version > base::win::VERSION_XP ||
+ process_integrity == base::INTEGRITY_UNKNOWN);
+ ASSERT_TRUE(windows_version == base::win::VERSION_XP ||
+ process_integrity >= base::LOW_INTEGRITY);
+ }
+};
+
+ProcessCompatibilityCheckTest::ProcessIdMap
+ ProcessCompatibilityCheckTest::mock_processes_;
+ProcessCompatibilityCheckTest::HandleToIdMap
+ ProcessCompatibilityCheckTest::back_to_pid_map_;
+
+bool ProcessCompatibilityCheckTest::testing_as_wow_ = false;
+unsigned int ProcessCompatibilityCheckTest::next_proc_handle_ = 0;
+WORD ProcessCompatibilityCheckTest::mocked_system_type_ =
+ PROCESSOR_ARCHITECTURE_INTEL;
+base::win::Version ProcessCompatibilityCheckTest::windows_version_ =
+ base::win::VERSION_XP;
+
+TEST_F(ProcessCompatibilityCheckTest, AllIsGoodOnx86) {
+ TestingProcessCompatibility::CreateAs_x86();
+
+ bool all_is_good = false;
+ ASSERT_HRESULT_SUCCEEDED(
+ ProcessCompatibilityCheck::IsCompatible(42, &all_is_good));
+ ASSERT_TRUE(all_is_good);
+}
+
+TEST_F(ProcessCompatibilityCheckTest, Only32PassSieveWithWow) {
+ TestingProcessCompatibility::CreateAsFull_Wow64(true);
+
+ const int wow_pool = 10;
+ const int total_count = 15;
+
+ for (int i = 1; i < total_count; ++i)
+ MakeMockProcess(i, i >= wow_pool, base::INTEGRITY_UNKNOWN, i < wow_pool);
+ PerformCompatibilityCheck(NULL);
+}
+
+TEST_F(ProcessCompatibilityCheckTest, Only64PassSieveWithNoWow) {
+ TestingProcessCompatibility::CreateAsFull_Wow64(false);
+
+ const int wow_pool = 10;
+ const int total_count = 15;
+
+ for (int i = 1; i < total_count; ++i)
+ MakeMockProcess(i, i >= wow_pool, base::INTEGRITY_UNKNOWN, i >= wow_pool);
+ PerformCompatibilityCheck(NULL);
+}
+
+TEST_F(ProcessCompatibilityCheckTest, AllIsGoodOnx86_WithInstantiation) {
+ scoped_ptr<TestingProcessCompatibilityInstantiation>
+ test_instance(TestingProcessCompatibilityInstantiation::CreateAs_x86());
+
+ bool all_is_good = false;
+ ASSERT_HRESULT_SUCCEEDED(
+ test_instance->IsProcessCompatible(42, &all_is_good));
+ ASSERT_TRUE(all_is_good);
+}
+
+TEST_F(ProcessCompatibilityCheckTest, Only32PassSieve_WithInstantiation) {
+ scoped_ptr<TestingProcessCompatibilityInstantiation>
+ test_instance(
+ TestingProcessCompatibilityInstantiation::CreateAsFull_Wow64(true));
+ const int wow_pool = 10;
+ const int total_count = 15;
+
+ for (int i = 1; i < total_count; ++i)
+ MakeMockProcess(i, i >= wow_pool, base::INTEGRITY_UNKNOWN, i < wow_pool);
+ PerformCompatibilityCheck(test_instance.get());
+}
+
+TEST_F(ProcessCompatibilityCheckTest, Only64PassSieve_WithInstantiation) {
+ scoped_ptr<TestingProcessCompatibilityInstantiation>
+ test_instance(
+ TestingProcessCompatibilityInstantiation::CreateAsFull_Wow64(false));
+
+ const int wow_pool = 10;
+ const int total_count = 15;
+
+ for (int i = 1; i < total_count; ++i)
+ MakeMockProcess(i, i >= wow_pool, base::INTEGRITY_UNKNOWN, i >= wow_pool);
+ PerformCompatibilityCheck(test_instance.get());
+}
+
+TEST_F(ProcessCompatibilityCheckTest, MediumIntegrityFilterOnWin32_Instance) {
+ scoped_ptr<TestingProcessCompatibilityInstantiation>
+ test_instance(
+ TestingProcessCompatibilityInstantiation::CreateInstance(
+ PROCESSOR_ARCHITECTURE_INTEL, false,
+ base::win::VERSION_WIN7, base::MEDIUM_INTEGRITY));
+
+ MakeMockProcess(1, false, base::MEDIUM_INTEGRITY, true);
+ MakeMockProcess(2, false, base::LOW_INTEGRITY, true);
+ MakeMockProcess(3, false, base::HIGH_INTEGRITY, false); // Can't access.
+ MakeMockProcess(4, false, base::MEDIUM_INTEGRITY, true);
+ PerformCompatibilityCheck(test_instance.get());
+}
+
+TEST_F(ProcessCompatibilityCheckTest, HighIntegrityFilterOnWin32_Instance) {
+ scoped_ptr<TestingProcessCompatibilityInstantiation>
+ test_instance(
+ TestingProcessCompatibilityInstantiation::CreateInstance(
+ PROCESSOR_ARCHITECTURE_INTEL, false,
+ base::win::VERSION_WIN7, base::HIGH_INTEGRITY));
+
+ MakeMockProcess(1, false, base::MEDIUM_INTEGRITY, false);
+ MakeMockProcess(2, false, base::LOW_INTEGRITY, false);
+ MakeMockProcess(3, false, base::HIGH_INTEGRITY, true);
+ MakeMockProcess(4, false, base::MEDIUM_INTEGRITY, false);
+ PerformCompatibilityCheck(test_instance.get());
+}
+
+TEST_F(ProcessCompatibilityCheckTest, MediumIntegrityFilterOnWow64_Instance) {
+ scoped_ptr<TestingProcessCompatibilityInstantiation>
+ test_instance(
+ TestingProcessCompatibilityInstantiation::CreateInstance(
+ PROCESSOR_ARCHITECTURE_AMD64, false,
+ base::win::VERSION_WIN7, base::MEDIUM_INTEGRITY));
+
+ MakeMockProcess(1, false, base::MEDIUM_INTEGRITY, true);
+ MakeMockProcess(2, false, base::LOW_INTEGRITY, true);
+ MakeMockProcess(3, false, base::HIGH_INTEGRITY, false);
+ MakeMockProcess(4, false, base::MEDIUM_INTEGRITY, true);
+ MakeMockProcess(5, true, base::MEDIUM_INTEGRITY, false);
+ MakeMockProcess(6, true, base::HIGH_INTEGRITY, false);
+ PerformCompatibilityCheck(test_instance.get());
+}
+
+TEST_F(ProcessCompatibilityCheckTest, MediumIntegrityFilterOnWow64) {
+ TestingProcessCompatibility::SetUpSingletonProcess(
+ PROCESSOR_ARCHITECTURE_AMD64, false,
+ base::win::VERSION_WIN7, base::MEDIUM_INTEGRITY);
+
+ MakeMockProcess(1, false, base::MEDIUM_INTEGRITY, true);
+ MakeMockProcess(2, false, base::LOW_INTEGRITY, true);
+ MakeMockProcess(3, false, base::HIGH_INTEGRITY, false);
+ MakeMockProcess(4, false, base::MEDIUM_INTEGRITY, true);
+ MakeMockProcess(5, true, base::MEDIUM_INTEGRITY, false);
+ MakeMockProcess(6, true, base::HIGH_INTEGRITY, false);
+ PerformCompatibilityCheck(NULL);
+}
+
+} // namespace
diff --git a/ceee/common/sidestep_integration.cc b/ceee/common/sidestep_integration.cc
new file mode 100644
index 0000000..71e3f10
--- /dev/null
+++ b/ceee/common/sidestep_integration.cc
@@ -0,0 +1,20 @@
+// Copyright (c) 2010 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.
+//
+// Integrate SideStep into our project.
+
+#include "base/logging.h"
+#include "ceee/testing/sidestep/integration.h"
+
+namespace sidestep {
+
+void AssertImpl(bool assertion_is_true, const char* message) {
+ DCHECK(assertion_is_true) << message;
+}
+
+void LogImpl(const char* message) {
+ DLOG(INFO) << message;
+}
+
+} // namespace sidestep
diff --git a/ceee/common/sidestep_unittest.cc b/ceee/common/sidestep_unittest.cc
new file mode 100644
index 0000000..86832b1
--- /dev/null
+++ b/ceee/common/sidestep_unittest.cc
@@ -0,0 +1,12 @@
+// Copyright (c) 2010 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.
+//
+// Unit test for SideStep.
+
+#include "ceee/testing/sidestep/integration.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+TEST(SideStep, AllTests) {
+ EXPECT_TRUE(sidestep::UnitTests());
+}
diff --git a/ceee/common/window_utils.cc b/ceee/common/window_utils.cc
new file mode 100644
index 0000000..059c6ec
--- /dev/null
+++ b/ceee/common/window_utils.cc
@@ -0,0 +1,142 @@
+// Copyright (c) 2010 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.
+//
+// Utilities for windows (as in HWND).
+
+#include "ceee/common/window_utils.h"
+
+#include "base/logging.h"
+#include "ceee/common/com_utils.h"
+#include "ceee/common/process_utils_win.h"
+
+namespace {
+
+static bool CanAccessWindowProcess(HWND window) {
+ bool can_access = false;
+
+ return SUCCEEDED(process_utils_win::ProcessCompatibilityCheck::IsCompatible(
+ window, &can_access)) && can_access;
+}
+
+} // namespace
+
+namespace window_utils {
+
+bool IsWindowClass(HWND window, const std::wstring& window_class) {
+ if (!::IsWindow(window)) {
+ // No DCHECK, we may get her while a window is being destroyed.
+ return false;
+ }
+
+ wchar_t class_name[MAX_PATH] = { 0 };
+ int chars_copied = ::GetClassName(window, class_name, arraysize(class_name));
+ LOG_IF(ERROR, chars_copied == 0) << "GetClassName, err=" << ::GetLastError();
+
+ return (window_class == class_name);
+}
+
+HWND GetTopLevelParent(HWND window) {
+ HWND top_level = window;
+ while (HWND parent = ::GetParent(top_level)) {
+ top_level = parent;
+ }
+ return top_level;
+}
+
+bool IsWindowThread(HWND window) {
+ DWORD process_id = 0;
+ return ::GetCurrentThreadId() == ::GetWindowThreadProcessId(window,
+ &process_id) && ::GetCurrentProcessId() == process_id;
+}
+
+bool WindowHasNoThread(HWND window) {
+ return ::GetWindowThreadProcessId(window, NULL) == 0;
+}
+
+// Changes made to this struct must also be made to the unittest code.
+struct FindDescendentData {
+ FindDescendentData(const std::wstring& window_class, bool only_visible,
+ bool check_compatibility)
+ : window_class(window_class), only_visible(only_visible),
+ check_process_access(check_compatibility), descendent(NULL) {
+ }
+ const std::wstring& window_class;
+ bool only_visible;
+ HWND descendent;
+ bool check_process_access;
+};
+
+BOOL CALLBACK HandleDescendentWindowProc(HWND window, LPARAM param) {
+ DCHECK(window && param);
+ FindDescendentData* data = reinterpret_cast<FindDescendentData*>(param);
+ if (data && IsWindowClass(window, data->window_class) &&
+ (!data->only_visible || IsWindowVisible(window)) &&
+ (!data->check_process_access || CanAccessWindowProcess(window))) {
+ data->descendent = window;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool FindDescendentWindowEx(
+ HWND ancestor, const std::wstring& window_class,
+ bool only_visible, bool only_accessible, HWND* descendent) {
+ FindDescendentData data(window_class, only_visible, only_accessible);
+ ::EnumChildWindows(ancestor, HandleDescendentWindowProc,
+ reinterpret_cast<LPARAM>(&data));
+ if (data.descendent != NULL) {
+ if (descendent != NULL)
+ *descendent = data.descendent;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool FindDescendentWindow(
+ HWND ancestor, const std::wstring& window_class, bool only_visible,
+ HWND* descendent) {
+ return FindDescendentWindowEx(ancestor, window_class,
+ only_visible, true, descendent);
+}
+
+struct FindTopLevelWindowsData {
+ FindTopLevelWindowsData(const std::wstring& window_class,
+ bool check_compatibility,
+ std::set<HWND>* windows)
+ : window_class(window_class),
+ windows(windows),
+ check_process_access(check_compatibility) {
+ }
+ const std::wstring& window_class;
+ std::set<HWND>* windows;
+ bool check_process_access;
+};
+
+BOOL CALLBACK HandleChildWindowProc(HWND window, LPARAM param) {
+ DCHECK(window && param);
+ FindTopLevelWindowsData* data =
+ reinterpret_cast<FindTopLevelWindowsData*>(param);
+ if (data != NULL && (data->window_class.empty() ||
+ IsWindowClass(window, data->window_class)) &&
+ (!data->check_process_access || CanAccessWindowProcess(window))) {
+ DCHECK(data->windows != NULL);
+ data->windows->insert(window);
+ }
+ return TRUE;
+}
+
+void FindTopLevelWindowsEx(const std::wstring& window_class, bool only_visible,
+ std::set<HWND>* top_level_windows) {
+ FindTopLevelWindowsData find_data(window_class, only_visible,
+ top_level_windows);
+ ::EnumWindows(HandleChildWindowProc, reinterpret_cast<LPARAM>(&find_data));
+}
+
+void FindTopLevelWindows(const std::wstring& window_class,
+ std::set<HWND>* top_level_windows) {
+ FindTopLevelWindowsEx(window_class, true, top_level_windows);
+}
+
+} // namespace window_utils
diff --git a/ceee/common/window_utils.h b/ceee/common/window_utils.h
new file mode 100644
index 0000000..f77bbed
--- /dev/null
+++ b/ceee/common/window_utils.h
@@ -0,0 +1,76 @@
+// Copyright (c) 2010 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.
+//
+// Utilities for windows (as in HWND).
+
+#ifndef CEEE_COMMON_WINDOW_UTILS_H_
+#define CEEE_COMMON_WINDOW_UTILS_H_
+
+#include <windows.h>
+
+#include <set>
+#include <string>
+
+namespace window_utils {
+
+// Returns true if the given window is of the specified window class.
+bool IsWindowClass(HWND window, const std::wstring& window_class);
+
+// Returns the top-level window that is an ancestor of the provided window.
+// Note that this will return the window passed in if it has no parent, or if
+// it is not a window.
+HWND GetTopLevelParent(HWND window);
+
+// Returns true if we are currently running in the given HWND's thread.
+bool IsWindowThread(HWND window);
+
+// Returns true if <p> window is not associated to any thread anymore.
+bool WindowHasNoThread(HWND window);
+
+// Returns true if we could find a descendent of the ancestor window with the
+// specified window class, and visible if @p only_visible is true.
+// Also returns the HWND of the descendent in @p descendent.
+//
+// @param ancestor The ancestor window to parse the descendent hierarchy of.
+// As documented in EnumChildWindows, if ancestor is NULL, we only check
+// top level windows.
+// @param window_class The window class we are looking for.
+// @param only_visible Specify whether we only want a visible descendent
+// (if true) or don't care about the visibility (if false).
+// @param only_accessible Specify if windows associated with processes not
+// accessible from the current process are to be filtered out. Particularly
+// relevant if ancestor == NULL.
+// @param descendent Where to return the handle of the descendent window,
+// or NULL if you don't need the handle.
+bool FindDescendentWindowEx(HWND ancestor,
+ const std::wstring& window_class,
+ bool only_visible,
+ bool only_accessible,
+ HWND* descendent);
+
+// Abbreviated version of the above, provided for backward compatibility.
+// Assumes only_accessible == true.
+bool FindDescendentWindow(HWND ancestor,
+ const std::wstring& window_class,
+ bool only_visible,
+ HWND* descendent);
+
+
+// Finds all the top level windows. The @p top_level_windows are filtered
+// by the provided @p window_class name if it's not empty. An empty
+// @p window_class name means that we are looking for all children.
+// @p only_visible means that windows associated with processes not accessible
+// from the current process are to be filtered out.
+void FindTopLevelWindowsEx(const std::wstring& window_class,
+ bool only_visible,
+ std::set<HWND>* top_level_windows);
+
+// Abbreviated version of the above, provided for backward compatibility.
+// Assumes only_visible == true.
+void FindTopLevelWindows(const std::wstring& window_class,
+ std::set<HWND>* top_level_windows);
+
+} // namespace window_utils
+
+#endif // CEEE_COMMON_WINDOW_UTILS_H_
diff --git a/ceee/common/window_utils_unittest.cc b/ceee/common/window_utils_unittest.cc
new file mode 100644
index 0000000..a1902ee
--- /dev/null
+++ b/ceee/common/window_utils_unittest.cc
@@ -0,0 +1,417 @@
+// Copyright (c) 2010 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.
+//
+// Tests for utilities for windows (as in HWND).
+
+#include "ceee/common/window_utils.h"
+
+#include "base/process_util.h"
+#include "ceee/common/process_utils_win.h"
+#include "ceee/testing/utils/mock_win32.h"
+#include "ceee/testing/utils/test_utils.h"
+#include "ceee/common/windows_constants.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+using testing::_;
+using testing::DoAll;
+using testing::Invoke;
+using testing::NotNull;
+using testing::Return;
+using testing::SetArgumentPointee;
+using testing::StrictMock;
+using testing::WithArgs;
+
+TEST(WindowUtils, IsWindowClas) {
+ HWND window = ::GetDesktopWindow();
+ ASSERT_TRUE(window);
+ EXPECT_TRUE(window_utils::IsWindowClass(window,
+ windows::kDesktopWindowClass));
+ EXPECT_FALSE(window_utils::IsWindowClass(
+ window, std::wstring(windows::kDesktopWindowClass) + L"stop"));
+}
+
+TEST(WindowUtils, IsWindowClassNotWindow) {
+ testing::LogDisabler no_dcheck;
+ ASSERT_FALSE(window_utils::IsWindowClass(NULL, L"Foofoo"));
+}
+
+TEST(WindowUtils, GetTopLevelParent) {
+ // Identity
+ HWND desktop_window = ::GetDesktopWindow();
+ ASSERT_EQ(desktop_window, window_utils::GetTopLevelParent(desktop_window));
+
+ // Child of a child
+ HWND tray_window = ::FindWindowEx(NULL, NULL,
+ windows::kShellTrayWindowClass, NULL);
+ HWND notify_window = ::FindWindowEx(tray_window, NULL,
+ windows::kTrayNotifyWindowClass, NULL);
+ HWND child_of_notify_window = ::FindWindowEx(notify_window, NULL,
+ NULL, NULL);
+ ASSERT_TRUE(tray_window);
+ ASSERT_TRUE(child_of_notify_window);
+ ASSERT_EQ(tray_window,
+ window_utils::GetTopLevelParent(child_of_notify_window));
+
+ // Not a window
+ StrictMock<testing::MockUser32> user32;
+ EXPECT_CALL(user32, GetParent(_)).WillOnce(Return((HWND)NULL));
+ ASSERT_EQ((HWND)0x42, window_utils::GetTopLevelParent((HWND)0x42));
+}
+
+TEST(WindowUtils, IsWindowThread) {
+ DWORD process_id = ::GetCurrentProcessId();
+ DWORD thread_id = ::GetCurrentThreadId();
+
+ // Success.
+ StrictMock<testing::MockUser32> user32;
+ EXPECT_CALL(user32, GetWindowThreadProcessId((HWND)42, NotNull())).
+ WillOnce(DoAll(SetArgumentPointee<1>(process_id), Return(thread_id)));
+ EXPECT_TRUE(window_utils::IsWindowThread((HWND)42));
+
+ // Wrong thread.
+ EXPECT_CALL(user32, GetWindowThreadProcessId((HWND)42, NotNull())).
+ WillOnce(DoAll(SetArgumentPointee<1>(process_id), Return(0)));
+ EXPECT_FALSE(window_utils::IsWindowThread((HWND)42));
+
+ // Wrong process.
+ EXPECT_CALL(user32, GetWindowThreadProcessId((HWND)42, NotNull())).
+ WillOnce(DoAll(SetArgumentPointee<1>(0), Return(thread_id)));
+ EXPECT_FALSE(window_utils::IsWindowThread((HWND)42));
+}
+
+TEST(WindowUtils, WindowHasNoThread) {
+ // Success.
+ StrictMock<testing::MockUser32> user32;
+ EXPECT_CALL(user32, GetWindowThreadProcessId((HWND)42, NULL)).
+ WillOnce(Return(1)); // Any non-zero value will do...
+ EXPECT_FALSE(window_utils::WindowHasNoThread((HWND)42));
+
+ // No thread for window.
+ EXPECT_CALL(user32, GetWindowThreadProcessId((HWND)42, NULL)).
+ WillOnce(Return(0));
+ EXPECT_TRUE(window_utils::WindowHasNoThread((HWND)42));
+}
+
+MOCK_STATIC_CLASS_BEGIN(MockProcessIntegrityQuery)
+ MOCK_STATIC_INIT_BEGIN(MockProcessIntegrityQuery)
+ MOCK_STATIC_INIT2(base::GetProcessIntegrityLevel,
+ GetProcessIntegrityLevel);
+ MOCK_STATIC_INIT_END()
+
+ MOCK_STATIC2(bool, , GetProcessIntegrityLevel, HANDLE,
+ base::IntegrityLevel*);
+MOCK_STATIC_CLASS_END(MockProcessIntegrityQuery)
+
+MOCK_STATIC_CLASS_BEGIN(PartialMockWindowUtils)
+ MOCK_STATIC_INIT_BEGIN(PartialMockWindowUtils)
+ MOCK_STATIC_INIT2(window_utils::IsWindowClass, IsWindowClass);
+ MOCK_STATIC_INIT_END()
+
+ MOCK_STATIC2(bool, , IsWindowClass, HWND, const std::wstring&);
+MOCK_STATIC_CLASS_END(PartialMockWindowUtils)
+
+
+class MockCompatibilityCheck {
+ public:
+ MockCompatibilityCheck(bool on_64_bit,
+ base::IntegrityLevel current_process_integrity) {
+ MockAccess::SetUp(on_64_bit,
+ current_process_integrity != base::INTEGRITY_UNKNOWN,
+ current_process_integrity);
+ mock_integrity_ = new MockProcessIntegrityQuery();
+ EXPECT_CALL(*mock_integrity_, GetProcessIntegrityLevel(_, _)).
+ Times(testing::AnyNumber()).WillRepeatedly(Invoke(
+ MockCompatibilityCheck::MockProcessIntegrityLevel));
+ EXPECT_CALL(mock_user32_, GetWindowThreadProcessId(_, _)).
+ Times(testing::AnyNumber()).WillRepeatedly(Invoke(
+ MockCompatibilityCheck::MockThreadProcessFromWindow));
+ EXPECT_CALL(mock_user32_, IsWindow(_)).Times(0);
+ EXPECT_CALL(mock_user32_, IsWindowVisible(_)).
+ Times(testing::AnyNumber()).WillRepeatedly(Invoke(
+ MockCompatibilityCheck::MockIsWindowVisible));
+ EXPECT_CALL(mock_window_tests_, IsWindowClass(_, _)).
+ Times(testing::AnyNumber()).WillRepeatedly(Return(true));
+ }
+
+ ~MockCompatibilityCheck() {
+ delete mock_integrity_;
+ mock_integrity_ = NULL;
+ for (WindowsMap::iterator it = mock_windows_.begin();
+ it != mock_windows_.end(); ++it) {
+ delete it->second;
+ }
+ mock_windows_.clear();
+ process_id_reference_.clear();
+ process_handle_reference_.clear();
+ MockAccess::TearDown();
+ }
+
+ void AddWindow(HWND fake_window, bool visible,
+ base::IntegrityLevel owner_integrity, bool process_is_64) {
+ ASSERT_TRUE(mock_windows_.find(fake_window) == mock_windows_.end());
+ MockWindowRepresentation * new_window =
+ new MockWindowRepresentation(fake_window, visible, process_is_64,
+ owner_integrity);
+ mock_windows_[fake_window] = new_window;
+ process_id_reference_[new_window->owning_process_id] = new_window;
+ }
+
+ static BOOL MockEnumWindows(WNDENUMPROC enum_proc, LPARAM param) {
+ for (WindowsMap::const_iterator it = mock_windows_.begin();
+ it != mock_windows_.end(); ++it) {
+ if (!(enum_proc(it->first, param)))
+ break;
+ }
+ return TRUE;
+ }
+
+ StrictMock<testing::MockUser32> mock_user32_;
+
+ protected:
+ class MockAccess : public process_utils_win::ProcessCompatibilityCheck {
+ public:
+ static void SetUp(bool on_64_bit, bool integrity_checks,
+ base::IntegrityLevel current_process_integrity) {
+ PatchState(on_64_bit ? PROCESSOR_ARCHITECTURE_AMD64
+ : PROCESSOR_ARCHITECTURE_INTEL,
+ on_64_bit, integrity_checks, current_process_integrity,
+ MockCompatibilityCheck::SpoofOpenProcess,
+ MockCompatibilityCheck::SpoofCloseHandle,
+ MockCompatibilityCheck::SpoofIsWow64Process);
+ }
+
+ static void TearDown() {
+ ResetState();
+ }
+ };
+
+ struct MockWindowRepresentation {
+ MockWindowRepresentation(HWND window_handle, bool window_visible,
+ bool is_64_bit, base::IntegrityLevel integrity) {
+ window = window_handle;
+ visible = window_visible;
+ owning_process_is_64 = is_64_bit;
+ owning_process_integrity = integrity;
+ owning_process_id = reinterpret_cast<int>(window_handle) + 1000;
+ }
+
+ HWND window;
+ bool visible;
+ DWORD owning_process_id;
+ bool owning_process_is_64;
+ base::IntegrityLevel owning_process_integrity;
+ };
+
+ // Own version of IsWow64Process. Parameter names and types to match
+ // kernel32 function's signature.
+ static BOOL WINAPI SpoofIsWow64Process(HANDLE hProcess, PBOOL Wow64Process) {
+ ProcessHandleMap::const_iterator window_it =
+ process_handle_reference_.find(hProcess);
+ if (window_it != process_handle_reference_.end()) {
+ *Wow64Process = !window_it->second->owning_process_is_64;
+ } else {
+ *Wow64Process = TRUE;
+ }
+ return TRUE;
+ }
+
+ // Own version of OpenProcess. Issues handles as required.
+ static HANDLE WINAPI SpoofOpenProcess(DWORD dwDesiredAccess,
+ BOOL bInheritHandle,
+ DWORD dwProcessId) {
+ ProcessIdMap::const_iterator found_window_it =
+ process_id_reference_.find(dwProcessId);
+ if (found_window_it != process_id_reference_.end()) {
+ HANDLE expected_handle_value =
+ reinterpret_cast<HANDLE>(found_window_it->second->window);
+ process_handle_reference_[expected_handle_value] =
+ found_window_it->second;
+ return expected_handle_value;
+ }
+
+ return NULL;
+ }
+
+ static BOOL WINAPI SpoofCloseHandle(HANDLE hObject) {
+ return process_handle_reference_.erase(hObject) == 1;
+ }
+
+ static bool MockProcessIntegrityLevel(HANDLE process,
+ base::IntegrityLevel* level) {
+ ProcessHandleMap::const_iterator window_it =
+ process_handle_reference_.find(process);
+ if (window_it != process_handle_reference_.end()) {
+ *level = window_it->second->owning_process_integrity;
+ return true;
+ }
+ return false;
+ }
+
+ static DWORD MockThreadProcessFromWindow(HWND window, DWORD* process_id) {
+ WindowsMap::const_iterator window_it = mock_windows_.find(window);
+ if (window_it != mock_windows_.end()) {
+ *process_id = window_it->second->owning_process_id;
+ return 42;
+ } else {
+ *process_id = 0;
+ return 0;
+ }
+ }
+
+ static BOOL MockIsWindowVisible(HWND window) {
+ WindowsMap::const_iterator window_it = mock_windows_.find(window);
+ if (window_it != mock_windows_.end())
+ return window_it->second->visible;
+
+ return FALSE;
+ }
+
+ typedef std::map<HWND, MockWindowRepresentation*> WindowsMap;
+ typedef std::map<DWORD, MockWindowRepresentation*> ProcessIdMap;
+ typedef std::map<HANDLE, MockWindowRepresentation*> ProcessHandleMap;
+
+ MockProcessIntegrityQuery* mock_integrity_;
+ PartialMockWindowUtils mock_window_tests_;
+
+ static WindowsMap mock_windows_;
+ static ProcessIdMap process_id_reference_;
+ static ProcessHandleMap process_handle_reference_;
+};
+
+MockCompatibilityCheck::WindowsMap MockCompatibilityCheck::mock_windows_;
+MockCompatibilityCheck::ProcessIdMap
+ MockCompatibilityCheck::process_id_reference_;
+MockCompatibilityCheck::ProcessHandleMap
+ MockCompatibilityCheck::process_handle_reference_;
+
+const HWND kDescendent = reinterpret_cast<HWND>(24);
+struct FindDescendentData {
+ const std::wstring& window_class;
+ bool only_visible;
+ HWND descendent;
+};
+
+BOOL MockEnumChildWindows(HWND ancestor, WNDENUMPROC enum_proc, LPARAM param) {
+ FindDescendentData* data = reinterpret_cast<FindDescendentData*>(param);
+ if (!data->only_visible)
+ data->descendent = kDescendent;
+ return TRUE;
+}
+
+TEST(WindowUtils, FindDescendentWindow) {
+ testing::LogDisabler no_dcheck;
+
+ // Test failure cases.
+ StrictMock<testing::MockUser32> user32;
+ const HWND kAncestor = reinterpret_cast<HWND>(42);
+ HWND descendent = NULL;
+ EXPECT_CALL(user32, EnumChildWindows(kAncestor, _, _)).
+ WillOnce(Return(FALSE));
+ EXPECT_FALSE(window_utils::FindDescendentWindow(kAncestor, L"",
+ false, &descendent));
+ EXPECT_EQ(NULL, descendent);
+
+ // Success.
+ EXPECT_CALL(user32, EnumChildWindows(kAncestor, _, _)).
+ WillRepeatedly(Invoke(MockEnumChildWindows));
+ EXPECT_TRUE(window_utils::FindDescendentWindow(kAncestor, L"",
+ false, &descendent));
+ EXPECT_EQ(kDescendent, descendent);
+
+ // Don't find invisible window if we don't want them.
+ descendent = NULL;
+ EXPECT_FALSE(window_utils::FindDescendentWindow(kAncestor, L"",
+ true, &descendent));
+ EXPECT_EQ(NULL, descendent);
+
+ // Still work even if we are not interested in the window handle.
+ EXPECT_TRUE(window_utils::FindDescendentWindow(kAncestor, L"", false, NULL));
+}
+
+TEST(WindowUtils, FindDescendentWindowWithFilter) {
+ const HWND kAncestor = reinterpret_cast<HWND>(444);
+ HWND descendent = NULL;
+ MockCompatibilityCheck compatibility_check(true, base::MEDIUM_INTEGRITY);
+ compatibility_check.AddWindow(reinterpret_cast<HWND>(38), true,
+ base::HIGH_INTEGRITY, false); // Unreachable.
+ compatibility_check.AddWindow(reinterpret_cast<HWND>(39), true,
+ base::LOW_INTEGRITY, true); // Unreachable.
+ compatibility_check.AddWindow(reinterpret_cast<HWND>(42), true,
+ base::MEDIUM_INTEGRITY, false); // OK
+
+ EXPECT_CALL(compatibility_check.mock_user32_,
+ EnumChildWindows(kAncestor, _, _)).WillRepeatedly(
+ WithArgs<1, 2>(Invoke(MockCompatibilityCheck::MockEnumWindows)));
+
+ EXPECT_TRUE(window_utils::FindDescendentWindow(kAncestor, L"",
+ false, &descendent));
+ EXPECT_EQ(reinterpret_cast<HWND>(42), descendent);
+}
+
+TEST(WindowUtils, FindVisibleDescendentWindowWithFilter) {
+ const HWND kAncestor = reinterpret_cast<HWND>(444);
+ HWND descendent = NULL;
+ MockCompatibilityCheck compatibility_check(true, base::MEDIUM_INTEGRITY);
+ compatibility_check.AddWindow(reinterpret_cast<HWND>(38), false,
+ base::HIGH_INTEGRITY, false); // Unreachable.
+ compatibility_check.AddWindow(reinterpret_cast<HWND>(39), true,
+ base::LOW_INTEGRITY, true); // Unreachable.
+ compatibility_check.AddWindow(reinterpret_cast<HWND>(40), false,
+ base::MEDIUM_INTEGRITY, false); // Invisible.
+ compatibility_check.AddWindow(reinterpret_cast<HWND>(42), true,
+ base::MEDIUM_INTEGRITY, false); // OK
+
+ EXPECT_CALL(compatibility_check.mock_user32_,
+ EnumChildWindows(kAncestor, _, _)).WillRepeatedly(
+ WithArgs<1, 2>(Invoke(MockCompatibilityCheck::MockEnumWindows)));
+
+ EXPECT_TRUE(window_utils::FindDescendentWindow(kAncestor, L"",
+ true, &descendent));
+ EXPECT_EQ(reinterpret_cast<HWND>(42), descendent);
+}
+
+TEST(WindowUtils, FindWindowsWithoutFilter) {
+ std::set<HWND> out_set;
+ MockCompatibilityCheck compatibility_check(true, base::MEDIUM_INTEGRITY);
+ compatibility_check.AddWindow(reinterpret_cast<HWND>(38), true,
+ base::HIGH_INTEGRITY, false); // Unreachable.
+ compatibility_check.AddWindow(reinterpret_cast<HWND>(39), true,
+ base::LOW_INTEGRITY, true); // Unreachable.
+ compatibility_check.AddWindow(reinterpret_cast<HWND>(42), true,
+ base::MEDIUM_INTEGRITY, false); // OK
+
+ EXPECT_CALL(compatibility_check.mock_user32_,
+ EnumWindows(_, _)).WillRepeatedly(Invoke(
+ MockCompatibilityCheck::MockEnumWindows));
+
+ window_utils::FindTopLevelWindowsEx(L"", false, &out_set);
+ EXPECT_EQ(out_set.size(), 3);
+ ASSERT_TRUE(out_set.find(reinterpret_cast<HWND>(38)) != out_set.end());
+ ASSERT_TRUE(out_set.find(reinterpret_cast<HWND>(39)) != out_set.end());
+ ASSERT_TRUE(out_set.find(reinterpret_cast<HWND>(42)) != out_set.end());
+}
+
+TEST(WindowUtils, FindWindowsWithFilter) {
+ std::set<HWND> out_set;
+ MockCompatibilityCheck compatibility_check(true, base::MEDIUM_INTEGRITY);
+ compatibility_check.AddWindow(reinterpret_cast<HWND>(38), true,
+ base::HIGH_INTEGRITY, false); // Unreachable.
+ compatibility_check.AddWindow(reinterpret_cast<HWND>(39), true,
+ base::LOW_INTEGRITY, true); // Unreachable.
+ compatibility_check.AddWindow(reinterpret_cast<HWND>(42), true,
+ base::MEDIUM_INTEGRITY, false); // OK
+
+ EXPECT_CALL(compatibility_check.mock_user32_,
+ EnumWindows(_, _)).WillRepeatedly(Invoke(
+ MockCompatibilityCheck::MockEnumWindows));
+
+ window_utils::FindTopLevelWindows(L"", &out_set);
+ EXPECT_EQ(out_set.size(), 1);
+ ASSERT_TRUE(out_set.find(reinterpret_cast<HWND>(42)) != out_set.end());
+}
+
+
+} // namespace
diff --git a/ceee/common/windows_constants.cc b/ceee/common/windows_constants.cc
new file mode 100644
index 0000000..1562272
--- /dev/null
+++ b/ceee/common/windows_constants.cc
@@ -0,0 +1,22 @@
+// Copyright (c) 2010 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.
+//
+// Constants for things in the Windows environment (window class names,
+// process names, etc.)
+
+#include "ceee/common/windows_constants.h"
+
+namespace windows {
+
+const wchar_t kExplorerModuleName[] = L"explorer.exe";
+const wchar_t kDesktopWindowClass[] = L"#32769";
+const wchar_t kIeTabWindowClass[] = L"TabWindowClass";
+const wchar_t kIeFrameWindowClass[] = L"IEFrame";
+const wchar_t kHiddenIeFrameWindowClass[] = L"Internet Explorer_Hidden";
+const wchar_t kIeTabContentParentWindowClass[] = L"Shell DocObject View";
+const wchar_t kIeTabContentWindowClass[] = L"Internet Explorer_Server";
+const wchar_t kShellTrayWindowClass[] = L"Shell_TrayWnd";
+const wchar_t kTrayNotifyWindowClass[] = L"TrayNotifyWnd";
+
+} // namespace windows
diff --git a/ceee/common/windows_constants.h b/ceee/common/windows_constants.h
new file mode 100644
index 0000000..9ddb65b
--- /dev/null
+++ b/ceee/common/windows_constants.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2010 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.
+//
+// Constants for things in the Windows environment (window class names,
+// process names, etc.)
+
+#ifndef CEEE_COMMON_WINDOWS_CONSTANTS_H_
+#define CEEE_COMMON_WINDOWS_CONSTANTS_H_
+
+namespace windows {
+
+// Module names
+
+// Windows Explorer.
+extern const wchar_t kExplorerModuleName[];
+
+// Window class names
+
+// The desktop window.
+extern const wchar_t kDesktopWindowClass[];
+
+// The IE window containing the whole tab.
+extern const wchar_t kIeTabWindowClass[];
+
+// The top-level IE window.
+extern const wchar_t kIeFrameWindowClass[];
+
+// A hidden top-level IE window.
+extern const wchar_t kHiddenIeFrameWindowClass[];
+
+// The parent window of the tab content window.
+extern const wchar_t kIeTabContentParentWindowClass[];
+
+// The IE window containing the tab content.
+extern const wchar_t kIeTabContentWindowClass[];
+
+// The shell's tray window.
+extern const wchar_t kShellTrayWindowClass[];
+
+// The shell's notification window.
+extern const wchar_t kTrayNotifyWindowClass[];
+
+} // namespace windows
+
+#endif // CEEE_COMMON_WINDOWS_CONSTANTS_H_