diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-07-31 15:50:41 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2019-08-30 12:35:23 +0000 |
commit | 7b2ffa587235a47d4094787d72f38102089f402a (patch) | |
tree | 30e82af9cbab08a7fa028bb18f4f2987a3f74dfa /chromium/ui/base | |
parent | d94af01c90575348c4e81a418257f254b6f8d225 (diff) | |
download | qtwebengine-chromium-7b2ffa587235a47d4094787d72f38102089f402a.tar.gz |
BASELINE: Update Chromium to 76.0.3809.94
Change-Id: I321c3f5f929c105aec0f98c5091ef6108822e647
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/ui/base')
133 files changed, 2304 insertions, 872 deletions
diff --git a/chromium/ui/base/BUILD.gn b/chromium/ui/base/BUILD.gn index 960d5835fcf..20371e7cd4e 100644 --- a/chromium/ui/base/BUILD.gn +++ b/chromium/ui/base/BUILD.gn @@ -44,6 +44,7 @@ component("ui_data_pack") { deps = [ "//base", + "//net", ] defines = [ "UI_DATA_PACK_IMPLEMENTATION" ] @@ -108,18 +109,12 @@ jumbo_component("base") { "cocoa/focus_window_set.mm", "cocoa/menu_controller.h", "cocoa/menu_controller.mm", - "cocoa/nib_loading.h", - "cocoa/nib_loading.mm", - "cocoa/ns_view_ids.h", - "cocoa/ns_view_ids.mm", "cocoa/quartz_util.h", "cocoa/quartz_util.mm", "cocoa/remote_accessibility_api.h", "cocoa/remote_accessibility_api.mm", "cocoa/remote_layer_api.h", "cocoa/remote_layer_api.mm", - "cocoa/remote_views_window.h", - "cocoa/remote_views_window.mm", "cocoa/secure_password_input.h", "cocoa/secure_password_input.mm", "cocoa/text_services_context_menu.cc", @@ -223,7 +218,6 @@ jumbo_component("base") { "page_transition_types.h", "platform_window_defaults.cc", "platform_window_defaults.h", - "property_data.h", "resource/resource_bundle.cc", "resource/resource_bundle.h", "resource/resource_bundle_android.cc", @@ -484,11 +478,6 @@ jumbo_component("base") { } } - if (use_aura) { - deps += [ "//ui/events" ] - sources += [ "window_tracker_template.h" ] - } - if (!use_aura || !is_linux) { sources -= [ "resource/resource_bundle_auralinux.cc" ] } @@ -670,6 +659,8 @@ jumbo_static_library("test_support") { "test/cocoa_helper.mm", "test/menu_test_observer.h", "test/menu_test_observer.mm", + "test/ns_ax_tree_validator.h", + "test/ns_ax_tree_validator.mm", "test/nswindow_fullscreen_notification_waiter.h", "test/nswindow_fullscreen_notification_waiter.mm", "test/scoped_fake_full_keyboard_access.h", diff --git a/chromium/ui/base/accelerators/accelerator.h b/chromium/ui/base/accelerators/accelerator.h index 5ece4bf4168..9bcf7de889d 100644 --- a/chromium/ui/base/accelerators/accelerator.h +++ b/chromium/ui/base/accelerators/accelerator.h @@ -125,7 +125,7 @@ class UI_BASE_EXPORT Accelerator { bool interrupted_by_mouse_event_; // The |source_device_id_| of the KeyEvent. - int source_device_id_ = -1; + int source_device_id_ = ui::ED_UNKNOWN_DEVICE; }; // An interface that classes that want to register for keyboard accelerators diff --git a/chromium/ui/base/accelerators/global_media_keys_listener_win.h b/chromium/ui/base/accelerators/global_media_keys_listener_win.h index 4b95a20b1dc..8de4946c61c 100644 --- a/chromium/ui/base/accelerators/global_media_keys_listener_win.h +++ b/chromium/ui/base/accelerators/global_media_keys_listener_win.h @@ -32,6 +32,7 @@ class UI_BASE_EXPORT GlobalMediaKeysListenerWin : public MediaKeysListener { // MediaKeysListener implementation. bool StartWatchingMediaKey(KeyboardCode key_code) override; void StopWatchingMediaKey(KeyboardCode key_code) override; + void SetIsMediaPlaying(bool is_playing) override {} private: // Called by SingletonHwndObserver. diff --git a/chromium/ui/base/accelerators/media_keys_listener.h b/chromium/ui/base/accelerators/media_keys_listener.h index 9072523c656..997718d8aff 100644 --- a/chromium/ui/base/accelerators/media_keys_listener.h +++ b/chromium/ui/base/accelerators/media_keys_listener.h @@ -49,6 +49,13 @@ class UI_BASE_EXPORT MediaKeysListener { virtual bool StartWatchingMediaKey(KeyboardCode key_code) = 0; // Stop listening for a given media key. virtual void StopWatchingMediaKey(KeyboardCode key_code) = 0; + + // Informs the listener whether or not media is currently playing. In some + // implementations this will prevent us from calling PlayPause unnecessarily. + // TODO(https://crbug.com/974035): Once the MediaKeysListenerManager has been + // refactored to work with system media controls this should no longer be + // needed and should be deleted. + virtual void SetIsMediaPlaying(bool is_playing) = 0; }; } // namespace ui diff --git a/chromium/ui/base/accelerators/media_keys_listener_mac.mm b/chromium/ui/base/accelerators/media_keys_listener_mac.mm index 71b417ee8b6..f4e3126a4ef 100644 --- a/chromium/ui/base/accelerators/media_keys_listener_mac.mm +++ b/chromium/ui/base/accelerators/media_keys_listener_mac.mm @@ -46,6 +46,7 @@ class MediaKeysListenerImpl : public MediaKeysListener { // MediaKeysListener: bool StartWatchingMediaKey(KeyboardCode key_code) override; void StopWatchingMediaKey(KeyboardCode key_code) override; + void SetIsMediaPlaying(bool is_playing) override {} private: // Callback on media key event. diff --git a/chromium/ui/base/accelerators/mpris_media_keys_listener.cc b/chromium/ui/base/accelerators/mpris_media_keys_listener.cc index 7edd4913fdb..816538497ad 100644 --- a/chromium/ui/base/accelerators/mpris_media_keys_listener.cc +++ b/chromium/ui/base/accelerators/mpris_media_keys_listener.cc @@ -88,6 +88,10 @@ void MprisMediaKeysListener::StopWatchingMediaKey(KeyboardCode key_code) { } } +void MprisMediaKeysListener::SetIsMediaPlaying(bool is_playing) { + is_media_playing_ = is_playing; +} + void MprisMediaKeysListener::OnNext() { MaybeSendKeyCode(VKEY_MEDIA_NEXT_TRACK); } @@ -97,7 +101,8 @@ void MprisMediaKeysListener::OnPrevious() { } void MprisMediaKeysListener::OnPause() { - MaybeSendKeyCode(VKEY_MEDIA_PLAY_PAUSE); + if (is_media_playing_) + MaybeSendKeyCode(VKEY_MEDIA_PLAY_PAUSE); } void MprisMediaKeysListener::OnPlayPause() { @@ -109,7 +114,8 @@ void MprisMediaKeysListener::OnStop() { } void MprisMediaKeysListener::OnPlay() { - MaybeSendKeyCode(VKEY_MEDIA_PLAY_PAUSE); + if (!is_media_playing_) + MaybeSendKeyCode(VKEY_MEDIA_PLAY_PAUSE); } void MprisMediaKeysListener::MaybeSendKeyCode(KeyboardCode key_code) { diff --git a/chromium/ui/base/accelerators/mpris_media_keys_listener.h b/chromium/ui/base/accelerators/mpris_media_keys_listener.h index 166b2492851..ff95808bdc1 100644 --- a/chromium/ui/base/accelerators/mpris_media_keys_listener.h +++ b/chromium/ui/base/accelerators/mpris_media_keys_listener.h @@ -35,6 +35,7 @@ class UI_BASE_EXPORT MprisMediaKeysListener // MediaKeysListener implementation. bool StartWatchingMediaKey(KeyboardCode key_code) override; void StopWatchingMediaKey(KeyboardCode key_code) override; + void SetIsMediaPlaying(bool is_playing) override; // mpris::MprisServiceObserver implementation. void OnNext() override; @@ -57,6 +58,7 @@ class UI_BASE_EXPORT MprisMediaKeysListener MediaKeysListener::Delegate* delegate_; base::flat_set<KeyboardCode> key_codes_; mpris::MprisService* service_ = nullptr; + bool is_media_playing_ = false; DISALLOW_COPY_AND_ASSIGN(MprisMediaKeysListener); }; diff --git a/chromium/ui/base/accelerators/mpris_media_keys_listener_unittest.cc b/chromium/ui/base/accelerators/mpris_media_keys_listener_unittest.cc index 29b570e37ed..5f4eb42dea8 100644 --- a/chromium/ui/base/accelerators/mpris_media_keys_listener_unittest.cc +++ b/chromium/ui/base/accelerators/mpris_media_keys_listener_unittest.cc @@ -118,4 +118,32 @@ TEST_F(MprisMediaKeysListenerTest, ListenForMultipleKeys) { listener()->OnPrevious(); } +TEST_F(MprisMediaKeysListenerTest, DoesNotFirePlayPauseOnPauseEventWhenPaused) { + // Should be set to true when we start listening for the key. + EXPECT_CALL(mock_mpris_service(), SetCanPlay(true)); + EXPECT_CALL(mock_mpris_service(), SetCanPause(true)); + EXPECT_CALL(delegate(), OnMediaKeysAccelerator(_)).Times(0); + + listener()->Initialize(); + listener()->StartWatchingMediaKey(ui::VKEY_MEDIA_PLAY_PAUSE); + listener()->SetIsMediaPlaying(false); + + // Simulate media key press. + listener()->OnPause(); +} + +TEST_F(MprisMediaKeysListenerTest, DoesNotFirePlayPauseOnPlayEventWhenPlaying) { + // Should be set to true when we start listening for the key. + EXPECT_CALL(mock_mpris_service(), SetCanPlay(true)); + EXPECT_CALL(mock_mpris_service(), SetCanPause(true)); + EXPECT_CALL(delegate(), OnMediaKeysAccelerator(_)).Times(0); + + listener()->Initialize(); + listener()->StartWatchingMediaKey(ui::VKEY_MEDIA_PLAY_PAUSE); + listener()->SetIsMediaPlaying(true); + + // Simulate media key press. + listener()->OnPlay(); +} + } // namespace ui diff --git a/chromium/ui/base/accelerators/remote_command_media_keys_listener_mac.h b/chromium/ui/base/accelerators/remote_command_media_keys_listener_mac.h index e07717568f0..192c921ab29 100644 --- a/chromium/ui/base/accelerators/remote_command_media_keys_listener_mac.h +++ b/chromium/ui/base/accelerators/remote_command_media_keys_listener_mac.h @@ -37,6 +37,7 @@ class UI_BASE_EXPORT API_AVAILABLE(macos(10.12.2)) // MediaKeysListener implementation. bool StartWatchingMediaKey(KeyboardCode key_code) override; void StopWatchingMediaKey(KeyboardCode key_code) override; + void SetIsMediaPlaying(bool is_playing) override; // now_playing::RemoteCommandCenterDelegateObserver implementation. void OnNext() override; @@ -61,6 +62,7 @@ class UI_BASE_EXPORT API_AVAILABLE(macos(10.12.2)) base::flat_set<KeyboardCode> key_codes_; now_playing::RemoteCommandCenterDelegate* remote_command_center_delegate_ = nullptr; + bool is_media_playing_ = false; DISALLOW_COPY_AND_ASSIGN(RemoteCommandMediaKeysListenerMac); }; diff --git a/chromium/ui/base/accelerators/remote_command_media_keys_listener_mac.mm b/chromium/ui/base/accelerators/remote_command_media_keys_listener_mac.mm index ddcc54426c3..0f7265d6cea 100644 --- a/chromium/ui/base/accelerators/remote_command_media_keys_listener_mac.mm +++ b/chromium/ui/base/accelerators/remote_command_media_keys_listener_mac.mm @@ -91,6 +91,10 @@ void RemoteCommandMediaKeysListenerMac::StopWatchingMediaKey( } } +void RemoteCommandMediaKeysListenerMac::SetIsMediaPlaying(bool is_playing) { + is_media_playing_ = is_playing; +} + void RemoteCommandMediaKeysListenerMac::OnNext() { MaybeSend(VKEY_MEDIA_NEXT_TRACK); } @@ -100,7 +104,8 @@ void RemoteCommandMediaKeysListenerMac::OnPrevious() { } void RemoteCommandMediaKeysListenerMac::OnPause() { - MaybeSend(VKEY_MEDIA_PLAY_PAUSE); + if (is_media_playing_) + MaybeSend(VKEY_MEDIA_PLAY_PAUSE); } void RemoteCommandMediaKeysListenerMac::OnPlayPause() { @@ -112,7 +117,8 @@ void RemoteCommandMediaKeysListenerMac::OnStop() { } void RemoteCommandMediaKeysListenerMac::OnPlay() { - MaybeSend(VKEY_MEDIA_PLAY_PAUSE); + if (!is_media_playing_) + MaybeSend(VKEY_MEDIA_PLAY_PAUSE); } void RemoteCommandMediaKeysListenerMac::MaybeSend(KeyboardCode key_code) { diff --git a/chromium/ui/base/accelerators/remote_command_media_keys_listener_mac_unittest.mm b/chromium/ui/base/accelerators/remote_command_media_keys_listener_mac_unittest.mm index 123b3f00497..c5860602e3b 100644 --- a/chromium/ui/base/accelerators/remote_command_media_keys_listener_mac_unittest.mm +++ b/chromium/ui/base/accelerators/remote_command_media_keys_listener_mac_unittest.mm @@ -118,4 +118,50 @@ TEST_F(RemoteCommandMediaKeysListenerMacTest, ListenForMultipleKeys) { } } +TEST_F(RemoteCommandMediaKeysListenerMacTest, + DoesNotFirePlayPauseOnPauseEventWhenPaused) { + if (@available(macOS 10.12.2, *)) { + now_playing::MockRemoteCommandCenterDelegate rcc_delegate; + MockMediaKeysListenerDelegate delegate; + RemoteCommandMediaKeysListenerMac listener(&delegate); + listener.SetRemoteCommandCenterDelegateForTesting(&rcc_delegate); + + EXPECT_CALL(rcc_delegate, AddObserver(&listener)); + EXPECT_CALL(rcc_delegate, SetCanPlayPause(true)); + EXPECT_CALL(rcc_delegate, SetCanPlay(true)); + EXPECT_CALL(rcc_delegate, SetCanPause(true)); + EXPECT_CALL(delegate, OnMediaKeysAccelerator(_)).Times(0); + + listener.Initialize(); + listener.StartWatchingMediaKey(ui::VKEY_MEDIA_PLAY_PAUSE); + listener.SetIsMediaPlaying(false); + + // Simulate media key press. + listener.OnPause(); + } +} + +TEST_F(RemoteCommandMediaKeysListenerMacTest, + DoesNotFirePlayPauseOnPlayEventWhenPlaying) { + if (@available(macOS 10.12.2, *)) { + now_playing::MockRemoteCommandCenterDelegate rcc_delegate; + MockMediaKeysListenerDelegate delegate; + RemoteCommandMediaKeysListenerMac listener(&delegate); + listener.SetRemoteCommandCenterDelegateForTesting(&rcc_delegate); + + EXPECT_CALL(rcc_delegate, AddObserver(&listener)); + EXPECT_CALL(rcc_delegate, SetCanPlayPause(true)); + EXPECT_CALL(rcc_delegate, SetCanPlay(true)); + EXPECT_CALL(rcc_delegate, SetCanPause(true)); + EXPECT_CALL(delegate, OnMediaKeysAccelerator(_)).Times(0); + + listener.Initialize(); + listener.StartWatchingMediaKey(ui::VKEY_MEDIA_PLAY_PAUSE); + listener.SetIsMediaPlaying(true); + + // Simulate media key press. + listener.OnPlay(); + } +} + } // namespace ui diff --git a/chromium/ui/base/accelerators/system_media_controls_media_keys_listener.cc b/chromium/ui/base/accelerators/system_media_controls_media_keys_listener.cc index a07e0eb4d7b..f4de72a8323 100644 --- a/chromium/ui/base/accelerators/system_media_controls_media_keys_listener.cc +++ b/chromium/ui/base/accelerators/system_media_controls_media_keys_listener.cc @@ -104,6 +104,10 @@ void SystemMediaControlsMediaKeysListener::StopWatchingMediaKey( } } +void SystemMediaControlsMediaKeysListener::SetIsMediaPlaying(bool is_playing) { + is_media_playing_ = is_playing; +} + void SystemMediaControlsMediaKeysListener::OnNext() { MaybeSendKeyCode(VKEY_MEDIA_NEXT_TRACK); } @@ -113,7 +117,8 @@ void SystemMediaControlsMediaKeysListener::OnPrevious() { } void SystemMediaControlsMediaKeysListener::OnPause() { - MaybeSendKeyCode(VKEY_MEDIA_PLAY_PAUSE); + if (is_media_playing_) + MaybeSendKeyCode(VKEY_MEDIA_PLAY_PAUSE); } void SystemMediaControlsMediaKeysListener::OnStop() { @@ -121,7 +126,8 @@ void SystemMediaControlsMediaKeysListener::OnStop() { } void SystemMediaControlsMediaKeysListener::OnPlay() { - MaybeSendKeyCode(VKEY_MEDIA_PLAY_PAUSE); + if (!is_media_playing_) + MaybeSendKeyCode(VKEY_MEDIA_PLAY_PAUSE); } void SystemMediaControlsMediaKeysListener::MaybeSendKeyCode( diff --git a/chromium/ui/base/accelerators/system_media_controls_media_keys_listener.h b/chromium/ui/base/accelerators/system_media_controls_media_keys_listener.h index c900d8414c9..1bc133b7af6 100644 --- a/chromium/ui/base/accelerators/system_media_controls_media_keys_listener.h +++ b/chromium/ui/base/accelerators/system_media_controls_media_keys_listener.h @@ -35,6 +35,7 @@ class UI_BASE_EXPORT SystemMediaControlsMediaKeysListener // MediaKeysListener implementation. bool StartWatchingMediaKey(KeyboardCode key_code) override; void StopWatchingMediaKey(KeyboardCode key_code) override; + void SetIsMediaPlaying(bool is_playing) override; // system_media_controls::SystemMediaControlsServiceObserver implementation. void OnNext() override; @@ -61,6 +62,8 @@ class UI_BASE_EXPORT SystemMediaControlsMediaKeysListener system_media_controls::SystemMediaControlsService* service_ = nullptr; + bool is_media_playing_ = false; + DISALLOW_COPY_AND_ASSIGN(SystemMediaControlsMediaKeysListener); }; diff --git a/chromium/ui/base/accelerators/system_media_controls_media_keys_listener_unittest.cc b/chromium/ui/base/accelerators/system_media_controls_media_keys_listener_unittest.cc index b07c9a59eef..f4d20b0e35f 100644 --- a/chromium/ui/base/accelerators/system_media_controls_media_keys_listener_unittest.cc +++ b/chromium/ui/base/accelerators/system_media_controls_media_keys_listener_unittest.cc @@ -132,4 +132,36 @@ TEST_F(SystemMediaControlsMediaKeysListenerTest, ListenForMultipleKeys) { listener()->OnPrevious(); } +TEST_F(SystemMediaControlsMediaKeysListenerTest, + DoesNotFirePlayPauseOnPauseEventWhenPaused) { + // Should be set to true when we start listening for the key. + EXPECT_CALL(mock_system_media_controls_service(), SetIsPlayEnabled(true)); + EXPECT_CALL(mock_system_media_controls_service(), SetIsPauseEnabled(true)); + + EXPECT_CALL(delegate(), OnMediaKeysAccelerator(_)).Times(0); + + ASSERT_TRUE(listener()->Initialize()); + listener()->StartWatchingMediaKey(ui::VKEY_MEDIA_PLAY_PAUSE); + listener()->SetIsMediaPlaying(false); + + // Simulate media key press. + listener()->OnPause(); +} + +TEST_F(SystemMediaControlsMediaKeysListenerTest, + DoesNotFirePlayPauseOnPlayEventWhenPlaying) { + // Should be set to true when we start listening for the key. + EXPECT_CALL(mock_system_media_controls_service(), SetIsPlayEnabled(true)); + EXPECT_CALL(mock_system_media_controls_service(), SetIsPauseEnabled(true)); + + EXPECT_CALL(delegate(), OnMediaKeysAccelerator(_)).Times(0); + + ASSERT_TRUE(listener()->Initialize()); + listener()->StartWatchingMediaKey(ui::VKEY_MEDIA_PLAY_PAUSE); + listener()->SetIsMediaPlaying(true); + + // Simulate media key press. + listener()->OnPlay(); +} + } // namespace ui diff --git a/chromium/ui/base/class_property.cc b/chromium/ui/base/class_property.cc index 162ac890cb8..d6a5378aeb9 100644 --- a/chromium/ui/base/class_property.cc +++ b/chromium/ui/base/class_property.cc @@ -20,14 +20,6 @@ int64_t PropertyHandler::SetPropertyInternal(const void* key, PropertyDeallocator deallocator, int64_t value, int64_t default_value) { - // TODO(https://crbug.com/952087): ideally this code would early out if the - // value isn't changing. Unfortunately that breaks some assumptions. - // |is_value_changing| is a best guess at whether the value is changing, and - // doesn't handle non-POD types. - const bool is_value_changing = - value != GetPropertyInternal(key, default_value); - std::unique_ptr<PropertyData> data = - BeforePropertyChange(key, is_value_changing); int64_t old = GetPropertyInternal(key, default_value); if (value == default_value) { prop_map_.erase(key); @@ -38,16 +30,10 @@ int64_t PropertyHandler::SetPropertyInternal(const void* key, prop_value.deallocator = deallocator; prop_map_[key] = prop_value; } - AfterPropertyChange(key, old, std::move(data)); + AfterPropertyChange(key, old); return old; } -std::unique_ptr<PropertyData> PropertyHandler::BeforePropertyChange( - const void* key, - bool is_value_changing) { - return nullptr; -} - void PropertyHandler::ClearProperties() { // Clear properties. for (std::map<const void*, Value>::const_iterator iter = prop_map_.begin(); diff --git a/chromium/ui/base/class_property.h b/chromium/ui/base/class_property.h index 7da967e1293..40bcdeb99f5 100644 --- a/chromium/ui/base/class_property.h +++ b/chromium/ui/base/class_property.h @@ -8,11 +8,9 @@ #include <stdint.h> #include <map> -#include <memory> #include <set> #include "base/time/time.h" -#include "ui/base/property_data.h" #include "ui/base/ui_base_export.h" #include "ui/base/ui_base_types.h" @@ -93,16 +91,7 @@ class UI_BASE_EXPORT PropertyHandler { protected: friend class subtle::PropertyHelper; - // Called from SetPropertyInternal() prior to changing the value. If - // |is_value_changing| is false, the new value is the same as the old value - // (in other words, the value isn't really changing, but observers will - // still be notified). - virtual std::unique_ptr<PropertyData> BeforePropertyChange( - const void* key, - bool is_value_changing); - virtual void AfterPropertyChange(const void* key, - int64_t old_value, - std::unique_ptr<PropertyData> data) {} + virtual void AfterPropertyChange(const void* key, int64_t old_value) {} void ClearProperties(); // Called by the public {Set,Get,Clear}Property functions. diff --git a/chromium/ui/base/clipboard/clipboard.h b/chromium/ui/base/clipboard/clipboard.h index 913149dbd93..f6ca0055401 100644 --- a/chromium/ui/base/clipboard/clipboard.h +++ b/chromium/ui/base/clipboard/clipboard.h @@ -129,6 +129,7 @@ class COMPONENT_EXPORT(BASE_CLIPBOARD) Clipboard : public base::ThreadChecker { base::string16* result) const = 0; // Reads a bookmark from the clipboard, if available. + // |title| or |url| may be null. virtual void ReadBookmark(base::string16* title, std::string* url) const = 0; // Reads raw data from the clipboard with the given format type. Stores result @@ -146,8 +147,8 @@ class COMPONENT_EXPORT(BASE_CLIPBOARD) Clipboard : public base::ThreadChecker { protected: static Clipboard* Create(); - Clipboard() {} - virtual ~Clipboard() {} + Clipboard() = default; + virtual ~Clipboard() = default; // ObjectType designates the type of data to be stored in the clipboard. This // designation is shared across all OSes. The system-specific designation diff --git a/chromium/ui/base/clipboard/clipboard_aura.cc b/chromium/ui/base/clipboard/clipboard_aura.cc index 653b3ac1f6a..7d73d97f6ba 100644 --- a/chromium/ui/base/clipboard/clipboard_aura.cc +++ b/chromium/ui/base/clipboard/clipboard_aura.cc @@ -50,7 +50,7 @@ class ClipboardData { : web_smart_paste_(false), format_(0) {} - virtual ~ClipboardData() {} + virtual ~ClipboardData() = default; // Bitmask of AuraClipboardFormat types. int format() const { return format_; } @@ -164,7 +164,7 @@ class AuraClipboard { AuraClipboard() : sequence_number_(0) { } - ~AuraClipboard() {} + ~AuraClipboard() = default; void Clear() { sequence_number_++; @@ -279,14 +279,18 @@ class AuraClipboard { // Reads bookmark from the data at the top of clipboard stack. void ReadBookmark(base::string16* title, std::string* url) const { - title->clear(); - url->clear(); + if (title) + title->clear(); + if (url) + url->clear(); if (!HasFormat(BOOKMARK)) return; const ClipboardData* data = GetData(); - *title = base::UTF8ToUTF16(data->bookmark_title()); - *url = data->bookmark_url(); + if (title) + *title = base::UTF8ToUTF16(data->bookmark_title()); + if (url) + *url = data->bookmark_url(); } void ReadData(const std::string& type, std::string* result) const { diff --git a/chromium/ui/base/clipboard/clipboard_constants.h b/chromium/ui/base/clipboard/clipboard_constants.h index 8c499112292..9f114db255e 100644 --- a/chromium/ui/base/clipboard/clipboard_constants.h +++ b/chromium/ui/base/clipboard/clipboard_constants.h @@ -20,7 +20,7 @@ class NSString; namespace ui { -#if defined(OS_MACOSX) && !defined(USE_AURA) +#if defined(OS_MACOSX) COMPONENT_EXPORT(BASE_CLIPBOARD_TYPES) extern NSString* const kWebCustomDataPboardType; #endif diff --git a/chromium/ui/base/clipboard/clipboard_format_type.h b/chromium/ui/base/clipboard/clipboard_format_type.h index eb5b91ff29f..07d8e08273e 100644 --- a/chromium/ui/base/clipboard/clipboard_format_type.h +++ b/chromium/ui/base/clipboard/clipboard_format_type.h @@ -5,6 +5,8 @@ #ifndef UI_BASE_CLIPBOARD_CLIPBOARD_FORMAT_TYPE_H_ #define UI_BASE_CLIPBOARD_CLIPBOARD_FORMAT_TYPE_H_ +#include <map> +#include <memory> #include <string> #include "base/component_export.h" @@ -60,7 +62,9 @@ struct COMPONENT_EXPORT(BASE_CLIPBOARD_TYPES) ClipboardFormatType { static const ClipboardFormatType& GetTextHtmlType(); static const ClipboardFormatType& GetCFHDropType(); static const ClipboardFormatType& GetFileDescriptorType(); + static const ClipboardFormatType& GetFileDescriptorWType(); static const ClipboardFormatType& GetFileContentZeroType(); + static const ClipboardFormatType& GetFileContentAtIndexType(LONG index); static const ClipboardFormatType& GetIDListType(); #endif @@ -99,6 +103,18 @@ struct COMPONENT_EXPORT(BASE_CLIPBOARD_TYPES) ClipboardFormatType { #if defined(OS_WIN) explicit ClipboardFormatType(UINT native_format); ClipboardFormatType(UINT native_format, LONG index); + ClipboardFormatType(UINT native_format, LONG index, DWORD tymed); + + // When there are multiple files in the data store and they are described + // using a file group descriptor, the file contents are retrieved by + // requesting the CFSTR_FILECONTENTS clipboard format type and also providing + // an index into the data (the first file corresponds to index 0). This + // function returns a map of index to CFSTR_FILECONTENTS clipboard format + // type. + static std::map<LONG, ClipboardFormatType>& GetFileContentTypeMap(); + + // FORMATETC: + // https://docs.microsoft.com/en-us/windows/desktop/com/the-formatetc-structure FORMATETC data_; #elif defined(USE_AURA) || defined(OS_ANDROID) || defined(OS_FUCHSIA) explicit ClipboardFormatType(const std::string& native_format); diff --git a/chromium/ui/base/clipboard/clipboard_format_type_android.cc b/chromium/ui/base/clipboard/clipboard_format_type_android.cc index 74a6e3f4b52..e241d16009b 100644 --- a/chromium/ui/base/clipboard/clipboard_format_type_android.cc +++ b/chromium/ui/base/clipboard/clipboard_format_type_android.cc @@ -22,12 +22,12 @@ const char kBookmarkFormat[] = "bookmark"; } // namespace // ClipboardFormatType implementation. -ClipboardFormatType::ClipboardFormatType() {} +ClipboardFormatType::ClipboardFormatType() = default; ClipboardFormatType::ClipboardFormatType(const std::string& native_format) : data_(native_format) {} -ClipboardFormatType::~ClipboardFormatType() {} +ClipboardFormatType::~ClipboardFormatType() = default; std::string ClipboardFormatType::Serialize() const { return data_; diff --git a/chromium/ui/base/clipboard/clipboard_format_type_win.cc b/chromium/ui/base/clipboard/clipboard_format_type_win.cc index 86e7d916c8d..bb242e0af5f 100644 --- a/chromium/ui/base/clipboard/clipboard_format_type_win.cc +++ b/chromium/ui/base/clipboard/clipboard_format_type_win.cc @@ -8,34 +8,30 @@ #include "base/lazy_instance.h" #include "base/logging.h" +#include "base/memory/ptr_util.h" #include "base/strings/string_number_conversions.h" #include "base/strings/utf_string_conversions.h" namespace ui { // ClipboardFormatType implementation. -ClipboardFormatType::ClipboardFormatType() : data_() {} +ClipboardFormatType::ClipboardFormatType() = default; -ClipboardFormatType::ClipboardFormatType(UINT native_format) : data_() { - // There's no good way to actually initialize this in the constructor in - // C++03. - data_.cfFormat = static_cast<CLIPFORMAT>(native_format); - data_.dwAspect = DVASPECT_CONTENT; - data_.lindex = -1; - data_.tymed = TYMED_HGLOBAL; -} +ClipboardFormatType::ClipboardFormatType(UINT native_format) + : ClipboardFormatType(native_format, -1) {} ClipboardFormatType::ClipboardFormatType(UINT native_format, LONG index) - : data_() { - // There's no good way to actually initialize this in the constructor in - // C++03. - data_.cfFormat = static_cast<CLIPFORMAT>(native_format); - data_.dwAspect = DVASPECT_CONTENT; - data_.lindex = index; - data_.tymed = TYMED_HGLOBAL; -} + : ClipboardFormatType(native_format, index, TYMED_HGLOBAL) {} -ClipboardFormatType::~ClipboardFormatType() {} +// In C++ 20, we can use designated initializers. +ClipboardFormatType::ClipboardFormatType(UINT native_format, + LONG index, + DWORD tymed) + : data_{/* .cfFormat */ static_cast<CLIPFORMAT>(native_format), + /* .ptd */ nullptr, /* .dwAspect */ DVASPECT_CONTENT, + /* .lindex */ index, /* .tymed*/ tymed} {} + +ClipboardFormatType::~ClipboardFormatType() = default; std::string ClipboardFormatType::Serialize() const { return base::NumberToString(data_.cfFormat); @@ -167,21 +163,61 @@ const ClipboardFormatType& ClipboardFormatType::GetCFHDropType() { return type.Get(); } +// Nothing prevents the drag source app from using the CFSTR_FILEDESCRIPTORA +// ANSI format (e.g., it could be that it doesn't support UNICODE). So need to +// register both the ANSI and UNICODE file group descriptors. // static const ClipboardFormatType& ClipboardFormatType::GetFileDescriptorType() { CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE( - type, ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR)); + type, ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA)); + return type.Get(); +} + +// static +const ClipboardFormatType& ClipboardFormatType::GetFileDescriptorWType() { + CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE( + type, ::RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW)); return type.Get(); } // static const ClipboardFormatType& ClipboardFormatType::GetFileContentZeroType() { + // Note this uses a storage media type of TYMED_HGLOBAL, which is not commonly + // used with CFSTR_FILECONTENTS (but used in Chromium--see + // OSExchangeDataProviderWin::SetFileContents). Use GetFileContentAtIndexType + // if TYMED_ISTREAM and TYMED_ISTORAGE are needed. + // TODO(https://crbug.com/950756): Should TYMED_ISTREAM / TYMED_ISTORAGE be + // used instead of TYMED_HGLOBAL in + // OSExchangeDataProviderWin::SetFileContents. CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE( type, ::RegisterClipboardFormat(CFSTR_FILECONTENTS), 0); return type.Get(); } // static +std::map<LONG, ClipboardFormatType>& +ClipboardFormatType::GetFileContentTypeMap() { + static base::NoDestructor<std::map<LONG, ClipboardFormatType>> + index_to_type_map; + return *index_to_type_map; +} + +// static +const ClipboardFormatType& ClipboardFormatType::GetFileContentAtIndexType( + LONG index) { + auto& index_to_type_map = GetFileContentTypeMap(); + + // Use base::WrapUnique instead of std::make_unique here since + // ClipboardFormatType constructor is private. See + // https://chromium.googlesource.com/chromium/src/+/HEAD/styleguide/c++/c++-dos-and-donts.md. + auto insert_or_assign_result = index_to_type_map.insert( + {index, + ClipboardFormatType(::RegisterClipboardFormat(CFSTR_FILECONTENTS), index, + TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE)}); + return insert_or_assign_result.first->second; +} + +// static const ClipboardFormatType& ClipboardFormatType::GetIDListType() { CR_STATIC_UI_CLIPBOARD_FORMAT_TYPE( type, ::RegisterClipboardFormat(CFSTR_SHELLIDLIST)); diff --git a/chromium/ui/base/clipboard/clipboard_monitor.cc b/chromium/ui/base/clipboard/clipboard_monitor.cc index e9e8cbe7960..b8bd0944d8a 100644 --- a/chromium/ui/base/clipboard/clipboard_monitor.cc +++ b/chromium/ui/base/clipboard/clipboard_monitor.cc @@ -9,7 +9,7 @@ namespace ui { -ClipboardMonitor::ClipboardMonitor() {} +ClipboardMonitor::ClipboardMonitor() = default; ClipboardMonitor::~ClipboardMonitor() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); diff --git a/chromium/ui/base/clipboard/clipboard_observer.h b/chromium/ui/base/clipboard/clipboard_observer.h index 1aa813d63e6..99c1c5bdc01 100644 --- a/chromium/ui/base/clipboard/clipboard_observer.h +++ b/chromium/ui/base/clipboard/clipboard_observer.h @@ -17,7 +17,7 @@ class COMPONENT_EXPORT(BASE_CLIPBOARD) ClipboardObserver { virtual void OnClipboardDataChanged() = 0; protected: - virtual ~ClipboardObserver() {} + virtual ~ClipboardObserver() = default; }; } // namespace ui diff --git a/chromium/ui/base/clipboard/clipboard_test_template.h b/chromium/ui/base/clipboard/clipboard_test_template.h index f81607b4bd8..c0ee5e19765 100644 --- a/chromium/ui/base/clipboard/clipboard_test_template.h +++ b/chromium/ui/base/clipboard/clipboard_test_template.h @@ -59,7 +59,7 @@ namespace ui { template <typename ClipboardTraits> class ClipboardTest : public PlatformTest { public: - ClipboardTest() {} + ClipboardTest() = default; ~ClipboardTest() override = default; // PlatformTest: diff --git a/chromium/ui/base/clipboard/clipboard_util_win.cc b/chromium/ui/base/clipboard/clipboard_util_win.cc index 077df5a6d48..a460c373f18 100644 --- a/chromium/ui/base/clipboard/clipboard_util_win.cc +++ b/chromium/ui/base/clipboard/clipboard_util_win.cc @@ -6,14 +6,21 @@ #include <shellapi.h> #include <wininet.h> // For INTERNET_MAX_URL_LENGTH. +#include <wrl/client.h> +#include <algorithm> +#include <limits> +#include <utility> -#include "base/files/file_path.h" +#include "base/files/file_util.h" #include "base/logging.h" #include "base/stl_util.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" +#include "base/task/post_task.h" +#include "base/task/task_traits.h" +#include "base/threading/scoped_blocking_call.h" #include "base/win/scoped_hglobal.h" #include "base/win/shlwapi.h" #include "net/base/filename_util.h" @@ -85,6 +92,354 @@ void SplitUrlAndTitle(const base::string16& str, } } +// Performs a case-insensitive search for a file path in a vector of existing +// filepaths. Case-insensivity is needed for file systems such as Windows where +// A.txt and a.txt are considered the same file name. +bool ContainsFilePathCaseInsensitive( + const std::vector<base::FilePath>& existing_filenames, + const base::FilePath& candidate_path) { + return std::find_if(std::begin(existing_filenames), + std::end(existing_filenames), + [&candidate_path](const base::FilePath& elem) { + return base::FilePath::CompareEqualIgnoreCase( + elem.value(), candidate_path.value()); + }) != std::end(existing_filenames); +} + +// Returns a unique display name for a virtual file, as it is possible that the +// filenames found in the file group descriptor are not unique (e.g. multiple +// emails with the same subject line are dragged out of Outlook.exe). +// |uniquifier| is incremented on encountering a non-unique file name. +base::FilePath GetUniqueVirtualFilename( + const base::string16& candidate_name, + const std::vector<base::FilePath>& existing_filenames, + unsigned int* uniquifier) { + // Remove any possible filepath components/separators that drag source may + // have included in virtual file name. + base::FilePath unique_name = base::FilePath(candidate_name).BaseName(); + + // To mitigate against running up against MAX_PATH limitations (temp files + // failing to be created), truncate the display name. + const size_t kTruncatedDisplayNameLength = 128; + const base::string16 extension = unique_name.Extension(); + unique_name = unique_name.RemoveExtension(); + base::string16 truncated = unique_name.value(); + if (truncated.length() > kTruncatedDisplayNameLength) { + truncated.erase(kTruncatedDisplayNameLength); + unique_name = base::FilePath(truncated); + } + unique_name = unique_name.AddExtension(extension); + + // Replace any file name illegal characters. + unique_name = net::GenerateFileName(GURL(), std::string(), std::string(), + base::UTF16ToUTF8(unique_name.value()), + std::string(), std::string()); + + // Make the file name unique. This is more involved than just marching through + // |existing_filenames|, finding the first match, uniquifying, then breaking + // out of the loop. For example, consider an array of candidate display names + // {"A (1) (2)", "A", "A (1) ", "A"}. In the first three iterations of the + // outer loop in GetVirtualFilenames, the candidate names are already unique + // and so simply pushed to the vector of |filenames|. On the fourth iteration + // of the outer loop and second iteration of the inner loop (that in + // GetUniqueVirtualFilename), the duplicate name is encountered and the fourth + // item is tentatively uniquified to "A (1)". If this inner loop were exited + // now, the final |filenames| would be {"A (1) (2)", "A", "A (1) ", "A (1)"} + // and would contain duplicate entries. So try not breaking out of the + // inner loop. In that case on the third iteration of the inner loop, the + // tentative unique name encounters another duplicate, so now gets uniquefied + // to "A (1) (2)" and if we then don't restart the loop, we would end up with + // the final |filenames| being {"A (1) (2)", "A", "A (1) ", "A (1) (2)"} and + // we still have duplicate entries. Instead we need to test against the + // entire collection of existing names on each uniquification attempt. + + // Same value used in base::GetUniquePathNumber. + static const int kMaxUniqueFiles = 100; + int count = 1; + for (; count <= kMaxUniqueFiles; ++count) { + if (!ContainsFilePathCaseInsensitive(existing_filenames, unique_name)) + break; + + unique_name = unique_name.InsertBeforeExtensionASCII( + base::StringPrintf(" (%d)", (*uniquifier)++)); + } + if (count > kMaxUniqueFiles) + unique_name = base::FilePath(); + + return unique_name; +} + +// Creates a uniquely-named temporary file based on the suggested filename, or +// an empty path on error. The file will be empty and all handles closed after +// this function returns. +base::FilePath CreateTemporaryFileWithSuggestedName( + const base::FilePath& suggested_name) { + base::FilePath temp_path1; + if (!base::CreateTemporaryFile(&temp_path1)) + return base::FilePath(); + + base::FilePath temp_path2 = temp_path1.DirName().Append(suggested_name); + + // Make filename unique. + temp_path2 = base::GetUniquePath(temp_path2); + + base::File::Error replace_file_error = base::File::FILE_OK; + if (!ReplaceFile(temp_path1, temp_path2, &replace_file_error)) + return base::FilePath(); + + return temp_path2; +} + +// This method performs file I/O and thus is executed on a worker thread. An +// empty FilePath for the temp file is returned on failure. +base::FilePath WriteFileContentsToTempFile(const base::FilePath& suggested_name, + HGLOBAL hdata) { + base::ScopedBlockingCall scoped_blocking_call(FROM_HERE, + base::BlockingType::MAY_BLOCK); + + base::FilePath temp_path = base::FilePath(); + + if (!hdata) + return temp_path; + + temp_path = CreateTemporaryFileWithSuggestedName(suggested_name); + + if (!temp_path.empty()) { + base::win::ScopedHGlobal<char*> data(hdata); + // Don't write to the temp file for empty content--leave it at 0-bytes. + if (!(data.Size() == 1 && data.get()[0] == '\0')) { + if (base::WriteFile(temp_path, data.get(), data.Size()) < 0) { + base::DeleteFile(temp_path, false); + return base::FilePath(); + } + } + } + + ::GlobalFree(hdata); + + return temp_path; +} + +std::vector< + std::pair</*temp path*/ base::FilePath, /*display name*/ base::FilePath>> +WriteAllFileContentsToTempFiles( + const std::vector<base::FilePath>& display_names, + const std::vector<HGLOBAL>& memory_backed_contents) { + DCHECK_EQ(display_names.size(), memory_backed_contents.size()); + + std::vector<std::pair<base::FilePath, base::FilePath>> filepaths_and_names; + for (size_t i = 0; i < display_names.size(); i++) { + base::FilePath temp_path = WriteFileContentsToTempFile( + display_names[i], memory_backed_contents[i]); + + filepaths_and_names.push_back({temp_path, display_names[i]}); + } + + return filepaths_and_names; +} + +// Caller's responsibility to call GlobalFree on returned HGLOBAL when done with +// the data. This method must be performed on main thread as it is using the +// IDataObject marshalled there. +HGLOBAL CopyFileContentsToHGlobal(IDataObject* data_object, LONG index) { + DCHECK(data_object); + HGLOBAL hdata = nullptr; + + if (!HasData(data_object, + ClipboardFormatType::GetFileContentAtIndexType(index))) + return hdata; + + STGMEDIUM content; + if (!GetData(data_object, + ClipboardFormatType::GetFileContentAtIndexType(index), &content)) + return hdata; + + HRESULT hr = S_OK; + + if (content.tymed == TYMED_ISTORAGE) { + // For example, messages dragged out of Outlook.exe. + + Microsoft::WRL::ComPtr<ILockBytes> lock_bytes; + hr = ::CreateILockBytesOnHGlobal(nullptr, /* fDeleteOnRelease*/ FALSE, + &lock_bytes); + + Microsoft::WRL::ComPtr<IStorage> storage; + if (SUCCEEDED(hr)) { + hr = ::StgCreateDocfileOnILockBytes( + lock_bytes.Get(), STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, + 0, &storage); + } + + if (SUCCEEDED(hr)) + hr = content.pstg->CopyTo(0, nullptr, nullptr, storage.Get()); + + if (SUCCEEDED(hr)) + hr = storage->Commit(STGC_OVERWRITE); + + if (SUCCEEDED(hr)) + hr = ::GetHGlobalFromILockBytes(lock_bytes.Get(), &hdata); + + if (FAILED(hr)) + hdata = nullptr; + } else if (content.tymed == TYMED_ISTREAM) { + // For example, attachments dragged out of messages in Outlook.exe. + + Microsoft::WRL::ComPtr<IStream> stream; + hr = + ::CreateStreamOnHGlobal(nullptr, /* fDeleteOnRelease */ FALSE, &stream); + if (SUCCEEDED(hr)) { + // A properly implemented IDataObject::GetData moves the stream pointer to + // the end. Need to seek to the beginning before copying the data then + // seek back to the original position. + const LARGE_INTEGER zero_displacement = {}; + ULARGE_INTEGER original_position = {}; + // Obtain the original stream pointer position. If the stream doesn't + // support seek, will still attempt to copy the data unless the failure is + // due to access being denied (enterprise protected data e.g.). + HRESULT hr_seek = content.pstm->Seek(zero_displacement, STREAM_SEEK_CUR, + &original_position); + if (hr_seek != E_ACCESSDENIED) { + if (SUCCEEDED(hr_seek)) { + // Seek to the beginning. + hr_seek = + content.pstm->Seek(zero_displacement, STREAM_SEEK_SET, nullptr); + } + + // Copy all data to the file stream. + ULARGE_INTEGER max_bytes; + max_bytes.QuadPart = std::numeric_limits<uint64_t>::max(); + hr = content.pstm->CopyTo(stream.Get(), max_bytes, nullptr, nullptr); + + if (SUCCEEDED(hr_seek)) { + // Restore the stream pointer to its original position. + LARGE_INTEGER original_offset; + original_offset.QuadPart = original_position.QuadPart; + content.pstm->Seek(original_offset, STREAM_SEEK_SET, nullptr); + } + } else { + // Access was denied. + hr = hr_seek; + } + + if (SUCCEEDED(hr)) + hr = ::GetHGlobalFromStream(stream.Get(), &hdata); + + if (FAILED(hr)) + hdata = nullptr; + } + } else if (content.tymed == TYMED_HGLOBAL) { + // For example, anchor (internet shortcut) dragged out of Spartan Edge. + // Copy the data as it will be written to a file on a worker thread and we + // need to call ReleaseStgMedium to free the memory allocated by the drag + // source. + base::win::ScopedHGlobal<char*> data_source(content.hGlobal); + hdata = ::GlobalAlloc(GHND, data_source.Size()); + if (hdata) { + base::win::ScopedHGlobal<char*> data_destination(hdata); + memcpy(data_destination.get(), data_source.get(), data_source.Size()); + } + } + + // Safe to release the medium now since all the data has been copied. + ReleaseStgMedium(&content); + + return hdata; +} + +base::string16 ConvertString(const char* string) { + return base::UTF8ToWide(string); +} + +base::string16 ConvertString(const wchar_t* string) { + return string; +} + +template <typename FileGroupDescriptorType> +struct FileGroupDescriptorData; + +template <> +struct FileGroupDescriptorData<FILEGROUPDESCRIPTORW> { + static bool get(IDataObject* data_object, STGMEDIUM* medium) { + return GetData(data_object, ClipboardFormatType::GetFileDescriptorWType(), + medium); + } +}; + +template <> +struct FileGroupDescriptorData<FILEGROUPDESCRIPTORA> { + static bool get(IDataObject* data_object, STGMEDIUM* medium) { + return GetData(data_object, ClipboardFormatType::GetFileDescriptorType(), + medium); + } +}; + +// Retrieves display names of virtual files, making sure they are unique. +// Use template parameter of FILEGROUPDESCRIPTORW for retrieving unicode data +// and FILEGROUPDESCRIPTORA for ascii. +template <typename FileGroupDescriptorType> +bool GetVirtualFilenames(IDataObject* data_object, + std::vector<base::FilePath>* filenames) { + STGMEDIUM medium; + + if (!FileGroupDescriptorData<FileGroupDescriptorType>::get(data_object, + &medium)) + return false; + + { + base::win::ScopedHGlobal<FileGroupDescriptorType*> fgd(medium.hGlobal); + if (!fgd.get()) + return false; + + unsigned int num_files = fgd->cItems; + // We expect there to be at least one file in here. + DCHECK_GE(num_files, 1u); + + // Value to be incremented to ensure a unique display name, as it is + // possible that the filenames found in the file group descriptor are not + // unique (e.g. multiple emails with the same subject line are dragged out + // of Outlook.exe). + unsigned int uniquifier = 1; + + for (size_t i = 0; i < num_files; i++) { + base::FilePath display_name; + // Folder entries not currently supported--skip this item. + if ((fgd->fgd[i].dwFlags & FD_ATTRIBUTES) && + (fgd->fgd[i].dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + DLOG(WARNING) << "GetVirtualFilenames: display name '" + << ConvertString(fgd->fgd[i].cFileName) + << "' refers to a directory (not supported)."; + continue; + } + display_name = GetUniqueVirtualFilename( + ConvertString(fgd->fgd[i].cFileName), *filenames, &uniquifier); + + filenames->push_back(display_name); + } + } + + ReleaseStgMedium(&medium); + return !filenames->empty(); +} + +template <typename FileGroupDescriptorType> +bool GetFileNameFromFirstDescriptor(IDataObject* data_object, + base::string16* filename) { + STGMEDIUM medium; + + if (!FileGroupDescriptorData<FileGroupDescriptorType>::get(data_object, + &medium)) + return false; + + { + base::win::ScopedHGlobal<FileGroupDescriptorType*> fgd(medium.hGlobal); + // We expect there to be at least one file in here. + DCHECK_GE(fgd->cItems, 1u); + filename->assign(ConvertString(fgd->fgd[0].cFileName)); + } + ReleaseStgMedium(&medium); + return true; +} + } // namespace bool ClipboardUtil::HasUrl(IDataObject* data_object, bool convert_filenames) { @@ -102,9 +457,21 @@ bool ClipboardUtil::HasFilenames(IDataObject* data_object) { HasData(data_object, ClipboardFormatType::GetFilenameType()); } +bool ClipboardUtil::HasVirtualFilenames(IDataObject* data_object) { + DCHECK(data_object); + // Favor real files on the file system over virtual files. + return !HasFilenames(data_object) && + HasData(data_object, + ClipboardFormatType::GetFileContentAtIndexType(0)) && + (HasData(data_object, ClipboardFormatType::GetFileDescriptorWType()) || + HasData(data_object, ClipboardFormatType::GetFileDescriptorType())); +} + bool ClipboardUtil::HasFileContents(IDataObject* data_object) { DCHECK(data_object); - return HasData(data_object, ClipboardFormatType::GetFileContentZeroType()); + return HasData(data_object, ClipboardFormatType::GetFileContentZeroType()) && + (HasData(data_object, ClipboardFormatType::GetFileDescriptorWType()) || + HasData(data_object, ClipboardFormatType::GetFileDescriptorType())); } bool ClipboardUtil::HasHtml(IDataObject* data_object) { @@ -216,6 +583,57 @@ bool ClipboardUtil::GetFilenames(IDataObject* data_object, return false; } +bool ClipboardUtil::GetVirtualFilenames( + IDataObject* data_object, + std::vector<base::FilePath>* filenames) { + DCHECK(data_object && filenames); + if (!HasVirtualFilenames(data_object)) + return false; + + // Nothing prevents the drag source app from using the CFSTR_FILEDESCRIPTORA + // ANSI format (e.g., it could be that it doesn't support UNICODE). So need to + // check for both the ANSI and UNICODE file group descriptors. + if (ui::GetVirtualFilenames<FILEGROUPDESCRIPTORW>(data_object, filenames)) { + // file group descriptor using unicode. + return true; + } + + if (ui::GetVirtualFilenames<FILEGROUPDESCRIPTORA>(data_object, filenames)) { + // file group descriptor using ascii. + return true; + } + + return false; +} + +bool ClipboardUtil::GetVirtualFilesAsTempFiles( + IDataObject* data_object, + base::OnceCallback< + void(const std::vector<std::pair</*temp path*/ base::FilePath, + /*display name*/ base::FilePath>>&)> + callback) { + // Retrieve the display names of the virtual files. + std::vector<base::FilePath> display_names; + if (!GetVirtualFilenames(data_object, &display_names)) + return false; + + // Write the file contents to global memory. + std::vector<HGLOBAL> memory_backed_contents; + for (size_t i = 0; i < display_names.size(); i++) { + HGLOBAL hdata = CopyFileContentsToHGlobal(data_object, i); + memory_backed_contents.push_back(hdata); + } + + // Queue a task to actually write the temp files on a worker thread. + base::PostTaskWithTraitsAndReplyWithResult( + FROM_HERE, {base::MayBlock(), base::TaskPriority::USER_BLOCKING}, + base::BindOnce(&WriteAllFileContentsToTempFiles, display_names, + memory_backed_contents), + std::move(callback)); // callback on the UI thread + + return true; +} + bool ClipboardUtil::GetPlainText(IDataObject* data_object, base::string16* plain_text) { DCHECK(data_object && plain_text); @@ -289,10 +707,10 @@ bool ClipboardUtil::GetHtml(IDataObject* data_object, } bool ClipboardUtil::GetFileContents(IDataObject* data_object, - base::string16* filename, std::string* file_contents) { + base::string16* filename, + std::string* file_contents) { DCHECK(data_object && filename && file_contents); - if (!HasData(data_object, ClipboardFormatType::GetFileContentZeroType()) && - !HasData(data_object, ClipboardFormatType::GetFileDescriptorType())) + if (!HasFileContents(data_object)) return false; STGMEDIUM content; @@ -307,18 +725,22 @@ bool ClipboardUtil::GetFileContents(IDataObject* data_object, ReleaseStgMedium(&content); } - STGMEDIUM description; - if (GetData(data_object, ClipboardFormatType::GetFileDescriptorType(), - &description)) { - { - base::win::ScopedHGlobal<FILEGROUPDESCRIPTOR*> fgd(description.hGlobal); - // We expect there to be at least one file in here. - DCHECK_GE(fgd->cItems, 1u); - filename->assign(fgd->fgd[0].cFileName); - } - ReleaseStgMedium(&description); + // Nothing prevents the drag source app from using the CFSTR_FILEDESCRIPTORA + // ANSI format (e.g., it could be that it doesn't support UNICODE). So need to + // check for both the ANSI and UNICODE file group descriptors. + if (GetFileNameFromFirstDescriptor<FILEGROUPDESCRIPTORW>(data_object, + filename)) { + // file group descriptor using unicode. + return true; } - return true; + + if (GetFileNameFromFirstDescriptor<FILEGROUPDESCRIPTORA>(data_object, + filename)) { + // file group descriptor using ASCII. + return true; + } + + return false; } bool ClipboardUtil::GetWebCustomData( diff --git a/chromium/ui/base/clipboard/clipboard_util_win.h b/chromium/ui/base/clipboard/clipboard_util_win.h index b4d0ae9528f..bfcea85cec8 100644 --- a/chromium/ui/base/clipboard/clipboard_util_win.h +++ b/chromium/ui/base/clipboard/clipboard_util_win.h @@ -11,9 +11,11 @@ #include <stddef.h> #include <string> #include <unordered_map> +#include <utility> #include <vector> #include "base/component_export.h" +#include "base/files/file_path.h" #include "base/strings/string16.h" class GURL; @@ -27,6 +29,7 @@ class COMPONENT_EXPORT(BASE_CLIPBOARD) ClipboardUtil { // Returns true if it does. static bool HasUrl(IDataObject* data_object, bool convert_filenames); static bool HasFilenames(IDataObject* data_object); + static bool HasVirtualFilenames(IDataObject* data_object); static bool HasPlainText(IDataObject* data_object); static bool HasFileContents(IDataObject* data_object); static bool HasHtml(IDataObject* data_object); @@ -43,6 +46,32 @@ class COMPONENT_EXPORT(BASE_CLIPBOARD) ClipboardUtil { // Only returns true if |*filenames| is not empty. static bool GetFilenames(IDataObject* data_object, std::vector<base::string16>* filenames); + + // Fills a vector of display names of "virtual files" in the data store, but + // does not actually retrieve the file contents. Display names are assured to + // be unique. Method is called on drag enter of the Chromium drop target, when + // only the display names are needed. Method only returns true if |filenames| + // is not empty. + static bool GetVirtualFilenames(IDataObject* data_object, + std::vector<base::FilePath>* filenames); + + // Retrieves "virtual file" contents via creation of intermediary temp files. + // Method is called on dropping on the Chromium drop target. Since creating + // the temp files involves file I/O, the method is asynchronous and the caller + // must provide a callback function that receives a vector of pairs of temp + // file paths and display names. Method immediately returns false if there are + // no virtual files in the data object, in which case the callback will never + // be invoked. + // TODO(https://crbug.com/951574): Implement virtual file extraction to + // dynamically stream data to the renderer when File's bytes are actually + // requested + static bool GetVirtualFilesAsTempFiles( + IDataObject* data_object, + base::OnceCallback< + void(const std::vector<std::pair</*temp path*/ base::FilePath, + /*display name*/ base::FilePath>>&)> + callback); + static bool GetPlainText(IDataObject* data_object, base::string16* plain_text); static bool GetHtml(IDataObject* data_object, @@ -72,6 +101,6 @@ class COMPONENT_EXPORT(BASE_CLIPBOARD) ClipboardUtil { size_t* fragment_start, size_t* fragment_end); }; -} +} // namespace ui #endif // UI_BASE_CLIPBOARD_CLIPBOARD_UTIL_WIN_H_ diff --git a/chromium/ui/base/cocoa/nib_loading.h b/chromium/ui/base/cocoa/nib_loading.h deleted file mode 100644 index 263ac4883b9..00000000000 --- a/chromium/ui/base/cocoa/nib_loading.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_BASE_COCOA_NIB_LOADING_H_ -#define UI_BASE_COCOA_NIB_LOADING_H_ - -#import <Cocoa/Cocoa.h> - -#include "ui/base/ui_base_export.h" - -namespace ui { - -// Given the name of a nib file, gets an unowned reference to the NSView in the -// nib. Requires a nib with just a single root view. -UI_BASE_EXPORT NSView* GetViewFromNib(NSString* name); - -} // namespace ui - -#endif // UI_BASE_COCOA_NIB_LOADING_H_ diff --git a/chromium/ui/base/cocoa/nib_loading.mm b/chromium/ui/base/cocoa/nib_loading.mm deleted file mode 100644 index bfa1c621cd3..00000000000 --- a/chromium/ui/base/cocoa/nib_loading.mm +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "ui/base/cocoa/nib_loading.h" - -#include "base/mac/bundle_locations.h" -#include "base/mac/scoped_nsobject.h" -#include "base/mac/sdk_forward_declarations.h" - -namespace ui { - -NSView* GetViewFromNib(NSString* name) { - base::scoped_nsobject<NSNib> nib( - [[NSNib alloc] initWithNibNamed:name - bundle:base::mac::FrameworkBundle()]); - if (!nib) - return nil; - - NSArray* objects; - BOOL success = [nib instantiateWithOwner:nil topLevelObjects:&objects]; - if (!success) - return nil; - - // For some strange reason, even nibs that appear to have but one top-level - // object often have more (an NSApplication, etc.). Filter out what isn't - // desired. - for (NSView* view in objects) { - if (![view isKindOfClass:[NSView class]]) - continue; - - return [[view retain] autorelease]; - } - - return nil; -} - -} // namespace ui diff --git a/chromium/ui/base/cocoa/ns_view_ids.h b/chromium/ui/base/cocoa/ns_view_ids.h deleted file mode 100644 index 749fdd14dc0..00000000000 --- a/chromium/ui/base/cocoa/ns_view_ids.h +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_BASE_COCOA_NS_VIEW_IDS_H_ -#define UI_BASE_COCOA_NS_VIEW_IDS_H_ - -#include <stdint.h> - -#include "base/macros.h" -#include "ui/base/ui_base_export.h" - -@class NSView; - -namespace ui { - -// A class used to manage NSViews across processes. -// - NSViews that may be instantiated in another process are assigned an id in -// the browser process. -// - Instantiating a ScopedNSViewIdMapping in the process where the NSView is -// created will make NSViewIds::GetNSView return the specified NSView. -// - This mechanism is used by mojo methods to refer to NSViews across -// interfaces (in particular, to specify parent NSViews). -class UI_BASE_EXPORT NSViewIds { - public: - // Get a new id to use with a new NSView. This is to only be called in the - // browser process. - static uint64_t GetNewId(); - - // Return an NSView given its id. This is to be called in an app shim process. - static NSView* GetNSView(uint64_t ns_view_id); -}; - -// A scoped mapping from |ns_view_id| to |view|. While this object exists, -// NSViewIds::GetNSView will return |view| when queried with |ns_view_id|. This -// is to be instantiated in the app shim process. -class UI_BASE_EXPORT ScopedNSViewIdMapping { - public: - ScopedNSViewIdMapping(uint64_t ns_view_id, NSView* view); - ~ScopedNSViewIdMapping(); - - private: - const uint64_t ns_view_id_; - DISALLOW_COPY_AND_ASSIGN(ScopedNSViewIdMapping); -}; - -} // namespace ui - -#endif // UI_BASE_COCOA_NS_VIEW_IDS_H_ diff --git a/chromium/ui/base/cocoa/ns_view_ids.mm b/chromium/ui/base/cocoa/ns_view_ids.mm deleted file mode 100644 index 3af9ba81e47..00000000000 --- a/chromium/ui/base/cocoa/ns_view_ids.mm +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/base/cocoa/ns_view_ids.h" - -#import <Cocoa/Cocoa.h> -#include <map> -#include <utility> - -#include "base/logging.h" -#include "base/no_destructor.h" - -namespace ui { - -std::map<uint64_t, NSView*>& GetNSViewIdMap() { - static base::NoDestructor<std::map<uint64_t, NSView*>> instance; - return *instance; -} - -// static -uint64_t NSViewIds::GetNewId() { - static uint64_t next_id = 1; - return next_id++; -} - -// static -NSView* NSViewIds::GetNSView(uint64_t ns_view_id) { - auto& view_map = GetNSViewIdMap(); - auto found = view_map.find(ns_view_id); - if (found == view_map.end()) - return nil; - return found->second; -} - -ScopedNSViewIdMapping::ScopedNSViewIdMapping(uint64_t ns_view_id, NSView* view) - : ns_view_id_(ns_view_id) { - auto result = GetNSViewIdMap().insert(std::make_pair(ns_view_id, view)); - DCHECK(result.second); -} - -ScopedNSViewIdMapping::~ScopedNSViewIdMapping() { - auto& view_map = GetNSViewIdMap(); - auto found = view_map.find(ns_view_id_); - DCHECK(found != view_map.end()); - view_map.erase(found); -} - -} // namespace ui diff --git a/chromium/ui/base/cocoa/remote_views_window.h b/chromium/ui/base/cocoa/remote_views_window.h deleted file mode 100644 index 634d54a9390..00000000000 --- a/chromium/ui/base/cocoa/remote_views_window.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_BASE_COCOA_REMOTE_VIEWS_WINDOW_H_ -#define UI_BASE_COCOA_REMOTE_VIEWS_WINDOW_H_ - -#include "ui/base/ui_base_export.h" -#include "ui/gfx/native_widget_types.h" - -namespace ui { - -// Returns true if the specified NSWindow corresponds to an NSWindow that is -// being viewed in a remote process. -bool UI_BASE_EXPORT IsWindowUsingRemoteViews(gfx::NativeWindow window); - -// Create a transparent NSWindow that is in the same position as |window|, -// but is at the ModalPanel window level, so that it will appear over all -// other window. -NSWindow* UI_BASE_EXPORT -CreateTransparentRemoteViewsClone(gfx::NativeWindow window); - -} // namespace ui - -#endif // UI_BASE_COCOA_REMOTE_VIEWS_WINDOW_H_ diff --git a/chromium/ui/base/cocoa/remote_views_window.mm b/chromium/ui/base/cocoa/remote_views_window.mm deleted file mode 100644 index c9531ef7241..00000000000 --- a/chromium/ui/base/cocoa/remote_views_window.mm +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2018 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/base/cocoa/remote_views_window.h" - -#import <Cocoa/Cocoa.h> - -// An NSWindow is using RemoteMacViews if it has no BridgedNativeWidgetImpl. -// This is the most expedient method of determining if an NSWindow uses -// RemoteMacViews. -namespace views { -class BridgedNativeWidgetImpl; -} // namespace views - -@interface NSWindow (Private) -- (views::BridgedNativeWidgetImpl*)bridgeImpl; -@end - -namespace ui { - -bool IsWindowUsingRemoteViews(gfx::NativeWindow gfx_window) { - NSWindow* ns_window = gfx_window.GetNativeNSWindow(); - if ([ns_window respondsToSelector:@selector(bridgeImpl)]) { - if (![ns_window bridgeImpl]) - return true; - } - return false; -} - -NSWindow* UI_BASE_EXPORT -CreateTransparentRemoteViewsClone(gfx::NativeWindow remote_window) { - DCHECK(IsWindowUsingRemoteViews(remote_window)); - NSWindow* window = [[NSWindow alloc] - initWithContentRect:[remote_window.GetNativeNSWindow() frame] - styleMask:NSWindowStyleMaskBorderless - backing:NSBackingStoreBuffered - defer:NO]; - [window setAlphaValue:0]; - [window makeKeyAndOrderFront:nil]; - [window setLevel:NSModalPanelWindowLevel]; - return window; -} - -} // namespace ui diff --git a/chromium/ui/base/cocoa/views_hostable.h b/chromium/ui/base/cocoa/views_hostable.h index 58aa58df6f2..5768ced0cf2 100644 --- a/chromium/ui/base/cocoa/views_hostable.h +++ b/chromium/ui/base/cocoa/views_hostable.h @@ -11,12 +11,19 @@ #include "ui/gfx/geometry/rect.h" #include "ui/gfx/native_widget_types.h" +namespace remote_cocoa { +namespace mojom { +class BridgeFactory; +} // namespace mojom +} // namespace remote_cocoa + namespace ui { class Layer; // Interface that it used to stitch a content::WebContentsView into a // views::View. +// TODO(ccameron): Move this to components/remote_cocoa. class ViewsHostableView { public: // Host interface through which the WebContentsView may indicate that its C++ @@ -26,10 +33,11 @@ class ViewsHostableView { // Query the ui::Layer of the host. virtual ui::Layer* GetUiLayer() const = 0; - // Return the id for the process in which the host NSView exists. Used to - // migrate the content::WebContentsView and content::RenderWidgetHostview - // to that process. - virtual uint64_t GetViewsFactoryHostId() const = 0; + // Return the mojo interface to the application in a remote process in which + // the host NSView exists. Used to migrate the content::WebContentsView and + // content::RenderWidgetHostView to that process. + virtual remote_cocoa::mojom::BridgeFactory* GetRemoteCocoaApplication() + const = 0; // The id for the views::View's NSView. Used to add the // content::WebContentsView's NSView as a child view. diff --git a/chromium/ui/base/dragdrop/download_file_interface.h b/chromium/ui/base/dragdrop/download_file_interface.h index db82ff2d2c6..294653571e7 100644 --- a/chromium/ui/base/dragdrop/download_file_interface.h +++ b/chromium/ui/base/dragdrop/download_file_interface.h @@ -32,7 +32,7 @@ class UI_BASE_EXPORT DownloadFileObserver protected: friend class base::RefCountedThreadSafe<DownloadFileObserver>; - virtual ~DownloadFileObserver() {} + virtual ~DownloadFileObserver() = default; }; // Defines the interface to control how a file is downloaded. @@ -51,7 +51,7 @@ class UI_BASE_EXPORT DownloadFileProvider protected: friend class base::RefCountedThreadSafe<DownloadFileProvider>; - virtual ~DownloadFileProvider() {} + virtual ~DownloadFileProvider() = default; }; } // namespace ui diff --git a/chromium/ui/base/dragdrop/drag_source_win.h b/chromium/ui/base/dragdrop/drag_source_win.h index ccb79aa8cd0..ecb129c7155 100644 --- a/chromium/ui/base/dragdrop/drag_source_win.h +++ b/chromium/ui/base/dragdrop/drag_source_win.h @@ -32,7 +32,7 @@ class DragSourceWin // are an error - it is only public because a WRL helper function creates the // objects. DragSourceWin(); - ~DragSourceWin() override {} + ~DragSourceWin() override = default; // Stop the drag operation at the next chance we get. This doesn't // synchronously stop the drag (since Windows is controlling that), diff --git a/chromium/ui/base/dragdrop/drop_target_win.cc b/chromium/ui/base/dragdrop/drop_target_win.cc index 5dddafa3591..9b943290947 100644 --- a/chromium/ui/base/dragdrop/drop_target_win.cc +++ b/chromium/ui/base/dragdrop/drop_target_win.cc @@ -14,7 +14,7 @@ IDropTargetHelper* DropTargetWin::cached_drop_target_helper_ = nullptr; DropTargetWin::DropTargetWin() : hwnd_(nullptr), ref_count_(0) {} -DropTargetWin::~DropTargetWin() {} +DropTargetWin::~DropTargetWin() = default; void DropTargetWin::Init(HWND hwnd) { DCHECK(!hwnd_); diff --git a/chromium/ui/base/dragdrop/file_info.cc b/chromium/ui/base/dragdrop/file_info.cc index e0f63dc582a..dec0d9008ce 100644 --- a/chromium/ui/base/dragdrop/file_info.cc +++ b/chromium/ui/base/dragdrop/file_info.cc @@ -6,13 +6,13 @@ namespace ui { -FileInfo::FileInfo() {} +FileInfo::FileInfo() = default; FileInfo::FileInfo(const base::FilePath& path, const base::FilePath& display_name) : path(path), display_name(display_name) {} -FileInfo::~FileInfo() {} +FileInfo::~FileInfo() = default; bool FileInfo::operator==(const FileInfo& other) const { return path == other.path && display_name == other.display_name; diff --git a/chromium/ui/base/dragdrop/os_exchange_data.cc b/chromium/ui/base/dragdrop/os_exchange_data.cc index 11cf7dbe66d..d6a4a737117 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data.cc +++ b/chromium/ui/base/dragdrop/os_exchange_data.cc @@ -4,6 +4,10 @@ #include "ui/base/dragdrop/os_exchange_data.h" +#include <utility> +#include <vector> + +#include "base/callback.h" #include "base/pickle.h" #include "build/build_config.h" #include "ui/base/clipboard/clipboard_format_type.h" @@ -19,7 +23,7 @@ OSExchangeData::DownloadFileInfo::DownloadFileInfo( downloader(downloader) { } -OSExchangeData::DownloadFileInfo::~DownloadFileInfo() {} +OSExchangeData::DownloadFileInfo::~DownloadFileInfo() = default; OSExchangeData::OSExchangeData() : provider_(OSExchangeDataProviderFactory::CreateProvider()) { @@ -136,6 +140,22 @@ bool OSExchangeData::GetFileContents(base::FilePath* filename, return provider_->GetFileContents(filename, file_contents); } +bool OSExchangeData::HasVirtualFilenames() const { + return provider_->HasVirtualFilenames(); +} + +bool OSExchangeData::GetVirtualFilenames( + std::vector<FileInfo>* filenames) const { + return provider_->GetVirtualFilenames(filenames); +} + +bool OSExchangeData::GetVirtualFilesAsTempFiles( + base::OnceCallback< + void(const std::vector<std::pair<base::FilePath, base::FilePath>>&)> + callback) const { + return provider_->GetVirtualFilesAsTempFiles(std::move(callback)); +} + void OSExchangeData::SetDownloadFileInfo(const DownloadFileInfo& download) { provider_->SetDownloadFileInfo(download); } diff --git a/chromium/ui/base/dragdrop/os_exchange_data.h b/chromium/ui/base/dragdrop/os_exchange_data.h index cd915154ea1..343689e41ad 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data.h +++ b/chromium/ui/base/dragdrop/os_exchange_data.h @@ -8,6 +8,8 @@ #include <memory> #include <set> #include <string> +#include <utility> +#include <vector> #include "build/build_config.h" @@ -121,6 +123,18 @@ class UI_BASE_EXPORT OSExchangeData { virtual bool GetFileContents(base::FilePath* filename, std::string* file_contents) const = 0; virtual bool HasFileContents() const = 0; + virtual bool HasVirtualFilenames() const = 0; + virtual bool GetVirtualFilenames( + std::vector<FileInfo>* file_names) const = 0; + virtual bool GetVirtualFilesAsTempFiles( + base::OnceCallback<void( + const std::vector<std::pair</*temp path*/ base::FilePath, + /*display name*/ base::FilePath>>&)> + callback) const = 0; + virtual void SetVirtualFileContentsForTesting( + const std::vector<std::pair<base::FilePath, std::string>>& + filenames_and_contents, + DWORD tymed) = 0; virtual void SetDownloadFileInfo(const DownloadFileInfo& download) = 0; #endif @@ -213,6 +227,44 @@ class UI_BASE_EXPORT OSExchangeData { bool GetFileContents(base::FilePath* filename, std::string* file_contents) const; + // Methods used to query and retrieve file data from a drag source + // IDataObject implementation packaging the data with the + // CFSTR_FILEDESCRIPTOR/CFSTR_FILECONTENTS clipboard formats instead of the + // more common CF_HDROP. These formats are intended to represent "virtual + // files," not files that live on the platform file system. For a drop target + // to read the file contents, it must be streamed from the drag source + // application. + + // Method that returns true if there are virtual files packaged in the data + // store. + bool HasVirtualFilenames() const; + + // Retrieves names of any "virtual files" in the data store packaged using the + // CFSTR_FILEDESCRIPTOR/CFSTR_FILECONTENTS clipboard formats instead of the + // more common CF_HDROP used for "real files." Real files are preferred over + // virtual files here to avoid duplication, as the data store may package + // the same file lists using different formats. GetVirtualFilenames just + // retrieves the display names but not the temp file paths. The temp files + // are only created upon drop via a call to the async method + // GetVirtualFilesAsTempFiles. + bool GetVirtualFilenames(std::vector<FileInfo>* file_names) const; + + // Retrieves "virtual file" contents via creation of intermediary temp files. + // Method is called on dropping on the Chromium drop target. Since creating + // the temp files involves file I/O, the method is asynchronous and the caller + // must provide a callback function that receives a vector of pairs of temp + // file paths and display names. Method immediately returns false if there are + // no virtual files in the data object, in which case the callback will never + // be invoked. + // TODO(https://crbug.com/951574): Implement virtual file extraction to + // dynamically stream data to the renderer when File's bytes are actually + // requested + bool GetVirtualFilesAsTempFiles( + base::OnceCallback<void(const std::vector</*temp path*/ std::pair< + base::FilePath, + /*display name*/ base::FilePath>>&)> callback) + const; + // Adds a download file with full path (CF_HDROP). void SetDownloadFileInfo(const DownloadFileInfo& download); #endif diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider_aura.cc b/chromium/ui/base/dragdrop/os_exchange_data_provider_aura.cc index 40dbe89567b..7815313e6cd 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_provider_aura.cc +++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_aura.cc @@ -17,7 +17,7 @@ OSExchangeDataProviderAura::OSExchangeDataProviderAura() : formats_(0) { } -OSExchangeDataProviderAura::~OSExchangeDataProviderAura() {} +OSExchangeDataProviderAura::~OSExchangeDataProviderAura() = default; std::unique_ptr<OSExchangeData::Provider> OSExchangeDataProviderAura::Clone() const { diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider_factory.cc b/chromium/ui/base/dragdrop/os_exchange_data_provider_factory.cc index 9c3a9ab2cbe..30a259cb1d3 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_provider_factory.cc +++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_factory.cc @@ -18,28 +18,9 @@ namespace ui { -OSExchangeDataProviderFactory::Factory* factory_ = nullptr; - -// static -void OSExchangeDataProviderFactory::SetFactory(Factory* factory) { - DCHECK(!factory_ || !factory); - factory_ = factory; -} - -// static -OSExchangeDataProviderFactory::Factory* -OSExchangeDataProviderFactory::TakeFactory() { - OSExchangeDataProviderFactory::Factory* to_return = factory_; - factory_ = nullptr; - return to_return; -} - //static std::unique_ptr<OSExchangeData::Provider> OSExchangeDataProviderFactory::CreateProvider() { - if (factory_) - return factory_->BuildProvider(); - #if defined(USE_X11) return std::make_unique<OSExchangeDataProviderAuraX11>(); #elif defined(OS_LINUX) diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider_factory.h b/chromium/ui/base/dragdrop/os_exchange_data_provider_factory.h index 4029d089f62..626b7ea2f8c 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_provider_factory.h +++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_factory.h @@ -12,23 +12,10 @@ namespace ui { -// Builds OSExchangeDataProviders. We need to be able to switch providers at -// runtime based on the configuration flags. If no factory is set, -// CreateProvider() will default to the current operating system's default. +// Builds platform specific OSExchangeDataProviders. class UI_BASE_EXPORT OSExchangeDataProviderFactory { public: - class Factory { - public: - virtual std::unique_ptr<OSExchangeData::Provider> BuildProvider() = 0; - }; - - // Sets the factory which builds the providers. - static void SetFactory(Factory* factory); - - // Returns the current factory and sets the factory to null. - static Factory* TakeFactory(); - - // Creates a Provider based on the current configuration. + // Creates a Provider based on the current platform. static std::unique_ptr<OSExchangeData::Provider> CreateProvider(); }; diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider_win.cc b/chromium/ui/base/dragdrop/os_exchange_data_provider_win.cc index a9bd4912f3d..291b95010c7 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_provider_win.cc +++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_win.cc @@ -4,6 +4,7 @@ #include "ui/base/dragdrop/os_exchange_data_provider_win.h" +#include <coml2api.h> #include <objbase.h> #include <objidl.h> #include <shlobj.h> @@ -14,6 +15,8 @@ #include <algorithm> #include <iterator> +#include "base/callback.h" +#include "base/containers/span.h" #include "base/files/file_path.h" #include "base/i18n/file_util_icu.h" #include "base/logging.h" @@ -23,6 +26,7 @@ #include "base/strings/utf_string_conversions.h" #include "base/win/scoped_hdc.h" #include "base/win/scoped_hglobal.h" +#include "base/win/shlwapi.h" #include "net/base/filename_util.h" #include "skia/ext/skia_utils_win.h" #include "third_party/skia/include/core/SkBitmap.h" @@ -372,6 +376,124 @@ void OSExchangeDataProviderWin::SetFilenames( ClipboardFormatType::GetCFHDropType().ToFormatEtc(), storage)); } +void OSExchangeDataProviderWin::SetVirtualFileContentsForTesting( + const std::vector<std::pair<base::FilePath, std::string>>& + filenames_and_contents, + DWORD tymed) { + size_t num_files = filenames_and_contents.size(); + if (!num_files) + return; + + // Allocate storage for the file group descriptor as CFSTR_FILEDESCRIPTORW. + // The fgd[] member of FILEGROUPDESCRIPTORW is of size one, thus sizeof + // (FILEDESCRIPTORW) is already the correct allocation size if there is only + // one item. Otherwise need to add room for each FILEDESCRIPTORW struct. + const size_t total_bytes_fgd = sizeof(FILEGROUPDESCRIPTORW) + + (sizeof(FILEDESCRIPTORW) * (num_files - 1)); + + HANDLE hdata = ::GlobalAlloc(GPTR, total_bytes_fgd); + if (!hdata) + return; + + base::win::ScopedHGlobal<FILEGROUPDESCRIPTORW*> locked_mem(hdata); + + FILEGROUPDESCRIPTORW* descriptor = locked_mem.get(); + descriptor->cItems = num_files; + + STGMEDIUM* storage = new STGMEDIUM; + storage->tymed = TYMED_HGLOBAL; + storage->hGlobal = hdata; + storage->pUnkForRelease = NULL; + + data_->contents_.push_back(std::make_unique<DataObjectImpl::StoredDataInfo>( + ClipboardFormatType::GetFileDescriptorWType().ToFormatEtc(), storage)); + + for (size_t i = 0; i < num_files; i++) { + // Fill in each FILEDESCRIPTORW with file name. + descriptor->fgd[i].dwFlags |= FD_UNICODE; + base::string16 file_name = filenames_and_contents[i].first.value(); + wcsncpy_s(descriptor->fgd[i].cFileName, MAX_PATH, file_name.c_str(), + std::min(file_name.size(), static_cast<size_t>(MAX_PATH - 1u))); + + // Add the contents of each file as CFSTR_FILECONTENTS. + base::span<const uint8_t> data_buffer = + base::make_span(reinterpret_cast<const uint8_t*>( + filenames_and_contents[i].second.data()), + filenames_and_contents[i].second.length()); + SetVirtualFileContentAtIndexForTesting(data_buffer, tymed, i); + } +} + +void OSExchangeDataProviderWin::SetVirtualFileContentAtIndexForTesting( + base::span<const uint8_t> data_buffer, + DWORD tymed, + size_t index) { + std::unique_ptr<STGMEDIUM> storage_for_contents; + + if (tymed == TYMED_ISTORAGE) { + storage_for_contents = std::make_unique<STGMEDIUM>(); + storage_for_contents->pUnkForRelease = nullptr; + + Microsoft::WRL::ComPtr<ILockBytes> lock_bytes; + HRESULT hr = ::CreateILockBytesOnHGlobal( + nullptr, /* fDeleteOnRelease*/ TRUE, &lock_bytes); + + if (SUCCEEDED(hr)) { + hr = ::StgCreateDocfileOnILockBytes( + lock_bytes.Get(), STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, + 0, &storage_for_contents->pstg); + } + + Microsoft::WRL::ComPtr<IStream> destination_stream; + if (SUCCEEDED(hr)) { + hr = storage_for_contents->pstg->CreateStream( + L"Contents", STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_CREATE, 0, + 0, &destination_stream); + } + + Microsoft::WRL::ComPtr<IStream> source_stream; + if (SUCCEEDED(hr)) { + source_stream = + ::SHCreateMemStream(data_buffer.data(), data_buffer.size_bytes()); + } + + if (source_stream) { + // Copy the data to the storage stream. + ULARGE_INTEGER bytes_to_copy; + bytes_to_copy.QuadPart = data_buffer.size_bytes(); + hr = source_stream->CopyTo(destination_stream.Get(), bytes_to_copy, + nullptr, nullptr); + } + if (SUCCEEDED(hr)) + hr = storage_for_contents->pstg->Commit(STGC_DEFAULT); + if (SUCCEEDED(hr)) + storage_for_contents->tymed = TYMED_ISTORAGE; + + } else if (tymed == TYMED_ISTREAM) { + storage_for_contents = std::make_unique<STGMEDIUM>(); + storage_for_contents->pUnkForRelease = nullptr; + storage_for_contents->pstm = + ::SHCreateMemStream(data_buffer.data(), data_buffer.size_bytes()); + if (storage_for_contents->pstm) { + // A properly implemented IDataObject::GetData moves the stream pointer + // to end. + const LARGE_INTEGER zero_displacement = {}; + HRESULT hr = storage_for_contents->pstm->Seek(zero_displacement, + STREAM_SEEK_END, nullptr); + if (SUCCEEDED(hr)) + storage_for_contents->tymed = TYMED_ISTREAM; + } + } else if (tymed == TYMED_HGLOBAL) { + storage_for_contents.reset( + GetStorageForBytes(data_buffer.data(), data_buffer.size_bytes())); + } + ClipboardFormatType type = + ClipboardFormatType::GetFileContentAtIndexType(index); + // Pass ownership of |storage_for_contents| here. + data_->contents_.push_back(std::make_unique<DataObjectImpl::StoredDataInfo>( + type.ToFormatEtc(), storage_for_contents.release())); +} + void OSExchangeDataProviderWin::SetPickledData( const ClipboardFormatType& format, const base::Pickle& data) { @@ -383,12 +505,12 @@ void OSExchangeDataProviderWin::SetPickledData( void OSExchangeDataProviderWin::SetFileContents( const base::FilePath& filename, const std::string& file_contents) { - // Add CFSTR_FILEDESCRIPTOR + // Add CFSTR_FILEDESCRIPTORW. STGMEDIUM* storage = GetStorageForFileDescriptor(filename); data_->contents_.push_back(std::make_unique<DataObjectImpl::StoredDataInfo>( - ClipboardFormatType::GetFileDescriptorType().ToFormatEtc(), storage)); + ClipboardFormatType::GetFileDescriptorWType().ToFormatEtc(), storage)); - // Add CFSTR_FILECONTENTS + // Add CFSTR_FILECONTENTS. storage = GetStorageForBytes(file_contents.data(), file_contents.length()); data_->contents_.push_back(std::make_unique<DataObjectImpl::StoredDataInfo>( ClipboardFormatType::GetFileContentZeroType().ToFormatEtc(), storage)); @@ -457,6 +579,39 @@ bool OSExchangeDataProviderWin::GetFilenames( return success; } +bool OSExchangeDataProviderWin::HasVirtualFilenames() const { + return ClipboardUtil::HasVirtualFilenames(source_object_.Get()); +} + +bool OSExchangeDataProviderWin::GetVirtualFilenames( + std::vector<FileInfo>* filenames) const { + // ui_base_clipboard can't use FileInfo struct which is part of ui_base, so + // use FilePath instead. + // TODO(https://crbug.com/950360): ui_base_clipboard can't use FileInfo struct + // which is part of ui_base (layering issue). + std::vector<base::FilePath> display_names; + bool success = + ClipboardUtil::GetVirtualFilenames(source_object_.Get(), &display_names); + + if (success) { + // On dragenter scenarios, need a placeholder file path for drag metadata + // checks without actually creating the temp file. + base::FilePath temp_path(FILE_PATH_LITERAL("temp.tmp")); + + for (const auto& display_name : display_names) + filenames->push_back(FileInfo(temp_path, display_name)); + } + return success; +} + +bool OSExchangeDataProviderWin::GetVirtualFilesAsTempFiles( + base::OnceCallback< + void(const std::vector<std::pair<base::FilePath, base::FilePath>>&)> + callback) const { + return ClipboardUtil::GetVirtualFilesAsTempFiles(source_object_.Get(), + std::move(callback)); +} + bool OSExchangeDataProviderWin::GetPickledData( const ClipboardFormatType& format, base::Pickle* data) const { @@ -1119,10 +1274,10 @@ static STGMEDIUM* GetStorageForFileDescriptor( const base::FilePath& path) { base::string16 file_name = path.value(); DCHECK(!file_name.empty()); - HANDLE hdata = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTOR)); - base::win::ScopedHGlobal<FILEGROUPDESCRIPTOR*> locked_mem(hdata); + HANDLE hdata = GlobalAlloc(GPTR, sizeof(FILEGROUPDESCRIPTORW)); + base::win::ScopedHGlobal<FILEGROUPDESCRIPTORW*> locked_mem(hdata); - FILEGROUPDESCRIPTOR* descriptor = locked_mem.get(); + FILEGROUPDESCRIPTORW* descriptor = locked_mem.get(); descriptor->cItems = 1; descriptor->fgd[0].dwFlags = FD_LINKUI; wcsncpy_s(descriptor->fgd[0].cFileName, MAX_PATH, file_name.c_str(), diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider_win.h b/chromium/ui/base/dragdrop/os_exchange_data_provider_win.h index 96fa77ec763..a914f043ebe 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_provider_win.h +++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_win.h @@ -9,6 +9,7 @@ #include <shlobj.h> #include <stddef.h> #include <wrl/client.h> +#include <utility> #include <memory> #include <string> @@ -154,6 +155,13 @@ class UI_BASE_EXPORT OSExchangeDataProviderWin void SetURL(const GURL& url, const base::string16& title) override; void SetFilename(const base::FilePath& path) override; void SetFilenames(const std::vector<FileInfo>& filenames) override; + // Test only method for adding virtual file content to the data store. The + // first value in the pair is the file display name, the second is a string + // providing the file content. + void SetVirtualFileContentsForTesting( + const std::vector<std::pair<base::FilePath, std::string>>& + filenames_and_contents, + DWORD tymed) override; void SetPickledData(const ClipboardFormatType& format, const base::Pickle& data) override; void SetFileContents(const base::FilePath& filename, @@ -166,6 +174,12 @@ class UI_BASE_EXPORT OSExchangeDataProviderWin base::string16* title) const override; bool GetFilename(base::FilePath* path) const override; bool GetFilenames(std::vector<FileInfo>* filenames) const override; + bool HasVirtualFilenames() const override; + bool GetVirtualFilenames(std::vector<FileInfo>* filenames) const override; + bool GetVirtualFilesAsTempFiles( + base::OnceCallback< + void(const std::vector<std::pair<base::FilePath, base::FilePath>>&)> + callback) const override; bool GetPickledData(const ClipboardFormatType& format, base::Pickle* data) const override; bool GetFileContents(base::FilePath* filename, @@ -185,6 +199,10 @@ class UI_BASE_EXPORT OSExchangeDataProviderWin gfx::Vector2d GetDragImageOffset() const override; private: + void SetVirtualFileContentAtIndexForTesting(base::span<const uint8_t> data, + DWORD tymed, + size_t index); + scoped_refptr<DataObjectImpl> data_; Microsoft::WRL::ComPtr<IDataObject> source_object_; diff --git a/chromium/ui/base/dragdrop/os_exchange_data_win_unittest.cc b/chromium/ui/base/dragdrop/os_exchange_data_win_unittest.cc index 161c2519a9b..bf74612e2df 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_win_unittest.cc +++ b/chromium/ui/base/dragdrop/os_exchange_data_win_unittest.cc @@ -6,10 +6,15 @@ #include <memory> +#include "base/bind.h" +#include "base/callback.h" +#include "base/files/file_util.h" #include "base/memory/ref_counted.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_task_environment.h" #include "base/win/scoped_hglobal.h" #include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" #include "ui/base/clipboard/clipboard_format_type.h" #include "ui/base/dragdrop/file_info.h" #include "ui/base/dragdrop/os_exchange_data_provider_win.h" @@ -17,8 +22,41 @@ namespace ui { +namespace { +const std::vector<DWORD> kStorageMediaTypesForVirtualFiles = { + TYMED_ISTORAGE, + TYMED_ISTREAM, + TYMED_HGLOBAL, +}; + +} // namespace + +class OSExchangeDataWinTest : public ::testing::Test { + public: + OSExchangeDataWinTest() + : scoped_task_environment_( + base::test::ScopedTaskEnvironment::MainThreadType::UI) {} + + void OnGotVirtualFilesAsTempFiles( + const std::vector<std::pair<base::FilePath, base::FilePath>>& + filepaths_and_names) { + // Clear any previous results and cache a vector of FileInfo objects for + // verification. + retrieved_virtual_files_.clear(); + + for (const auto& filepath_and_name : filepaths_and_names) { + retrieved_virtual_files_.push_back( + FileInfo(filepath_and_name.first, filepath_and_name.second)); + } + } + + protected: + std::vector<FileInfo> retrieved_virtual_files_; + base::test::ScopedTaskEnvironment scoped_task_environment_; +}; + // Test getting using the IDataObject COM API -TEST(OSExchangeDataWinTest, StringDataAccessViaCOM) { +TEST_F(OSExchangeDataWinTest, StringDataAccessViaCOM) { OSExchangeData data; std::wstring input = L"O hai googlz."; data.SetString(input); @@ -38,7 +76,7 @@ TEST(OSExchangeDataWinTest, StringDataAccessViaCOM) { } // Test setting using the IDataObject COM API -TEST(OSExchangeDataWinTest, StringDataWritingViaCOM) { +TEST_F(OSExchangeDataWinTest, StringDataWritingViaCOM) { OSExchangeData data; std::wstring input = L"http://www.google.com/"; @@ -72,7 +110,7 @@ TEST(OSExchangeDataWinTest, StringDataWritingViaCOM) { } // Verifies SetData invoked twice with the same data clobbers existing data. -TEST(OSExchangeDataWinTest, RemoveData) { +TEST_F(OSExchangeDataWinTest, RemoveData) { OSExchangeData data; std::wstring input = L"http://www.google.com/"; std::wstring input2 = L"http://www.google2.com/"; @@ -118,7 +156,7 @@ TEST(OSExchangeDataWinTest, RemoveData) { EXPECT_EQ(GURL(input2).spec(), url_from_data.spec()); } -TEST(OSExchangeDataWinTest, URLDataAccessViaCOM) { +TEST_F(OSExchangeDataWinTest, URLDataAccessViaCOM) { OSExchangeData data; GURL url("http://www.google.com/"); data.SetURL(url, L""); @@ -138,7 +176,7 @@ TEST(OSExchangeDataWinTest, URLDataAccessViaCOM) { ReleaseStgMedium(&medium); } -TEST(OSExchangeDataWinTest, MultipleFormatsViaCOM) { +TEST_F(OSExchangeDataWinTest, MultipleFormatsViaCOM) { OSExchangeData data; std::string url_spec = "http://www.google.com/"; GURL url(url_spec); @@ -173,7 +211,7 @@ TEST(OSExchangeDataWinTest, MultipleFormatsViaCOM) { ReleaseStgMedium(&medium); } -TEST(OSExchangeDataWinTest, EnumerationViaCOM) { +TEST_F(OSExchangeDataWinTest, EnumerationViaCOM) { OSExchangeData data; data.SetURL(GURL("http://www.google.com/"), L""); data.SetString(L"O hai googlz."); @@ -262,7 +300,7 @@ TEST(OSExchangeDataWinTest, EnumerationViaCOM) { } } -TEST(OSExchangeDataWinTest, TestURLExchangeFormatsViaCOM) { +TEST_F(OSExchangeDataWinTest, TestURLExchangeFormatsViaCOM) { OSExchangeData data; std::string url_spec = "http://www.google.com/"; GURL url(url_spec); @@ -291,7 +329,7 @@ TEST(OSExchangeDataWinTest, TestURLExchangeFormatsViaCOM) { } } -TEST(OSExchangeDataWinTest, FileContents) { +TEST_F(OSExchangeDataWinTest, FileContents) { OSExchangeData data; std::string file_contents("data\0with\0nulls", 15); data.SetFileContents(base::FilePath(L"filename.txt"), file_contents); @@ -304,7 +342,482 @@ TEST(OSExchangeDataWinTest, FileContents) { EXPECT_EQ(file_contents, read_contents); } -TEST(OSExchangeDataWinTest, CFHtml) { +TEST_F(OSExchangeDataWinTest, VirtualFiles) { + const base::FilePath path_placeholder(FILE_PATH_LITERAL("temp.tmp")); + + const std::vector<std::pair<base::FilePath, std::string>> + kTestFilenames_and_Contents = { + {base::FilePath(FILE_PATH_LITERAL("filename.txt")), + std::string("just some data")}, + {base::FilePath(FILE_PATH_LITERAL("another filename.txt")), + std::string("just some data\0with\0nulls", 25)}, + {base::FilePath(FILE_PATH_LITERAL("and another filename.txt")), + std::string("just some more data")}, + }; + + for (const auto& tymed : kStorageMediaTypesForVirtualFiles) { + OSExchangeData data; + data.provider().SetVirtualFileContentsForTesting( + kTestFilenames_and_Contents, tymed); + + OSExchangeData copy(data.provider().Clone()); + std::vector<FileInfo> file_infos; + EXPECT_TRUE(copy.GetVirtualFilenames(&file_infos)); + EXPECT_EQ(kTestFilenames_and_Contents.size(), file_infos.size()); + for (size_t i = 0; i < file_infos.size(); i++) { + EXPECT_EQ(kTestFilenames_and_Contents[i].first, + file_infos[i].display_name); + EXPECT_EQ(path_placeholder, file_infos[i].path); + } + + std::string read_contents; + base::FilePath temp_dir; + EXPECT_TRUE(base::GetTempDir(&temp_dir)); + + // Callback for GetVirtualFilesAsTempFiles is executed when all virtual + // files are backed by temp files. + auto callback = + base::BindOnce(&OSExchangeDataWinTest::OnGotVirtualFilesAsTempFiles, + base::Unretained(this)); + + EXPECT_TRUE(copy.GetVirtualFilesAsTempFiles(std::move(callback))); + + // RunUntilIdle assures all async tasks are run. + scoped_task_environment_.RunUntilIdle(); + + EXPECT_EQ(kTestFilenames_and_Contents.size(), + retrieved_virtual_files_.size()); + for (size_t i = 0; i < retrieved_virtual_files_.size(); i++) { + EXPECT_EQ(kTestFilenames_and_Contents[i].first, + retrieved_virtual_files_[i].display_name); + // Check if the temp files that back the virtual files are actually + // created in the temp directory. Need to compare long file paths here + // because GetTempDir can return a short ("8.3") path if the test is run + // under a username that is too long. + EXPECT_EQ( + base::MakeLongFilePath(temp_dir), + base::MakeLongFilePath(retrieved_virtual_files_[i].path.DirName())); + EXPECT_EQ(kTestFilenames_and_Contents[i].first.Extension(), + retrieved_virtual_files_[i].path.Extension()); + EXPECT_TRUE(base::ReadFileToString(retrieved_virtual_files_[i].path, + &read_contents)); + if (tymed != TYMED_ISTORAGE) { + EXPECT_EQ(kTestFilenames_and_Contents[i].second, read_contents); + } else { + // IStorage uses compound files, so temp files won't be flat text files. + // Just make sure the original contents appears in the compound files. + EXPECT_TRUE(read_contents.find(kTestFilenames_and_Contents[i].second) != + std::string::npos); + } + } + } +} + +TEST_F(OSExchangeDataWinTest, VirtualFilesRealFilesPreferred) { + // Verify that no virtual files retrieved if there is real file data. + const std::vector<FileInfo> kTestFilenames = { + {base::FilePath(FILE_PATH_LITERAL("C:\\tmp\\test_file1")), + base::FilePath()}, + {base::FilePath(FILE_PATH_LITERAL("C:\\tmp\\test_file2")), + base::FilePath()}, + }; + + const std::vector<std::pair<base::FilePath, std::string>> + kTestFilenames_and_Contents = { + {base::FilePath(FILE_PATH_LITERAL("filename.txt")), + std::string("just some data")}, + {base::FilePath(FILE_PATH_LITERAL("another filename.txt")), + std::string("just some data\0with\0nulls", 25)}, + {base::FilePath(FILE_PATH_LITERAL("and another filename.txt")), + std::string("just some more data")}, + }; + + for (const auto& tymed : kStorageMediaTypesForVirtualFiles) { + OSExchangeData data; + data.SetFilenames(kTestFilenames); + data.provider().SetVirtualFileContentsForTesting( + kTestFilenames_and_Contents, tymed); + + OSExchangeData copy(data.provider().Clone()); + + std::vector<FileInfo> real_filenames; + EXPECT_TRUE(copy.GetFilenames(&real_filenames)); + EXPECT_EQ(kTestFilenames.size(), real_filenames.size()); + EXPECT_EQ(kTestFilenames, real_filenames); + + std::vector<FileInfo> file_infos; + EXPECT_FALSE(copy.GetVirtualFilenames(&file_infos)); + EXPECT_EQ(static_cast<size_t>(0), file_infos.size()); + + // Callback for GetVirtualFilesAsTempFiles is executed when all virtual + // files are backed by temp files. + auto callback = + base::BindOnce(&OSExchangeDataWinTest::OnGotVirtualFilesAsTempFiles, + base::Unretained(this)); + + EXPECT_FALSE(copy.GetVirtualFilesAsTempFiles(std::move(callback))); + + // RunUntilIdle assures all async tasks are run. + scoped_task_environment_.RunUntilIdle(); + + EXPECT_EQ(static_cast<size_t>(0), retrieved_virtual_files_.size()); + } +} + +TEST_F(OSExchangeDataWinTest, VirtualFilesDuplicateNames) { + const std::vector<std::pair<base::FilePath, std::string>> + kTestFilenames_and_Contents = { + {base::FilePath(FILE_PATH_LITERAL("A (1) (2).txt")), + std::string("just some data")}, + {base::FilePath(FILE_PATH_LITERAL("A.txt")), + std::string("just some more data")}, + {base::FilePath(FILE_PATH_LITERAL("A (1).txt")), + std::string("just some more more data")}, + {base::FilePath(FILE_PATH_LITERAL("A.txt")), + std::string("just some more more more data")}, + }; + + for (const auto& tymed : kStorageMediaTypesForVirtualFiles) { + OSExchangeData data; + data.provider().SetVirtualFileContentsForTesting( + kTestFilenames_and_Contents, tymed); + + OSExchangeData copy(data.provider().Clone()); + std::vector<FileInfo> file_infos; + EXPECT_TRUE(copy.GetVirtualFilenames(&file_infos)); + EXPECT_EQ(kTestFilenames_and_Contents.size(), file_infos.size()); + for (size_t i = 0; i < file_infos.size(); i++) { + // Check that display name is unique. + for (size_t j = 0; j < i; j++) { + EXPECT_FALSE(base::FilePath::CompareEqualIgnoreCase( + file_infos[j].display_name.value(), + file_infos[i].display_name.value())); + } + } + + std::string read_contents; + base::FilePath temp_dir; + EXPECT_TRUE(base::GetTempDir(&temp_dir)); + + // Callback for GetVirtualFilesAsTempFiles is executed when all virtual + // files are backed by temp files. + auto callback = + base::BindOnce(&OSExchangeDataWinTest::OnGotVirtualFilesAsTempFiles, + base::Unretained(this)); + + EXPECT_TRUE(copy.GetVirtualFilesAsTempFiles(std::move(callback))); + + // RunUntilIdle assures all async tasks are run. + scoped_task_environment_.RunUntilIdle(); + + EXPECT_EQ(kTestFilenames_and_Contents.size(), file_infos.size()); + for (size_t i = 0; i < retrieved_virtual_files_.size(); i++) { + // Check that display name is unique. + for (size_t j = 0; j < i; j++) { + EXPECT_FALSE(base::FilePath::CompareEqualIgnoreCase( + retrieved_virtual_files_[j].display_name.value(), + retrieved_virtual_files_[i].display_name.value())); + } + + // Check that temp file path is unique. + for (size_t j = 0; j < i; j++) { + EXPECT_FALSE(base::FilePath::CompareEqualIgnoreCase( + retrieved_virtual_files_[j].path.value(), + retrieved_virtual_files_[i].path.value())); + } + + // Check if the temp files that back the virtual files are actually + // created in the temp directory. Need to compare long file paths here + // because GetTempDir can return a short ("8.3") path if the test is run + // under a username that is too long. + EXPECT_EQ( + base::MakeLongFilePath(temp_dir), + base::MakeLongFilePath(retrieved_virtual_files_[i].path.DirName())); + EXPECT_EQ(kTestFilenames_and_Contents[i].first.Extension(), + retrieved_virtual_files_[i].path.Extension()); + EXPECT_TRUE(base::ReadFileToString(retrieved_virtual_files_[i].path, + &read_contents)); + if (tymed != TYMED_ISTORAGE) { + EXPECT_EQ(kTestFilenames_and_Contents[i].second, read_contents); + } else { + // IStorage uses compound files, so temp files won't be flat text files. + // Just make sure the original contents appears in the compound files. + EXPECT_TRUE(read_contents.find(kTestFilenames_and_Contents[i].second) != + std::string::npos); + } + } + } +} // namespace ui + +TEST_F(OSExchangeDataWinTest, VirtualFilesDuplicateNamesCaseInsensitivity) { + const std::vector<std::pair<base::FilePath, std::string>> + kTestFilenames_and_Contents = { + {base::FilePath(FILE_PATH_LITERAL("a.txt")), + std::string("just some data")}, + {base::FilePath(FILE_PATH_LITERAL("B.txt")), + std::string("just some more data")}, + {base::FilePath(FILE_PATH_LITERAL("A.txt")), + std::string("just some more more data")}, + }; + + for (const auto& tymed : kStorageMediaTypesForVirtualFiles) { + OSExchangeData data; + data.provider().SetVirtualFileContentsForTesting( + kTestFilenames_and_Contents, tymed); + + OSExchangeData copy(data.provider().Clone()); + std::vector<FileInfo> file_infos; + EXPECT_TRUE(copy.GetVirtualFilenames(&file_infos)); + EXPECT_EQ(kTestFilenames_and_Contents.size(), file_infos.size()); + for (size_t i = 0; i < file_infos.size(); i++) { + // Check that display name is unique. + for (size_t j = 0; j < i; j++) { + EXPECT_FALSE(base::FilePath::CompareEqualIgnoreCase( + file_infos[j].display_name.value(), + file_infos[i].display_name.value())); + } + } + + std::string read_contents; + base::FilePath temp_dir; + EXPECT_TRUE(base::GetTempDir(&temp_dir)); + + // Callback for GetVirtualFilesAsTempFiles is executed when all virtual + // files are backed by temp files. + auto callback = + base::BindOnce(&OSExchangeDataWinTest::OnGotVirtualFilesAsTempFiles, + base::Unretained(this)); + + EXPECT_TRUE(copy.GetVirtualFilesAsTempFiles(std::move(callback))); + + // RunUntilIdle assures all async tasks are run. + scoped_task_environment_.RunUntilIdle(); + + EXPECT_EQ(kTestFilenames_and_Contents.size(), file_infos.size()); + for (size_t i = 0; i < retrieved_virtual_files_.size(); i++) { + // Check that display name is unique. + for (size_t j = 0; j < i; j++) { + EXPECT_FALSE(base::FilePath::CompareEqualIgnoreCase( + retrieved_virtual_files_[j].display_name.value(), + retrieved_virtual_files_[i].display_name.value())); + } + + // Check that temp file path is unique. + for (size_t j = 0; j < i; j++) { + EXPECT_FALSE(base::FilePath::CompareEqualIgnoreCase( + retrieved_virtual_files_[j].path.value(), + retrieved_virtual_files_[i].path.value())); + } + + // Check if the temp files that back the virtual files are actually + // created in the temp directory. Need to compare long file paths here + // because GetTempDir can return a short ("8.3") path if the test is run + // under a username that is too long. + EXPECT_EQ( + base::MakeLongFilePath(temp_dir), + base::MakeLongFilePath(retrieved_virtual_files_[i].path.DirName())); + EXPECT_EQ(kTestFilenames_and_Contents[i].first.Extension(), + retrieved_virtual_files_[i].path.Extension()); + EXPECT_TRUE(base::ReadFileToString(retrieved_virtual_files_[i].path, + &read_contents)); + if (tymed != TYMED_ISTORAGE) { + EXPECT_EQ(kTestFilenames_and_Contents[i].second, read_contents); + } else { + // IStorage uses compound files, so temp files won't be flat text files. + // Just make sure the original contents appears in the compound files. + EXPECT_TRUE(read_contents.find(kTestFilenames_and_Contents[i].second) != + std::string::npos); + } + } + } +} + +TEST_F(OSExchangeDataWinTest, VirtualFilesInvalidAndDuplicateNames) { + const base::string16 kInvalidFileNameCharacters( + FILE_PATH_LITERAL("\\/:*?\"<>|")); + const base::string16 kInvalidFilePathCharacters( + FILE_PATH_LITERAL("/*?\"<>|")); + const base::FilePath pathWithInvalidFileNameCharacters = + base::FilePath(kInvalidFileNameCharacters) + .AddExtension(FILE_PATH_LITERAL("txt")); + const base::FilePath empty_display_name(FILE_PATH_LITERAL("")); + const base::FilePath maxpath_display_name = + base::FilePath(base::string16(MAX_PATH - 5, L'a')) + .AddExtension(FILE_PATH_LITERAL("txt")); + + const std::vector<std::pair<base::FilePath, std::string>> + kTestFilenames_and_Contents = { + {pathWithInvalidFileNameCharacters, std::string("just some data")}, + {pathWithInvalidFileNameCharacters, + std::string("just some data\0with\0nulls", 25)}, + {// Test that still get a unique name if a previous uniquified + // name is duplicate of this one. + pathWithInvalidFileNameCharacters.InsertBeforeExtension( + FILE_PATH_LITERAL(" (1)")), + std::string("just some more data")}, + // Expect a default display name to be generated ("download" if it + // matters). + {empty_display_name, std::string("data for an empty display name")}, + {empty_display_name, + std::string("data for another empty display name")}, + // Expect a good behavior if the display name length exceeds MAX_PATH. + {maxpath_display_name, + std::string("data for a >MAX_PATH display name")}, + {maxpath_display_name, + std::string("data for another >MAX_PATH display name")}, + }; + + for (const auto& tymed : kStorageMediaTypesForVirtualFiles) { + OSExchangeData data; + data.provider().SetVirtualFileContentsForTesting( + kTestFilenames_and_Contents, tymed); + + OSExchangeData copy(data.provider().Clone()); + std::vector<FileInfo> file_infos; + EXPECT_TRUE(copy.GetVirtualFilenames(&file_infos)); + EXPECT_EQ(kTestFilenames_and_Contents.size(), file_infos.size()); + for (size_t i = 0; i < file_infos.size(); i++) { + // Check that display name does not contain invalid characters. + EXPECT_EQ(std::string::npos, + file_infos[i].display_name.value().find_first_of( + kInvalidFileNameCharacters)); + // Check that display name is unique. + for (size_t j = 0; j < i; j++) { + EXPECT_FALSE(base::FilePath::CompareEqualIgnoreCase( + file_infos[j].display_name.value(), + file_infos[i].display_name.value())); + } + } + + std::string read_contents; + base::FilePath temp_dir; + EXPECT_TRUE(base::GetTempDir(&temp_dir)); + + // Callback for GetVirtualFilesAsTempFiles is executed when all virtual + // files are backed by temp files. + auto callback = + base::BindOnce(&OSExchangeDataWinTest::OnGotVirtualFilesAsTempFiles, + base::Unretained(this)); + + EXPECT_TRUE(copy.GetVirtualFilesAsTempFiles(std::move(callback))); + + // RunUntilIdle assures all async tasks are run. + scoped_task_environment_.RunUntilIdle(); + + EXPECT_EQ(kTestFilenames_and_Contents.size(), file_infos.size()); + for (size_t i = 0; i < retrieved_virtual_files_.size(); i++) { + // Check that display name does not contain invalid characters. + EXPECT_EQ(std::string::npos, + retrieved_virtual_files_[i].display_name.value().find_first_of( + kInvalidFileNameCharacters)); + // Check that display name is unique. + for (size_t j = 0; j < i; j++) { + EXPECT_FALSE(base::FilePath::CompareEqualIgnoreCase( + retrieved_virtual_files_[j].display_name.value(), + retrieved_virtual_files_[i].display_name.value())); + } + // Check that temp file path does not contain invalid characters (except + // for separator). + EXPECT_EQ(std::string::npos, + retrieved_virtual_files_[i].path.value().find_first_of( + kInvalidFilePathCharacters)); + // Check that temp file path is unique. + for (size_t j = 0; j < i; j++) { + EXPECT_FALSE(base::FilePath::CompareEqualIgnoreCase( + retrieved_virtual_files_[j].path.value(), + retrieved_virtual_files_[i].path.value())); + } + + // Check if the temp files that back the virtual files are actually + // created in the temp directory. Need to compare long file paths here + // because GetTempDir can return a short ("8.3") path if the test is run + // under a username that is too long. + EXPECT_EQ( + base::MakeLongFilePath(temp_dir), + base::MakeLongFilePath(retrieved_virtual_files_[i].path.DirName())); + EXPECT_EQ(kTestFilenames_and_Contents[i].first.Extension(), + retrieved_virtual_files_[i].path.Extension()); + // Ability to read the contents implies a temp file was successfully + // created on the file system even though the original suggested display + // name had invalid filename characters. + EXPECT_TRUE(base::ReadFileToString(retrieved_virtual_files_[i].path, + &read_contents)); + if (tymed != TYMED_ISTORAGE) { + EXPECT_EQ(kTestFilenames_and_Contents[i].second, read_contents); + } else { + // IStorage uses compound files, so temp files won't be flat text files. + // Just make sure the original contents appears in the compound files. + EXPECT_TRUE(read_contents.find(kTestFilenames_and_Contents[i].second) != + std::string::npos); + } + } + } +} + +TEST_F(OSExchangeDataWinTest, VirtualFilesEmptyContents) { + const std::vector<std::pair<base::FilePath, std::string>> + kTestFilenames_and_Contents = { + {base::FilePath(FILE_PATH_LITERAL("file_with_no_contents.txt")), + std::string()}, + }; + + for (const auto& tymed : kStorageMediaTypesForVirtualFiles) { + OSExchangeData data; + data.provider().SetVirtualFileContentsForTesting( + kTestFilenames_and_Contents, tymed); + + OSExchangeData copy(data.provider().Clone()); + std::vector<FileInfo> file_infos; + EXPECT_TRUE(copy.GetVirtualFilenames(&file_infos)); + EXPECT_EQ(kTestFilenames_and_Contents.size(), file_infos.size()); + for (size_t i = 0; i < file_infos.size(); i++) { + EXPECT_EQ(kTestFilenames_and_Contents[i].first, + file_infos[i].display_name); + } + + std::string read_contents; + base::FilePath temp_dir; + EXPECT_TRUE(base::GetTempDir(&temp_dir)); + + // Callback for GetVirtualFilesAsTempFiles is executed when all virtual + // files are backed by temp files. + auto callback = + base::BindOnce(&OSExchangeDataWinTest::OnGotVirtualFilesAsTempFiles, + base::Unretained(this)); + + EXPECT_TRUE(copy.GetVirtualFilesAsTempFiles(std::move(callback))); + + // RunUntilIdle assures all async tasks are run. + scoped_task_environment_.RunUntilIdle(); + + EXPECT_EQ(kTestFilenames_and_Contents.size(), + retrieved_virtual_files_.size()); + for (size_t i = 0; i < retrieved_virtual_files_.size(); i++) { + EXPECT_EQ(kTestFilenames_and_Contents[i].first, + retrieved_virtual_files_[i].display_name); + + // Check if the temp files that back the virtual files are actually + // created in the temp directory. Need to compare long file paths here + // because GetTempDir can return a short ("8.3") path if the test is run + // under a username that is too long. + EXPECT_EQ( + base::MakeLongFilePath(temp_dir), + base::MakeLongFilePath(retrieved_virtual_files_[i].path.DirName())); + EXPECT_EQ(kTestFilenames_and_Contents[i].first.Extension(), + retrieved_virtual_files_[i].path.Extension()); + EXPECT_TRUE(base::ReadFileToString(retrieved_virtual_files_[i].path, + &read_contents)); + // IStorage uses compound files, so temp files won't be flat text files. + // Just make sure the original contents appear in the compound files. + if (tymed != TYMED_ISTORAGE) { + EXPECT_EQ(kTestFilenames_and_Contents[i].second, read_contents); + EXPECT_EQ(static_cast<size_t>(0), read_contents.length()); + } + } + } +} + +TEST_F(OSExchangeDataWinTest, CFHtml) { OSExchangeData data; GURL url("http://www.google.com/"); std::wstring html( @@ -332,13 +845,13 @@ TEST(OSExchangeDataWinTest, CFHtml) { ReleaseStgMedium(&medium); } -TEST(OSExchangeDataWinTest, SetURLWithMaxPath) { +TEST_F(OSExchangeDataWinTest, SetURLWithMaxPath) { OSExchangeData data; std::wstring long_title(L'a', MAX_PATH + 1); data.SetURL(GURL("http://google.com"), long_title); } -TEST(OSExchangeDataWinTest, ProvideURLForPlainTextURL) { +TEST_F(OSExchangeDataWinTest, ProvideURLForPlainTextURL) { OSExchangeData data; data.SetString(L"http://google.com"); diff --git a/chromium/ui/base/emoji/emoji_panel_helper_win.cc b/chromium/ui/base/emoji/emoji_panel_helper_win.cc index a4b1eca7ea3..aa0e6492696 100644 --- a/chromium/ui/base/emoji/emoji_panel_helper_win.cc +++ b/chromium/ui/base/emoji/emoji_panel_helper_win.cc @@ -14,7 +14,7 @@ namespace ui { bool IsEmojiPanelSupported() { // Emoji picker is supported on Windows 10's Spring 2018 Update and // above. - return base::win::GetVersion() >= base::win::Version::VERSION_WIN10_RS4; + return base::win::GetVersion() >= base::win::Version::WIN10_RS4; } void ShowEmojiPanel() { diff --git a/chromium/ui/base/idle/BUILD.gn b/chromium/ui/base/idle/BUILD.gn index d9e3eea541d..530d02ade78 100644 --- a/chromium/ui/base/idle/BUILD.gn +++ b/chromium/ui/base/idle/BUILD.gn @@ -13,6 +13,13 @@ component("idle") { defines = [ "IS_UI_BASE_IDLE_IMPL" ] + # All targets in this file are allowed to access any of the headers. + friend = [ ":*" ] + + public = [ + "idle.h", + ] + deps = [ "//base", "//ui/base", @@ -26,6 +33,8 @@ component("idle") { "idle.cc", "idle.h", "idle_chromeos.cc", + "idle_internal.cc", + "idle_internal.h", "idle_mac.mm", "idle_win.cc", ] @@ -66,3 +75,17 @@ component("idle") { ] } } + +static_library("test_support") { + testonly = true + + sources = [ + "scoped_set_idle_state.cc", + "scoped_set_idle_state.h", + ] + + deps = [ + ":idle", + "//base", + ] +} diff --git a/chromium/ui/base/idle/idle.cc b/chromium/ui/base/idle/idle.cc index 0333fbc18ce..3f0e34d2bdf 100644 --- a/chromium/ui/base/idle/idle.cc +++ b/chromium/ui/base/idle/idle.cc @@ -4,9 +4,14 @@ #include "ui/base/idle/idle.h" +#include "ui/base/idle/idle_internal.h" + namespace ui { IdleState CalculateIdleState(int idle_threshold) { + if (IdleStateForTesting().has_value()) + return IdleStateForTesting().value(); + if (CheckIdleStateIsLocked()) return IDLE_STATE_LOCKED; diff --git a/chromium/ui/base/idle/idle_android.cc b/chromium/ui/base/idle/idle_android.cc index f21e03dbff6..40190e89587 100644 --- a/chromium/ui/base/idle/idle_android.cc +++ b/chromium/ui/base/idle/idle_android.cc @@ -9,6 +9,7 @@ #include "base/logging.h" #include "base/memory/singleton.h" #include "jni/IdleDetector_jni.h" +#include "ui/base/idle/idle_internal.h" using base::android::AttachCurrentThread; using base::android::ConvertJavaStringToUTF8; @@ -71,6 +72,9 @@ int CalculateIdleTime() { } bool CheckIdleStateIsLocked() { + if (IdleStateForTesting().has_value()) + return IdleStateForTesting().value() == IDLE_STATE_LOCKED; + return AndroidIdleMonitor::GetInstance()->CheckIdleStateIsLocked(); } diff --git a/chromium/ui/base/idle/idle_chromeos.cc b/chromium/ui/base/idle/idle_chromeos.cc index a58a28591a0..a0f4cde65dc 100644 --- a/chromium/ui/base/idle/idle_chromeos.cc +++ b/chromium/ui/base/idle/idle_chromeos.cc @@ -6,6 +6,7 @@ #include "base/time/time.h" #include "chromeos/dbus/session_manager/session_manager_client.h" +#include "ui/base/idle/idle_internal.h" #include "ui/base/user_activity/user_activity_detector.h" namespace ui { @@ -17,6 +18,9 @@ int CalculateIdleTime() { } bool CheckIdleStateIsLocked() { + if (IdleStateForTesting().has_value()) + return IdleStateForTesting().value() == IDLE_STATE_LOCKED; + return chromeos::SessionManagerClient::Get()->IsScreenLocked(); } diff --git a/chromium/ui/base/idle/idle_internal.cc b/chromium/ui/base/idle/idle_internal.cc new file mode 100644 index 00000000000..ac62882ee5f --- /dev/null +++ b/chromium/ui/base/idle/idle_internal.cc @@ -0,0 +1,16 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/base/idle/idle_internal.h" + +#include "base/no_destructor.h" + +namespace ui { + +base::Optional<IdleState>& IdleStateForTesting() { + static base::NoDestructor<base::Optional<IdleState>> idle_state; + return *idle_state; +} + +} // namespace ui diff --git a/chromium/ui/base/idle/idle_internal.h b/chromium/ui/base/idle/idle_internal.h new file mode 100644 index 00000000000..01e959854f9 --- /dev/null +++ b/chromium/ui/base/idle/idle_internal.h @@ -0,0 +1,20 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_BASE_IDLE_IDLE_INTERNAL_H_ +#define UI_BASE_IDLE_IDLE_INTERNAL_H_ + +#include "base/component_export.h" +#include "base/optional.h" +#include "ui/base/idle/idle.h" + +namespace ui { + +// An optional idle state set by tests via a ScopedSetIdleState to override the +// actual idle state of the system. +COMPONENT_EXPORT(UI_BASE_IDLE) base::Optional<IdleState>& IdleStateForTesting(); + +} // namespace ui + +#endif // UI_BASE_IDLE_IDLE_INTERNAL_H_ diff --git a/chromium/ui/base/idle/idle_linux.cc b/chromium/ui/base/idle/idle_linux.cc index 7058b873b82..ec0ed5c89e2 100644 --- a/chromium/ui/base/idle/idle_linux.cc +++ b/chromium/ui/base/idle/idle_linux.cc @@ -4,6 +4,8 @@ #include "ui/base/idle/idle.h" +#include "ui/base/idle/idle_internal.h" + #if defined(USE_X11) #include "ui/base/idle/idle_query_x11.h" #include "ui/base/idle/screensaver_window_finder_x11.h" @@ -21,6 +23,9 @@ int CalculateIdleTime() { } bool CheckIdleStateIsLocked() { + if (IdleStateForTesting().has_value()) + return IdleStateForTesting().value() == IDLE_STATE_LOCKED; + #if defined(USE_X11) // Usually the screensaver is used to lock the screen. return ScreensaverWindowFinder::ScreensaverWindowExists(); diff --git a/chromium/ui/base/idle/idle_mac.mm b/chromium/ui/base/idle/idle_mac.mm index dc355502c7c..54535863d1f 100644 --- a/chromium/ui/base/idle/idle_mac.mm +++ b/chromium/ui/base/idle/idle_mac.mm @@ -7,6 +7,8 @@ #include <ApplicationServices/ApplicationServices.h> #import <Cocoa/Cocoa.h> +#include "ui/base/idle/idle_internal.h" + @interface MacScreenMonitor : NSObject { @private BOOL screensaverRunning_; @@ -92,6 +94,9 @@ int CalculateIdleTime() { } bool CheckIdleStateIsLocked() { + if (IdleStateForTesting().has_value()) + return IdleStateForTesting().value() == IDLE_STATE_LOCKED; + return [g_screenMonitor isScreensaverRunning] || [g_screenMonitor isScreenLocked]; } diff --git a/chromium/ui/base/idle/idle_win.cc b/chromium/ui/base/idle/idle_win.cc index 4946406f2f5..2c1d3a7d601 100644 --- a/chromium/ui/base/idle/idle_win.cc +++ b/chromium/ui/base/idle/idle_win.cc @@ -7,6 +7,7 @@ #include <limits.h> #include <windows.h> +#include "ui/base/idle/idle_internal.h" #include "ui/base/win/lock_state.h" namespace ui { @@ -51,6 +52,9 @@ int CalculateIdleTime() { } bool CheckIdleStateIsLocked() { + if (IdleStateForTesting().has_value()) + return IdleStateForTesting().value() == IDLE_STATE_LOCKED; + return ui::IsWorkstationLocked() || IsScreensaverRunning(); } diff --git a/chromium/ui/base/idle/scoped_set_idle_state.cc b/chromium/ui/base/idle/scoped_set_idle_state.cc new file mode 100644 index 00000000000..93baa00b0df --- /dev/null +++ b/chromium/ui/base/idle/scoped_set_idle_state.cc @@ -0,0 +1,20 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/base/idle/scoped_set_idle_state.h" + +#include "ui/base/idle/idle_internal.h" + +namespace ui { + +ScopedSetIdleState::ScopedSetIdleState(IdleState state) + : previous_state_(IdleStateForTesting()) { + IdleStateForTesting() = state; +} + +ScopedSetIdleState::~ScopedSetIdleState() { + IdleStateForTesting() = previous_state_; +} + +} // namespace ui diff --git a/chromium/ui/base/idle/scoped_set_idle_state.h b/chromium/ui/base/idle/scoped_set_idle_state.h new file mode 100644 index 00000000000..fa3e61786c5 --- /dev/null +++ b/chromium/ui/base/idle/scoped_set_idle_state.h @@ -0,0 +1,26 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_BASE_IDLE_SCOPED_SET_IDLE_STATE_H_ +#define UI_BASE_IDLE_SCOPED_SET_IDLE_STATE_H_ + +#include "base/optional.h" +#include "ui/base/idle/idle.h" + +namespace ui { + +class ScopedSetIdleState { + public: + explicit ScopedSetIdleState(IdleState state); + ~ScopedSetIdleState(); + + private: + base::Optional<IdleState> previous_state_; + + DISALLOW_COPY_AND_ASSIGN(ScopedSetIdleState); +}; + +} // namespace ui + +#endif // UI_BASE_IDLE_SCOPED_SET_IDLE_STATE_H_
\ No newline at end of file diff --git a/chromium/ui/base/ime/BUILD.gn b/chromium/ui/base/ime/BUILD.gn index 8826b2124c1..ce594dc2f60 100644 --- a/chromium/ui/base/ime/BUILD.gn +++ b/chromium/ui/base/ime/BUILD.gn @@ -7,6 +7,7 @@ import("//build/config/ui.gni") source_set("text_input_types") { sources = [ + "text_input_action.h", "text_input_flags.h", "text_input_mode.h", "text_input_type.h", diff --git a/chromium/ui/base/ime/constants.cc b/chromium/ui/base/ime/constants.cc index e9556078fc3..9acc87b7747 100644 --- a/chromium/ui/base/ime/constants.cc +++ b/chromium/ui/base/ime/constants.cc @@ -6,6 +6,18 @@ namespace ui { +// Here, we define attributes of ui::Event::Properties objects +// kPropertyFromVK const char kPropertyFromVK[] = "from_vk"; +// Properties of the kPropertyFromVK attribute + +// kFromVKIsMirroring is the index of the isMirrorring property on the +// kPropertyFromVK attribute. This is non-zero if mirroring and zero if not +// mirroring +const size_t kPropertyFromVKIsMirroringIndex = 0; +// kFromVKSize is the size of the kPropertyFromVK attribute +// It is equal to the number of kPropertyFromVK +const size_t kPropertyFromVKSize = 1; + } // namespace ui diff --git a/chromium/ui/base/ime/constants.h b/chromium/ui/base/ime/constants.h index 3224e34e65f..aab369adb8b 100644 --- a/chromium/ui/base/ime/constants.h +++ b/chromium/ui/base/ime/constants.h @@ -6,6 +6,7 @@ #define UI_BASE_IME_CONSTANTS_H_ #include "base/component_export.h" +#include "stddef.h" namespace ui { @@ -16,6 +17,12 @@ namespace ui { // Textfield). COMPONENT_EXPORT(UI_BASE_IME) extern const char kPropertyFromVK[]; +// kPropertyFromVKIsMirroringIndex is an index into kPropertyFromVK +// and is used when the key event occurs when mirroring is detected. +COMPONENT_EXPORT(UI_BASE_IME) +extern const size_t kPropertyFromVKIsMirroringIndex; +COMPONENT_EXPORT(UI_BASE_IME) extern const size_t kPropertyFromVKSize; + } // namespace ui #endif // UI_BASE_IME_CONSTANTS_H_ diff --git a/chromium/ui/base/ime/dummy_text_input_client.cc b/chromium/ui/base/ime/dummy_text_input_client.cc index 1bedbfeb6d6..da3fc28f88d 100644 --- a/chromium/ui/base/ime/dummy_text_input_client.cc +++ b/chromium/ui/base/ime/dummy_text_input_client.cc @@ -145,11 +145,15 @@ bool DummyTextInputClient::ShouldDoLearning() { return false; } -#if defined(OS_WIN) -void DummyTextInputClient::SetCompositionFromExistingText( +#if defined(OS_WIN) || defined(OS_CHROMEOS) +bool DummyTextInputClient::SetCompositionFromExistingText( const gfx::Range& range, - const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) {} + const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) { + return false; +} +#endif +#if defined(OS_WIN) void DummyTextInputClient::SetActiveCompositionForAccessibility( const gfx::Range& range, const base::string16& active_composition_text, diff --git a/chromium/ui/base/ime/dummy_text_input_client.h b/chromium/ui/base/ime/dummy_text_input_client.h index a06a70eb07b..0d9f505357e 100644 --- a/chromium/ui/base/ime/dummy_text_input_client.h +++ b/chromium/ui/base/ime/dummy_text_input_client.h @@ -56,11 +56,13 @@ class DummyTextInputClient : public TextInputClient { ukm::SourceId GetClientSourceForMetrics() const override; bool ShouldDoLearning() override; -#if defined(OS_WIN) - // Overridden from ui::TextInputClient(Windows only): - void SetCompositionFromExistingText( +#if defined(OS_WIN) || defined(OS_CHROMEOS) + bool SetCompositionFromExistingText( const gfx::Range& range, const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) override; +#endif + +#if defined(OS_WIN) void SetActiveCompositionForAccessibility( const gfx::Range& range, const base::string16& active_composition_text, diff --git a/chromium/ui/base/ime/ime_engine_handler_interface.h b/chromium/ui/base/ime/ime_engine_handler_interface.h index 37d015cbc5e..2b10904785b 100644 --- a/chromium/ui/base/ime/ime_engine_handler_interface.h +++ b/chromium/ui/base/ime/ime_engine_handler_interface.h @@ -114,10 +114,6 @@ class COMPONENT_EXPORT(UI_BASE_IME) IMEEngineHandlerInterface { // Called when the composition bounds changed. virtual void SetCompositionBounds(const std::vector<gfx::Rect>& bounds) = 0; - // Returns whether the engine is interested in key events. - // If not, InputMethodChromeOS won't feed it with key events. - virtual bool IsInterestedInKeyEvent() const = 0; - #if defined(OS_CHROMEOS) // Called when a property is activated or changed. diff --git a/chromium/ui/base/ime/ime_input_context_handler_interface.h b/chromium/ui/base/ime/ime_input_context_handler_interface.h index 21c0f45c541..88abe7dd29a 100644 --- a/chromium/ui/base/ime/ime_input_context_handler_interface.h +++ b/chromium/ui/base/ime/ime_input_context_handler_interface.h @@ -25,6 +25,15 @@ class COMPONENT_EXPORT(UI_BASE_IME) IMEInputContextHandlerInterface { // Called when the engine commit a text. virtual void CommitText(const std::string& text) = 0; +#if defined(OS_CHROMEOS) + // Called when the engine changes the composition range. + // Returns whether the operation was successful. + virtual bool SetCompositionRange( + uint32_t before, + uint32_t after, + const std::vector<ui::ImeTextSpan>& text_spans) = 0; +#endif + // Called when the engine updates composition text. virtual void UpdateCompositionText(const CompositionText& text, uint32_t cursor_pos, diff --git a/chromium/ui/base/ime/ime_text_span.cc b/chromium/ui/base/ime/ime_text_span.cc index 14b482ed591..4f3aeadbc2b 100644 --- a/chromium/ui/base/ime/ime_text_span.cc +++ b/chromium/ui/base/ime/ime_text_span.cc @@ -9,17 +9,6 @@ namespace ui { -ImeTextSpan::ImeTextSpan() : ImeTextSpan(0, 0, Thickness::kThin) {} - -ImeTextSpan::ImeTextSpan(uint32_t start_offset, - uint32_t end_offset, - Thickness thickness) - : ImeTextSpan(Type::kComposition, - start_offset, - end_offset, - thickness, - SK_ColorTRANSPARENT) {} - ImeTextSpan::ImeTextSpan(Type type, uint32_t start_offset, uint32_t end_offset, diff --git a/chromium/ui/base/ime/ime_text_span.h b/chromium/ui/base/ime/ime_text_span.h index 4e449567d8d..67902494fcb 100644 --- a/chromium/ui/base/ime/ime_text_span.h +++ b/chromium/ui/base/ime/ime_text_span.h @@ -37,18 +37,12 @@ struct COMPONENT_EXPORT(UI_BASE_IME_TYPES) ImeTextSpan { kThick, }; - // The default constructor is used by generated Mojo code. - ImeTextSpan(); - // TODO(huangs): remove this constructor. - ImeTextSpan(uint32_t start_offset, - uint32_t end_offset, - Thickness thickness); ImeTextSpan( - Type type, - uint32_t start_offset, - uint32_t end_offset, - Thickness thickness, - SkColor background_color, + Type type = Type::kComposition, + uint32_t start_offset = 0, + uint32_t end_offset = 0, + Thickness thickness = Thickness::kThin, + SkColor background_color = SK_ColorTRANSPARENT, SkColor suggestion_highlight_color = SK_ColorTRANSPARENT, const std::vector<std::string>& suggestions = std::vector<std::string>()); diff --git a/chromium/ui/base/ime/init/input_method_factory.cc b/chromium/ui/base/ime/init/input_method_factory.cc index 15147aa7045..81f7f8a9a1e 100644 --- a/chromium/ui/base/ime/init/input_method_factory.cc +++ b/chromium/ui/base/ime/init/input_method_factory.cc @@ -61,7 +61,7 @@ std::unique_ptr<InputMethod> CreateInputMethod( return std::make_unique<InputMethodChromeOS>(delegate); #elif defined(OS_WIN) if (base::FeatureList::IsEnabled(features::kTSFImeSupport) && - base::win::GetVersion() >= base::win::VERSION_WIN10_RS2) { + base::win::GetVersion() >= base::win::Version::WIN10_RS3) { return std::make_unique<InputMethodWinTSF>(delegate, widget); } return std::make_unique<InputMethodWinImm32>(delegate, widget); diff --git a/chromium/ui/base/ime/input_method_base.cc b/chromium/ui/base/ime/input_method_base.cc index b2203febab1..b621ced8c27 100644 --- a/chromium/ui/base/ime/input_method_base.cc +++ b/chromium/ui/base/ime/input_method_base.cc @@ -254,6 +254,15 @@ void InputMethodBase::UpdateCompositionText(const CompositionText& composition_, SendFakeProcessKeyEvent(false); } +#if defined(OS_CHROMEOS) +bool InputMethodBase::SetCompositionRange( + uint32_t before, + uint32_t after, + const std::vector<ui::ImeTextSpan>& text_spans) { + return false; +} +#endif + void InputMethodBase::DeleteSurroundingText(int32_t offset, uint32_t length) {} SurroundingTextInfo InputMethodBase::GetSurroundingTextInfo() { diff --git a/chromium/ui/base/ime/input_method_base.h b/chromium/ui/base/ime/input_method_base.h index be649f4f709..fb57c53ccbc 100644 --- a/chromium/ui/base/ime/input_method_base.h +++ b/chromium/ui/base/ime/input_method_base.h @@ -90,6 +90,14 @@ class COMPONENT_EXPORT(UI_BASE_IME) InputMethodBase void UpdateCompositionText(const CompositionText& text, uint32_t cursor_pos, bool visible) override; + +#if defined(OS_CHROMEOS) + bool SetCompositionRange( + uint32_t before, + uint32_t after, + const std::vector<ui::ImeTextSpan>& text_spans) override; +#endif + void DeleteSurroundingText(int32_t offset, uint32_t length) override; SurroundingTextInfo GetSurroundingTextInfo() override; void SendKeyEvent(KeyEvent* event) override; diff --git a/chromium/ui/base/ime/input_method_delegate.cc b/chromium/ui/base/ime/input_method_delegate.cc index 92c25586fa4..a0e165d5c9b 100644 --- a/chromium/ui/base/ime/input_method_delegate.cc +++ b/chromium/ui/base/ime/input_method_delegate.cc @@ -4,19 +4,11 @@ #include "ui/base/ime/input_method_delegate.h" -#include "base/callback.h" -#include "ui/base/ime/mojo/ime.mojom.h" #include "ui/events/event.h" namespace ui { namespace internal { -bool InputMethodDelegate::ConnectToImeEngine( - ::ime::mojom::ImeEngineRequest engine_request, - ::ime::mojom::ImeEngineClientPtr client) { - return false; -} - // static void InputMethodDelegate::RunDispatchKeyEventPostIMECallback( KeyEvent* key_event, diff --git a/chromium/ui/base/ime/input_method_delegate.h b/chromium/ui/base/ime/input_method_delegate.h index 324982e39ad..c6c238cf228 100644 --- a/chromium/ui/base/ime/input_method_delegate.h +++ b/chromium/ui/base/ime/input_method_delegate.h @@ -5,19 +5,8 @@ #ifndef UI_BASE_IME_INPUT_METHOD_DELEGATE_H_ #define UI_BASE_IME_INPUT_METHOD_DELEGATE_H_ -#include "base/callback_forward.h" +#include "base/callback.h" #include "base/component_export.h" -#include "mojo/public/cpp/bindings/interface_ptr.h" -#include "mojo/public/cpp/bindings/interface_request.h" - -namespace ime { -namespace mojom { - -class ImeEngine; -class ImeEngineClient; - -} // namespace mojom -} // namespace ime namespace ui { @@ -45,10 +34,6 @@ class COMPONENT_EXPORT(UI_BASE_IME) InputMethodDelegate { KeyEvent* key_event, DispatchKeyEventPostIMECallback callback) = 0; - virtual bool ConnectToImeEngine( - mojo::InterfaceRequest<::ime::mojom::ImeEngine> engine_request, - mojo::InterfacePtr<::ime::mojom::ImeEngineClient> client); - protected: static void RunDispatchKeyEventPostIMECallback( KeyEvent* key_event, diff --git a/chromium/ui/base/ime/linux/input_method_auralinux.cc b/chromium/ui/base/ime/linux/input_method_auralinux.cc index 1bfa4258a69..ae5043c43d5 100644 --- a/chromium/ui/base/ime/linux/input_method_auralinux.cc +++ b/chromium/ui/base/ime/linux/input_method_auralinux.cc @@ -92,8 +92,7 @@ ui::EventDispatchDetails InputMethodAuraLinux::DispatchKeyEvent( // or the ET_KEY_RELEASED event of all key. // 2) |filtered| == true && NeedInsertChar(): the ET_KEY_PRESSED event of // character key. - if (text_input_type_ != TEXT_INPUT_TYPE_PASSWORD && - GetEngine() && GetEngine()->IsInterestedInKeyEvent() && + if (text_input_type_ != TEXT_INPUT_TYPE_PASSWORD && GetEngine() && (!filtered || NeedInsertChar())) { ui::IMEEngineHandlerInterface::KeyEventDoneCallback callback = base::BindOnce(&InputMethodAuraLinux::ProcessKeyEventByEngineDone, diff --git a/chromium/ui/base/ime/mock_ime_input_context_handler.cc b/chromium/ui/base/ime/mock_ime_input_context_handler.cc index e01c87efa16..fdb9a392026 100644 --- a/chromium/ui/base/ime/mock_ime_input_context_handler.cc +++ b/chromium/ui/base/ime/mock_ime_input_context_handler.cc @@ -32,6 +32,15 @@ void MockIMEInputContextHandler::UpdateCompositionText( last_update_composition_arg_.is_visible = visible; } +#if defined(OS_CHROMEOS) +bool MockIMEInputContextHandler::SetCompositionRange( + uint32_t before, + uint32_t after, + const std::vector<ui::ImeTextSpan>& text_spans) { + return false; +} +#endif + void MockIMEInputContextHandler::DeleteSurroundingText(int32_t offset, uint32_t length) { ++delete_surrounding_text_call_count_; diff --git a/chromium/ui/base/ime/mock_ime_input_context_handler.h b/chromium/ui/base/ime/mock_ime_input_context_handler.h index 2d0df397974..9e66f32375d 100644 --- a/chromium/ui/base/ime/mock_ime_input_context_handler.h +++ b/chromium/ui/base/ime/mock_ime_input_context_handler.h @@ -36,6 +36,14 @@ class COMPONENT_EXPORT(UI_BASE_IME) MockIMEInputContextHandler void UpdateCompositionText(const CompositionText& text, uint32_t cursor_pos, bool visible) override; + +#if defined(OS_CHROMEOS) + bool SetCompositionRange( + uint32_t before, + uint32_t after, + const std::vector<ui::ImeTextSpan>& text_spans) override; +#endif + void DeleteSurroundingText(int32_t offset, uint32_t length) override; SurroundingTextInfo GetSurroundingTextInfo() override; void SendKeyEvent(KeyEvent* event) override; diff --git a/chromium/ui/base/ime/mojo/BUILD.gn b/chromium/ui/base/ime/mojo/BUILD.gn index 003b9776d81..bdb71a25691 100644 --- a/chromium/ui/base/ime/mojo/BUILD.gn +++ b/chromium/ui/base/ime/mojo/BUILD.gn @@ -6,8 +6,6 @@ import("//mojo/public/tools/bindings/mojom.gni") mojom("mojo") { sources = [ - "ime.mojom", - "ime_engine_factory_registry.mojom", "ime_types.mojom", ] diff --git a/chromium/ui/base/ime/mojo/ime.mojom b/chromium/ui/base/ime/mojo/ime.mojom deleted file mode 100644 index d28cbf8dd71..00000000000 --- a/chromium/ui/base/ime/mojo/ime.mojom +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -module ime.mojom; - -import "ui/base/ime/mojo/ime_types.mojom"; -import "ui/events/mojo/event.mojom"; -import "ui/gfx/geometry/mojo/geometry.mojom"; - -// The data of the text input field for the IME. -// It is passed from the client to the IME through StartInput method. -struct EditorInfo { - ui.mojom.TextInputType type; - ui.mojom.TextInputMode mode; - int32 flags; - ui.mojom.FocusReason focus_reason; - bool should_do_learning; -}; - -// Represents the IME (a.k.a. input-method engine). -// The client uses this interface to communicate with the IME. -interface ImeEngine { - // This method is called when the app starts to receive text (e.g. an input - // field is focused) and it is ready for this ImeEngine to process received - // events and send result text back to the app. - StartInput(EditorInfo info); - - // This method is called when the app stops to receive text (e.g. the focused - // input field lost the focus). The ImeEngine usually handles this to reset - // its internal states. - FinishInput(); - - // This method is called when the app wants to cancel the ongoing composition. - CancelInput(); - - // Dispatches a key event to this ImeEngine, which will respond with a boolean - // value of true means the key event is handled and false unhandled. - // If the key event is handled, the app should NOT apply its default behaviors - // (e.g. shortcuts, generate text, etc.). - ProcessKeyEvent(ui.mojom.Event key_event) => (bool handled); - - // Called when a new surrounding text is set by the app. - // The |text| is the surrounding text and |cursor| is 0 based index of cursor - // position in |text|. If there is selection range, |anchor| represents - // opposite index from |cursor|. Otherwise |anchor| is equal to |cursor|. - // If not all surrounding text is given |offset| indicates the starting - // offset of |text|. - UpdateSurroundingInfo(string text, int32 cursor, int32 anchor, int32 offset); - - // Called when the composition bounds in screen changes. - // The composition bounds can be changed when ImeEngine changes composition or - // the text field's coordinates is changed by the app. - UpdateCompositionBounds(array<gfx.mojom.Rect> bounds); -}; - -// Used by |ImeEngine| to communicate state back to the client. -// The app should generate the corresponding results to the input field: -// - immediately if didn't dispatch a key event to IME; -// (e.g. by IME's on-screen keyboard) -// - later after the IME responds the |ProcessKeyEvent| with the result; -interface ImeEngineClient { - // Called when the IME wants to insert the |text| to the input field. - CommitText(string text); - - // Called when the IME wants to generate/update the composition text to the - // input field. - UpdateCompositionText(ui.mojom.CompositionText composition_text, - uint32 cursor_pos, - bool visible); - - // Called when the IME wants to remove a piece of text in the input field. - DeleteSurroundingText(int32 offset, uint32 length); - - // Called when the IME wants to silumate a physical key event to the app. - // Usually this is for on-screen keyboard support (e.g. simulate Enter key). - SendKeyEvent(ui.mojom.Event key_event); - - // Called when the ImeEngine is deactivated and this client should reconnect - // for the new active ImeEngine. - Reconnect(); -}; - -// Implemented by the IME. -// An IME should implement both ImeEngine and ImeEngineFactory interfaces. -// The |ImeEngineFactoryRegistry| calls |CreateEngine| to make the ImeEngine -// and ImeEngineClient can hold each other. -interface ImeEngineFactory { - CreateEngine(ImeEngine& engine_request, ImeEngineClient client); -}; diff --git a/chromium/ui/base/ime/mojo/ime_engine_factory_registry.mojom b/chromium/ui/base/ime/mojo/ime_engine_factory_registry.mojom deleted file mode 100644 index c947a6fae5c..00000000000 --- a/chromium/ui/base/ime/mojo/ime_engine_factory_registry.mojom +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2019 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -module ime.mojom; - -import "ui/base/ime/mojo/ime.mojom"; - -// Implemented by IMM service and used by ImeEngine to set the active -// ImeEngineFactory, through which the client can connect to the ImeEngine. -interface ImeEngineFactoryRegistry { - ActivateFactory(ImeEngineFactory factory); -}; diff --git a/chromium/ui/base/ime/mojo/ime_struct_traits_unittest.cc b/chromium/ui/base/ime/mojo/ime_struct_traits_unittest.cc index 86f6d561751..34e14deaf83 100644 --- a/chromium/ui/base/ime/mojo/ime_struct_traits_unittest.cc +++ b/chromium/ui/base/ime/mojo/ime_struct_traits_unittest.cc @@ -99,7 +99,8 @@ TEST_F(IMEStructTraitsTest, TextInputType) { TEST_F(IMEStructTraitsTest, CompositionText) { ui::CompositionText input; input.text = base::UTF8ToUTF16("abcdefghij"); - ui::ImeTextSpan ime_text_span_1(0, 2, ui::ImeTextSpan::Thickness::kThin); + ui::ImeTextSpan ime_text_span_1(ui::ImeTextSpan::Type::kComposition, 0, 2, + ui::ImeTextSpan::Thickness::kThin); ime_text_span_1.underline_color = SK_ColorGRAY; input.ime_text_spans.push_back(ime_text_span_1); ui::ImeTextSpan ime_text_span_2(ui::ImeTextSpan::Type::kComposition, 3, 6, diff --git a/chromium/ui/base/ime/text_input_action.h b/chromium/ui/base/ime/text_input_action.h new file mode 100644 index 00000000000..742a57ce4a7 --- /dev/null +++ b/chromium/ui/base/ime/text_input_action.h @@ -0,0 +1,29 @@ +// Copyright 2019 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_BASE_IME_TEXT_INPUT_ACTION_H_ +#define UI_BASE_IME_TEXT_INPUT_ACTION_H_ + +namespace ui { + +// This mode corresponds to enterkeyhint +// https://html.spec.whatwg.org/multipage/interaction.html#input-modalities:-the-enterkeyhint-attribute +// +// A Java counterpart will be generated for this enum. +// GENERATED_JAVA_ENUM_PACKAGE: org.chromium.ui.base.ime +enum class TextInputAction { + kDefault, + kEnter, + kDone, + kGo, + kNext, + kPrevious, + kSearch, + kSend, + kMaxValue = kSend, +}; + +} // namespace ui + +#endif // UI_BASE_IME_TEXT_INPUT_ACTION_H_ diff --git a/chromium/ui/base/ime/text_input_client.h b/chromium/ui/base/ime/text_input_client.h index 680be4e6b88..a1e54e19e23 100644 --- a/chromium/ui/base/ime/text_input_client.h +++ b/chromium/ui/base/ime/text_input_client.h @@ -204,13 +204,16 @@ class COMPONENT_EXPORT(UI_BASE_IME) TextInputClient { // fields that are considered 'private' (e.g. in incognito tabs). virtual bool ShouldDoLearning() = 0; -#if defined(OS_WIN) +#if defined(OS_WIN) || defined(OS_CHROMEOS) // Start composition over a given UTF-16 code range from existing text. This // should only be used for composition scenario when IME wants to start - // composition on existing text. - virtual void SetCompositionFromExistingText( + // composition on existing text. Returns whether the operation was successful. + virtual bool SetCompositionFromExistingText( const gfx::Range& range, const std::vector<ui::ImeTextSpan>& ui_ime_text_spans) = 0; +#endif + +#if defined(OS_WIN) // Notifies accessibility about active composition. This API is currently // only defined for TSF which is available only on Windows // https://docs.microsoft.com/en-us/windows/desktop/api/UIAutomationCore/ diff --git a/chromium/ui/base/ime/win/BUILD.gn b/chromium/ui/base/ime/win/BUILD.gn index e2416035045..34333cf6523 100644 --- a/chromium/ui/base/ime/win/BUILD.gn +++ b/chromium/ui/base/ime/win/BUILD.gn @@ -44,6 +44,8 @@ jumbo_component("win") { libs = [ "imm32.lib" ] + ldflags = [ "/DELAYLOAD:imm32.dll" ] + jumbo_excluded_sources = [ # tsf_text_store.cc needs INITGUID to be defined before # including any header to properly generate GUID objects. That diff --git a/chromium/ui/base/ime/win/imm32_manager.cc b/chromium/ui/base/ime/win/imm32_manager.cc index 7fbaf37a7c7..75018d3fffa 100644 --- a/chromium/ui/base/ime/win/imm32_manager.cc +++ b/chromium/ui/base/ime/win/imm32_manager.cc @@ -489,4 +489,8 @@ void IMM32Manager::ConvertInputModeToImmFlags(TextInputMode input_mode, *new_conversion_mode = initial_conversion_mode; } +bool IMM32Manager::IsImm32ImeActive() { + return ::ImmGetIMEFileName(::GetKeyboardLayout(0), nullptr, 0) > 0; +} + } // namespace ui diff --git a/chromium/ui/base/ime/win/imm32_manager.h b/chromium/ui/base/ime/win/imm32_manager.h index 958b3c1965f..7edb11e515e 100644 --- a/chromium/ui/base/ime/win/imm32_manager.h +++ b/chromium/ui/base/ime/win/imm32_manager.h @@ -244,6 +244,8 @@ class COMPONENT_EXPORT(UI_BASE_IME_WIN) IMM32Manager { BOOL* open, DWORD* new_conversion_mode); + // Return true if current active IME is IMM32-bassed. + bool IsImm32ImeActive(); protected: // Retrieves the composition information. diff --git a/chromium/ui/base/ime/win/input_method_win_base.cc b/chromium/ui/base/ime/win/input_method_win_base.cc index 7de0c932179..2627f1eae90 100644 --- a/chromium/ui/base/ime/win/input_method_win_base.cc +++ b/chromium/ui/base/ime/win/input_method_win_base.cc @@ -38,10 +38,10 @@ constexpr size_t kExtraNumberOfChars = 20; std::unique_ptr<InputMethodKeyboardController> CreateKeyboardController( HWND toplevel_window_handle) { if (base::FeatureList::IsEnabled(features::kInputPaneOnScreenKeyboard) && - base::win::GetVersion() >= base::win::VERSION_WIN10_RS4) { + base::win::GetVersion() >= base::win::Version::WIN10_RS4) { return std::make_unique<OnScreenKeyboardDisplayManagerInputPane>( toplevel_window_handle); - } else if (base::win::GetVersion() >= base::win::VERSION_WIN8) { + } else if (base::win::GetVersion() >= base::win::Version::WIN8) { return std::make_unique<OnScreenKeyboardDisplayManagerTabTip>( toplevel_window_handle); } @@ -245,8 +245,7 @@ ui::EventDispatchDetails InputMethodWinBase::DispatchKeyEvent( // 1) |char_msgs| is empty when the event is non-character key. // 2) |char_msgs|.size() == 1 when the event is character key and the WM_CHAR // messages have been combined in the event processing flow. - if (char_msgs.size() <= 1 && GetEngine() && - GetEngine()->IsInterestedInKeyEvent()) { + if (char_msgs.size() <= 1 && GetEngine()) { ui::IMEEngineHandlerInterface::KeyEventDoneCallback callback = base::BindOnce(&InputMethodWinBase::ProcessKeyEventDone, weak_ptr_factory_.GetWeakPtr(), diff --git a/chromium/ui/base/ime/win/input_method_win_imm32.cc b/chromium/ui/base/ime/win/input_method_win_imm32.cc index 15030eb55a3..2bd1043590f 100644 --- a/chromium/ui/base/ime/win/input_method_win_imm32.cc +++ b/chromium/ui/base/ime/win/input_method_win_imm32.cc @@ -11,6 +11,7 @@ #include "base/auto_reset.h" #include "base/bind.h" #include "base/command_line.h" +#include "base/metrics/histogram_macros.h" #include "ui/base/ime/ime_bridge.h" #include "ui/base/ime/ime_engine_handler_interface.h" #include "ui/base/ime/text_input_client.h" @@ -212,6 +213,8 @@ LRESULT InputMethodWinImm32::OnImeStartComposition(HWND window_handle, composing_window_handle_ = window_handle; imm32_manager_.CreateImeWindow(window_handle); imm32_manager_.ResetComposition(window_handle); + UMA_HISTOGRAM_BOOLEAN("InputMethod.CompositionWithImm32BasedIme", + imm32_manager_.IsImm32ImeActive()); return 0; } @@ -338,9 +341,7 @@ void InputMethodWinImm32::UpdateIMEState() { // We disable input method in password field. const HWND window_handle = toplevel_window_handle_; const TextInputType text_input_type = - (GetEngine() && GetEngine()->IsInterestedInKeyEvent()) - ? TEXT_INPUT_TYPE_NONE - : GetTextInputType(); + GetEngine() ? TEXT_INPUT_TYPE_NONE : GetTextInputType(); const TextInputMode text_input_mode = GetTextInputMode(); switch (text_input_type) { case ui::TEXT_INPUT_TYPE_NONE: diff --git a/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_input_pane.cc b/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_input_pane.cc index cd84d61f0b9..b114660da81 100644 --- a/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_input_pane.cc +++ b/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_input_pane.cc @@ -182,7 +182,7 @@ OnScreenKeyboardDisplayManagerInputPane:: main_task_runner_)), is_keyboard_visible_(false), weak_factory_(this) { - DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN10_RS1); + DCHECK_GE(base::win::GetVersion(), base::win::Version::WIN10_RS1); DCHECK(main_task_runner_->BelongsToCurrentThread()); // We post the initiation of |virtual_keyboard_input_pane_| to the background diff --git a/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.cc b/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.cc index 97a295875a9..60fca60db2b 100644 --- a/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.cc +++ b/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_tab_tip.cc @@ -254,7 +254,7 @@ void OnScreenKeyboardDetector::HandleKeyboardHidden() { OnScreenKeyboardDisplayManagerTabTip::OnScreenKeyboardDisplayManagerTabTip( HWND hwnd) : hwnd_(hwnd) { - DCHECK_GE(base::win::GetVersion(), base::win::VERSION_WIN8); + DCHECK_GE(base::win::GetVersion(), base::win::Version::WIN8); } OnScreenKeyboardDisplayManagerTabTip::~OnScreenKeyboardDisplayManagerTabTip() {} diff --git a/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_unittest.cc b/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_unittest.cc index 6cc53194ba0..3887fb1512f 100644 --- a/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_unittest.cc +++ b/chromium/ui/base/ime/win/on_screen_keyboard_display_manager_unittest.cc @@ -139,7 +139,7 @@ class OnScreenKeyboardTest : public ::testing::Test { // from the registry. TEST_F(OnScreenKeyboardTest, OSKPath) { // The on screen keyboard is only available on Windows 8+. - if (base::win::GetVersion() < base::win::VERSION_WIN8) + if (base::win::GetVersion() < base::win::Version::WIN8) return; std::unique_ptr<OnScreenKeyboardDisplayManagerTabTip> @@ -164,7 +164,7 @@ TEST_F(OnScreenKeyboardTest, OSKPath) { TEST_F(OnScreenKeyboardTest, InputPane) { // InputPane is supported only on RS1 and later. - if (base::win::GetVersion() < base::win::VERSION_WIN10_RS1) + if (base::win::GetVersion() < base::win::Version::WIN10_RS1) return; std::unique_ptr<OnScreenKeyboardDisplayManagerInputPane> keyboard_display_manager = CreateInputPane(); diff --git a/chromium/ui/base/ime/win/tsf_input_scope.cc b/chromium/ui/base/ime/win/tsf_input_scope.cc index 2c0db97324f..aac64cb44f1 100644 --- a/chromium/ui/base/ime/win/tsf_input_scope.cc +++ b/chromium/ui/base/ime/win/tsf_input_scope.cc @@ -188,8 +188,18 @@ std::vector<InputScope> GetInputScopes(TextInputType text_input_type, } ITfInputScope* CreateInputScope(TextInputType text_input_type, - TextInputMode text_input_mode) { - return new TSFInputScope(GetInputScopes(text_input_type, text_input_mode)); + TextInputMode text_input_mode, + bool should_do_learning) { + std::vector<InputScope> input_scopes; + // Should set input scope to IS_PRIVATE if we are in "incognito" or "guest" + // mode. Note that the IS_PRIVATE input scope is only support from WIN10. + if (!should_do_learning && + (base::win::GetVersion() >= base::win::Version::WIN10)) { + input_scopes.push_back(IS_PRIVATE); + } else { + input_scopes = GetInputScopes(text_input_type, text_input_mode); + } + return new TSFInputScope(input_scopes); } void SetInputScopeForTsfUnawareWindow(HWND window_handle, diff --git a/chromium/ui/base/ime/win/tsf_input_scope.h b/chromium/ui/base/ime/win/tsf_input_scope.h index a895cb13b74..3a62423c118 100644 --- a/chromium/ui/base/ime/win/tsf_input_scope.h +++ b/chromium/ui/base/ime/win/tsf_input_scope.h @@ -35,7 +35,8 @@ std::vector<InputScope> GetInputScopes(TextInputType text_input_type, // reference count. COMPONENT_EXPORT(UI_BASE_IME_WIN) ITfInputScope* CreateInputScope(TextInputType text_input_type, - TextInputMode text_input_mode); + TextInputMode text_input_mode, + bool should_do_learning); // A wrapper of the SetInputScopes API exported by msctf.dll. // http://msdn.microsoft.com/en-us/library/windows/desktop/ms629026.aspx diff --git a/chromium/ui/base/ime/win/tsf_input_scope_unittest.cc b/chromium/ui/base/ime/win/tsf_input_scope_unittest.cc index 9c10d47122b..890fee0255f 100644 --- a/chromium/ui/base/ime/win/tsf_input_scope_unittest.cc +++ b/chromium/ui/base/ime/win/tsf_input_scope_unittest.cc @@ -6,7 +6,9 @@ #include <InputScope.h> #include <stddef.h> +#include <wrl/client.h> +#include "base/win/windows_version.h" #include "testing/gtest/include/gtest/gtest.h" namespace ui { @@ -103,5 +105,54 @@ INSTANTIATE_TEST_SUITE_P(, TSFInputScopeTest, ::testing::ValuesIn(kGetInputScopesTestCases)); +struct CreateInputScopesTestCase { + TextInputType input_type; + TextInputMode input_mode; + bool should_do_learning; + UINT expected_size; + InputScope expected_input_scopes[2]; +}; +class TSFCreateInputScopeTest + : public testing::TestWithParam<CreateInputScopesTestCase> {}; +const CreateInputScopesTestCase kCreateInputScopesTestCases[] = { + // Test cases of TextInputType. + {TEXT_INPUT_TYPE_NONE, TEXT_INPUT_MODE_DEFAULT, true, 1, {IS_DEFAULT}}, + {TEXT_INPUT_TYPE_TEXT, TEXT_INPUT_MODE_DEFAULT, false, 1, {IS_PRIVATE}}, + {TEXT_INPUT_TYPE_PASSWORD, TEXT_INPUT_MODE_DEFAULT, true, 1, {IS_PASSWORD}}, + {TEXT_INPUT_TYPE_PASSWORD, TEXT_INPUT_MODE_DEFAULT, false, 1, {IS_PRIVATE}}, + // Test cases of TextInputMode. + {TEXT_INPUT_TYPE_NONE, TEXT_INPUT_MODE_DEFAULT, true, 1, {IS_DEFAULT}}, + {TEXT_INPUT_TYPE_NONE, TEXT_INPUT_MODE_URL, false, 1, {IS_PRIVATE}}, + {TEXT_INPUT_TYPE_NONE, TEXT_INPUT_MODE_SEARCH, true, 1, {IS_SEARCH}}, + {TEXT_INPUT_TYPE_NONE, TEXT_INPUT_MODE_SEARCH, false, 1, {IS_PRIVATE}}, + // Mixed test cases. + {TEXT_INPUT_TYPE_NUMBER, + TEXT_INPUT_MODE_NUMERIC, + true, + 2, + {IS_NUMBER, IS_DIGITS}}, + {TEXT_INPUT_TYPE_NUMBER, TEXT_INPUT_MODE_NUMERIC, false, 1, {IS_PRIVATE}}, +}; +TEST_P(TSFCreateInputScopeTest, CreateInputScopes) { + if (base::win::GetVersion() < base::win::Version::WIN10) + return; + const CreateInputScopesTestCase& test_case = GetParam(); + Microsoft::WRL::ComPtr<ITfInputScope> input_scope = + tsf_inputscope::CreateInputScope(test_case.input_type, + test_case.input_mode, + test_case.should_do_learning); + UINT c_input_scopes = 0; + InputScope* input_scopes = nullptr; + HRESULT result = input_scope->GetInputScopes(&input_scopes, &c_input_scopes); + EXPECT_EQ(S_OK, result); + EXPECT_EQ(test_case.expected_size, c_input_scopes); + for (size_t i = 0; i < test_case.expected_size; ++i) + EXPECT_EQ(test_case.expected_input_scopes[i], input_scopes[i]); + CoTaskMemFree(input_scopes); +} +INSTANTIATE_TEST_SUITE_P(, + TSFCreateInputScopeTest, + ::testing::ValuesIn(kCreateInputScopesTestCases)); + } // namespace } // namespace ui diff --git a/chromium/ui/base/ime/win/tsf_text_store.cc b/chromium/ui/base/ime/win/tsf_text_store.cc index b82b3e2e93c..5968d0957d0 100644 --- a/chromium/ui/base/ime/win/tsf_text_store.cc +++ b/chromium/ui/base/ime/win/tsf_text_store.cc @@ -260,10 +260,12 @@ STDMETHODIMP TSFTextStore::GetText(LONG acp_start, text_buffer[i] = result[i]; } - if (run_info_buffer_size) { + if (*text_buffer_copied > 0 && run_info_buffer_size) { run_info_buffer[0].uCount = *text_buffer_copied; run_info_buffer[0].type = TS_RT_PLAIN; *run_info_buffer_copied = 1; + } else { + *run_info_buffer_copied = 0; } *next_acp = acp_end; @@ -311,7 +313,10 @@ STDMETHODIMP TSFTextStore::GetTextExt(TsViewCookie view_cookie, tmp_rect.set_width(0); result_rect = gfx::Rect(tmp_rect); } else { - return TS_E_NOLAYOUT; + // PPAPI flash does not support GetCompositionCharacterBounds. We need + // to call GetCaretBounds instead to get correct text bounds info. + // TODO(https://crbug.com/963706): Remove this hack. + result_rect = gfx::Rect(text_input_client_->GetCaretBounds()); } } else if (text_input_client_->GetCompositionCharacterBounds( start_pos - 1, &tmp_rect)) { @@ -341,17 +346,20 @@ STDMETHODIMP TSFTextStore::GetTextExt(TsViewCookie view_cookie, // first character bounds instead of returning TS_E_NOLAYOUT. } } else { - return TS_E_NOLAYOUT; + // PPAPI flash does not support GetCompositionCharacterBounds. We need + // to call GetCaretBounds instead to get correct text bounds info. + // TODO(https://crbug.com/963706): Remove this hack. + if (start_pos == 0) { + result_rect = gfx::Rect(text_input_client_->GetCaretBounds()); + } else { + return TS_E_NOLAYOUT; + } } } else { - // Hack for PPAPI flash. PPAPI flash does not support GetCaretBounds, so - // it's better to return previous caret rectangle instead. - // TODO(nona, kinaba): Remove this hack. - if (start_pos == 0) { - result_rect = gfx::Rect(text_input_client_->GetCaretBounds()); - } else { - return TS_E_NOLAYOUT; - } + // Caret Bounds may be incorrect if focus is in flash control and + // |start_pos| is not equal to |end_pos|. In this case, it's better to + // return previous caret rectangle instead. + result_rect = gfx::Rect(text_input_client_->GetCaretBounds()); } } *rect = display::win::ScreenWin::DIPToScreenRect(window_handle_, result_rect) @@ -672,7 +680,8 @@ STDMETHODIMP TSFTextStore::RetrieveRequestedAttrs( attribute_buffer[0].varValue.vt = VT_UNKNOWN; attribute_buffer[0].varValue.punkVal = tsf_inputscope::CreateInputScope(text_input_client_->GetTextInputType(), - text_input_client_->GetTextInputMode()); + text_input_client_->GetTextInputMode(), + text_input_client_->ShouldDoLearning()); attribute_buffer[0].varValue.punkVal->AddRef(); *attribute_buffer_copied = 1; return S_OK; diff --git a/chromium/ui/base/ime/win/tsf_text_store_unittest.cc b/chromium/ui/base/ime/win/tsf_text_store_unittest.cc index 8ff63eecb50..927a00218da 100644 --- a/chromium/ui/base/ime/win/tsf_text_store_unittest.cc +++ b/chromium/ui/base/ime/win/tsf_text_store_unittest.cc @@ -67,7 +67,7 @@ class MockTextInputClient : public TextInputClient { MOCK_METHOD1(SetTextEditCommandForNextKeyEvent, void(TextEditCommand)); MOCK_CONST_METHOD0(GetClientSourceForMetrics, ukm::SourceId()); MOCK_METHOD2(SetCompositionFromExistingText, - void(const gfx::Range&, const std::vector<ui::ImeTextSpan>&)); + bool(const gfx::Range&, const std::vector<ui::ImeTextSpan>&)); MOCK_METHOD3(SetActiveCompositionForAccessibility, void(const gfx::Range&, const base::string16&, bool)); }; @@ -275,10 +275,14 @@ class TSFTextStoreTestCallback { ASSERT_EQ(expected_string.size(), text_buffer_copied); EXPECT_EQ(expected_string, base::string16(buffer, buffer + text_buffer_copied)); - EXPECT_EQ(1u, run_info_buffer_copied); - EXPECT_EQ(expected_string.size(), run_info.uCount); - EXPECT_EQ(TS_RT_PLAIN, run_info.type); - EXPECT_EQ(expected_next_acp, next_acp); + if (text_buffer_copied > 0) { + EXPECT_EQ(1u, run_info_buffer_copied); + EXPECT_EQ(expected_string.size(), run_info.uCount); + EXPECT_EQ(TS_RT_PLAIN, run_info.type); + EXPECT_EQ(expected_next_acp, next_acp); + } else { + EXPECT_EQ(0u, run_info_buffer_copied); + } } void GetTextErrorTest(LONG acp_start, LONG acp_end, HRESULT error_code) { @@ -1344,8 +1348,10 @@ class GetTextExtTestCallback : public TSFTextStoreTestCallback { // GetCompositionCharacterBounds. SetInternalState(L"a", 0, 0, 1); layout_prepared_character_num_ = 0; - has_composition_text_ = false; GetTextExtTest(view_cookie, 0, 1, 1, 2, 4, 6); + SetInternalState(L"abc", 0, 0, 3); + GetTextExtNoLayoutTest(view_cookie, 2, 3); + return S_OK; } diff --git a/chromium/ui/base/material_design/material_design_controller.cc b/chromium/ui/base/material_design/material_design_controller.cc index bf0caa69189..97e42d5478b 100644 --- a/chromium/ui/base/material_design/material_design_controller.cc +++ b/chromium/ui/base/material_design/material_design_controller.cc @@ -69,7 +69,7 @@ void MaterialDesignController::Initialize() { // TabletModeClient's default state is in non-tablet mode. automatic_touch_ui_ = true; #elif defined(OS_WIN) - if (base::win::GetVersion() >= base::win::VERSION_WIN10) { + if (base::win::GetVersion() >= base::win::Version::WIN10) { // Win 10+ uses dynamic mode by default and checks the current tablet mode // state to determine whether to start in touch mode. automatic_touch_ui_ = true; diff --git a/chromium/ui/base/models/combobox_model_observer.h b/chromium/ui/base/models/combobox_model_observer.h index f8e281e1842..42d15bdc0e0 100644 --- a/chromium/ui/base/models/combobox_model_observer.h +++ b/chromium/ui/base/models/combobox_model_observer.h @@ -5,6 +5,7 @@ #ifndef UI_BASE_MODELS_COMBOBOX_MODEL_OBSERVER_H_ #define UI_BASE_MODELS_COMBOBOX_MODEL_OBSERVER_H_ +#include "base/observer_list_types.h" #include "ui/base/ui_base_export.h" namespace ui { @@ -12,14 +13,14 @@ namespace ui { class ComboboxModel; // Observer for the ComboboxModel. -class UI_BASE_EXPORT ComboboxModelObserver { +class UI_BASE_EXPORT ComboboxModelObserver : public base::CheckedObserver { public: // Invoked when |model| has changed in some way. The observer should assume // everything changed. virtual void OnComboboxModelChanged(ComboboxModel* model) = 0; protected: - virtual ~ComboboxModelObserver() {} + ~ComboboxModelObserver() override = default; }; } // namespace ui diff --git a/chromium/ui/base/models/simple_menu_model.cc b/chromium/ui/base/models/simple_menu_model.cc index 317563bbb9d..709c5844430 100644 --- a/chromium/ui/base/models/simple_menu_model.cc +++ b/chromium/ui/base/models/simple_menu_model.cc @@ -122,11 +122,10 @@ void SimpleMenuModel::AddRadioItemWithStringId(int command_id, int string_id, AddRadioItem(command_id, l10n_util::GetStringUTF16(string_id), group_id); } -void SimpleMenuModel::AddHighlightedItemWithStringIdAndIcon( - int command_id, - int string_id, - const gfx::ImageSkia& icon) { - Item item(command_id, TYPE_HIGHLIGHTED, l10n_util::GetStringUTF16(string_id)); +void SimpleMenuModel::AddHighlightedItemWithIcon(int command_id, + const base::string16& label, + const gfx::ImageSkia& icon) { + Item item(command_id, TYPE_HIGHLIGHTED, label); item.icon = gfx::Image(icon); AppendItem(std::move(item)); } @@ -171,6 +170,17 @@ void SimpleMenuModel::AddSubMenuWithStringId(int command_id, AddSubMenu(command_id, l10n_util::GetStringUTF16(string_id), model); } +void SimpleMenuModel::AddSubMenuWithStringIdAndIcon( + int command_id, + int string_id, + MenuModel* model, + const gfx::ImageSkia& icon) { + Item item(command_id, TYPE_SUBMENU, l10n_util::GetStringUTF16(string_id)); + item.submenu = model; + item.icon = gfx::Image(icon); + AppendItem(std::move(item)); +} + void SimpleMenuModel::AddActionableSubMenu(int command_id, const base::string16& label, MenuModel* model) { diff --git a/chromium/ui/base/models/simple_menu_model.h b/chromium/ui/base/models/simple_menu_model.h index e034b8153bb..7381e707c93 100644 --- a/chromium/ui/base/models/simple_menu_model.h +++ b/chromium/ui/base/models/simple_menu_model.h @@ -90,9 +90,9 @@ class UI_BASE_EXPORT SimpleMenuModel : public MenuModel { void AddCheckItemWithStringId(int command_id, int string_id); void AddRadioItem(int command_id, const base::string16& label, int group_id); void AddRadioItemWithStringId(int command_id, int string_id, int group_id); - void AddHighlightedItemWithStringIdAndIcon(int command_id, - int string_id, - const gfx::ImageSkia& icon); + void AddHighlightedItemWithIcon(int command_id, + const base::string16& label, + const gfx::ImageSkia& icon); // Adds a separator of the specified type to the model. // - Adding a separator after another separator is always invalid if they @@ -108,6 +108,10 @@ class UI_BASE_EXPORT SimpleMenuModel : public MenuModel { const base::string16& label, MenuModel* model); void AddSubMenuWithStringId(int command_id, int string_id, MenuModel* model); + void AddSubMenuWithStringIdAndIcon(int command_id, + int string_id, + MenuModel* model, + const gfx::ImageSkia& icon); void AddActionableSubMenu(int command_id, const base::string16& label, MenuModel* model); diff --git a/chromium/ui/base/models/tree_model.h b/chromium/ui/base/models/tree_model.h index 7be9d24598b..73ae4f0bf65 100644 --- a/chromium/ui/base/models/tree_model.h +++ b/chromium/ui/base/models/tree_model.h @@ -63,21 +63,20 @@ class UI_BASE_EXPORT TreeModelObserver { // hierarchy and observer notification. See tree_node_model.h. class UI_BASE_EXPORT TreeModel { public: + using Nodes = std::vector<TreeModelNode*>; + // Returns the root of the tree. This may or may not be shown in the tree, // see SetRootShown for details. virtual TreeModelNode* GetRoot() = 0; - // Returns the number of children in |parent|. - virtual int GetChildCount(TreeModelNode* parent) = 0; - - // Returns the child node of |parent| at |index|. - virtual TreeModelNode* GetChild(TreeModelNode* parent, int index) = 0; + // Returns the children of |parent|. + virtual Nodes GetChildren(const TreeModelNode* parent) const = 0; // Returns the index of |child| in |parent|. - virtual int GetIndexOf(TreeModelNode* parent, TreeModelNode* child) = 0; + virtual int GetIndexOf(TreeModelNode* parent, TreeModelNode* child) const = 0; // Returns the parent of |node|, or NULL if |node| is the root. - virtual TreeModelNode* GetParent(TreeModelNode* node) = 0; + virtual TreeModelNode* GetParent(TreeModelNode* node) const = 0; // Adds an observer of the model. virtual void AddObserver(TreeModelObserver* observer) = 0; diff --git a/chromium/ui/base/models/tree_node_iterator.h b/chromium/ui/base/models/tree_node_iterator.h index 7d53b2dbbe4..755a8a39c47 100644 --- a/chromium/ui/base/models/tree_node_iterator.h +++ b/chromium/ui/base/models/tree_node_iterator.h @@ -45,7 +45,7 @@ class TreeNodeIterator { } explicit TreeNodeIterator(NodeType* node) { - if (!node->empty()) + if (!node->children().empty()) positions_.push(Position<NodeType>(node, 0)); } diff --git a/chromium/ui/base/models/tree_node_model.h b/chromium/ui/base/models/tree_node_model.h index 70b8ef1ce5c..fac7dfbc517 100644 --- a/chromium/ui/base/models/tree_node_model.h +++ b/chromium/ui/base/models/tree_node_model.h @@ -17,6 +17,10 @@ #include "base/strings/string16.h" #include "ui/base/models/tree_model.h" +namespace bookmarks { +class BookmarkModel; +} + namespace ui { // TreeNodeModel and TreeNodes provide an implementation of TreeModel around @@ -72,6 +76,8 @@ namespace ui { template <class NodeType> class TreeNode : public TreeModelNode { public: + using TreeNodes = std::vector<std::unique_ptr<NodeType>>; + TreeNode() : parent_(nullptr) {} explicit TreeNode(const base::string16& title) @@ -84,7 +90,7 @@ class TreeNode : public TreeModelNode { NodeType* Add(std::unique_ptr<NodeType> node, int index) { DCHECK(node); DCHECK_GE(index, 0); - DCHECK_LE(index, child_count()); + DCHECK_LE(size_t{index}, children_.size()); DCHECK(!node->parent_); node->parent_ = static_cast<NodeType*>(this); NodeType* node_ptr = node.get(); @@ -94,7 +100,8 @@ class TreeNode : public TreeModelNode { // Removes the node at the given index. Returns the removed node. std::unique_ptr<NodeType> Remove(int index) { - DCHECK(index >= 0 && index < child_count()); + DCHECK_GE(index, 0); + DCHECK_LT(size_t{index}, children_.size()); children_[index]->parent_ = nullptr; std::unique_ptr<NodeType> ptr = std::move(children_[index]); children_.erase(children_.begin() + index); @@ -104,12 +111,9 @@ class TreeNode : public TreeModelNode { // Removes the given node. Prefer to remove by index if you know it to avoid // the search for the node to remove. std::unique_ptr<NodeType> Remove(NodeType* node) { - auto i = std::find_if(children_.begin(), children_.end(), - [node](const std::unique_ptr<NodeType>& ptr) { - return ptr.get() == node; - }); - DCHECK(i != children_.end()); - return Remove(i - children_.begin()); + int i = GetIndexOf(node); + DCHECK_NE(-1, i); + return Remove(i); } // Removes all the children from this node. @@ -122,12 +126,12 @@ class TreeNode : public TreeModelNode { // Returns true if this is the root node. bool is_root() const { return parent_ == nullptr; } + const TreeNodes& children() const { return children_; } + // Returns the number of children. + // TODO(https://crbug.com/956419): Remove; use children().size(). int child_count() const { return static_cast<int>(children_.size()); } - // Returns true if this node has no children. - bool empty() const { return children_.empty(); } - // Returns the number of all nodes in the subtree rooted at this node, // including this node. int GetTotalNodeCount() const { @@ -138,9 +142,10 @@ class TreeNode : public TreeModelNode { } // Returns the node at |index|. + // TODO(https://crbug.com/956419): Remove; use children()[index]. const NodeType* GetChild(int index) const { DCHECK_GE(index, 0); - DCHECK_LT(index, child_count()); + DCHECK_LT(size_t{index}, children_.size()); return children_[index].get(); } NodeType* GetChild(int index) { @@ -174,10 +179,10 @@ class TreeNode : public TreeModelNode { return parent_ ? parent_->HasAncestor(ancestor) : false; } - protected: - std::vector<std::unique_ptr<NodeType>>& children() { return children_; } - private: + // TODO(https://crbug.com/956314): Remove this. + friend class bookmarks::BookmarkModel; + // Title displayed in the tree. base::string16 title_; @@ -185,7 +190,7 @@ class TreeNode : public TreeModelNode { NodeType* parent_; // This node's children. - typename std::vector<std::unique_ptr<NodeType>> children_; + TreeNodes children_; DISALLOW_COPY_AND_ASSIGN(TreeNode); }; @@ -226,9 +231,12 @@ class TreeNodeModel : public TreeModel { : root_(std::move(root)) {} virtual ~TreeNodeModel() override {} - NodeType* AsNode(TreeModelNode* model_node) { + static NodeType* AsNode(TreeModelNode* model_node) { return static_cast<NodeType*>(model_node); } + static const NodeType* AsNode(const TreeModelNode* model_node) { + return static_cast<const NodeType*>(model_node); + } NodeType* Add(NodeType* parent, std::unique_ptr<NodeType> node, int index) { DCHECK(parent && node); @@ -278,22 +286,23 @@ class TreeNodeModel : public TreeModel { return root_.get(); } - int GetChildCount(TreeModelNode* parent) override { - DCHECK(parent); - return AsNode(parent)->child_count(); - } - - NodeType* GetChild(TreeModelNode* parent, int index) override { + Nodes GetChildren(const TreeModelNode* parent) const override { DCHECK(parent); - return AsNode(parent)->GetChild(index); + const auto& children = AsNode(parent)->children(); + Nodes nodes; + nodes.reserve(children.size()); + std::transform(children.cbegin(), children.cend(), + std::back_inserter(nodes), + [](const auto& child) { return child.get(); }); + return nodes; } - int GetIndexOf(TreeModelNode* parent, TreeModelNode* child) override { + int GetIndexOf(TreeModelNode* parent, TreeModelNode* child) const override { DCHECK(parent); return AsNode(parent)->GetIndexOf(AsNode(child)); } - TreeModelNode* GetParent(TreeModelNode* node) override { + TreeModelNode* GetParent(TreeModelNode* node) const override { DCHECK(node); return AsNode(node)->parent(); } diff --git a/chromium/ui/base/models/tree_node_model_unittest.cc b/chromium/ui/base/models/tree_node_model_unittest.cc index b39d82bccf3..f5df9488777 100644 --- a/chromium/ui/base/models/tree_node_model_unittest.cc +++ b/chromium/ui/base/models/tree_node_model_unittest.cc @@ -98,14 +98,14 @@ TEST_F(TreeNodeModelTest, RemoveNode) { TestNode* child1 = root->Add(std::make_unique<TestNode>(), 0); - EXPECT_EQ(1, model.GetChildCount(root)); + EXPECT_FALSE(root->children().empty()); // Now remove |child1| from |root| and release the memory. model.Remove(root, child1); EXPECT_EQ("added=0 removed=1 changed=0", GetObserverCountStateAndClear()); - EXPECT_EQ(0, model.GetChildCount(root)); + EXPECT_TRUE(root->children().empty()); } // Verifies if the nodes added under the root are all deleted when calling @@ -140,7 +140,7 @@ TEST_F(TreeNodeModelTest, DeleteAllNodes) { root.DeleteAll(); EXPECT_EQ(0, root.child_count()); - EXPECT_TRUE(root.empty()); + EXPECT_TRUE(root.children().empty()); } // Verifies if GetIndexOf() returns the correct index for the specified node. diff --git a/chromium/ui/base/property_data.h b/chromium/ui/base/property_data.h deleted file mode 100644 index abe13cc746b..00000000000 --- a/chromium/ui/base/property_data.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2017 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_BASE_PROPERTY_DATA_H_ -#define UI_BASE_PROPERTY_DATA_H_ - -#include "ui/base/ui_base_export.h" - -namespace ui { - -// Descendants of ui::PropertyHandler may return a descendant of this class -// when overriding BeforePropertyChange(). This instance is then passed to -// AfterPropertyChange() in order to preserve and/or communicate data between -// those two calls. -struct UI_BASE_EXPORT PropertyData { - virtual ~PropertyData() {} -}; - -} - -#endif diff --git a/chromium/ui/base/resource/data_pack.cc b/chromium/ui/base/resource/data_pack.cc index f6cbde5fcc5..da3771f0193 100644 --- a/chromium/ui/base/resource/data_pack.cc +++ b/chromium/ui/base/resource/data_pack.cc @@ -21,6 +21,7 @@ #include "base/synchronization/lock.h" #include "base/sys_byteorder.h" #include "build/build_config.h" +#include "net/filter/gzip_header.h" // For details of the file layout, see // http://dev.chromium.org/developers/design-documents/linuxresourcesandlocalizedstrings @@ -84,6 +85,14 @@ void MaybePrintResourceId(uint16_t resource_id) { } } +bool HasGzipHeader(base::StringPiece* data) { + net::GZipHeader header; + const char* header_end = nullptr; + net::GZipHeader::Status header_status = + header.ReadMore(data->data(), data->length(), &header_end); + return header_status == net::GZipHeader::COMPLETE_HEADER; +} + // Convenience class to write data to a file. Usage is the following: // 1) Create a new instance, passing a base::FilePath. // 2) Call Write() repeatedly to write all desired data to the file. @@ -380,6 +389,17 @@ bool DataPack::HasResource(uint16_t resource_id) const { return !!LookupEntryById(resource_id); } +bool DataPack::IsGzipped(uint16_t resource_id, bool* is_gzipped) const { + DCHECK(is_gzipped); + if (!HasResource(resource_id)) + return false; + + base::StringPiece data; + CHECK(GetStringPiece(resource_id, &data)); + *is_gzipped = HasGzipHeader(&data); + return true; +} + bool DataPack::GetStringPiece(uint16_t resource_id, base::StringPiece* data) const { // It won't be hard to make this endian-agnostic, but it's not worth diff --git a/chromium/ui/base/resource/data_pack.h b/chromium/ui/base/resource/data_pack.h index 6d252b07ccf..3a8f66c7219 100644 --- a/chromium/ui/base/resource/data_pack.h +++ b/chromium/ui/base/resource/data_pack.h @@ -60,6 +60,7 @@ class UI_DATA_PACK_EXPORT DataPack : public ResourceHandle { // ResourceHandle implementation: bool HasResource(uint16_t resource_id) const override; + bool IsGzipped(uint16_t resource_id, bool* is_gzipped) const override; bool GetStringPiece(uint16_t resource_id, base::StringPiece* data) const override; base::RefCountedStaticMemory* GetStaticMemory( diff --git a/chromium/ui/base/resource/data_pack_literal.cc b/chromium/ui/base/resource/data_pack_literal.cc index 3218f848e94..7d35c6a2ccd 100644 --- a/chromium/ui/base/resource/data_pack_literal.cc +++ b/chromium/ui/base/resource/data_pack_literal.cc @@ -29,10 +29,15 @@ const char kSamplePakContentsV5[] = { 0x01, 0x00, 0x28, 0x00, 0x00, 0x00, // index entry 1 0x04, 0x00, 0x28, 0x00, 0x00, 0x00, // index entry 4 0x06, 0x00, 0x34, 0x00, 0x00, 0x00, // index entry 6 - 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // extra entry for the size of last + 0x00, 0x00, 0x52, 0x00, 0x00, 0x00, // extra entry for the size of last 0x0a, 0x00, 0x01, 0x00, // alias table - 't', 'h', 'i', 's', ' ', 'i', 's', ' ', 'i', 'd', ' ', '4', - 't', 'h', 'i', 's', ' ', 'i', 's', ' ', 'i', 'd', ' ', '6'}; + + 't', 'h', 'i', 's', ' ', 'i', 's', ' ', 'i', 'd', ' ', '4', + // "this is id 6" gzipped with + // echo "this is id 6" | gzip -f -c | hexdump -C + 0x1f, 0x8b, 0x08, 0x00, 0x2e, 0x15, 0xc1, 0x5c, 0x00, 0x03, 0x2b, 0xc9, + 0xc8, 0x2c, 0x56, 0x00, 0xa1, 0x14, 0x05, 0x33, 0x2e, 0x00, 0x10, 0x61, + 0x1b, 0x8b, 0x0d, 0x00, 0x00, 0x00}; const size_t kSamplePakSizeV5 = sizeof(kSamplePakContentsV5); diff --git a/chromium/ui/base/resource/data_pack_unittest.cc b/chromium/ui/base/resource/data_pack_unittest.cc index 9ddeee9a84a..17bd93d5fb3 100644 --- a/chromium/ui/base/resource/data_pack_unittest.cc +++ b/chromium/ui/base/resource/data_pack_unittest.cc @@ -175,7 +175,6 @@ TEST(DataPackTest, LoadFromBufferV5) { EXPECT_EQ("this is id 4", data); ASSERT_TRUE(pack.HasResource(6)); ASSERT_TRUE(pack.GetStringPiece(6, &data)); - EXPECT_EQ("this is id 6", data); // Try reading zero-length data blobs, just in case. ASSERT_TRUE(pack.GetStringPiece(1, &data)); @@ -186,6 +185,16 @@ TEST(DataPackTest, LoadFromBufferV5) { // Try looking up an invalid key. ASSERT_FALSE(pack.HasResource(140)); ASSERT_FALSE(pack.GetStringPiece(140, &data)); + + bool is_gzipped; + ASSERT_TRUE(pack.IsGzipped(1, &is_gzipped)); + ASSERT_FALSE(is_gzipped); + ASSERT_TRUE(pack.IsGzipped(4, &is_gzipped)); + ASSERT_FALSE(is_gzipped); + ASSERT_TRUE(pack.IsGzipped(6, &is_gzipped)); + ASSERT_TRUE(is_gzipped); + ASSERT_TRUE(pack.IsGzipped(10, &is_gzipped)); + ASSERT_FALSE(is_gzipped); } INSTANTIATE_TEST_SUITE_P(WriteBINARY, diff --git a/chromium/ui/base/resource/resource_bundle.cc b/chromium/ui/base/resource/resource_bundle.cc index 340291d8c8f..ab883f7cb8b 100644 --- a/chromium/ui/base/resource/resource_bundle.cc +++ b/chromium/ui/base/resource/resource_bundle.cc @@ -415,7 +415,7 @@ base::string16 ResourceBundle::MaybeMangleLocalizedString( if (!mangle_localized_strings_) return str; - // IDS_DEFAULT_FONT_SIZE and friends are localization "strings" that are + // IDS_MINIMUM_FONT_SIZE and friends are localization "strings" that are // actually integral constants. These should not be mangled or they become // impossible to parse. int ignored; @@ -555,6 +555,15 @@ base::StringPiece ResourceBundle::GetRawDataResourceForScale( return base::StringPiece(); } +bool ResourceBundle::IsGzipped(int resource_id) const { + bool is_gzipped; + for (const auto& pack : data_packs_) { + if (pack->IsGzipped(resource_id, &is_gzipped)) + return is_gzipped; + } + return false; +} + base::string16 ResourceBundle::GetLocalizedString(int resource_id) { #if DCHECK_IS_ON() { diff --git a/chromium/ui/base/resource/resource_bundle.h b/chromium/ui/base/resource/resource_bundle.h index 1f0f0b575e4..23005f6da22 100644 --- a/chromium/ui/base/resource/resource_bundle.h +++ b/chromium/ui/base/resource/resource_bundle.h @@ -217,6 +217,10 @@ class UI_BASE_EXPORT ResourceBundle { // Loads the raw bytes of a scale independent data resource. base::RefCountedMemory* LoadDataResourceBytes(int resource_id) const; + // Whether the |resource_id| is gzipped in this bundle. False is also returned + // if the resource is not found. + bool IsGzipped(int resource_id) const; + // Loads the raw bytes of a data resource nearest the scale factor // |scale_factor| into |bytes|, without doing any processing or // interpretation of the resource. Use ResourceHandle::SCALE_FACTOR_NONE diff --git a/chromium/ui/base/resource/resource_bundle_android.cc b/chromium/ui/base/resource/resource_bundle_android.cc index 899bb583517..6686f17a223 100644 --- a/chromium/ui/base/resource/resource_bundle_android.cc +++ b/chromium/ui/base/resource/resource_bundle_android.cc @@ -8,7 +8,6 @@ #include "base/android/jni_android.h" #include "base/android/jni_string.h" #include "base/logging.h" -#include "base/metrics/histogram_macros.h" #include "base/path_service.h" #include "jni/ResourceBundle_jni.h" #include "ui/base/l10n/l10n_util.h" @@ -78,17 +77,6 @@ std::unique_ptr<DataPack> LoadDataPackFromLocalePak( return data_pack; } -enum class LoadFailureReason { - kLocalePakNotFound, - kPackLoadFailedPrimary, - kPackLoadFailedSecondary, - kMaxValue = kPackLoadFailedSecondary, -}; - -void LogLoadLocaleFailureReason(LoadFailureReason reason) { - UMA_HISTOGRAM_ENUMERATION("Android.ResourceBundle.LoadLocaleFailure", reason); -} - } // namespace void ResourceBundle::LoadCommonResources() { @@ -134,7 +122,6 @@ std::string ResourceBundle::LoadLocaleResources( if (locale_file_path.empty()) { // It's possible that there is no locale.pak. LOG(WARNING) << "locale_file_path.empty() for locale " << app_locale; - LogLoadLocaleFailureReason(LoadFailureReason::kLocalePakNotFound); return std::string(); } int flags = base::File::FLAG_OPEN | base::File::FLAG_READ; @@ -145,10 +132,8 @@ std::string ResourceBundle::LoadLocaleResources( locale_resources_data_ = LoadDataPackFromLocalePak( g_locale_pack_fd, g_locale_pack_region); - if (!locale_resources_data_.get()) { - LogLoadLocaleFailureReason(LoadFailureReason::kPackLoadFailedPrimary); + if (!locale_resources_data_.get()) return std::string(); - } // Load secondary locale .pak file if it exists. For debug build monochrome, // a secondary locale pak will always be loaded; however, it should be @@ -161,10 +146,8 @@ std::string ResourceBundle::LoadLocaleResources( secondary_locale_resources_data_ = LoadDataPackFromLocalePak( g_secondary_locale_pack_fd, g_secondary_locale_pack_region); - if (!secondary_locale_resources_data_.get()) { - LogLoadLocaleFailureReason(LoadFailureReason::kPackLoadFailedSecondary); + if (!secondary_locale_resources_data_.get()) return std::string(); - } } return app_locale; diff --git a/chromium/ui/base/resource/resource_bundle_unittest.cc b/chromium/ui/base/resource/resource_bundle_unittest.cc index be36b9ad616..4b3f0f50d28 100644 --- a/chromium/ui/base/resource/resource_bundle_unittest.cc +++ b/chromium/ui/base/resource/resource_bundle_unittest.cc @@ -35,6 +35,7 @@ using ::testing::_; using ::testing::Between; +using ::testing::DoAll; using ::testing::Property; using ::testing::Return; using ::testing::ReturnArg; @@ -246,6 +247,25 @@ TEST_F(ResourceBundleTest, DelegateGetRawDataResource) { EXPECT_EQ(string_piece.data(), result.data()); } +TEST_F(ResourceBundleTest, IsGzipped) { + base::ScopedTempDir dir; + ASSERT_TRUE(dir.CreateUniqueTempDir()); + base::FilePath data_path = + dir.GetPath().Append(FILE_PATH_LITERAL("sample.pak")); + // Dump contents into a pak file and load it. + ASSERT_EQ(base::WriteFile(data_path, kSamplePakContentsV5, kSamplePakSizeV5), + static_cast<int>(kSamplePakSizeV5)); + ResourceBundle* resource_bundle = CreateResourceBundle(nullptr); + resource_bundle->AddDataPackFromPath(data_path, SCALE_FACTOR_100P); + + ASSERT_FALSE(resource_bundle->IsGzipped(1)); + ASSERT_FALSE(resource_bundle->IsGzipped(4)); + ASSERT_TRUE(resource_bundle->IsGzipped(6)); + ASSERT_FALSE(resource_bundle->IsGzipped(10)); + // Ask for a non-existent resource ID. + ASSERT_FALSE(resource_bundle->IsGzipped(200)); +} + TEST_F(ResourceBundleTest, DelegateGetLocalizedString) { MockResourceBundleDelegate delegate; ResourceBundle* resource_bundle = CreateResourceBundle(&delegate); diff --git a/chromium/ui/base/resource/resource_data_dll_win.cc b/chromium/ui/base/resource/resource_data_dll_win.cc index 12c54c99eb3..153971554c3 100644 --- a/chromium/ui/base/resource/resource_data_dll_win.cc +++ b/chromium/ui/base/resource/resource_data_dll_win.cc @@ -28,6 +28,10 @@ bool ResourceDataDLL::HasResource(uint16_t resource_id) const { &data_size); } +bool ResourceDataDLL::IsGzipped(uint16_t resource_id, bool* is_gzipped) const { + return false; +} + bool ResourceDataDLL::GetStringPiece(uint16_t resource_id, base::StringPiece* data) const { DCHECK(data); diff --git a/chromium/ui/base/resource/resource_data_dll_win.h b/chromium/ui/base/resource/resource_data_dll_win.h index c5f0e60ac38..1e21a828e29 100644 --- a/chromium/ui/base/resource/resource_data_dll_win.h +++ b/chromium/ui/base/resource/resource_data_dll_win.h @@ -21,6 +21,7 @@ class ResourceDataDLL : public ResourceHandle { // ResourceHandle implementation: bool HasResource(uint16_t resource_id) const override; + bool IsGzipped(uint16_t resource_id, bool* is_gzipped) const override; bool GetStringPiece(uint16_t resource_id, base::StringPiece* data) const override; base::RefCountedStaticMemory* GetStaticMemory( diff --git a/chromium/ui/base/resource/resource_handle.h b/chromium/ui/base/resource/resource_handle.h index 055b0370868..1ebd50032fc 100644 --- a/chromium/ui/base/resource/resource_handle.h +++ b/chromium/ui/base/resource/resource_handle.h @@ -31,6 +31,10 @@ class UI_DATA_PACK_EXPORT ResourceHandle { // Returns true if the DataPack contains a resource with id |resource_id|. virtual bool HasResource(uint16_t resource_id) const = 0; + // Whether a specific |resource_id| is gzipped. Returns false if + // !HasResource(resource_id). |is_gzipped| cannot be nullptr. + virtual bool IsGzipped(uint16_t resource_id, bool* is_gzipped) const = 0; + // Get resource by id |resource_id|, filling in |data|. // The data is owned by the DataPack object and should not be modified. // Returns false if the resource id isn't found. diff --git a/chromium/ui/base/ui_base_features.cc b/chromium/ui/base/ui_base_features.cc index 32528f9e6e4..396d1cca111 100644 --- a/chromium/ui/base/ui_base_features.cc +++ b/chromium/ui/base/ui_base_features.cc @@ -45,7 +45,7 @@ bool IsNotificationIndicatorEnabled() { // Enables GPU rasterization for all UI drawing (where not blacklisted). const base::Feature kUiGpuRasterization = {"UiGpuRasterization", -#if defined(OS_MACOSX) || defined(OS_CHROMEOS) +#if defined(OS_MACOSX) || defined(OS_CHROMEOS) || defined(OS_FUCHSIA) base::FEATURE_ENABLED_BY_DEFAULT #else base::FEATURE_DISABLED_BY_DEFAULT @@ -85,7 +85,7 @@ const base::Feature kTSFImeSupport = {"TSFImeSupport", base::FEATURE_ENABLED_BY_DEFAULT}; bool IsUsingWMPointerForTouch() { - return base::win::GetVersion() >= base::win::VERSION_WIN8 && + return base::win::GetVersion() >= base::win::Version::WIN8 && base::FeatureList::IsEnabled(kPointerEventsForTouch); } @@ -122,16 +122,15 @@ const base::Feature kDirectManipulationStylus = { const base::Feature kMash = {"Mash", base::FEATURE_DISABLED_BY_DEFAULT}; -const base::Feature kMashOopViz = {"MashOopViz", - base::FEATURE_DISABLED_BY_DEFAULT}; - const base::Feature kSingleProcessMash = {"SingleProcessMash", base::FEATURE_DISABLED_BY_DEFAULT}; -#if defined(OS_CHROMEOS) -// Connecting the client and IME engine via Mojo. https://crbug.com/937167 -const base::Feature kMojoIMF = {"MojoIMF", base::FEATURE_DISABLED_BY_DEFAULT}; -#endif +const base::Feature kFormControlsRefresh = {"FormControlsRefresh", + base::FEATURE_DISABLED_BY_DEFAULT}; + +bool IsFormControlsRefreshEnabled() { + return base::FeatureList::IsEnabled(features::kFormControlsRefresh); +} bool IsUsingWindowService() { return IsSingleProcessMash() || IsMultiProcessMash(); @@ -141,23 +140,11 @@ bool IsMultiProcessMash() { return base::FeatureList::IsEnabled(features::kMash); } -bool IsMashOopVizEnabled() { - return base::FeatureList::IsEnabled(features::kMashOopViz); -} - bool IsSingleProcessMash() { return base::FeatureList::IsEnabled(features::kSingleProcessMash) && !base::FeatureList::IsEnabled(features::kMash); } -bool IsMojoImfEnabled() { -#if defined(OS_CHROMEOS) - return base::FeatureList::IsEnabled(features::kMojoIMF); -#else - return false; -#endif -} - bool IsAutomaticUiAdjustmentsForTouchEnabled() { #if defined(OS_WIN) || defined(OS_CHROMEOS) return base::FeatureList::IsEnabled( @@ -186,12 +173,6 @@ bool IsOzoneDrmMojo() { IsMultiProcessMash(); } -#if defined(OS_MACOSX) -const base::Feature kDarkMode = {"DarkMode", base::FEATURE_ENABLED_BY_DEFAULT}; -#else -const base::Feature kDarkMode = {"DarkMode", base::FEATURE_DISABLED_BY_DEFAULT}; -#endif - #if defined(OS_CHROMEOS) const base::Feature kHandwritingGesture = {"HandwritingGesture", base::FEATURE_DISABLED_BY_DEFAULT}; diff --git a/chromium/ui/base/ui_base_features.h b/chromium/ui/base/ui_base_features.h index 462b7ab7905..332d7c4182e 100644 --- a/chromium/ui/base/ui_base_features.h +++ b/chromium/ui/base/ui_base_features.h @@ -68,20 +68,15 @@ extern const base::Feature kDirectManipulationStylus; // TODO(jamescook): Make flag only available in Chrome OS. COMPONENT_EXPORT(UI_BASE_FEATURES) extern const base::Feature kMash; -// Used to run Viz in its own process when kMash is enabled. Viz is run in Ash -// process by default. -// TODO(mohsen): Remove this when Viz can run fully in a separate process. Then -// make it the default kMash behavior. -COMPONENT_EXPORT(UI_BASE_FEATURES) extern const base::Feature kMashOopViz; - // NOTE: Do not access directly outside of tests. Use IsSingleProcessMash() // to avoid problems when Mash and SingleProcessMash are both enabled. COMPONENT_EXPORT(UI_BASE_FEATURES) extern const base::Feature kSingleProcessMash; -#if defined(OS_CHROMEOS) -COMPONENT_EXPORT(UI_BASE_FEATURES) extern const base::Feature kMojoIMF; -#endif +// Used to enable the new controls UI. +COMPONENT_EXPORT(UI_BASE_FEATURES) +extern const base::Feature kFormControlsRefresh; +COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsFormControlsRefreshEnabled(); // Returns true if Chrome's aura usage is backed by the WindowService. COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsUsingWindowService(); @@ -90,8 +85,6 @@ COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsUsingWindowService(); // service and Viz graphics). See //ash/README.md. COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsMultiProcessMash(); -COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsMashOopVizEnabled(); - // Returns true if code outside of ash is using the WindowService. In this mode // there are two aura::Envs. Ash uses one with Env::Mode::LOCAL. Non-ash code // uses an aura::Env with a mode of MUS. The non-ash code using mus targets the @@ -100,11 +93,6 @@ COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsMashOopVizEnabled(); // See //ash/README.md. COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsSingleProcessMash(); -// Returns true if the client connects the active ime engine through mojo IPCs. -// This can only return true on Chrome OS, and it can only take effect when -// IsSingleProcessMash() returns true. -COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsMojoImfEnabled(); - // Whether the UI may accommodate touch input in response to hardware changes. COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsAutomaticUiAdjustmentsForTouchEnabled(); @@ -126,10 +114,6 @@ COMPONENT_EXPORT(UI_BASE_FEATURES) extern const base::Feature kEnableOzoneDrmMojo; COMPONENT_EXPORT(UI_BASE_FEATURES) bool IsOzoneDrmMojo(); -// Whether default UI should use a dark mode color scheme, if enabled on -// macOS Mojave/Windows 10. -COMPONENT_EXPORT(UI_BASE_FEATURES) extern const base::Feature kDarkMode; - #if defined(OS_CHROMEOS) COMPONENT_EXPORT(UI_BASE_FEATURES) extern const base::Feature kHandwritingGesture; diff --git a/chromium/ui/base/ui_base_paths.cc b/chromium/ui/base/ui_base_paths.cc index b3974de5d26..11ead8635aa 100644 --- a/chromium/ui/base/ui_base_paths.cc +++ b/chromium/ui/base/ui_base_paths.cc @@ -65,7 +65,7 @@ bool PathProvider(int key, base::FilePath* result) { if (!base::PathService::Get(ui::DIR_RESOURCE_PAKS_ANDROID, &cur)) return false; #else - if (!base::PathService::Get(base::DIR_MODULE, &cur)) + if (!base::PathService::Get(base::DIR_ASSETS, &cur)) return false; #endif cur = cur.AppendASCII("ui_test.pak"); diff --git a/chromium/ui/base/webui/web_ui_util.cc b/chromium/ui/base/webui/web_ui_util.cc index 489fb7ecd62..29ad19d4bdc 100644 --- a/chromium/ui/base/webui/web_ui_util.cc +++ b/chromium/ui/base/webui/web_ui_util.cc @@ -11,6 +11,7 @@ #include "base/logging.h" #include "base/memory/ref_counted_memory.h" #include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" #include "base/strings/string_util.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" @@ -115,10 +116,7 @@ void ParsePathAndImageSpec(const GURL& url, std::string* path, float* scale_factor, int* frame_index) { - *path = net::UnescapeURLComponent( - url.path().substr(1), - net::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS | - net::UnescapeRule::SPACES); + *path = net::UnescapeBinaryURLComponent(url.path_piece().substr(1)); if (scale_factor) *scale_factor = 1.0f; if (frame_index) diff --git a/chromium/ui/base/win/hwnd_subclass.cc b/chromium/ui/base/win/hwnd_subclass.cc index 184698ba041..c6325d5628d 100644 --- a/chromium/ui/base/win/hwnd_subclass.cc +++ b/chromium/ui/base/win/hwnd_subclass.cc @@ -11,6 +11,7 @@ #include "base/memory/ptr_util.h" #include "base/memory/singleton.h" #include "base/stl_util.h" +#include "ui/base/win/touch_input.h" #include "ui/gfx/win/hwnd_util.h" namespace { @@ -34,19 +35,6 @@ WNDPROC GetCurrentWndProc(HWND target) { return reinterpret_cast<WNDPROC>(GetWindowLongPtr(target, GWLP_WNDPROC)); } -// Not defined before Win7 -BOOL GetTouchInputInfoWrapper(HTOUCHINPUT handle, UINT count, - PTOUCHINPUT pointer, int size) { - typedef BOOL(WINAPI *GetTouchInputInfoPtr)(HTOUCHINPUT, UINT, - PTOUCHINPUT, int); - GetTouchInputInfoPtr get_touch_input_info_func = - reinterpret_cast<GetTouchInputInfoPtr>( - GetProcAddress(GetModuleHandleA("user32.dll"), "GetTouchInputInfo")); - if (get_touch_input_info_func) - return get_touch_input_info_func(handle, count, pointer, size); - return FALSE; -} - } // namespace namespace ui { @@ -140,8 +128,8 @@ LRESULT HWNDSubclass::OnWndProc(HWND hwnd, if (message == WM_TOUCH) { TOUCHINPUT point; - if (GetTouchInputInfoWrapper(reinterpret_cast<HTOUCHINPUT>(l_param), 1, - &point, sizeof(TOUCHINPUT))) { + if (ui::GetTouchInputInfoWrapper(reinterpret_cast<HTOUCHINPUT>(l_param), 1, + &point, sizeof(TOUCHINPUT))) { POINT touch_location = {TOUCH_COORD_TO_PIXEL(point.x), TOUCH_COORD_TO_PIXEL(point.y)}; HWND actual_target = WindowFromPoint(touch_location); diff --git a/chromium/ui/base/win/lock_state.cc b/chromium/ui/base/win/lock_state.cc index 3f6d9395367..03e4297d409 100644 --- a/chromium/ui/base/win/lock_state.cc +++ b/chromium/ui/base/win/lock_state.cc @@ -29,7 +29,7 @@ bool IsSessionLocked() { auto session_flags = info->Data.WTSInfoExLevel1.SessionFlags; // For Windows 7 SessionFlags has inverted logic: // https://msdn.microsoft.com/en-us/library/windows/desktop/ee621019. - if (base::win::GetVersion() == base::win::VERSION_WIN7) + if (base::win::GetVersion() == base::win::Version::WIN7) is_locked = session_flags == WTS_SESSIONSTATE_UNLOCK; else is_locked = session_flags == WTS_SESSIONSTATE_LOCK; diff --git a/chromium/ui/base/win/scoped_ole_initializer.cc b/chromium/ui/base/win/scoped_ole_initializer.cc index 48361aa0896..d3d72f883b8 100644 --- a/chromium/ui/base/win/scoped_ole_initializer.cc +++ b/chromium/ui/base/win/scoped_ole_initializer.cc @@ -4,31 +4,19 @@ #include "ui/base/win/scoped_ole_initializer.h" +#include <ole2.h> + #include "base/logging.h" namespace ui { -ScopedOleInitializer::ScopedOleInitializer() - : -#ifndef NDEBUG - // Using the windows API directly to avoid dependency on platform_thread. - thread_id_(GetCurrentThreadId()), -#endif - hr_(OleInitialize(NULL)) { -#ifndef NDEBUG - if (hr_ == S_FALSE) { - LOG(ERROR) << "Multiple OleInitialize() calls for thread " << thread_id_; - } else { - DCHECK_NE(OLE_E_WRONGCOMPOBJ, hr_) << "Incompatible DLLs on machine"; - DCHECK_NE(RPC_E_CHANGED_MODE, hr_) << "Invalid COM thread model change"; - } -#endif +ScopedOleInitializer::ScopedOleInitializer() : hr_(OleInitialize(NULL)) { + DCHECK_NE(OLE_E_WRONGCOMPOBJ, hr_) << "Incompatible DLLs on machine"; + DCHECK_NE(RPC_E_CHANGED_MODE, hr_) << "Invalid COM thread model change"; } ScopedOleInitializer::~ScopedOleInitializer() { -#ifndef NDEBUG - DCHECK_EQ(thread_id_, GetCurrentThreadId()); -#endif + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); if (SUCCEEDED(hr_)) OleUninitialize(); } diff --git a/chromium/ui/base/win/scoped_ole_initializer.h b/chromium/ui/base/win/scoped_ole_initializer.h index 255a68ccc23..28085f5f022 100644 --- a/chromium/ui/base/win/scoped_ole_initializer.h +++ b/chromium/ui/base/win/scoped_ole_initializer.h @@ -5,9 +5,9 @@ #ifndef UI_BASE_WIN_SCOPED_OLE_INITIALIZER_H_ #define UI_BASE_WIN_SCOPED_OLE_INITIALIZER_H_ -#include <ole2.h> - #include "base/macros.h" +#include "base/threading/thread_checker.h" +#include "base/win/windows_types.h" #include "ui/base/ui_base_export.h" namespace ui { @@ -18,13 +18,7 @@ class UI_BASE_EXPORT ScopedOleInitializer { ~ScopedOleInitializer(); private: -#ifndef NDEBUG - // In debug builds we use this variable to catch a potential bug where a - // ScopedOleInitializer instance is deleted on a different thread than it - // was initially created on. If that ever happens it can have bad - // consequences and the cause can be tricky to track down. - DWORD thread_id_; -#endif + THREAD_CHECKER(thread_checker_); HRESULT hr_; DISALLOW_COPY_AND_ASSIGN(ScopedOleInitializer); diff --git a/chromium/ui/base/win/shell.cc b/chromium/ui/base/win/shell.cc index 6301000c92d..1c63dea60d3 100644 --- a/chromium/ui/base/win/shell.cc +++ b/chromium/ui/base/win/shell.cc @@ -185,7 +185,7 @@ bool IsAeroGlassEnabled() { bool IsDwmCompositionEnabled() { // As of Windows 8, DWM composition is always enabled. // In Windows 7 this can change at runtime. - if (base::win::GetVersion() >= base::win::VERSION_WIN8) { + if (base::win::GetVersion() >= base::win::Version::WIN8) { return true; } BOOL is_enabled; diff --git a/chromium/ui/base/win/system_media_controls/BUILD.gn b/chromium/ui/base/win/system_media_controls/BUILD.gn index 41ca48375e5..c205104b118 100644 --- a/chromium/ui/base/win/system_media_controls/BUILD.gn +++ b/chromium/ui/base/win/system_media_controls/BUILD.gn @@ -18,6 +18,10 @@ component("system_media_controls") { "//base", "//ui/gfx", ] + + public_deps = [ + "//skia", + ] } static_library("test_support") { diff --git a/chromium/ui/base/win/system_media_controls/mock_system_media_controls_service.h b/chromium/ui/base/win/system_media_controls/mock_system_media_controls_service.h index 4f5c9138eb9..817b51fce39 100644 --- a/chromium/ui/base/win/system_media_controls/mock_system_media_controls_service.h +++ b/chromium/ui/base/win/system_media_controls/mock_system_media_controls_service.h @@ -25,6 +25,7 @@ class MockSystemMediaControlsService : public SystemMediaControlsService { MOCK_METHOD1(AddObserver, void(SystemMediaControlsServiceObserver* observer)); MOCK_METHOD1(RemoveObserver, void(SystemMediaControlsServiceObserver* observer)); + MOCK_METHOD1(SetEnabled, void(bool enabled)); MOCK_METHOD1(SetIsNextEnabled, void(bool value)); MOCK_METHOD1(SetIsPreviousEnabled, void(bool value)); MOCK_METHOD1(SetIsPlayEnabled, void(bool value)); @@ -32,6 +33,12 @@ class MockSystemMediaControlsService : public SystemMediaControlsService { MOCK_METHOD1(SetIsStopEnabled, void(bool value)); MOCK_METHOD1(SetPlaybackStatus, void(ABI::Windows::Media::MediaPlaybackStatus status)); + MOCK_METHOD1(SetTitle, void(const base::string16& title)); + MOCK_METHOD1(SetArtist, void(const base::string16& artist)); + MOCK_METHOD1(SetThumbnail, void(const SkBitmap& bitmap)); + MOCK_METHOD0(ClearThumbnail, void()); + MOCK_METHOD0(ClearMetadata, void()); + MOCK_METHOD0(UpdateDisplay, void()); private: DISALLOW_COPY_AND_ASSIGN(MockSystemMediaControlsService); diff --git a/chromium/ui/base/win/system_media_controls/system_media_controls_service.h b/chromium/ui/base/win/system_media_controls/system_media_controls_service.h index fe66681f041..8b5eba68a5c 100644 --- a/chromium/ui/base/win/system_media_controls/system_media_controls_service.h +++ b/chromium/ui/base/win/system_media_controls/system_media_controls_service.h @@ -8,6 +8,8 @@ #include <windows.media.control.h> #include "base/component_export.h" +#include "base/strings/string16.h" +#include "third_party/skia/include/core/SkBitmap.h" namespace system_media_controls { @@ -29,6 +31,9 @@ class COMPONENT_EXPORT(SYSTEM_MEDIA_CONTROLS) SystemMediaControlsService { virtual void AddObserver(SystemMediaControlsServiceObserver* observer) = 0; virtual void RemoveObserver(SystemMediaControlsServiceObserver* observer) = 0; + // Enables/disables the service. + virtual void SetEnabled(bool enabled) = 0; + // TODO(steimel): Add other controls. // Enable or disable specific controls. virtual void SetIsNextEnabled(bool value) = 0; @@ -40,6 +45,14 @@ class COMPONENT_EXPORT(SYSTEM_MEDIA_CONTROLS) SystemMediaControlsService { // Setters for metadata. virtual void SetPlaybackStatus( ABI::Windows::Media::MediaPlaybackStatus status) = 0; + virtual void SetTitle(const base::string16& title) = 0; + virtual void SetArtist(const base::string16& artist) = 0; + virtual void SetThumbnail(const SkBitmap& bitmap) = 0; + + // Helpers for metadata. + virtual void ClearThumbnail() = 0; + virtual void ClearMetadata() = 0; + virtual void UpdateDisplay() = 0; protected: virtual ~SystemMediaControlsService(); diff --git a/chromium/ui/base/win/system_media_controls/system_media_controls_service_impl.cc b/chromium/ui/base/win/system_media_controls/system_media_controls_service_impl.cc index 9d7a1a44b76..7862e3c8bb7 100644 --- a/chromium/ui/base/win/system_media_controls/system_media_controls_service_impl.cc +++ b/chromium/ui/base/win/system_media_controls/system_media_controls_service_impl.cc @@ -13,6 +13,7 @@ #include "base/win/core_winrt_util.h" #include "base/win/scoped_hstring.h" #include "ui/base/win/system_media_controls/system_media_controls_service_observer.h" +#include "ui/gfx/codec/png_codec.h" #include "ui/gfx/win/singleton_hwnd.h" namespace system_media_controls { @@ -24,6 +25,12 @@ using ABI::Windows::Media::ISystemMediaTransportControlsButtonPressedEventArgs; using ABI::Windows::Media::SystemMediaTransportControls; using ABI::Windows::Media::SystemMediaTransportControlsButton; using ABI::Windows::Media::SystemMediaTransportControlsButtonPressedEventArgs; +using ABI::Windows::Storage::Streams::IDataWriter; +using ABI::Windows::Storage::Streams::IDataWriterFactory; +using ABI::Windows::Storage::Streams::IOutputStream; +using ABI::Windows::Storage::Streams::IRandomAccessStream; +using ABI::Windows::Storage::Streams::IRandomAccessStreamReference; +using ABI::Windows::Storage::Streams::IRandomAccessStreamReferenceStatics; // static SystemMediaControlsServiceImpl* SystemMediaControlsServiceImpl::GetInstance() { @@ -38,7 +45,7 @@ SystemMediaControlsServiceImpl::~SystemMediaControlsServiceImpl() { if (has_valid_registration_token_) { DCHECK(system_media_controls_); system_media_controls_->remove_ButtonPressed(registration_token_); - system_media_controls_->put_IsEnabled(false); + ClearMetadata(); } } @@ -81,6 +88,22 @@ bool SystemMediaControlsServiceImpl::Initialize() { if (FAILED(hr)) return false; + hr = system_media_controls_->get_DisplayUpdater(&display_updater_); + if (FAILED(hr)) + return false; + + // The current MediaSession API implementation matches the SMTC music type + // most closely, since MediaSession has the artist property which the SMTC + // only presents to music playback types. + hr = display_updater_->put_Type( + ABI::Windows::Media::MediaPlaybackType::MediaPlaybackType_Music); + if (FAILED(hr)) + return false; + + hr = display_updater_->get_MusicProperties(&display_properties_); + if (FAILED(hr)) + return false; + initialized_ = true; return true; } @@ -95,6 +118,12 @@ void SystemMediaControlsServiceImpl::RemoveObserver( observers_.RemoveObserver(observer); } +void SystemMediaControlsServiceImpl::SetEnabled(bool enabled) { + DCHECK(initialized_); + HRESULT hr = system_media_controls_->put_IsEnabled(enabled); + DCHECK(SUCCEEDED(hr)); +} + void SystemMediaControlsServiceImpl::SetIsNextEnabled(bool value) { DCHECK(initialized_); HRESULT hr = system_media_controls_->put_IsNextEnabled(value); @@ -132,6 +161,138 @@ void SystemMediaControlsServiceImpl::SetPlaybackStatus( DCHECK(SUCCEEDED(hr)); } +void SystemMediaControlsServiceImpl::SetTitle(const base::string16& title) { + DCHECK(initialized_); + DCHECK(display_properties_); + base::win::ScopedHString h_title = base::win::ScopedHString::Create(title); + HRESULT hr = display_properties_->put_Title(h_title.get()); + DCHECK(SUCCEEDED(hr)); +} + +void SystemMediaControlsServiceImpl::SetArtist(const base::string16& artist) { + DCHECK(initialized_); + DCHECK(display_properties_); + base::win::ScopedHString h_artist = base::win::ScopedHString::Create(artist); + HRESULT hr = display_properties_->put_Artist(h_artist.get()); + DCHECK(SUCCEEDED(hr)); +} + +void SystemMediaControlsServiceImpl::SetThumbnail(const SkBitmap& bitmap) { + DCHECK(initialized_); + DCHECK(display_updater_); + // Use |icon_data_writer_| to write the bitmap data into |icon_stream_| so we + // can populate |icon_stream_reference_| and then give it to the SMTC. All of + // these are member variables to avoid a race condition between them being + // destructed and the async operation completing. + base::win::ScopedHString id = base::win::ScopedHString::Create( + RuntimeClass_Windows_Storage_Streams_InMemoryRandomAccessStream); + HRESULT hr = base::win::RoActivateInstance(id.get(), &icon_stream_); + DCHECK(SUCCEEDED(hr)); + + Microsoft::WRL::ComPtr<IDataWriterFactory> data_writer_factory; + hr = base::win::GetActivationFactory< + IDataWriterFactory, RuntimeClass_Windows_Storage_Streams_DataWriter>( + &data_writer_factory); + DCHECK(SUCCEEDED(hr)); + + Microsoft::WRL::ComPtr<IOutputStream> output_stream; + hr = icon_stream_.As(&output_stream); + DCHECK(SUCCEEDED(hr)); + + hr = data_writer_factory->CreateDataWriter(output_stream.Get(), + &icon_data_writer_); + DCHECK(SUCCEEDED(hr)); + + std::vector<unsigned char> icon_png; + gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, false, &icon_png); + hr = icon_data_writer_->WriteBytes(icon_png.size(), (BYTE*)icon_png.data()); + DCHECK(SUCCEEDED(hr)); + + // Store the written bytes in the stream, an async operation. + Microsoft::WRL::ComPtr< + ABI::Windows::Foundation::IAsyncOperation<unsigned int>> + store_async_operation; + hr = icon_data_writer_->StoreAsync(&store_async_operation); + DCHECK(SUCCEEDED(hr)); + + // Make a callback that gives the icon to the SMTC once the bits make it into + // |icon_stream_| + auto store_async_callback = Microsoft::WRL::Callback< + ABI::Windows::Foundation::IAsyncOperationCompletedHandler<unsigned int>>( + [this](ABI::Windows::Foundation::IAsyncOperation<unsigned int>* async_op, + ABI::Windows::Foundation::AsyncStatus status) mutable { + // Check the async operation completed successfully. + ABI::Windows::Foundation::IAsyncInfo* async_info; + HRESULT hr = async_op->QueryInterface( + IID_IAsyncInfo, reinterpret_cast<void**>(&async_info)); + DCHECK(SUCCEEDED(hr)); + async_info->get_ErrorCode(&hr); + if (SUCCEEDED(hr) && + status == ABI::Windows::Foundation::AsyncStatus::Completed) { + Microsoft::WRL::ComPtr<IRandomAccessStreamReferenceStatics> + reference_statics; + HRESULT hr = base::win::GetActivationFactory< + IRandomAccessStreamReferenceStatics, + RuntimeClass_Windows_Storage_Streams_RandomAccessStreamReference>( + &reference_statics); + DCHECK(SUCCEEDED(hr)); + + hr = reference_statics->CreateFromStream(icon_stream_.Get(), + &icon_stream_reference_); + DCHECK(SUCCEEDED(hr)); + + hr = display_updater_->put_Thumbnail(icon_stream_reference_.Get()); + DCHECK(SUCCEEDED(hr)); + + hr = display_updater_->Update(); + DCHECK(SUCCEEDED(hr)); + } + return hr; + }); + + hr = store_async_operation->put_Completed(store_async_callback.Get()); + DCHECK(SUCCEEDED(hr)); +} + +void SystemMediaControlsServiceImpl::ClearThumbnail() { + DCHECK(initialized_); + DCHECK(display_updater_); + HRESULT hr = display_updater_->put_Thumbnail(nullptr); + DCHECK(SUCCEEDED(hr)); + + hr = display_updater_->Update(); + DCHECK(SUCCEEDED(hr)); +} + +void SystemMediaControlsServiceImpl::ClearMetadata() { + DCHECK(initialized_); + DCHECK(display_updater_); + HRESULT hr = display_updater_->ClearAll(); + DCHECK(SUCCEEDED(hr)); + + // To prevent disabled controls and the executable name from showing up in the + // SMTC, we need to tell them that we are disabled. + hr = system_media_controls_->put_IsEnabled(false); + DCHECK(SUCCEEDED(hr)); +} + +void SystemMediaControlsServiceImpl::UpdateDisplay() { + DCHECK(initialized_); + DCHECK(system_media_controls_); + DCHECK(display_updater_); + HRESULT hr = system_media_controls_->put_IsEnabled(true); + DCHECK(SUCCEEDED(hr)); + + // |ClearAll()| unsets the type, if we don't set it again then the artist + // won't be displayed. + hr = display_updater_->put_Type( + ABI::Windows::Media::MediaPlaybackType::MediaPlaybackType_Music); + DCHECK(SUCCEEDED(hr)); + + hr = display_updater_->Update(); + DCHECK(SUCCEEDED(hr)); +} + void SystemMediaControlsServiceImpl::OnPlay() { for (SystemMediaControlsServiceObserver& obs : observers_) obs.OnPlay(); diff --git a/chromium/ui/base/win/system_media_controls/system_media_controls_service_impl.h b/chromium/ui/base/win/system_media_controls/system_media_controls_service_impl.h index fb10cdecb9e..025ab923a77 100644 --- a/chromium/ui/base/win/system_media_controls/system_media_controls_service_impl.h +++ b/chromium/ui/base/win/system_media_controls/system_media_controls_service_impl.h @@ -37,6 +37,7 @@ class COMPONENT_EXPORT(SYSTEM_MEDIA_CONTROLS) SystemMediaControlsServiceImpl // SystemMediaControlsService implementation. void AddObserver(SystemMediaControlsServiceObserver* observer) override; void RemoveObserver(SystemMediaControlsServiceObserver* observer) override; + void SetEnabled(bool enabled) override; void SetIsNextEnabled(bool value) override; void SetIsPreviousEnabled(bool value) override; void SetIsPlayEnabled(bool value) override; @@ -44,6 +45,12 @@ class COMPONENT_EXPORT(SYSTEM_MEDIA_CONTROLS) SystemMediaControlsServiceImpl void SetIsStopEnabled(bool value) override; void SetPlaybackStatus( ABI::Windows::Media::MediaPlaybackStatus status) override; + void SetTitle(const base::string16& title) override; + void SetArtist(const base::string16& artist) override; + void SetThumbnail(const SkBitmap& bitmap) override; + void ClearThumbnail() override; + void ClearMetadata() override; + void UpdateDisplay() override; private: friend struct base::DefaultSingletonTraits<SystemMediaControlsServiceImpl>; @@ -60,8 +67,22 @@ class COMPONENT_EXPORT(SYSTEM_MEDIA_CONTROLS) SystemMediaControlsServiceImpl void OnPrevious(); void OnStop(); + // Control and keep track of the metadata. Microsoft::WRL::ComPtr<ABI::Windows::Media::ISystemMediaTransportControls> system_media_controls_; + Microsoft::WRL::ComPtr< + ABI::Windows::Media::ISystemMediaTransportControlsDisplayUpdater> + display_updater_; + Microsoft::WRL::ComPtr<ABI::Windows::Media::IMusicDisplayProperties> + display_properties_; + Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IDataWriter> + icon_data_writer_; + Microsoft::WRL::ComPtr<ABI::Windows::Storage::Streams::IRandomAccessStream> + icon_stream_; + Microsoft::WRL::ComPtr< + ABI::Windows::Storage::Streams::IRandomAccessStreamReference> + icon_stream_reference_; + EventRegistrationToken registration_token_; // True if we've already tried to connect to the SystemMediaTransportControls. diff --git a/chromium/ui/base/win/touch_input.cc b/chromium/ui/base/win/touch_input.cc index 0114bdd3a7f..708d5662c82 100644 --- a/chromium/ui/base/win/touch_input.cc +++ b/chromium/ui/base/win/touch_input.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "ui/base/win/touch_input.h" +#include "base/win/win_util.h" namespace ui { @@ -10,11 +11,9 @@ BOOL GetTouchInputInfoWrapper(HTOUCHINPUT handle, UINT count, PTOUCHINPUT pointer, int size) { - typedef BOOL(WINAPI *GetTouchInputInfoPtr)(HTOUCHINPUT, UINT, - PTOUCHINPUT, int); - static GetTouchInputInfoPtr get_touch_input_info_func = - reinterpret_cast<GetTouchInputInfoPtr>( - GetProcAddress(GetModuleHandleA("user32.dll"), "GetTouchInputInfo")); + static const auto get_touch_input_info_func = + reinterpret_cast<decltype(&::GetTouchInputInfo)>( + base::win::GetUser32FunctionPointer("GetTouchInputInfo")); if (get_touch_input_info_func) return get_touch_input_info_func(handle, count, pointer, size); return FALSE; diff --git a/chromium/ui/base/window_tracker_template.h b/chromium/ui/base/window_tracker_template.h deleted file mode 100644 index e91bdac8077..00000000000 --- a/chromium/ui/base/window_tracker_template.h +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_BASE_WINDOW_TRACKER_TEMPLATE_H_ -#define UI_BASE_WINDOW_TRACKER_TEMPLATE_H_ - -#include <vector> - -#include "base/macros.h" -#include "base/stl_util.h" - -namespace ui { - -// This class is used to track an ordered list of objects that support an -// observer interface with the function OnWindowDestroying(). When the object -// is destroyed it is removed from the ordered list of objects. -// Examples of T include aura::Window and its corresponding -// aura::WindowObserver interface. -template <class T, class TObserver> -class WindowTrackerTemplate : public TObserver { - public: - // A vector<> is used for tracking the windows (instead of a set<>) because - // the user may want to know about the order of the windows that have been - // added. - using WindowList = std::vector<T*>; - - explicit WindowTrackerTemplate(const WindowList& windows) { - for (T* window : windows) - Add(window); - } - WindowTrackerTemplate() {} - ~WindowTrackerTemplate() override { RemoveAll(); } - - // Returns the set of windows being observed. - const WindowList& windows() const { return windows_; } - - // Adds |window| to the set of Windows being tracked. - void Add(T* window) { - if (base::ContainsValue(windows_, window)) - return; - - window->AddObserver(this); - windows_.push_back(window); - } - - void RemoveAll() { - for (T* window : windows_) - window->RemoveObserver(this); - windows_.clear(); - } - - // Removes |window| from the set of windows being tracked. - void Remove(T* window) { - auto iter = std::find(windows_.begin(), windows_.end(), window); - if (iter != windows_.end()) { - window->RemoveObserver(this); - windows_.erase(iter); - } - } - - T* Pop() { - DCHECK(!windows_.empty()); - T* result = windows_[0]; - Remove(result); - return result; - } - - // Returns true if |window| was previously added and has not been removed or - // deleted. - bool Contains(T* window) const { - return base::ContainsValue(windows_, window); - } - - // Observer overrides: - void OnWindowDestroying(T* window) override { - DCHECK(Contains(window)); - Remove(window); - } - - private: - WindowList windows_; - - DISALLOW_COPY_AND_ASSIGN(WindowTrackerTemplate); -}; - -} // namespace ui - -#endif // UI_BASE_WINDOW_TRACKER_TEMPLATE_H_ diff --git a/chromium/ui/base/x/x11_display_util.cc b/chromium/ui/base/x/x11_display_util.cc index e34c5cc6fda..c72d65ce129 100644 --- a/chromium/ui/base/x/x11_display_util.cc +++ b/chromium/ui/base/x/x11_display_util.cc @@ -101,6 +101,21 @@ void ClipWorkArea(std::vector<display::Display>* displays, primary.set_work_area(work_area); } +int GetRefreshRateFromXRRModeInfo(XRRModeInfo* modes, + int num_of_mode, + RRMode current_mode_id) { + for (int i = 0; i < num_of_mode; i++) { + XRRModeInfo mode_info = modes[i]; + if (mode_info.id != current_mode_id) + continue; + if (!mode_info.hTotal || !mode_info.vTotal) + return 0; + + // Refresh Rate = Pixel Clock / (Horizontal Total * Vertical Total) + return mode_info.dotClock / (mode_info.hTotal * mode_info.vTotal); + } + return 0; +} } // namespace int GetXrandrVersion(XDisplay* xdisplay) { @@ -232,9 +247,14 @@ std::vector<display::Display> BuildDisplaysFromXRandRInfo( gfx::ICCProfile icc_profile = ui::GetICCProfileForMonitor( monitor_iter == output_to_monitor.end() ? 0 : monitor_iter->second); icc_profile.HistogramDisplay(display.id()); - display.set_color_space(icc_profile.GetColorSpace()); + display.set_color_space(icc_profile.GetPrimariesOnlyColorSpace()); } + // Set monitor refresh rate + int refresh_rate = GetRefreshRateFromXRRModeInfo( + resources->modes, resources->nmode, crtc->mode); + display.set_display_frequency(refresh_rate); + displays.push_back(display); } } diff --git a/chromium/ui/base/x/x11_util.cc b/chromium/ui/base/x/x11_util.cc index 7a10483eaec..544d5fdbfec 100644 --- a/chromium/ui/base/x/x11_util.cc +++ b/chromium/ui/base/x/x11_util.cc @@ -1299,6 +1299,14 @@ gfx::ICCProfile GetICCProfileForMonitor(int monitor) { return icc_profile; } +bool IsSyncExtensionAvailable() { + auto* display = gfx::GetXDisplay(); + int unused; + static bool result = XSyncQueryExtension(display, &unused, &unused) && + XSyncInitialize(display, &unused, &unused); + return result; +} + XRefcountedMemory::XRefcountedMemory(unsigned char* x11_data, size_t length) : x11_data_(length ? x11_data : nullptr), length_(length) { } diff --git a/chromium/ui/base/x/x11_util.h b/chromium/ui/base/x/x11_util.h index ee367584b40..daf755e4403 100644 --- a/chromium/ui/base/x/x11_util.h +++ b/chromium/ui/base/x/x11_util.h @@ -301,6 +301,9 @@ UI_BASE_X_EXPORT bool WmSupportsHint(XAtom atom); // Returns the ICCProfile corresponding to |monitor| using XGetWindowProperty. UI_BASE_X_EXPORT gfx::ICCProfile GetICCProfileForMonitor(int monitor); +// Return true if the display supports SYNC extension. +UI_BASE_X_EXPORT bool IsSyncExtensionAvailable(); + // Manages a piece of X11 allocated memory as a RefCountedMemory segment. This // object takes ownership over the passed in memory and will free it with the // X11 allocator when done. |