diff options
author | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-08 14:30:41 +0200 |
---|---|---|
committer | Jocelyn Turcotte <jocelyn.turcotte@digia.com> | 2014-08-12 13:49:54 +0200 |
commit | ab0a50979b9eb4dfa3320eff7e187e41efedf7a9 (patch) | |
tree | 498dfb8a97ff3361a9f7486863a52bb4e26bb898 /chromium/base/win | |
parent | 4ce69f7403811819800e7c5ae1318b2647e778d1 (diff) |
Update Chromium to beta version 37.0.2062.68
Change-Id: I188e3b5aff1bec75566014291b654eb19f5bc8ca
Reviewed-by: Andras Becsi <andras.becsi@digia.com>
Diffstat (limited to 'chromium/base/win')
26 files changed, 699 insertions, 353 deletions
diff --git a/chromium/base/win/OWNERS b/chromium/base/win/OWNERS index 3aae3d6bec8..65ed72168fd 100644 --- a/chromium/base/win/OWNERS +++ b/chromium/base/win/OWNERS @@ -1 +1,2 @@ cpu@chromium.org +rvargas@chromium.org diff --git a/chromium/base/win/dllmain.cc b/chromium/base/win/dllmain.cc index 9d2a6dc76ce..907c7f4034e 100644 --- a/chromium/base/win/dllmain.cc +++ b/chromium/base/win/dllmain.cc @@ -54,9 +54,9 @@ static void NTAPI on_callback(PVOID h, DWORD reason, PVOID reserved); #endif // _WIN64 -// Explicitly depend on tlssup.cc variable to bracket the list of TLS callbacks. -extern "C" PIMAGE_TLS_CALLBACK __xl_a; -extern "C" PIMAGE_TLS_CALLBACK __xl_z; +// Explicitly depend on VC\crt\src\tlssup.c variables +// to bracket the list of TLS callbacks. +extern "C" PIMAGE_TLS_CALLBACK __xl_a, __xl_z; // extern "C" suppresses C++ name mangling so we know the symbol names for the // linker /INCLUDE:symbol pragmas above. diff --git a/chromium/base/win/event_trace_consumer.h b/chromium/base/win/event_trace_consumer.h index c1b42b4fd93..9322e1e9b4f 100644 --- a/chromium/base/win/event_trace_consumer.h +++ b/chromium/base/win/event_trace_consumer.h @@ -16,7 +16,7 @@ namespace base { namespace win { // This class is a base class that makes it easier to consume events -// from realtime or file sessions. Concrete consumers need to sublass +// from realtime or file sessions. Concrete consumers need to subclass // a specialization of this class and override the ProcessEvent and/or // the ProcessBuffer methods to implement the event consumption logic. // Usage might look like: diff --git a/chromium/base/win/event_trace_consumer_unittest.cc b/chromium/base/win/event_trace_consumer_unittest.cc index d238192c4f8..9066a7c8ff0 100644 --- a/chromium/base/win/event_trace_consumer_unittest.cc +++ b/chromium/base/win/event_trace_consumer_unittest.cc @@ -43,10 +43,9 @@ class TestConsumer: public EtwTraceConsumerBase<TestConsumer> { } void ClearQueue() { - EventQueue::const_iterator it(events_.begin()), end(events_.end()); - - for (; it != end; ++it) { - delete [] it->MofData; + for (EventQueue::const_iterator it(events_.begin()), end(events_.end()); + it != end; ++it) { + delete[] it->MofData; } events_.clear(); @@ -56,7 +55,7 @@ class TestConsumer: public EtwTraceConsumerBase<TestConsumer> { events_.push_back(*event); EVENT_TRACE& back = events_.back(); - if (NULL != event->MofData && 0 != event->MofLength) { + if (event->MofData != NULL && event->MofLength != 0) { back.MofData = new char[event->MofLength]; memcpy(back.MofData, event->MofData, event->MofLength); } @@ -94,7 +93,7 @@ class EtwTraceConsumerBaseTest: public testing::Test { } virtual void TearDown() { - // Cleanup any potentially danging sessions. + // Cleanup any potentially dangling sessions. EtwTraceProperties ignore; EtwTraceController::Stop(session_name_.c_str(), &ignore); } @@ -112,14 +111,12 @@ TEST_F(EtwTraceConsumerBaseTest, Initialize) { TEST_F(EtwTraceConsumerBaseTest, OpenRealtimeSucceedsWhenNoSession) { TestConsumer consumer_; - ASSERT_HRESULT_SUCCEEDED( consumer_.OpenRealtimeSession(session_name_.c_str())); } TEST_F(EtwTraceConsumerBaseTest, ConsumerImmediateFailureWhenNoSession) { TestConsumer consumer_; - ASSERT_HRESULT_SUCCEEDED( consumer_.OpenRealtimeSession(session_name_.c_str())); ASSERT_HRESULT_FAILED(consumer_.Consume()); @@ -131,22 +128,18 @@ class EtwTraceConsumerRealtimeTest: public EtwTraceConsumerBaseTest { public: virtual void SetUp() { EtwTraceConsumerBaseTest::SetUp(); - ASSERT_HRESULT_SUCCEEDED( consumer_.OpenRealtimeSession(session_name_.c_str())); } virtual void TearDown() { consumer_.Close(); - EtwTraceConsumerBaseTest::TearDown(); } DWORD ConsumerThread() { ::SetEvent(consumer_ready_.Get()); - - HRESULT hr = consumer_.Consume(); - return hr; + return consumer_.Consume(); } static DWORD WINAPI ConsumerThreadMainProc(void* arg) { @@ -157,12 +150,11 @@ class EtwTraceConsumerRealtimeTest: public EtwTraceConsumerBaseTest { HRESULT StartConsumerThread() { consumer_ready_.Set(::CreateEvent(NULL, TRUE, FALSE, NULL)); EXPECT_TRUE(consumer_ready_ != NULL); - consumer_thread_.Set(::CreateThread(NULL, 0, ConsumerThreadMainProc, - this, 0, NULL)); - if (NULL == consumer_thread_.Get()) + consumer_thread_.Set(::CreateThread(NULL, 0, ConsumerThreadMainProc, this, + 0, NULL)); + if (consumer_thread_.Get() == NULL) return HRESULT_FROM_WIN32(::GetLastError()); - HRESULT hr = S_OK; HANDLE events[] = { consumer_ready_, consumer_thread_ }; DWORD result = ::WaitForMultipleObjects(arraysize(events), events, FALSE, INFINITE); @@ -173,26 +165,21 @@ class EtwTraceConsumerRealtimeTest: public EtwTraceConsumerBaseTest { case WAIT_OBJECT_0 + 1: { // The thread finished. This may race with the event, so check // explicitly for the event here, before concluding there's trouble. - if (WAIT_OBJECT_0 == ::WaitForSingleObject(consumer_ready_, 0)) + if (::WaitForSingleObject(consumer_ready_, 0) == WAIT_OBJECT_0) return S_OK; DWORD exit_code = 0; if (::GetExitCodeThread(consumer_thread_, &exit_code)) return exit_code; - else - return HRESULT_FROM_WIN32(::GetLastError()); - break; + return HRESULT_FROM_WIN32(::GetLastError()); } default: return E_UNEXPECTED; - break; } - - return hr; } // Waits for consumer_ thread to exit, and returns its exit code. HRESULT JoinConsumerThread() { - if (WAIT_OBJECT_0 != ::WaitForSingleObject(consumer_thread_, INFINITE)) + if (::WaitForSingleObject(consumer_thread_, INFINITE) != WAIT_OBJECT_0) return HRESULT_FROM_WIN32(::GetLastError()); DWORD exit_code = 0; @@ -211,10 +198,8 @@ class EtwTraceConsumerRealtimeTest: public EtwTraceConsumerBaseTest { TEST_F(EtwTraceConsumerRealtimeTest, ConsumerReturnsWhenSessionClosed) { EtwTraceController controller; - - HRESULT hr = controller.StartRealtimeSession(session_name_.c_str(), - 100 * 1024); - if (hr == E_ACCESSDENIED) { + if (controller.StartRealtimeSession(session_name_.c_str(), 100 * 1024) == + E_ACCESSDENIED) { VLOG(1) << "You must be an administrator to run this test on Vista"; return; } @@ -224,7 +209,6 @@ TEST_F(EtwTraceConsumerRealtimeTest, ConsumerReturnsWhenSessionClosed) { // Wait around for the consumer_ thread a bit. ASSERT_EQ(WAIT_TIMEOUT, ::WaitForSingleObject(consumer_thread_, 50)); - ASSERT_HRESULT_SUCCEEDED(controller.Stop(NULL)); // The consumer_ returns success on session stop. @@ -234,34 +218,32 @@ TEST_F(EtwTraceConsumerRealtimeTest, ConsumerReturnsWhenSessionClosed) { namespace { // {57E47923-A549-476f-86CA-503D57F59E62} -DEFINE_GUID(kTestEventType, - 0x57e47923, 0xa549, 0x476f, 0x86, 0xca, 0x50, 0x3d, 0x57, 0xf5, 0x9e, 0x62); +DEFINE_GUID( + kTestEventType, + 0x57e47923, 0xa549, 0x476f, 0x86, 0xca, 0x50, 0x3d, 0x57, 0xf5, 0x9e, 0x62); } // namespace TEST_F(EtwTraceConsumerRealtimeTest, ConsumeEvent) { EtwTraceController controller; - HRESULT hr = controller.StartRealtimeSession(session_name_.c_str(), - 100 * 1024); - if (hr == E_ACCESSDENIED) { + if (controller.StartRealtimeSession(session_name_.c_str(), 100 * 1024) == + E_ACCESSDENIED) { VLOG(1) << "You must be an administrator to run this test on Vista"; return; } - ASSERT_HRESULT_SUCCEEDED(controller.EnableProvider(test_provider_, - TRACE_LEVEL_VERBOSE, 0xFFFFFFFF)); + ASSERT_HRESULT_SUCCEEDED(controller.EnableProvider( + test_provider_, TRACE_LEVEL_VERBOSE, 0xFFFFFFFF)); EtwTraceProvider provider(test_provider_); ASSERT_EQ(ERROR_SUCCESS, provider.Register()); // Start the consumer_. ASSERT_HRESULT_SUCCEEDED(StartConsumerThread()); - ASSERT_EQ(0, TestConsumer::events_.size()); EtwMofEvent<1> event(kTestEventType, 1, TRACE_LEVEL_ERROR); EXPECT_EQ(ERROR_SUCCESS, provider.Log(&event.header)); - EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(TestConsumer::sank_event_, INFINITE)); ASSERT_HRESULT_SUCCEEDED(controller.Stop(NULL)); @@ -306,8 +288,8 @@ class EtwTraceConsumerDataTest: public EtwTraceConsumerBaseTest { return hr; // Enable our provider. - EXPECT_HRESULT_SUCCEEDED(controller.EnableProvider(test_provider_, - TRACE_LEVEL_VERBOSE, 0xFFFFFFFF)); + EXPECT_HRESULT_SUCCEEDED(controller.EnableProvider( + test_provider_, TRACE_LEVEL_VERBOSE, 0xFFFFFFFF)); EtwTraceProvider provider(test_provider_); // Then register our provider, means we get a session handle immediately. @@ -374,7 +356,7 @@ TEST_F(EtwTraceConsumerDataTest, RoundTrip) { return; } ASSERT_HRESULT_SUCCEEDED(hr) << "RoundTripEvent failed"; - ASSERT_TRUE(NULL != trace); + ASSERT_TRUE(trace != NULL); ASSERT_EQ(sizeof(kData), trace->MofLength); ASSERT_STREQ(kData, reinterpret_cast<const char*>(trace->MofData)); } diff --git a/chromium/base/win/event_trace_controller.cc b/chromium/base/win/event_trace_controller.cc index 0391fbc3016..9a35a6bd57a 100644 --- a/chromium/base/win/event_trace_controller.cc +++ b/chromium/base/win/event_trace_controller.cc @@ -64,7 +64,7 @@ HRESULT EtwTraceController::Start(const wchar_t* session_name, } HRESULT EtwTraceController::StartFileSession(const wchar_t* session_name, - const wchar_t* logfile_path, bool realtime) { + const wchar_t* logfile_path, bool realtime) { DCHECK(NULL == session_ && session_name_.empty()); EtwTraceProperties prop; diff --git a/chromium/base/win/iat_patch_function.cc b/chromium/base/win/iat_patch_function.cc index a4a89028b87..21c39950cc3 100644 --- a/chromium/base/win/iat_patch_function.cc +++ b/chromium/base/win/iat_patch_function.cc @@ -56,11 +56,23 @@ DWORD ModifyCode(void* old_code, void* new_code, int length) { } // Change the page protection so that we can write. + MEMORY_BASIC_INFORMATION memory_info; DWORD error = NO_ERROR; DWORD old_page_protection = 0; + + if (!VirtualQuery(old_code, &memory_info, sizeof(memory_info))) { + error = GetLastError(); + return error; + } + + DWORD is_executable = (PAGE_EXECUTE | PAGE_EXECUTE_READ | + PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY) & + memory_info.Protect; + if (VirtualProtect(old_code, length, - PAGE_READWRITE, + is_executable ? PAGE_EXECUTE_READWRITE : + PAGE_READWRITE, &old_page_protection)) { // Write the data. @@ -74,7 +86,6 @@ DWORD ModifyCode(void* old_code, void* new_code, int length) { &old_page_protection); } else { error = GetLastError(); - NOTREACHED(); } return error; @@ -274,5 +285,10 @@ DWORD IATPatchFunction::Unpatch() { return error; } +void* IATPatchFunction::original_function() const { + DCHECK(is_patched()); + return original_function_; +} + } // namespace win } // namespace base diff --git a/chromium/base/win/iat_patch_function.h b/chromium/base/win/iat_patch_function.h index 3ae1f3c460a..5026e0eb954 100644 --- a/chromium/base/win/iat_patch_function.h +++ b/chromium/base/win/iat_patch_function.h @@ -57,6 +57,8 @@ class BASE_EXPORT IATPatchFunction { return (NULL != intercept_function_); } + void* original_function() const; + private: HMODULE module_handle_; void* intercept_function_; diff --git a/chromium/base/win/message_window.cc b/chromium/base/win/message_window.cc index 56660740fc1..57fe64c7981 100644 --- a/chromium/base/win/message_window.cc +++ b/chromium/base/win/message_window.cc @@ -52,7 +52,7 @@ MessageWindow::WindowClass::WindowClass() window_class.hIconSm = NULL; atom_ = RegisterClassEx(&window_class); if (atom_ == 0) { - LOG_GETLASTERROR(ERROR) + PLOG(ERROR) << "Failed to register the window class for a message-only window"; } } @@ -108,7 +108,7 @@ bool MessageWindow::DoCreate(const MessageCallback& message_callback, window_ = CreateWindow(MAKEINTATOM(window_class.atom()), window_name, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, window_class.instance(), this); if (!window_) { - LOG_GETLASTERROR(ERROR) << "Failed to create a message-only window"; + PLOG(ERROR) << "Failed to create a message-only window"; return false; } diff --git a/chromium/base/win/metro.cc b/chromium/base/win/metro.cc index c78cc0946ee..62743c799b5 100644 --- a/chromium/base/win/metro.cc +++ b/chromium/base/win/metro.cc @@ -12,10 +12,6 @@ namespace base { namespace win { -namespace { -bool g_should_tsf_aware_required = false; -} - HMODULE GetMetroModule() { const HMODULE kUninitialized = reinterpret_cast<HMODULE>(1); static HMODULE metro_module = kUninitialized; @@ -70,40 +66,6 @@ bool IsProcessImmersive(HANDLE process) { return false; } -bool IsTSFAwareRequired() { - // Although this function is equal to IsMetroProcess at this moment, - // Chrome for Win7 and Vista may support TSF in the future. - return g_should_tsf_aware_required || IsMetroProcess(); -} - -void SetForceToUseTSF() { - g_should_tsf_aware_required = true; - - // Since Windows 8 Metro mode disables CUAS (Cicero Unaware Application - // Support) via ImmDisableLegacyIME API, Chrome must be fully TSF-aware on - // Metro mode. For debugging purposes, explicitly call ImmDisableLegacyIME so - // that one can test TSF functionality even on Windows 8 desktop mode. Note - // that CUAS cannot be disabled on Windows Vista/7 where ImmDisableLegacyIME - // is not available. - typedef BOOL (* ImmDisableLegacyIMEFunc)(); - HMODULE imm32 = ::GetModuleHandleA("imm32.dll"); - if (imm32 == NULL) - return; - - ImmDisableLegacyIMEFunc imm_disable_legacy_ime = - reinterpret_cast<ImmDisableLegacyIMEFunc>( - ::GetProcAddress(imm32, "ImmDisableLegacyIME")); - - if (imm_disable_legacy_ime == NULL) { - // Unsupported API, just do nothing. - return; - } - - if (!imm_disable_legacy_ime()) { - DVLOG(1) << "Failed to disable legacy IME."; - } -} - wchar_t* LocalAllocAndCopyString(const string16& src) { size_t dest_size = (src.length() + 1) * sizeof(wchar_t); wchar_t* dest = reinterpret_cast<wchar_t*>(LocalAlloc(LPTR, dest_size)); diff --git a/chromium/base/win/metro.h b/chromium/base/win/metro.h index b2208fcb49c..5894ef06dae 100644 --- a/chromium/base/win/metro.h +++ b/chromium/base/win/metro.h @@ -76,17 +76,6 @@ BASE_EXPORT bool IsMetroProcess(); // immersive (Metro) process. BASE_EXPORT bool IsProcessImmersive(HANDLE process); -// Returns true if this process is running under Text Services Framework (TSF) -// and browser must be TSF-aware. -BASE_EXPORT bool IsTSFAwareRequired(); - -// Sets browser to use Text Services Framework (TSF) regardless of process -// status. On Windows 8, this function also disables CUAS (Cicero Unaware -// Application Support) to emulate Windows Metro mode in terms of IME -// functionality. This should be beneficial in QA process because on can test -// IME functionality in Windows 8 desktop mode. -BASE_EXPORT void SetForceToUseTSF(); - // Allocates and returns the destination string via the LocalAlloc API after // copying the src to it. BASE_EXPORT wchar_t* LocalAllocAndCopyString(const string16& src); diff --git a/chromium/base/win/object_watcher.cc b/chromium/base/win/object_watcher.cc index 078f5b9fa1c..3bb1cd32c52 100644 --- a/chromium/base/win/object_watcher.cc +++ b/chromium/base/win/object_watcher.cc @@ -43,7 +43,7 @@ bool ObjectWatcher::StartWatching(HANDLE object, Delegate* delegate) { if (!RegisterWaitForSingleObject(&wait_object_, object, DoneWaiting, this, INFINITE, wait_flags)) { - DLOG_GETLASTERROR(FATAL) << "RegisterWaitForSingleObject failed"; + DPLOG(FATAL) << "RegisterWaitForSingleObject failed"; object_ = NULL; wait_object_ = NULL; return false; @@ -65,7 +65,7 @@ bool ObjectWatcher::StopWatching() { // Blocking call to cancel the wait. Any callbacks already in progress will // finish before we return from this call. if (!UnregisterWaitEx(wait_object_, INVALID_HANDLE_VALUE)) { - DLOG_GETLASTERROR(FATAL) << "UnregisterWaitEx failed"; + DPLOG(FATAL) << "UnregisterWaitEx failed"; return false; } diff --git a/chromium/base/win/registry.cc b/chromium/base/win/registry.cc index 8bfe4329711..a6cb9ae89f7 100644 --- a/chromium/base/win/registry.cc +++ b/chromium/base/win/registry.cc @@ -10,8 +10,7 @@ #include "base/logging.h" #include "base/strings/string_util.h" #include "base/threading/thread_restrictions.h" - -#pragma comment(lib, "shlwapi.lib") // for SHDeleteKey +#include "base/win/windows_version.h" namespace base { namespace win { @@ -30,23 +29,29 @@ inline DWORD to_wchar_size(DWORD byte_size) { return (byte_size + sizeof(wchar_t) - 1) / sizeof(wchar_t); } +// Mask to pull WOW64 access flags out of REGSAM access. +const REGSAM kWow64AccessMask = KEY_WOW64_32KEY | KEY_WOW64_64KEY; + } // namespace // RegKey ---------------------------------------------------------------------- RegKey::RegKey() : key_(NULL), - watch_event_(0) { + watch_event_(0), + wow64access_(0) { } RegKey::RegKey(HKEY key) : key_(key), - watch_event_(0) { + watch_event_(0), + wow64access_(0) { } RegKey::RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access) : key_(NULL), - watch_event_(0) { + watch_event_(0), + wow64access_(0) { if (rootkey) { if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK)) Create(rootkey, subkey, access); @@ -54,6 +59,7 @@ RegKey::RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access) Open(rootkey, subkey, access); } else { DCHECK(!subkey); + wow64access_ = access & kWow64AccessMask; } } @@ -69,43 +75,77 @@ LONG RegKey::Create(HKEY rootkey, const wchar_t* subkey, REGSAM access) { LONG RegKey::CreateWithDisposition(HKEY rootkey, const wchar_t* subkey, DWORD* disposition, REGSAM access) { DCHECK(rootkey && subkey && access && disposition); - Close(); - + HKEY subhkey = NULL; LONG result = RegCreateKeyEx(rootkey, subkey, 0, NULL, - REG_OPTION_NON_VOLATILE, access, NULL, &key_, + REG_OPTION_NON_VOLATILE, access, NULL, &subhkey, disposition); + if (result == ERROR_SUCCESS) { + Close(); + key_ = subhkey; + wow64access_ = access & kWow64AccessMask; + } + return result; } LONG RegKey::CreateKey(const wchar_t* name, REGSAM access) { DCHECK(name && access); + // After the application has accessed an alternate registry view using one of + // the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations + // (create, delete, or open) on child registry keys must explicitly use the + // same flag. Otherwise, there can be unexpected behavior. + // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx. + if ((access & kWow64AccessMask) != wow64access_) { + NOTREACHED(); + return ERROR_INVALID_PARAMETER; + } HKEY subkey = NULL; LONG result = RegCreateKeyEx(key_, name, 0, NULL, REG_OPTION_NON_VOLATILE, access, NULL, &subkey, NULL); - Close(); + if (result == ERROR_SUCCESS) { + Close(); + key_ = subkey; + wow64access_ = access & kWow64AccessMask; + } - key_ = subkey; return result; } LONG RegKey::Open(HKEY rootkey, const wchar_t* subkey, REGSAM access) { DCHECK(rootkey && subkey && access); - Close(); + HKEY subhkey = NULL; + + LONG result = RegOpenKeyEx(rootkey, subkey, 0, access, &subhkey); + if (result == ERROR_SUCCESS) { + Close(); + key_ = subhkey; + wow64access_ = access & kWow64AccessMask; + } - LONG result = RegOpenKeyEx(rootkey, subkey, 0, access, &key_); return result; } LONG RegKey::OpenKey(const wchar_t* relative_key_name, REGSAM access) { DCHECK(relative_key_name && access); + // After the application has accessed an alternate registry view using one of + // the [KEY_WOW64_32KEY / KEY_WOW64_64KEY] flags, all subsequent operations + // (create, delete, or open) on child registry keys must explicitly use the + // same flag. Otherwise, there can be unexpected behavior. + // http://msdn.microsoft.com/en-us/library/windows/desktop/aa384129.aspx. + if ((access & kWow64AccessMask) != wow64access_) { + NOTREACHED(); + return ERROR_INVALID_PARAMETER; + } HKEY subkey = NULL; LONG result = RegOpenKeyEx(key_, relative_key_name, 0, access, &subkey); // We have to close the current opened key before replacing it with the new // one. - Close(); - - key_ = subkey; + if (result == ERROR_SUCCESS) { + Close(); + key_ = subkey; + wow64access_ = access & kWow64AccessMask; + } return result; } @@ -114,9 +154,11 @@ void RegKey::Close() { if (key_) { ::RegCloseKey(key_); key_ = NULL; + wow64access_ = 0; } } +// TODO(wfh): Remove this and other unsafe methods. See http://crbug.com/375400 void RegKey::Set(HKEY key) { if (key_ != key) { Close(); @@ -125,6 +167,7 @@ void RegKey::Set(HKEY key) { } HKEY RegKey::Take() { + DCHECK(wow64access_ == 0); StopWatching(); HKEY key = key_; key_ = NULL; @@ -155,8 +198,43 @@ LONG RegKey::GetValueNameAt(int index, std::wstring* name) const { LONG RegKey::DeleteKey(const wchar_t* name) { DCHECK(key_); DCHECK(name); - LONG result = SHDeleteKey(key_, name); - return result; + HKEY subkey = NULL; + + // Verify the key exists before attempting delete to replicate previous + // behavior. + LONG result = + RegOpenKeyEx(key_, name, 0, READ_CONTROL | wow64access_, &subkey); + if (result != ERROR_SUCCESS) + return result; + RegCloseKey(subkey); + + return RegDelRecurse(key_, std::wstring(name), wow64access_); +} + +LONG RegKey::DeleteEmptyKey(const wchar_t* name) { + DCHECK(key_); + DCHECK(name); + + HKEY target_key = NULL; + LONG result = RegOpenKeyEx(key_, name, 0, KEY_READ | wow64access_, + &target_key); + + if (result != ERROR_SUCCESS) + return result; + + DWORD count = 0; + result = RegQueryInfoKey(target_key, NULL, 0, NULL, NULL, NULL, NULL, &count, + NULL, NULL, NULL, NULL); + + RegCloseKey(target_key); + + if (result != ERROR_SUCCESS) + return result; + + if (count == 0) + return RegDeleteKeyExWrapper(key_, name, wow64access_, 0); + + return ERROR_DIR_NOT_EMPTY; } LONG RegKey::DeleteValue(const wchar_t* value_name) { @@ -329,6 +407,83 @@ LONG RegKey::StopWatching() { return result; } +// static +LONG RegKey::RegDeleteKeyExWrapper(HKEY hKey, + const wchar_t* lpSubKey, + REGSAM samDesired, + DWORD Reserved) { + typedef LSTATUS(WINAPI* RegDeleteKeyExPtr)(HKEY, LPCWSTR, REGSAM, DWORD); + + RegDeleteKeyExPtr reg_delete_key_ex_func = + reinterpret_cast<RegDeleteKeyExPtr>( + GetProcAddress(GetModuleHandleA("advapi32.dll"), "RegDeleteKeyExW")); + + if (reg_delete_key_ex_func) + return reg_delete_key_ex_func(hKey, lpSubKey, samDesired, Reserved); + + // Windows XP does not support RegDeleteKeyEx, so fallback to RegDeleteKey. + return RegDeleteKey(hKey, lpSubKey); +} + +// static +LONG RegKey::RegDelRecurse(HKEY root_key, + const std::wstring& name, + REGSAM access) { + // First, see if the key can be deleted without having to recurse. + LONG result = RegDeleteKeyExWrapper(root_key, name.c_str(), access, 0); + if (result == ERROR_SUCCESS) + return result; + + HKEY target_key = NULL; + result = RegOpenKeyEx( + root_key, name.c_str(), 0, KEY_ENUMERATE_SUB_KEYS | access, &target_key); + + if (result == ERROR_FILE_NOT_FOUND) + return ERROR_SUCCESS; + if (result != ERROR_SUCCESS) + return result; + + std::wstring subkey_name(name); + + // Check for an ending slash and add one if it is missing. + if (!name.empty() && subkey_name[name.length() - 1] != L'\\') + subkey_name += L"\\"; + + // Enumerate the keys + result = ERROR_SUCCESS; + const DWORD kMaxKeyNameLength = MAX_PATH; + const size_t base_key_length = subkey_name.length(); + std::wstring key_name; + while (result == ERROR_SUCCESS) { + DWORD key_size = kMaxKeyNameLength; + result = RegEnumKeyEx(target_key, + 0, + WriteInto(&key_name, kMaxKeyNameLength), + &key_size, + NULL, + NULL, + NULL, + NULL); + + if (result != ERROR_SUCCESS) + break; + + key_name.resize(key_size); + subkey_name.resize(base_key_length); + subkey_name += key_name; + + if (RegDelRecurse(root_key, subkey_name, access) != ERROR_SUCCESS) + break; + } + + RegCloseKey(target_key); + + // Try again to delete the key. + result = RegDeleteKeyExWrapper(root_key, name.c_str(), access, 0); + + return result; +} + // RegistryValueIterator ------------------------------------------------------ RegistryValueIterator::RegistryValueIterator(HKEY root_key, diff --git a/chromium/base/win/registry.h b/chromium/base/win/registry.h index f97f4f5a379..af1aee7dce9 100644 --- a/chromium/base/win/registry.h +++ b/chromium/base/win/registry.h @@ -53,11 +53,11 @@ class BASE_EXPORT RegKey { // Transfers ownership away from this object. HKEY Take(); - // Returns false if this key does not have the specified value, of if an error + // Returns false if this key does not have the specified value, or if an error // occurrs while attempting to access it. bool HasValue(const wchar_t* value_name) const; - // Returns the number of values for this key, of 0 if the number cannot be + // Returns the number of values for this key, or 0 if the number cannot be // determined. DWORD GetValueCount() const; @@ -71,6 +71,10 @@ class BASE_EXPORT RegKey { // it. LONG DeleteKey(const wchar_t* name); + // Deletes an empty subkey. If the subkey has subkeys or values then this + // will fail. + LONG DeleteEmptyKey(const wchar_t* name); + // Deletes a single value within the key. LONG DeleteValue(const wchar_t* name); @@ -132,8 +136,20 @@ class BASE_EXPORT RegKey { HKEY Handle() const { return key_; } private: + // Calls RegDeleteKeyEx on supported platforms, alternatively falls back to + // RegDeleteKey. + static LONG RegDeleteKeyExWrapper(HKEY hKey, + const wchar_t* lpSubKey, + REGSAM samDesired, + DWORD Reserved); + + // Recursively deletes a key and all of its subkeys. + static LONG RegDelRecurse(HKEY root_key, + const std::wstring& name, + REGSAM access); HKEY key_; // The registry key being iterated. HANDLE watch_event_; + REGSAM wow64access_; DISALLOW_COPY_AND_ASSIGN(RegKey); }; diff --git a/chromium/base/win/registry_unittest.cc b/chromium/base/win/registry_unittest.cc index 155402a351f..84074b38586 100644 --- a/chromium/base/win/registry_unittest.cc +++ b/chromium/base/win/registry_unittest.cc @@ -9,6 +9,7 @@ #include "base/compiler_specific.h" #include "base/stl_util.h" +#include "base/win/windows_version.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { @@ -16,31 +17,54 @@ namespace win { namespace { -const wchar_t kRootKey[] = L"Base_Registry_Unittest"; - class RegistryTest : public testing::Test { - public: - RegistryTest() {} - protected: +#if defined(_WIN64) + static const REGSAM kNativeViewMask = KEY_WOW64_64KEY; + static const REGSAM kRedirectedViewMask = KEY_WOW64_32KEY; +#else + static const REGSAM kNativeViewMask = KEY_WOW64_32KEY; + static const REGSAM kRedirectedViewMask = KEY_WOW64_64KEY; +#endif // _WIN64 + + RegistryTest() {} virtual void SetUp() OVERRIDE { // Create a temporary key. RegKey key(HKEY_CURRENT_USER, L"", KEY_ALL_ACCESS); key.DeleteKey(kRootKey); ASSERT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_READ)); ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, kRootKey, KEY_READ)); + foo_software_key_ = L"Software\\"; + foo_software_key_ += kRootKey; + foo_software_key_ += L"\\Foo"; } virtual void TearDown() OVERRIDE { // Clean up the temporary key. RegKey key(HKEY_CURRENT_USER, L"", KEY_SET_VALUE); ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey)); + ASSERT_NE(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_READ)); } + static bool IsRedirectorPresent() { +#if defined(_WIN64) + return true; +#else + return OSInfo::GetInstance()->wow64_status() == OSInfo::WOW64_ENABLED; +#endif + } + + const wchar_t* const kRootKey = L"Base_Registry_Unittest"; + std::wstring foo_software_key_; + private: DISALLOW_COPY_AND_ASSIGN(RegistryTest); }; +// static +const REGSAM RegistryTest::kNativeViewMask; +const REGSAM RegistryTest::kRedirectedViewMask; + TEST_F(RegistryTest, ValueTest) { RegKey key; @@ -158,6 +182,173 @@ TEST_F(RegistryTest, TruncatedCharTest) { EXPECT_FALSE(iterator.Valid()); } +TEST_F(RegistryTest, RecursiveDelete) { + RegKey key; + // Create kRootKey->Foo + // \->Bar (TestValue) + // \->Foo (TestValue) + // \->Bar + // \->Foo + // \->Moo + // \->Foo + // and delete kRootKey->Foo + std::wstring foo_key(kRootKey); + foo_key += L"\\Foo"; + ASSERT_EQ(ERROR_SUCCESS, + key.Create(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Bar", KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(L"TestValue", L"TestData")); + ASSERT_EQ(ERROR_SUCCESS, + key.Create(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Moo", KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, + key.Create(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Foo", KEY_WRITE)); + foo_key += L"\\Bar"; + ASSERT_EQ(ERROR_SUCCESS, + key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE)); + foo_key += L"\\Foo"; + ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Foo", KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, key.WriteValue(L"TestValue", L"TestData")); + ASSERT_EQ(ERROR_SUCCESS, + key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ)); + + ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_WRITE)); + ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(L"Bar")); + ASSERT_NE(ERROR_SUCCESS, key.DeleteEmptyKey(L"Foo")); + ASSERT_NE(ERROR_SUCCESS, key.DeleteEmptyKey(L"Foo\\Bar\\Foo")); + ASSERT_NE(ERROR_SUCCESS, key.DeleteEmptyKey(L"Foo\\Bar")); + ASSERT_EQ(ERROR_SUCCESS, key.DeleteEmptyKey(L"Foo\\Foo")); + + ASSERT_EQ(ERROR_SUCCESS, + key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Bar", KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"Foo", KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, + key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L"")); + ASSERT_NE(ERROR_SUCCESS, + key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ)); + + ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L"Foo")); + ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(L"Foo")); + ASSERT_NE(ERROR_SUCCESS, + key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ)); +} + +// This test requires running as an Administrator as it tests redirected +// registry writes to HKLM\Software +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa384253.aspx +// TODO(wfh): flaky test on Vista. See http://crbug.com/377917 +TEST_F(RegistryTest, DISABLED_Wow64RedirectedFromNative) { + if (!IsRedirectorPresent()) + return; + + RegKey key; + + // Test redirected key access from non-redirected. + ASSERT_EQ(ERROR_SUCCESS, + key.Create(HKEY_LOCAL_MACHINE, + foo_software_key_.c_str(), + KEY_WRITE | kRedirectedViewMask)); + ASSERT_NE(ERROR_SUCCESS, + key.Open(HKEY_LOCAL_MACHINE, foo_software_key_.c_str(), KEY_READ)); + ASSERT_NE(ERROR_SUCCESS, + key.Open(HKEY_LOCAL_MACHINE, + foo_software_key_.c_str(), + KEY_READ | kNativeViewMask)); + + // Open the non-redirected view of the parent and try to delete the test key. + ASSERT_EQ(ERROR_SUCCESS, + key.Open(HKEY_LOCAL_MACHINE, L"Software", KEY_SET_VALUE)); + ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey)); + ASSERT_EQ(ERROR_SUCCESS, + key.Open(HKEY_LOCAL_MACHINE, + L"Software", + KEY_SET_VALUE | kNativeViewMask)); + ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey)); + + // Open the redirected view and delete the key created above. + ASSERT_EQ(ERROR_SUCCESS, + key.Open(HKEY_LOCAL_MACHINE, + L"Software", + KEY_SET_VALUE | kRedirectedViewMask)); + ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey)); +} + +// Test for the issue found in http://crbug.com/384587 where OpenKey would call +// Close() and reset wow64_access_ flag to 0 and cause a NOTREACHED to hit on a +// subsequent OpenKey call. +TEST_F(RegistryTest, SameWowFlags) { + RegKey key; + + ASSERT_EQ(ERROR_SUCCESS, + key.Open(HKEY_LOCAL_MACHINE, + L"Software", + KEY_READ | KEY_WOW64_64KEY)); + ASSERT_EQ(ERROR_SUCCESS, + key.OpenKey(L"Microsoft", + KEY_READ | KEY_WOW64_64KEY)); + ASSERT_EQ(ERROR_SUCCESS, + key.OpenKey(L"Windows", + KEY_READ | KEY_WOW64_64KEY)); +} + +// TODO(wfh): flaky test on Vista. See http://crbug.com/377917 +TEST_F(RegistryTest, DISABLED_Wow64NativeFromRedirected) { + if (!IsRedirectorPresent()) + return; + RegKey key; + + // Test non-redirected key access from redirected. + ASSERT_EQ(ERROR_SUCCESS, + key.Create(HKEY_LOCAL_MACHINE, + foo_software_key_.c_str(), + KEY_WRITE | kNativeViewMask)); + ASSERT_EQ(ERROR_SUCCESS, + key.Open(HKEY_LOCAL_MACHINE, foo_software_key_.c_str(), KEY_READ)); + ASSERT_NE(ERROR_SUCCESS, + key.Open(HKEY_LOCAL_MACHINE, + foo_software_key_.c_str(), + KEY_READ | kRedirectedViewMask)); + + // Open the redirected view of the parent and try to delete the test key + // from the non-redirected view. + ASSERT_EQ(ERROR_SUCCESS, + key.Open(HKEY_LOCAL_MACHINE, + L"Software", + KEY_SET_VALUE | kRedirectedViewMask)); + ASSERT_NE(ERROR_SUCCESS, key.DeleteKey(kRootKey)); + + ASSERT_EQ(ERROR_SUCCESS, + key.Open(HKEY_LOCAL_MACHINE, + L"Software", + KEY_SET_VALUE | kNativeViewMask)); + ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(kRootKey)); +} + +TEST_F(RegistryTest, OpenSubKey) { + RegKey key; + ASSERT_EQ(ERROR_SUCCESS, + key.Open(HKEY_CURRENT_USER, + kRootKey, + KEY_READ | KEY_CREATE_SUB_KEY)); + + ASSERT_NE(ERROR_SUCCESS, key.OpenKey(L"foo", KEY_READ)); + ASSERT_EQ(ERROR_SUCCESS, key.CreateKey(L"foo", KEY_READ)); + ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_READ)); + ASSERT_EQ(ERROR_SUCCESS, key.OpenKey(L"foo", KEY_READ)); + + std::wstring foo_key(kRootKey); + foo_key += L"\\Foo"; + ASSERT_EQ(ERROR_SUCCESS, + key.Open(HKEY_CURRENT_USER, foo_key.c_str(), KEY_READ)); + + ASSERT_EQ(ERROR_SUCCESS, key.Open(HKEY_CURRENT_USER, kRootKey, KEY_WRITE)); + ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L"foo")); +} + } // namespace } // namespace win diff --git a/chromium/base/win/scoped_com_initializer.h b/chromium/base/win/scoped_com_initializer.h index 392c351cc7d..92228baa507 100644 --- a/chromium/base/win/scoped_com_initializer.h +++ b/chromium/base/win/scoped_com_initializer.h @@ -16,6 +16,11 @@ namespace win { // Initializes COM in the constructor (STA or MTA), and uninitializes COM in the // destructor. +// +// WARNING: This should only be used once per thread, ideally scoped to a +// similar lifetime as the thread itself. You should not be using this in +// random utility functions that make COM calls -- instead ensure these +// functions are running on a COM-supporting thread! class ScopedCOMInitializer { public: // Enum value provided to initialize the thread as an MTA instead of STA. diff --git a/chromium/base/win/scoped_gdi_object.h b/chromium/base/win/scoped_gdi_object.h index d44310a1598..57b013e2fba 100644 --- a/chromium/base/win/scoped_gdi_object.h +++ b/chromium/base/win/scoped_gdi_object.h @@ -60,7 +60,7 @@ class ScopedGDIObject { // An explicit specialization for HICON because we have to call DestroyIcon() // instead of DeleteObject() for HICON. template<> -void ScopedGDIObject<HICON>::Close() { +void inline ScopedGDIObject<HICON>::Close() { if (object_) DestroyIcon(object_); } diff --git a/chromium/base/win/scoped_handle.h b/chromium/base/win/scoped_handle.h index 0d038e010bd..a85e08d26e5 100644 --- a/chromium/base/win/scoped_handle.h +++ b/chromium/base/win/scoped_handle.h @@ -27,11 +27,9 @@ namespace win { // Generic wrapper for raw handles that takes care of closing handles // automatically. The class interface follows the style of -// the ScopedStdioHandle class with a few additions: +// the ScopedFILE class with one addition: // - IsValid() method can tolerate multiple invalid handle values such as NULL // and INVALID_HANDLE_VALUE (-1) for Win32 handles. -// - Receive() method allows to receive a handle value from a function that -// takes a raw handle pointer only. template <class Traits, class Verifier> class GenericScopedHandle { MOVE_ONLY_TYPE_FOR_CPP_03(GenericScopedHandle, RValue) @@ -168,7 +166,7 @@ class BASE_EXPORT VerifierTraits { DISALLOW_IMPLICIT_CONSTRUCTORS(VerifierTraits); }; -typedef GenericScopedHandle<HandleTraits, VerifierTraits> ScopedHandle; +typedef GenericScopedHandle<HandleTraits, DummyVerifierTraits> ScopedHandle; } // namespace win } // namespace base diff --git a/chromium/base/win/scoped_process_information_unittest.cc b/chromium/base/win/scoped_process_information_unittest.cc index 550076ef9c6..0b720cb1a39 100644 --- a/chromium/base/win/scoped_process_information_unittest.cc +++ b/chromium/base/win/scoped_process_information_unittest.cc @@ -46,8 +46,7 @@ MULTIPROCESS_TEST_MAIN(ReturnNine) { void ScopedProcessInformationTest::DoCreateProcess( const std::string& main_id, PROCESS_INFORMATION* process_handle) { - std::wstring cmd_line = - this->MakeCmdLine(main_id, false).GetCommandLineString(); + std::wstring cmd_line = MakeCmdLine(main_id).GetCommandLineString(); STARTUPINFO startup_info = {}; startup_info.cb = sizeof(startup_info); diff --git a/chromium/base/win/shortcut.cc b/chromium/base/win/shortcut.cc index 57a93dc6524..7dace5988d0 100644 --- a/chromium/base/win/shortcut.cc +++ b/chromium/base/win/shortcut.cc @@ -11,6 +11,7 @@ #include "base/file_util.h" #include "base/threading/thread_restrictions.h" #include "base/win/scoped_comptr.h" +#include "base/win/scoped_propvariant.h" #include "base/win/win_util.h" #include "base/win/windows_version.h" @@ -172,52 +173,138 @@ bool CreateOrUpdateShortcutLink(const FilePath& shortcut_path, return succeeded; } -bool ResolveShortcut(const FilePath& shortcut_path, - FilePath* target_path, - string16* args) { +bool ResolveShortcutProperties(const FilePath& shortcut_path, + uint32 options, + ShortcutProperties* properties) { + DCHECK(options && properties); base::ThreadRestrictions::AssertIOAllowed(); - HRESULT result; + if (options & ~ShortcutProperties::PROPERTIES_ALL) + NOTREACHED() << "Unhandled property is used."; + ScopedComPtr<IShellLink> i_shell_link; // Get pointer to the IShellLink interface. - result = i_shell_link.CreateInstance(CLSID_ShellLink, NULL, - CLSCTX_INPROC_SERVER); - if (FAILED(result)) + if (FAILED(i_shell_link.CreateInstance(CLSID_ShellLink, NULL, + CLSCTX_INPROC_SERVER))) { return false; + } ScopedComPtr<IPersistFile> persist; // Query IShellLink for the IPersistFile interface. - result = persist.QueryFrom(i_shell_link); - if (FAILED(result)) + if (FAILED(persist.QueryFrom(i_shell_link))) return false; // Load the shell link. - result = persist->Load(shortcut_path.value().c_str(), STGM_READ); - if (FAILED(result)) + if (FAILED(persist->Load(shortcut_path.value().c_str(), STGM_READ))) return false; - WCHAR temp[MAX_PATH]; - if (target_path) { + // Reset |properties|. + properties->options = 0; + + wchar_t temp[MAX_PATH]; + if (options & ShortcutProperties::PROPERTIES_TARGET) { // Try to find the target of a shortcut. - result = i_shell_link->Resolve(0, SLR_NO_UI | SLR_NOSEARCH); - if (FAILED(result)) + if (FAILED(i_shell_link->Resolve(0, SLR_NO_UI | SLR_NOSEARCH))) return false; + if (FAILED(i_shell_link->GetPath(temp, MAX_PATH, NULL, SLGP_UNCPRIORITY))) + return false; + properties->set_target(FilePath(temp)); + } - result = i_shell_link->GetPath(temp, MAX_PATH, NULL, SLGP_UNCPRIORITY); - if (FAILED(result)) + if (options & ShortcutProperties::PROPERTIES_WORKING_DIR) { + if (FAILED(i_shell_link->GetWorkingDirectory(temp, MAX_PATH))) return false; + properties->set_working_dir(FilePath(temp)); + } - *target_path = FilePath(temp); + if (options & ShortcutProperties::PROPERTIES_ARGUMENTS) { + if (FAILED(i_shell_link->GetArguments(temp, MAX_PATH))) + return false; + properties->set_arguments(temp); } - if (args) { - result = i_shell_link->GetArguments(temp, MAX_PATH); - if (FAILED(result)) + if (options & ShortcutProperties::PROPERTIES_DESCRIPTION) { + // Note: description length constrained by MAX_PATH. + if (FAILED(i_shell_link->GetDescription(temp, MAX_PATH))) return false; + properties->set_description(temp); + } - *args = string16(temp); + if (options & ShortcutProperties::PROPERTIES_ICON) { + int temp_index; + if (FAILED(i_shell_link->GetIconLocation(temp, MAX_PATH, &temp_index))) + return false; + properties->set_icon(FilePath(temp), temp_index); } + + // Windows 7+ options, avoiding unnecessary work. + if ((options & ShortcutProperties::PROPERTIES_WIN7) && + GetVersion() >= VERSION_WIN7) { + ScopedComPtr<IPropertyStore> property_store; + if (FAILED(property_store.QueryFrom(i_shell_link))) + return false; + + if (options & ShortcutProperties::PROPERTIES_APP_ID) { + ScopedPropVariant pv_app_id; + if (property_store->GetValue(PKEY_AppUserModel_ID, + pv_app_id.Receive()) != S_OK) { + return false; + } + switch (pv_app_id.get().vt) { + case VT_EMPTY: + properties->set_app_id(L""); + break; + case VT_LPWSTR: + properties->set_app_id(pv_app_id.get().pwszVal); + break; + default: + NOTREACHED() << "Unexpected variant type: " << pv_app_id.get().vt; + return false; + } + } + + if (options & ShortcutProperties::PROPERTIES_DUAL_MODE) { + ScopedPropVariant pv_dual_mode; + if (property_store->GetValue(PKEY_AppUserModel_IsDualMode, + pv_dual_mode.Receive()) != S_OK) { + return false; + } + switch (pv_dual_mode.get().vt) { + case VT_EMPTY: + properties->set_dual_mode(false); + break; + case VT_BOOL: + properties->set_dual_mode(pv_dual_mode.get().boolVal == VARIANT_TRUE); + break; + default: + NOTREACHED() << "Unexpected variant type: " << pv_dual_mode.get().vt; + return false; + } + } + } + + return true; +} + +bool ResolveShortcut(const FilePath& shortcut_path, + FilePath* target_path, + string16* args) { + uint32 options = 0; + if (target_path) + options |= ShortcutProperties::PROPERTIES_TARGET; + if (args) + options |= ShortcutProperties::PROPERTIES_ARGUMENTS; + DCHECK(options); + + ShortcutProperties properties; + if (!ResolveShortcutProperties(shortcut_path, options, &properties)) + return false; + + if (target_path) + *target_path = properties.target; + if (args) + *args = properties.arguments; return true; } @@ -237,7 +324,7 @@ bool TaskbarUnpinShortcutLink(const wchar_t* shortcut) { base::ThreadRestrictions::AssertIOAllowed(); // "Unpin from taskbar" is only supported after Win7. - if (base::win::GetVersion() < base::win::VERSION_WIN7) + if (GetVersion() < VERSION_WIN7) return false; int result = reinterpret_cast<int>(ShellExecute(NULL, L"taskbarunpin", diff --git a/chromium/base/win/shortcut.h b/chromium/base/win/shortcut.h index 0f5fb0f686b..13940ed52c4 100644 --- a/chromium/base/win/shortcut.h +++ b/chromium/base/win/shortcut.h @@ -31,13 +31,21 @@ enum ShortcutOperation { // setting |options| as desired. struct ShortcutProperties { enum IndividualProperties { - PROPERTIES_TARGET = 1 << 0, - PROPERTIES_WORKING_DIR = 1 << 1, - PROPERTIES_ARGUMENTS = 1 << 2, - PROPERTIES_DESCRIPTION = 1 << 3, - PROPERTIES_ICON = 1 << 4, - PROPERTIES_APP_ID = 1 << 5, - PROPERTIES_DUAL_MODE = 1 << 6, + PROPERTIES_TARGET = 1U << 0, + PROPERTIES_WORKING_DIR = 1U << 1, + PROPERTIES_ARGUMENTS = 1U << 2, + PROPERTIES_DESCRIPTION = 1U << 3, + PROPERTIES_ICON = 1U << 4, + PROPERTIES_APP_ID = 1U << 5, + PROPERTIES_DUAL_MODE = 1U << 6, + // Be sure to update the values below when adding a new property. + PROPERTIES_BASIC = PROPERTIES_TARGET | + PROPERTIES_WORKING_DIR | + PROPERTIES_ARGUMENTS | + PROPERTIES_DESCRIPTION | + PROPERTIES_ICON, + PROPERTIES_WIN7 = PROPERTIES_APP_ID | PROPERTIES_DUAL_MODE, + PROPERTIES_ALL = PROPERTIES_BASIC | PROPERTIES_WIN7 }; ShortcutProperties() : icon_index(-1), dual_mode(false), options(0U) {} @@ -117,14 +125,25 @@ BASE_EXPORT bool CreateOrUpdateShortcutLink( const ShortcutProperties& properties, ShortcutOperation operation); -// Resolve Windows shortcut (.LNK file) -// This methods tries to resolve a shortcut .LNK file. The path of the shortcut -// to resolve is in |shortcut_path|. If |target_path| is not NULL, the target -// will be resolved and placed in |target_path|. If |args| is not NULL, the -// arguments will be retrieved and placed in |args|. The function returns true -// if all requested fields are found successfully. -// Callers can safely use the same variable for both |shortcut_path| and -// |target_path|. +// Resolves Windows shortcut (.LNK file) +// This methods tries to resolve selected properties of a shortcut .LNK file. +// The path of the shortcut to resolve is in |shortcut_path|. |options| is a bit +// field composed of ShortcutProperties::IndividualProperties, to specify which +// properties to read. It should be non-0. The resulting data are read into +// |properties|, which must not be NULL. The function returns true if all +// requested properties are successfully read. Otherwise some reads have failed +// and intermediate values written to |properties| should be ignored. +BASE_EXPORT bool ResolveShortcutProperties(const FilePath& shortcut_path, + uint32 options, + ShortcutProperties* properties); + +// Resolves Windows shortcut (.LNK file). +// This is a wrapper to ResolveShortcutProperties() to handle the common use +// case of resolving target and arguments. |target_path| and |args| are +// optional output variables that are ignored if NULL (but at least one must be +// non-NULL). The function returns true if all requested fields are found +// successfully. Callers can safely use the same variable for both +// |shortcut_path| and |target_path|. BASE_EXPORT bool ResolveShortcut(const FilePath& shortcut_path, FilePath* target_path, string16* args); diff --git a/chromium/base/win/shortcut_unittest.cc b/chromium/base/win/shortcut_unittest.cc index eaf152eb9dc..7a6f41dae92 100644 --- a/chromium/base/win/shortcut_unittest.cc +++ b/chromium/base/win/shortcut_unittest.cc @@ -12,6 +12,7 @@ #include "base/test/test_file_util.h" #include "base/test/test_shortcut_win.h" #include "base/win/scoped_com_initializer.h" +#include "base/win/windows_version.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { @@ -33,8 +34,7 @@ class ShortcutTest : public testing::Test { // Shortcut 1's properties { const FilePath target_file(temp_dir_.path().Append(L"Target 1.txt")); - file_util::WriteFile(target_file, kFileContents, - arraysize(kFileContents)); + WriteFile(target_file, kFileContents, arraysize(kFileContents)); link_properties_.set_target(target_file); link_properties_.set_working_dir(temp_dir_.path()); @@ -48,11 +48,10 @@ class ShortcutTest : public testing::Test { // Shortcut 2's properties (all different from properties of shortcut 1). { const FilePath target_file_2(temp_dir_.path().Append(L"Target 2.txt")); - file_util::WriteFile(target_file_2, kFileContents2, - arraysize(kFileContents2)); + WriteFile(target_file_2, kFileContents2, arraysize(kFileContents2)); FilePath icon_path_2; - base::CreateTemporaryFileInDir(temp_dir_.path(), &icon_path_2); + CreateTemporaryFileInDir(temp_dir_.path(), &icon_path_2); link_properties_2_.set_target(target_file_2); link_properties_2_.set_working_dir(temp_dir_2_.path()); @@ -80,6 +79,56 @@ class ShortcutTest : public testing::Test { } // namespace +TEST_F(ShortcutTest, CreateAndResolveShortcutProperties) { + uint32 valid_properties = ShortcutProperties::PROPERTIES_BASIC; + if (GetVersion() >= VERSION_WIN7) + valid_properties |= ShortcutProperties::PROPERTIES_WIN7; + + // Test all properties. + FilePath file_1(temp_dir_.path().Append(L"Link1.lnk")); + ASSERT_TRUE(CreateOrUpdateShortcutLink( + file_1, link_properties_, SHORTCUT_CREATE_ALWAYS)); + + ShortcutProperties properties_read_1; + ASSERT_TRUE(ResolveShortcutProperties( + file_1, ShortcutProperties::PROPERTIES_ALL, &properties_read_1)); + EXPECT_EQ(valid_properties, properties_read_1.options); + ValidatePathsAreEqual(link_properties_.target, properties_read_1.target); + ValidatePathsAreEqual(link_properties_.working_dir, + properties_read_1.working_dir); + EXPECT_EQ(link_properties_.arguments, properties_read_1.arguments); + EXPECT_EQ(link_properties_.description, properties_read_1.description); + ValidatePathsAreEqual(link_properties_.icon, properties_read_1.icon); + EXPECT_EQ(link_properties_.icon_index, properties_read_1.icon_index); + if (GetVersion() >= VERSION_WIN7) { + EXPECT_EQ(link_properties_.app_id, properties_read_1.app_id); + EXPECT_EQ(link_properties_.dual_mode, properties_read_1.dual_mode); + } + + // Test simple shortcut with no special properties set. + FilePath file_2(temp_dir_.path().Append(L"Link2.lnk")); + ShortcutProperties only_target_properties; + only_target_properties.set_target(link_properties_.target); + ASSERT_TRUE(CreateOrUpdateShortcutLink( + file_2, only_target_properties, SHORTCUT_CREATE_ALWAYS)); + + ShortcutProperties properties_read_2; + ASSERT_TRUE(ResolveShortcutProperties( + file_2, ShortcutProperties::PROPERTIES_ALL, &properties_read_2)); + EXPECT_EQ(valid_properties, properties_read_2.options); + ValidatePathsAreEqual(only_target_properties.target, + properties_read_2.target); + ValidatePathsAreEqual(FilePath(), properties_read_2.working_dir); + EXPECT_EQ(L"", properties_read_2.arguments); + EXPECT_EQ(L"", properties_read_2.description); + ValidatePathsAreEqual(FilePath(), properties_read_2.icon); + EXPECT_EQ(0, properties_read_2.icon_index); + if (GetVersion() >= VERSION_WIN7) { + EXPECT_EQ(L"", properties_read_2.app_id); + EXPECT_FALSE(properties_read_2.dual_mode); + } +} + TEST_F(ShortcutTest, CreateAndResolveShortcut) { ShortcutProperties only_target_properties; only_target_properties.set_target(link_properties_.target); diff --git a/chromium/base/win/startup_information_unittest.cc b/chromium/base/win/startup_information_unittest.cc index d637ebd68a1..43276c5ee34 100644 --- a/chromium/base/win/startup_information_unittest.cc +++ b/chromium/base/win/startup_information_unittest.cc @@ -62,7 +62,7 @@ TEST_F(StartupInformationTest, InheritStdOut) { sizeof(events[0]))); std::wstring cmd_line = - this->MakeCmdLine("FireInheritedEvents", false).GetCommandLineString(); + MakeCmdLine("FireInheritedEvents").GetCommandLineString(); PROCESS_INFORMATION temp_process_info = {}; ASSERT_TRUE(::CreateProcess(NULL, const_cast<wchar_t*>(cmd_line.c_str()), diff --git a/chromium/base/win/text_services_message_filter.cc b/chromium/base/win/text_services_message_filter.cc deleted file mode 100644 index 7ce233d9fd4..00000000000 --- a/chromium/base/win/text_services_message_filter.cc +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright (c) 2012 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 "base/win/text_services_message_filter.h" - -namespace base { -namespace win { - -TextServicesMessageFilter::TextServicesMessageFilter() - : client_id_(TF_CLIENTID_NULL), - is_initialized_(false) { -} - -TextServicesMessageFilter::~TextServicesMessageFilter() { - if (is_initialized_) - thread_mgr_->Deactivate(); -} - -bool TextServicesMessageFilter::Init() { - if (FAILED(thread_mgr_.CreateInstance(CLSID_TF_ThreadMgr))) - return false; - - if (FAILED(message_pump_.QueryFrom(thread_mgr_))) - return false; - - if (FAILED(keystroke_mgr_.QueryFrom(thread_mgr_))) - return false; - - if (FAILED(thread_mgr_->Activate(&client_id_))) - return false; - - is_initialized_ = true; - return is_initialized_; -} - -// Wraps for ITfMessagePump::PeekMessage with win32 PeekMessage signature. -// Obtains messages from application message queue. -BOOL TextServicesMessageFilter::DoPeekMessage(MSG* msg, - HWND window_handle, - UINT msg_filter_min, - UINT msg_filter_max, - UINT remove_msg) { - BOOL result = FALSE; - if (FAILED(message_pump_->PeekMessage(msg, window_handle, - msg_filter_min, msg_filter_max, - remove_msg, &result))) { - result = FALSE; - } - - return result; -} - -// Sends message to Text Service Manager. -// The message will be used to input composition text. -// Returns true if |message| was consumed by text service manager. -bool TextServicesMessageFilter::ProcessMessage(const MSG& msg) { - if (msg.message == WM_KEYDOWN) { - BOOL eaten = FALSE; - HRESULT hr = keystroke_mgr_->TestKeyDown(msg.wParam, msg.lParam, &eaten); - if (FAILED(hr) && !eaten) - return false; - eaten = FALSE; - hr = keystroke_mgr_->KeyDown(msg.wParam, msg.lParam, &eaten); - return (SUCCEEDED(hr) && !!eaten); - } - - if (msg.message == WM_KEYUP) { - BOOL eaten = FALSE; - HRESULT hr = keystroke_mgr_->TestKeyUp(msg.wParam, msg.lParam, &eaten); - if (FAILED(hr) && !eaten) - return false; - eaten = FALSE; - hr = keystroke_mgr_->KeyUp(msg.wParam, msg.lParam, &eaten); - return (SUCCEEDED(hr) && !!eaten); - } - - return false; -} - -} // namespace win -} // namespace base diff --git a/chromium/base/win/text_services_message_filter.h b/chromium/base/win/text_services_message_filter.h deleted file mode 100644 index 704c1da640b..00000000000 --- a/chromium/base/win/text_services_message_filter.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2012 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 BASE_WIN_TEXT_SERVICES_MESSAGE_FILTER_H_ -#define BASE_WIN_TEXT_SERVICES_MESSAGE_FILTER_H_ - -#include <msctf.h> -#include <Windows.h> - -#include "base/memory/scoped_ptr.h" -#include "base/message_loop/message_pump_win.h" -#include "base/win/metro.h" -#include "base/win/scoped_comptr.h" - -namespace base { -namespace win { - -// TextServicesMessageFilter extends MessageFilter with methods that are using -// Text Services Framework COM component. -class BASE_EXPORT TextServicesMessageFilter - : public base::MessagePumpForUI::MessageFilter { - public: - TextServicesMessageFilter(); - virtual ~TextServicesMessageFilter(); - virtual BOOL DoPeekMessage(MSG* msg, - HWND window_handle, - UINT msg_filter_min, - UINT msg_filter_max, - UINT remove_msg) OVERRIDE; - virtual bool ProcessMessage(const MSG& msg) OVERRIDE; - - bool Init(); - - private: - TfClientId client_id_; - bool is_initialized_; - base::win::ScopedComPtr<ITfThreadMgr> thread_mgr_; - base::win::ScopedComPtr<ITfMessagePump> message_pump_; - base::win::ScopedComPtr<ITfKeystrokeMgr> keystroke_mgr_; - - DISALLOW_COPY_AND_ASSIGN(TextServicesMessageFilter); -}; - -} // namespace win -} // namespace base - -#endif // BASE_WIN_TEXT_SERVICES_MESSAGE_FILTER_H_ diff --git a/chromium/base/win/win_util.cc b/chromium/base/win/win_util.cc index 7b85418a1ea..ee923522fc9 100644 --- a/chromium/base/win/win_util.cc +++ b/chromium/base/win/win_util.cc @@ -6,6 +6,7 @@ #include <aclapi.h> #include <lm.h> +#include <powrprof.h> #include <shellapi.h> #include <shlobj.h> #include <shobjidl.h> // Must be before propkey. @@ -221,14 +222,29 @@ void SetAbortBehaviorForCrashReporting() { signal(SIGABRT, ForceCrashOnSigAbort); } -bool IsTouchEnabledDevice() { - if (base::win::GetVersion() < base::win::VERSION_WIN7) +bool IsTabletDevice() { + if (GetSystemMetrics(SM_MAXIMUMTOUCHES) == 0) return false; - const int kMultiTouch = NID_INTEGRATED_TOUCH | NID_MULTI_INPUT | NID_READY; - int sm = GetSystemMetrics(SM_DIGITIZER); - if ((sm & kMultiTouch) == kMultiTouch) { - return true; - } + + base::win::Version version = base::win::GetVersion(); + if (version == base::win::VERSION_XP) + return (GetSystemMetrics(SM_TABLETPC) != 0); + + // If the device is docked, the user is treating the device as a PC. + if (GetSystemMetrics(SM_SYSTEMDOCKED) != 0) + return false; + + // PlatformRoleSlate was only added in Windows 8, but prior to Win8 it is + // still possible to check for a mobile power profile. + POWER_PLATFORM_ROLE role = PowerDeterminePlatformRole(); + bool mobile_power_profile = (role == PlatformRoleMobile); + bool slate_power_profile = false; + if (version >= base::win::VERSION_WIN8) + slate_power_profile = (role == PlatformRoleSlate); + + if (mobile_power_profile || slate_power_profile) + return (GetSystemMetrics(SM_CONVERTIBLESLATEMODE) == 0); + return false; } @@ -329,41 +345,29 @@ bool DismissVirtualKeyboard() { typedef HWND (*MetroRootWindow) (); -// As of this writing, GetMonitorInfo function seem to return wrong values -// for rcWork.left and rcWork.top in case of split screen situation inside -// metro mode. In order to get required values we query for core window screen -// coordinates. -// TODO(shrikant): Remove detour code once GetMonitorInfo is fixed for 8.1. -BOOL GetMonitorInfoWrapper(HMONITOR monitor, MONITORINFO* mi) { - BOOL ret = ::GetMonitorInfo(monitor, mi); -#if !defined(USE_ASH) - if (base::win::IsMetroProcess() && - base::win::GetVersion() >= base::win::VERSION_WIN8_1) { - static MetroRootWindow root_window = NULL; - if (!root_window) { - HMODULE metro = base::win::GetMetroModule(); - // There are apparently instances when current process is inside metro - // environment but metro driver dll is not loaded. - if (!metro) { - return ret; - } - root_window = reinterpret_cast<MetroRootWindow>( - ::GetProcAddress(metro, "GetRootWindow")); - } - ret = ::GetWindowRect(root_window(), &(mi->rcWork)); - } -#endif - return ret; -} +enum DomainEnrollementState {UNKNOWN = -1, NOT_ENROLLED, ENROLLED}; +static volatile long int g_domain_state = UNKNOWN; bool IsEnrolledToDomain() { - LPWSTR domain; - NETSETUP_JOIN_STATUS join_status; - if(::NetGetJoinInformation(NULL, &domain, &join_status) != NERR_Success) - return false; - ::NetApiBufferFree(domain); + // Doesn't make any sense to retry inside a user session because joining a + // domain will only kick in on a restart. + if (g_domain_state == UNKNOWN) { + LPWSTR domain; + NETSETUP_JOIN_STATUS join_status; + if(::NetGetJoinInformation(NULL, &domain, &join_status) != NERR_Success) + return false; + ::NetApiBufferFree(domain); + ::InterlockedCompareExchange(&g_domain_state, + join_status == ::NetSetupDomainName ? + ENROLLED : NOT_ENROLLED, + UNKNOWN); + } + + return g_domain_state == ENROLLED; +} - return join_status == ::NetSetupDomainName; +void SetDomainStateForTesting(bool state) { + g_domain_state = state ? ENROLLED : NOT_ENROLLED; } } // namespace win diff --git a/chromium/base/win/win_util.h b/chromium/base/win/win_util.h index eebb64ac8ef..573c4c72506 100644 --- a/chromium/base/win/win_util.h +++ b/chromium/base/win/win_util.h @@ -107,9 +107,10 @@ BASE_EXPORT bool ShouldCrashOnProcessDetach(); // process is aborted. BASE_EXPORT void SetAbortBehaviorForCrashReporting(); -// A touch enabled device by this definition is something that has -// integrated multi-touch ready to use and has Windows version > Windows7. -BASE_EXPORT bool IsTouchEnabledDevice(); +// A tablet is a device that is touch enabled and also is being used +// "like a tablet". This is used primarily for metrics in order to gain some +// insight into how users use Chrome. +BASE_EXPORT bool IsTabletDevice(); // Get the size of a struct up to and including the specified member. // This is necessary to set compatible struct sizes for different versions @@ -126,13 +127,13 @@ BASE_EXPORT bool DisplayVirtualKeyboard(); // above. Returns true on success. BASE_EXPORT bool DismissVirtualKeyboard(); -// Returns monitor info after correcting rcWorkArea based on metro version. -// see bug #247430 for more details. -BASE_EXPORT BOOL GetMonitorInfoWrapper(HMONITOR monitor, MONITORINFO* mi); - // Returns true if the machine is enrolled to a domain. BASE_EXPORT bool IsEnrolledToDomain(); +// Used by tests to mock any wanted state. Call with |state| set to true to +// simulate being in a domain and false otherwise. +BASE_EXPORT void SetDomainStateForTesting(bool state); + } // namespace win } // namespace base |