summaryrefslogtreecommitdiffstats
path: root/ceee/common
diff options
context:
space:
mode:
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_