diff --git a/0) b/0) new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/LICENSE.chromium_os b/LICENSE.chromium_os deleted file mode 100644 index 2e71f04e137a9..0000000000000 --- a/LICENSE.chromium_os +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2006-2009 The ChromiumOS Authors -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google LLC nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index 08f1a0445babd..610c30a916a3c 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,2 @@ -# ![Logo](chrome/app/theme/chromium/product_logo_64.png) Chromium - -Chromium is an open-source browser project that aims to build a safer, faster, -and more stable way for all users to experience the web. - -The project's web site is https://www.chromium.org. - -To check out the source code locally, don't use `git clone`! Instead, -follow [the instructions on how to get the code](docs/get_the_code.md). - -Documentation in the source is rooted in [docs/README.md](docs/README.md). - -Learn how to [Get Around the Chromium Source Code Directory Structure -](https://www.chromium.org/developers/how-tos/getting-around-the-chrome-source-code). - -For historical reasons, there are some small top level directories. Now the -guidance is that new top level directories are for product (e.g. Chrome, -Android WebView, Ash). Even if these products have multiple executables, the -code should be in subdirectories of the product. - -If you found a bug, please file it at https://crbug.com/new. +# supermium +A Chromium-based browser for Windows Vista (exkernel), 7 and 8.x. diff --git a/base/allocator/partition_allocator/partition_address_space.cc b/base/allocator/partition_allocator/partition_address_space.cc index fe5ebd51f3af1..398e6a66b6797 100644 --- a/base/allocator/partition_allocator/partition_address_space.cc +++ b/base/allocator/partition_allocator/partition_address_space.cc @@ -43,6 +43,34 @@ namespace { #if BUILDFLAG(IS_WIN) +#if PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) +bool IsLegacyWindowsVersion() { + // Use ::RtlGetVersion instead of ::GetVersionEx or helpers from + // VersionHelpers.h because those alternatives change their behavior depending + // on whether or not the calling executable has a compatibility manifest + // resource. It's better for the allocator to not depend on that to decide the + // pool size. + // Assume legacy if ::RtlGetVersion is not available or it fails. + using RtlGetVersion = LONG(WINAPI*)(OSVERSIONINFOEX*); + const RtlGetVersion rtl_get_version = reinterpret_cast( + ::GetProcAddress(::GetModuleHandle(L"ntdll.dll"), "RtlGetVersion")); + if (!rtl_get_version) { + return true; + } + + OSVERSIONINFOEX version_info = {}; + version_info.dwOSVersionInfoSize = sizeof(version_info); + if (rtl_get_version(&version_info) != ERROR_SUCCESS) { + return true; + } + + // Anything prior to Windows 8.1 is considered legacy for the allocator. + // Windows 8.1 is major 6 with minor 3. + return version_info.dwMajorVersion < 6 || + (version_info.dwMajorVersion == 6 && version_info.dwMinorVersion < 3); +} +#endif // PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) + PA_NOINLINE void HandlePoolAllocFailureOutOfVASpace() { PA_NO_CODE_FOLDING(); PA_CHECK(false); @@ -66,9 +94,9 @@ PA_NOINLINE void HandlePoolAllocFailure() { // it must be VA space exhaustion. HandlePoolAllocFailureOutOfVASpace(); } else if (alloc_page_error_code == ERROR_COMMITMENT_LIMIT) { - // Should not happen, since as of Windows 8.1+, reserving address space - // should not be charged against the commit limit, aside from a very small - // amount per 64kiB block. Keep this path anyway, to check in crash reports. + // On Windows <8.1, MEM_RESERVE increases commit charge to account for + // not-yet-committed PTEs needed to cover that VA space, if it was to be + // committed (see crbug.com/1101421#c16). HandlePoolAllocFailureOutOfCommitCharge(); } else #endif // BUILDFLAG(IS_WIN) @@ -87,10 +115,7 @@ std::ptrdiff_t PartitionAddressSpace::brp_pool_shadow_offset_ = 0; #endif #if PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) -#if !BUILDFLAG(IS_IOS) -#error Dynamic pool size is only supported on iOS. -#endif - +#if BUILDFLAG(IS_IOS) namespace { bool IsIOSTestProcess() { // On iOS, only applications with the extended virtual addressing entitlement @@ -132,6 +157,15 @@ PA_ALWAYS_INLINE size_t PartitionAddressSpace::RegularPoolSize() { PA_ALWAYS_INLINE size_t PartitionAddressSpace::BRPPoolSize() { return IsIOSTestProcess() ? kBRPPoolSizeForIOSTestProcess : kBRPPoolSize; } +#else +PA_ALWAYS_INLINE size_t PartitionAddressSpace::RegularPoolSize() { + return IsLegacyWindowsVersion() ? kRegularPoolSizeForLegacyWindows + : kRegularPoolSize; +} +PA_ALWAYS_INLINE size_t PartitionAddressSpace::BRPPoolSize() { + return IsLegacyWindowsVersion() ? kBRPPoolSizeForLegacyWindows : kBRPPoolSize; +} +#endif // BUILDFLAG(IS_IOS) #endif // PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) void PartitionAddressSpace::Init() { diff --git a/base/allocator/partition_allocator/partition_address_space.h b/base/allocator/partition_allocator/partition_address_space.h index 53c116fb69160..008091fc0732f 100644 --- a/base/allocator/partition_allocator/partition_address_space.h +++ b/base/allocator/partition_allocator/partition_address_space.h @@ -277,6 +277,16 @@ class PA_COMPONENT_EXPORT(PARTITION_ALLOC) PartitionAddressSpace { static constexpr size_t kThreadIsolatedPoolSize = kGiB / 4; static_assert(base::bits::IsPowerOfTwo(kThreadIsolatedPoolSize)); #endif +#if PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) + // We can't afford pool sizes as large as kPoolMaxSize on Windows <8.1 (see + // crbug.com/1101421 and crbug.com/1217759). + static constexpr size_t kRegularPoolSizeForLegacyWindows = 1 * kGiB; + static constexpr size_t kBRPPoolSizeForLegacyWindows = 1 * kGiB; + static_assert(kRegularPoolSizeForLegacyWindows < kRegularPoolSize); + static_assert(kBRPPoolSizeForLegacyWindows < kBRPPoolSize); + static_assert(base::bits::IsPowerOfTwo(kRegularPoolSizeForLegacyWindows)); + static_assert(base::bits::IsPowerOfTwo(kBRPPoolSizeForLegacyWindows)); +#endif // PA_CONFIG(DYNAMICALLY_SELECT_POOL_SIZE) static constexpr size_t kConfigurablePoolMaxSize = kPoolMaxSize; static constexpr size_t kConfigurablePoolMinSize = 1 * kGiB; static_assert(kConfigurablePoolMinSize <= kConfigurablePoolMaxSize); diff --git a/base/allocator/partition_allocator/partition_alloc_base/rand_util_win.cc b/base/allocator/partition_allocator/partition_alloc_base/rand_util_win.cc index c6071fa8b092f..cac35bc52ad53 100644 --- a/base/allocator/partition_allocator/partition_alloc_base/rand_util_win.cc +++ b/base/allocator/partition_allocator/partition_alloc_base/rand_util_win.cc @@ -8,36 +8,32 @@ #include #include +// #define needed to link in RtlGenRandom(), a.k.a. SystemFunction036. See the +// "Community Additions" comment on MSDN here: +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx +#define SystemFunction036 NTAPI SystemFunction036 +#include +#undef SystemFunction036 + #include #include #include "base/allocator/partition_allocator/partition_alloc_base/check.h" -// Prototype for ProcessPrng. -// See: https://learn.microsoft.com/en-us/windows/win32/seccng/processprng -extern "C" { -BOOL WINAPI ProcessPrng(PBYTE pbData, SIZE_T cbData); -} - namespace partition_alloc::internal::base { void RandBytes(void* output, size_t output_length) { - // Import bcryptprimitives directly rather than cryptbase to avoid opening a - // handle to \\Device\KsecDD in the renderer. - // Note: we cannot use a magic static here as PA runs too early in process - // startup, but this should be safe as the process will be single-threaded - // when this first runs. - static decltype(&ProcessPrng) process_prng_fn = nullptr; - if (!process_prng_fn) { - HMODULE hmod = LoadLibraryW(L"bcryptprimitives.dll"); - PA_BASE_CHECK(hmod); - process_prng_fn = reinterpret_cast( - GetProcAddress(hmod, "ProcessPrng")); - PA_BASE_CHECK(process_prng_fn); + char* output_ptr = static_cast(output); + while (output_length > 0) { + const ULONG output_bytes_this_pass = static_cast(std::min( + output_length, static_cast(std::numeric_limits::max()))); + const bool success = + RtlGenRandom(output_ptr, output_bytes_this_pass) != FALSE; + PA_BASE_CHECK(success); + output_length -= output_bytes_this_pass; + output_ptr += output_bytes_this_pass; } - BOOL success = process_prng_fn(static_cast(output), output_length); - // ProcessPrng is documented to always return TRUE. - PA_BASE_CHECK(success); + } } // namespace partition_alloc::internal::base diff --git a/base/allocator/partition_allocator/partition_alloc_config.h b/base/allocator/partition_allocator/partition_alloc_config.h index bb6195f590759..224a3507195c7 100644 --- a/base/allocator/partition_allocator/partition_alloc_config.h +++ b/base/allocator/partition_allocator/partition_alloc_config.h @@ -32,7 +32,7 @@ static_assert(sizeof(void*) == 8, ""); #else static_assert(sizeof(void*) != 8, ""); -#endif // PA_CONFIG(HAS_64_BITS_POINTERS) +#endif // BUILDFLAG(HAS_64_BITS_POINTERS) #if BUILDFLAG(HAS_64_BIT_POINTERS) && \ (defined(__ARM_NEON) || defined(__ARM_NEON__)) && defined(__ARM_FP) @@ -41,18 +41,23 @@ static_assert(sizeof(void*) != 8, ""); #define PA_CONFIG_STARSCAN_NEON_SUPPORTED() 0 #endif -#if BUILDFLAG(HAS_64_BIT_POINTERS) && BUILDFLAG(IS_IOS) +#if BUILDFLAG(HAS_64_BIT_POINTERS) && (BUILDFLAG(IS_IOS) || BUILDFLAG(IS_WIN)) // Allow PA to select an alternate pool size at run-time before initialization, // rather than using a single constexpr value. // // This is needed on iOS because iOS test processes can't handle large pools // (see crbug.com/1250788). // +// This is needed on Windows, because OS versions <8.1 incur commit charge even +// on reserved address space, thus don't handle large pools well (see +// crbug.com/1101421 and crbug.com/1217759). +// // This setting is specific to 64-bit, as 32-bit has a different implementation. #define PA_CONFIG_DYNAMICALLY_SELECT_POOL_SIZE() 1 #else #define PA_CONFIG_DYNAMICALLY_SELECT_POOL_SIZE() 0 -#endif // BUILDFLAG(HAS_64_BIT_POINTERS) && BUILDFLAG(IS_IOS) +#endif // BUILDFLAG(HAS_64_BIT_POINTERS) && (BUILDFLAG(IS_IOS) || + // BUILDFLAG(IS_WIN)) #if BUILDFLAG(HAS_64_BIT_POINTERS) && \ (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID)) diff --git a/base/command_line.cc b/base/command_line.cc index d762b168a9ccf..5bbdb4e86d69a 100644 --- a/base/command_line.cc +++ b/base/command_line.cc @@ -27,8 +27,8 @@ #include #include - #include "base/strings/string_util_win.h" +#include "base/win/windows_version.h" #endif // BUILDFLAG(IS_WIN) namespace base { @@ -301,6 +301,7 @@ bool CommandLine::HasSwitch(StringPiece switch_string) const { } bool CommandLine::HasSwitch(const char switch_constant[]) const { + return HasSwitch(StringPiece(switch_constant)); } diff --git a/base/features.cc b/base/features.cc index 784865efe2c31..99c1f6691e96c 100644 --- a/base/features.cc +++ b/base/features.cc @@ -32,6 +32,11 @@ BASE_FEATURE(kSupportsUserDataFlatHashMap, "SupportsUserDataFlatHashMap", FEATURE_DISABLED_BY_DEFAULT); + +BASE_FEATURE(kForceDarkModeFlag, + "ForceDarkModeFlag", + base::FEATURE_DISABLED_BY_DEFAULT); + #if BUILDFLAG(IS_ANDROID) // Force to enable LowEndDeviceMode partially on Android mid-range devices. // Such devices aren't considered low-end, but we'd like experiment with diff --git a/base/features.h b/base/features.h index 90a69b41cb86e..1e30223b26023 100644 --- a/base/features.h +++ b/base/features.h @@ -23,6 +23,8 @@ BASE_EXPORT BASE_DECLARE_FEATURE(kOptimizeDataUrls); BASE_EXPORT BASE_DECLARE_FEATURE(kSupportsUserDataFlatHashMap); +BASE_EXPORT BASE_DECLARE_FEATURE(kForceDarkModeFlag); + #if BUILDFLAG(IS_ANDROID) BASE_EXPORT BASE_DECLARE_FEATURE(kPartialLowEndModeOnMidRangeDevices); extern const BASE_EXPORT FeatureParam diff --git a/base/message_loop/message_pump_win.cc b/base/message_loop/message_pump_win.cc index e6d84c4bbdaae..64e760163da31 100644 --- a/base/message_loop/message_pump_win.cc +++ b/base/message_loop/message_pump_win.cc @@ -789,6 +789,7 @@ bool MessagePumpForIO::GetIOItem(DWORD timeout, IOItem* item) { &overlapped, timeout)) { if (!overlapped) return false; // Nothing in the queue. + item->error = GetLastError(); item->bytes_transfered = 0; } diff --git a/base/process/process_win.cc b/base/process/process_win.cc index fbb666b329b2a..917e2a9a18233 100644 --- a/base/process/process_win.cc +++ b/base/process/process_win.cc @@ -271,30 +271,6 @@ bool Process::SetPriority(Priority priority) { ? IDLE_PRIORITY_CLASS : NORMAL_PRIORITY_CLASS; - if (base::win::OSInfo::GetInstance()->version() >= - base::win::Version::WIN11 && - FeatureList::IsEnabled(kUseEcoQoSForBackgroundProcess)) { - PROCESS_POWER_THROTTLING_STATE power_throttling; - RtlZeroMemory(&power_throttling, sizeof(power_throttling)); - power_throttling.Version = PROCESS_POWER_THROTTLING_CURRENT_VERSION; - - if (priority == Priority::kBestEffort) { - // Sets Eco QoS level. - power_throttling.ControlMask = PROCESS_POWER_THROTTLING_EXECUTION_SPEED; - power_throttling.StateMask = PROCESS_POWER_THROTTLING_EXECUTION_SPEED; - } else { - // Uses system default. - power_throttling.ControlMask = 0; - power_throttling.StateMask = 0; - } - bool ret = - ::SetProcessInformation(Handle(), ProcessPowerThrottling, - &power_throttling, sizeof(power_throttling)); - if (ret == 0) { - DPLOG(ERROR) << "Setting process QoS policy fails"; - } - } - return (::SetPriorityClass(Handle(), priority_class) != 0); } diff --git a/base/rand_util_win.cc b/base/rand_util_win.cc index 549f4362af8b6..2d9a1633b564f 100644 --- a/base/rand_util_win.cc +++ b/base/rand_util_win.cc @@ -9,6 +9,13 @@ #include #include +// #define needed to link in RtlGenRandom(), a.k.a. SystemFunction036. See the +// "Community Additions" comment on MSDN here: +// http://msdn.microsoft.com/en-us/library/windows/desktop/aa387694.aspx +#define SystemFunction036 NTAPI SystemFunction036 +#include +#undef SystemFunction036 + #include #include #include @@ -18,12 +25,6 @@ #include "third_party/boringssl/src/include/openssl/crypto.h" #include "third_party/boringssl/src/include/openssl/rand.h" -// Prototype for ProcessPrng. -// See: https://learn.microsoft.com/en-us/windows/win32/seccng/processprng -extern "C" { -BOOL WINAPI ProcessPrng(PBYTE pbData, SIZE_T cbData); -} - namespace base { namespace internal { @@ -53,18 +54,6 @@ bool UseBoringSSLForRandBytes() { namespace { -// Import bcryptprimitives!ProcessPrng rather than cryptbase!RtlGenRandom to -// avoid opening a handle to \\Device\KsecDD in the renderer. -decltype(&ProcessPrng) GetProcessPrng() { - HMODULE hmod = LoadLibraryW(L"bcryptprimitives.dll"); - CHECK(hmod); - decltype(&ProcessPrng) process_prng_fn = - reinterpret_cast( - GetProcAddress(hmod, "ProcessPrng")); - CHECK(process_prng_fn); - return process_prng_fn; -} - void RandBytes(void* output, size_t output_length, bool avoid_allocation) { if (!avoid_allocation && internal::UseBoringSSLForRandBytes()) { // Ensure BoringSSL is initialized so it can use things like RDRAND. @@ -74,10 +63,16 @@ void RandBytes(void* output, size_t output_length, bool avoid_allocation) { return; } - static decltype(&ProcessPrng) process_prng_fn = GetProcessPrng(); - BOOL success = process_prng_fn(static_cast(output), output_length); - // ProcessPrng is documented to always return TRUE. - CHECK(success); + char* output_ptr = static_cast(output); + while (output_length > 0) { + const ULONG output_bytes_this_pass = static_cast(std::min( + output_length, static_cast(std::numeric_limits::max()))); + const bool success = + RtlGenRandom(output_ptr, output_bytes_this_pass) != FALSE; + CHECK(success); + output_length -= output_bytes_this_pass; + output_ptr += output_bytes_this_pass; + } } } // namespace diff --git a/base/task/thread_pool/thread_group.cc b/base/task/thread_pool/thread_group.cc index 7e9dd368eb0ec..0fb94478eb47b 100644 --- a/base/task/thread_pool/thread_group.cc +++ b/base/task/thread_pool/thread_group.cc @@ -16,7 +16,9 @@ #if BUILDFLAG(IS_WIN) #include "base/win/com_init_check_hook.h" +#include "base/win/scoped_com_initializer.h" #include "base/win/scoped_winrt_initializer.h" +#include "base/win/windows_version.h" #endif namespace base { @@ -329,8 +331,18 @@ bool ThreadGroup::ShouldYield(TaskSourceSortKey sort_key) { std::unique_ptr ThreadGroup::GetScopedWindowsThreadEnvironment(WorkerEnvironment environment) { std::unique_ptr scoped_environment; - if (environment == WorkerEnvironment::COM_MTA) { - scoped_environment = std::make_unique(); + switch (environment) { + case WorkerEnvironment::COM_MTA: { + if (win::GetVersion() >= win::Version::WIN8) { + scoped_environment = std::make_unique(); + } else { + scoped_environment = std::make_unique( + win::ScopedCOMInitializer::kMTA); + } + break; + } + default: + break; } DCHECK(!scoped_environment || scoped_environment->Succeeded()); @@ -344,4 +356,4 @@ bool ThreadGroup::CurrentThreadHasGroup() { } } // namespace internal -} // namespace base +} // namespace base \ No newline at end of file diff --git a/base/threading/platform_thread_win.cc b/base/threading/platform_thread_win.cc index 83a1402780b0d..a7cdeb078197f 100644 --- a/base/threading/platform_thread_win.cc +++ b/base/threading/platform_thread_win.cc @@ -50,6 +50,13 @@ namespace { // Flag used to set thread priority to |THREAD_PRIORITY_LOWEST| for // |kUseThreadPriorityLowest| Feature. std::atomic g_use_thread_priority_lowest{false}; +// The most common value returned by ::GetThreadPriority() after background +// thread mode is enabled on Windows 7. +constexpr int kWin7BackgroundThreadModePriority = 4; + +// Value sometimes returned by ::GetThreadPriority() after thread priority is +// set to normal on Windows 7. +constexpr int kWin7NormalPriority = 3; // Flag used to map Compositing ThreadType |THREAD_PRIORITY_ABOVE_NORMAL| on the // UI thread for |kAboveNormalCompositingBrowserWin| Feature. std::atomic g_above_normal_compositing_browser{true}; @@ -236,7 +243,10 @@ void AssertMemoryPriority(HANDLE thread, int memory_priority) { reinterpret_cast(::GetProcAddress( ::GetModuleHandle(L"Kernel32.dll"), "GetThreadInformation")); - DCHECK(get_thread_information_fn); + if (!get_thread_information_fn) { + DCHECK_EQ(win::GetVersion(), win::Version::WIN7); + return; + } MEMORY_PRIORITY_INFORMATION memory_priority_information = {}; DCHECK(get_thread_information_fn(thread, ::ThreadMemoryPriority, @@ -536,8 +546,14 @@ ThreadPriorityForTest PlatformThread::GetCurrentThreadPriorityForTest() { return ThreadPriorityForTest::kBackground; switch (priority) { + case kWin7BackgroundThreadModePriority: + DCHECK_EQ(win::GetVersion(), win::Version::WIN7); + return ThreadPriorityForTest::kBackground; case THREAD_PRIORITY_BELOW_NORMAL: return ThreadPriorityForTest::kUtility; + case kWin7NormalPriority: + DCHECK_EQ(win::GetVersion(), win::Version::WIN7); + [[fallthrough]]; case THREAD_PRIORITY_NORMAL: return ThreadPriorityForTest::kNormal; case kWinDisplayPriority1: diff --git a/base/win/hstring_reference.cc b/base/win/hstring_reference.cc index 8e4506d08a188..6b2b1a6290990 100644 --- a/base/win/hstring_reference.cc +++ b/base/win/hstring_reference.cc @@ -42,6 +42,9 @@ bool HStringReference::ResolveCoreWinRTStringDelayload() { } HStringReference::HStringReference(const wchar_t* str, size_t length) { + + // This was added because otherwise, the checks below would consistently fail. + ResolveCoreWinRTStringDelayload(); DCHECK(g_winrt_string_loaded); // String must be null terminated for WindowsCreateStringReference. // nullptr str is OK so long as the length is 0. diff --git a/base/win/registry.cc b/base/win/registry.cc index c33cda7211238..88741550f934e 100644 --- a/base/win/registry.cc +++ b/base/win/registry.cc @@ -23,6 +23,7 @@ #include "base/win/object_watcher.h" #include "base/win/scoped_handle.h" #include "base/win/shlwapi.h" +#include "base/win/windows_version.h" extern "C" NTSTATUS WINAPI NtDeleteKey(IN HANDLE KeyHandle); @@ -87,8 +88,9 @@ bool RegKey::Watcher::StartWatching(HKEY key, ChangeCallback callback) { } DWORD filter = REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_ATTRIBUTES | - REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY | - REG_NOTIFY_THREAD_AGNOSTIC; + REG_NOTIFY_CHANGE_LAST_SET | REG_NOTIFY_CHANGE_SECURITY; + if (base::win::GetVersion() >= base::win::Version::WIN8) + filter |= REG_NOTIFY_THREAD_AGNOSTIC; // Watch the registry key for a change of value. LONG result = RegNotifyChangeKeyValue(key, /*bWatchSubtree=*/TRUE, filter, diff --git a/base/win/scoped_winrt_initializer.cc b/base/win/scoped_winrt_initializer.cc index 4c93dcfabf658..85f83dab8192b 100644 --- a/base/win/scoped_winrt_initializer.cc +++ b/base/win/scoped_winrt_initializer.cc @@ -5,14 +5,61 @@ #include "base/win/scoped_winrt_initializer.h" #include +#include + +#include #include "base/check_op.h" +#include "base/threading/scoped_thread_priority.h" #include "base/win/com_init_util.h" +#include "base/win/core_winrt_util.h" namespace base::win { +namespace { + +FARPROC LoadComBaseFunction(const char* function_name) { + static HMODULE const handle = []() { + // Mitigate the issues caused by loading DLLs on a background thread + // (http://crbug/973868). + SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY(); + return ::LoadLibraryEx(L"combase.dll", nullptr, + LOAD_LIBRARY_SEARCH_SYSTEM32); + }(); + return handle ? ::GetProcAddress(handle, function_name) : nullptr; +} + +decltype(&::RoInitialize) GetRoInitializeFunction() { + static decltype(&::RoInitialize) const function = + reinterpret_cast( + LoadComBaseFunction("RoInitialize")); + return function; +} + +decltype(&::RoUninitialize) GetRoUninitializeFunction() { + static decltype(&::RoUninitialize) const function = + reinterpret_cast( + LoadComBaseFunction("RoUninitialize")); + return function; +} + +HRESULT CallRoInitialize(RO_INIT_TYPE init_type) { + auto ro_initialize_func = GetRoInitializeFunction(); + if (!ro_initialize_func) + return E_FAIL; + return ro_initialize_func(init_type); +} + +void CallRoUninitialize() { + auto ro_uninitialize_func = GetRoUninitializeFunction(); + if (ro_uninitialize_func) + ro_uninitialize_func(); +} + +} // namespace + ScopedWinrtInitializer::ScopedWinrtInitializer() - : hr_(::RoInitialize(RO_INIT_MULTITHREADED)) { + : hr_(CallRoInitialize(RO_INIT_MULTITHREADED)) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); #if DCHECK_IS_ON() if (SUCCEEDED(hr_)) @@ -25,7 +72,7 @@ ScopedWinrtInitializer::ScopedWinrtInitializer() ScopedWinrtInitializer::~ScopedWinrtInitializer() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); if (SUCCEEDED(hr_)) - ::RoUninitialize(); + CallRoUninitialize(); } bool ScopedWinrtInitializer::Succeeded() const { diff --git a/base/win/win_util.cc b/base/win/win_util.cc index 61880ddc38aa1..116b73e16e61e 100644 --- a/base/win/win_util.cc +++ b/base/win/win_util.cc @@ -66,6 +66,22 @@ namespace base { namespace win { namespace { + +// Disables the DirectWrite font rendering system on windows. +const char kDisableDirectWrite[] = "disable-direct-write"; + +bool ShouldUseDirectWrite() { + // If the flag is currently on, and we're on WinVista or above, we enable + // DirectWrite. There is no reason to not install Platform Update or + // even use the Windows 7 Platform Update dwrite.dll with the extended kernel. + if (GetVersion() < base::win::Version::VISTA) { + return false; + } + // If forced off, don't use it. + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + return !command_line.HasSwitch(kDisableDirectWrite); +} // Sets the value of |property_key| to |property_value| in |property_store|. bool SetPropVariantValueForPropertyStore( @@ -95,10 +111,10 @@ void __cdecl ForceCrashOnSigAbort(int) { *((volatile int*)nullptr) = 0x1337; } -// Returns the current platform role. We use the PowerDeterminePlatformRoleEx +// Returns the current platform role. We use the PowerDeterminePlatformRole // API for that. POWER_PLATFORM_ROLE GetPlatformRole() { - return PowerDeterminePlatformRoleEx(POWER_PLATFORM_ROLE_V2); + return PowerDeterminePlatformRole(); } // Because we used to support versions earlier than 8.1, we dynamically load @@ -124,8 +140,10 @@ bool SetProcessDpiAwarenessWrapper(PROCESS_DPI_AWARENESS value) { return false; } - NOTREACHED() << "SetProcessDpiAwarenessInternal " - "should be available on all platforms >= Windows 8.1"; + DCHECK_LT(GetVersion(), Version::WIN8_1) << "SetProcessDpiAwarenessInternal " + "should be available on all " + "platforms >= Windows 8.1"; + return false; } @@ -640,6 +658,8 @@ bool IsJoinedToAzureAD() { bool IsUser32AndGdi32Available() { static auto is_user32_and_gdi32_available = []() { // If win32k syscalls aren't disabled, then user32 and gdi32 are available. + if (!ShouldUseDirectWrite()) + return true; auto get_process_mitigation_policy = reinterpret_cast(::GetProcAddress( ::GetModuleHandleA("kernel32.dll"), "GetProcessMitigationPolicy")); @@ -714,7 +734,7 @@ void DisableFlicks(HWND hwnd) { } void EnableHighDPISupport() { - if (!IsUser32AndGdi32Available()) + if (!IsUser32AndGdi32Available() || GetVersion() < Version::VISTA) return; // Enable per-monitor V2 if it is available (Win10 1703 or later). diff --git a/build/config/win/BUILD.gn b/build/config/win/BUILD.gn index 497d479895dfe..2d2abbf6db1c6 100644 --- a/build/config/win/BUILD.gn +++ b/build/config/win/BUILD.gn @@ -43,6 +43,10 @@ declare_args() { # and with this switch, clang emits it like this: # foo/bar.cc:12:34: error: something went wrong use_clang_diagnostics_format = false + + + # Indicates whether to use /pdbpagesize:8192 to allow PDBs larger than 4 GiB. + use_large_pdbs = false } # This is included by reference in the //build/config/compiler config that @@ -127,7 +131,9 @@ config("compiler") { # microarchitecture. MSVC only supports a subset of architectures, and the # next step after SSE2 will be AVX. if (current_cpu == "x86" || current_cpu == "x64") { - cflags += [ "-msse3" ] + if (target_cpu == "x64") { + cflags += [ "-msse3" ] + } } # Enable ANSI escape codes if something emulating them is around (cmd.exe @@ -169,14 +175,14 @@ config("compiler") { ldflags += [ "/lldignoreenv" ] } - # Some binaries create PDBs larger than 4 GiB. Increasing the PDB page size - # to 8 KiB allows 8 GiB PDBs. The larger page size also allows larger block maps - # which is a PDB limit that was hit in https://crbug.com/1406510. The page size - # can easily be increased in the future to allow even larger PDBs or larger - # block maps. - # This flag requires lld-link.exe or link.exe from VS 2022 or later to create - # the PDBs, and tools from circa 22H2 or later to consume the PDBs. - ldflags += [ "/pdbpagesize:8192" ] + if (use_large_pdbs) { + # This allows PDBs up to 8 GiB in size. This requires lld-link.exe or + # link.exe from VS 2022 or later. + if (!defined(configs)) { + configs = [] + } + configs += [ ":pdb_larger_than_4gb" ] + } if (!is_debug && !is_component_build) { # Enable standard linker optimizations like GC (/OPT:REF) and ICF in static @@ -682,3 +688,15 @@ config("lean_and_mean") { config("nominmax") { defines = [ "NOMINMAX" ] } + +# Some binaries create PDBs larger than 4 GiB. Increasing the PDB page size +# to 8 KiB allows 8 GiB PDBs. The larger page size also allows larger block maps +# which is a PDB limit that was hit in https://crbug.com/1406510. The page size +# can easily be increased in the future to allow even larger PDBs or larger +# block maps. +config("pdb_larger_than_4gb") { + if (!defined(ldflags)) { + ldflags = [] + } + ldflags += [ "/pdbpagesize:8192" ] +} diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc index e7d9dd1726cd8..565818d963478 100644 --- a/chrome/browser/about_flags.cc +++ b/chrome/browser/about_flags.cc @@ -15,6 +15,7 @@ #include "base/base_switches.h" #include "base/command_line.h" +#include "base/features.h" #include "base/feature_list.h" #include "base/functional/bind.h" #include "base/functional/callback.h" @@ -5355,6 +5356,14 @@ const FeatureEntry kFeatureEntries[] = { kForceDarkVariations, "ForceDarkVariations")}, #endif // BUILDFLAG(IS_CHROMEOS_ASH) + {"force-dark-mode", + flag_descriptions::kForceDarkModeFlagName, flag_descriptions::kForceDarkModeFlagDescription, kOsAll, + FEATURE_VALUE_TYPE(base::features::kForceDarkModeFlag)}, +#if BUILDFLAG(IS_WIN) + {"force-xp-theme", + flag_descriptions::kForceXpThemeName, flag_descriptions::kForceXpThemeDescription, kOsWin, + FEATURE_VALUE_TYPE(kForceXpTheme)}, +#endif #if BUILDFLAG(IS_ANDROID) {"enable-accessibility-page-zoom", flag_descriptions::kAccessibilityPageZoomName, @@ -6967,10 +6976,6 @@ const FeatureEntry kFeatureEntries[] = { flag_descriptions::kEnableWindowsGamingInputDataFetcherName, flag_descriptions::kEnableWindowsGamingInputDataFetcherDescription, kOsWin, FEATURE_VALUE_TYPE(features::kEnableWindowsGamingInputDataFetcher)}, - - {"windows11-mica-titlebar", flag_descriptions::kWindows11MicaTitlebarName, - flag_descriptions::kWindows11MicaTitlebarDescription, kOsWin, - FEATURE_VALUE_TYPE(kWindows11MicaTitlebar)}, #endif #if BUILDFLAG(IS_ANDROID) diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json index 30f77fb4f37ab..3d5d8db9ae658 100644 --- a/chrome/browser/flag-metadata.json +++ b/chrome/browser/flag-metadata.json @@ -4546,6 +4546,11 @@ "owners": [ "chromeos-camera-eng@google.com" ], "expiry_milestone": 120 }, + { + "name": "force-dark-mode", + "owners": [ "win32", "supermium" ], + "expiry_milestone": -1 + }, { "name": "force-enable-fast-checkout-capabilities", "owners": [ "vizcay@google.com", "bwolfgang@google.com", "jkeitel@google.com" ], @@ -4612,6 +4617,11 @@ // have no access to commandline flags. "expiry_milestone": -1 }, + { + "name": "force-xp-theme", + "owners": [ "win32", "supermium" ], + "expiry_milestone": -1 + }, { "name": "forced-colors", "owners": [ "almaher@microsoft.com" ], diff --git a/chrome/browser/flag-never-expire-list.json b/chrome/browser/flag-never-expire-list.json index 296c1a9d26c82..acc079e787890 100644 --- a/chrome/browser/flag-never-expire-list.json +++ b/chrome/browser/flag-never-expire-list.json @@ -76,12 +76,14 @@ "extensions-on-chrome-urls", "external-navigation-debug-logs", "force-color-profile", + "force-dark-mode", "force-effective-connection-type", "force-show-update-menu-badge", "force-startup-signin-promo", "force-text-direction", "force-ui-direction", "force-update-menu-type", + "force-xp-theme", "fullscreen-promos-manager-skip-internal-limits", "fullscreen-viewport-adjustment-experiment", "gesture-properties-dbus-service", diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc index 2d229e6ed04d3..20cedafdf0655 100644 --- a/chrome/browser/flag_descriptions.cc +++ b/chrome/browser/flag_descriptions.cc @@ -2784,6 +2784,16 @@ const char kVmPerBootShaderCacheDescription[] = const char kAutoWebContentsDarkModeName[] = "Auto Dark Mode for Web Contents"; const char kAutoWebContentsDarkModeDescription[] = "Automatically render all web contents using a dark theme."; + +const char kForceDarkModeFlagName[] = "Forced Dark Mode for UI"; +const char kForceDarkModeFlagDescription[] = + "Enables dark mode for all UI elements (but not web contents - " + "enable #enable-force-dark for darkening web contents)."; + +const char kForceXpThemeName[] = "Use Classic Theme"; +const char kForceXpThemeDescription[] = + "Use the classic Chrome theme designed to mimick \"Aero\" window controls. " + "Typically used when desktop composition is disabled or unavailable."; const char kForcedColorsName[] = "Forced Colors"; const char kForcedColorsDescription[] = diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h index bccfce996cac4..321201ce03f27 100644 --- a/chrome/browser/flag_descriptions.h +++ b/chrome/browser/flag_descriptions.h @@ -1593,6 +1593,12 @@ extern const char kBookmarksRefreshDescription[]; extern const char kAutoWebContentsDarkModeName[]; extern const char kAutoWebContentsDarkModeDescription[]; +extern const char kForceDarkModeFlagName[]; +extern const char kForceDarkModeFlagDescription[]; + +extern const char kForceXpThemeName[]; +extern const char kForceXpThemeDescription[]; + extern const char kForcedColorsName[]; extern const char kForcedColorsDescription[]; diff --git a/chrome/browser/themes/theme_helper_win.cc b/chrome/browser/themes/theme_helper_win.cc index 259668e16470f..aef2186c2dee7 100644 --- a/chrome/browser/themes/theme_helper_win.cc +++ b/chrome/browser/themes/theme_helper_win.cc @@ -8,6 +8,11 @@ #include "chrome/browser/themes/theme_properties.h" #include "chrome/browser/win/titlebar_config.h" #include "chrome/grit/theme_resources.h" +#include "base/win/windows_version.h" + +BASE_FEATURE(kForceXpTheme, + "ForceXpTheme", + base::FEATURE_DISABLED_BY_DEFAULT); int ThemeHelperWin::GetDefaultDisplayProperty(int id) const { if (id == ThemeProperties::SHOULD_FILL_BACKGROUND_TAB_COLOR) { @@ -19,5 +24,10 @@ int ThemeHelperWin::GetDefaultDisplayProperty(int id) const { bool ThemeHelperWin::ShouldUseNativeFrame( const CustomThemeSupplier* theme_supplier) const { - return true; + // If it returns false, the XP fallback theme is used. + // And yes, Chromium has muddied the waters of what is considered "native". + // Aero Glass is "native", Mica is "native", and so is the Windows 10-style theme that is drawn + // by Chromium itself. + // Only the "original" Chromium theme that mimicks Aero is not considered native. + return (!HasCustomImage(IDR_THEME_FRAME, theme_supplier) && (base::win::GetVersion() >= base::win::Version::VISTA)) && !base::FeatureList::IsEnabled(kForceXpTheme); } diff --git a/chrome/browser/ui/color/win/native_chrome_color_mixer_win.cc b/chrome/browser/ui/color/win/native_chrome_color_mixer_win.cc index c0f049e590a92..a83f4de371809 100644 --- a/chrome/browser/ui/color/win/native_chrome_color_mixer_win.cc +++ b/chrome/browser/ui/color/win/native_chrome_color_mixer_win.cc @@ -8,6 +8,7 @@ #include "base/functional/bind.h" #include "base/no_destructor.h" #include "base/win/windows_version.h" +#include "chrome/browser/themes/browser_theme_pack.h" #include "chrome/browser/themes/theme_properties.h" #include "chrome/browser/ui/color/chrome_color_id.h" #include "chrome/browser/win/titlebar_config.h" @@ -44,6 +45,13 @@ class FrameColorHelper { static FrameColorHelper* Get(); private: + // Returns whether there is a custom image provided for the given id. + bool HasCustomImage(int id, const ui::ColorProviderKey& key) const; + + // Returns true if colors from DWM can be used, i.e. this is a native frame + // on Windows 8+. + bool DwmColorsAllowed(const ui::ColorProviderKey& key) const; + // Returns the Tint for the given |id|. If there is no tint, the identity tint // {-1, -1, -1} is returned and won't tint the color on which it is used. color_utils::HSL GetTint(int id, const ui::ColorProviderKey& key) const; @@ -88,16 +96,22 @@ void FrameColorHelper::AddNativeChromeColors( return absl::nullopt; }; - // When we're custom-drawing the titlebar we want to use either the colors - // we calculated in OnDwmKeyUpdated() or the default colors. When we're not - // custom-drawing the titlebar we want to match the color Windows actually - // uses because some things (like the incognito icon) use this color to - // decide whether they should draw in light or dark mode. Incognito colors - // should be the same as non-incognito in all cases here. - - constexpr SkColor kSystemMicaLightFrameColor = + if (DwmColorsAllowed(key)) { + // When we're custom-drawing the titlebar we want to use either the colors + // we calculated in OnDwmKeyUpdated() or the default colors. When we're not + // custom-drawing the titlebar we want to match the color Windows actually + // uses because some things (like the incognito icon) use this color to + // decide whether they should draw in light or dark mode. Incognito colors + // should be the same as non-incognito in all cases here. + + constexpr SkColor kSystemSolidLightFrameColor = SK_ColorWHITE; + constexpr SkColor kSystemSolidDarkInactiveFrameColor = + SkColorSetRGB(0x2B, 0x2B, 0x2B); + + constexpr SkColor kSystemMicaLightFrameColor = SkColorSetRGB(0xE8, 0xE8, 0xE8); - constexpr SkColor kSystemMicaDarkFrameColor = SkColorSetRGB(0x20, 0x20, 0x20); + constexpr SkColor kSystemMicaDarkFrameColor = SkColorSetRGB(0x20, 0x20, 0x20); + absl::optional active_frame_transform; if (auto color = get_theme_color(TP::COLOR_FRAME_ACTIVE)) { @@ -155,13 +169,37 @@ void FrameColorHelper::AddNativeChromeColors( mixer[ui::kColorSysHeaderContainerInactive] = {ui::kColorSysBase}; } - if (ShouldDefaultThemeUseMicaTitlebar() && !key.app_controller) { - mixer[kColorNewTabButtonBackgroundFrameActive] = {SK_ColorTRANSPARENT}; - mixer[kColorNewTabButtonBackgroundFrameInactive] = {SK_ColorTRANSPARENT}; - mixer[kColorNewTabButtonInkDropFrameActive] = - ui::GetColorWithMaxContrast(ui::kColorFrameActive); - mixer[kColorNewTabButtonInkDropFrameInactive] = + if (auto color = get_theme_color(TP::COLOR_FRAME_INACTIVE)) { + mixer[ui::kColorFrameInactive] = {color.value()}; + } else if (dwm_inactive_frame_color_) { + mixer[ui::kColorFrameInactive] = {dwm_inactive_frame_color_.value()}; + } else if (ShouldDefaultThemeUseMicaTitlebar() && !key.app_controller) { + mixer[ui::kColorFrameInactive] = {key.color_mode == ColorMode::kDark + ? kSystemMicaDarkFrameColor + : kSystemMicaLightFrameColor}; + } else if (!ShouldCustomDrawSystemTitlebar()) { + mixer[ui::kColorFrameInactive] = {key.color_mode == ColorMode::kDark + ? kSystemSolidDarkInactiveFrameColor + : kSystemSolidLightFrameColor}; + } else if (dwm_frame_color_) { + mixer[ui::kColorFrameInactive] = + ui::HSLShift({dwm_frame_color_.value()}, + GetTint(ThemeProperties::TINT_FRAME_INACTIVE, key)); + } + + if (ShouldDefaultThemeUseMicaTitlebar() && !key.app_controller) { + mixer[kColorNewTabButtonBackgroundFrameActive] = {SK_ColorTRANSPARENT}; + mixer[kColorNewTabButtonBackgroundFrameInactive] = {SK_ColorTRANSPARENT}; + mixer[kColorNewTabButtonInkDropFrameInactive] = ui::GetColorWithMaxContrast(ui::kColorFrameInactive); + mixer[kColorNewTabButtonInkDropFrameActive] = + ui::GetColorWithMaxContrast(ui::kColorFrameActive); + } + } else { + if (auto color = get_theme_color(TP::COLOR_FRAME_ACTIVE)) + mixer[ui::kColorFrameActive] = {color.value()}; + if (auto color = get_theme_color(TP::COLOR_FRAME_INACTIVE)) + mixer[ui::kColorFrameInactive] = {color.value()}; } } @@ -184,6 +222,19 @@ FrameColorHelper* FrameColorHelper::Get() { return g_frame_color_helper.get(); } +bool FrameColorHelper::HasCustomImage( + int id, + const ui::ColorProviderKey& key) const { + return BrowserThemePack::IsPersistentImageID(id) && key.custom_theme && + key.custom_theme->HasCustomImage(id); +} + +bool FrameColorHelper::DwmColorsAllowed( + const ui::ColorProviderKey& key) const { + return (!ShouldCustomDrawSystemTitlebar() || + !HasCustomImage(IDR_THEME_FRAME, key)); +} + color_utils::HSL FrameColorHelper::GetTint( int id, const ui::ColorProviderKey& key) const { diff --git a/chrome/browser/ui/sharing_hub/screenshot/screenshot_captured_bubble_controller.cc b/chrome/browser/ui/sharing_hub/screenshot/screenshot_captured_bubble_controller.cc index 298ddbffdd8ed..b7538a3180c05 100644 --- a/chrome/browser/ui/sharing_hub/screenshot/screenshot_captured_bubble_controller.cc +++ b/chrome/browser/ui/sharing_hub/screenshot/screenshot_captured_bubble_controller.cc @@ -35,7 +35,6 @@ void ScreenshotCapturedBubbleController::ShowBubble( const gfx::Image& captured_image = image.image; ui::ScopedClipboardWriter(ui::ClipboardBuffer::kCopyPaste) .WriteImage(*captured_image.ToSkBitmap()); - Browser* browser = chrome::FindBrowserWithWebContents(&GetWebContents()); browser->window()->ShowScreenshotCapturedBubble(&GetWebContents(), captured_image); diff --git a/chrome/browser/ui/startup/bad_flags_prompt.cc b/chrome/browser/ui/startup/bad_flags_prompt.cc index f4b8bc02b8da0..2a14dfc62f263 100644 --- a/chrome/browser/ui/startup/bad_flags_prompt.cc +++ b/chrome/browser/ui/startup/bad_flags_prompt.cc @@ -69,7 +69,6 @@ const char* const kBadFlags[] = { sandbox::policy::switches::kDisableGpuSandbox, sandbox::policy::switches::kDisableSeccompFilterSandbox, sandbox::policy::switches::kDisableSetuidSandbox, - sandbox::policy::switches::kNoSandbox, #if BUILDFLAG(IS_WIN) sandbox::policy::switches::kAllowThirdPartyModules, #endif diff --git a/chrome/browser/ui/test/test_browser_ui.cc b/chrome/browser/ui/test/test_browser_ui.cc index 2f61aef15bbf8..2b49f57680e6f 100644 --- a/chrome/browser/ui/test/test_browser_ui.cc +++ b/chrome/browser/ui/test/test_browser_ui.cc @@ -5,6 +5,7 @@ #include "chrome/browser/ui/test/test_browser_ui.h" #include "base/command_line.h" +#include "base/features.h" #include "base/test/gtest_util.h" #include "base/test/test_switches.h" #include "build/build_config.h" @@ -159,8 +160,8 @@ void TestBrowserUi::ShowAndVerifyUi() { // Gold files for pixel tests are for light mode, so if dark mode is not // forced, and host is in dark mode, skip test. if (!IsInteractiveUi() && - !base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kForceDarkMode) && + (!base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kForceDarkMode) || base::FeatureList::IsEnabled(base::features::kForceDarkModeFlag) && ui::NativeTheme::GetInstanceForNativeUi()->ShouldUseDarkColors()) { GTEST_SKIP() << "Host is in dark mode; skipping test"; } diff --git a/chrome/browser/ui/views/frame/browser_frame_view_win.cc b/chrome/browser/ui/views/frame/browser_frame_view_win.cc index d681a79ff9df7..66f165e05c7bb 100644 --- a/chrome/browser/ui/views/frame/browser_frame_view_win.cc +++ b/chrome/browser/ui/views/frame/browser_frame_view_win.cc @@ -403,7 +403,7 @@ void BrowserFrameViewWin::ResetWindowControls() { void BrowserFrameViewWin::OnThemeChanged() { BrowserNonClientFrameView::OnThemeChanged(); if (!ShouldBrowserCustomDrawTitlebar(browser_view())) { - SetSystemMicaTitlebarAttributes(); + SetSystemTitlebarAttributes(); } } @@ -643,24 +643,26 @@ bool BrowserFrameViewWin::ShouldShowWindowTitle(TitlebarType type) const { void BrowserFrameViewWin::TabletModeChanged() { if (!ShouldBrowserCustomDrawTitlebar(browser_view())) { - SetSystemMicaTitlebarAttributes(); + SetSystemTitlebarAttributes(); } } -void BrowserFrameViewWin::SetSystemMicaTitlebarAttributes() { - CHECK(SystemTitlebarCanUseMicaMaterial()); - - const BOOL dark_titlebar_enabled = GetNativeTheme()->ShouldUseDarkColors(); - DwmSetWindowAttribute(views::HWNDForWidget(frame()), - DWMWA_USE_IMMERSIVE_DARK_MODE, &dark_titlebar_enabled, - sizeof(dark_titlebar_enabled)); +void BrowserFrameViewWin::SetSystemTitlebarAttributes() { + if (SystemTitlebarSupportsDarkMode()) { + const BOOL dark_titlebar_enabled = GetNativeTheme()->ShouldUseDarkColors(); + DwmSetWindowAttribute(views::HWNDForWidget(frame()), + DWMWA_USE_IMMERSIVE_DARK_MODE, &dark_titlebar_enabled, + sizeof(dark_titlebar_enabled)); + } - const DWM_SYSTEMBACKDROP_TYPE dwm_backdrop_type = - browser_view()->GetTabStripVisible() ? DWMSBT_TABBEDWINDOW - : DWMSBT_MAINWINDOW; - DwmSetWindowAttribute(views::HWNDForWidget(frame()), - DWMWA_SYSTEMBACKDROP_TYPE, &dwm_backdrop_type, - sizeof(dwm_backdrop_type)); + if (ShouldBrowserUseMicaTitlebar(browser_view())) { + const DWM_SYSTEMBACKDROP_TYPE dwm_backdrop_type = + browser_view()->GetTabStripVisible() ? DWMSBT_TABBEDWINDOW + : DWMSBT_MAINWINDOW; + DwmSetWindowAttribute(views::HWNDForWidget(frame()), + DWMWA_SYSTEMBACKDROP_TYPE, &dwm_backdrop_type, + sizeof(dwm_backdrop_type)); + } } SkColor BrowserFrameViewWin::GetTitlebarColor() const { diff --git a/chrome/browser/ui/views/frame/browser_frame_view_win.h b/chrome/browser/ui/views/frame/browser_frame_view_win.h index 40c9246a8833f..bb24a265a06c7 100644 --- a/chrome/browser/ui/views/frame/browser_frame_view_win.h +++ b/chrome/browser/ui/views/frame/browser_frame_view_win.h @@ -146,8 +146,8 @@ class BrowserFrameViewWin : public BrowserNonClientFrameView, // Called when the device enters or exits tablet mode. void TabletModeChanged(); - // Sets DWM attributes for rendering the system-drawn Mica titlebar. - void SetSystemMicaTitlebarAttributes(); + // Sets DWM attributes for rendering the system-drawn titlebar. + void SetSystemTitlebarAttributes(); // Paint various sub-components of this view. void PaintTitlebar(gfx::Canvas* canvas) const; diff --git a/chrome/browser/ui/views/sharing_hub/screenshot/screenshot_captured_bubble.cc b/chrome/browser/ui/views/sharing_hub/screenshot/screenshot_captured_bubble.cc index 1bf912250ff05..e768404fabf43 100644 --- a/chrome/browser/ui/views/sharing_hub/screenshot/screenshot_captured_bubble.cc +++ b/chrome/browser/ui/views/sharing_hub/screenshot/screenshot_captured_bubble.cc @@ -65,6 +65,7 @@ ScreenshotCapturedBubble::ScreenshotCapturedBubble( image_(image), web_contents_(web_contents->GetWeakPtr()), profile_(profile) { + SetButtons(ui::DIALOG_BUTTON_NONE); SetTitle(IDS_BROWSER_SHARING_SCREENSHOT_POST_CAPTURE_TITLE); } diff --git a/chrome/browser/win/titlebar_config.cc b/chrome/browser/win/titlebar_config.cc index d0b41c4ea5441..527b2c158fab0 100644 --- a/chrome/browser/win/titlebar_config.cc +++ b/chrome/browser/win/titlebar_config.cc @@ -45,9 +45,8 @@ bool ShouldCustomDrawSystemTitlebar() { } bool ShouldBrowserCustomDrawTitlebar(BrowserView* browser_view) { + return ShouldCustomDrawSystemTitlebar() || - !ThemeServiceFactory::GetForProfile(browser_view->GetProfile()) - ->UsingSystemTheme() || (!browser_view->browser()->is_type_normal() && !browser_view->browser()->is_type_popup() && !browser_view->browser()->is_type_devtools()); @@ -71,4 +70,4 @@ bool ShouldBrowserUseMicaTitlebar(class BrowserView *) { bool SystemTitlebarSupportsDarkMode() { return base::win::GetVersion() >= base::win::Version::WIN11; -} \ No newline at end of file +} diff --git a/chrome/browser/win/titlebar_config.h b/chrome/browser/win/titlebar_config.h index 48f413792d5aa..596d9ad041c21 100644 --- a/chrome/browser/win/titlebar_config.h +++ b/chrome/browser/win/titlebar_config.h @@ -10,10 +10,18 @@ class BrowserView; BASE_DECLARE_FEATURE(kWindows11MicaTitlebar); +BASE_DECLARE_FEATURE(kForceXpTheme); // Returns whether we should custom draw the titlebar for a browser window. bool ShouldBrowserCustomDrawTitlebar(BrowserView* browser_view); +// Returns whether we should always custom draw the system title bar. +bool ShouldCustomDrawSystemTitlebar(); + +// Returns whether we should use the Mica titlebar material for a browser +// window. +bool ShouldBrowserUseMicaTitlebar(BrowserView* browser_view); + // Returns whether we should use the Mica titlebar in standard browser windows // using the default theme. bool ShouldDefaultThemeUseMicaTitlebar(); @@ -22,4 +30,7 @@ bool ShouldDefaultThemeUseMicaTitlebar(); // material. bool SystemTitlebarCanUseMicaMaterial(); +// Returns whether the system-drawn titlebar can be drawn in dark mode. +bool SystemTitlebarSupportsDarkMode(); + #endif // CHROME_BROWSER_WIN_TITLEBAR_CONFIG_H_ diff --git a/chrome/chrome_elf/hook_util/hook_util.cc b/chrome/chrome_elf/hook_util/hook_util.cc index ce309c8b6888b..96ab0afa9bbf3 100644 --- a/chrome/chrome_elf/hook_util/hook_util.cc +++ b/chrome/chrome_elf/hook_util/hook_util.cc @@ -8,6 +8,9 @@ #include // windows.h must be before #include "base/win/pe_image.h" +#include "chrome/chrome_elf/nt_registry/nt_registry.h" // utils +#include "sandbox/win/src/internal_types.h" +#include "sandbox/win/src/service_resolver.h" namespace { @@ -194,6 +197,44 @@ DWORD RemoveIATHook(void* intercept_function, namespace elf_hook { +//------------------------------------------------------------------------------ +// System Service hooking support +//------------------------------------------------------------------------------ + +sandbox::ServiceResolverThunk* HookSystemService(bool relaxed) { + // Create a thunk via the appropriate ServiceResolver instance. + sandbox::ServiceResolverThunk* thunk = nullptr; + + // No hooking on unsupported OS versions. + if (!::IsWindows7OrGreater()) + return thunk; + + // Pseudo-handle, no need to close. + HANDLE current_process = ::GetCurrentProcess(); + +#if defined(_WIN64) + // ServiceResolverThunk can handle all the formats in 64-bit (instead only + // handling one like it does in 32-bit versions). + thunk = new sandbox::ServiceResolverThunk(current_process, relaxed); +#else + BOOL is_wow64 = FALSE; + if (::IsWow64Process(::GetCurrentProcess(), &is_wow64) && is_wow64) { + if (::IsWindows10OrGreater()) + thunk = new sandbox::Wow64W10ResolverThunk(current_process, relaxed); + else if (::IsWindows8OrGreater()) + thunk = new sandbox::Wow64W8ResolverThunk(current_process, relaxed); + else + thunk = new sandbox::Wow64ResolverThunk(current_process, relaxed); + } else if (::IsWindows8OrGreater()) { + thunk = new sandbox::Win8ResolverThunk(current_process, relaxed); + } else { + thunk = new sandbox::ServiceResolverThunk(current_process, relaxed); + } +#endif + + return thunk; +} + //------------------------------------------------------------------------------ // Import Address Table hooking support //------------------------------------------------------------------------------ diff --git a/chrome/chrome_elf/hook_util/hook_util.h b/chrome/chrome_elf/hook_util/hook_util.h index 9070b9d93a6f0..687042ec330c7 100644 --- a/chrome/chrome_elf/hook_util/hook_util.h +++ b/chrome/chrome_elf/hook_util/hook_util.h @@ -7,8 +7,20 @@ #include +namespace sandbox { +class ServiceResolverThunk; +} + namespace elf_hook { +//------------------------------------------------------------------------------ +// System Service hooking support +//------------------------------------------------------------------------------ + +// Creates a |ServiceResolverThunk| based on the OS version. Ownership of the +// resulting thunk is passed to the caller. +sandbox::ServiceResolverThunk* HookSystemService(bool relaxed); + //------------------------------------------------------------------------------ // Import Address Table hooking support //------------------------------------------------------------------------------ diff --git a/chrome/common/chrome_constants.cc b/chrome/common/chrome_constants.cc index 3a39e2bb7b608..f3926eb56e280 100644 --- a/chrome/common/chrome_constants.cc +++ b/chrome/common/chrome_constants.cc @@ -16,7 +16,7 @@ #elif BUILDFLAG(GOOGLE_CHROME_FOR_TESTING_BRANDING) #define PRODUCT_STRING "Google Chrome for Testing" #elif BUILDFLAG(CHROMIUM_BRANDING) -#define PRODUCT_STRING "Chromium" +#define PRODUCT_STRING "Supermium" #else #error Unknown branding #endif diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc index 8c62578d98c5f..3ad284e05463a 100644 --- a/chrome/common/chrome_switches.cc +++ b/chrome/common/chrome_switches.cc @@ -771,6 +771,10 @@ const char kMakeChromeDefault[] = "make-chrome-default"; #endif // BUILDFLAG(IS_MAC) #if BUILDFLAG(IS_WIN) +// Disables custom-drawing the window titlebar on Windows 10. +const char kDisableWindows10CustomTitlebar[] = + "disable-windows10-custom-titlebar"; + // Force-enables the profile shortcut manager. This is needed for tests since // they use a custom-user-data-dir which disables this. const char kEnableProfileShortcutManager[] = "enable-profile-shortcut-manager"; diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h index d06cebbece2da..a233435b771ac 100644 --- a/chrome/common/chrome_switches.h +++ b/chrome/common/chrome_switches.h @@ -244,6 +244,7 @@ extern const char kMakeChromeDefault[]; #endif // BUILDFLAG(IS_MAC) #if BUILDFLAG(IS_WIN) +extern const char kDisableWindows10CustomTitlebar[]; extern const char kEnableProfileShortcutManager[]; extern const char kFromInstaller[]; extern const char kHideIcons[]; diff --git a/chrome/install_static/chromium_install_modes.cc b/chrome/install_static/chromium_install_modes.cc index 0ffb200b16e34..402d21e5200a8 100644 --- a/chrome/install_static/chromium_install_modes.cc +++ b/chrome/install_static/chromium_install_modes.cc @@ -16,7 +16,7 @@ namespace install_static { const wchar_t kCompanyPathName[] = L""; -const wchar_t kProductPathName[] = L"Chromium"; +const wchar_t kProductPathName[] = L"Supermium"; const size_t kProductPathNameLength = _countof(kProductPathName) - 1; @@ -34,14 +34,14 @@ const InstallConstants kInstallModes[] = { .logo_suffix = L"", // No logo suffix for the primary install mode. .app_guid = L"", // Empty app_guid since no integration with Google Update. - .base_app_name = L"Chromium", // A distinct base_app_name. - .base_app_id = L"Chromium", // A distinct base_app_id. - .browser_prog_id_prefix = L"ChromiumHTM", // Browser ProgID prefix. + .base_app_name = L"Supermium", // A distinct base_app_name. + .base_app_id = L"Supermium", // A distinct base_app_id. + .browser_prog_id_prefix = L"SupermiumHTM", // Browser ProgID prefix. .browser_prog_id_description = - L"Chromium HTML Document", // Browser ProgID description. - .pdf_prog_id_prefix = L"ChromiumPDF", // PDF ProgID prefix. + L"Supermium HTML Document", // Browser ProgID description. + .pdf_prog_id_prefix = L"SupermiumPDF", // PDF ProgID prefix. .pdf_prog_id_description = - L"Chromium PDF Document", // PDF ProgID description. + L"Supermium PDF Document", // PDF ProgID description. .active_setup_guid = L"{7D2B3E1D-D096-4594-9D8F-A6667F12E0AC}", // Active Setup GUID. .legacy_command_execute_clsid = diff --git a/chrome/services/util_win/processor_metrics.cc b/chrome/services/util_win/processor_metrics.cc index 9e1ae266141a3..8d58fc54c7add 100644 --- a/chrome/services/util_win/processor_metrics.cc +++ b/chrome/services/util_win/processor_metrics.cc @@ -7,7 +7,6 @@ #include #include #include -#include #include #include "base/metrics/histogram_functions.h" @@ -130,34 +129,15 @@ void RecordCetAvailability() { } } -void RecordEnclaveAvailability() { - base::UmaHistogramBoolean("Windows.Enclave.SGX.Available", - ::IsEnclaveTypeSupported(ENCLAVE_TYPE_SGX)); - base::UmaHistogramBoolean("Windows.Enclave.SGX2.Available", - ::IsEnclaveTypeSupported(ENCLAVE_TYPE_SGX2)); - base::UmaHistogramBoolean("Windows.Enclave.VBS.Available", - ::IsEnclaveTypeSupported(ENCLAVE_TYPE_VBS)); - // This one is less documented, but appears to be used "when attestation - // evidence does not include VBS data.". This was added in Windows 19H1. - base::UmaHistogramBoolean("Windows.Enclave.VBSBasic.Available", - ::IsEnclaveTypeSupported(ENCLAVE_TYPE_VBS_BASIC)); -} - void RecordProcessorMetrics() { - // These metrics do not require a WMI connection. + base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, + base::BlockingType::MAY_BLOCK); + ComPtr wmi_services; + if (!base::win::CreateLocalWmiConnection(true, &wmi_services)) + return; + RecordProcessorMetricsFromWMI(wmi_services); + RecordHypervStatusFromWMI(wmi_services); RecordCetAvailability(); - RecordEnclaveAvailability(); - - { - base::ScopedBlockingCall scoped_blocking_call( - FROM_HERE, base::BlockingType::MAY_BLOCK); - ComPtr wmi_services; - if (!base::win::CreateLocalWmiConnection(true, &wmi_services)) { - return; - } - RecordProcessorMetricsFromWMI(wmi_services); - RecordHypervStatusFromWMI(wmi_services); - } } } // namespace diff --git a/components/embedder_support/user_agent_utils.cc b/components/embedder_support/user_agent_utils.cc index c957a691efcac..59bcdbb763bc2 100644 --- a/components/embedder_support/user_agent_utils.cc +++ b/components/embedder_support/user_agent_utils.cc @@ -88,13 +88,45 @@ int GetPreRS5UniversalApiContractVersion() { return 6; } // The list above should account for all Windows versions prior to - // RS5. - NOTREACHED(); + // RS5. 0 represents all pre-Win10 releases. return 0; } +int GetLegacyWindowsVersion() { +// The User Agent Client Hints specification indicates that Windows 7 through 8.x +// should be handled as having a major version of 0 while using the OS' minor version. +// (i.e. Windows 7 is 0.1). This does not account for how to handle Windows Vista and below. +// As a result, Vista will report as 0.60 and NT 5.x as 0.5x. +// TODO: introduce UA-CH spoofer to avoid any undesirable impacts of having the "wrong" values in future. + const base::win::Version version = base::win::OSInfo::Kernel32Version(); + if (version == base::win::Version::WIN8_1) { + return 3; + } + if (version == base::win::Version::WIN8) { + return 2; + } + if (version == base::win::Version::WIN7) { + return 1; + } + if (version == base::win::Version::VISTA) { + return 60; + } + if (version == base::win::Version::SERVER_2003) { + return 52; + } + if (version == base::win::Version::XP) { + return 51; + } + if (version == base::win::Version::PRE_XP) { + return 50; + } + return 0; +} + // Returns the UniversalApiContract version number, which is available for -// Windows versions greater than RS5. Otherwise, returns 0. +// Windows versions greater than RS5. Otherwise, returns a version value +// representing the Windows version (non-zero major version for early Windows 10, +// non-zero minor version for pre-Windows 10). const std::string& GetUniversalApiContractVersion() { // Do not use this for runtime environment detection logic. This method should // only be used to help populate the Sec-CH-UA-Platform client hint. If @@ -108,6 +140,7 @@ const std::string& GetUniversalApiContractVersion() { if (base::win::OSInfo::Kernel32Version() <= base::win::Version::WIN10_RS4) { major_version = GetPreRS5UniversalApiContractVersion(); + minor_version = GetLegacyWindowsVersion(); } else { base::win::RegKey version_key( HKEY_LOCAL_MACHINE, kWindowsRuntimeWellKnownContractsRegKeyName, diff --git a/content/browser/renderer_host/direct_manipulation_helper_win.cc b/content/browser/renderer_host/direct_manipulation_helper_win.cc index b5ad20605fbd8..d760a109ff576 100644 --- a/content/browser/renderer_host/direct_manipulation_helper_win.cc +++ b/content/browser/renderer_host/direct_manipulation_helper_win.cc @@ -181,9 +181,12 @@ void DirectManipulationHelper::OnPointerHitTest(WPARAM w_param) { // For WM_POINTER, the pointer type will show the event from mouse. // For WM_POINTERACTIVATE, the pointer id will be different with the following // message. + using GetPointerTypeFn = BOOL(WINAPI*)(UINT32, POINTER_INPUT_TYPE*); UINT32 pointer_id = GET_POINTERID_WPARAM(w_param); POINTER_INPUT_TYPE pointer_type; - if (::GetPointerType(pointer_id, &pointer_type) && + static const auto get_pointer_type = reinterpret_cast( + base::win::GetUser32FunctionPointer("GetPointerType")); + if (get_pointer_type && get_pointer_type(pointer_id, &pointer_type) && pointer_type == PT_TOUCHPAD) { viewport_->SetContact(pointer_id); } diff --git a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc index 891840540bb13..9df2e08a91a7f 100644 --- a/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc +++ b/content/browser/renderer_host/dwrite_font_lookup_table_builder_win.cc @@ -34,7 +34,6 @@ #include "base/win/registry.h" #include "content/browser/renderer_host/dwrite_font_file_util_win.h" #include "content/browser/renderer_host/dwrite_font_proxy_impl_win.h" -#include "content/browser/renderer_host/dwrite_font_uma_logging_win.h" #include "content/public/browser/content_browser_client.h" #include "content/public/common/content_client.h" #include "content/public/common/content_features.h" @@ -186,7 +185,6 @@ void DWriteFontLookupTableBuilder::InitializeDirectWrite() { if (!collection_) { base::UmaHistogramSparse( "DirectWrite.Fonts.Proxy.GetSystemFontCollectionResult", hr); - LogMessageFilterError(MessageFilterError::ERROR_NO_COLLECTION); return; } } @@ -368,6 +366,9 @@ void DWriteFontLookupTableBuilder:: void DWriteFontLookupTableBuilder::PrepareFontUniqueNameTable( const std::string& browser_version) { + if (!collection_) { + return; + } TRACE_EVENT0("dwrite,fonts", "DWriteFontLookupTableBuilder::PrepareFontUniqueNameTable"); DCHECK(!HasDWriteUniqueFontLookups()); diff --git a/content/browser/renderer_host/dwrite_font_proxy_impl_win.cc b/content/browser/renderer_host/dwrite_font_proxy_impl_win.cc index f636cde9ec134..6a4ca9d02de9a 100644 --- a/content/browser/renderer_host/dwrite_font_proxy_impl_win.cc +++ b/content/browser/renderer_host/dwrite_font_proxy_impl_win.cc @@ -36,6 +36,9 @@ #include "third_party/abseil-cpp/absl/utility/utility.h" #include "third_party/blink/public/common/font_unique_name_lookup/font_unique_name_table.pb.h" #include "third_party/blink/public/common/font_unique_name_lookup/icu_fold_case_util.h" +#include "third_party/skia/include/core/SkFontMgr.h" +#include "third_party/skia/include/core/SkTypeface.h" +#include "third_party/skia/include/ports/SkTypeface_win.h" #include "ui/gfx/win/direct_write.h" #include "ui/gfx/win/text_analysis_source.h" @@ -461,6 +464,25 @@ void DWriteFontProxyImpl::GetUniqueNameLookupTableIfAvailable( DWriteFontLookupTableBuilder::GetInstance()->DuplicateMemoryRegion()); } +void DWriteFontProxyImpl::GetUniqueFontLookupMode( + GetUniqueFontLookupModeCallback callback) { + InitializeDirectWrite(); + // If factory3_ is available, that means we can use IDWriteFontSet to filter + // for PostScript name and full font name directly and do not need to build + // the lookup table. + blink::mojom::UniqueFontLookupMode lookup_mode = + factory3_.Get() ? blink::mojom::UniqueFontLookupMode::kSingleLookups + : blink::mojom::UniqueFontLookupMode::kRetrieveTable; + std::move(callback).Run(lookup_mode); +} + +void DWriteFontProxyImpl::GetUniqueNameLookupTable( + GetUniqueNameLookupTableCallback callback) { + DCHECK(base::FeatureList::IsEnabled(features::kFontSrcLocalMatching)); + DWriteFontLookupTableBuilder::GetInstance()->QueueShareMemoryRegionWhenReady( + base::SequencedTaskRunner::GetCurrentDefault(), std::move(callback)); +} + void DWriteFontProxyImpl::MatchUniqueFont( const std::u16string& unique_font_name, MatchUniqueFontCallback callback) { @@ -557,25 +579,6 @@ void DWriteFontProxyImpl::MatchUniqueFont( std::move(callback).Run(std::move(font_file), ttc_index); } -void DWriteFontProxyImpl::GetUniqueFontLookupMode( - GetUniqueFontLookupModeCallback callback) { - InitializeDirectWrite(); - // If factory3_ is available, that means we can use IDWriteFontSet to filter - // for PostScript name and full font name directly and do not need to build - // the lookup table. - blink::mojom::UniqueFontLookupMode lookup_mode = - factory3_.Get() ? blink::mojom::UniqueFontLookupMode::kSingleLookups - : blink::mojom::UniqueFontLookupMode::kRetrieveTable; - std::move(callback).Run(lookup_mode); -} - -void DWriteFontProxyImpl::GetUniqueNameLookupTable( - GetUniqueNameLookupTableCallback callback) { - DCHECK(base::FeatureList::IsEnabled(features::kFontSrcLocalMatching)); - DWriteFontLookupTableBuilder::GetInstance()->QueueShareMemoryRegionWhenReady( - base::SequencedTaskRunner::GetCurrentDefault(), std::move(callback)); -} - void DWriteFontProxyImpl::FallbackFamilyAndStyleForCodepoint( const std::string& base_family_name, const std::string& locale_name, diff --git a/content/browser/renderer_host/dwrite_font_proxy_impl_win.h b/content/browser/renderer_host/dwrite_font_proxy_impl_win.h index a5c4d44998d4f..3beaa0fb58fef 100644 --- a/content/browser/renderer_host/dwrite_font_proxy_impl_win.h +++ b/content/browser/renderer_host/dwrite_font_proxy_impl_win.h @@ -64,7 +64,6 @@ class CONTENT_EXPORT DWriteFontProxyImpl MapCharactersCallback callback) override; void MatchUniqueFont(const std::u16string& unique_font_name, MatchUniqueFontCallback callback) override; - void GetUniqueFontLookupMode( GetUniqueFontLookupModeCallback callback) override; diff --git a/content/browser/renderer_host/dwrite_font_proxy_impl_win_unittest.cc b/content/browser/renderer_host/dwrite_font_proxy_impl_win_unittest.cc index 5f8c1b00295ce..152f73280e000 100644 --- a/content/browser/renderer_host/dwrite_font_proxy_impl_win_unittest.cc +++ b/content/browser/renderer_host/dwrite_font_proxy_impl_win_unittest.cc @@ -25,6 +25,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "third_party/blink/public/common/font_unique_name_lookup/font_table_matcher.h" #include "third_party/icu/source/common/unicode/umachine.h" +#include "ui/gfx/test/font_fallback_test_data.h" namespace content { @@ -182,6 +183,32 @@ TEST_F(DWriteFontProxyImplUnitTest, TestCustomFontFiles) { } } +TEST_F(DWriteFontProxyImplUnitTest, FallbackFamily) { + for (auto& fallback_request : gfx::kGetFontFallbackTests) { + blink::mojom::FallbackFamilyAndStylePtr fallback_family_and_style; + UChar32 codepoint; + U16_GET(fallback_request.text.c_str(), 0, 0, fallback_request.text.size(), + codepoint); + dwrite_font_proxy().FallbackFamilyAndStyleForCodepoint( + "Times New Roman", fallback_request.language_tag, codepoint, + &fallback_family_and_style); + + EXPECT_TRUE(base::Contains(fallback_request.fallback_fonts, + fallback_family_and_style->fallback_family_name)) + << "Did not find expected fallback font for language: " + << fallback_request.language_tag << ", codepoint U+" << std::hex + << codepoint << " DWrite returned font name: \"" + << fallback_family_and_style->fallback_family_name << "\"" + << ", expected: " + << base::JoinString(fallback_request.fallback_fonts, ", "); + EXPECT_EQ(fallback_family_and_style->weight, 400u); + EXPECT_EQ(fallback_family_and_style->width, + 5u); // SkFontStyle::Width::kNormal_Width + EXPECT_EQ(fallback_family_and_style->slant, + 0u); // SkFontStyle::Slant::kUpright_Slant + } +} + } // namespace } // namespace content diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc index 522868ab9b0e6..36ce4866d9e7a 100644 --- a/content/browser/renderer_host/render_process_host_impl.cc +++ b/content/browser/renderer_host/render_process_host_impl.cc @@ -3465,6 +3465,7 @@ void RenderProcessHostImpl::PropagateBrowserCommandLineToRenderer( switches::kRendererWaitForJavaDebugger, #endif #if BUILDFLAG(IS_WIN) + switches::kDisableDirectWrite, switches::kDisableHighResTimer, switches::kTrySupportedChannelLayouts, switches::kRaiseTimerFrequency, diff --git a/content/child/dwrite_font_proxy/dwrite_font_proxy_init_impl_win.cc b/content/child/dwrite_font_proxy/dwrite_font_proxy_init_impl_win.cc index a529ad9aac0b9..62a7b97e33d95 100644 --- a/content/child/dwrite_font_proxy/dwrite_font_proxy_init_impl_win.cc +++ b/content/child/dwrite_font_proxy/dwrite_font_proxy_init_impl_win.cc @@ -53,7 +53,7 @@ void CreateDirectWriteFactory(IDWriteFactory** factory) { void InitializeDWriteFontProxy() { TRACE_EVENT0("dwrite,fonts", "InitializeDWriteFontProxy"); mswr::ComPtr factory; - + CreateDirectWriteFactory(&factory); if (!g_font_collection) { @@ -79,7 +79,7 @@ void InitializeDWriteFontProxy() { skia::OverrideDefaultSkFontMgr(std::move(skia_font_manager)); - DCHECK(g_font_fallback); + blink::WebFontRendering::SetUseSkiaFontFallback(g_font_fallback ? true : false); } void UninitializeDWriteFontProxy() { diff --git a/content/common/sandbox_init_win.cc b/content/common/sandbox_init_win.cc index 498f60227d13e..724ce1120b720 100644 --- a/content/common/sandbox_init_win.cc +++ b/content/common/sandbox_init_win.cc @@ -17,6 +17,7 @@ #include "sandbox/policy/win/sandbox_win.h" #include "sandbox/win/src/sandbox.h" #include "sandbox/win/src/sandbox_types.h" +#include "ui/gfx/win/direct_write.h" namespace content { diff --git a/content/ppapi_plugin/ppapi_plugin_main.cc b/content/ppapi_plugin/ppapi_plugin_main.cc index 89e84b2c25c52..8af071d123a33 100644 --- a/content/ppapi_plugin/ppapi_plugin_main.cc +++ b/content/ppapi_plugin/ppapi_plugin_main.cc @@ -172,7 +172,12 @@ int PpapiPluginMain(MainFunctionParams parameters) { #if BUILDFLAG(IS_WIN) if (!base::win::IsUser32AndGdi32Available()) gfx::win::InitializeDirectWrite(); - InitializeDWriteFontProxy(); + bool use_direct_write = gfx::win::IsDirectWriteEnabled(); + if (use_direct_write) { + InitializeDWriteFontProxy(); + } + + blink::WebFontRendering::setUseDirectWrite(use_direct_write); int antialiasing_enabled = 1; base::StringToInt( diff --git a/content/renderer/renderer_main_platform_delegate_win.cc b/content/renderer/renderer_main_platform_delegate_win.cc index 7a493adbd504c..42398a1ddeba6 100644 --- a/content/renderer/renderer_main_platform_delegate_win.cc +++ b/content/renderer/renderer_main_platform_delegate_win.cc @@ -27,6 +27,18 @@ #include "ui/gfx/win/direct_write.h" namespace content { +namespace { + +// Windows-only skia sandbox support +// These are used for GDI-path rendering. +void SkiaPreCacheFont(const LOGFONT& logfont) { + RenderThread* render_thread = RenderThread::Get(); + if (render_thread) { + render_thread->PreCacheFont(logfont); + } +} + +} // namespace RendererMainPlatformDelegate::RendererMainPlatformDelegate( const MainFunctionParams& parameters) @@ -40,8 +52,16 @@ void RendererMainPlatformDelegate::PlatformInitialize() { // Be mindful of what resources you acquire here. They can be used by // malicious code if the renderer gets compromised. - bool no_sandbox = - command_line.HasSwitch(sandbox::policy::switches::kNoSandbox); + bool no_sandbox =command_line.HasSwitch(sandbox::policy::switches::kNoSandbox); + + bool use_direct_write = gfx::win::ShouldUseDirectWrite(); + + if (use_direct_write) { + InitializeDWriteFontProxy(); + } else { + SkTypeface_SetEnsureLOGFONTAccessibleProc(SkiaPreCacheFont); + } + blink::WebFontRendering::setUseDirectWrite(use_direct_write); if (!no_sandbox) { // ICU DateFormat class (used in base/time_format.cc) needs to get the diff --git a/content/shell/browser/shell_content_browser_client.cc b/content/shell/browser/shell_content_browser_client.cc index 477b6208acdca..1a96ed330dd9e 100644 --- a/content/shell/browser/shell_content_browser_client.cc +++ b/content/shell/browser/shell_content_browser_client.cc @@ -14,6 +14,7 @@ #include "base/base_switches.h" #include "base/command_line.h" +#include "base/features.h" #include "base/containers/cxx20_erase.h" #include "base/containers/flat_set.h" #include "base/feature_list.h" @@ -61,7 +62,6 @@ #include "content/public/browser/web_contents_view_delegate.h" #include "content/public/common/content_features.h" #include "content/public/common/content_switch_dependent_feature_overrides.h" -#include "content/public/common/content_switches.h" #include "content/public/common/url_constants.h" #include "content/public/common/user_agent.h" #include "content/shell/browser/shell.h" @@ -518,7 +518,7 @@ void ShellContentBrowserClient::OverrideWebkitPrefs( WebContents* web_contents, blink::web_pref::WebPreferences* prefs) { if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kForceDarkMode)) { + switches::kForceDarkMode) || base::FeatureList::IsEnabled(base::features::kForceDarkModeFlag)) { prefs->preferred_color_scheme = blink::mojom::PreferredColorScheme::kDark; } else { prefs->preferred_color_scheme = blink::mojom::PreferredColorScheme::kLight; diff --git a/content/test/dwrite_font_fake_sender_win.cc b/content/test/dwrite_font_fake_sender_win.cc index bed299f10f56f..3d1b5f57984d6 100644 --- a/content/test/dwrite_font_fake_sender_win.cc +++ b/content/test/dwrite_font_fake_sender_win.cc @@ -146,6 +146,12 @@ void FakeFontCollection::MapCharacters( void FakeFontCollection::MatchUniqueFont(const std::u16string& unique_font_name, MatchUniqueFontCallback callback) {} +void FakeFontCollection::FallbackFamilyAndStyleForCodepoint( + const std::string& base_family_name, + const std::string& locale_name, + uint32_t codepoint, + FallbackFamilyAndStyleForCodepointCallback callback) {} + FakeFontCollection::~FakeFontCollection() = default; } // namespace content diff --git a/content/test/dwrite_font_fake_sender_win.h b/content/test/dwrite_font_fake_sender_win.h index e5c7230ffcaad..3906ff44e64a6 100644 --- a/content/test/dwrite_font_fake_sender_win.h +++ b/content/test/dwrite_font_fake_sender_win.h @@ -125,6 +125,11 @@ class FakeFontCollection : public blink::mojom::DWriteFontProxy { MapCharactersCallback callback) override; void MatchUniqueFont(const std::u16string& unique_font_name, MatchUniqueFontCallback callback) override; + void FallbackFamilyAndStyleForCodepoint( + const std::string& base_family_name, + const std::string& locale_name, + uint32_t codepoint, + FallbackFamilyAndStyleForCodepointCallback callback) override; private: std::vector fonts_; diff --git a/content/web_test/browser/web_test_control_host.cc b/content/web_test/browser/web_test_control_host.cc index 60bc586693ed0..27c9ce52cb581 100644 --- a/content/web_test/browser/web_test_control_host.cc +++ b/content/web_test/browser/web_test_control_host.cc @@ -19,6 +19,7 @@ #include "base/barrier_closure.h" #include "base/base64.h" #include "base/command_line.h" +#include "base/features.h" #include "base/containers/contains.h" #include "base/functional/bind.h" #include "base/functional/callback.h" @@ -772,7 +773,7 @@ void WebTestControlHost::OverrideWebkitPrefs( ApplyWebTestDefaultPreferences(prefs); } if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kForceDarkMode)) { + switches::kForceDarkMode) || base::FeatureList::IsEnabled(base::features::kForceDarkModeFlag)) { prefs->preferred_color_scheme = blink::mojom::PreferredColorScheme::kDark; } else { prefs->preferred_color_scheme = blink::mojom::PreferredColorScheme::kLight; diff --git a/docs/README.md b/docs/README.md index 08f0375d8cb5f..eee1a9bc043e7 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,20 +1,33 @@ -# Chromium docs +# Supermium - an up-to-date, open-source browser for legacy Windows users -This directory contains [chromium project](https://www.chromium.org/Home/) -documentation in [Gitiles-flavored Markdown]. It is automatically -[rendered by Gitiles]. +The aim of Supermium is to provide the latest Chromium features, support for rendering the web and security patches to users +of Windows Vista (with extended kernel), Windows 7 and Windows 8.x. -[Gitiles-flavored Markdown]: https://gerrit.googlesource.com/gitiles/+/master/Documentation/markdown.md -[rendered by Gitiles]: https://chromium.googlesource.com/chromium/src/+/main/docs/ +Releases are expected every one to two months. -**If you add new documents, please also add a link to them in the Document Index -below.** +# Support this and other legacy software projects at: http://paypal.me/win32420 -[TOC] +# Future objectives -## Creating Documentation +-Maintaining support for Manifest v2 -### Guidelines +-Supporting Windows Vista (without extended kernel) + +-Restoring GDI font rendering + +-Adding basic GUI to installer to allow user to pick an install directory + +-Adding trapezoidal tabs option + +-"ungoogled" mode + +# Future objectives of lower priority + +-Supporting Windows XP + +-SSE2 build + +Upstream README.md follows below: * See the [Chromium Documentation Guidelines](documentation_guidelines.md) and the @@ -453,4 +466,4 @@ used when committed. * [Vanilla msysgit workflow](vanilla_msysgit_workflow.md) - A workflow for using mostly vanilla git on Windows. * [Old Options](chrome_settings.md) - Pre-Material Design chrome://settings - notes. + notes. \ No newline at end of file diff --git a/docs/website b/docs/website index 5943ad5e004c1..243c140619996 160000 --- a/docs/website +++ b/docs/website @@ -1 +1 @@ -Subproject commit 5943ad5e004c1cde2f869f1d67008a729e9ec12e +Subproject commit 243c1406199960e001e1dceccafc6fc2db6897f9 diff --git a/google_apis/google_api_keys.cc b/google_apis/google_api_keys.cc index dd27a1b0f7618..2718e84ea9c59 100644 --- a/google_apis/google_api_keys.cc +++ b/google_apis/google_api_keys.cc @@ -37,7 +37,7 @@ #define DUMMY_API_TOKEN "dummytoken" #if !defined(GOOGLE_API_KEY) -#define GOOGLE_API_KEY DUMMY_API_TOKEN +#define GOOGLE_API_KEY "AIzaSyBOti4mM-6x9WDnZIjIeyEU21OpBXqWBgw" #endif #if !defined(GOOGLE_METRICS_SIGNING_KEY) @@ -45,11 +45,11 @@ #endif #if !defined(GOOGLE_CLIENT_ID_MAIN) -#define GOOGLE_CLIENT_ID_MAIN DUMMY_API_TOKEN +#define GOOGLE_CLIENT_ID_MAIN "77185425430.apps.googleusercontent.com" #endif #if !defined(GOOGLE_CLIENT_SECRET_MAIN) -#define GOOGLE_CLIENT_SECRET_MAIN DUMMY_API_TOKEN +#define GOOGLE_CLIENT_SECRET_MAIN "OTJgUOQcT7lO7GsGZq2G4IlT" #endif #if !defined(GOOGLE_CLIENT_ID_REMOTING) diff --git a/net/third_party/quiche/src b/net/third_party/quiche/src index 12dae956d9720..7d201495d8a5e 160000 --- a/net/third_party/quiche/src +++ b/net/third_party/quiche/src @@ -1 +1 @@ -Subproject commit 12dae956d97203cd2abbb7b33db641fca0115f77 +Subproject commit 7d201495d8a5ef115787765fb12af0d5575281c1 diff --git a/sandbox/policy/win/sandbox_warmup.cc b/sandbox/policy/win/sandbox_warmup.cc index 1c5142a49ceb7..fa36708b609a6 100644 --- a/sandbox/policy/win/sandbox_warmup.cc +++ b/sandbox/policy/win/sandbox_warmup.cc @@ -18,48 +18,16 @@ #include #undef SystemFunction036 -// Prototype for ProcessPrng. -// See: https://learn.microsoft.com/en-us/windows/win32/seccng/processprng -extern "C" { -BOOL WINAPI ProcessPrng(PBYTE pbData, SIZE_T cbData); -} - namespace sandbox::policy { -namespace { - -// Import bcryptprimitives!ProcessPrng rather than cryptbase!RtlGenRandom to -// avoid opening a handle to \\Device\KsecDD in the renderer. -decltype(&ProcessPrng) GetProcessPrng() { - HMODULE hmod = LoadLibraryW(L"bcryptprimitives.dll"); - CHECK(hmod); - decltype(&ProcessPrng) process_prng_fn = - reinterpret_cast( - GetProcAddress(hmod, "ProcessPrng")); - CHECK(process_prng_fn); - return process_prng_fn; -} - -} // namespace - void WarmupRandomnessInfrastructure() { BYTE data[1]; - - if (base::FeatureList::IsEnabled( - sandbox::policy::features::kWinSboxWarmupProcessPrng)) { - // TODO(crbug.com/74242) Call a warmup function exposed by boringssl. - static decltype(&ProcessPrng) process_prng_fn = GetProcessPrng(); - BOOL success = process_prng_fn(data, sizeof(data)); - // ProcessPrng is documented to always return TRUE. - CHECK(success); - } else { // This loads advapi!SystemFunction036 which is forwarded to // cryptbase!SystemFunction036. This allows boringsll and Chrome to call // RtlGenRandom from within the sandbox. This has the unfortunate side // effect of opening a handle to \\Device\KsecDD which we will later close // in processes that do not need this. RtlGenRandom(data, sizeof(data)); - } } } // namespace sandbox::policy diff --git a/sandbox/policy/win/sandbox_win.cc b/sandbox/policy/win/sandbox_win.cc index bb03c880256fc..8943d7863213b 100644 --- a/sandbox/policy/win/sandbox_win.cc +++ b/sandbox/policy/win/sandbox_win.cc @@ -64,6 +64,7 @@ #include "sandbox/win/src/process_mitigations.h" #include "sandbox/win/src/sandbox.h" #include "third_party/abseil-cpp/absl/types/optional.h" +#include "ui/gfx/win/direct_write.h" namespace sandbox { namespace policy { @@ -176,19 +177,20 @@ bool AddWindowsFontsDir(TargetConfig* config) { if (!base::PathService::Get(base::DIR_WINDOWS_FONTS, &directory)) { return false; } - - ResultCode result = - config->AddRule(SubSystem::kFiles, Semantics::kFilesAllowReadonly, - directory.value().c_str()); - if (result != SBOX_ALL_OK) - return false; - - std::wstring directory_str = directory.value() + L"\\*"; - result = config->AddRule(SubSystem::kFiles, Semantics::kFilesAllowReadonly, - directory_str.c_str()); - if (result != SBOX_ALL_OK) - return false; - + + if (gfx::win::ShouldUseDirectWrite()) { + ResultCode result = + config->AddRule(SubSystem::kFiles, Semantics::kFilesAllowReadonly, + directory.value().c_str()); + if (result != SBOX_ALL_OK) + return false; + + std::wstring directory_str = directory.value() + L"\\*"; + result = config->AddRule(SubSystem::kFiles, Semantics::kFilesAllowReadonly, + directory_str.c_str()); + if (result != SBOX_ALL_OK) + return false; + } return true; } #endif // !defined(NACL_WIN64) @@ -578,6 +580,12 @@ ResultCode GenerateConfigForSandboxedProcess(const base::CommandLine& cmd_line, MITIGATION_SEHOP | MITIGATION_NONSYSTEM_FONT_DISABLE | MITIGATION_IMAGE_LOAD_NO_REMOTE | MITIGATION_IMAGE_LOAD_NO_LOW_LABEL | MITIGATION_RESTRICT_INDIRECT_BRANCH_PREDICTION | MITIGATION_KTM_COMPONENT; + + #if !defined(NACL_WIN64) + // Don't block font loading with GDI. + if (!gfx::win::ShouldUseDirectWrite()) + mitigations ^= sandbox::MITIGATION_NONSYSTEM_FONT_DISABLE; + #endif // CET is enabled with the CETCOMPAT bit on chrome.exe so must be // disabled for processes we know are not compatible. diff --git a/sandbox/win/src/broker_services.cc b/sandbox/win/src/broker_services.cc index 9dfe4b3d8ba21..62ca137396f14 100644 --- a/sandbox/win/src/broker_services.cc +++ b/sandbox/win/src/broker_services.cc @@ -451,7 +451,11 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path, if (container) startup_info->SetAppContainer(container); - startup_info->AddJobToAssociate(policy_base->GetJobHandle()); + // On Win10, jobs are associated via startup_info. + if (base::win::GetVersion() >= base::win::Version::WIN10 && + policy_base->HasJob()) { + startup_info->AddJobToAssociate(policy_base->GetJobHandle()); + } if (!startup_info->BuildStartupInformation()) return SBOX_ERROR_PROC_THREAD_ATTRIBUTES; diff --git a/sandbox/win/src/handle_closer_agent.cc b/sandbox/win/src/handle_closer_agent.cc index 11f19502adc0f..0c922b0599314 100644 --- a/sandbox/win/src/handle_closer_agent.cc +++ b/sandbox/win/src/handle_closer_agent.cc @@ -10,6 +10,7 @@ #include "base/logging.h" #include "base/win/static_constants.h" #include "base/win/win_util.h" +#include "base/win/windows_version.h" #include "sandbox/win/src/win_utils.h" #include "third_party/abseil-cpp/absl/types/optional.h" @@ -149,8 +150,11 @@ bool HandleCloserAgent::CloseHandles() { return true; absl::optional handle_map = GetCurrentProcessHandles(); - if (!handle_map) - return false; + // Fallback for pre-Windows 8.1. + if (!handle_map) { + DCHECK(base::win::GetVersion() < base::win::Version::WIN8_1); + handle_map = GetCurrentProcessHandlesWin7(); + } for (const HandleMap::value_type& handle_to_close : handles_to_close_) { ProcessHandleMap::iterator result = handle_map->find(handle_to_close.first); diff --git a/sandbox/win/src/interception.cc b/sandbox/win/src/interception.cc index f9f4f061aeec0..a8edb9ef8a774 100644 --- a/sandbox/win/src/interception.cc +++ b/sandbox/win/src/interception.cc @@ -19,6 +19,7 @@ #include "base/rand_util.h" #include "base/scoped_native_library.h" #include "base/win/pe_image.h" +#include "base/win/windows_version.h" #include "sandbox/win/src/interception_internal.h" #include "sandbox/win/src/interceptors.h" #include "sandbox/win/src/internal_types.h" @@ -427,7 +428,25 @@ ResultCode InterceptionManager::PatchClientFunctions( if (!ntdll_base) return SBOX_ERROR_NO_HANDLE; - ServiceResolverThunk thunk(child_->Process(), /*relaxed=*/true); + std::unique_ptr thunk; +#if defined(_WIN64) + thunk = std::make_unique(child_->Process(), true); +#else + base::win::OSInfo* os_info = base::win::OSInfo::GetInstance(); + base::win::Version real_os_version = os_info->Kernel32Version(); + if (os_info->IsWowX86OnAMD64()) { + if (real_os_version >= base::win::Version::WIN10) + thunk.reset(new Wow64W10ResolverThunk(child_->Process(), true)); + else if (real_os_version >= base::win::Version::WIN8) + thunk.reset(new Wow64W8ResolverThunk(child_->Process(), true)); + else + thunk.reset(new Wow64ResolverThunk(child_->Process(), true)); + } else if (real_os_version >= base::win::Version::WIN8) { + thunk.reset(new Win8ResolverThunk(child_->Process(), true)); + } else { + thunk.reset(new ServiceResolverThunk(child_->Process(), true)); + } +#endif for (auto interception : interceptions_) { const std::wstring ntdll(kNtdllName); @@ -437,7 +456,7 @@ ResultCode InterceptionManager::PatchClientFunctions( if (INTERCEPTION_SERVICE_CALL != interception.type) return SBOX_ERROR_BAD_PARAMS; - NTSTATUS ret = thunk.Setup( + NTSTATUS ret = thunk->Setup( ntdll_base, nullptr, interception.function.c_str(), interception.interceptor.c_str(), interception.interceptor_address, &thunks->thunks[dll_data->num_thunks], diff --git a/sandbox/win/src/process_mitigations.cc b/sandbox/win/src/process_mitigations.cc index 7817726d0ee8d..8556757b0b65e 100644 --- a/sandbox/win/src/process_mitigations.cc +++ b/sandbox/win/src/process_mitigations.cc @@ -24,6 +24,7 @@ #include "sandbox/win/src/nt_internals.h" #include "sandbox/win/src/restricted_token_utils.h" #include "sandbox/win/src/win_utils.h" +#include "ui/gfx/win/direct_write.h" // These are missing in 10.0.19551.0 but are in 10.0.19041.0 and 10.0.20226.0. #ifndef PROCESS_CREATION_MITIGATION_POLICY2_CET_USER_SHADOW_STACKS_STRICT_MODE @@ -115,6 +116,12 @@ bool ApplyProcessMitigationsToCurrentProcess(MitigationFlags starting_flags, return true; base::win::Version version = base::win::GetVersion(); + + #if !defined(NACL_WIN64) + // Don't block font loading with GDI. + if (!gfx::win::ShouldUseDirectWrite()) + flags ^= sandbox::MITIGATION_NONSYSTEM_FONT_DISABLE; + #endif if (flags & MITIGATION_DLL_SEARCH_ORDER) { HMODULE module = ::GetModuleHandleA("kernel32.dll"); @@ -454,6 +461,12 @@ void ConvertProcessMitigationsToPolicy(MitigationFlags flags, if (version < base::win::Version::WIN8) return; + #if !defined(NACL_WIN64) + // Don't block font loading with GDI. + if (!gfx::win::ShouldUseDirectWrite()) + *policy_value_1 ^= sandbox::MITIGATION_NONSYSTEM_FONT_DISABLE; + #endif + // Everything >= Win8, do not return before the end of the function where // the final policy bitmap is sanity checked against what is supported on this // machine. The API required to do so is only available since Win8. diff --git a/sandbox/win/src/service_resolver.h b/sandbox/win/src/service_resolver.h index dc74bd6e52539..97f828e3feeb0 100644 --- a/sandbox/win/src/service_resolver.h +++ b/sandbox/win/src/service_resolver.h @@ -53,23 +53,18 @@ class [[clang::lto_visibility_public]] ServiceResolverThunk size_t GetThunkSize() const override; // Call this to set up ntdll_base_ which will allow for local patches. - void AllowLocalPatches(); + virtual void AllowLocalPatches(); // Verifies that the function specified by |target_name| in |target_module| is // a service and copies the data from that function into |thunk_storage|. If // |storage_bytes| is too small, then the method fails. - NTSTATUS CopyThunk(const void* target_module, - const char* target_name, - BYTE* thunk_storage, - size_t storage_bytes, - size_t* storage_used); + virtual NTSTATUS CopyThunk(const void* target_module, + const char* target_name, + BYTE* thunk_storage, + size_t storage_bytes, + size_t* storage_used); - // Checks if a target was patched correctly for a jump. This is only for use - // in testing in 32-bit builds. Will always return true on 64-bit builds. Set - // |thunk_storage| to the same pointer passed to Setup(). - bool VerifyJumpTargetForTesting(void* thunk_storage) const; - - private: + protected: // The unit test will use this member to allow local patch on a buffer. HMODULE ntdll_base_; @@ -87,15 +82,15 @@ class [[clang::lto_visibility_public]] ServiceResolverThunk // Returns true if the code pointer by target_ corresponds to the expected // type of function. Saves that code on the first part of the thunk pointed // by local_thunk (should be directly accessible from the parent). - bool IsFunctionAService(void* local_thunk) const; + virtual bool IsFunctionAService(void* local_thunk) const; // Performs the actual patch of target_. // local_thunk must be already fully initialized, and the first part must // contain the original code. The real type of this buffer is ServiceFullThunk // (yes, private). remote_thunk (real type ServiceFullThunk), must be // allocated on the child, and will contain the thunk data, after this call. - // Returns the appropriate status code. - NTSTATUS PerformPatch(void* local_thunk, void* remote_thunk); + // Returns the apropriate status code. + virtual NTSTATUS PerformPatch(void* local_thunk, void* remote_thunk); // Provides basically the same functionality as IsFunctionAService but it // continues even if it does not recognize the function code. remote_thunk @@ -107,6 +102,74 @@ class [[clang::lto_visibility_public]] ServiceResolverThunk ULONG relative_jump_; }; +// This is the concrete resolver used to perform service-call type functions +// inside ntdll.dll on WOW64 (32 bit ntdll on 64 bit Vista). +class Wow64ResolverThunk : public ServiceResolverThunk { + public: + // The service resolver needs a child process to write to. + Wow64ResolverThunk(HANDLE process, bool relaxed) + : ServiceResolverThunk(process, relaxed) {} + + Wow64ResolverThunk(const Wow64ResolverThunk&) = delete; + Wow64ResolverThunk& operator=(const Wow64ResolverThunk&) = delete; + + ~Wow64ResolverThunk() override {} + + private: + bool IsFunctionAService(void* local_thunk) const override; +}; + +// This is the concrete resolver used to perform service-call type functions +// inside ntdll.dll on WOW64 for Windows 8. +class Wow64W8ResolverThunk : public ServiceResolverThunk { + public: + // The service resolver needs a child process to write to. + Wow64W8ResolverThunk(HANDLE process, bool relaxed) + : ServiceResolverThunk(process, relaxed) {} + + Wow64W8ResolverThunk(const Wow64W8ResolverThunk&) = delete; + Wow64W8ResolverThunk& operator=(const Wow64W8ResolverThunk&) = delete; + + ~Wow64W8ResolverThunk() override {} + + private: + bool IsFunctionAService(void* local_thunk) const override; +}; + +// This is the concrete resolver used to perform service-call type functions +// inside ntdll.dll on Windows 8. +class Win8ResolverThunk : public ServiceResolverThunk { + public: + // The service resolver needs a child process to write to. + Win8ResolverThunk(HANDLE process, bool relaxed) + : ServiceResolverThunk(process, relaxed) {} + + Win8ResolverThunk(const Win8ResolverThunk&) = delete; + Win8ResolverThunk& operator=(const Win8ResolverThunk&) = delete; + + ~Win8ResolverThunk() override {} + + private: + bool IsFunctionAService(void* local_thunk) const override; +}; + +// This is the concrete resolver used to perform service-call type functions +// inside ntdll.dll on WOW64 for Windows 10. +class Wow64W10ResolverThunk : public ServiceResolverThunk { + public: + // The service resolver needs a child process to write to. + Wow64W10ResolverThunk(HANDLE process, bool relaxed) + : ServiceResolverThunk(process, relaxed) {} + + Wow64W10ResolverThunk(const Wow64W10ResolverThunk&) = delete; + Wow64W10ResolverThunk& operator=(const Wow64W10ResolverThunk&) = delete; + + ~Wow64W10ResolverThunk() override {} + + private: + bool IsFunctionAService(void* local_thunk) const override; +}; + } // namespace sandbox #endif // SANDBOX_WIN_SRC_SERVICE_RESOLVER_H_ diff --git a/sandbox/win/src/service_resolver_32.cc b/sandbox/win/src/service_resolver_32.cc index cb43a3d053a74..dcf787f76f033 100644 --- a/sandbox/win/src/service_resolver_32.cc +++ b/sandbox/win/src/service_resolver_32.cc @@ -20,16 +20,43 @@ namespace { const BYTE kMovEax = 0xB8; const BYTE kMovEdx = 0xBA; const USHORT kMovEdxEsp = 0xD48B; +const USHORT kCallPtrEdx = 0x12FF; const USHORT kCallEdx = 0xD2FF; const BYTE kCallEip = 0xE8; const BYTE kRet = 0xC2; const BYTE kRet2 = 0xC3; const USHORT kJmpEdx = 0xE2FF; +const USHORT kXorEcx = 0xC933; +const ULONG kLeaEdx = 0x0424548D; +const ULONG kCallFs1 = 0xC015FF64; +const USHORT kCallFs2 = 0; +const BYTE kCallFs3 = 0; +const BYTE kAddEsp1 = 0x83; +const USHORT kAddEsp2 = 0x4C4; const BYTE kJmp32 = 0xE9; const USHORT kSysenter = 0x340F; -// Service code for 32 bit Windows. Introduced in Windows 8. -struct ServiceEntry32 { +// Service code for 32 bit systems. +// NOTE: on win2003 "call dword ptr [edx]" is "call edx". +struct ServiceEntry { + // This struct contains roughly the following code: + // 00 mov eax,25h + // 05 mov edx,offset SharedUserData!SystemCallStub (7ffe0300) + // 0a call dword ptr [edx] + // 0c ret 2Ch + // 0f nop + BYTE mov_eax; // = B8 + ULONG service_id; + BYTE mov_edx; // = BA + ULONG stub; + USHORT call_ptr_edx; // = FF 12 + BYTE ret; // = C2 + USHORT num_params; + BYTE nop; +}; + +// Service code for 32 bit Windows 8. +struct ServiceEntryW8 { // This struct contains the following code: // 00 b825000000 mov eax,25h // 05 e803000000 call eip+3 @@ -50,13 +77,60 @@ struct ServiceEntry32 { USHORT nop; }; -// Service code for a 32 bit process under Wow64. Introduced in Windows 10. -// Also used for the patching process. -struct ServiceEntryWow64 { +// Service code for a 32 bit process running on a 64 bit os. +struct Wow64Entry { + // This struct may contain one of two versions of code: + // 1. For XP, Vista and 2K3: + // 00 b825000000 mov eax, 25h + // 05 33c9 xor ecx, ecx + // 07 8d542404 lea edx, [esp + 4] + // 0b 64ff15c0000000 call dword ptr fs:[0C0h] + // 12 c22c00 ret 2Ch + // + // 2. For Windows 7: + // 00 b825000000 mov eax, 25h + // 05 33c9 xor ecx, ecx + // 07 8d542404 lea edx, [esp + 4] + // 0b 64ff15c0000000 call dword ptr fs:[0C0h] + // 12 83c404 add esp, 4 + // 15 c22c00 ret 2Ch + // + // So we base the structure on the bigger one: + BYTE mov_eax; // = B8 + ULONG service_id; + USHORT xor_ecx; // = 33 C9 + ULONG lea_edx; // = 8D 54 24 04 + ULONG call_fs1; // = 64 FF 15 C0 + USHORT call_fs2; // = 00 00 + BYTE call_fs3; // = 00 + BYTE add_esp1; // = 83 or ret + USHORT add_esp2; // = C4 04 or num_params + BYTE ret; // = C2 + USHORT num_params; +}; + +// Service code for a 32 bit process running on 64 bit Windows 8. +struct Wow64EntryW8 { + // 00 b825000000 mov eax, 25h + // 05 64ff15c0000000 call dword ptr fs:[0C0h] + // 0b c22c00 ret 2Ch + // 0f 90 nop + BYTE mov_eax; // = B8 + ULONG service_id; + ULONG call_fs1; // = 64 FF 15 C0 + USHORT call_fs2; // = 00 00 + BYTE call_fs3; // = 00 + BYTE ret; // = C2 + USHORT num_params; + BYTE nop; +}; + +// Service code for a 32 bit process running on 64 bit Windows 10. +struct Wow64EntryW10 { // 00 b828000000 mov eax, 28h // 05 bab0d54877 mov edx, 7748D5B0h // 09 ffd2 call edx - // 0c c22800 ret 28h + // 0b c22800 ret 28h BYTE mov_eax; // = B8 ULONG service_id; BYTE mov_edx; // = BA @@ -64,86 +138,27 @@ struct ServiceEntryWow64 { USHORT call_edx; // = FF D2 BYTE ret; // = C2 USHORT num_params; - BYTE nop; }; // Make sure that relaxed patching works as expected. -const size_t kMinServiceSize = offsetof(ServiceEntryWow64, ret); -// Maximum size of the entry, was the size of the Windows Vista WoW64 entry. -// Keep this fixed for compatibility reasons. -const size_t kMaxServiceSize = 24; -static_assert(sizeof(ServiceEntry32) >= kMinServiceSize, - "wrong minimum service length"); -static_assert(sizeof(ServiceEntry32) < kMaxServiceSize, - "wrong maximum service length"); -static_assert(sizeof(ServiceEntryWow64) >= kMinServiceSize, - "wrong minimum service length"); -static_assert(sizeof(ServiceEntryWow64) < kMaxServiceSize, - "wrong maximum service length"); +const size_t kMinServiceSize = offsetof(ServiceEntry, ret); +static_assert(sizeof(ServiceEntryW8) >= kMinServiceSize, + "wrong service length"); +static_assert(sizeof(Wow64Entry) >= kMinServiceSize, "wrong service length"); +static_assert(sizeof(Wow64EntryW8) >= kMinServiceSize, "wrong service length"); struct ServiceFullThunk { union { - ServiceEntryWow64 original; - // Pad the entry to the maximum size. - char dummy[kMaxServiceSize]; + ServiceEntry original; + ServiceEntryW8 original_w8; + Wow64Entry wow_64; + Wow64EntryW8 wow_64_w8; }; int internal_thunk; // Dummy member to the beginning of the internal thunk. }; #pragma pack(pop) -bool IsWow64Process() { - // We don't need to use IsWow64Process2 as this returns the expected result - // when running in the ARM64 x86 emulator. - BOOL is_wow64 = FALSE; - return ::IsWow64Process(::GetCurrentProcess(), &is_wow64) && is_wow64; -} - -bool IsFunctionAService32(HANDLE process, void* target, void* local_thunk) { - ServiceEntry32 function_code; - SIZE_T read; - if (!::ReadProcessMemory(process, target, &function_code, - sizeof(function_code), &read)) { - return false; - } - - if (sizeof(function_code) != read) - return false; - - if (kMovEax != function_code.mov_eax || kCallEip != function_code.call_eip || - function_code.call_offset != 3 || kRet != function_code.ret_p || - kMovEdxEsp != function_code.mov_edx_esp || - kSysenter != function_code.sysenter || kRet2 != function_code.ret) { - return false; - } - - // Save the verified code - memcpy(local_thunk, &function_code, sizeof(function_code)); - - return true; -} - -bool IsFunctionAServiceWow64(HANDLE process, void* target, void* local_thunk) { - ServiceEntryWow64 function_code; - SIZE_T read; - if (!::ReadProcessMemory(process, target, &function_code, - sizeof(function_code), &read)) { - return false; - } - - if (sizeof(function_code) != read) - return false; - - if (kMovEax != function_code.mov_eax || kMovEdx != function_code.mov_edx || - kCallEdx != function_code.call_edx || kRet != function_code.ret) { - return false; - } - - // Save the verified code - memcpy(local_thunk, &function_code, sizeof(function_code)); - return true; -} - } // namespace namespace sandbox { @@ -212,14 +227,69 @@ NTSTATUS ServiceResolverThunk::CopyThunk(const void* target_module, } bool ServiceResolverThunk::IsFunctionAService(void* local_thunk) const { - static bool is_wow64 = IsWow64Process(); - return is_wow64 ? IsFunctionAServiceWow64(process_, target_, local_thunk) - : IsFunctionAService32(process_, target_, local_thunk); + ServiceEntry function_code; + SIZE_T read; + if (!::ReadProcessMemory(process_, target_, &function_code, + sizeof(function_code), &read)) { + return false; + } + + if (sizeof(function_code) != read) + return false; + + if (kMovEax != function_code.mov_eax || kMovEdx != function_code.mov_edx || + (kCallPtrEdx != function_code.call_ptr_edx && + kCallEdx != function_code.call_ptr_edx) || + kRet != function_code.ret) { + return false; + } + + // Find the system call pointer if we don't already have it. + if (kCallEdx != function_code.call_ptr_edx) { + DWORD ki_system_call; + if (!::ReadProcessMemory(process_, + base::bit_cast(function_code.stub), + &ki_system_call, sizeof(ki_system_call), &read)) { + return false; + } + + if (sizeof(ki_system_call) != read) + return false; + + HMODULE module_1, module_2; + // last check, call_stub should point to a KiXXSystemCall function on ntdll + if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + base::bit_cast(ki_system_call), + &module_1)) { + return false; + } + + if (ntdll_base_) { + // This path is only taken when running the unit tests. We want to be + // able to patch a buffer in memory, so target_ is not inside ntdll. + module_2 = ntdll_base_; + } else { + if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + reinterpret_cast(target_), + &module_2)) + return false; + } + + if (module_1 != module_2) + return false; + } + + // Save the verified code + memcpy(local_thunk, &function_code, sizeof(function_code)); + + return true; } NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk, void* remote_thunk) { - ServiceEntryWow64 intercepted_code; + ServiceEntry intercepted_code; size_t bytes_to_write = sizeof(intercepted_code); ServiceFullThunk* full_local_thunk = reinterpret_cast(local_thunk); @@ -232,15 +302,15 @@ NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk, intercepted_code.mov_eax = kMovEax; intercepted_code.service_id = full_local_thunk->original.service_id; intercepted_code.mov_edx = kMovEdx; - intercepted_code.mov_edx_param = + intercepted_code.stub = base::bit_cast(&full_remote_thunk->internal_thunk); - intercepted_code.call_edx = kJmpEdx; + intercepted_code.call_ptr_edx = kJmpEdx; bytes_to_write = kMinServiceSize; if (relative_jump_) { intercepted_code.mov_eax = kJmp32; intercepted_code.service_id = relative_jump_; - bytes_to_write = offsetof(ServiceEntryWow64, mov_edx); + bytes_to_write = offsetof(ServiceEntry, mov_edx); } // setup the thunk @@ -276,7 +346,7 @@ NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk, bool ServiceResolverThunk::SaveOriginalFunction(void* local_thunk, void* remote_thunk) { - ServiceEntryWow64 function_code; + ServiceEntry function_code; SIZE_T read; if (!::ReadProcessMemory(process_, target_, &function_code, sizeof(function_code), &read)) { @@ -312,18 +382,100 @@ bool ServiceResolverThunk::SaveOriginalFunction(void* local_thunk, return true; } -bool ServiceResolverThunk::VerifyJumpTargetForTesting( - void* thunk_storage) const { - const size_t kJmp32Size = 5; - ServiceEntryWow64* patched = static_cast(target_); - if (kJmp32 != patched->mov_eax) { +bool Wow64ResolverThunk::IsFunctionAService(void* local_thunk) const { + Wow64Entry function_code; + SIZE_T read; + if (!::ReadProcessMemory(process_, target_, &function_code, + sizeof(function_code), &read)) { + return false; + } + + if (sizeof(function_code) != read) + return false; + + if (kMovEax != function_code.mov_eax || kXorEcx != function_code.xor_ecx || + kLeaEdx != function_code.lea_edx || kCallFs1 != function_code.call_fs1 || + kCallFs2 != function_code.call_fs2 || + kCallFs3 != function_code.call_fs3) { + return false; + } + + if ((kAddEsp1 == function_code.add_esp1 && + kAddEsp2 == function_code.add_esp2 && kRet == function_code.ret) || + kRet == function_code.add_esp1) { + // Save the verified code + memcpy(local_thunk, &function_code, sizeof(function_code)); + return true; + } + + return false; +} + +bool Wow64W8ResolverThunk::IsFunctionAService(void* local_thunk) const { + Wow64EntryW8 function_code; + SIZE_T read; + if (!::ReadProcessMemory(process_, target_, &function_code, + sizeof(function_code), &read)) { return false; } - ULONG source_addr = base::bit_cast(target_); - ULONG target_addr = base::bit_cast(thunk_storage); - return target_addr + kMaxServiceSize - kJmp32Size - source_addr == - patched->service_id; + if (sizeof(function_code) != read) + return false; + + if (kMovEax != function_code.mov_eax || kCallFs1 != function_code.call_fs1 || + kCallFs2 != function_code.call_fs2 || + kCallFs3 != function_code.call_fs3 || kRet != function_code.ret) { + return false; + } + + // Save the verified code + memcpy(local_thunk, &function_code, sizeof(function_code)); + return true; +} + +bool Win8ResolverThunk::IsFunctionAService(void* local_thunk) const { + ServiceEntryW8 function_code; + SIZE_T read; + if (!::ReadProcessMemory(process_, target_, &function_code, + sizeof(function_code), &read)) { + return false; + } + + if (sizeof(function_code) != read) + return false; + + if (kMovEax != function_code.mov_eax || kCallEip != function_code.call_eip || + function_code.call_offset != 3 || kRet != function_code.ret_p || + kMovEdxEsp != function_code.mov_edx_esp || + kSysenter != function_code.sysenter || kRet2 != function_code.ret) { + return false; + } + + // Save the verified code + memcpy(local_thunk, &function_code, sizeof(function_code)); + + return true; +} + +bool Wow64W10ResolverThunk::IsFunctionAService(void* local_thunk) const { + Wow64EntryW10 function_code; + SIZE_T read; + if (!::ReadProcessMemory(process_, target_, &function_code, + sizeof(function_code), &read)) { + return false; + } + + if (sizeof(function_code) != read) + return false; + + if (kMovEax != function_code.mov_eax || kMovEdx != function_code.mov_edx || + kCallEdx != function_code.call_edx || kRet != function_code.ret) { + return false; + } + + // Save the verified code + memcpy(local_thunk, &function_code, sizeof(function_code)); + return true; } } // namespace sandbox diff --git a/sandbox/win/src/service_resolver_64.cc b/sandbox/win/src/service_resolver_64.cc index 33b91d04ad114..38e451da143a3 100644 --- a/sandbox/win/src/service_resolver_64.cc +++ b/sandbox/win/src/service_resolver_64.cc @@ -282,8 +282,4 @@ NTSTATUS ServiceResolverThunk::PerformPatch(void* local_thunk, return STATUS_SUCCESS; } -bool ServiceResolverThunk::VerifyJumpTargetForTesting(void*) const { - return true; -} - } // namespace sandbox diff --git a/sandbox/win/src/service_resolver_unittest.cc b/sandbox/win/src/service_resolver_unittest.cc index aa05e37c0c0e0..01d0fee18b17d 100644 --- a/sandbox/win/src/service_resolver_unittest.cc +++ b/sandbox/win/src/service_resolver_unittest.cc @@ -13,53 +13,98 @@ #include "base/bit_cast.h" #include "base/memory/raw_ptr.h" -#include "sandbox/win/src/nt_internals.h" +#include "base/win/windows_version.h" #include "sandbox/win/src/resolver.h" #include "testing/gtest/include/gtest/gtest.h" namespace { +class ResolverThunkTest { + public: + virtual ~ResolverThunkTest() {} + + virtual sandbox::ServiceResolverThunk* resolver() = 0; + + // Sets the interception target to the desired address. + void set_target(void* target) { fake_target_ = target; } + + protected: + // Holds the address of the fake target. + raw_ptr fake_target_; +}; + // This is the concrete resolver used to perform service-call type functions // inside ntdll.dll. -class ServiceResolverTest : public sandbox::ServiceResolverThunk { +template +class ResolverThunkTestImpl : public T, public ResolverThunkTest { public: // The service resolver needs a child process to write to. - explicit ServiceResolverTest(bool relaxed) - : sandbox::ServiceResolverThunk(::GetCurrentProcess(), relaxed) {} + explicit ResolverThunkTestImpl(bool relaxed) + : T(::GetCurrentProcess(), relaxed) {} - ServiceResolverTest(const ServiceResolverTest&) = delete; - ServiceResolverTest& operator=(const ServiceResolverTest&) = delete; + ResolverThunkTestImpl(const ResolverThunkTestImpl&) = delete; + ResolverThunkTestImpl& operator=(const ResolverThunkTestImpl&) = delete; - // Sets the interception target to the desired address. - void set_target(void* target) { fake_target_ = target; } + sandbox::ServiceResolverThunk* resolver() { return this; } protected: // Overrides Resolver::Init - NTSTATUS Init(const void* target_module, - const void* interceptor_module, - const char* target_name, - const char* interceptor_name, - const void* interceptor_entry_point, - void* thunk_storage, - size_t storage_bytes) final { + virtual NTSTATUS Init(const void* target_module, + const void* interceptor_module, + const char* target_name, + const char* interceptor_name, + const void* interceptor_entry_point, + void* thunk_storage, + size_t storage_bytes) { NTSTATUS ret = STATUS_SUCCESS; - ret = sandbox::ServiceResolverThunk::Init( - target_module, interceptor_module, target_name, interceptor_name, - interceptor_entry_point, thunk_storage, storage_bytes); + ret = T::Init(target_module, interceptor_module, target_name, + interceptor_name, interceptor_entry_point, thunk_storage, + storage_bytes); EXPECT_EQ(STATUS_SUCCESS, ret); this->target_ = fake_target_; return ret; } - - // Holds the address of the fake target. - raw_ptr fake_target_; }; +typedef ResolverThunkTestImpl WinXpResolverTest; + +#if !defined(_WIN64) +typedef ResolverThunkTestImpl Win8ResolverTest; +typedef ResolverThunkTestImpl Wow64ResolverTest; +typedef ResolverThunkTestImpl + Wow64W8ResolverTest; +typedef ResolverThunkTestImpl + Wow64W10ResolverTest; +#endif + +const BYTE kJump32 = 0xE9; + +void CheckJump(void* source, void* target) { +#pragma pack(push) +#pragma pack(1) + struct Code { + BYTE jump; + ULONG delta; + }; +#pragma pack(pop) + +#if defined(_WIN64) + FAIL() << "Running 32-bit codepath"; +#else + Code* patched = reinterpret_cast(source); + EXPECT_EQ(kJump32, patched->jump); + + ULONG source_addr = base::bit_cast(source); + ULONG target_addr = base::bit_cast(target); + EXPECT_EQ(target_addr + 19 - source_addr, patched->delta); +#endif +} + NTSTATUS PatchNtdllWithResolver(const char* function, bool relaxed, - ServiceResolverTest& resolver) { + ResolverThunkTest* thunk_test) { HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll"); EXPECT_TRUE(ntdll_base); @@ -72,20 +117,21 @@ NTSTATUS PatchNtdllWithResolver(const char* function, BYTE service[50]; memcpy(service, target, sizeof(service)); - resolver.set_target(service); + thunk_test->set_target(service); + sandbox::ServiceResolverThunk* resolver = thunk_test->resolver(); // Any pointer will do as an interception_entry_point - void* function_entry = &resolver; - size_t thunk_size = resolver.GetThunkSize(); - std::unique_ptr thunk = std::make_unique(thunk_size); + void* function_entry = resolver; + size_t thunk_size = resolver->GetThunkSize(); + std::unique_ptr thunk(new char[thunk_size]); size_t used; - resolver.AllowLocalPatches(); + resolver->AllowLocalPatches(); - NTSTATUS ret = resolver.Setup(ntdll_base, nullptr, function, nullptr, - function_entry, thunk.get(), thunk_size, &used); + NTSTATUS ret = + resolver->Setup(ntdll_base, nullptr, function, nullptr, function_entry, + thunk.get(), thunk_size, &used); if (NT_SUCCESS(ret)) { - const BYTE kJump32 = 0xE9; EXPECT_EQ(thunk_size, used); EXPECT_NE(0, memcmp(service, target, sizeof(service))); EXPECT_NE(kJump32, service[0]); @@ -93,18 +139,30 @@ NTSTATUS PatchNtdllWithResolver(const char* function, if (relaxed) { // It's already patched, let's patch again, and simulate a direct patch. service[0] = kJump32; - ret = resolver.Setup(ntdll_base, nullptr, function, nullptr, - function_entry, thunk.get(), thunk_size, &used); - EXPECT_TRUE(resolver.VerifyJumpTargetForTesting(thunk.get())); + ret = resolver->Setup(ntdll_base, nullptr, function, nullptr, + function_entry, thunk.get(), thunk_size, &used); + CheckJump(service, thunk.get()); } } return ret; } +std::unique_ptr GetTestResolver(bool relaxed) { +#if defined(_WIN64) + return std::make_unique(relaxed); +#else + base::win::OSInfo* os_info = base::win::OSInfo::GetInstance(); + if (os_info->IsWowX86OnAMD64()) + return std::make_unique(relaxed); + + return std::make_unique(relaxed); +#endif +} + NTSTATUS PatchNtdll(const char* function, bool relaxed) { - ServiceResolverTest resolver(relaxed); - return PatchNtdllWithResolver(function, relaxed, resolver); + std::unique_ptr thunk_test = GetTestResolver(relaxed); + return PatchNtdllWithResolver(function, relaxed, thunk_test.get()); } TEST(ServiceResolverTest, PatchesServices) { @@ -155,26 +213,26 @@ TEST(ServiceResolverTest, PatchesPatchedServices) { TEST(ServiceResolverTest, MultiplePatchedServices) { // We don't support "relaxed mode" for Win64 apps. #if !defined(_WIN64) - ServiceResolverTest thunk_test(true); - NTSTATUS ret = PatchNtdllWithResolver("NtClose", true, thunk_test); + std::unique_ptr thunk_test = GetTestResolver(true); + NTSTATUS ret = PatchNtdllWithResolver("NtClose", true, thunk_test.get()); EXPECT_EQ(STATUS_SUCCESS, ret) << "NtClose, last error: " << ::GetLastError(); - ret = PatchNtdllWithResolver("NtCreateFile", true, thunk_test); + ret = PatchNtdllWithResolver("NtCreateFile", true, thunk_test.get()); EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateFile, last error: " << ::GetLastError(); - ret = PatchNtdllWithResolver("NtCreateMutant", true, thunk_test); + ret = PatchNtdllWithResolver("NtCreateMutant", true, thunk_test.get()); EXPECT_EQ(STATUS_SUCCESS, ret) << "NtCreateMutant, last error: " << ::GetLastError(); - ret = PatchNtdllWithResolver("NtMapViewOfSection", true, thunk_test); + ret = PatchNtdllWithResolver("NtMapViewOfSection", true, thunk_test.get()); EXPECT_EQ(STATUS_SUCCESS, ret) << "NtMapViewOfSection, last error: " << ::GetLastError(); #endif } TEST(ServiceResolverTest, LocalPatchesAllowed) { - ServiceResolverTest resolver(true); + std::unique_ptr thunk_test = GetTestResolver(true); HMODULE ntdll_base = ::GetModuleHandle(L"ntdll.dll"); ASSERT_TRUE(ntdll_base); @@ -187,25 +245,26 @@ TEST(ServiceResolverTest, LocalPatchesAllowed) { BYTE service[50]; memcpy(service, target, sizeof(service)); - resolver.set_target(service); + thunk_test->set_target(service); + sandbox::ServiceResolverThunk* resolver = thunk_test->resolver(); // Any pointer will do as an interception_entry_point - void* function_entry = &resolver; - size_t thunk_size = resolver.GetThunkSize(); - std::unique_ptr thunk = std::make_unique(thunk_size); + void* function_entry = resolver; + size_t thunk_size = resolver->GetThunkSize(); + std::unique_ptr thunk(new char[thunk_size]); size_t used; NTSTATUS ret = STATUS_UNSUCCESSFUL; // First try patching without having allowed local patches. - ret = resolver.Setup(ntdll_base, nullptr, kFunctionName, nullptr, - function_entry, thunk.get(), thunk_size, &used); + ret = resolver->Setup(ntdll_base, nullptr, kFunctionName, nullptr, + function_entry, thunk.get(), thunk_size, &used); EXPECT_FALSE(NT_SUCCESS(ret)); // Now allow local patches and check that things work. - resolver.AllowLocalPatches(); - ret = resolver.Setup(ntdll_base, nullptr, kFunctionName, nullptr, - function_entry, thunk.get(), thunk_size, &used); + resolver->AllowLocalPatches(); + ret = resolver->Setup(ntdll_base, nullptr, kFunctionName, nullptr, + function_entry, thunk.get(), thunk_size, &used); EXPECT_EQ(STATUS_SUCCESS, ret); } diff --git a/sandbox/win/src/win_utils.cc b/sandbox/win/src/win_utils.cc index afdddac091985..35c9cf95fb772 100644 --- a/sandbox/win/src/win_utils.cc +++ b/sandbox/win/src/win_utils.cc @@ -570,6 +570,36 @@ absl::optional GetCurrentProcessHandles() { return handle_map; } +absl::optional GetCurrentProcessHandlesWin7() { + DWORD handle_count = UINT_MAX; + const int kInvalidHandleThreshold = 100; + const size_t kHandleOffset = 4; // Handles are always a multiple of 4. + + if (!::GetProcessHandleCount(::GetCurrentProcess(), &handle_count)) + return absl::nullopt; + ProcessHandleMap handle_map; + + uint32_t handle_value = 0; + int invalid_count = 0; + + // Keep incrementing until we hit the number of handles reported by + // GetProcessHandleCount(). If we hit a very long sequence of invalid + // handles we assume that we've run past the end of the table. + while (handle_count && invalid_count < kInvalidHandleThreshold) { + handle_value += kHandleOffset; + HANDLE handle = base::win::Uint32ToHandle(handle_value); + auto type_name = GetTypeNameFromHandle(handle); + if (!type_name) { + ++invalid_count; + continue; + } + + --handle_count; + handle_map[type_name.value()].push_back(handle); + } + return handle_map; +} + } // namespace sandbox void ResolveNTFunctionPtr(const char* name, void* ptr) { diff --git a/sandbox/win/src/win_utils.h b/sandbox/win/src/win_utils.h index e416d6cda9979..7f199feb9d8c1 100644 --- a/sandbox/win/src/win_utils.h +++ b/sandbox/win/src/win_utils.h @@ -115,6 +115,12 @@ void* GetProcessBaseAddress(HANDLE process); // use them. absl::optional GetCurrentProcessHandles(); +// Fallback function for GetCurrentProcessHandles. Should only be needed on +// Windows 7 which doesn't support the API to query all process handles. This +// uses a brute force method to get the process handles. +absl::optional GetCurrentProcessHandlesWin7(); + + } // namespace sandbox // Resolves a function name in NTDLL to a function pointer. The second parameter diff --git a/skia/BUILD.gn b/skia/BUILD.gn index c8a80ba574507..34d3e52ed41ca 100644 --- a/skia/BUILD.gn +++ b/skia/BUILD.gn @@ -462,6 +462,7 @@ component("skia") { if (is_win) { # See SK_TYPEFACE_FACTORY_DIRECTWRITE sources += [ + "//third_party/skia/src/ports/SkFontHost_win.cpp", "//third_party/skia/src/ports/SkFontMgr_win_dw.cpp", "//third_party/skia/src/ports/SkOSFile_win.cpp", "//third_party/skia/src/ports/SkRemotableFontMgr_win_dw.cpp", diff --git a/third_party/angle b/third_party/angle index 8a8c8fc280d74..ab9bbb9b11b35 160000 --- a/third_party/angle +++ b/third_party/angle @@ -1 +1 @@ -Subproject commit 8a8c8fc280d74b34731e0e417b19bff7c967388a +Subproject commit ab9bbb9b11b3522b891d8152b6c67ae1b1869f05 diff --git a/third_party/beto-core/src b/third_party/beto-core/src index 45756ea770c41..b902b346037ea 160000 --- a/third_party/beto-core/src +++ b/third_party/beto-core/src @@ -1 +1 @@ -Subproject commit 45756ea770c41085d5c71156b2c46da0b10117d6 +Subproject commit b902b346037ea3f4aadf8177021f6f917b16e648 diff --git a/third_party/blink/public/mojom/dwrite_font_proxy/dwrite_font_proxy.mojom b/third_party/blink/public/mojom/dwrite_font_proxy/dwrite_font_proxy.mojom index b53a952116ece..c6ddb5b7e6061 100644 --- a/third_party/blink/public/mojom/dwrite_font_proxy/dwrite_font_proxy.mojom +++ b/third_party/blink/public/mojom/dwrite_font_proxy/dwrite_font_proxy.mojom @@ -20,6 +20,13 @@ struct DWriteFontStyle { uint8 font_stretch; }; +struct FallbackFamilyAndStyle { + string fallback_family_name; + uint16 weight; + uint8 width; + uint8 slant; +}; + struct MapCharactersResult { uint32 family_index; mojo_base.mojom.String16 family_name; @@ -111,4 +118,18 @@ interface DWriteFontProxy { uint32 reading_direction, mojo_base.mojom.String16 base_family_name) => (MapCharactersResult out); + + // For a given base family name, bcp47 language tag, and codepoint to look up, + // return a font family name that is available on the system to display the + // given codepoint. This internally calls Skia's + // SkFontMgr_DirectWrite::matchFamilyStyleCharacter which executes + // IDWriteTextLayout based fallback code, which cannot be run in the renderer + // due triggering loading the DWrite system font collection. + // Use only on Windows 8.0 and earlier - otherwise better fallback API is + // available through using a proxies IDWriteFontFallback. + [Sync] + FallbackFamilyAndStyleForCodepoint(string base_family_name, + string bcp47_language_tag, + uint32 codepoint) + => (FallbackFamilyAndStyle fallback_result); }; diff --git a/third_party/blink/public/web/win/web_font_rendering.h b/third_party/blink/public/web/win/web_font_rendering.h index 851b6c95d0c69..1c3135b59201f 100644 --- a/third_party/blink/public/web/win/web_font_rendering.h +++ b/third_party/blink/public/web/win/web_font_rendering.h @@ -18,6 +18,7 @@ class WebFontRenderingClient; class BLINK_EXPORT WebFontRendering { public: + static void setUseDirectWrite(bool); static void SetSkiaFontManager(sk_sp); // Set an instance of |WebFontPrewarmer|. The instance must be kept alive // until the process exits. @@ -34,6 +35,7 @@ class BLINK_EXPORT WebFontRendering { int32_t font_height); static void SetAntialiasedTextEnabled(bool); static void SetLCDTextEnabled(bool); + static void SetUseSkiaFontFallback(bool); }; } // namespace blink diff --git a/third_party/blink/renderer/core/layout/web_font_rendering_win.cc b/third_party/blink/renderer/core/layout/web_font_rendering_win.cc index 178b04c75c258..de8cc3fcdc8ed 100644 --- a/third_party/blink/renderer/core/layout/web_font_rendering_win.cc +++ b/third_party/blink/renderer/core/layout/web_font_rendering_win.cc @@ -9,6 +9,12 @@ namespace blink { +// static +void WebFontRendering::setUseDirectWrite(bool useDirectWrite) +{ + FontCache::setUseDirectWrite(useDirectWrite); +} + // static void WebFontRendering::SetSkiaFontManager(sk_sp font_mgr) { FontCache::SetFontManager(std::move(font_mgr)); @@ -59,4 +65,9 @@ void WebFontRendering::SetLCDTextEnabled(bool enabled) { FontCache::SetLCDTextEnabled(enabled); } +// static +void WebFontRendering::SetUseSkiaFontFallback(bool use_skia_font_fallback) { + FontCache::SetUseSkiaFontFallback(use_skia_font_fallback); +} + } // namespace blink diff --git a/third_party/blink/renderer/platform/BUILD.gn b/third_party/blink/renderer/platform/BUILD.gn index 5ecdd70e2152a..05fd65dd9f830 100644 --- a/third_party/blink/renderer/platform/BUILD.gn +++ b/third_party/blink/renderer/platform/BUILD.gn @@ -1632,6 +1632,8 @@ component("platform") { sources += [ "fonts/win/dwrite_font_format_support.cc", "fonts/win/dwrite_font_format_support.h", + "fonts/win/fallback_family_style_cache_win.cc", + "fonts/win/fallback_family_style_cache_win.h", "fonts/win/font_cache_skia_win.cc", "fonts/win/font_fallback_win.cc", "fonts/win/font_fallback_win.h", @@ -2243,7 +2245,10 @@ source_set("blink_platform_unittests_sources") { if (is_android) { sources += [ "fonts/android/font_cache_android_test.cc" ] } else if (is_win) { - sources += [ "text/locale_win_test.cc" ] + sources += [ + "fonts/win/fallback_lru_cache_win_test.cc", + "text/locale_win_test.cc", + ] } else if (is_mac) { sources += [ "fonts/mac/font_matcher_mac_test.mm", diff --git a/third_party/blink/renderer/platform/fonts/font_cache.cc b/third_party/blink/renderer/platform/fonts/font_cache.cc index 2d160f4de81ce..6bbc23d28a841 100644 --- a/third_party/blink/renderer/platform/fonts/font_cache.cc +++ b/third_party/blink/renderer/platform/fonts/font_cache.cc @@ -59,6 +59,7 @@ #include "ui/gfx/font_list.h" #if BUILDFLAG(IS_WIN) +#include #include "third_party/skia/include/ports/SkTypeface_win.h" #endif @@ -77,8 +78,10 @@ float FontCache::device_scale_factor_ = 1.0; #endif #if BUILDFLAG(IS_WIN) +bool FontCache::s_useDirectWrite = false; bool FontCache::antialiased_text_enabled_ = false; bool FontCache::lcd_text_enabled_ = false; +bool FontCache::use_skia_font_fallback_ = false; static bool should_use_test_font_mgr = false; #endif // BUILDFLAG(IS_WIN) @@ -95,7 +98,11 @@ FontCache::FontCache() // This code path is only for unit tests. This SkFontMgr does not work in // sandboxed environments, but injecting this initialization code to all // unit tests isn't easy. - font_manager_ = SkFontMgr_New_DirectWrite(); + if(s_useDirectWrite) { + font_manager_ = SkFontMgr_New_DirectWrite(); + } else { + font_manager_ = SkFontMgr_New_GDI(); + } // Set |is_test_font_mgr_| to capture if this is not happening in the // production code. crbug.com/561873 is_test_font_mgr_ = true; diff --git a/third_party/blink/renderer/platform/fonts/font_cache.h b/third_party/blink/renderer/platform/fonts/font_cache.h index ae5cee24b4c49..7da0d5c0da219 100644 --- a/third_party/blink/renderer/platform/fonts/font_cache.h +++ b/third_party/blink/renderer/platform/fonts/font_cache.h @@ -38,6 +38,7 @@ #include "base/gtest_prod_util.h" #include "base/memory/scoped_refptr.h" #include "build/build_config.h" +#include "mojo/public/cpp/bindings/remote.h" #include "third_party/blink/renderer/platform/fonts/fallback_list_composite_key.h" #include "third_party/blink/renderer/platform/fonts/font_cache_client.h" #include "third_party/blink/renderer/platform/fonts/font_data_cache.h" @@ -60,6 +61,11 @@ #include "ui/gfx/font_fallback_linux.h" #endif +#if BUILDFLAG(IS_WIN) +#include "third_party/blink/public/mojom/dwrite_font_proxy/dwrite_font_proxy.mojom-blink.h" +#include "third_party/blink/renderer/platform/fonts/win/fallback_family_style_cache_win.h" +#endif + class SkString; class SkTypeface; @@ -197,8 +203,10 @@ class PLATFORM_EXPORT FontCache final { #if BUILDFLAG(IS_WIN) // TODO(https://crbug.com/808221) System font style configuration is not // related to FontCache. Move it somewhere else, e.g. to WebThemeEngine. + static bool useDirectWrite() { return s_useDirectWrite; } static bool AntialiasedTextEnabled() { return antialiased_text_enabled_; } static bool LcdTextEnabled() { return lcd_text_enabled_; } + static void setUseDirectWrite(bool useDirectWrite) { s_useDirectWrite = useDirectWrite; } static void SetAntialiasedTextEnabled(bool enabled) { antialiased_text_enabled_ = enabled; } @@ -222,6 +230,13 @@ class PLATFORM_EXPORT FontCache final { static const AtomicString& StatusFontFamily() { return *status_font_family_name_; } + static void SetUseSkiaFontFallback(bool use_skia_font_fallback) { + use_skia_font_fallback_ = use_skia_font_fallback; + } + + // On Windows pre 8.1 establish a connection to the DWriteFontProxy service in + // order to retrieve family names for fallback lookup. + void EnsureServiceConnected(); scoped_refptr GetFallbackFamilyNameFromHardcodedChoices( const FontDescription&, @@ -352,6 +367,7 @@ class PLATFORM_EXPORT FontCache final { static SkFontMgr* static_font_manager_; #if BUILDFLAG(IS_WIN) + static bool s_useDirectWrite; static WebFontPrewarmer* prewarmer_; static bool antialiased_text_enabled_; static bool lcd_text_enabled_; @@ -362,10 +378,13 @@ class PLATFORM_EXPORT FontCache final { static int32_t small_caption_font_height_; static AtomicString* status_font_family_name_; static int32_t status_font_height_; + static bool use_skia_font_fallback_; // Windows creates an SkFontMgr for unit testing automatically. This flag is // to ensure it's not happening in the production from the crash log. bool is_test_font_mgr_ = false; + mojo::Remote service_; + std::unique_ptr fallback_params_cache_; #endif // BUILDFLAG(IS_WIN) #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) diff --git a/third_party/blink/renderer/platform/fonts/font_custom_platform_data.cc b/third_party/blink/renderer/platform/fonts/font_custom_platform_data.cc index da25d2d225f2a..b9477fc827f84 100644 --- a/third_party/blink/renderer/platform/fonts/font_custom_platform_data.cc +++ b/third_party/blink/renderer/platform/fonts/font_custom_platform_data.cc @@ -128,6 +128,34 @@ FontPlatformData FontCustomPlatformData::GetFontPlatformData( // now, going with a reasonable upper limit. Deduplication is // handled by Skia with priority given to the last occuring // assignment. + #if BUILDFLAG(IS_WIN) + if (!FontCache::useDirectWrite()) { + // FIXME: Skia currently renders synthetic bold and italics with + // hinting and without linear metrics on the windows GDI backend + // while the DirectWrite backend does the right thing. Using + // legacyCreateTypeface and specifying the bold/italics style allows + // for proper rendering of synthetic style. Once Skia has been + // updated this workaround will no longer be needed. + // http://crbug.com/332958 + bool syntheticBold = bold && !return_typeface->isBold(); + bool syntheticItalic = italic && !return_typeface->isItalic(); + if (syntheticBold || syntheticItalic) { + SkString name; + sk_sp font_mgr(SkFontMgr::RefDefault()); + return_typeface->getFamilyName(&name); + + SkFontStyle realStyle = return_typeface->fontStyle(); + SkFontStyle syntheticStyle = SkFontStyle( + realStyle.weight() + (syntheticBold ? 200 : 0), + realStyle.width(), + syntheticItalic ? SkFontStyle::kItalic_Slant : realStyle.slant()); + sk_sp typeface = font_mgr->legacyMakeTypeface(name.c_str(), syntheticStyle); + syntheticBold = false; + syntheticItalic = false; + return FontPlatformData(typeface, "", size, syntheticBold, syntheticItalic, text_rendering, resolved_font_features, orientation); + } + } + #endif FontFormatCheck::VariableFontSubType font_sub_type = FontFormatCheck::ProbeVariableFont(base_typeface_); bool synthetic_bold = bold; diff --git a/third_party/blink/renderer/platform/fonts/font_fallback_iterator.cc b/third_party/blink/renderer/platform/fonts/font_fallback_iterator.cc index bb6625fe0df54..7f5945591bee8 100644 --- a/third_party/blink/renderer/platform/fonts/font_fallback_iterator.cc +++ b/third_party/blink/renderer/platform/fonts/font_fallback_iterator.cc @@ -83,8 +83,9 @@ scoped_refptr FontFallbackIterator::UniqueOrNext( // Save first candidate to be returned if all other fonts fail, and we need // it to render the .notdef glyph. - if (!first_candidate_) + if (!first_candidate_) { first_candidate_ = candidate; + } return candidate; } @@ -153,8 +154,9 @@ scoped_refptr FontFallbackIterator::Next( if (fallback_stage_ == kFirstCandidateForNotdefGlyph) { fallback_stage_ = kOutOfLuck; - if (!first_candidate_) - FontCache::CrashWithFontInfo(&font_description_); + if (!first_candidate_) { + FontCache::CrashWithFontInfo(&font_description_); + } return first_candidate_; } diff --git a/third_party/blink/renderer/platform/fonts/font_platform_data_cache.cc b/third_party/blink/renderer/platform/fonts/font_platform_data_cache.cc index fae5547d5ef83..33cc33aaa4424 100644 --- a/third_party/blink/renderer/platform/fonts/font_platform_data_cache.cc +++ b/third_party/blink/renderer/platform/fonts/font_platform_data_cache.cc @@ -116,6 +116,7 @@ FontPlatformData* FontPlatformDataCache::GetOrCreateFontPlatformData( FontPlatformData* const platform_data = GetOrCreateFontPlatformData( font_cache, font_description, create_by_alternate_family, AlternateFontName::kNoAlternate); + if (!platform_data) return nullptr; diff --git a/third_party/blink/renderer/platform/fonts/skia/font_cache_skia.cc b/third_party/blink/renderer/platform/fonts/skia/font_cache_skia.cc index ae4a61fa41713..6af5a8818e759 100644 --- a/third_party/blink/renderer/platform/fonts/skia/font_cache_skia.cc +++ b/third_party/blink/renderer/platform/fonts/skia/font_cache_skia.cc @@ -193,7 +193,6 @@ scoped_refptr FontCache::GetLastResortFallbackFont( AlternateFontName::kLastResort); } #endif - DCHECK(font_platform_data); return FontDataFromFontPlatformData(font_platform_data, should_retain); } @@ -233,6 +232,12 @@ sk_sp FontCache::CreateTypeface( // TODO(https://crbug.com/1425390: Assign FontCache::font_manager_ in the // ctor. auto font_manager = font_manager_ ? font_manager_ : SkFontMgr::RefDefault(); +#if BUILDFLAG(IS_WIN) + if(!useDirectWrite()) { + return sk_sp(font_manager->legacyMakeTypeface( + name.empty() ? nullptr : name.c_str(), font_description.SkiaFontStyle())); + } +#endif return sk_sp(font_manager->matchFamilyStyle( name.empty() ? nullptr : name.c_str(), font_description.SkiaFontStyle())); } diff --git a/third_party/blink/renderer/platform/fonts/win/fallback_family_style_cache_win.cc b/third_party/blink/renderer/platform/fonts/win/fallback_family_style_cache_win.cc new file mode 100644 index 0000000000000..2e8c855270541 --- /dev/null +++ b/third_party/blink/renderer/platform/fonts/win/fallback_family_style_cache_win.cc @@ -0,0 +1,90 @@ +// Copyright 2019 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/fonts/win/fallback_family_style_cache_win.h" + +#include "third_party/blink/renderer/platform/fonts/font_platform_data.h" + +namespace blink { + +namespace { + +const wtf_size_t kMaxCacheSlots = 16; + +String makeCacheKey(FontDescription::GenericFamilyType generic_family, + String bcp47_language_tag, + FontFallbackPriority fallback_priority) { + StringBuilder cache_key; + cache_key.Append(bcp47_language_tag); + cache_key.AppendNumber( + static_cast< + std::underlying_type::type>( + generic_family)); + cache_key.AppendNumber( + static_cast::type>( + fallback_priority)); + return cache_key.ToString(); +} + +void getFallbackFamilyAndStyle(SkTypeface* typeface, + String* fallback_family, + SkFontStyle* fallback_style) { + SkString family; + typeface->getFamilyName(&family); + *fallback_family = family.c_str(); + + *fallback_style = typeface->fontStyle(); +} +} // namespace + +FallbackFamilyStyleCache::FallbackFamilyStyleCache() + : recent_fallback_fonts_(kMaxCacheSlots) {} + +void FallbackFamilyStyleCache::Put( + FontDescription::GenericFamilyType generic_family, + String bcp47_language_tag, + FontFallbackPriority fallback_priority, + SkTypeface* typeface) { + String cache_key = + makeCacheKey(generic_family, bcp47_language_tag, fallback_priority); + + auto it = recent_fallback_fonts_.Get(cache_key); + if (it != recent_fallback_fonts_.end()) { + it->second.insert(0, sk_ref_sp(typeface)); + } else { + TypefaceVector typefaces; + typefaces.push_back(sk_ref_sp(typeface)); + recent_fallback_fonts_.Put(std::move(cache_key), std::move(typefaces)); + } +} + +void FallbackFamilyStyleCache::Get( + FontDescription::GenericFamilyType generic_family, + String bcp47_language_tag, + FontFallbackPriority fallback_priority, + UChar32 character, + String* fallback_family, + SkFontStyle* fallback_style) { + auto it = recent_fallback_fonts_.Get( + makeCacheKey(generic_family, bcp47_language_tag, fallback_priority)); + if (it == recent_fallback_fonts_.end()) + return; + TypefaceVector& typefaces = it->second; + for (wtf_size_t i = 0; i < typefaces.size(); ++i) { + sk_sp& typeface = typefaces.at(i); + if (typeface->unicharToGlyph(character)) { + getFallbackFamilyAndStyle(typeface.get(), fallback_family, + fallback_style); + sk_sp tmp_typeface(typeface); + // For the vector of typefaces for this specific language tag, since this + // SkTypeface had a glyph, move it to the beginning to accelerate + // subsequent lookups. + typefaces.EraseAt(i); + typefaces.insert(0, std::move(tmp_typeface)); + return; + } + } +} + +} // namespace blink diff --git a/third_party/blink/renderer/platform/fonts/win/fallback_family_style_cache_win.h b/third_party/blink/renderer/platform/fonts/win/fallback_family_style_cache_win.h new file mode 100644 index 0000000000000..72e27fa69b27f --- /dev/null +++ b/third_party/blink/renderer/platform/fonts/win/fallback_family_style_cache_win.h @@ -0,0 +1,59 @@ +// Copyright 2019 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_WIN_FALLBACK_FAMILY_STYLE_CACHE_WIN_H_ +#define THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_WIN_FALLBACK_FAMILY_STYLE_CACHE_WIN_H_ + +#include "base/containers/lru_cache.h" +#include "third_party/blink/renderer/platform/fonts/font_description.h" +#include "third_party/blink/renderer/platform/fonts/font_fallback_priority.h" +#include "third_party/skia/include/core/SkRefCnt.h" +#include "third_party/skia/include/core/SkTypeface.h" + +namespace blink { + +using TypefaceVector = Vector>; +using FallbackLruCache = base::HashingLRUCache; + +class FallbackFamilyStyleCache { + USING_FAST_MALLOC(FallbackFamilyStyleCache); + + public: + FallbackFamilyStyleCache(); + FallbackFamilyStyleCache(const FallbackFamilyStyleCache&) = delete; + FallbackFamilyStyleCache& operator=(const FallbackFamilyStyleCache&) = delete; + + // Places a SkTypeface object in the cache for specified language tag and + // fallback priority, taking a reference on SkTypeface. Adds the |SkTypeface| + // to the beginning of a list of typefaces if previous |SkTypefaces| objects + // where added for this set of parameters. Note, the internal list of + // typefaces for a language tag and fallback priority is not checked for + // duplicates when adding a |typeface| object. + void Put(FontDescription::GenericFamilyType generic_family, + String bcp47_language_tag, + FontFallbackPriority fallback_priority, + SkTypeface* typeface); + + // Fetches a |fallback_family| and |fallback_style| for a given language tag, + // fallback priority and codepoint. Checks the internal cache for whether a + // fallback font with glyph coverage for |character| is available for the + // given parameters, then returns its family name and style. + void Get(FontDescription::GenericFamilyType generic_family, + String bcp47_language_tag, + FontFallbackPriority fallback_priority, + UChar32 character, + String* fallback_family, + SkFontStyle* fallback_style); + + // Empties the internal cache, deleting keys and unrefing the typefaces that + // were placed in the cache. + void Clear(); + + private: + FallbackLruCache recent_fallback_fonts_; +}; + +} // namespace blink + +#endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_WIN_FALLBACK_FAMILY_STYLE_CACHE_WIN_H_ diff --git a/third_party/blink/renderer/platform/fonts/win/fallback_lru_cache_win_test.cc b/third_party/blink/renderer/platform/fonts/win/fallback_lru_cache_win_test.cc new file mode 100644 index 0000000000000..0363fdca92dde --- /dev/null +++ b/third_party/blink/renderer/platform/fonts/win/fallback_lru_cache_win_test.cc @@ -0,0 +1,99 @@ +// Copyright 2019 The Chromium Authors +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "third_party/blink/renderer/platform/fonts/win/fallback_family_style_cache_win.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/blink/renderer/platform/wtf/text/character_names.h" +#include "third_party/blink/renderer/platform/wtf/text/string_hash.h" +#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" +#include "third_party/skia/include/core/SkFontMgr.h" +#include "third_party/skia/include/core/SkRefCnt.h" +#include "third_party/skia/include/core/SkTypeface.h" + +namespace { + +const char kHanSimplifiedLocale[] = "zh-Hans"; +const size_t kLruCacheTestSize = 5; +const char kFontFamilyNameArial[] = "Arial"; +const UChar32 kFirstCJKIdeograph = 0x4E00; +const UChar32 kSecondCJKIdeograph = kFirstCJKIdeograph + 1; + +sk_sp fallbackForLocale(String locale, UChar32 codepoint) { + sk_sp font_mgr = SkFontMgr::RefDefault(); + std::string locale_string(locale.Ascii()); + const char* locale_char = locale_string.c_str(); + return sk_sp(font_mgr->matchFamilyStyleCharacter( + kFontFamilyNameArial, SkFontStyle(), &locale_char, 1, codepoint)); +} + +void fillCacheWithDummies(blink::FallbackLruCache& lru_cache, + const char* format_string, + size_t count) { + for (size_t i = 0; i < count; ++i) { + blink::TypefaceVector dummy_typefaces; + dummy_typefaces.push_back( + SkTypeface::MakeFromName(kFontFamilyNameArial, SkFontStyle())); + lru_cache.Put(String::Format(format_string, i), std::move(dummy_typefaces)); + } +} + +} // namespace + +namespace blink { + +TEST(FallbackLruCacheTest, KeepChineseWhenFetched) { + // Put a Chinese font in the cache, add size - 1 more dummy fallback fonts so + // that the cache is full. Get() and verify typeface for Chinese to move them + // up to the top of the cache. Then fill again with size - 1 items and verify + // that Chinese is still in the cache. Then fill with # size items to evict + // the Chinese font and ensure it's gone. + FallbackLruCache lru_cache(kLruCacheTestSize); + EXPECT_EQ(lru_cache.size(), 0u); + TypefaceVector fallback_typefaces_zh; + fallback_typefaces_zh.push_back( + fallbackForLocale(kHanSimplifiedLocale, kFirstCJKIdeograph)); + lru_cache.Put(kHanSimplifiedLocale, std::move(fallback_typefaces_zh)); + + EXPECT_EQ(lru_cache.size(), 1u); + + fillCacheWithDummies(lru_cache, "dummy_locale_%zu", kLruCacheTestSize - 1); + auto it = lru_cache.Get(kHanSimplifiedLocale); + EXPECT_TRUE(it != lru_cache.end()); + TypefaceVector& chinese_typefaces = it->second; + EXPECT_TRUE(chinese_typefaces.at(0)->unicharToGlyph(0x4E01)); + EXPECT_EQ(lru_cache.size(), kLruCacheTestSize); + + fillCacheWithDummies(lru_cache, "dummy_locale_2nd_%zu", + kLruCacheTestSize - 1); + it = lru_cache.Get(kHanSimplifiedLocale); + EXPECT_TRUE(it != lru_cache.end()); + chinese_typefaces = it->second; + EXPECT_EQ(chinese_typefaces.size(), 1u); + EXPECT_TRUE(chinese_typefaces.at(0)->unicharToGlyph(kSecondCJKIdeograph)); + EXPECT_EQ(lru_cache.size(), kLruCacheTestSize); + + fillCacheWithDummies(lru_cache, "dummy_locale_3rd_%zu", kLruCacheTestSize); + it = lru_cache.Get(kHanSimplifiedLocale); + EXPECT_TRUE(it == lru_cache.end()); + EXPECT_EQ(lru_cache.size(), kLruCacheTestSize); +} + +TEST(FallbackLruCacheTest, LargeFillAndClear) { + FallbackLruCache lru_cache(kLruCacheTestSize); + EXPECT_EQ(lru_cache.size(), 0u); + fillCacheWithDummies(lru_cache, "dummy_locale_%zu", 1000); + EXPECT_EQ(lru_cache.size(), kLruCacheTestSize); + lru_cache.Clear(); + EXPECT_EQ(lru_cache.size(), 0u); +} + +TEST(FallbackLruCacheTest, KeyOverride) { + FallbackLruCache lru_cache(kLruCacheTestSize); + EXPECT_EQ(lru_cache.size(), 0u); + fillCacheWithDummies(lru_cache, "same_locale", 10); + EXPECT_EQ(lru_cache.size(), 1u); +} + +} // namespace blink diff --git a/third_party/blink/renderer/platform/fonts/win/font_cache_skia_win.cc b/third_party/blink/renderer/platform/fonts/win/font_cache_skia_win.cc index 83efc5cc57615..d057134c9bd24 100644 --- a/third_party/blink/renderer/platform/fonts/win/font_cache_skia_win.cc +++ b/third_party/blink/renderer/platform/fonts/win/font_cache_skia_win.cc @@ -42,6 +42,8 @@ #include "base/feature_list.h" #include "base/metrics/histogram_functions.h" #include "base/trace_event/trace_event.h" +#include "third_party/blink/public/common/thread_safe_browser_interface_broker_proxy.h" +#include "third_party/blink/public/platform/platform.h" #include "third_party/blink/public/platform/web_font_prewarmer.h" #include "third_party/blink/renderer/platform/fonts/bitmap_glyphs_block_list.h" #include "third_party/blink/renderer/platform/fonts/font_cache.h" @@ -163,6 +165,13 @@ void FontCache::SetStatusFontMetrics(const AtomicString& family_name, status_font_height_ = EnsureMinimumFontHeightIfNeeded(font_height); } +void FontCache::EnsureServiceConnected() { + if (service_) + return; + Platform::Current()->GetBrowserInterfaceBroker()->GetInterface( + service_.BindNewPipeAndPassReceiver()); +} + // TODO(https://crbug.com/976737): This function is deprecated and only intended // to run in parallel with the API based OOP font fallback calls to compare the // results and track them in UMA for a while until we decide to remove this @@ -247,30 +256,90 @@ scoped_refptr FontCache::GetDWriteFallbackFamily( font_description, fallback_priority, codepoint); DCHECK(fallback_locale); - const std::string family_name = font_description.Family().FamilyName().Utf8(); + // On Pre Windows 8.1 (where use_skia_font_fallback_ is false) we cannot call + // the Skia version, as there is no IDWriteFontFallback (which is + // proxyable). If no IDWriteFontFallback API exists in the DWrite Skia + // SkTypeface implemnetation it will proceed to call the layoutFallback method + // of SkTypeface DWrite implementation. This method we must not call in the + // renderer as it causes stability issues due to reaching a path that will try + // to load the system font collection in-process and thus load DLLs that are + // blocked in the renderer, see comment in dwrite_font_proxy_init_impl_win.cc + // InitializeDWriteFontProxy(). Hence, for Windows pre 8.1 we add a + // DWriteFontProxy code path to retrieve a family name as string for a + // character + language tag and call matchFamilyStyleCharacter on the browser + // side, where we can do that. + if (!use_skia_font_fallback_) { + String fallback_family; + SkFontStyle fallback_style; + + if (UNLIKELY(!fallback_params_cache_)) { + fallback_params_cache_ = std::make_unique(); + } - Bcp47Vector locales; - locales.push_back(fallback_locale->LocaleForSkFontMgr()); - sk_sp typeface(font_manager_->matchFamilyStyleCharacter( - family_name.c_str(), font_description.SkiaFontStyle(), locales.data(), - locales.size(), codepoint)); + fallback_params_cache_->Get( + font_description.GenericFamily(), fallback_locale->LocaleForSkFontMgr(), + fallback_priority, codepoint, &fallback_family, &fallback_style); + bool result_from_cache = !fallback_family.IsNull(); - if (!typeface) { - return nullptr; - } + if (!result_from_cache) { + EnsureServiceConnected(); + + // After Mojo IPC, on the browser side, this ultimately reaches + // Skia's matchFamilyStyleCharacter for Windows, which does not implement + // traversing the language tag stack but only processes the most important + // one, so we use FallbackLocaleForCharacter() to determine what locale to + // choose to achieve the best possible result. + + if (!GetOutOfProcessFallbackFamily( + codepoint, font_description.GenericFamily(), + fallback_locale->LocaleForSkFontMgr(), fallback_priority, + service_, &fallback_family, &fallback_style)) + return nullptr; + + if (fallback_family.empty()) + return nullptr; + } + + FontFaceCreationParams create_by_family((AtomicString(fallback_family))); + FontDescription fallback_updated_font_description(font_description); + fallback_updated_font_description.UpdateFromSkiaFontStyle(fallback_style); + FontPlatformData* data = GetFontPlatformData( + fallback_updated_font_description, create_by_family); + if (!data || !data->FontContainsCharacter(codepoint)) + return nullptr; + + if (!result_from_cache) { + fallback_params_cache_->Put(font_description.GenericFamily(), + fallback_locale->LocaleForSkFontMgr(), + fallback_priority, data->Typeface()); + } + return FontDataFromFontPlatformData(data, kDoNotRetain); + } else { + std::string family_name = font_description.Family().FamilyName().Utf8(); + + Bcp47Vector locales; + locales.push_back(fallback_locale->LocaleForSkFontMgr()); + sk_sp typeface(font_manager_->matchFamilyStyleCharacter( + family_name.c_str(), font_description.SkiaFontStyle(), locales.data(), + locales.size(), codepoint)); + + if (!typeface) + return nullptr; - SkString skia_family; - typeface->getFamilyName(&skia_family); - FontDescription fallback_updated_font_description(font_description); - fallback_updated_font_description.UpdateFromSkiaFontStyle( - typeface->fontStyle()); - const FontFaceCreationParams create_by_family(ToAtomicString(skia_family)); - FontPlatformData* data = - GetFontPlatformData(fallback_updated_font_description, create_by_family); - if (!data || !data->FontContainsCharacter(codepoint)) { - return nullptr; + SkString skia_family; + typeface->getFamilyName(&skia_family); + FontDescription fallback_updated_font_description(font_description); + fallback_updated_font_description.UpdateFromSkiaFontStyle( + typeface->fontStyle()); + FontFaceCreationParams create_by_family(ToAtomicString(skia_family)); + FontPlatformData* data = GetFontPlatformData( + fallback_updated_font_description, create_by_family); + if (!data || !data->FontContainsCharacter(codepoint)) + return nullptr; + return FontDataFromFontPlatformData(data, kDoNotRetain); } - return FontDataFromFontPlatformData(data, kDoNotRetain); + NOTREACHED(); + return nullptr; } // Given the desired base font, this will create a SimpleFontData for a specific @@ -296,9 +365,10 @@ scoped_refptr FontCache::PlatformFallbackFontForCharacter( GetFallbackFamilyNameFromHardcodedChoices(font_description, character, fallback_priority); - // Fall through to running the API-based fallback. + // Fall through to running the API based fallback on Windows 8.1 and above + // where API fallback was previously available. if (RuntimeEnabledFeatures::LegacyWindowsDWriteFontFallbackEnabled() || - !hardcoded_list_fallback_font) { + (!hardcoded_list_fallback_font && use_skia_font_fallback_)) { return GetDWriteFallbackFamily(font_description, character, fallback_priority); } @@ -439,7 +509,7 @@ std::unique_ptr FontCache::CreateFontPlatformData( } else { typeface = CreateTypeface(font_description, creation_params, name); - + // For a family match, Windows will always give us a valid pointer here, // even if the face name is non-existent. We have to double-check and see if // the family name was really used. diff --git a/third_party/blink/renderer/platform/fonts/win/font_fallback_win.cc b/third_party/blink/renderer/platform/fonts/win/font_fallback_win.cc index fe7c9a5c819bd..12b8e76a39921 100644 --- a/third_party/blink/renderer/platform/fonts/win/font_fallback_win.cc +++ b/third_party/blink/renderer/platform/fonts/win/font_fallback_win.cc @@ -48,11 +48,19 @@ namespace blink { namespace { +const char kArial[] = "Arial"; +const char kCourierNew[] = "Courier New"; +const char kTimesNewRoman[] = "Times New Roman"; + static inline bool IsFontPresent(const UChar* font_name, SkFontMgr* font_manager) { String family = font_name; - sk_sp tf( - font_manager->matchFamilyStyle(family.Utf8().c_str(), SkFontStyle())); + sk_sp tf; + if(FontCache::useDirectWrite()) { + tf = font_manager->matchFamilyStyle(family.Utf8().c_str(), SkFontStyle()); + } else { + tf = font_manager->legacyMakeTypeface(family.Utf8().data(), SkFontStyle()); + } if (!tf) return false; @@ -538,4 +546,38 @@ const UChar* GetFallbackFamily(UChar32 character, return family; } +bool GetOutOfProcessFallbackFamily( + UChar32 character, + FontDescription::GenericFamilyType generic_family, + String bcp47_language_tag, + FontFallbackPriority, + const mojo::Remote& service, + String* fallback_family, + SkFontStyle* fallback_style) { + String base_family_name_approximation; + switch (generic_family) { + case FontDescription::kMonospaceFamily: + base_family_name_approximation = kCourierNew; + break; + case FontDescription::kSansSerifFamily: + base_family_name_approximation = kArial; + break; + default: + base_family_name_approximation = kTimesNewRoman; + } + + mojom::blink::FallbackFamilyAndStylePtr fallback_family_and_style; + bool mojo_result = service->FallbackFamilyAndStyleForCodepoint( + base_family_name_approximation, bcp47_language_tag, character, + &fallback_family_and_style); + + SECURITY_DCHECK(fallback_family); + SECURITY_DCHECK(fallback_style); + *fallback_family = fallback_family_and_style->fallback_family_name; + *fallback_style = SkFontStyle( + fallback_family_and_style->weight, fallback_family_and_style->width, + static_cast(fallback_family_and_style->slant)); + return mojo_result; +} + } // namespace blink diff --git a/third_party/blink/renderer/platform/fonts/win/font_fallback_win.h b/third_party/blink/renderer/platform/fonts/win/font_fallback_win.h index 6a51e150dc5b9..254eba78a3639 100644 --- a/third_party/blink/renderer/platform/fonts/win/font_fallback_win.h +++ b/third_party/blink/renderer/platform/fonts/win/font_fallback_win.h @@ -34,10 +34,13 @@ #include #include +#include "mojo/public/cpp/bindings/remote.h" +#include "third_party/blink/public/mojom/dwrite_font_proxy/dwrite_font_proxy.mojom-blink-forward.h" #include "third_party/blink/renderer/platform/fonts/font_description.h" #include "third_party/blink/renderer/platform/fonts/font_fallback_priority.h" #include "third_party/blink/renderer/platform/platform_export.h" #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h" +#include "third_party/skia/include/core/SkFontStyle.h" class SkFontMgr; @@ -55,6 +58,19 @@ PLATFORM_EXPORT const UChar* GetFallbackFamily( FontFallbackPriority, SkFontMgr* font_manager); +// Return a font family that can render |character| based on what script +// that characters belong to by performing an out of process lookup and using +// system fallback API based on IDWriteTextLayout. This method is only to be +// used on pre Windows 8.1, as otherwise IDWriteFontFallback API is available. +PLATFORM_EXPORT bool GetOutOfProcessFallbackFamily( + UChar32 character, + FontDescription::GenericFamilyType, + String bcp47_language_tag, + FontFallbackPriority, + const mojo::Remote& font_proxy, + String* fallback_family, + SkFontStyle* fallback_style); + } // namespace blink #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_FONTS_WIN_FONT_FALLBACK_WIN_H_ diff --git a/tools/metrics/histograms/metadata/windows/histograms.xml b/tools/metrics/histograms/metadata/windows/histograms.xml index aee2749f193b7..247779fee1b3c 100644 --- a/tools/metrics/histograms/metadata/windows/histograms.xml +++ b/tools/metrics/histograms/metadata/windows/histograms.xml @@ -76,30 +76,6 @@ chromium-metrics-reviews@google.com. - - ajgo@chromium.org - wfh@chromium.org - - Whether or not support for {Type} is/are available for use on the system, by - calling IsEnclaveTypeSupported. Reported once per browser session, on - startup. - - - - - - - - - brucedawson@chromium.org diff --git a/tools/page_cycler/acid3 b/tools/page_cycler/acid3 index 6be0a66a1ebd7..a926d0a32e02c 160000 --- a/tools/page_cycler/acid3 +++ b/tools/page_cycler/acid3 @@ -1 +1 @@ -Subproject commit 6be0a66a1ebd7ebc5abc1b2f405a945f6d871521 +Subproject commit a926d0a32e02c4c03ae95bb798e6c780e0e184ba diff --git a/ui/gfx/font_render_params_win.cc b/ui/gfx/font_render_params_win.cc index 4769c5853ee61..825b5f901c60e 100644 --- a/ui/gfx/font_render_params_win.cc +++ b/ui/gfx/font_render_params_win.cc @@ -12,6 +12,7 @@ #include "base/memory/singleton.h" #include "base/win/registry.h" #include "ui/gfx/win/singleton_hwnd_observer.h" +#include "ui/gfx/win/direct_write.h" namespace gfx { @@ -83,7 +84,8 @@ class CachedFontRenderParams { BOOL enabled = false; if (SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &enabled, 0) && enabled) { params_->antialiasing = true; - params_->subpixel_positioning = true; + // GDI does not support subpixel positioning. + params_->subpixel_positioning = win::IsDirectWriteEnabled(); UINT type = 0; if (SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &type, 0) && diff --git a/ui/gfx/switches.cc b/ui/gfx/switches.cc index 9576188a9872f..762135509f974 100644 --- a/ui/gfx/switches.cc +++ b/ui/gfx/switches.cc @@ -8,6 +8,11 @@ #include "build/build_config.h" namespace switches { + +#if BUILDFLAG(IS_WIN) +// Disables the DirectWrite font rendering system on windows. +const char kDisableDirectWrite[] = "disable-direct-write"; +#endif // Scale factor to apply to every animation duration. Must be >= 0.0. This will // only apply to LinearAnimation and its subclasses. diff --git a/ui/gfx/switches.h b/ui/gfx/switches.h index 37011f67b3d33..a838d42264fc1 100644 --- a/ui/gfx/switches.h +++ b/ui/gfx/switches.h @@ -18,6 +18,10 @@ GFX_SWITCHES_EXPORT extern const char kEnableNativeGpuMemoryBuffers[]; GFX_SWITCHES_EXPORT extern const char kForcePrefersReducedMotion[]; GFX_SWITCHES_EXPORT extern const char kHeadless[]; +#if BUILDFLAG(IS_WIN) +GFX_SWITCHES_EXPORT extern const char kDisableDirectWrite[]; +#endif + #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) GFX_SWITCHES_EXPORT extern const char kX11Display[]; GFX_SWITCHES_EXPORT extern const char kNoXshm[]; diff --git a/ui/gfx/win/direct_write.cc b/ui/gfx/win/direct_write.cc index ff07f3fc157b0..77e9ef71203a4 100644 --- a/ui/gfx/win/direct_write.cc +++ b/ui/gfx/win/direct_write.cc @@ -8,6 +8,7 @@ #include +#include "base/command_line.h" #include "base/debug/alias.h" #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" @@ -20,12 +21,30 @@ namespace gfx { namespace win { + +GFX_EXPORT bool ShouldUseDirectWrite() { + // If the flag is currently on, and we're on WinVista or above, we enable + // DirectWrite. There is no reason to not install Platform Update or + // even use the Windows 7 Platform Update dwrite.dll with the extended kernel. + if (base::win::GetVersion() < base::win::Version::VISTA) { + return false; + } + // If forced off, don't use it. + const base::CommandLine& command_line = + *base::CommandLine::ForCurrentProcess(); + return !command_line.HasSwitch("disable-direct-write"); +} namespace { +static bool dwrite_enabled = false; + // Pointer to the global IDWriteFactory interface. IDWriteFactory* g_direct_write_factory = nullptr; + + + void SetDirectWriteFactory(IDWriteFactory* factory) { DCHECK(!g_direct_write_factory); // We grab a reference on the DirectWrite factory. This reference is @@ -37,9 +56,24 @@ void SetDirectWriteFactory(IDWriteFactory* factory) { } // anonymous namespace void CreateDWriteFactory(IDWriteFactory** factory) { + if (!gfx::win::ShouldUseDirectWrite()) + return; + + using DWriteCreateFactoryProc = decltype(DWriteCreateFactory)*; + HMODULE dwrite_dll = LoadLibraryW(L"dwrite.dll"); + if (!dwrite_dll) + return; + + DWriteCreateFactoryProc dwrite_create_factory_proc = + reinterpret_cast( + GetProcAddress(dwrite_dll, "DWriteCreateFactory")); + if (!dwrite_create_factory_proc) + return; + Microsoft::WRL::ComPtr factory_unknown; + HRESULT hr = - DWriteCreateFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), + dwrite_create_factory_proc(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), &factory_unknown); if (FAILED(hr)) { base::debug::Alias(&hr); @@ -59,7 +93,11 @@ void InitializeDirectWrite() { Microsoft::WRL::ComPtr factory; CreateDWriteFactory(&factory); - CHECK(!!factory); + if (factory == nullptr) { + sk_sp direct_write_font_mgr = SkFontMgr_New_GDI(); + skia::OverrideDefaultSkFontMgr(std::move(direct_write_font_mgr)); + return; + } SetDirectWriteFactory(factory.Get()); // The skia call to create a new DirectWrite font manager instance can fail @@ -89,11 +127,11 @@ void InitializeDirectWrite() { iteration = -1; base::UmaHistogramSparse("DirectWrite.Fonts.Gfx.InitializeLoopCount", iteration); - // TODO(crbug.com/956064): Move to a CHECK when the cause of the crash is - // fixed and remove the if statement that fallback to GDI font manager. DCHECK(!!direct_write_font_mgr); if (!direct_write_font_mgr) direct_write_font_mgr = SkFontMgr_New_GDI(); + else + dwrite_enabled = true; // Override the default skia font manager. This must be called before any // use of the skia font manager is done (e.g. before any call to @@ -101,6 +139,10 @@ void InitializeDirectWrite() { skia::OverrideDefaultSkFontMgr(std::move(direct_write_font_mgr)); } +bool IsDirectWriteEnabled() { + return dwrite_enabled; +} + IDWriteFactory* GetDirectWriteFactory() { // Some unittests may access this accessor without any previous call to // |InitializeDirectWrite|. A call to |InitializeDirectWrite| after this diff --git a/ui/gfx/win/direct_write.h b/ui/gfx/win/direct_write.h index 72022056d29c4..6185acdd2c2de 100644 --- a/ui/gfx/win/direct_write.h +++ b/ui/gfx/win/direct_write.h @@ -13,10 +13,16 @@ namespace gfx { namespace win { + +// Returns whether DirectWrite font rendering should be used. +GFX_EXPORT bool ShouldUseDirectWrite(); GFX_EXPORT void InitializeDirectWrite(); -// Creates a DirectWrite factory. +// Returns true if we are using DirectWrite for font metrics and rendering. +GFX_EXPORT bool IsDirectWriteEnabled(); + +// Creates a DirectWrite factory, if using DirectWrite. GFX_EXPORT void CreateDWriteFactory(IDWriteFactory** factory); // Returns the global DirectWrite factory. diff --git a/ui/native_theme/native_theme.cc b/ui/native_theme/native_theme.cc index f6556e141a76b..d5a59986f315a 100644 --- a/ui/native_theme/native_theme.cc +++ b/ui/native_theme/native_theme.cc @@ -7,6 +7,7 @@ #include #include "base/command_line.h" +#include "base/features.h" #include "base/containers/fixed_flat_map.h" #include "base/functional/bind.h" #include "base/logging.h" @@ -205,7 +206,7 @@ void NativeTheme::SetPreferredContrast( bool NativeTheme::IsForcedDarkMode() { static bool kIsForcedDarkMode = base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kForceDarkMode); + switches::kForceDarkMode) || base::FeatureList::IsEnabled(base::features::kForceDarkModeFlag); return kIsForcedDarkMode; } diff --git a/ui/snapshot/snapshot_win.cc b/ui/snapshot/snapshot_win.cc index 74d600db1ea5e..d6b9e8fcadd25 100644 --- a/ui/snapshot/snapshot_win.cc +++ b/ui/snapshot/snapshot_win.cc @@ -5,7 +5,6 @@ #include "ui/snapshot/snapshot_win.h" #include -#include #include "base/functional/callback.h" #include "base/win/windows_version.h" @@ -21,6 +20,17 @@ #include "ui/snapshot/snapshot.h" #include "ui/snapshot/snapshot_aura.h" +namespace { + +// Windows 8.1 is the first version that supports PW_RENDERFULLCONTENT. +// Without that flag PrintWindow may not correctly capture what's actually +// onscreen. +bool UseAuraSnapshot() { + return (base::win::GetVersion() < base::win::Version::WIN8_1); +} + +} // namespace + namespace ui { namespace internal { @@ -29,6 +39,7 @@ bool GrabHwndSnapshot(HWND window_handle, const gfx::Rect& snapshot_bounds_in_pixels, const gfx::Rect& clip_rect_in_pixels, gfx::Image* image) { + BOOL result = false; gfx::Rect snapshot_bounds_in_window = snapshot_bounds_in_pixels + clip_rect_in_pixels.OffsetFromOrigin(); gfx::Size bitmap_size(snapshot_bounds_in_window.right(), @@ -43,8 +54,32 @@ bool GrabHwndSnapshot(HWND window_handle, // but works starting in Windows 8.1. It allows for capturing the contents of // the window that are drawn using DirectComposition. UINT flags = PW_CLIENTONLY | PW_RENDERFULLCONTENT; - - BOOL result = PrintWindow(window_handle, mem_hdc, flags); + + if (base::win::GetVersion() >= base::win::Version::WIN8_1){ + result = PrintWindow(window_handle, mem_hdc, flags); + } + else { + // PrintWindow does not work for pre-Windows 8.1. So we'll use BitBlt. + // Copying from the window's actual HDC doesn't work so let's just use the full screen HDC. + // When a snapshot is captured the focus should be on the browser window anyway. + HDC window_hdc = GetDC(NULL); + + RECT window_rect; + + memset(&window_rect, 0, sizeof(RECT)); + + result = GetWindowRect(window_handle, &window_rect); + + if (!result) { + PLOG(ERROR) << "Failed to get valid rect for snapshot area."; + return false; + } + // The left of the snapshot "window" rect is offset by 8 pixels to remove a bit of the dark grey showing through. + result = BitBlt(mem_hdc, 0, 0, bitmap_size.width(), bitmap_size.height(), + window_hdc, window_rect.left + 8, window_rect.top, SRCCOPY); + + DeleteDC(window_hdc); + } if (!result) { PLOG(ERROR) << "Failed to print window"; return false; @@ -85,6 +120,11 @@ bool GrabViewSnapshot(gfx::NativeView view_handle, bool GrabWindowSnapshot(gfx::NativeWindow window_handle, const gfx::Rect& snapshot_bounds, gfx::Image* image) { + if (UseAuraSnapshot()) { + // Not supported in Aura. Callers should fall back to the async version. + return false; + } + DCHECK(window_handle); gfx::Rect window_bounds = window_handle->GetBoundsInRootWindow(); aura::WindowTreeHost* host = window_handle->GetHost(); @@ -109,6 +149,10 @@ bool GrabWindowSnapshot(gfx::NativeWindow window_handle, void GrabWindowSnapshotAsync(gfx::NativeWindow window, const gfx::Rect& source_rect, GrabWindowSnapshotAsyncCallback callback) { + if (UseAuraSnapshot()) { + GrabWindowSnapshotAsyncAura(window, source_rect, std::move(callback)); + return; + } gfx::Image image; GrabWindowSnapshot(window, source_rect, &image); std::move(callback).Run(image); @@ -117,6 +161,10 @@ void GrabWindowSnapshotAsync(gfx::NativeWindow window, void GrabViewSnapshotAsync(gfx::NativeView view, const gfx::Rect& source_rect, GrabWindowSnapshotAsyncCallback callback) { + if (UseAuraSnapshot()) { + GrabWindowSnapshotAsyncAura(view, source_rect, std::move(callback)); + return; + } NOTIMPLEMENTED(); std::move(callback).Run(gfx::Image()); } @@ -125,8 +173,13 @@ void GrabWindowSnapshotAndScaleAsync(gfx::NativeWindow window, const gfx::Rect& source_rect, const gfx::Size& target_size, GrabWindowSnapshotAsyncCallback callback) { + if (UseAuraSnapshot()) { + GrabWindowSnapshotAndScaleAsyncAura(window, source_rect, target_size, + std::move(callback)); + return; + } NOTIMPLEMENTED(); std::move(callback).Run(gfx::Image()); } -} // namespace ui +} // namespace ui \ No newline at end of file diff --git a/ui/views/controls/scrollbar/scroll_bar.cc b/ui/views/controls/scrollbar/scroll_bar.cc index da848fcdc1784..4184e62a9d93f 100644 --- a/ui/views/controls/scrollbar/scroll_bar.cc +++ b/ui/views/controls/scrollbar/scroll_bar.cc @@ -438,6 +438,11 @@ void ScrollBar::TrackClicked() { void ScrollBar::ScrollContentsToOffset() { ScrollToPosition(contents_scroll_offset_); + // Safeguard against a divide-by-zero bug that happens when separating + // multiple tabs into separate windows. + contents_size_ = std::max(1, contents_size_); + viewport_size_ = std::max(1, viewport_size_); + thumb_->SetPosition(CalculateThumbPosition(contents_scroll_offset_)); } diff --git a/ui/views/win/hwnd_message_handler.cc b/ui/views/win/hwnd_message_handler.cc index ca4de35de6e95..48b4310395086 100644 --- a/ui/views/win/hwnd_message_handler.cc +++ b/ui/views/win/hwnd_message_handler.cc @@ -3624,7 +3624,6 @@ bool HWNDMessageHandler::IsSynthesizedMouseMessage(unsigned int message, } void HWNDMessageHandler::PerformDwmTransition() { - CHECK(IsFrameSystemDrawn()); dwm_transition_desired_ = false; delegate_->HandleFrameChanged();