diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-12-10 16:19:40 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-12-10 16:01:50 +0000 |
commit | 51f6c2793adab2d864b3d2b360000ef8db1d3e92 (patch) | |
tree | 835b3b4446b012c75e80177cef9fbe6972cc7dbe /chromium/ui | |
parent | 6036726eb981b6c4b42047513b9d3f4ac865daac (diff) | |
download | qtwebengine-chromium-51f6c2793adab2d864b3d2b360000ef8db1d3e92.tar.gz |
BASELINE: Update Chromium to 71.0.3578.93
Change-Id: I6a32086c33670e1b033f8b10e6bf1fd4da1d105d
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/ui')
1198 files changed, 32736 insertions, 18455 deletions
diff --git a/chromium/ui/accelerated_widget_mac/display_link_mac.cc b/chromium/ui/accelerated_widget_mac/display_link_mac.cc index a20981063ac..81de0b1942a 100644 --- a/chromium/ui/accelerated_widget_mac/display_link_mac.cc +++ b/chromium/ui/accelerated_widget_mac/display_link_mac.cc @@ -31,6 +31,36 @@ struct ScopedTypeRefTraits<CVDisplayLinkRef> { namespace ui { +using DisplayLinkMap = std::map<CGDirectDisplayID, DisplayLinkMac*>; + +namespace { + +// The task runner to post tasks to from the display link thread. Note that this +// is initialized with the very first DisplayLinkMac instance, and is never +// changed (even, e.g, in tests that re-initialize the main thread task runner). +// https://885329 +scoped_refptr<base::SingleThreadTaskRunner> GetMainThreadTaskRunner() { + static scoped_refptr<base::SingleThreadTaskRunner> task_runner = + base::ThreadTaskRunnerHandle::Get(); + return task_runner; +} + +// Each display link instance consumes a non-negligible number of cycles, so +// make all display links on the same screen share the same object. +// +// Note that this is a weak map, holding non-owning pointers to the +// DisplayLinkMac objects. DisplayLinkMac is a ref-counted class, and is +// jointly owned by the various callers that got a copy by calling +// GetForDisplay(). +// +// ** This map may only be accessed from the main thread. ** +DisplayLinkMap& GetAllDisplayLinks() { + static base::NoDestructor<DisplayLinkMap> all_display_links; + return *all_display_links; +} + +} // namespace + // static scoped_refptr<DisplayLinkMac> DisplayLinkMac::GetForDisplay( CGDirectDisplayID display_id) { @@ -193,21 +223,6 @@ void DisplayLinkMac::StopDisplayLink() { } // static -DisplayLinkMac::DisplayLinkMap& DisplayLinkMac::GetAllDisplayLinks() { - DCHECK_EQ(base::ThreadTaskRunnerHandle::Get(), GetMainThreadTaskRunner()); - static base::NoDestructor<DisplayLinkMac::DisplayLinkMap> all_display_links; - return *all_display_links; -} - -// static -scoped_refptr<base::SingleThreadTaskRunner> -DisplayLinkMac::GetMainThreadTaskRunner() { - static scoped_refptr<base::SingleThreadTaskRunner> task_runner = - base::ThreadTaskRunnerHandle::Get(); - return task_runner; -} - -// static CVReturn DisplayLinkMac::DisplayLinkCallback( CVDisplayLinkRef display_link, const CVTimeStamp* now, diff --git a/chromium/ui/accelerated_widget_mac/display_link_mac.h b/chromium/ui/accelerated_widget_mac/display_link_mac.h index 2fce8971bed..b552163bff7 100644 --- a/chromium/ui/accelerated_widget_mac/display_link_mac.h +++ b/chromium/ui/accelerated_widget_mac/display_link_mac.h @@ -14,10 +14,6 @@ #include "base/time/time.h" #include "ui/accelerated_widget_mac/accelerated_widget_mac_export.h" -namespace base { -class SingleThreadTaskRunner; -} - namespace ui { class ACCELERATED_WIDGET_MAC_EXPORT DisplayLinkMac @@ -52,20 +48,6 @@ class ACCELERATED_WIDGET_MAC_EXPORT DisplayLinkMac // Processes the display link callback. void UpdateVSyncParameters(const CVTimeStamp& time); - // Each display link instance consumes a non-negligible number of cycles, so - // make all display links on the same screen share the same object. This map - // must only be changed from the main thread. - // - // Note that this is a weak map, holding non-owning pointers to the - // DisplayLinkMac objects. DisplayLinkMac is a ref-counted class, and is - // jointly owned by the various callers that got a copy by calling - // GetForDisplay(). - using DisplayLinkMap = std::map<CGDirectDisplayID, DisplayLinkMac*>; - static DisplayLinkMap& GetAllDisplayLinks(); - - // The task runner to post tasks to from the display link thread. - static scoped_refptr<base::SingleThreadTaskRunner> GetMainThreadTaskRunner(); - // Called by the system on the display link thread, and posts a call to // DoUpdateVSyncParameters() to the UI thread. static CVReturn DisplayLinkCallback( diff --git a/chromium/ui/accessibility/BUILD.gn b/chromium/ui/accessibility/BUILD.gn index b5471edc3ee..085e5d6da13 100644 --- a/chromium/ui/accessibility/BUILD.gn +++ b/chromium/ui/accessibility/BUILD.gn @@ -4,6 +4,7 @@ import("//build/config/linux/pkg_config.gni") import("//build/config/features.gni") +import("//build/config/jumbo.gni") import("//build/config/ui.gni") import("//mojo/public/tools/bindings/mojom.gni") import("//services/service_manager/public/service_manifest.gni") @@ -28,7 +29,7 @@ mojom("ax_enums_mojo") { ] } -component("accessibility") { +jumbo_component("accessibility") { sources = [ "ax_action_data.cc", "ax_action_data.h", @@ -41,6 +42,8 @@ component("accessibility") { "ax_export.h", "ax_host_delegate.cc", "ax_host_delegate.h", + "ax_mode.h", + "ax_mode_observer.h", "ax_node.cc", "ax_node.h", "ax_node_data.cc", @@ -65,6 +68,8 @@ component("accessibility") { "ax_tree_combiner.h", "ax_tree_data.cc", "ax_tree_data.h", + "ax_tree_id.cc", + "ax_tree_id.h", "ax_tree_id_registry.cc", "ax_tree_id_registry.h", "ax_tree_serializer.cc", @@ -75,6 +80,8 @@ component("accessibility") { "platform/ax_android_constants.h", "platform/ax_platform_node.cc", "platform/ax_platform_node.h", + "platform/ax_platform_node_test_helper.cc", + "platform/ax_platform_node_test_helper.h", "platform/ax_unique_id.cc", "platform/ax_unique_id.h", ] @@ -199,6 +206,7 @@ test("accessibility_unittests") { "mojom/ax_event_mojom_traits_unittest.cc", "mojom/ax_node_data_mojom_traits_unittest.cc", "mojom/ax_tree_data_mojom_traits_unittest.cc", + "mojom/ax_tree_id_mojom_traits_unittest.cc", "mojom/ax_tree_update_mojom_traits_unittest.cc", "platform/ax_platform_node_unittest.cc", "platform/ax_platform_node_unittest.h", diff --git a/chromium/ui/accessibility/PRESUBMIT.py b/chromium/ui/accessibility/PRESUBMIT.py index b0d403420f7..44659333f39 100644 --- a/chromium/ui/accessibility/PRESUBMIT.py +++ b/chromium/ui/accessibility/PRESUBMIT.py @@ -9,8 +9,8 @@ import os, re, json AX_MOJOM = 'ui/accessibility/ax_enums.mojom' AUTOMATION_IDL = 'chrome/common/extensions/api/automation.idl' -AX_JS_FILE = 'content/browser/resources/accessibility/accessibility.js' -AX_MODE_HEADER = 'ui/accessibility/ax_modes.h' +AX_JS_FILE = 'chrome/browser/resources/accessibility/accessibility.js' +AX_MODE_HEADER = 'ui/accessibility/ax_mode.h' def InitialLowerCamelCase(unix_name): words = unix_name.split('_') diff --git a/chromium/ui/accessibility/ax_action_data.h b/chromium/ui/accessibility/ax_action_data.h index bc3fc27c545..543c072caa6 100644 --- a/chromium/ui/accessibility/ax_action_data.h +++ b/chromium/ui/accessibility/ax_action_data.h @@ -7,6 +7,7 @@ #include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_export.h" +#include "ui/accessibility/ax_tree_id.h" #include "ui/gfx/geometry/rect.h" namespace ui { @@ -31,7 +32,7 @@ struct AX_EXPORT AXActionData { ax::mojom::Action action = ax::mojom::Action::kNone; // The ID of the tree that this action should be performed on. - int target_tree_id = -1; + ui::AXTreeID target_tree_id = ui::AXTreeIDUnknown(); // The source extension id (if any) of this action. std::string source_extension_id; diff --git a/chromium/ui/accessibility/ax_enum_util.cc b/chromium/ui/accessibility/ax_enum_util.cc index 2a38f68c589..654deaf19b9 100644 --- a/chromium/ui/accessibility/ax_enum_util.cc +++ b/chromium/ui/accessibility/ax_enum_util.cc @@ -54,6 +54,8 @@ const char* ToString(ax::mojom::Event event) { return "liveRegionChanged"; case ax::mojom::Event::kLoadComplete: return "loadComplete"; + case ax::mojom::Event::kLoadStart: + return "loadStart"; case ax::mojom::Event::kLocationChanged: return "locationChanged"; case ax::mojom::Event::kMediaStartedPlaying: @@ -162,6 +164,8 @@ ax::mojom::Event ParseEvent(const char* event) { return ax::mojom::Event::kLiveRegionChanged; if (0 == strcmp(event, "loadComplete")) return ax::mojom::Event::kLoadComplete; + if (0 == strcmp(event, "loadStart")) + return ax::mojom::Event::kLoadStart; if (0 == strcmp(event, "locationChanged")) return ax::mojom::Event::kLocationChanged; if (0 == strcmp(event, "mediaStartedPlaying")) @@ -1162,8 +1166,8 @@ const char* ToString(ax::mojom::StringAttribute string_attribute) { return "ariaInvalidValue"; case ax::mojom::StringAttribute::kAutoComplete: return "autoComplete"; - case ax::mojom::StringAttribute::kChromeChannel: - return "chromeChannel"; + case ax::mojom::StringAttribute::kChildTreeId: + return "childTreeId"; case ax::mojom::StringAttribute::kClassName: return "className"; case ax::mojom::StringAttribute::kContainerLiveRelevant: @@ -1216,8 +1220,8 @@ ax::mojom::StringAttribute ParseStringAttribute(const char* string_attribute) { return ax::mojom::StringAttribute::kAriaInvalidValue; if (0 == strcmp(string_attribute, "autoComplete")) return ax::mojom::StringAttribute::kAutoComplete; - if (0 == strcmp(string_attribute, "chromeChannel")) - return ax::mojom::StringAttribute::kChromeChannel; + if (0 == strcmp(string_attribute, "childTreeId")) + return ax::mojom::StringAttribute::kChildTreeId; if (0 == strcmp(string_attribute, "className")) return ax::mojom::StringAttribute::kClassName; if (0 == strcmp(string_attribute, "containerLiveRelevant")) @@ -1333,8 +1337,6 @@ const char* ToString(ax::mojom::IntAttribute int_attribute) { return "nextOnLineId"; case ax::mojom::IntAttribute::kPreviousOnLineId: return "previousOnLineId"; - case ax::mojom::IntAttribute::kChildTreeId: - return "childTreeId"; case ax::mojom::IntAttribute::kRestriction: return "restriction"; case ax::mojom::IntAttribute::kSetSize: @@ -1443,8 +1445,6 @@ ax::mojom::IntAttribute ParseIntAttribute(const char* int_attribute) { return ax::mojom::IntAttribute::kNextOnLineId; if (0 == strcmp(int_attribute, "previousOnLineId")) return ax::mojom::IntAttribute::kPreviousOnLineId; - if (0 == strcmp(int_attribute, "childTreeId")) - return ax::mojom::IntAttribute::kChildTreeId; if (0 == strcmp(int_attribute, "restriction")) return ax::mojom::IntAttribute::kRestriction; if (0 == strcmp(int_attribute, "setSize")) @@ -1592,8 +1592,6 @@ const char* ToString(ax::mojom::IntListAttribute int_list_attribute) { return "labelledbyIds"; case ax::mojom::IntListAttribute::kRadioGroupIds: return "radioGroupIds"; - case ax::mojom::IntListAttribute::kLineBreaks: - return "lineBreaks"; case ax::mojom::IntListAttribute::kMarkerTypes: return "markerTypes"; case ax::mojom::IntListAttribute::kMarkerStarts: @@ -1631,8 +1629,6 @@ ax::mojom::IntListAttribute ParseIntListAttribute( return ax::mojom::IntListAttribute::kLabelledbyIds; if (0 == strcmp(int_list_attribute, "radioGroupIds")) return ax::mojom::IntListAttribute::kRadioGroupIds; - if (0 == strcmp(int_list_attribute, "lineBreaks")) - return ax::mojom::IntListAttribute::kLineBreaks; if (0 == strcmp(int_list_attribute, "markerTypes")) return ax::mojom::IntListAttribute::kMarkerTypes; if (0 == strcmp(int_list_attribute, "markerStarts")) @@ -2155,12 +2151,16 @@ const char* ToString(ax::mojom::NameFrom name_from) { return "attribute"; case ax::mojom::NameFrom::kAttributeExplicitlyEmpty: return "attributeExplicitlyEmpty"; + case ax::mojom::NameFrom::kCaption: + return "caption"; case ax::mojom::NameFrom::kContents: return "contents"; case ax::mojom::NameFrom::kPlaceholder: return "placeholder"; case ax::mojom::NameFrom::kRelatedElement: return "relatedElement"; + case ax::mojom::NameFrom::kTitle: + return "title"; case ax::mojom::NameFrom::kValue: return "value"; } @@ -2177,12 +2177,16 @@ ax::mojom::NameFrom ParseNameFrom(const char* name_from) { return ax::mojom::NameFrom::kAttribute; if (0 == strcmp(name_from, "attributeExplicitlyEmpty")) return ax::mojom::NameFrom::kAttributeExplicitlyEmpty; + if (0 == strcmp(name_from, "caption")) + return ax::mojom::NameFrom::kCaption; if (0 == strcmp(name_from, "contents")) return ax::mojom::NameFrom::kContents; if (0 == strcmp(name_from, "placeholder")) return ax::mojom::NameFrom::kPlaceholder; if (0 == strcmp(name_from, "relatedElement")) return ax::mojom::NameFrom::kRelatedElement; + if (0 == strcmp(name_from, "title")) + return ax::mojom::NameFrom::kTitle; if (0 == strcmp(name_from, "value")) return ax::mojom::NameFrom::kValue; return ax::mojom::NameFrom::kNone; diff --git a/chromium/ui/accessibility/ax_enums.mojom b/chromium/ui/accessibility/ax_enums.mojom index 7469bfdfd21..2c35cf14722 100644 --- a/chromium/ui/accessibility/ax_enums.mojom +++ b/chromium/ui/accessibility/ax_enums.mojom @@ -49,6 +49,7 @@ enum Event { kLiveRegionCreated, // Implicit kLiveRegionChanged, // Web kLoadComplete, // Web + kLoadStart, // Web / AuraLinux kLocationChanged, // Web kMediaStartedPlaying, // Native / Automation kMediaStoppedPlaying, // Native / Automation @@ -426,7 +427,7 @@ enum StringAttribute { // Only used when invalid_state == invalid_state_other. kAriaInvalidValue, kAutoComplete, - kChromeChannel, // Native / Automation + kChildTreeId, kClassName, // Native / Android kContainerLiveRelevant, kContainerLiveStatus, @@ -511,9 +512,6 @@ enum IntAttribute { kNextOnLineId, kPreviousOnLineId, - // Identifies a child tree which this node hosts. - kChildTreeId, - // Input restriction, if any, such as readonly or disabled. // Of type AXRestriction, see below. // No value or enabled control or other object that is not disabled. @@ -632,11 +630,6 @@ enum IntListAttribute { kLabelledbyIds, kRadioGroupIds, - // For static text. Character indices where line breaks occur. Note that - // this attribute is only available on Chrome OS and will be deprecated - // soon. - kLineBreaks, - // For static text. These int lists must be the same size; they represent // the start and end character offset of each marker. Examples of markers // include spelling and grammar errors, and find-in-page matches. @@ -816,9 +809,11 @@ enum NameFrom { kUninitialized, kAttribute, kAttributeExplicitlyEmpty, + kCaption, kContents, kPlaceholder, kRelatedElement, + kTitle, kValue, }; diff --git a/chromium/ui/accessibility/ax_event_generator.cc b/chromium/ui/accessibility/ax_event_generator.cc index 25dd625a1fa..ed747ec39cc 100644 --- a/chromium/ui/accessibility/ax_event_generator.cc +++ b/chromium/ui/accessibility/ax_event_generator.cc @@ -304,8 +304,11 @@ void AXEventGenerator::OnTreeDataChanged(AXTree* tree, const ui::AXTreeData& new_tree_data) { DCHECK_EQ(tree_, tree); - if (new_tree_data.loaded && !old_tree_data.loaded) + if (new_tree_data.loaded && !old_tree_data.loaded && + ShouldFireLoadEvents(tree->root())) { AddEvent(tree->root(), Event::LOAD_COMPLETE); + } + if (new_tree_data.sel_anchor_object_id != old_tree_data.sel_anchor_object_id || new_tree_data.sel_anchor_offset != old_tree_data.sel_anchor_offset || @@ -355,8 +358,12 @@ void AXEventGenerator::OnAtomicUpdateFinished( const std::vector<Change>& changes) { DCHECK_EQ(tree_, tree); - if (root_changed && tree->data().loaded) - AddEvent(tree->root(), Event::LOAD_COMPLETE); + if (root_changed && ShouldFireLoadEvents(tree->root())) { + if (tree->data().loaded) + AddEvent(tree->root(), Event::LOAD_COMPLETE); + else + AddEvent(tree->root(), Event::LOAD_START); + } for (const auto& change : changes) { if ((change.type == NODE_CREATED || change.type == SUBTREE_CREATED)) { @@ -450,4 +457,11 @@ void AXEventGenerator::FireRelationSourceEvents(AXTree* tree, tree->intlist_reverse_relations().end(), callback); } +// Attempts to suppress load-related events that we presume no AT will be +// interested in under any circumstances, such as pages which have no size. +bool AXEventGenerator::ShouldFireLoadEvents(AXNode* node) { + const AXNodeData& data = node->data(); + return data.location.width() || data.location.height(); +} + } // namespace ui diff --git a/chromium/ui/accessibility/ax_event_generator.h b/chromium/ui/accessibility/ax_event_generator.h index cc89b5d7e22..4e84982bcf9 100644 --- a/chromium/ui/accessibility/ax_event_generator.h +++ b/chromium/ui/accessibility/ax_event_generator.h @@ -35,6 +35,7 @@ class AX_EXPORT AXEventGenerator : public AXTreeDelegate { LIVE_REGION_CREATED, LIVE_REGION_NODE_CHANGED, // Fired on a node within a live region. LOAD_COMPLETE, + LOAD_START, MENU_ITEM_SELECTED, NAME_CHANGED, OTHER_ATTRIBUTE_CHANGED, @@ -186,6 +187,7 @@ class AX_EXPORT AXEventGenerator : public AXTreeDelegate { void FireLiveRegionEvents(AXNode* node); void FireActiveDescendantEvents(); void FireRelationSourceEvents(AXTree* tree, AXNode* target_node); + bool ShouldFireLoadEvents(AXNode* node); AXTree* tree_ = nullptr; // Not owned. std::map<AXNode*, std::set<EventParams>> tree_events_; diff --git a/chromium/ui/accessibility/ax_event_generator_unittest.cc b/chromium/ui/accessibility/ax_event_generator_unittest.cc index 7ed3e0a6a41..99db15e9015 100644 --- a/chromium/ui/accessibility/ax_event_generator_unittest.cc +++ b/chromium/ui/accessibility/ax_event_generator_unittest.cc @@ -62,6 +62,9 @@ std::string DumpEvents(AXEventGenerator* generator) { case AXEventGenerator::Event::LOAD_COMPLETE: event_name = "LOAD_COMPLETE"; break; + case AXEventGenerator::Event::LOAD_START: + event_name = "LOAD_START"; + break; case AXEventGenerator::Event::MENU_ITEM_SELECTED: event_name = "MENU_ITEM_SELECTED"; break; @@ -118,6 +121,7 @@ TEST(AXEventGeneratorTest, LoadCompleteSameTree) { initial_state.root_id = 1; initial_state.nodes.resize(1); initial_state.nodes[0].id = 1; + initial_state.nodes[0].location = gfx::RectF(0, 0, 800, 600); initial_state.has_tree_data = true; AXTree tree(initial_state); @@ -142,10 +146,55 @@ TEST(AXEventGeneratorTest, LoadCompleteNewTree) { load_complete_update.root_id = 2; load_complete_update.nodes.resize(1); load_complete_update.nodes[0].id = 2; + load_complete_update.nodes[0].location = gfx::RectF(0, 0, 800, 600); load_complete_update.has_tree_data = true; load_complete_update.tree_data.loaded = true; EXPECT_TRUE(tree.Unserialize(load_complete_update)); EXPECT_EQ("LOAD_COMPLETE on 2", DumpEvents(&event_generator)); + + // Load complete should not be emitted for sizeless roots. + load_complete_update.root_id = 3; + load_complete_update.nodes.resize(1); + load_complete_update.nodes[0].id = 3; + load_complete_update.nodes[0].location = gfx::RectF(0, 0, 0, 0); + load_complete_update.has_tree_data = true; + load_complete_update.tree_data.loaded = true; + EXPECT_TRUE(tree.Unserialize(load_complete_update)); + EXPECT_EQ("", DumpEvents(&event_generator)); + + // TODO(accessibility): http://crbug.com/888758 + // Load complete should not be emitted for chrome-search URLs. + load_complete_update.root_id = 4; + load_complete_update.nodes.resize(1); + load_complete_update.nodes[0].id = 4; + load_complete_update.nodes[0].location = gfx::RectF(0, 0, 800, 600); + load_complete_update.nodes[0].AddStringAttribute( + ax::mojom::StringAttribute::kUrl, "chrome-search://foo"); + load_complete_update.has_tree_data = true; + load_complete_update.tree_data.loaded = true; + EXPECT_TRUE(tree.Unserialize(load_complete_update)); + EXPECT_EQ("LOAD_COMPLETE on 4", DumpEvents(&event_generator)); +} + +TEST(AXEventGeneratorTest, LoadStart) { + AXTreeUpdate initial_state; + initial_state.root_id = 1; + initial_state.nodes.resize(1); + initial_state.nodes[0].id = 1; + initial_state.nodes[0].location = gfx::RectF(0, 0, 800, 600); + initial_state.has_tree_data = true; + AXTree tree(initial_state); + + AXEventGenerator event_generator(&tree); + AXTreeUpdate load_start_update; + load_start_update.root_id = 2; + load_start_update.nodes.resize(1); + load_start_update.nodes[0].id = 2; + load_start_update.nodes[0].location = gfx::RectF(0, 0, 800, 600); + load_start_update.has_tree_data = true; + load_start_update.tree_data.loaded = false; + EXPECT_TRUE(tree.Unserialize(load_start_update)); + EXPECT_EQ("LOAD_START on 2", DumpEvents(&event_generator)); } TEST(AXEventGeneratorTest, DocumentSelectionChanged) { diff --git a/chromium/ui/accessibility/ax_host_delegate.cc b/chromium/ui/accessibility/ax_host_delegate.cc index a7ecd1b0cda..9e7104d488e 100644 --- a/chromium/ui/accessibility/ax_host_delegate.cc +++ b/chromium/ui/accessibility/ax_host_delegate.cc @@ -11,7 +11,7 @@ namespace ui { AXHostDelegate::AXHostDelegate() : tree_id_(AXTreeIDRegistry::GetInstance()->GetOrCreateAXTreeID(this)) {} -AXHostDelegate::AXHostDelegate(int32_t tree_id) : tree_id_(tree_id) { +AXHostDelegate::AXHostDelegate(AXTreeID tree_id) : tree_id_(tree_id) { AXTreeIDRegistry::GetInstance()->SetDelegateForID(this, tree_id); } diff --git a/chromium/ui/accessibility/ax_host_delegate.h b/chromium/ui/accessibility/ax_host_delegate.h index 785df2594c5..4d4960914cf 100644 --- a/chromium/ui/accessibility/ax_host_delegate.h +++ b/chromium/ui/accessibility/ax_host_delegate.h @@ -5,9 +5,8 @@ #ifndef UI_ACCESSIBILITY_AX_HOST_DELEGATE_H_ #define UI_ACCESSIBILITY_AX_HOST_DELEGATE_H_ -#include <stdint.h> - #include "ui/accessibility/ax_export.h" +#include "ui/accessibility/ax_tree_id.h" namespace ui { @@ -26,23 +25,23 @@ class AX_EXPORT AXHostDelegate { // Handle an action from an accessibility client. virtual void PerformAction(const ui::AXActionData& data) = 0; + // A tree id appropriate for annotating events sent to an accessibility + // client. + AXTreeID tree_id() const { return tree_id_; } + protected: // A delegate with an automatically assigned tree id. AXHostDelegate(); // A delegate with an explicit tree id. The caller is responsible for ensuring // the uniqueness of the id. - explicit AXHostDelegate(int32_t tree_id); - - // A tree id appropriate for annotating events sent to an accessibility - // client. - int32_t tree_id() const { return tree_id_; } + explicit AXHostDelegate(AXTreeID tree_id); private: // Register or unregister this class with |AXTreeIDRegistry|. void UpdateActiveState(bool active); - int32_t tree_id_; + AXTreeID tree_id_; }; } // namespace ui diff --git a/chromium/ui/accessibility/ax_modes.h b/chromium/ui/accessibility/ax_mode.h index b6015763e1e..ac52a5ecc5c 100644 --- a/chromium/ui/accessibility/ax_modes.h +++ b/chromium/ui/accessibility/ax_mode.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_ACCESSIBILITY_PLATFORM_AX_MODE_H_ -#define UI_ACCESSIBILITY_PLATFORM_AX_MODE_H_ +#ifndef UI_ACCESSIBILITY_AX_MODE_H_ +#define UI_ACCESSIBILITY_AX_MODE_H_ namespace ui { @@ -89,4 +89,4 @@ static constexpr AXMode kAXModeComplete(AXMode::kNativeAPIs | } // namespace ui -#endif // UI_ACCESSIBILITY_PLATFORM_AX_MODE_H_ +#endif // UI_ACCESSIBILITY_AX_MODE_H_ diff --git a/chromium/ui/accessibility/ax_mode_observer.h b/chromium/ui/accessibility/ax_mode_observer.h index f517c36d7bb..1f2f2afa18a 100644 --- a/chromium/ui/accessibility/ax_mode_observer.h +++ b/chromium/ui/accessibility/ax_mode_observer.h @@ -6,7 +6,7 @@ #define UI_ACCESSIBILITY_PLATFORM_AX_MODE_OBSERVER_H_ #include "ui/accessibility/ax_export.h" -#include "ui/accessibility/ax_modes.h" +#include "ui/accessibility/ax_mode.h" namespace ui { diff --git a/chromium/ui/accessibility/ax_node_data.cc b/chromium/ui/accessibility/ax_node_data.cc index 159933ca13d..9f72d3026df 100644 --- a/chromium/ui/accessibility/ax_node_data.cc +++ b/chromium/ui/accessibility/ax_node_data.cc @@ -9,6 +9,7 @@ #include <algorithm> #include <set> +#include "base/no_destructor.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" @@ -132,7 +133,6 @@ bool IsNodeIdIntAttribute(ax::mojom::IntAttribute attr) { case ax::mojom::IntAttribute::kHierarchicalLevel: case ax::mojom::IntAttribute::kNameFrom: case ax::mojom::IntAttribute::kDescriptionFrom: - case ax::mojom::IntAttribute::kChildTreeId: case ax::mojom::IntAttribute::kSetSize: case ax::mojom::IntAttribute::kPosInSet: case ax::mojom::IntAttribute::kColorValue: @@ -174,7 +174,6 @@ bool IsNodeIdIntListAttribute(ax::mojom::IntListAttribute attr) { // add a new attribute without explicitly considering whether it's // a node id attribute or not. case ax::mojom::IntListAttribute::kNone: - case ax::mojom::IntListAttribute::kLineBreaks: case ax::mojom::IntListAttribute::kMarkerTypes: case ax::mojom::IntListAttribute::kMarkerStarts: case ax::mojom::IntListAttribute::kMarkerEnds: @@ -311,9 +310,9 @@ bool AXNodeData::HasStringAttribute( const std::string& AXNodeData::GetStringAttribute( ax::mojom::StringAttribute attribute) const { - CR_DEFINE_STATIC_LOCAL(std::string, empty_string, ()); + static base::NoDestructor<std::string> empty_string; auto iter = FindInVectorOfPairs(attribute, string_attributes); - return iter != string_attributes.end() ? iter->second : empty_string; + return iter != string_attributes.end() ? iter->second : *empty_string; } bool AXNodeData::GetStringAttribute(ax::mojom::StringAttribute attribute, @@ -352,11 +351,11 @@ bool AXNodeData::HasIntListAttribute( const std::vector<int32_t>& AXNodeData::GetIntListAttribute( ax::mojom::IntListAttribute attribute) const { - CR_DEFINE_STATIC_LOCAL(std::vector<int32_t>, empty_vector, ()); + static base::NoDestructor<std::vector<int32_t>> empty_vector; auto iter = FindInVectorOfPairs(attribute, intlist_attributes); if (iter != intlist_attributes.end()) return iter->second; - return empty_vector; + return *empty_vector; } bool AXNodeData::GetIntListAttribute(ax::mojom::IntListAttribute attribute, @@ -378,11 +377,11 @@ bool AXNodeData::HasStringListAttribute( const std::vector<std::string>& AXNodeData::GetStringListAttribute( ax::mojom::StringListAttribute attribute) const { - CR_DEFINE_STATIC_LOCAL(std::vector<std::string>, empty_vector, ()); + static base::NoDestructor<std::vector<std::string>> empty_vector; auto iter = FindInVectorOfPairs(attribute, stringlist_attributes); if (iter != stringlist_attributes.end()) return iter->second; - return empty_vector; + return *empty_vector; } bool AXNodeData::GetStringListAttribute( @@ -567,6 +566,11 @@ void AXNodeData::AddAction(ax::mojom::Action action_enum) { actions = ModifyFlag(actions, static_cast<uint32_t>(action_enum), true); } +void AXNodeData::RemoveState(ax::mojom::State state_enum) { + DCHECK_NE(state_enum, ax::mojom::State::kNone); + state = ModifyFlag(state, static_cast<uint32_t>(state_enum), false); +} + std::string AXNodeData::ToString() const { std::string result; @@ -718,9 +722,6 @@ std::string AXNodeData::ToString() const { case ax::mojom::IntAttribute::kPreviousOnLineId: result += " previous_on_line_id=" + value; break; - case ax::mojom::IntAttribute::kChildTreeId: - result += " child_tree_id=" + value; - break; case ax::mojom::IntAttribute::kColorValue: result += base::StringPrintf(" color_value=&%X", int_attribute.second); break; @@ -915,8 +916,8 @@ std::string AXNodeData::ToString() const { case ax::mojom::StringAttribute::kAutoComplete: result += " autocomplete=" + value; break; - case ax::mojom::StringAttribute::kChromeChannel: - result += " chrome_channel=" + value; + case ax::mojom::StringAttribute::kChildTreeId: + result += " child_tree_id=" + value; break; case ax::mojom::StringAttribute::kClassName: result += " class_name=" + value; @@ -1073,9 +1074,6 @@ std::string AXNodeData::ToString() const { case ax::mojom::IntListAttribute::kRadioGroupIds: result += " radio_group_ids=" + IntVectorToString(values); break; - case ax::mojom::IntListAttribute::kLineBreaks: - result += " line_breaks=" + IntVectorToString(values); - break; case ax::mojom::IntListAttribute::kMarkerTypes: { std::string types_str; for (size_t i = 0; i < values.size(); ++i) { diff --git a/chromium/ui/accessibility/ax_node_data.h b/chromium/ui/accessibility/ax_node_data.h index 468b13fc775..c7d3320e7aa 100644 --- a/chromium/ui/accessibility/ax_node_data.h +++ b/chromium/ui/accessibility/ax_node_data.h @@ -134,6 +134,9 @@ struct AX_EXPORT AXNodeData { void AddState(ax::mojom::State state_enum); void AddAction(ax::mojom::Action action_enum); + // Remove bits in the given enum's corresponding bitfield. + void RemoveState(ax::mojom::State state_enum); + // Helper functions to get some common int attributes with some specific // enum types: ax::mojom::CheckedState GetCheckedState() const { diff --git a/chromium/ui/accessibility/ax_node_position.cc b/chromium/ui/accessibility/ax_node_position.cc index 9711f344677..3e522353332 100644 --- a/chromium/ui/accessibility/ax_node_position.cc +++ b/chromium/ui/accessibility/ax_node_position.cc @@ -33,13 +33,13 @@ base::string16 AXNodePosition::GetInnerText() const { } void AXNodePosition::AnchorChild(int child_index, - int* tree_id, + AXTreeID* tree_id, int32_t* child_id) const { DCHECK(tree_id); DCHECK(child_id); if (!GetAnchor() || child_index < 0 || child_index >= AnchorChildCount()) { - *tree_id = INVALID_TREE_ID; + *tree_id = AXTreeIDUnknown(); *child_id = INVALID_ANCHOR_ID; return; } @@ -58,12 +58,12 @@ int AXNodePosition::AnchorIndexInParent() const { return GetAnchor() ? GetAnchor()->index_in_parent() : INVALID_INDEX; } -void AXNodePosition::AnchorParent(int* tree_id, int32_t* parent_id) const { +void AXNodePosition::AnchorParent(AXTreeID* tree_id, int32_t* parent_id) const { DCHECK(tree_id); DCHECK(parent_id); if (!GetAnchor() || !GetAnchor()->parent()) { - *tree_id = INVALID_TREE_ID; + *tree_id = AXTreeIDUnknown(); *parent_id = INVALID_ANCHOR_ID; return; } @@ -73,7 +73,7 @@ void AXNodePosition::AnchorParent(int* tree_id, int32_t* parent_id) const { *parent_id = parent->id(); } -AXNode* AXNodePosition::GetNodeInTree(int tree_id, int32_t node_id) const { +AXNode* AXNodePosition::GetNodeInTree(AXTreeID tree_id, int32_t node_id) const { if (!tree_ || node_id == INVALID_ANCHOR_ID) return nullptr; return AXNodePosition::tree_->GetFromId(node_id); diff --git a/chromium/ui/accessibility/ax_node_position.h b/chromium/ui/accessibility/ax_node_position.h index ec8d7715b3e..e40fd12e6c3 100644 --- a/chromium/ui/accessibility/ax_node_position.h +++ b/chromium/ui/accessibility/ax_node_position.h @@ -32,12 +32,12 @@ class AX_EXPORT AXNodePosition : public AXPosition<AXNodePosition, AXNode> { protected: AXNodePosition(const AXNodePosition& other) = default; void AnchorChild(int child_index, - int* tree_id, + AXTreeID* tree_id, int32_t* child_id) const override; int AnchorChildCount() const override; int AnchorIndexInParent() const override; - void AnchorParent(int* tree_id, int32_t* parent_id) const override; - AXNode* GetNodeInTree(int tree_id, int32_t node_id) const override; + void AnchorParent(AXTreeID* tree_id, int32_t* parent_id) const override; + AXNode* GetNodeInTree(AXTreeID tree_id, int32_t node_id) const override; int MaxTextOffset() const override; bool IsInWhiteSpace() const override; std::vector<int32_t> GetWordStartOffsets() const override; diff --git a/chromium/ui/accessibility/ax_node_position_unittest.cc b/chromium/ui/accessibility/ax_node_position_unittest.cc index 86028fad895..55c0753aaa6 100644 --- a/chromium/ui/accessibility/ax_node_position_unittest.cc +++ b/chromium/ui/accessibility/ax_node_position_unittest.cc @@ -201,7 +201,7 @@ void AXPositionTest::SetUp() { initial_state.nodes.push_back(static_text2_); initial_state.nodes.push_back(inline_box2_); initial_state.has_tree_data = true; - initial_state.tree_data.tree_id = 0; + initial_state.tree_data.tree_id = AXTreeID::FromString("0"); initial_state.tree_data.title = "Dialog title"; AXSerializableTree src_tree(initial_state); diff --git a/chromium/ui/accessibility/ax_position.h b/chromium/ui/accessibility/ax_position.h index 6115ded53bb..0f3e6296a32 100644 --- a/chromium/ui/accessibility/ax_position.h +++ b/chromium/ui/accessibility/ax_position.h @@ -20,6 +20,7 @@ #include "base/strings/utf_string_conversions.h" #include "ui/accessibility/ax_enum_util.h" #include "ui/accessibility/ax_enums.mojom.h" +#include "ui/accessibility/ax_tree_id.h" namespace ui { @@ -63,7 +64,7 @@ bool operator!=(const AXPosition<AXPositionType, AXNodeType>& first, // distinguish between "before text" and "after text" positions. To do this, if // the child index is 0 and the anchor is a leaf node, then it's an "after text" // position. If the child index is |BEFORE_TEXT| and the anchor is a leaf node, -// then his is a "before text" position. +// then this is a "before text" position. // It doesn't make sense to have a "before text" position on a text position, // because it is identical to setting its offset to the first character. // @@ -80,13 +81,13 @@ bool operator!=(const AXPosition<AXPositionType, AXNodeType>& first, // // This class can be copied using the |Clone| method. It is designed to be // immutable. + template <class AXPositionType, class AXNodeType> class AXPosition { public: using AXPositionInstance = std::unique_ptr<AXPosition<AXPositionType, AXNodeType>>; - static const int INVALID_TREE_ID = -1; static const int32_t INVALID_ANCHOR_ID = -1; static const int BEFORE_TEXT = -1; static const int INVALID_INDEX = -2; @@ -94,13 +95,13 @@ class AXPosition { static AXPositionInstance CreateNullPosition() { AXPositionInstance new_position(new AXPositionType()); - new_position->Initialize(AXPositionKind::NULL_POSITION, INVALID_TREE_ID, + new_position->Initialize(AXPositionKind::NULL_POSITION, AXTreeIDUnknown(), INVALID_ANCHOR_ID, INVALID_INDEX, INVALID_OFFSET, ax::mojom::TextAffinity::kDownstream); return new_position; } - static AXPositionInstance CreateTreePosition(int tree_id, + static AXPositionInstance CreateTreePosition(AXTreeID tree_id, int32_t anchor_id, int child_index) { AXPositionInstance new_position(new AXPositionType()); @@ -111,7 +112,7 @@ class AXPosition { } static AXPositionInstance CreateTextPosition( - int tree_id, + AXTreeID tree_id, int32_t anchor_id, int text_offset, ax::mojom::TextAffinity affinity) { @@ -139,9 +140,9 @@ class AXPosition { } else { str_child_index = base::IntToString(child_index_); } - str = "TreePosition tree_id=" + base::IntToString(tree_id_) + - " anchor_id=" + base::IntToString(anchor_id_) + " child_index=" + - str_child_index; + str = "TreePosition tree_id=" + tree_id_.ToString() + + " anchor_id=" + base::IntToString(anchor_id_) + + " child_index=" + str_child_index; break; } case AXPositionKind::TEXT_POSITION: { @@ -151,7 +152,7 @@ class AXPosition { } else { str_text_offset = base::IntToString(text_offset_); } - str = "TextPosition tree_id=" + base::IntToString(tree_id_) + + str = "TextPosition tree_id=" + tree_id_.ToString() + " anchor_id=" + base::IntToString(anchor_id_) + " text_offset=" + str_text_offset + " affinity=" + ui::ToString(static_cast<ax::mojom::TextAffinity>(affinity_)); @@ -176,13 +177,12 @@ class AXPosition { return str + " annotated_text=" + annotated_text; } - int tree_id() const { return tree_id_; } + AXTreeID tree_id() const { return tree_id_; } int32_t anchor_id() const { return anchor_id_; } AXNodeType* GetAnchor() const { - if (tree_id_ == INVALID_TREE_ID || anchor_id_ == INVALID_ANCHOR_ID) + if (tree_id_ == AXTreeIDUnknown() || anchor_id_ == INVALID_ANCHOR_ID) return nullptr; - DCHECK_GE(tree_id_, 0); DCHECK_GE(anchor_id_, 0); return GetNodeInTree(tree_id_, anchor_id_); } @@ -513,10 +513,10 @@ class AXPosition { if (child_index < 0 || child_index >= AnchorChildCount()) return CreateNullPosition(); - int tree_id = INVALID_TREE_ID; + AXTreeID tree_id = AXTreeIDUnknown(); int32_t child_id = INVALID_ANCHOR_ID; AnchorChild(child_index, &tree_id, &child_id); - DCHECK_NE(tree_id, INVALID_TREE_ID); + DCHECK_NE(tree_id, AXTreeIDUnknown()); DCHECK_NE(child_id, INVALID_ANCHOR_ID); switch (kind_) { case AXPositionKind::NULL_POSITION: @@ -543,10 +543,10 @@ class AXPosition { if (IsNullPosition()) return CreateNullPosition(); - int tree_id = INVALID_TREE_ID; + AXTreeID tree_id = AXTreeIDUnknown(); int32_t parent_id = INVALID_ANCHOR_ID; AnchorParent(&tree_id, &parent_id); - if (tree_id == INVALID_TREE_ID || parent_id == INVALID_ANCHOR_ID) + if (tree_id == AXTreeIDUnknown() || parent_id == INVALID_ANCHOR_ID) return CreateNullPosition(); switch (kind_) { @@ -1153,7 +1153,7 @@ class AXPosition { const AXPosition<AXPositionType, AXNodeType>& other) = default; virtual void Initialize(AXPositionKind kind, - int tree_id, + AXTreeID tree_id, int32_t anchor_id, int child_index, int text_offset, @@ -1173,7 +1173,7 @@ class AXPosition { (text_offset_ < 0 || text_offset_ > MaxTextOffset()))) { // Reset to the null position. kind_ = AXPositionKind::NULL_POSITION; - tree_id_ = INVALID_TREE_ID; + tree_id_ = AXTreeIDUnknown(); anchor_id_ = INVALID_ANCHOR_ID; child_index_ = INVALID_INDEX; text_offset_ = INVALID_OFFSET; @@ -1267,12 +1267,13 @@ class AXPosition { // Abstract methods. virtual void AnchorChild(int child_index, - int* tree_id, + AXTreeID* tree_id, int32_t* child_id) const = 0; virtual int AnchorChildCount() const = 0; virtual int AnchorIndexInParent() const = 0; - virtual void AnchorParent(int* tree_id, int32_t* parent_id) const = 0; - virtual AXNodeType* GetNodeInTree(int tree_id, int32_t node_id) const = 0; + virtual void AnchorParent(AXTreeID* tree_id, int32_t* parent_id) const = 0; + virtual AXNodeType* GetNodeInTree(AXTreeID tree_id, + int32_t node_id) const = 0; // Returns the length of text that this anchor node takes up in its parent. // On some platforms, embedded objects are represented in their parent with a // single embedded object character. @@ -1285,7 +1286,7 @@ class AXPosition { private: AXPositionKind kind_; - int tree_id_; + AXTreeID tree_id_; int32_t anchor_id_; // For text positions, |child_index_| is initially set to |-1| and only @@ -1299,8 +1300,6 @@ class AXPosition { }; template <class AXPositionType, class AXNodeType> -const int AXPosition<AXPositionType, AXNodeType>::INVALID_TREE_ID; -template <class AXPositionType, class AXNodeType> const int32_t AXPosition<AXPositionType, AXNodeType>::INVALID_ANCHOR_ID; template <class AXPositionType, class AXNodeType> const int AXPosition<AXPositionType, AXNodeType>::BEFORE_TEXT; diff --git a/chromium/ui/accessibility/ax_role_properties.cc b/chromium/ui/accessibility/ax_role_properties.cc index 4641a52c25c..487ba26d71a 100644 --- a/chromium/ui/accessibility/ax_role_properties.cc +++ b/chromium/ui/accessibility/ax_role_properties.cc @@ -8,7 +8,7 @@ namespace ui { namespace { -#if defined(OS_WIN) +#if defined(OS_WIN) || defined(OS_CHROMEOS) static bool kExposeLayoutTableAsDataTable = true; #else static bool kExposeLayoutTableAsDataTable = false; @@ -120,6 +120,27 @@ bool IsTableLikeRole(ax::mojom::Role role) { } } +bool IsTableHeaderRole(ax::mojom::Role role) { + switch (role) { + case ax::mojom::Role::kColumnHeader: + case ax::mojom::Role::kRowHeader: + return true; + default: + return false; + } +} + +bool IsTableRowRole(ax::mojom::Role role) { + switch (role) { + case ax::mojom::Role::kRow: + return true; + case ax::mojom::Role::kLayoutTableRow: + return kExposeLayoutTableAsDataTable; + default: + return false; + } +} + bool IsContainerWithSelectableChildrenRole(ax::mojom::Role role) { switch (role) { case ax::mojom::Role::kComboBoxGrouping: @@ -214,6 +235,17 @@ bool IsMenuRelated(ax::mojom::Role role) { } } +bool IsMenuItem(ax::mojom::Role role) { + switch (role) { + case ax::mojom::Role::kMenuItem: + case ax::mojom::Role::kMenuItemCheckBox: + case ax::mojom::Role::kMenuItemRadio: + return true; + default: + return false; + } +} + bool IsImage(ax::mojom::Role role) { switch (role) { case ax::mojom::Role::kCanvas: diff --git a/chromium/ui/accessibility/ax_role_properties.h b/chromium/ui/accessibility/ax_role_properties.h index 3b6776ada78..bfb756f9800 100644 --- a/chromium/ui/accessibility/ax_role_properties.h +++ b/chromium/ui/accessibility/ax_role_properties.h @@ -32,6 +32,12 @@ AX_EXPORT bool IsCellOrTableHeaderRole(ax::mojom::Role role); // Returns true if this node is a table, a grid or a treegrid. AX_EXPORT bool IsTableLikeRole(ax::mojom::Role role); +// Returns true if this node is a table header. +AX_EXPORT bool IsTableHeaderRole(ax::mojom::Role role); + +// Returns true if this node is a row. +AX_EXPORT bool IsTableRowRole(ax::mojom::Role role); + // Returns true if the provided role is selectable from the standpoint of UI // automation. AX_EXPORT bool IsUIASelectable(ax::mojom::Role role); @@ -48,6 +54,10 @@ AX_EXPORT bool IsControl(ax::mojom::Role role); // Returns true if this node is a menu or related role. AX_EXPORT bool IsMenuRelated(ax::mojom::Role role); +// Returns true if the role is a menu item, including menu item +// check box and menu item radio buttons. +AX_EXPORT bool IsMenuItem(ax::mojom::Role role); + // Returns true if it's an image, graphic, canvas, etc. AX_EXPORT bool IsImage(ax::mojom::Role role); diff --git a/chromium/ui/accessibility/ax_tree.cc b/chromium/ui/accessibility/ax_tree.cc index 7b7f71aece4..648a3f3c30d 100644 --- a/chromium/ui/accessibility/ax_tree.cc +++ b/chromium/ui/accessibility/ax_tree.cc @@ -324,7 +324,7 @@ std::set<int32_t> AXTree::GetReverseRelations(ax::mojom::IntListAttribute attr, } std::set<int32_t> AXTree::GetNodeIdsForChildTreeId( - int32_t child_tree_id) const { + AXTreeID child_tree_id) const { // Conceptually, this is the "const" version of: // return child_tree_id_reverse_map_[child_tree_id]; const auto& result = child_tree_id_reverse_map_.find(child_tree_id); @@ -349,16 +349,11 @@ bool AXTree::Unserialize(const AXTreeUpdate& update) { bool root_updated = false; if (update.node_id_to_clear != 0) { AXNode* node = GetFromId(update.node_id_to_clear); - if (!node) { - error_ = base::StringPrintf("Bad node_id_to_clear: %d", - update.node_id_to_clear); - return false; - } // Only destroy the root if the root was replaced and not if it's simply // updated. To figure out if the root was simply updated, we compare the ID // of the new root with the existing root ID. - if (node == root_) { + if (node && node == root_) { if (update.root_id != old_root_id) { // Clear root_ before calling DestroySubtree so that root_ doesn't ever // point to an invalid node. @@ -372,7 +367,7 @@ bool AXTree::Unserialize(const AXTreeUpdate& update) { // If the root has simply been updated, we treat it like an update to any // other node. - if (root_ && (node != root_ || root_updated)) { + if (node && root_ && (node != root_ || root_updated)) { for (int i = 0; i < node->child_count(); ++i) DestroySubtree(node->ChildAtIndex(i), &update_state); std::vector<AXNode*> children; @@ -679,22 +674,6 @@ void AXTree::UpdateReverseRelations(AXNode* node, const AXNodeData& new_data) { int id = new_data.id; auto int_callback = [this, node, id](ax::mojom::IntAttribute attr, const int& old_id, const int& new_id) { - if (attr == ax::mojom::IntAttribute::kChildTreeId) { - // Remove old_id -> id from the map, and clear map keys if - // their values are now empty. - if (child_tree_id_reverse_map_.find(old_id) != - child_tree_id_reverse_map_.end()) { - child_tree_id_reverse_map_[old_id].erase(id); - if (child_tree_id_reverse_map_[old_id].empty()) - child_tree_id_reverse_map_.erase(old_id); - } - - // Add new_id -> id to the map, unless new_id is zero indicating that - // we're only removing a relation. - if (new_id) - child_tree_id_reverse_map_[new_id].insert(id); - } - if (!IsNodeIdIntAttribute(attr)) return; @@ -736,6 +715,33 @@ void AXTree::UpdateReverseRelations(AXNode* node, const AXNodeData& new_data) { CallIfAttributeValuesChanged(old_data.intlist_attributes, new_data.intlist_attributes, std::vector<int32_t>(), intlist_callback); + + auto string_callback = [this, node, id](ax::mojom::StringAttribute attr, + const std::string& old_string, + const std::string& new_string) { + if (attr == ax::mojom::StringAttribute::kChildTreeId) { + // Remove old_string -> id from the map, and clear map keys if + // their values are now empty. + AXTreeID old_ax_tree_id = AXTreeID::FromString(old_string); + if (child_tree_id_reverse_map_.find(old_ax_tree_id) != + child_tree_id_reverse_map_.end()) { + child_tree_id_reverse_map_[old_ax_tree_id].erase(id); + if (child_tree_id_reverse_map_[old_ax_tree_id].empty()) + child_tree_id_reverse_map_.erase(old_ax_tree_id); + } + + // Add new_string -> id to the map, unless new_id is zero indicating that + // we're only removing a relation. + if (!new_string.empty()) { + AXTreeID new_ax_tree_id = AXTreeID::FromString(new_string); + child_tree_id_reverse_map_[new_ax_tree_id].insert(id); + } + } + }; + + CallIfAttributeValuesChanged(old_data.string_attributes, + new_data.string_attributes, std::string(), + string_callback); } void AXTree::DestroySubtree(AXNode* node, diff --git a/chromium/ui/accessibility/ax_tree.h b/chromium/ui/accessibility/ax_tree.h index 3d3d744f89f..993eb22faab 100644 --- a/chromium/ui/accessibility/ax_tree.h +++ b/chromium/ui/accessibility/ax_tree.h @@ -224,7 +224,7 @@ class AX_EXPORT AXTree { // Given a child tree ID, return the node IDs of all nodes in the tree who // have a kChildTreeId int attribute with that value. - std::set<int32_t> GetNodeIdsForChildTreeId(int32_t child_tree_id) const; + std::set<int32_t> GetNodeIdsForChildTreeId(AXTreeID child_tree_id) const; // Map from a relation attribute to a map from a target id to source ids. const IntReverseRelationMap& int_reverse_relations() { @@ -318,7 +318,7 @@ class AX_EXPORT AXTree { // a reverse mapping from target nodes to source nodes. IntListReverseRelationMap intlist_reverse_relations_; // Map from child tree ID to the set of node IDs that contain that attribute. - std::map<int32_t, std::set<int32_t>> child_tree_id_reverse_map_; + std::map<AXTreeID, std::set<int32_t>> child_tree_id_reverse_map_; // Map from node ID to cached table info, if the given node is a table. // Invalidated every time the tree is updated. diff --git a/chromium/ui/accessibility/ax_tree_combiner.cc b/chromium/ui/accessibility/ax_tree_combiner.cc index 06a6ac15726..b0ab36fa1f4 100644 --- a/chromium/ui/accessibility/ax_tree_combiner.cc +++ b/chromium/ui/accessibility/ax_tree_combiner.cc @@ -17,7 +17,7 @@ AXTreeCombiner::~AXTreeCombiner() { void AXTreeCombiner::AddTree(const AXTreeUpdate& tree, bool is_root) { trees_.push_back(tree); if (is_root) { - DCHECK_EQ(root_tree_id_, -1); + DCHECK_EQ(root_tree_id_, AXTreeIDUnknown()); root_tree_id_ = tree.tree_data.tree_id; } } @@ -25,7 +25,7 @@ void AXTreeCombiner::AddTree(const AXTreeUpdate& tree, bool is_root) { bool AXTreeCombiner::Combine() { // First create a map from tree ID to tree update. for (const auto& tree : trees_) { - int32_t tree_id = tree.tree_data.tree_id; + AXTreeID tree_id = tree.tree_data.tree_id; if (tree_id_map_.find(tree_id) != tree_id_map_.end()) return false; tree_id_map_[tree.tree_data.tree_id] = &tree; @@ -46,7 +46,7 @@ bool AXTreeCombiner::Combine() { // have focus and mapping IDs from the tree data appropriately. combined_.has_tree_data = true; combined_.tree_data = root->tree_data; - int32_t focused_tree_id = root->tree_data.focused_tree_id; + AXTreeID focused_tree_id = root->tree_data.focused_tree_id; const AXTreeUpdate* focused_tree = root; if (tree_id_map_.find(focused_tree_id) != tree_id_map_.end()) focused_tree = tree_id_map_[focused_tree_id]; @@ -69,7 +69,7 @@ bool AXTreeCombiner::Combine() { return true; } -int32_t AXTreeCombiner::MapId(int32_t tree_id, int32_t node_id) { +int32_t AXTreeCombiner::MapId(AXTreeID tree_id, int32_t node_id) { auto tree_id_node_id = std::make_pair(tree_id, node_id); if (tree_id_node_id_map_[tree_id_node_id] == 0) tree_id_node_id_map_[tree_id_node_id] = next_id_++; @@ -77,11 +77,11 @@ int32_t AXTreeCombiner::MapId(int32_t tree_id, int32_t node_id) { } void AXTreeCombiner::ProcessTree(const AXTreeUpdate* tree) { - int32_t tree_id = tree->tree_data.tree_id; + AXTreeID tree_id = tree->tree_data.tree_id; for (size_t i = 0; i < tree->nodes.size(); ++i) { AXNodeData node = tree->nodes[i]; - int32_t child_tree_id = - node.GetIntAttribute(ax::mojom::IntAttribute::kChildTreeId); + AXTreeID child_tree_id = AXTreeID::FromString( + node.GetStringAttribute(ax::mojom::StringAttribute::kChildTreeId)); // Map the node's ID. node.id = MapId(tree_id, node.id); @@ -94,16 +94,11 @@ void AXTreeCombiner::ProcessTree(const AXTreeUpdate* tree) { if (node.offset_container_id > 0) node.offset_container_id = MapId(tree_id, node.offset_container_id); - // Map other int attributes that refer to node IDs, and remove the - // ax::mojom::IntAttribute::kChildTreeId attribute. + // Map other int attributes that refer to node IDs. for (size_t j = 0; j < node.int_attributes.size(); ++j) { auto& attr = node.int_attributes[j]; if (IsNodeIdIntAttribute(attr.first)) attr.second = MapId(tree_id, attr.second); - if (attr.first == ax::mojom::IntAttribute::kChildTreeId) { - attr.first = ax::mojom::IntAttribute::kNone; - attr.second = 0; - } } // Map other int list attributes that refer to node IDs. @@ -115,6 +110,15 @@ void AXTreeCombiner::ProcessTree(const AXTreeUpdate* tree) { } } + // Remove the ax::mojom::StringAttribute::kChildTreeId attribute. + for (size_t j = 0; j < node.string_attributes.size(); ++j) { + auto& attr = node.string_attributes[j]; + if (attr.first == ax::mojom::StringAttribute::kChildTreeId) { + attr.first = ax::mojom::StringAttribute::kNone; + attr.second = ""; + } + } + // See if this node has a child tree. As a sanity check make sure the // child tree lists this tree as its parent tree id. const AXTreeUpdate* child_tree = nullptr; diff --git a/chromium/ui/accessibility/ax_tree_combiner.h b/chromium/ui/accessibility/ax_tree_combiner.h index ce8fbc792d5..4dc2b63f8af 100644 --- a/chromium/ui/accessibility/ax_tree_combiner.h +++ b/chromium/ui/accessibility/ax_tree_combiner.h @@ -8,6 +8,7 @@ #include <vector> #include "ui/accessibility/ax_export.h" +#include "ui/accessibility/ax_tree_id_registry.h" #include "ui/accessibility/ax_tree_update.h" namespace ui { @@ -33,15 +34,15 @@ class AX_EXPORT AXTreeCombiner { const AXTreeUpdate& combined() { return combined_; } private: - int32_t MapId(int32_t tree_id, int32_t node_id); + int32_t MapId(AXTreeID tree_id, int32_t node_id); void ProcessTree(const AXTreeUpdate* tree); std::vector<ui::AXTreeUpdate> trees_; - int32_t root_tree_id_ = -1; + AXTreeID root_tree_id_; int32_t next_id_ = 1; - std::map<int32_t, const AXTreeUpdate*> tree_id_map_; - std::map<std::pair<int32_t, int32_t>, int32_t> tree_id_node_id_map_; + std::map<AXTreeID, const AXTreeUpdate*> tree_id_map_; + std::map<std::pair<AXTreeID, int32_t>, int32_t> tree_id_node_id_map_; AXTreeUpdate combined_; }; diff --git a/chromium/ui/accessibility/ax_tree_combiner_unittest.cc b/chromium/ui/accessibility/ax_tree_combiner_unittest.cc index 43116ac87cd..5e8d1dc7825 100644 --- a/chromium/ui/accessibility/ax_tree_combiner_unittest.cc +++ b/chromium/ui/accessibility/ax_tree_combiner_unittest.cc @@ -10,7 +10,7 @@ namespace ui { TEST(CombineAXTreesTest, RenumberOneTree) { AXTreeUpdate tree; tree.has_tree_data = true; - tree.tree_data.tree_id = 1; + tree.tree_data.tree_id = ui::AXTreeID::FromString("1"); tree.root_id = 2; tree.nodes.resize(3); tree.nodes[0].id = 2; @@ -39,7 +39,7 @@ TEST(CombineAXTreesTest, EmbedChildTree) { AXTreeUpdate parent_tree; parent_tree.root_id = 1; parent_tree.has_tree_data = true; - parent_tree.tree_data.tree_id = 1; + parent_tree.tree_data.tree_id = ui::AXTreeID::FromString("1"); parent_tree.nodes.resize(3); parent_tree.nodes[0].id = 1; parent_tree.nodes[0].child_ids.push_back(2); @@ -48,14 +48,14 @@ TEST(CombineAXTreesTest, EmbedChildTree) { parent_tree.nodes[1].role = ax::mojom::Role::kButton; parent_tree.nodes[2].id = 3; parent_tree.nodes[2].role = ax::mojom::Role::kIframe; - parent_tree.nodes[2].AddIntAttribute(ax::mojom::IntAttribute::kChildTreeId, - 2); + parent_tree.nodes[2].AddStringAttribute( + ax::mojom::StringAttribute::kChildTreeId, "2"); AXTreeUpdate child_tree; child_tree.root_id = 1; child_tree.has_tree_data = true; - child_tree.tree_data.parent_tree_id = 1; - child_tree.tree_data.tree_id = 2; + child_tree.tree_data.parent_tree_id = ui::AXTreeID::FromString("1"); + child_tree.tree_data.tree_id = ui::AXTreeID::FromString("2"); child_tree.nodes.resize(3); child_tree.nodes[0].id = 1; child_tree.nodes[0].child_ids.push_back(2); @@ -97,7 +97,7 @@ TEST(CombineAXTreesTest, MapAllIdAttributes) { AXTreeUpdate tree; tree.has_tree_data = true; - tree.tree_data.tree_id = 1; + tree.tree_data.tree_id = ui::AXTreeID::FromString("1"); tree.root_id = 11; tree.nodes.resize(2); tree.nodes[0].id = 11; @@ -157,8 +157,8 @@ TEST(CombineAXTreesTest, MapAllIdAttributes) { TEST(CombineAXTreesTest, FocusedTree) { AXTreeUpdate parent_tree; parent_tree.has_tree_data = true; - parent_tree.tree_data.tree_id = 1; - parent_tree.tree_data.focused_tree_id = 2; + parent_tree.tree_data.tree_id = ui::AXTreeID::FromString("1"); + parent_tree.tree_data.focused_tree_id = ui::AXTreeID::FromString("2"); parent_tree.tree_data.focus_id = 2; parent_tree.root_id = 1; parent_tree.nodes.resize(3); @@ -169,13 +169,13 @@ TEST(CombineAXTreesTest, FocusedTree) { parent_tree.nodes[1].role = ax::mojom::Role::kButton; parent_tree.nodes[2].id = 3; parent_tree.nodes[2].role = ax::mojom::Role::kIframe; - parent_tree.nodes[2].AddIntAttribute(ax::mojom::IntAttribute::kChildTreeId, - 2); + parent_tree.nodes[2].AddStringAttribute( + ax::mojom::StringAttribute::kChildTreeId, "2"); AXTreeUpdate child_tree; child_tree.has_tree_data = true; - child_tree.tree_data.parent_tree_id = 1; - child_tree.tree_data.tree_id = 2; + child_tree.tree_data.parent_tree_id = ui::AXTreeID::FromString("1"); + child_tree.tree_data.tree_id = ui::AXTreeID::FromString("2"); child_tree.tree_data.focus_id = 3; child_tree.root_id = 1; child_tree.nodes.resize(3); diff --git a/chromium/ui/accessibility/ax_tree_data.cc b/chromium/ui/accessibility/ax_tree_data.cc index 20e5f1ccce8..ec452ba6d57 100644 --- a/chromium/ui/accessibility/ax_tree_data.cc +++ b/chromium/ui/accessibility/ax_tree_data.cc @@ -23,12 +23,12 @@ AXTreeData::~AXTreeData() = default; std::string AXTreeData::ToString() const { std::string result; - if (tree_id != -1) - result += " tree_id=" + base::NumberToString(tree_id); - if (parent_tree_id != -1) - result += " parent_tree_id=" + base::NumberToString(parent_tree_id); - if (focused_tree_id != -1) - result += " focused_tree_id=" + base::NumberToString(focused_tree_id); + if (tree_id != AXTreeIDUnknown()) + result += " tree_id=" + tree_id.ToString(); + if (parent_tree_id != AXTreeIDUnknown()) + result += " parent_tree_id=" + parent_tree_id.ToString(); + if (focused_tree_id != AXTreeIDUnknown()) + result += " focused_tree_id=" + focused_tree_id.ToString(); if (!doctype.empty()) result += " doctype=" + doctype; diff --git a/chromium/ui/accessibility/ax_tree_data.h b/chromium/ui/accessibility/ax_tree_data.h index 0233764b109..dec27f4b145 100644 --- a/chromium/ui/accessibility/ax_tree_data.h +++ b/chromium/ui/accessibility/ax_tree_data.h @@ -15,6 +15,7 @@ #include "base/strings/string_split.h" #include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_export.h" +#include "ui/accessibility/ax_tree_id_registry.h" #include "ui/gfx/geometry/rect.h" namespace ui { @@ -33,14 +34,14 @@ struct AX_EXPORT AXTreeData { // public and copyable. // The globally unique ID of this accessibility tree. - int32_t tree_id = -1; + AXTreeID tree_id; // The ID of the accessibility tree that this tree is contained in, if any. - int32_t parent_tree_id = -1; + AXTreeID parent_tree_id; // The ID of the accessibility tree that has focus. This is typically set // on the root frame in a frame tree. - int32_t focused_tree_id = -1; + AXTreeID focused_tree_id; // Attributes specific to trees that are web frames. std::string doctype; diff --git a/chromium/ui/accessibility/ax_tree_id.cc b/chromium/ui/accessibility/ax_tree_id.cc new file mode 100644 index 00000000000..6076f1122b5 --- /dev/null +++ b/chromium/ui/accessibility/ax_tree_id.cc @@ -0,0 +1,62 @@ +// 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/accessibility/ax_tree_id.h" + +#include <iostream> + +#include "base/no_destructor.h" + +namespace ui { + +AXTreeID::AXTreeID() : id_("") {} + +AXTreeID::AXTreeID(const std::string& string) : id_(string) {} + +// static +AXTreeID AXTreeID::FromString(const std::string& string) { + return AXTreeID(string); +} + +bool AXTreeID::operator==(const AXTreeID& rhs) const { + return id_ == rhs.id_; +} + +bool AXTreeID::operator!=(const AXTreeID& rhs) const { + return id_ != rhs.id_; +} + +bool AXTreeID::operator<(const AXTreeID& rhs) const { + return id_ < rhs.id_; +} + +bool AXTreeID::operator<=(const AXTreeID& rhs) const { + return id_ <= rhs.id_; +} + +bool AXTreeID::operator>(const AXTreeID& rhs) const { + return id_ > rhs.id_; +} + +bool AXTreeID::operator>=(const AXTreeID& rhs) const { + return id_ >= rhs.id_; +} + +std::ostream& operator<<(std::ostream& stream, const AXTreeID& value) { + return stream << 0; +} + +const AXTreeID& AXTreeIDUnknown() { + static const base::NoDestructor<AXTreeID> ax_tree_id_unknown( + AXTreeID::FromString("")); + return *ax_tree_id_unknown; +} + +const AXTreeID& DesktopAXTreeID() { + static const base::NoDestructor<AXTreeID> desktop_ax_tree_id( + AXTreeID::FromString("0")); + return *desktop_ax_tree_id; +} + +} // namespace ui diff --git a/chromium/ui/accessibility/ax_tree_id.h b/chromium/ui/accessibility/ax_tree_id.h new file mode 100644 index 00000000000..946181bbbf3 --- /dev/null +++ b/chromium/ui/accessibility/ax_tree_id.h @@ -0,0 +1,58 @@ +// 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_ACCESSIBILITY_AX_TREE_ID_H_ +#define UI_ACCESSIBILITY_AX_TREE_ID_H_ + +#include <string> + +#include "ui/accessibility/ax_export.h" + +namespace mojo { +template <typename DataViewType, typename T> +struct StructTraits; +} + +namespace ax { +namespace mojom { +class AXTreeIDDataView; +} +} // namespace ax + +namespace ui { + +// A unique ID representing an accessibility tree. +class AX_EXPORT AXTreeID { + public: + AXTreeID(); + static AXTreeID FromString(const std::string& string); + const std::string& ToString() const { return id_; } + operator std::string() const { return id_; } + + bool operator==(const AXTreeID& rhs) const; + bool operator!=(const AXTreeID& rhs) const; + bool operator<(const AXTreeID& rhs) const; + bool operator<=(const AXTreeID& rhs) const; + bool operator>(const AXTreeID& rhs) const; + bool operator>=(const AXTreeID& rhs) const; + + private: + explicit AXTreeID(const std::string& string); + + friend struct mojo::StructTraits<ax::mojom::AXTreeIDDataView, ui::AXTreeID>; + + std::string id_; +}; + +AX_EXPORT std::ostream& operator<<(std::ostream& stream, const AXTreeID& value); + +// The value to use when an AXTreeID is unknown. +AX_EXPORT extern const AXTreeID& AXTreeIDUnknown(); + +// The AXTreeID of the desktop. +AX_EXPORT extern const AXTreeID& DesktopAXTreeID(); + +} // namespace ui + +#endif // UI_ACCESSIBILITY_AX_TREE_ID_H_ diff --git a/chromium/ui/accessibility/ax_tree_id_registry.cc b/chromium/ui/accessibility/ax_tree_id_registry.cc index d05fa5eebec..f0a47078aac 100644 --- a/chromium/ui/accessibility/ax_tree_id_registry.cc +++ b/chromium/ui/accessibility/ax_tree_id_registry.cc @@ -5,6 +5,7 @@ #include "ui/accessibility/ax_tree_id_registry.h" #include "base/memory/singleton.h" +#include "base/strings/string_number_conversions.h" #include "ui/accessibility/ax_host_delegate.h" namespace ui { @@ -14,23 +15,21 @@ AXTreeIDRegistry* AXTreeIDRegistry::GetInstance() { return base::Singleton<AXTreeIDRegistry>::get(); } -AXTreeIDRegistry::AXTreeID AXTreeIDRegistry::GetOrCreateAXTreeID( - int process_id, - int routing_id) { +AXTreeID AXTreeIDRegistry::GetOrCreateAXTreeID(int process_id, int routing_id) { FrameID frame_id(process_id, routing_id); auto it = frame_to_ax_tree_id_map_.find(frame_id); if (it != frame_to_ax_tree_id_map_.end()) return it->second; - AXTreeID new_id = ++ax_tree_id_counter_; + AXTreeID new_id = + AXTreeID::FromString(base::IntToString(++ax_tree_id_counter_)); frame_to_ax_tree_id_map_[frame_id] = new_id; ax_tree_to_frame_id_map_[new_id] = frame_id; return new_id; } -AXTreeIDRegistry::FrameID AXTreeIDRegistry::GetFrameID( - AXTreeIDRegistry::AXTreeID ax_tree_id) { +AXTreeIDRegistry::FrameID AXTreeIDRegistry::GetFrameID(AXTreeID ax_tree_id) { auto it = ax_tree_to_frame_id_map_.find(ax_tree_id); if (it != ax_tree_to_frame_id_map_.end()) return it->second; @@ -38,30 +37,29 @@ AXTreeIDRegistry::FrameID AXTreeIDRegistry::GetFrameID( return FrameID(-1, -1); } -AXTreeIDRegistry::AXTreeID AXTreeIDRegistry::GetOrCreateAXTreeID( - AXHostDelegate* delegate) { +AXTreeID AXTreeIDRegistry::GetOrCreateAXTreeID(AXHostDelegate* delegate) { for (auto it : id_to_host_delegate_) { if (it.second == delegate) return it.first; } - id_to_host_delegate_[++ax_tree_id_counter_] = delegate; - return ax_tree_id_counter_; + AXTreeID new_id = + AXTreeID::FromString(base::IntToString(++ax_tree_id_counter_)); + id_to_host_delegate_[new_id] = delegate; + return new_id; } -AXHostDelegate* AXTreeIDRegistry::GetHostDelegate( - AXTreeIDRegistry::AXTreeID ax_tree_id) { +AXHostDelegate* AXTreeIDRegistry::GetHostDelegate(AXTreeID ax_tree_id) { auto it = id_to_host_delegate_.find(ax_tree_id); if (it == id_to_host_delegate_.end()) return nullptr; return it->second; } -void AXTreeIDRegistry::SetDelegateForID(AXHostDelegate* delegate, - AXTreeIDRegistry::AXTreeID id) { +void AXTreeIDRegistry::SetDelegateForID(AXHostDelegate* delegate, AXTreeID id) { id_to_host_delegate_[id] = delegate; } -void AXTreeIDRegistry::RemoveAXTreeID(AXTreeIDRegistry::AXTreeID ax_tree_id) { +void AXTreeIDRegistry::RemoveAXTreeID(AXTreeID ax_tree_id) { auto frame_it = ax_tree_to_frame_id_map_.find(ax_tree_id); if (frame_it != ax_tree_to_frame_id_map_.end()) { frame_to_ax_tree_id_map_.erase(frame_it->second); diff --git a/chromium/ui/accessibility/ax_tree_id_registry.h b/chromium/ui/accessibility/ax_tree_id_registry.h index 3cdcc61ad5c..24e40bd1fc3 100644 --- a/chromium/ui/accessibility/ax_tree_id_registry.h +++ b/chromium/ui/accessibility/ax_tree_id_registry.h @@ -6,10 +6,12 @@ #define UI_ACCESSIBILITY_AX_TREE_ID_REGISTRY_H_ #include <map> +#include <string> #include <utility> #include "base/macros.h" #include "ui/accessibility/ax_export.h" +#include "ui/accessibility/ax_tree_id.h" namespace base { template <typename T> @@ -32,10 +34,6 @@ class AX_EXPORT AXTreeIDRegistry { public: using FrameID = std::pair<int, int>; - using AXTreeID = int; - - static constexpr AXTreeID kNoAXTreeID = -1; - // Get the single instance of this class. static AXTreeIDRegistry* GetInstance(); @@ -59,8 +57,8 @@ class AX_EXPORT AXTreeIDRegistry { AXTreeIDRegistry(); virtual ~AXTreeIDRegistry(); - // Tracks the current unique ax frame id. - AXTreeID ax_tree_id_counter_; + // Tracks the current unique ax tree id. + int ax_tree_id_counter_; // Maps an accessibility tree to its frame via ids. std::map<AXTreeID, FrameID> ax_tree_to_frame_id_map_; diff --git a/chromium/ui/accessibility/ax_tree_serializer.h b/chromium/ui/accessibility/ax_tree_serializer.h index 5b835a45c50..f11ded45a69 100644 --- a/chromium/ui/accessibility/ax_tree_serializer.h +++ b/chromium/ui/accessibility/ax_tree_serializer.h @@ -88,6 +88,16 @@ class AXTreeSerializer { // being serialized. void InvalidateSubtree(AXSourceNode node); + // Return whether or not this node is in the client tree. If you call + // this immediately after serializing, this indicates whether a given + // node is in the set of nodes that the client (the recipient of + // the AXTreeUpdates) is aware of. + // + // For example, you could use this to determine if a given node is + // reachable. If one of its ancestors is hidden and it was pruned + // from the accessibility tree, this would return false. + bool IsInClientTree(AXSourceNode node); + // Only for unit testing. Normally this class relies on getting a call // to SerializeChanges() every time the source tree changes. For unit // testing, it's convenient to create a static AXTree for the initial @@ -162,6 +172,10 @@ class AXTreeSerializer { // Visit all of the descendants of |node| once. void WalkAllDescendants(AXSourceNode node); + // Delete the entire client subtree but don't set the did_reset_ flag + // like when Reset() is called. + void InternalReset(); + // The tree source. AXTreeSource<AXSourceNode, AXNodeData, AXTreeData>* tree_; @@ -177,6 +191,11 @@ class AXTreeSerializer { // The maximum number of nodes to serialize in a given call to // SerializeChanges, or 0 if there's no maximum. size_t max_node_count_ = 0; + + // Keeps track of if Reset() was called. If so, we need to always + // explicitly set node_id_to_clear to ensure that the next serialized + // tree is treated as a completely new tree and not a partial update. + bool did_reset_ = false; }; // In order to keep track of what nodes the client knows about, we keep a @@ -203,6 +222,12 @@ AXTreeSerializer<AXSourceNode, AXNodeData, AXTreeData>::~AXTreeSerializer() { template <typename AXSourceNode, typename AXNodeData, typename AXTreeData> void AXTreeSerializer<AXSourceNode, AXNodeData, AXTreeData>::Reset() { + InternalReset(); + did_reset_ = true; +} + +template <typename AXSourceNode, typename AXNodeData, typename AXTreeData> +void AXTreeSerializer<AXSourceNode, AXNodeData, AXTreeData>::InternalReset() { client_tree_data_ = AXTreeData(); // Normally we use DeleteClientSubtree to remove nodes from the tree, @@ -371,7 +396,7 @@ bool AXTreeSerializer<AXSourceNode, AXNodeData, AXTreeData>::SerializeChanges( // If there's no LCA, just tell the client to destroy the whole // tree and then we'll serialize everything from the new root. out_update->node_id_to_clear = client_root_->id; - Reset(); + InternalReset(); } else if (need_delete) { // Otherwise, if we need to reserialize a subtree, first we need // to delete those nodes in our client tree so that @@ -394,7 +419,19 @@ bool AXTreeSerializer<AXSourceNode, AXNodeData, AXTreeData>::SerializeChanges( // DumpAccessibilityTreeTest.AccessibilityAriaOwns. WalkAllDescendants(lca); - return SerializeChangedNodes(lca, out_update); + if (!SerializeChangedNodes(lca, out_update)) + return false; + + // If we had a reset, ensure that the old tree is cleared before the client + // unserializes this update. If we didn't do this, there's a chance that + // treating this update as an incremental update could result in some + // reparenting. + if (did_reset_) { + out_update->node_id_to_clear = tree_->GetId(lca); + did_reset_ = false; + } + + return true; } template <typename AXSourceNode, typename AXNodeData, typename AXTreeData> @@ -406,6 +443,12 @@ void AXTreeSerializer<AXSourceNode, AXNodeData, AXTreeData>::InvalidateSubtree( } template <typename AXSourceNode, typename AXNodeData, typename AXTreeData> +bool AXTreeSerializer<AXSourceNode, AXNodeData, AXTreeData>::IsInClientTree( + AXSourceNode node) { + return !!ClientTreeNodeById(tree_->GetId(node)); +} + +template <typename AXSourceNode, typename AXNodeData, typename AXTreeData> void AXTreeSerializer<AXSourceNode, AXNodeData, AXTreeData>:: InvalidateClientSubtree(ClientTreeNode* client_node) { client_node->invalid = true; @@ -444,7 +487,8 @@ bool AXTreeSerializer<AXSourceNode, AXNodeData, AXTreeData>:: int id = tree_->GetId(node); ClientTreeNode* client_node = ClientTreeNodeById(id); if (!client_node) { - Reset(); + if (client_root_) + Reset(); client_root_ = new ClientTreeNode(); client_node = client_root_; client_node->id = id; diff --git a/chromium/ui/accessibility/ax_tree_serializer_unittest.cc b/chromium/ui/accessibility/ax_tree_serializer_unittest.cc index 023945043b4..3a5724b3f64 100644 --- a/chromium/ui/accessibility/ax_tree_serializer_unittest.cc +++ b/chromium/ui/accessibility/ax_tree_serializer_unittest.cc @@ -389,4 +389,76 @@ TEST_F(AXTreeSerializerTest, DuplicateIdsReturnsErrorAndFlushes) { ASSERT_EQ(static_cast<size_t>(5), update.nodes.size()); } +// If a tree serializer is reset, that means it doesn't know about +// the state of the client tree anymore. The safest thing to do in +// that circumstance is to force the client to clear everything. +TEST_F(AXTreeSerializerTest, ResetUpdatesNodeIdToClear) { + // (1 (2 (3 (4 (5))))) + treedata0_.root_id = 1; + treedata0_.nodes.resize(5); + treedata0_.nodes[0].id = 1; + treedata0_.nodes[0].child_ids.push_back(2); + treedata0_.nodes[1].id = 2; + treedata0_.nodes[1].child_ids.push_back(3); + treedata0_.nodes[2].id = 3; + treedata0_.nodes[2].child_ids.push_back(4); + treedata0_.nodes[3].id = 4; + treedata0_.nodes[3].child_ids.push_back(5); + treedata0_.nodes[4].id = 5; + + // Node 5 has been reparented from being a child of node 4, + // to a child of node 2. + // (1 (2 (3 (4) 5))) + treedata1_.root_id = 1; + treedata1_.nodes.resize(5); + treedata1_.nodes[0].id = 1; + treedata1_.nodes[0].child_ids.push_back(2); + treedata1_.nodes[1].id = 2; + treedata1_.nodes[1].child_ids.push_back(3); + treedata1_.nodes[1].child_ids.push_back(5); + treedata1_.nodes[2].id = 3; + treedata1_.nodes[2].child_ids.push_back(4); + treedata1_.nodes[3].id = 4; + treedata1_.nodes[4].id = 5; + + CreateTreeSerializer(); + + serializer_->Reset(); + + AXTreeUpdate update; + ASSERT_TRUE(serializer_->SerializeChanges(tree1_->GetFromId(4), &update)); + + // The update should unserialize without errors. + AXTree dst_tree(treedata0_); + EXPECT_TRUE(dst_tree.Unserialize(update)) << dst_tree.error(); +} + +// Ensure that calling Reset doesn't cause any problems if +// the root changes. +TEST_F(AXTreeSerializerTest, ResetWorksWithNewRootId) { + // (1 (2)) + treedata0_.root_id = 1; + treedata0_.nodes.resize(2); + treedata0_.nodes[0].id = 1; + treedata0_.nodes[0].child_ids.push_back(2); + treedata0_.nodes[1].id = 2; + + // (3 (4)) + treedata1_.root_id = 3; + treedata1_.nodes.resize(2); + treedata1_.nodes[0].id = 3; + treedata1_.nodes[0].child_ids.push_back(4); + treedata1_.nodes[1].id = 4; + + CreateTreeSerializer(); + serializer_->Reset(); + + AXTreeUpdate update; + ASSERT_TRUE(serializer_->SerializeChanges(tree1_->GetFromId(4), &update)); + + // The update should unserialize without errors. + AXTree dst_tree(treedata0_); + EXPECT_TRUE(dst_tree.Unserialize(update)) << dst_tree.error(); +} + } // namespace ui diff --git a/chromium/ui/accessibility/ax_tree_unittest.cc b/chromium/ui/accessibility/ax_tree_unittest.cc index bb1d3a24534..42d660a491d 100644 --- a/chromium/ui/accessibility/ax_tree_unittest.cc +++ b/chromium/ui/accessibility/ax_tree_unittest.cc @@ -323,25 +323,6 @@ TEST(AXTreeTest, SerializeAXTreeUpdate) { update.ToString()); } -TEST(AXTreeTest, DeleteUnknownSubtreeFails) { - AXNodeData root; - root.id = 1; - - AXTreeUpdate initial_state; - initial_state.root_id = 1; - initial_state.nodes.push_back(root); - AXTree tree(initial_state); - - // This should fail because we're asking it to delete - // a subtree rooted at id=2, which doesn't exist. - AXTreeUpdate update; - update.node_id_to_clear = 2; - update.nodes.resize(1); - update.nodes[0].id = 1; - EXPECT_FALSE(tree.Unserialize(update)); - ASSERT_EQ("Bad node_id_to_clear: 2", tree.error()); -} - TEST(AXTreeTest, LeaveOrphanedDeletedSubtreeFails) { AXTreeUpdate initial_state; initial_state.root_id = 1; @@ -1444,41 +1425,47 @@ TEST(AXTreeTest, ChildTreeIds) { initial_state.nodes[0].child_ids.push_back(3); initial_state.nodes[0].child_ids.push_back(4); initial_state.nodes[1].id = 2; - initial_state.nodes[1].AddIntAttribute(ax::mojom::IntAttribute::kChildTreeId, - 92); + initial_state.nodes[1].AddStringAttribute( + ax::mojom::StringAttribute::kChildTreeId, "92"); initial_state.nodes[2].id = 3; - initial_state.nodes[2].AddIntAttribute(ax::mojom::IntAttribute::kChildTreeId, - 93); + initial_state.nodes[2].AddStringAttribute( + ax::mojom::StringAttribute::kChildTreeId, "93"); initial_state.nodes[3].id = 4; - initial_state.nodes[3].AddIntAttribute(ax::mojom::IntAttribute::kChildTreeId, - 93); + initial_state.nodes[3].AddStringAttribute( + ax::mojom::StringAttribute::kChildTreeId, "93"); AXTree tree(initial_state); - auto child_tree_91_nodes = tree.GetNodeIdsForChildTreeId(91); + auto child_tree_91_nodes = + tree.GetNodeIdsForChildTreeId(AXTreeID::FromString("91")); EXPECT_EQ(0U, child_tree_91_nodes.size()); - auto child_tree_92_nodes = tree.GetNodeIdsForChildTreeId(92); + auto child_tree_92_nodes = + tree.GetNodeIdsForChildTreeId(AXTreeID::FromString("92")); EXPECT_EQ(1U, child_tree_92_nodes.size()); EXPECT_TRUE(base::ContainsKey(child_tree_92_nodes, 2)); - auto child_tree_93_nodes = tree.GetNodeIdsForChildTreeId(93); + auto child_tree_93_nodes = + tree.GetNodeIdsForChildTreeId(AXTreeID::FromString("93")); EXPECT_EQ(2U, child_tree_93_nodes.size()); EXPECT_TRUE(base::ContainsKey(child_tree_93_nodes, 3)); EXPECT_TRUE(base::ContainsKey(child_tree_93_nodes, 4)); AXTreeUpdate update = initial_state; - update.nodes[2].int_attributes.clear(); - update.nodes[2].AddIntAttribute(ax::mojom::IntAttribute::kChildTreeId, 92); - update.nodes[3].int_attributes.clear(); + update.nodes[2].string_attributes.clear(); + update.nodes[2].AddStringAttribute(ax::mojom::StringAttribute::kChildTreeId, + "92"); + update.nodes[3].string_attributes.clear(); EXPECT_TRUE(tree.Unserialize(update)); - child_tree_92_nodes = tree.GetNodeIdsForChildTreeId(92); + child_tree_92_nodes = + tree.GetNodeIdsForChildTreeId(AXTreeID::FromString("92")); EXPECT_EQ(2U, child_tree_92_nodes.size()); EXPECT_TRUE(base::ContainsKey(child_tree_92_nodes, 2)); EXPECT_TRUE(base::ContainsKey(child_tree_92_nodes, 3)); - child_tree_93_nodes = tree.GetNodeIdsForChildTreeId(93); + child_tree_93_nodes = + tree.GetNodeIdsForChildTreeId(AXTreeID::FromString("93")); EXPECT_EQ(0U, child_tree_93_nodes.size()); } diff --git a/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_cs.xtb b/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_cs.xtb index a2be5d28572..7a6fd118c61 100644 --- a/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_cs.xtb +++ b/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_cs.xtb @@ -10,7 +10,7 @@ <translation id="1703735871906654364">Procházení pomocí kurzoru</translation> <translation id="1791496371305830581">Povolit všechny animované obrázky</translation> <translation id="1996252509865389616">Aktivovat?</translation> -<translation id="2079545284768500474" /> +<translation id="2079545284768500474">Vrátit zpět</translation> <translation id="2179565792157161713">Otevřít dlouhý popis na nové kartě</translation> <translation id="2223143012868735942">Přizpůsobitelný barevný filtr používaný na webové stránky s cílem zlepšit vnímání barev.</translation> <translation id="2394933097471027016">Vyzkoušejte to – procházení pomocí kurzoru je na této stránce vždy aktivováno.</translation> diff --git a/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_gu.xtb b/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_gu.xtb index a2198a23de5..9c3b1d0116c 100644 --- a/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_gu.xtb +++ b/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_gu.xtb @@ -6,28 +6,28 @@ <translation id="145360476452865422">એનિમેશન નીતિ:</translation> <translation id="1555130319947370107">વાદળી</translation> <translation id="1588438908519853928">સામાન્ય</translation> -<translation id="1591070050619849194">બધા છબી એનિમેશન અક્ષમ કરો.</translation> +<translation id="1591070050619849194">બધા છબી એનિમેશન બંધ કરો.</translation> <translation id="1703735871906654364">કૅરેટ બ્રાઉઝિંગ</translation> <translation id="1791496371305830581">બધી એનિમેટ થતી છબીઓને મંજૂરી આપો.</translation> -<translation id="1996252509865389616">સક્ષમ કરીએ?</translation> -<translation id="2079545284768500474">પૂર્વવત કરો</translation> +<translation id="1996252509865389616">ચાલુ કરીએ?</translation> +<translation id="2079545284768500474">છેલ્લો ફેરફાર રદ કરો</translation> <translation id="2179565792157161713">નવા ટૅબમાં લાંબુ વર્ણન ખોલો</translation> <translation id="2223143012868735942">રંગ વિભાવના સુધારવા માટે વેબ પેજીસને કસ્ટમાઇઝ કરી શકાય તેવું રંગ ફિલ્ટર લાગુ કરવામાં આવ્યું.</translation> <translation id="2394933097471027016">તેને હમણાં અજમાવી જુઓ - કૅરેટ બ્રાઉઝિંગ આ પૃષ્ઠ પર હંમેશા સક્ષમ હોય છે!</translation> <translation id="2471847333270902538"><ph name="SITE" /> માટે રંગ યોજના:</translation> <translation id="2648340354586434750">શબ્દો દ્વારા ખસવા માટે <span class='key'>વિકલ્પ</span> ને પકડી રાખો.</translation> -<translation id="2795227192542594043">કીબોર્ડ દ્વારા ટેક્સ્ટ પસંદ કરવા દઈને, આ એક્સ્ટેન્શન તમને વેબ પૃષ્ઠોમાં એક ખસેડવા યોગ્ય કર્સર આપે છે.</translation> +<translation id="2795227192542594043">કીબોર્ડ દ્વારા ટેક્સ્ટ પસંદ કરવા દઈને, આ એક્સ્ટેંશન તમને વેબ પેજમાં એક ખસેડવા યોગ્ય કર્સર આપે છે.</translation> <translation id="2808027189040546825">પગલું 1: ફિન્ટેસ્ટ તારાઓ સાથે પંક્તિ પસંદ કરો:</translation> -<translation id="2965611304828530558"><p>જ્યારે તમે કોઈ લિંક અથવા કન્ટ્રોલ પર પહોંચો છો, ત્યારે તે આપમેળે ફોકસ થાય છે. લિંક અથવા બટનને ક્લિક કરવા માટે <span class='key'>Enter</span> દબાવો. </p> <p> જ્યારે કોઈ ફોકસ કરેલ કન્ટ્રોલ (જેમ કે ટેક્સ્ટ બોક્સ અથવા કોઈ સૂચિ બોક્સ) તીર કીઝને કેપ્ચર કરી રહી હોય, ત્યારે કૅરેટ બ્રાઉઝિંગને ચાલુ રાખવા માટે ડાબા અથવા જમણા તીર પછી <span class='key'>Esc</span> દબાવો. </p> <p> વૈકલ્પિક રીતે, ફોકસ કરવા યોગ્ય કન્ટ્રોલ પર ખસવા માટે <span class='key'>Tab</span> દબાવો. </p></translation> +<translation id="2965611304828530558"><p>જ્યારે તમે કોઈ લિંક અથવા કન્ટ્રોલ પર પહોંચો છો, ત્યારે તે ઑટોમૅટિક રીતે ફોકસ થાય છે. લિંક અથવા બટનને ક્લિક કરવા માટે <span class='key'>Enter</span> દબાવો. </p> <p> જ્યારે કોઈ ફોકસ કરેલ કન્ટ્રોલ (જેમ કે ટેક્સ્ટ બોક્સ અથવા કોઈ સૂચિ બોક્સ) Arrow કીને કેપ્ચર કરી રહી હોય, ત્યારે કૅરેટ બ્રાઉઝિંગને ચાલુ રાખવા માટે ડાબા અથવા જમણા તીર પછી <span class='key'>Esc</span> દબાવો. </p> <p> વૈકલ્પિક રીતે, ફોકસ કરવા યોગ્ય કન્ટ્રોલ પર ખસવા માટે <span class='key'>Tab</span> દબાવો. </p></translation> <translation id="3252573918265662711">સેટઅપ</translation> <translation id="3410969471888629217">સાઇટ કસ્ટમાઇઝેશન ભૂલી જાઓ</translation> -<translation id="3435896845095436175">સક્ષમ કરો</translation> +<translation id="3435896845095436175">ચાલુ કરો</translation> <translation id="3622586652998721735">ડિફૉલ્ટ યોજના તરીકે સેટ કરો</translation> <translation id="3812541808639806898">છબી 'Alt' ટેક્સ્ટ દર્શક</translation> <translation id="381767806621926835">કોઈપણ વસ્તુનું લાંબુ વર્ણન ઍક્સેસ કરવા માટે, તેના પર "longdesc" અથવા "aria-describedat" લક્ષણ વડે રાઇટ-ક્લિક કરો.</translation> <translation id="4023902424053835668">તીર કીઝનો ઉપયોગ કરીને વેબ પૃષ્ઠોની ટેક્સ્ટને બ્રાઉઝ કરો.</translation> <translation id="4388820049312272371">એક ઝડપી ફ્લેશ સાથે કર્સરની સ્થિતિને હાઇલાઇટ કરો.</translation> -<translation id="4394049700291259645">અક્ષમ કરો</translation> +<translation id="4394049700291259645">બંધ કરો</translation> <translation id="4769065380738716500">છબીઓ તેમની વૈકલ્પિક ટેક્સ્ટ દ્વારા બદલવામાં આવી છે.</translation> <translation id="4896660567607030658">કોઈ પ્રતિસાદ નથી, માત્ર કર્સર દર્શાવો.</translation> <translation id="4937901943818762779">એનિમેટ થતી છબીઓને મંજૂરી આપો, પરંતુ માત્ર એકજ વાર.</translation> @@ -39,7 +39,7 @@ <translation id="5331422999063554397">વિપરિત રંગ</translation> <translation id="5555153510860501336">ઉચ્ચ કોન્ટ્રાસ્ટ અક્ષમ છે</translation> <translation id="5558600050691192317">કીબોર્ડ આદેશો</translation> -<translation id="5594989420907487559">એનિમેશન્સ માત્ર એકજ વાર ચલાવો અથવા સંપૂર્ણપણે એનિમેશન્સ અક્ષમ કરો.</translation> +<translation id="5594989420907487559">એનિમેશન્સ માત્ર એકજ વાર ચલાવો અથવા સંપૂર્ણપણે એનિમેશન્સ બંધ કરો.</translation> <translation id="5631241868147802353">ડિફૉલ્ટ રંગ યોજના:</translation> <translation id="5650358096585648000">વિઝ્યુઅલ પ્રતિસાદ</translation> <translation id="5710185147685935461">વેબપૃષ્ઠોને વાંચવામાં વધુ સરળ બનાવવા માટે રંગ યોજનાને બદલો અથવા વિપરિત કરો.</translation> diff --git a/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_mr.xtb b/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_mr.xtb index 4c569304cd5..6c0a8abc85d 100644 --- a/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_mr.xtb +++ b/chromium/ui/accessibility/extensions/strings/accessibility_extensions_strings_mr.xtb @@ -18,7 +18,7 @@ <translation id="2648340354586434750">शब्दांनुसार हलविण्यासाठी <span class='key'>पर्याय</span> धरून ठेवा.</translation> <translation id="2795227192542594043">हा विस्तार आपल्याला कीबोर्डसह मजकूर निवडण्याची अनुमती देणारा, वेबपृष्ठामधील हलणारा कर्सर देतो.</translation> <translation id="2808027189040546825">चरण 1: सर्वाधिक अंधुक तारे असलेली पंक्ती निवडा:</translation> -<translation id="2965611304828530558"><p>आपण दुव्यावर किंवा नियंत्रणावर पोहचता तेव्हा, ते स्वयंचलितपणे फोकस केले जाते. दुवा किंवा बटण क्लिक करण्यासाठी <span class='key'>Enter</span> दाबा. </p> <p> फोकस केलेले नियंत्रण (जसे की मजकूर बॉक्स किंवा सूची बॉक्स) बाण की कॅप्चर करतात तेव्हा, कॅरेट ब्राउझिंग करणे सुरु ठेवण्यासाठी <span class='key'>Esc</span> त्यानंतर डावा किंवा उजवा बाण दाबा. </p> <p> वैकल्पिकपणे, पुढील फोकस करण्यायोग्य नियंत्रणावर हलविण्यासाठी <span class='key'>Tab</span> दाबा. </p></translation> +<translation id="2965611304828530558"><p>तुम्ही दुव्यावर किंवा नियंत्रणावर पोहचता तेव्हा, ते स्वयंचलितपणे फोकस केले जाते. दुवा किंवा बटण क्लिक करण्यासाठी <span class='key'>Enter</span> दाबा. </p> <p> फोकस केलेले नियंत्रण (जसे की मजकूर बॉक्स किंवा सूची बॉक्स) बाण की कॅप्चर करतात तेव्हा, कॅरेट ब्राउझिंग करणे सुरु ठेवण्यासाठी <span class='key'>Esc</span> त्यानंतर डावा किंवा उजवा बाण दाबा. </p> <p> वैकल्पिकपणे, पुढील फोकस करण्यायोग्य नियंत्रणावर हलविण्यासाठी <span class='key'>Tab</span> दाबा. </p></translation> <translation id="3252573918265662711">सेटअप</translation> <translation id="3410969471888629217">साइट सानुकूलने विसरा</translation> <translation id="3435896845095436175">सक्षम करा</translation> diff --git a/chromium/ui/accessibility/mojom/BUILD.gn b/chromium/ui/accessibility/mojom/BUILD.gn index d41dbd61dd7..ac10ca35074 100644 --- a/chromium/ui/accessibility/mojom/BUILD.gn +++ b/chromium/ui/accessibility/mojom/BUILD.gn @@ -12,6 +12,7 @@ mojom("mojom") { "ax_host.mojom", "ax_node_data.mojom", "ax_tree_data.mojom", + "ax_tree_id.mojom", "ax_tree_update.mojom", ] diff --git a/chromium/ui/accessibility/mojom/ax_action_data.mojom b/chromium/ui/accessibility/mojom/ax_action_data.mojom index 69ca868982e..f7e3238542d 100644 --- a/chromium/ui/accessibility/mojom/ax_action_data.mojom +++ b/chromium/ui/accessibility/mojom/ax_action_data.mojom @@ -5,13 +5,14 @@ module ax.mojom; import "ui/accessibility/ax_enums.mojom"; +import "ui/accessibility/mojom/ax_tree_id.mojom"; import "ui/gfx/geometry/mojo/geometry.mojom"; // A compact representation of an accessibility action and the arguments // associated with that action. See ui::AXActionData for full documentation. struct AXActionData { Action action; - int32 target_tree_id; + ax.mojom.AXTreeID target_tree_id; string source_extension_id; int32 target_node_id; int32 request_id; diff --git a/chromium/ui/accessibility/mojom/ax_action_data_mojom_traits.cc b/chromium/ui/accessibility/mojom/ax_action_data_mojom_traits.cc index 5e4010ec1a0..cfab7ca502b 100644 --- a/chromium/ui/accessibility/mojom/ax_action_data_mojom_traits.cc +++ b/chromium/ui/accessibility/mojom/ax_action_data_mojom_traits.cc @@ -12,7 +12,8 @@ bool StructTraits<ax::mojom::AXActionDataDataView, ui::AXActionData>::Read( ui::AXActionData* out) { if (!data.ReadAction(&out->action)) return false; - out->target_tree_id = data.target_tree_id(); + if (!data.ReadTargetTreeId(&out->target_tree_id)) + return false; if (!data.ReadSourceExtensionId(&out->source_extension_id)) return false; out->target_node_id = data.target_node_id(); diff --git a/chromium/ui/accessibility/mojom/ax_action_data_mojom_traits.h b/chromium/ui/accessibility/mojom/ax_action_data_mojom_traits.h index 39782b5734d..3a0b5730b90 100644 --- a/chromium/ui/accessibility/mojom/ax_action_data_mojom_traits.h +++ b/chromium/ui/accessibility/mojom/ax_action_data_mojom_traits.h @@ -7,6 +7,7 @@ #include "ui/accessibility/ax_action_data.h" #include "ui/accessibility/mojom/ax_action_data.mojom-shared.h" +#include "ui/accessibility/mojom/ax_tree_id_mojom_traits.h" #include "ui/gfx/geometry/mojo/geometry_struct_traits.h" namespace mojo { @@ -16,10 +17,10 @@ struct StructTraits<ax::mojom::AXActionDataDataView, ui::AXActionData> { static ax::mojom::Action action(const ui::AXActionData& a) { return a.action; } - static int32_t target_tree_id(const ui::AXActionData& a) { + static const ui::AXTreeID& target_tree_id(const ui::AXActionData& a) { return a.target_tree_id; } - static std::string source_extension_id(const ui::AXActionData& a) { + static const std::string& source_extension_id(const ui::AXActionData& a) { return a.source_extension_id; } static int32_t target_node_id(const ui::AXActionData& a) { @@ -42,13 +43,13 @@ struct StructTraits<ax::mojom::AXActionDataDataView, ui::AXActionData> { static int32_t custom_action_id(const ui::AXActionData& a) { return a.custom_action_id; } - static gfx::Rect target_rect(const ui::AXActionData& a) { + static const gfx::Rect& target_rect(const ui::AXActionData& a) { return a.target_rect; } - static gfx::Point target_point(const ui::AXActionData& a) { + static const gfx::Point& target_point(const ui::AXActionData& a) { return a.target_point; } - static std::string value(const ui::AXActionData& a) { return a.value; } + static const std::string& value(const ui::AXActionData& a) { return a.value; } static ax::mojom::Event hit_test_event_to_fire(const ui::AXActionData& a) { return a.hit_test_event_to_fire; } diff --git a/chromium/ui/accessibility/mojom/ax_action_data_mojom_traits_unittest.cc b/chromium/ui/accessibility/mojom/ax_action_data_mojom_traits_unittest.cc index 5ff45afc3c5..68603e3c5b5 100644 --- a/chromium/ui/accessibility/mojom/ax_action_data_mojom_traits_unittest.cc +++ b/chromium/ui/accessibility/mojom/ax_action_data_mojom_traits_unittest.cc @@ -17,7 +17,7 @@ using mojo::test::SerializeAndDeserialize; TEST(AXActionDataMojomTraitsTest, RoundTrip) { ui::AXActionData input; input.action = ax::mojom::Action::kBlur; - input.target_tree_id = 1; + input.target_tree_id = ui::AXTreeID::FromString("1"); input.source_extension_id = "extension_id"; input.target_node_id = 2; input.request_id = 3; @@ -37,7 +37,7 @@ TEST(AXActionDataMojomTraitsTest, RoundTrip) { SerializeAndDeserialize<ax::mojom::AXActionData>(&input, &output)); EXPECT_EQ(output.action, ax::mojom::Action::kBlur); - EXPECT_EQ(output.target_tree_id, 1); + EXPECT_EQ(output.target_tree_id.ToString(), "1"); EXPECT_EQ(output.source_extension_id, "extension_id"); EXPECT_EQ(output.target_node_id, 2); EXPECT_EQ(output.request_id, 3); diff --git a/chromium/ui/accessibility/mojom/ax_host.mojom b/chromium/ui/accessibility/mojom/ax_host.mojom index ac5b143828c..10c300b8747 100644 --- a/chromium/ui/accessibility/mojom/ax_host.mojom +++ b/chromium/ui/accessibility/mojom/ax_host.mojom @@ -7,6 +7,7 @@ module ax.mojom; import "ui/accessibility/ax_enums.mojom"; import "ui/accessibility/mojom/ax_action_data.mojom"; import "ui/accessibility/mojom/ax_event.mojom"; +import "ui/accessibility/mojom/ax_tree_id.mojom"; import "ui/accessibility/mojom/ax_tree_update.mojom"; const string kAXHostServiceName = "ax_host_service"; @@ -18,14 +19,17 @@ const string kAXHostServiceName = "ax_host_service"; // browser. // * Sends requests for actions (e.g. click a button) to the remote process. interface AXHost { - // Registers a host in a remote process. - // TODO(jamescook): Support multiple remote processes. - SetRemoteHost(AXRemoteHost remote); + // Registers a host in a remote process. |tree_id| is the ID to use as the + // root of the AX node tree. If |automation_enabled| is true then the remote + // process must send its initial AX node tree immediately (because a feature + // like ChromeVox is enabled). + RegisterRemoteHost(AXRemoteHost remote) => + (ax.mojom.AXTreeID tree_id, bool automation_enabled); // Handles an accessibility |event| (e.g. focus change) for |tree_id| in the // remote process. Includes |updates| to child nodes. HandleAccessibilityEvent( - int32 tree_id, array<AXTreeUpdate> updates, AXEvent event); + ax.mojom.AXTreeID tree_id, array<AXTreeUpdate> updates, AXEvent event); }; // Remote hosts run outside the browser process, for example in a mojo app like diff --git a/chromium/ui/accessibility/mojom/ax_tree_data.mojom b/chromium/ui/accessibility/mojom/ax_tree_data.mojom index 668134576bd..c2fabbf2be9 100644 --- a/chromium/ui/accessibility/mojom/ax_tree_data.mojom +++ b/chromium/ui/accessibility/mojom/ax_tree_data.mojom @@ -5,12 +5,13 @@ module ax.mojom; import "ui/accessibility/ax_enums.mojom"; +import "ui/accessibility/mojom/ax_tree_id.mojom"; // See ui::AXTreeData for comments / explanations of these fields. struct AXTreeData { - int32 tree_id; - int32 parent_tree_id; - int32 focused_tree_id; + ax.mojom.AXTreeID tree_id; + ax.mojom.AXTreeID parent_tree_id; + ax.mojom.AXTreeID focused_tree_id; string doctype; bool loaded; float loading_progress; diff --git a/chromium/ui/accessibility/mojom/ax_tree_data_mojom_traits.cc b/chromium/ui/accessibility/mojom/ax_tree_data_mojom_traits.cc index 4c465686e8c..50ad6115221 100644 --- a/chromium/ui/accessibility/mojom/ax_tree_data_mojom_traits.cc +++ b/chromium/ui/accessibility/mojom/ax_tree_data_mojom_traits.cc @@ -10,9 +10,12 @@ namespace mojo { bool StructTraits<ax::mojom::AXTreeDataDataView, ui::AXTreeData>::Read( ax::mojom::AXTreeDataDataView data, ui::AXTreeData* out) { - out->tree_id = data.tree_id(); - out->parent_tree_id = data.parent_tree_id(); - out->focused_tree_id = data.focused_tree_id(); + if (!data.ReadTreeId(&out->tree_id)) + return false; + if (!data.ReadParentTreeId(&out->parent_tree_id)) + return false; + if (!data.ReadFocusedTreeId(&out->focused_tree_id)) + return false; if (!data.ReadDoctype(&out->doctype)) return false; out->loaded = data.loaded(); diff --git a/chromium/ui/accessibility/mojom/ax_tree_data_mojom_traits.h b/chromium/ui/accessibility/mojom/ax_tree_data_mojom_traits.h index 0b3a2b138b9..6beea9732ad 100644 --- a/chromium/ui/accessibility/mojom/ax_tree_data_mojom_traits.h +++ b/chromium/ui/accessibility/mojom/ax_tree_data_mojom_traits.h @@ -7,26 +7,33 @@ #include "ui/accessibility/ax_tree_data.h" #include "ui/accessibility/mojom/ax_tree_data.mojom-shared.h" +#include "ui/accessibility/mojom/ax_tree_id_mojom_traits.h" namespace mojo { template <> struct StructTraits<ax::mojom::AXTreeDataDataView, ui::AXTreeData> { - static int32_t tree_id(const ui::AXTreeData& p) { return p.tree_id; } - static int32_t parent_tree_id(const ui::AXTreeData& p) { + static const ui::AXTreeID& tree_id(const ui::AXTreeData& p) { + return p.tree_id; + } + static const ui::AXTreeID& parent_tree_id(const ui::AXTreeData& p) { return p.parent_tree_id; } - static int32_t focused_tree_id(const ui::AXTreeData& p) { + static const ui::AXTreeID& focused_tree_id(const ui::AXTreeData& p) { return p.focused_tree_id; } - static std::string doctype(const ui::AXTreeData& p) { return p.doctype; } + static const std::string& doctype(const ui::AXTreeData& p) { + return p.doctype; + } static bool loaded(const ui::AXTreeData& p) { return p.loaded; } static float loading_progress(const ui::AXTreeData& p) { return p.loading_progress; } - static std::string mimetype(const ui::AXTreeData& p) { return p.mimetype; } - static std::string title(const ui::AXTreeData& p) { return p.title; } - static std::string url(const ui::AXTreeData& p) { return p.url; } + static const std::string& mimetype(const ui::AXTreeData& p) { + return p.mimetype; + } + static const std::string& title(const ui::AXTreeData& p) { return p.title; } + static const std::string& url(const ui::AXTreeData& p) { return p.url; } static int32_t focus_id(const ui::AXTreeData& p) { return p.focus_id; } static int32_t sel_anchor_object_id(const ui::AXTreeData& p) { return p.sel_anchor_object_id; diff --git a/chromium/ui/accessibility/mojom/ax_tree_data_mojom_traits_unittest.cc b/chromium/ui/accessibility/mojom/ax_tree_data_mojom_traits_unittest.cc index 02496fbb001..d64ff2c8fe7 100644 --- a/chromium/ui/accessibility/mojom/ax_tree_data_mojom_traits_unittest.cc +++ b/chromium/ui/accessibility/mojom/ax_tree_data_mojom_traits_unittest.cc @@ -12,9 +12,9 @@ using mojo::test::SerializeAndDeserialize; TEST(AXTreeDataMojomTraitsTest, TestSerializeAndDeserializeAXTreeData) { ui::AXTreeData input, output; - input.tree_id = 1; - input.parent_tree_id = 2; - input.focused_tree_id = 3; + input.tree_id = ui::AXTreeID::FromString("1"); + input.parent_tree_id = ui::AXTreeID::FromString("2"); + input.focused_tree_id = ui::AXTreeID::FromString("3"); input.doctype = "4"; input.loaded = true; input.loading_progress = 5; @@ -31,9 +31,9 @@ TEST(AXTreeDataMojomTraitsTest, TestSerializeAndDeserializeAXTreeData) { EXPECT_TRUE(SerializeAndDeserialize<ax::mojom::AXTreeData>(&input, &output)); - EXPECT_EQ(1, output.tree_id); - EXPECT_EQ(2, output.parent_tree_id); - EXPECT_EQ(3, output.focused_tree_id); + EXPECT_EQ("1", output.tree_id.ToString()); + EXPECT_EQ("2", output.parent_tree_id.ToString()); + EXPECT_EQ("3", output.focused_tree_id.ToString()); EXPECT_EQ("4", output.doctype); EXPECT_EQ(true, output.loaded); EXPECT_EQ(5, output.loading_progress); diff --git a/chromium/ui/accessibility/mojom/ax_tree_id.mojom b/chromium/ui/accessibility/mojom/ax_tree_id.mojom new file mode 100644 index 00000000000..6cc327b402d --- /dev/null +++ b/chromium/ui/accessibility/mojom/ax_tree_id.mojom @@ -0,0 +1,10 @@ +// 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. + +module ax.mojom; + +struct AXTreeID { + // Eventually this may become a base::UnguessableToken. + string id; +}; diff --git a/chromium/ui/accessibility/mojom/ax_tree_id.typemap b/chromium/ui/accessibility/mojom/ax_tree_id.typemap new file mode 100644 index 00000000000..034ddbe036c --- /dev/null +++ b/chromium/ui/accessibility/mojom/ax_tree_id.typemap @@ -0,0 +1,15 @@ +# 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. + +mojom = "//ui/accessibility/mojom/ax_tree_id.mojom" +public_headers = [ "//ui/accessibility/ax_tree_id.h" ] +traits_headers = [ "//ui/accessibility/mojom/ax_tree_id_mojom_traits.h" ] +sources = [ + "ax_tree_id_mojom_traits.cc", + "ax_tree_id_mojom_traits.h", +] +public_deps = [ + "//ui/accessibility", +] +type_mappings = [ "ax.mojom.AXTreeID=ui::AXTreeID" ] diff --git a/chromium/ui/accessibility/mojom/ax_tree_id_mojom_traits.cc b/chromium/ui/accessibility/mojom/ax_tree_id_mojom_traits.cc new file mode 100644 index 00000000000..949b7db86e4 --- /dev/null +++ b/chromium/ui/accessibility/mojom/ax_tree_id_mojom_traits.cc @@ -0,0 +1,18 @@ +// 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/accessibility/mojom/ax_tree_id_mojom_traits.h" + +namespace mojo { + +// static +bool StructTraits<ax::mojom::AXTreeIDDataView, ui::AXTreeID>::Read( + ax::mojom::AXTreeIDDataView data, + ui::AXTreeID* out) { + if (!data.ReadId(&out->id_)) + return false; + return true; +} + +} // namespace mojo diff --git a/chromium/ui/accessibility/mojom/ax_tree_id_mojom_traits.h b/chromium/ui/accessibility/mojom/ax_tree_id_mojom_traits.h new file mode 100644 index 00000000000..df481aad53a --- /dev/null +++ b/chromium/ui/accessibility/mojom/ax_tree_id_mojom_traits.h @@ -0,0 +1,22 @@ +// 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_ACCESSIBILITY_MOJOM_AX_TREE_ID_MOJOM_TRAITS_H_ +#define UI_ACCESSIBILITY_MOJOM_AX_TREE_ID_MOJOM_TRAITS_H_ + +#include "ui/accessibility/ax_tree_id.h" +#include "ui/accessibility/mojom/ax_tree_id.mojom-shared.h" + +namespace mojo { + +template <> +struct StructTraits<ax::mojom::AXTreeIDDataView, ui::AXTreeID> { + static const std::string& id(const ui::AXTreeID& p) { return p.ToString(); } + + static bool Read(ax::mojom::AXTreeIDDataView data, ui::AXTreeID* out); +}; + +} // namespace mojo + +#endif // UI_ACCESSIBILITY_MOJOM_AX_TREE_ID_MOJOM_TRAITS_H_ diff --git a/chromium/ui/accessibility/mojom/ax_tree_id_mojom_traits_unittest.cc b/chromium/ui/accessibility/mojom/ax_tree_id_mojom_traits_unittest.cc new file mode 100644 index 00000000000..3f0f4837caf --- /dev/null +++ b/chromium/ui/accessibility/mojom/ax_tree_id_mojom_traits_unittest.cc @@ -0,0 +1,18 @@ +// 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/accessibility/mojom/ax_tree_id_mojom_traits.h" +#include "mojo/public/cpp/test_support/test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/accessibility/ax_tree_id.h" +#include "ui/accessibility/mojom/ax_tree_id.mojom.h" + +using mojo::test::SerializeAndDeserialize; + +TEST(AXTreeIDMojomTraitsTest, TestSerializeAndDeserializeAXTreeID) { + ui::AXTreeID input = ui::AXTreeID::FromString("abc"); + ui::AXTreeID output; + EXPECT_TRUE(SerializeAndDeserialize<ax::mojom::AXTreeID>(&input, &output)); + EXPECT_EQ("abc", output.ToString()); +} diff --git a/chromium/ui/accessibility/mojom/typemaps.gni b/chromium/ui/accessibility/mojom/typemaps.gni index 0ed9a30f168..ba8b1e10e01 100644 --- a/chromium/ui/accessibility/mojom/typemaps.gni +++ b/chromium/ui/accessibility/mojom/typemaps.gni @@ -8,5 +8,6 @@ typemaps = [ "//ui/accessibility/mojom/ax_event.typemap", "//ui/accessibility/mojom/ax_node_data.typemap", "//ui/accessibility/mojom/ax_tree_data.typemap", + "//ui/accessibility/mojom/ax_tree_id.typemap", "//ui/accessibility/mojom/ax_tree_update.typemap", ] diff --git a/chromium/ui/accessibility/platform/aura_window_properties.cc b/chromium/ui/accessibility/platform/aura_window_properties.cc index e72b2d26e9a..2a51fadc5b8 100644 --- a/chromium/ui/accessibility/platform/aura_window_properties.cc +++ b/chromium/ui/accessibility/platform/aura_window_properties.cc @@ -4,15 +4,14 @@ #include "ui/accessibility/platform/aura_window_properties.h" +#include "ui/accessibility/ax_tree_id.h" #include "ui/base/class_property.h" DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(AX_EXPORT, ax::mojom::Role) namespace ui { -DEFINE_UI_CLASS_PROPERTY_KEY(AXTreeIDRegistry::AXTreeID, - kChildAXTreeID, - AXTreeIDRegistry::kNoAXTreeID); +DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(std::string, kChildAXTreeID, nullptr); DEFINE_UI_CLASS_PROPERTY_KEY(ax::mojom::Role, kAXRoleOverride, diff --git a/chromium/ui/accessibility/platform/aura_window_properties.h b/chromium/ui/accessibility/platform/aura_window_properties.h index c78850da22f..8fcf89fd3f7 100644 --- a/chromium/ui/accessibility/platform/aura_window_properties.h +++ b/chromium/ui/accessibility/platform/aura_window_properties.h @@ -5,15 +5,19 @@ #ifndef UI_ACCESSIBILITY_PLATFORM_AURA_WINDOW_PROPERTIES_H_ #define UI_ACCESSIBILITY_PLATFORM_AURA_WINDOW_PROPERTIES_H_ +#include <string> + #include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_export.h" -#include "ui/accessibility/ax_tree_id_registry.h" #include "ui/aura/window.h" namespace ui { -AX_EXPORT extern const aura::WindowProperty<AXTreeIDRegistry::AXTreeID>* const - kChildAXTreeID; +// Value is a serialized |ui::AXTreeID| because code in //ui/aura/mus needs +// to serialize the window property, but //ui/aura cannot depend on +// //ui/accessibility and hence cannot know about the type ui::AXTreeID. +// TODO(dmazzoni): Convert from string to base::UnguessableToken. +AX_EXPORT extern const aura::WindowProperty<std::string*>* const kChildAXTreeID; AX_EXPORT extern const aura::WindowProperty<ax::mojom::Role>* const kAXRoleOverride; diff --git a/chromium/ui/accessibility/platform/ax_platform_atk_hyperlink.cc b/chromium/ui/accessibility/platform/ax_platform_atk_hyperlink.cc index c13f6dbdf5d..c3d8f32a69d 100644 --- a/chromium/ui/accessibility/platform/ax_platform_atk_hyperlink.cc +++ b/chromium/ui/accessibility/platform/ax_platform_atk_hyperlink.cc @@ -10,23 +10,28 @@ namespace ui { -static gpointer ax_platform_atk_hyperlink_parent_class = nullptr; +struct _AXPlatformAtkHyperlinkPrivate { + AXPlatformNodeAuraLinux* platform_node = nullptr; + base::Optional<int> end_index; + base::Optional<int> start_index; +}; + +static gpointer kAXPlatformAtkHyperlinkParentClass = nullptr; static AXPlatformNodeAuraLinux* ToAXPlatformNodeAuraLinux( AXPlatformAtkHyperlink* atk_hyperlink) { if (!atk_hyperlink) return nullptr; - - return atk_hyperlink->m_object; + return atk_hyperlink->priv->platform_node; } -static void ax_platform_atk_hyperlink_finalize(GObject* atk_hyperlink) { - G_OBJECT_CLASS(ax_platform_atk_hyperlink_parent_class) - ->finalize(atk_hyperlink); +static void AXPlatformAtkHyperlinkFinalize(GObject* self) { + AX_PLATFORM_ATK_HYPERLINK(self)->priv->~AXPlatformAtkHyperlinkPrivate(); + G_OBJECT_CLASS(kAXPlatformAtkHyperlinkParentClass)->finalize(self); } -static gchar* ax_platform_atk_hyperlink_get_uri(AtkHyperlink* atk_hyperlink, - gint index) { +static gchar* AXPlatformAtkHyperlinkGetUri(AtkHyperlink* atk_hyperlink, + gint index) { AXPlatformNodeAuraLinux* obj = ToAXPlatformNodeAuraLinux(AX_PLATFORM_ATK_HYPERLINK(atk_hyperlink)); if (!obj) @@ -39,9 +44,8 @@ static gchar* ax_platform_atk_hyperlink_get_uri(AtkHyperlink* atk_hyperlink, obj->GetStringAttribute(ax::mojom::StringAttribute::kUrl).c_str()); } -static AtkObject* ax_platform_atk_hyperlink_get_object( - AtkHyperlink* atk_hyperlink, - gint index) { +static AtkObject* AXPlatformAtkHyperlinkGetObject(AtkHyperlink* atk_hyperlink, + gint index) { AXPlatformNodeAuraLinux* obj = ToAXPlatformNodeAuraLinux(AX_PLATFORM_ATK_HYPERLINK(atk_hyperlink)); if (!obj) @@ -53,45 +57,57 @@ static AtkObject* ax_platform_atk_hyperlink_get_object( return ATK_OBJECT(obj->GetNativeViewAccessible()); } -static gint ax_platform_atk_hyperlink_get_n_anchors( - AtkHyperlink* atk_hyperlink) { +static gint AXPlatformAtkHyperlinkGetNAnchors(AtkHyperlink* atk_hyperlink) { AXPlatformNodeAuraLinux* obj = ToAXPlatformNodeAuraLinux(AX_PLATFORM_ATK_HYPERLINK(atk_hyperlink)); return obj ? 1 : 0; } -static gboolean ax_platform_atk_hyperlink_is_valid( - AtkHyperlink* atk_hyperlink) { +static gboolean AXPlatformAtkHyperlinkIsValid(AtkHyperlink* atk_hyperlink) { AXPlatformNodeAuraLinux* obj = ToAXPlatformNodeAuraLinux(AX_PLATFORM_ATK_HYPERLINK(atk_hyperlink)); return obj ? TRUE : FALSE; } -static gboolean ax_platform_atk_hyperlink_is_selected_link( +static gboolean AXPlatformAtkHyperlinkIsSelectedLink( AtkHyperlink* atk_hyperlink) { AXPlatformNodeAuraLinux* obj = ToAXPlatformNodeAuraLinux(AX_PLATFORM_ATK_HYPERLINK(atk_hyperlink)); - if (!obj) return false; return obj->GetDelegate()->GetFocus() == obj->GetNativeViewAccessible(); } -static void ax_platform_atk_hyperlink_class_init(AtkHyperlinkClass* klass) { - GObjectClass* gobject_class = G_OBJECT_CLASS(klass); - ax_platform_atk_hyperlink_parent_class = g_type_class_peek_parent(klass); +static int AXPlatformAtkHyperlinkGetStartIndex(AtkHyperlink* atk_hyperlink) { + g_return_val_if_fail(IS_AX_PLATFORM_ATK_HYPERLINK(atk_hyperlink), 0); + AXPlatformAtkHyperlink* link = AX_PLATFORM_ATK_HYPERLINK(atk_hyperlink); + return link->priv->start_index ? *link->priv->start_index : 0; +} - gobject_class->finalize = ax_platform_atk_hyperlink_finalize; - klass->get_uri = ax_platform_atk_hyperlink_get_uri; - klass->get_object = ax_platform_atk_hyperlink_get_object; - klass->is_valid = ax_platform_atk_hyperlink_is_valid; - klass->get_n_anchors = ax_platform_atk_hyperlink_get_n_anchors; - klass->is_selected_link = ax_platform_atk_hyperlink_is_selected_link; - // TODO(jose.dapena) implement get_start_index and get_end_index methods - // that should provide the range of the link in the embedding text. +static int AXPlatformAtkHyperlinkGetEndIndex(AtkHyperlink* atk_hyperlink) { + g_return_val_if_fail(IS_AX_PLATFORM_ATK_HYPERLINK(atk_hyperlink), 0); + AXPlatformAtkHyperlink* link = AX_PLATFORM_ATK_HYPERLINK(atk_hyperlink); + return link->priv->end_index ? *link->priv->end_index : 0; +} + +static void AXPlatformAtkHyperlinkClassInit(AtkHyperlinkClass* klass) { + GObjectClass* gobject_class = G_OBJECT_CLASS(klass); + kAXPlatformAtkHyperlinkParentClass = g_type_class_peek_parent(klass); + + g_type_class_add_private(gobject_class, + sizeof(AXPlatformAtkHyperlinkPrivate)); + + gobject_class->finalize = AXPlatformAtkHyperlinkFinalize; + klass->get_uri = AXPlatformAtkHyperlinkGetUri; + klass->get_object = AXPlatformAtkHyperlinkGetObject; + klass->is_valid = AXPlatformAtkHyperlinkIsValid; + klass->get_n_anchors = AXPlatformAtkHyperlinkGetNAnchors; + klass->is_selected_link = AXPlatformAtkHyperlinkIsSelectedLink; + klass->get_start_index = AXPlatformAtkHyperlinkGetStartIndex; + klass->get_end_index = AXPlatformAtkHyperlinkGetEndIndex; } // @@ -208,10 +224,27 @@ static void atk_action_interface_init(AtkActionIface* iface) { iface->get_localized_name = ax_platform_atk_hyperlink_get_localized_name; } -void ax_platform_atk_hyperlink_set_object(AXPlatformAtkHyperlink* atk_hyperlink, - AXPlatformNodeAuraLinux* obj) { +void ax_platform_atk_hyperlink_set_object( + AXPlatformAtkHyperlink* atk_hyperlink, + AXPlatformNodeAuraLinux* platform_node) { g_return_if_fail(AX_PLATFORM_ATK_HYPERLINK(atk_hyperlink)); - atk_hyperlink->m_object = obj; + atk_hyperlink->priv->platform_node = platform_node; +} + +void ax_platform_atk_hyperlink_set_indices( + AXPlatformAtkHyperlink* atk_hyperlink, + int start_index, + int end_index) { + atk_hyperlink->priv->start_index = start_index; + atk_hyperlink->priv->end_index = end_index; +} + +static void AXPlatformAtkHyperlinkInit(AXPlatformAtkHyperlink* self, gpointer) { + AXPlatformAtkHyperlinkPrivate* priv = + G_TYPE_INSTANCE_GET_PRIVATE(self, ax_platform_atk_hyperlink_get_type(), + AXPlatformAtkHyperlinkPrivate); + self->priv = priv; + new (priv) AXPlatformAtkHyperlinkPrivate(); } GType ax_platform_atk_hyperlink_get_type() { @@ -224,12 +257,12 @@ GType ax_platform_atk_hyperlink_get_type() { sizeof(AXPlatformAtkHyperlinkClass), (GBaseInitFunc) nullptr, (GBaseFinalizeFunc) nullptr, - (GClassInitFunc)ax_platform_atk_hyperlink_class_init, + (GClassInitFunc)AXPlatformAtkHyperlinkClassInit, (GClassFinalizeFunc) nullptr, nullptr, /* class data */ sizeof(AXPlatformAtkHyperlink), /* instance size */ 0, /* nb preallocs */ - (GInstanceInitFunc) nullptr, + (GInstanceInitFunc)AXPlatformAtkHyperlinkInit, nullptr /* value table */ }; diff --git a/chromium/ui/accessibility/platform/ax_platform_atk_hyperlink.h b/chromium/ui/accessibility/platform/ax_platform_atk_hyperlink.h index 9704f8b5553..733d418ec20 100644 --- a/chromium/ui/accessibility/platform/ax_platform_atk_hyperlink.h +++ b/chromium/ui/accessibility/platform/ax_platform_atk_hyperlink.h @@ -30,10 +30,13 @@ G_BEGIN_DECLS typedef struct _AXPlatformAtkHyperlink AXPlatformAtkHyperlink; typedef struct _AXPlatformAtkHyperlinkClass AXPlatformAtkHyperlinkClass; +typedef struct _AXPlatformAtkHyperlinkPrivate AXPlatformAtkHyperlinkPrivate; struct _AXPlatformAtkHyperlink { AtkHyperlink parent; - AXPlatformNodeAuraLinux* m_object; + + /*< private >*/ + AXPlatformAtkHyperlinkPrivate* priv; }; struct _AXPlatformAtkHyperlinkClass { @@ -43,6 +46,10 @@ struct _AXPlatformAtkHyperlinkClass { GType ax_platform_atk_hyperlink_get_type(void) G_GNUC_CONST; void ax_platform_atk_hyperlink_set_object(AXPlatformAtkHyperlink* hyperlink, AXPlatformNodeAuraLinux* obj); +void ax_platform_atk_hyperlink_set_indices( + AXPlatformAtkHyperlink* atk_hyperlink, + int start_index, + int end_index); G_END_DECLS diff --git a/chromium/ui/accessibility/platform/ax_platform_node.h b/chromium/ui/accessibility/platform/ax_platform_node.h index 9430ddbb50a..278107bc458 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node.h +++ b/chromium/ui/accessibility/platform/ax_platform_node.h @@ -11,8 +11,8 @@ #include "build/build_config.h" #include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_export.h" +#include "ui/accessibility/ax_mode.h" #include "ui/accessibility/ax_mode_observer.h" -#include "ui/accessibility/ax_modes.h" #include "ui/gfx/native_widget_types.h" namespace ui { diff --git a/chromium/ui/accessibility/platform/ax_platform_node_auralinux.cc b/chromium/ui/accessibility/platform/ax_platform_node_auralinux.cc index 949ca8bb3a5..c61d9ea4e11 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_auralinux.cc +++ b/chromium/ui/accessibility/platform/ax_platform_node_auralinux.cc @@ -15,6 +15,7 @@ #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" #include "ui/accessibility/ax_action_data.h" +#include "ui/accessibility/ax_mode_observer.h" #include "ui/accessibility/ax_node_data.h" #include "ui/accessibility/ax_text_utils.h" #include "ui/accessibility/ax_tree_data.h" @@ -23,6 +24,8 @@ #include "ui/accessibility/platform/ax_platform_node_delegate.h" #include "ui/gfx/geometry/rect_conversions.h" +namespace ui { + // // ax_platform_node_auralinux AtkObject definition and implementation. // @@ -201,7 +204,7 @@ constexpr AtkRole kAtkFootnoteRole = ATK_ROLE_LIST_ITEM; struct _AXPlatformNodeAuraLinuxObject { AtkObject parent; - ui::AXPlatformNodeAuraLinux* m_object; + AXPlatformNodeAuraLinux* m_object; }; struct _AXPlatformNodeAuraLinuxClass { @@ -210,9 +213,9 @@ struct _AXPlatformNodeAuraLinuxClass { GType ax_platform_node_auralinux_get_type(); -static gpointer ax_platform_node_auralinux_parent_class = nullptr; +static gpointer kAXPlatformNodeAuraLinuxParentClass = nullptr; -static ui::AXPlatformNodeAuraLinux* ToAXPlatformNodeAuraLinux( +static AXPlatformNodeAuraLinux* ToAXPlatformNodeAuraLinux( AXPlatformNodeAuraLinuxObject* atk_object) { if (!atk_object) return nullptr; @@ -220,7 +223,7 @@ static ui::AXPlatformNodeAuraLinux* ToAXPlatformNodeAuraLinux( return atk_object->m_object; } -static ui::AXPlatformNodeAuraLinux* AtkObjectToAXPlatformNodeAuraLinux( +static AXPlatformNodeAuraLinux* AtkObjectToAXPlatformNodeAuraLinux( AtkObject* atk_object) { if (!atk_object) return nullptr; @@ -231,9 +234,8 @@ static ui::AXPlatformNodeAuraLinux* AtkObjectToAXPlatformNodeAuraLinux( return nullptr; } -static const gchar* ax_platform_node_auralinux_get_name(AtkObject* atk_object) { - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); +static const gchar* AXPlatformNodeAuraLinuxGetName(AtkObject* atk_object) { + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return nullptr; @@ -245,10 +247,9 @@ static const gchar* ax_platform_node_auralinux_get_name(AtkObject* atk_object) { return obj->GetStringAttribute(ax::mojom::StringAttribute::kName).c_str(); } -static const gchar* ax_platform_node_auralinux_get_description( +static const gchar* AXPlatformNodeAuraLinuxGetDescription( AtkObject* atk_object) { - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return nullptr; @@ -256,10 +257,8 @@ static const gchar* ax_platform_node_auralinux_get_description( .c_str(); } -static gint ax_platform_node_auralinux_get_index_in_parent( - AtkObject* atk_object) { - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); +static gint AXPlatformNodeAuraLinuxGetIndexInParent(AtkObject* atk_object) { + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return -1; @@ -267,28 +266,25 @@ static gint ax_platform_node_auralinux_get_index_in_parent( return obj->GetIndexInParent(); } -static AtkObject* ax_platform_node_auralinux_get_parent(AtkObject* atk_object) { - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); +static AtkObject* AXPlatformNodeAuraLinuxGetParent(AtkObject* atk_object) { + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return nullptr; return obj->GetParent(); } -static gint ax_platform_node_auralinux_get_n_children(AtkObject* atk_object) { - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); +static gint AXPlatformNodeAuraLinuxGetNChildren(AtkObject* atk_object) { + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return 0; return obj->GetChildCount(); } -static AtkObject* ax_platform_node_auralinux_ref_child( - AtkObject* atk_object, gint index) { - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); +static AtkObject* AXPlatformNodeAuraLinuxRefChild(AtkObject* atk_object, + gint index) { + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return nullptr; @@ -298,13 +294,12 @@ static AtkObject* ax_platform_node_auralinux_ref_child( return result; } -static AtkRelationSet* ax_platform_node_auralinux_ref_relation_set( +static AtkRelationSet* AXPlatformNodeAuraLinuxRefRelationSet( AtkObject* atk_object) { - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); AtkRelationSet* atk_relation_set = - ATK_OBJECT_CLASS(ax_platform_node_auralinux_parent_class)-> - ref_relation_set(atk_object); + ATK_OBJECT_CLASS(kAXPlatformNodeAuraLinuxParentClass) + ->ref_relation_set(atk_object); if (!obj) return atk_relation_set; @@ -313,33 +308,28 @@ static AtkRelationSet* ax_platform_node_auralinux_ref_relation_set( return atk_relation_set; } -static AtkAttributeSet* ax_platform_node_auralinux_get_attributes( +static AtkAttributeSet* AXPlatformNodeAuraLinuxGetAttributes( AtkObject* atk_object) { - - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return nullptr; return obj->GetAtkAttributes(); } -static AtkRole ax_platform_node_auralinux_get_role(AtkObject* atk_object) { - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); +static AtkRole AXPlatformNodeAuraLinuxGetRole(AtkObject* atk_object) { + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return ATK_ROLE_INVALID; return obj->GetAtkRole(); } -static AtkStateSet* ax_platform_node_auralinux_ref_state_set( - AtkObject* atk_object) { +static AtkStateSet* AXPlatformNodeAuraLinuxRefStateSet(AtkObject* atk_object) { AtkStateSet* atk_state_set = - ATK_OBJECT_CLASS(ax_platform_node_auralinux_parent_class)-> - ref_state_set(atk_object); + ATK_OBJECT_CLASS(kAXPlatformNodeAuraLinuxParentClass) + ->ref_state_set(atk_object); - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) { atk_state_set_add_state(atk_state_set, ATK_STATE_DEFUNCT); } else { @@ -368,10 +358,12 @@ static gfx::Point FindAtkObjectParentCoords(AtkObject* atk_object) { return FindAtkObjectParentCoords(atk_object); } -static void ax_platform_node_auralinux_get_extents(AtkComponent* atk_component, - gint* x, gint* y, - gint* width, gint* height, - AtkCoordType coord_type) { +static void AXPlatformNodeAuraLinuxGetExtents(AtkComponent* atk_component, + gint* x, + gint* y, + gint* width, + gint* height, + AtkCoordType coord_type) { g_return_if_fail(ATK_IS_COMPONENT(atk_component)); if (x) @@ -384,17 +376,17 @@ static void ax_platform_node_auralinux_get_extents(AtkComponent* atk_component, *height = 0; AtkObject* atk_object = ATK_OBJECT(atk_component); - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return; obj->GetExtents(x, y, width, height, coord_type); } -static void ax_platform_node_auralinux_get_position(AtkComponent* atk_component, - gint* x, gint* y, - AtkCoordType coord_type) { +static void AXPlatformNodeAuraLinuxGetPosition(AtkComponent* atk_component, + gint* x, + gint* y, + AtkCoordType coord_type) { g_return_if_fail(ATK_IS_COMPONENT(atk_component)); if (x) @@ -403,16 +395,16 @@ static void ax_platform_node_auralinux_get_position(AtkComponent* atk_component, *y = 0; AtkObject* atk_object = ATK_OBJECT(atk_component); - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return; obj->GetPosition(x, y, coord_type); } -static void ax_platform_node_auralinux_get_size(AtkComponent* atk_component, - gint* width, gint* height) { +static void AXPlatformNodeAuraLinuxGetSize(AtkComponent* atk_component, + gint* width, + gint* height) { g_return_if_fail(ATK_IS_COMPONENT(atk_component)); if (width) @@ -421,23 +413,21 @@ static void ax_platform_node_auralinux_get_size(AtkComponent* atk_component, *height = 0; AtkObject* atk_object = ATK_OBJECT(atk_component); - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return; obj->GetSize(width, height); } -static AtkObject* ax_platform_node_auralinux_ref_accessible_at_point( +static AtkObject* AXPlatformNodeAuraLinuxRefAccessibleAtPoint( AtkComponent* atk_component, gint x, gint y, AtkCoordType coord_type) { g_return_val_if_fail(ATK_IS_COMPONENT(atk_component), nullptr); AtkObject* atk_object = ATK_OBJECT(atk_component); - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return nullptr; @@ -447,93 +437,83 @@ static AtkObject* ax_platform_node_auralinux_ref_accessible_at_point( return result; } -static gboolean ax_platform_node_auralinux_grab_focus( - AtkComponent* atk_component) { +static gboolean AXPlatformNodeAuraLinuxGrabFocus(AtkComponent* atk_component) { g_return_val_if_fail(ATK_IS_COMPONENT(atk_component), FALSE); AtkObject* atk_object = ATK_OBJECT(atk_component); - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return FALSE; return obj->GrabFocus(); } -void ax_component_interface_base_init(AtkComponentIface* iface) { - iface->get_extents = ax_platform_node_auralinux_get_extents; - iface->get_position = ax_platform_node_auralinux_get_position; - iface->get_size = ax_platform_node_auralinux_get_size; - iface->ref_accessible_at_point = - ax_platform_node_auralinux_ref_accessible_at_point; - iface->grab_focus = ax_platform_node_auralinux_grab_focus; +void AXComponentInterfaceBaseInit(AtkComponentIface* iface) { + iface->get_extents = AXPlatformNodeAuraLinuxGetExtents; + iface->get_position = AXPlatformNodeAuraLinuxGetPosition; + iface->get_size = AXPlatformNodeAuraLinuxGetSize; + iface->ref_accessible_at_point = AXPlatformNodeAuraLinuxRefAccessibleAtPoint; + iface->grab_focus = AXPlatformNodeAuraLinuxGrabFocus; } static const GInterfaceInfo ComponentInfo = { - reinterpret_cast<GInterfaceInitFunc>(ax_component_interface_base_init), 0, 0 -}; + reinterpret_cast<GInterfaceInitFunc>(AXComponentInterfaceBaseInit), 0, 0}; // // AtkAction interface // -static gboolean ax_platform_node_auralinux_do_action(AtkAction* atk_action, - gint index) { +static gboolean AXPlatformNodeAuraLinuxDoAction(AtkAction* atk_action, + gint index) { g_return_val_if_fail(ATK_IS_ACTION(atk_action), FALSE); g_return_val_if_fail(!index, FALSE); AtkObject* atk_object = ATK_OBJECT(atk_action); - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return FALSE; return obj->DoDefaultAction(); } -static gint ax_platform_node_auralinux_get_n_actions(AtkAction* atk_action) { +static gint AXPlatformNodeAuraLinuxGetNActions(AtkAction* atk_action) { g_return_val_if_fail(ATK_IS_ACTION(atk_action), 0); AtkObject* atk_object = ATK_OBJECT(atk_action); - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return 0; return 1; } -static const gchar* ax_platform_node_auralinux_get_action_description( - AtkAction*, - gint) { +static const gchar* AXPlatformNodeAuraLinuxGetActionDescription(AtkAction*, + gint) { // Not implemented. Right now Orca does not provide this and // Chromium is not providing a string for the action description. return nullptr; } -static const gchar* ax_platform_node_auralinux_get_action_name( - AtkAction* atk_action, - gint index) { +static const gchar* AXPlatformNodeAuraLinuxGetActionName(AtkAction* atk_action, + gint index) { g_return_val_if_fail(ATK_IS_ACTION(atk_action), nullptr); g_return_val_if_fail(!index, nullptr); AtkObject* atk_object = ATK_OBJECT(atk_action); - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return nullptr; return obj->GetDefaultActionName(); } -static const gchar* ax_platform_node_auralinux_get_action_keybinding( +static const gchar* AXPlatformNodeAuraLinuxGetActionKeybinding( AtkAction* atk_action, gint index) { g_return_val_if_fail(ATK_IS_ACTION(atk_action), nullptr); g_return_val_if_fail(!index, nullptr); AtkObject* atk_object = ATK_OBJECT(atk_action); - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return nullptr; @@ -541,85 +521,79 @@ static const gchar* ax_platform_node_auralinux_get_action_keybinding( .c_str(); } -void ax_action_interface_base_init(AtkActionIface* iface) { - iface->do_action = ax_platform_node_auralinux_do_action; - iface->get_n_actions = ax_platform_node_auralinux_get_n_actions; - iface->get_description = ax_platform_node_auralinux_get_action_description; - iface->get_name = ax_platform_node_auralinux_get_action_name; - iface->get_keybinding = ax_platform_node_auralinux_get_action_keybinding; +void AXActionInterfaceBaseInit(AtkActionIface* iface) { + iface->do_action = AXPlatformNodeAuraLinuxDoAction; + iface->get_n_actions = AXPlatformNodeAuraLinuxGetNActions; + iface->get_description = AXPlatformNodeAuraLinuxGetActionDescription; + iface->get_name = AXPlatformNodeAuraLinuxGetActionName; + iface->get_keybinding = AXPlatformNodeAuraLinuxGetActionKeybinding; } static const GInterfaceInfo ActionInfo = { - reinterpret_cast<GInterfaceInitFunc>(ax_action_interface_base_init), - nullptr, nullptr}; + reinterpret_cast<GInterfaceInitFunc>(AXActionInterfaceBaseInit), nullptr, + nullptr}; // AtkDocument interface. -static const gchar* ax_platform_node_auralinux_get_document_attribute_value( +static const gchar* AXPlatformNodeAuraLinuGetDocumentAttributeValue( AtkDocument* atk_doc, const gchar* attribute) { g_return_val_if_fail(ATK_IS_DOCUMENT(atk_doc), nullptr); AtkObject* atk_object = ATK_OBJECT(atk_doc); - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return nullptr; return obj->GetDocumentAttributeValue(attribute); } -static AtkAttributeSet* ax_platform_node_auralinux_get_document_attributes( +static AtkAttributeSet* AXPlatformNodeAuraLinuxGetDocumentAttributes( AtkDocument* atk_doc) { g_return_val_if_fail(ATK_IS_DOCUMENT(atk_doc), 0); AtkObject* atk_object = ATK_OBJECT(atk_doc); - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return nullptr; return obj->GetDocumentAttributes(); } -void ax_document_interface_base_init(AtkDocumentIface* iface) { +void AXDocumentInterfaceBaseInit(AtkDocumentIface* iface) { iface->get_document_attribute_value = - ax_platform_node_auralinux_get_document_attribute_value; - iface->get_document_attributes = - ax_platform_node_auralinux_get_document_attributes; + AXPlatformNodeAuraLinuGetDocumentAttributeValue; + iface->get_document_attributes = AXPlatformNodeAuraLinuxGetDocumentAttributes; } static const GInterfaceInfo DocumentInfo = { - reinterpret_cast<GInterfaceInitFunc>(ax_document_interface_base_init), - nullptr, nullptr}; + reinterpret_cast<GInterfaceInitFunc>(AXDocumentInterfaceBaseInit), nullptr, + nullptr}; // // AtkImage interface. // -static void ax_platform_node_auralinux_get_image_position( - AtkImage* atk_img, - gint* x, - gint* y, - AtkCoordType coord_type) { +static void AXPlatformNodeGetImagePosition(AtkImage* atk_img, + gint* x, + gint* y, + AtkCoordType coord_type) { g_return_if_fail(ATK_IMAGE(atk_img)); AtkObject* atk_object = ATK_OBJECT(atk_img); - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return; obj->GetPosition(x, y, coord_type); } -static const gchar* ax_platform_node_auralinux_get_image_description( +static const gchar* AXPlatformNodeAuraLinuxGetImageDescription( AtkImage* atk_img) { g_return_val_if_fail(ATK_IMAGE(atk_img), nullptr); AtkObject* atk_object = ATK_OBJECT(atk_img); - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return nullptr; @@ -627,42 +601,39 @@ static const gchar* ax_platform_node_auralinux_get_image_description( .c_str(); } -static void ax_platform_node_auralinux_get_image_size(AtkImage* atk_img, - gint* width, - gint* height) { +static void AXPlatformNodeAuraLinuxGetImageSize(AtkImage* atk_img, + gint* width, + gint* height) { g_return_if_fail(ATK_IMAGE(atk_img)); AtkObject* atk_object = ATK_OBJECT(atk_img); - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return; obj->GetSize(width, height); } -void ax_image_interface_base_init(AtkImageIface* iface) { - iface->get_image_position = ax_platform_node_auralinux_get_image_position; - iface->get_image_description = - ax_platform_node_auralinux_get_image_description; - iface->get_image_size = ax_platform_node_auralinux_get_image_size; +void AXImageInterfaceBaseInit(AtkImageIface* iface) { + iface->get_image_position = AXPlatformNodeGetImagePosition; + iface->get_image_description = AXPlatformNodeAuraLinuxGetImageDescription; + iface->get_image_size = AXPlatformNodeAuraLinuxGetImageSize; } static const GInterfaceInfo ImageInfo = { - reinterpret_cast<GInterfaceInitFunc>(ax_image_interface_base_init), nullptr, + reinterpret_cast<GInterfaceInitFunc>(AXImageInterfaceBaseInit), nullptr, nullptr}; // // AtkValue interface // -static void ax_platform_node_auralinux_get_current_value(AtkValue* atk_value, - GValue* value) { +static void AXPlatformNodeAuraLinuxGetCurrentValue(AtkValue* atk_value, + GValue* value) { g_return_if_fail(ATK_VALUE(atk_value)); AtkObject* atk_object = ATK_OBJECT(atk_value); - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return; @@ -670,13 +641,12 @@ static void ax_platform_node_auralinux_get_current_value(AtkValue* atk_value, value); } -static void ax_platform_node_auralinux_get_minimum_value(AtkValue* atk_value, - GValue* value) { +static void AXPlatformNodeAuraLinuxGetMinimumValue(AtkValue* atk_value, + GValue* value) { g_return_if_fail(ATK_VALUE(atk_value)); AtkObject* atk_object = ATK_OBJECT(atk_value); - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return; @@ -684,13 +654,12 @@ static void ax_platform_node_auralinux_get_minimum_value(AtkValue* atk_value, value); } -static void ax_platform_node_auralinux_get_maximum_value(AtkValue* atk_value, - GValue* value) { +static void AXPlatformNodeAuraLinuxGetMaximumValue(AtkValue* atk_value, + GValue* value) { g_return_if_fail(ATK_VALUE(atk_value)); AtkObject* atk_object = ATK_OBJECT(atk_value); - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return; @@ -698,14 +667,12 @@ static void ax_platform_node_auralinux_get_maximum_value(AtkValue* atk_value, value); } -static void ax_platform_node_auralinux_get_minimum_increment( - AtkValue* atk_value, - GValue* value) { +static void AXPlatformNodeAuraLinuxGetMinimumIncrement(AtkValue* atk_value, + GValue* value) { g_return_if_fail(ATK_VALUE(atk_value)); AtkObject* atk_object = ATK_OBJECT(atk_value); - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return; @@ -713,29 +680,27 @@ static void ax_platform_node_auralinux_get_minimum_increment( value); } -static void ax_value_interface_base_init(AtkValueIface* iface) { - iface->get_current_value = ax_platform_node_auralinux_get_current_value; - iface->get_maximum_value = ax_platform_node_auralinux_get_maximum_value; - iface->get_minimum_value = ax_platform_node_auralinux_get_minimum_value; - iface->get_minimum_increment = - ax_platform_node_auralinux_get_minimum_increment; +static void AXValueInterfaceBaseInit(AtkValueIface* iface) { + iface->get_current_value = AXPlatformNodeAuraLinuxGetCurrentValue; + iface->get_maximum_value = AXPlatformNodeAuraLinuxGetMaximumValue; + iface->get_minimum_value = AXPlatformNodeAuraLinuxGetMinimumValue; + iface->get_minimum_increment = AXPlatformNodeAuraLinuxGetMinimumIncrement; } static const GInterfaceInfo ValueInfo = { - reinterpret_cast<GInterfaceInitFunc>(ax_value_interface_base_init), nullptr, + reinterpret_cast<GInterfaceInitFunc>(AXValueInterfaceBaseInit), nullptr, nullptr}; // // AtkHyperlinkImpl interface. // -static AtkHyperlink* ax_platform_node_auralinux_get_hyperlink( +static AtkHyperlink* AXPlatformNodeAuraLinuxGetHyperlink( AtkHyperlinkImpl* atk_hyperlink_impl) { g_return_val_if_fail(ATK_HYPERLINK_IMPL(atk_hyperlink_impl), 0); AtkObject* atk_object = ATK_OBJECT(atk_hyperlink_impl); - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return 0; @@ -745,24 +710,85 @@ static AtkHyperlink* ax_platform_node_auralinux_get_hyperlink( return atk_hyperlink; } -void ax_hyperlink_impl_interface_base_init(AtkHyperlinkImplIface* iface) { - iface->get_hyperlink = ax_platform_node_auralinux_get_hyperlink; +void AXHyperlinkImplInterfaceBaseInit(AtkHyperlinkImplIface* iface) { + iface->get_hyperlink = AXPlatformNodeAuraLinuxGetHyperlink; } static const GInterfaceInfo HyperlinkImplInfo = { - reinterpret_cast<GInterfaceInitFunc>(ax_hyperlink_impl_interface_base_init), + reinterpret_cast<GInterfaceInitFunc>(AXHyperlinkImplInterfaceBaseInit), nullptr, nullptr}; // +// AtkHypertext interface. +// + +static AtkHyperlink* AXPlatformNodeAuraLinuxHypertextGetLink( + AtkHypertext* hypertext, + int index) { + g_return_val_if_fail(ATK_HYPERTEXT(hypertext), 0); + auto* obj = AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(hypertext)); + if (!obj) + return nullptr; + + const AXHypertext& ax_hypertext = obj->GetHypertext(); + if (index > static_cast<int>(ax_hypertext.hyperlinks.size()) || index < 0) + return nullptr; + + int32_t id = ax_hypertext.hyperlinks[index]; + auto* link = AXPlatformNodeAuraLinux::GetFromUniqueId(id); + if (!link) + return nullptr; + + AtkHyperlink* atk_hyperlink = link->GetAtkHyperlink(); + for (const auto& key_value : ax_hypertext.hyperlink_offset_to_index) { + if (key_value.second == index) { + ax_platform_atk_hyperlink_set_indices( + AX_PLATFORM_ATK_HYPERLINK(atk_hyperlink), key_value.first, + key_value.first + 1); + } + } + + return atk_hyperlink; +} + +static int AXPlatformNodeAuraLinuxGetNLinks(AtkHypertext* hypertext) { + g_return_val_if_fail(ATK_HYPERTEXT(hypertext), 0); + AXPlatformNodeAuraLinux* obj = + AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(hypertext)); + return obj ? obj->GetHypertext().hyperlinks.size() : 0; +} + +static int AXPlatformNodeAuraLinuxGetLinkIndex(AtkHypertext* hypertext, + int char_index) { + g_return_val_if_fail(ATK_HYPERTEXT(hypertext), 0); + AXPlatformNodeAuraLinux* obj = + AtkObjectToAXPlatformNodeAuraLinux(ATK_OBJECT(hypertext)); + + auto it = obj->GetHypertext().hyperlink_offset_to_index.find(char_index); + if (it == obj->GetHypertext().hyperlink_offset_to_index.end()) + return -1; + return it->second; +} + +void AXHypertextInterfaceBaseInit(AtkHypertextIface* iface) { + iface->get_link = AXPlatformNodeAuraLinuxHypertextGetLink; + iface->get_n_links = AXPlatformNodeAuraLinuxGetNLinks; + iface->get_link_index = AXPlatformNodeAuraLinuxGetLinkIndex; +} + +static const GInterfaceInfo HypertextInfo = { + reinterpret_cast<GInterfaceInitFunc>(AXHypertextInterfaceBaseInit), nullptr, + nullptr}; + +// // AtkText interface. // -static gchar* ax_platform_node_auralinux_get_text(AtkText* atk_text, - gint start_offset, - gint end_offset) { +static gchar* AXPlatformNodeAuraLinuxGetText(AtkText* atk_text, + gint start_offset, + gint end_offset) { AtkObject* atk_object = ATK_OBJECT(atk_text); - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return nullptr; @@ -773,10 +799,9 @@ static gchar* ax_platform_node_auralinux_get_text(AtkText* atk_text, return g_utf8_substring(text.c_str(), start_offset, end_offset); } -static gint ax_platform_node_auralinux_get_character_count(AtkText* atk_text) { +static gint AXPlatformNodeAuraLinuxGetCharacterCount(AtkText* atk_text) { AtkObject* atk_object = ATK_OBJECT(atk_text); - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return 0; @@ -784,7 +809,7 @@ static gint ax_platform_node_auralinux_get_character_count(AtkText* atk_text) { return g_utf8_strlen(text.c_str(), -1); } -static AtkAttributeSet* ax_platform_node_auralinux_get_run_attributes( +static AtkAttributeSet* AXPlatformNodeAuraLinuxGetRunAttributes( AtkText* atk_text, gint offset, gint* start_offset, @@ -793,25 +818,146 @@ static AtkAttributeSet* ax_platform_node_auralinux_get_run_attributes( *end_offset = -1; AtkObject* atk_object = ATK_OBJECT(atk_text); - ui::AXPlatformNodeAuraLinux* obj = - AtkObjectToAXPlatformNodeAuraLinux(atk_object); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); if (!obj) return nullptr; *start_offset = 0; - *end_offset = ax_platform_node_auralinux_get_character_count(atk_text); + *end_offset = AXPlatformNodeAuraLinuxGetCharacterCount(atk_text); return nullptr; } -static void ax_text_interface_base_init(AtkTextIface* iface) { - iface->get_text = ax_platform_node_auralinux_get_text; - iface->get_run_attributes = ax_platform_node_auralinux_get_run_attributes; - iface->get_character_count = ax_platform_node_auralinux_get_character_count; +static gunichar AXPlatformNodeAuraLinuxGetCharacterAtOffset(AtkText* atk_text, + int offset) { + AtkObject* atk_object = ATK_OBJECT(atk_text); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); + if (!obj) + return 0; + + std::string text = obj->GetTextForATK(); + size_t limited_offset = std::max(0L, std::min(g_utf8_strlen(text.c_str(), -1), + static_cast<glong>(offset))); + + // According to the C++ documentation, the pointer returned by c_str() should + // be valid as long as any non-const operations are not performed on the + // std::string in question. + return g_utf8_get_char( + g_utf8_offset_to_pointer(text.c_str(), limited_offset)); +} + +// This function returns a single character as a UTF-8 encoded C string because +// the character may be encoded into more than one byte. +static char* AXPlatformNodeAuraLinuxGetCharacter(AtkText* atk_text, + int offset, + int* start_offset, + int* end_offset) { + *start_offset = -1; + *end_offset = -1; + + AtkObject* atk_object = ATK_OBJECT(atk_text); + AXPlatformNodeAuraLinux* obj = AtkObjectToAXPlatformNodeAuraLinux(atk_object); + if (!obj) + return nullptr; + + std::string text = obj->GetTextForATK(); + int text_length = static_cast<int>(g_utf8_strlen(text.c_str(), -1)); + *start_offset = std::max(0, std::min(text_length, offset)); + *end_offset = std::max(0, std::min(text_length, *start_offset + 1)); + + return g_utf8_substring(text.c_str(), *start_offset, *end_offset); +} + +static char* AXPlatformNodeAuraLinuxGetTextAtOffset( + AtkText* atk_text, + int offset, + AtkTextBoundary boundary_type, + int* start_offset, + int* end_offset) { + *start_offset = -1; + *end_offset = -1; + + if (boundary_type != ATK_TEXT_BOUNDARY_CHAR) { + NOTIMPLEMENTED(); + return nullptr; + } + + return AXPlatformNodeAuraLinuxGetCharacter(atk_text, offset, start_offset, + end_offset); +} + +static char* AXPlatformNodeAuraLinuxGetTextAfterOffset( + AtkText* atk_text, + int offset, + AtkTextBoundary boundary_type, + int* start_offset, + int* end_offset) { + *start_offset = -1; + *end_offset = -1; + + if (boundary_type != ATK_TEXT_BOUNDARY_CHAR) { + NOTIMPLEMENTED(); + return nullptr; + } + + return AXPlatformNodeAuraLinuxGetCharacter(atk_text, offset + 1, start_offset, + end_offset); +} + +static char* AXPlatformNodeAuraLinuxGetTextBeforeOffset( + AtkText* atk_text, + int offset, + AtkTextBoundary boundary_type, + int* start_offset, + int* end_offset) { + *start_offset = -1; + *end_offset = -1; + + if (boundary_type != ATK_TEXT_BOUNDARY_CHAR) { + NOTIMPLEMENTED(); + return nullptr; + } + + return AXPlatformNodeAuraLinuxGetCharacter(atk_text, offset - 1, start_offset, + end_offset); +} + +#if ATK_CHECK_VERSION(2, 10, 0) +static char* AXPlatformNodeAuraLinuxGetStringAtOffset( + AtkText* atk_text, + int offset, + AtkTextGranularity granularity, + int* start_offset, + int* end_offset) { + *start_offset = -1; + *end_offset = -1; + + if (granularity != ATK_TEXT_GRANULARITY_CHAR) { + NOTIMPLEMENTED(); + return nullptr; + } + + return AXPlatformNodeAuraLinuxGetCharacter(atk_text, offset, start_offset, + end_offset); +} +#endif + +static void AXTextInterfaceBaseInit(AtkTextIface* iface) { + iface->get_text = AXPlatformNodeAuraLinuxGetText; + iface->get_run_attributes = AXPlatformNodeAuraLinuxGetRunAttributes; + iface->get_character_count = AXPlatformNodeAuraLinuxGetCharacterCount; + iface->get_character_at_offset = AXPlatformNodeAuraLinuxGetCharacterAtOffset; + iface->get_text_after_offset = AXPlatformNodeAuraLinuxGetTextAfterOffset; + iface->get_text_before_offset = AXPlatformNodeAuraLinuxGetTextBeforeOffset; + iface->get_text_at_offset = AXPlatformNodeAuraLinuxGetTextAtOffset; + +#if ATK_CHECK_VERSION(2, 10, 0) + iface->get_string_at_offset = AXPlatformNodeAuraLinuxGetStringAtOffset; +#endif } static const GInterfaceInfo TextInfo = { - reinterpret_cast<GInterfaceInitFunc>(ax_text_interface_base_init), nullptr, + reinterpret_cast<GInterfaceInitFunc>(AXTextInterfaceBaseInit), nullptr, nullptr}; // @@ -819,41 +965,40 @@ static const GInterfaceInfo TextInfo = { // of the Atk* interfaces. // -static void ax_platform_node_auralinux_init(AtkObject* atk_object, - gpointer data) { - if (ATK_OBJECT_CLASS(ax_platform_node_auralinux_parent_class)->initialize) { - ATK_OBJECT_CLASS(ax_platform_node_auralinux_parent_class)->initialize( - atk_object, data); +static void AXPlatformNodeAuraLinuxInit(AtkObject* atk_object, gpointer data) { + if (ATK_OBJECT_CLASS(kAXPlatformNodeAuraLinuxParentClass)->initialize) { + ATK_OBJECT_CLASS(kAXPlatformNodeAuraLinuxParentClass) + ->initialize(atk_object, data); } AX_PLATFORM_NODE_AURALINUX(atk_object)->m_object = - reinterpret_cast<ui::AXPlatformNodeAuraLinux*>(data); + reinterpret_cast<AXPlatformNodeAuraLinux*>(data); } -static void ax_platform_node_auralinux_finalize(GObject* atk_object) { - G_OBJECT_CLASS(ax_platform_node_auralinux_parent_class)->finalize(atk_object); +static void AXPlatformNodeAuraLinuxFinalize(GObject* atk_object) { + G_OBJECT_CLASS(kAXPlatformNodeAuraLinuxParentClass)->finalize(atk_object); } -static void ax_platform_node_auralinux_class_init(AtkObjectClass* klass) { +static void AXPlatformNodeAuraLinuxClassInit(AtkObjectClass* klass) { GObjectClass* gobject_class = G_OBJECT_CLASS(klass); - ax_platform_node_auralinux_parent_class = g_type_class_peek_parent(klass); - - gobject_class->finalize = ax_platform_node_auralinux_finalize; - klass->initialize = ax_platform_node_auralinux_init; - klass->get_name = ax_platform_node_auralinux_get_name; - klass->get_description = ax_platform_node_auralinux_get_description; - klass->get_parent = ax_platform_node_auralinux_get_parent; - klass->get_n_children = ax_platform_node_auralinux_get_n_children; - klass->ref_child = ax_platform_node_auralinux_ref_child; - klass->get_role = ax_platform_node_auralinux_get_role; - klass->ref_state_set = ax_platform_node_auralinux_ref_state_set; - klass->get_index_in_parent = ax_platform_node_auralinux_get_index_in_parent; - klass->ref_relation_set = ax_platform_node_auralinux_ref_relation_set; - klass->get_attributes = ax_platform_node_auralinux_get_attributes; + kAXPlatformNodeAuraLinuxParentClass = g_type_class_peek_parent(klass); + + gobject_class->finalize = AXPlatformNodeAuraLinuxFinalize; + klass->initialize = AXPlatformNodeAuraLinuxInit; + klass->get_name = AXPlatformNodeAuraLinuxGetName; + klass->get_description = AXPlatformNodeAuraLinuxGetDescription; + klass->get_parent = AXPlatformNodeAuraLinuxGetParent; + klass->get_n_children = AXPlatformNodeAuraLinuxGetNChildren; + klass->ref_child = AXPlatformNodeAuraLinuxRefChild; + klass->get_role = AXPlatformNodeAuraLinuxGetRole; + klass->ref_state_set = AXPlatformNodeAuraLinuxRefStateSet; + klass->get_index_in_parent = AXPlatformNodeAuraLinuxGetIndexInParent; + klass->ref_relation_set = AXPlatformNodeAuraLinuxRefRelationSet; + klass->get_attributes = AXPlatformNodeAuraLinuxGetAttributes; } GType ax_platform_node_auralinux_get_type() { - ui::AXPlatformNodeAuraLinux::EnsureGTypeInit(); + AXPlatformNodeAuraLinux::EnsureGTypeInit(); static volatile gsize type_volatile = 0; if (g_once_init_enter(&type_volatile)) { @@ -861,7 +1006,7 @@ GType ax_platform_node_auralinux_get_type() { sizeof(AXPlatformNodeAuraLinuxClass), (GBaseInitFunc) nullptr, (GBaseFinalizeFunc) nullptr, - (GClassInitFunc)ax_platform_node_auralinux_class_init, + (GClassInitFunc)AXPlatformNodeAuraLinuxClassInit, (GClassFinalizeFunc) nullptr, nullptr, /* class data */ sizeof(AXPlatformNodeAuraLinuxObject), /* instance size */ @@ -878,8 +1023,7 @@ GType ax_platform_node_auralinux_get_type() { return type_volatile; } -void ax_platform_node_auralinux_detach( - AXPlatformNodeAuraLinuxObject* atk_object) { +void AXPlatformNodeAuraLinuxDetach(AXPlatformNodeAuraLinuxObject* atk_object) { if (atk_object->m_object) { atk_object_notify_state_change(ATK_OBJECT(atk_object), ATK_STATE_DEFUNCT, TRUE); @@ -889,8 +1033,6 @@ void ax_platform_node_auralinux_detach( G_END_DECLS -namespace ui { - void AXPlatformNodeAuraLinux::EnsureGTypeInit() { #if !GLIB_CHECK_VERSION(2, 36, 0) static bool first_time = true; @@ -910,6 +1052,12 @@ const char* AXPlatformNodeAuraLinux::GetUniqueAccessibilityGTypeName( return name; } +static bool IsRoleWithValueInterface(AtkRole role) { + return role == ATK_ROLE_SCROLL_BAR || role == ATK_ROLE_SLIDER || + role == ATK_ROLE_PROGRESS_BAR || role == ATK_ROLE_SEPARATOR || + role == ATK_ROLE_SPIN_BUTTON; +} + int AXPlatformNodeAuraLinux::GetGTypeInterfaceMask() { int interface_mask = 0; @@ -925,11 +1073,12 @@ int AXPlatformNodeAuraLinux::GetGTypeInterfaceMask() { // as well. interface_mask |= 1 << ATK_TEXT_INTERFACE; + if (!IsPlainTextField() && !IsChildOfLeaf()) + interface_mask |= 1 << ATK_HYPERTEXT_INTERFACE; + // Value Interface - int role = GetAtkRole(); - if (role == ATK_ROLE_SCROLL_BAR || role == ATK_ROLE_SLIDER || - role == ATK_ROLE_PROGRESS_BAR || role == ATK_ROLE_SEPARATOR || - role == ATK_ROLE_SPIN_BUTTON) { + AtkRole role = GetAtkRole(); + if (IsRoleWithValueInterface(role)) { interface_mask |= 1 << ATK_VALUE_INTERFACE; } @@ -982,6 +1131,8 @@ GType AXPlatformNodeAuraLinux::GetAccessibilityGType() { if (interface_mask_ & (1 << ATK_HYPERLINK_INTERFACE)) g_type_add_interface_static(type, ATK_TYPE_HYPERLINK_IMPL, &HyperlinkImplInfo); + if (interface_mask_ & (1 << ATK_HYPERTEXT_INTERFACE)) + g_type_add_interface_static(type, ATK_TYPE_HYPERTEXT, &HypertextInfo); if (interface_mask_ & (1 << ATK_TEXT_INTERFACE)) g_type_add_interface_static(type, ATK_TYPE_TEXT, &TextInfo); @@ -1009,7 +1160,7 @@ void AXPlatformNodeAuraLinux::DestroyAtkObjects() { if (atk_object_) { if (atk_object_ == current_focused_) current_focused_ = nullptr; - ax_platform_node_auralinux_detach(AX_PLATFORM_NODE_AURALINUX(atk_object_)); + AXPlatformNodeAuraLinuxDetach(AX_PLATFORM_NODE_AURALINUX(atk_object_)); g_object_unref(atk_object_); atk_object_ = nullptr; } @@ -1028,6 +1179,22 @@ AXPlatformNode* AXPlatformNode::FromNativeViewAccessible( return AtkObjectToAXPlatformNodeAuraLinux(accessible); } +using UniqueIdMap = base::hash_map<int32_t, AXPlatformNodeAuraLinux*>; +// Map from each AXPlatformNode's unique id to its instance. +base::LazyInstance<UniqueIdMap>::Leaky g_unique_id_map = + LAZY_INSTANCE_INITIALIZER; + +// static +AXPlatformNodeAuraLinux* AXPlatformNodeAuraLinux::GetFromUniqueId( + int32_t unique_id) { + UniqueIdMap* unique_ids = g_unique_id_map.Pointer(); + auto iter = unique_ids->find(unique_id); + if (iter != unique_ids->end()) + return iter->second; + + return nullptr; +} + // // AXPlatformNodeAuraLinux implementation. // @@ -1434,7 +1601,8 @@ void AXPlatformNodeAuraLinux::GetAtkState(AtkStateSet* atk_state_set) { atk_state_set_add_state(atk_state_set, ATK_STATE_EXPANDABLE); atk_state_set_add_state(atk_state_set, ATK_STATE_EXPANDED); } - if (data.HasState(ax::mojom::State::kFocusable)) + if (data.HasState(ax::mojom::State::kFocusable) || + SelectionAndFocusAreTheSame()) atk_state_set_add_state(atk_state_set, ATK_STATE_FOCUSABLE); if (data.HasState(ax::mojom::State::kHorizontal)) atk_state_set_add_state(atk_state_set, ATK_STATE_HORIZONTAL); @@ -1456,8 +1624,10 @@ void AXPlatformNodeAuraLinux::GetAtkState(AtkStateSet* atk_state_set) { static_cast<int32_t>(ax::mojom::InvalidState::kFalse)) atk_state_set_add_state(atk_state_set, ATK_STATE_INVALID_ENTRY); #if defined(ATK_216) - if (data.HasIntAttribute(ax::mojom::IntAttribute::kCheckedState)) + if (data.HasIntAttribute(ax::mojom::IntAttribute::kCheckedState) && + data.role != ax::mojom::Role::kToggleButton) { atk_state_set_add_state(atk_state_set, ATK_STATE_CHECKABLE); + } if (data.HasIntAttribute(ax::mojom::IntAttribute::kHasPopup)) atk_state_set_add_state(atk_state_set, ATK_STATE_HAS_POPUP); #endif @@ -1465,10 +1635,11 @@ void AXPlatformNodeAuraLinux::GetAtkState(AtkStateSet* atk_state_set) { atk_state_set_add_state(atk_state_set, ATK_STATE_BUSY); if (data.GetBoolAttribute(ax::mojom::BoolAttribute::kModal)) atk_state_set_add_state(atk_state_set, ATK_STATE_MODAL); - if (data.GetBoolAttribute(ax::mojom::BoolAttribute::kSelected)) { + if (data.HasBoolAttribute(ax::mojom::BoolAttribute::kSelected)) atk_state_set_add_state(atk_state_set, ATK_STATE_SELECTABLE); + if (data.GetBoolAttribute(ax::mojom::BoolAttribute::kSelected)) atk_state_set_add_state(atk_state_set, ATK_STATE_SELECTED); - } + if (IsPlainTextField() || IsRichTextField()) { atk_state_set_add_state(atk_state_set, ATK_STATE_SELECTABLE_TEXT); if (data.HasState(ax::mojom::State::kMultiline)) @@ -1486,18 +1657,9 @@ void AXPlatformNodeAuraLinux::GetAtkState(AtkStateSet* atk_state_set) { // Checked state const auto checked_state = GetData().GetCheckedState(); - switch (checked_state) { - case ax::mojom::CheckedState::kMixed: - atk_state_set_add_state(atk_state_set, ATK_STATE_INDETERMINATE); - break; - case ax::mojom::CheckedState::kTrue: - atk_state_set_add_state(atk_state_set, - data.role == ax::mojom::Role::kToggleButton - ? ATK_STATE_PRESSED - : ATK_STATE_CHECKED); - break; - default: - break; + if (checked_state == ax::mojom::CheckedState::kTrue || + checked_state == ax::mojom::CheckedState::kMixed) { + atk_state_set_add_state(atk_state_set, GetAtkStateTypeForCheckableNode()); } switch (GetData().GetRestriction()) { @@ -1523,13 +1685,18 @@ void AXPlatformNodeAuraLinux::GetAtkRelations( } AXPlatformNodeAuraLinux::AXPlatformNodeAuraLinux() - : interface_mask_(0), atk_object_(nullptr), atk_hyperlink_(nullptr) {} + : interface_mask_(0), + atk_object_(nullptr), + atk_hyperlink_(nullptr), + weak_factory_(this) {} AXPlatformNodeAuraLinux::~AXPlatformNodeAuraLinux() { DestroyAtkObjects(); } void AXPlatformNodeAuraLinux::Destroy() { + g_unique_id_map.Get().erase(GetUniqueId()); + DestroyAtkObjects(); AXPlatformNodeBase::Destroy(); } @@ -1537,6 +1704,7 @@ void AXPlatformNodeAuraLinux::Destroy() { void AXPlatformNodeAuraLinux::Init(AXPlatformNodeDelegate* delegate) { // Initialize ATK. AXPlatformNodeBase::Init(delegate); + g_unique_id_map.Get()[GetUniqueId()] = this; DataChanged(); } @@ -1590,6 +1758,21 @@ gfx::NativeViewAccessible AXPlatformNodeAuraLinux::GetNativeViewAccessible() { return atk_object_; } +void AXPlatformNodeAuraLinux::OnCheckedStateChanged() { + DCHECK(atk_object_); + + atk_object_notify_state_change( + ATK_OBJECT(atk_object_), GetAtkStateTypeForCheckableNode(), + GetData().GetCheckedState() != ax::mojom::CheckedState::kFalse); +} + +void AXPlatformNodeAuraLinux::OnExpandedStateChanged(bool is_expanded) { + DCHECK(atk_object_); + + atk_object_notify_state_change(ATK_OBJECT(atk_object_), ATK_STATE_EXPANDED, + is_expanded); +} + AtkObject* AXPlatformNodeAuraLinux::current_focused_ = nullptr; void AXPlatformNodeAuraLinux::OnFocused() { @@ -1610,13 +1793,93 @@ void AXPlatformNodeAuraLinux::OnFocused() { true); } +base::WeakPtr<AXPlatformNodeAuraLinux> + AXPlatformNodeAuraLinux::current_selected_ = nullptr; + +void AXPlatformNodeAuraLinux::OnSelected() { + if (current_selected_ && !current_selected_->GetData().GetBoolAttribute( + ax::mojom::BoolAttribute::kSelected)) { + atk_object_notify_state_change(ATK_OBJECT(current_selected_->atk_object_), + ATK_STATE_SELECTED, false); + } + + current_selected_ = weak_factory_.GetWeakPtr(); + if (ATK_IS_OBJECT(atk_object_)) { + atk_object_notify_state_change(ATK_OBJECT(atk_object_), ATK_STATE_SELECTED, + true); + } + + if (SelectionAndFocusAreTheSame()) + OnFocused(); +} + +bool AXPlatformNodeAuraLinux::SelectionAndFocusAreTheSame() { + if (AXPlatformNodeBase* container = GetSelectionContainer()) { + ax::mojom::Role role = container->GetData().role; + if (role == ax::mojom::Role::kMenuBar || role == ax::mojom::Role::kMenu) + return true; + if (role == ax::mojom::Role::kListBox && + !container->GetData().HasState(ax::mojom::State::kMultiselectable)) { + return container->GetDelegate()->GetFocus() == + container->GetNativeViewAccessible(); + } + } + + // TODO(accessibility): GetSelectionContainer returns nullptr when the current + // object is a descendant of a select element with a size of 1. Intentional? + // For now, handle that scenario here. + // + // If the selection is changing on a collapsed select element, focus remains + // on the select element and not the newly-selected descendant. + if (AXPlatformNodeBase* parent = FromNativeViewAccessible(GetParent())) { + if (parent->GetData().role == ax::mojom::Role::kMenuListPopup) + return !parent->GetData().HasState(ax::mojom::State::kInvisible); + } + + return false; +} + +void AXPlatformNodeAuraLinux::OnValueChanged() { + DCHECK(atk_object_); + + if (!IsRoleWithValueInterface(GetAtkRole())) + return; + + float float_val; + if (!GetFloatAttribute(ax::mojom::FloatAttribute::kValueForRange, &float_val)) + return; + + AtkPropertyValues property_values; + property_values.property_name = "accessible-value"; + + property_values.new_value = G_VALUE_INIT; + g_value_init(&property_values.new_value, G_TYPE_DOUBLE); + g_value_set_double(&property_values.new_value, + static_cast<double>(float_val)); + g_signal_emit_by_name(G_OBJECT(atk_object_), + "property-change::accessible-value", &property_values, + nullptr); +} + void AXPlatformNodeAuraLinux::NotifyAccessibilityEvent( ax::mojom::Event event_type) { switch (event_type) { + case ax::mojom::Event::kCheckedStateChanged: + OnCheckedStateChanged(); + break; + case ax::mojom::Event::kExpandedChanged: + OnExpandedStateChanged(GetData().HasState(ax::mojom::State::kExpanded)); + break; case ax::mojom::Event::kFocus: case ax::mojom::Event::kFocusContext: OnFocused(); break; + case ax::mojom::Event::kSelection: + OnSelected(); + break; + case ax::mojom::Event::kValueChanged: + OnValueChanged(); + break; default: break; } @@ -1626,6 +1889,10 @@ void AXPlatformNodeAuraLinux::UpdateHypertext() { hypertext_ = ComputeHypertext(); } +const AXHypertext& AXPlatformNodeAuraLinux::GetHypertext() { + return hypertext_; +} + int AXPlatformNodeAuraLinux::GetIndexInParent() { if (!GetParent()) return -1; @@ -1710,7 +1977,7 @@ const gchar* AXPlatformNodeAuraLinux::GetDefaultActionName() { if (!GetIntAttribute(ax::mojom::IntAttribute::kDefaultActionVerb, &action)) return nullptr; - base::string16 action_verb = ui::ActionVerbToUnlocalizedString( + base::string16 action_verb = ActionVerbToUnlocalizedString( static_cast<ax::mojom::DefaultActionVerb>(action)); ATK_AURALINUX_RETURN_STRING(base::UTF16ToUTF8(action_verb)); @@ -1722,6 +1989,14 @@ AtkAttributeSet* AXPlatformNodeAuraLinux::GetAtkAttributes() { return attribute_list; } +AtkStateType AXPlatformNodeAuraLinux::GetAtkStateTypeForCheckableNode() { + if (GetData().GetCheckedState() == ax::mojom::CheckedState::kMixed) + return ATK_STATE_INDETERMINATE; + if (GetData().role == ax::mojom::Role::kToggleButton) + return ATK_STATE_PRESSED; + return ATK_STATE_CHECKED; +} + // AtkDocumentHelpers const gchar* AXPlatformNodeAuraLinux::GetDocumentAttributeValue( @@ -1770,9 +2045,6 @@ AtkAttributeSet* AXPlatformNodeAuraLinux::GetDocumentAttributes() const { // AtkHyperlink* AXPlatformNodeAuraLinux::GetAtkHyperlink() { - DCHECK(ATK_HYPERLINK_IMPL(atk_object_)); - g_return_val_if_fail(ATK_HYPERLINK_IMPL(atk_object_), 0); - if (!atk_hyperlink_) { atk_hyperlink_ = ATK_HYPERLINK(g_object_new(AX_PLATFORM_ATK_HYPERLINK_TYPE, 0)); diff --git a/chromium/ui/accessibility/platform/ax_platform_node_auralinux.h b/chromium/ui/accessibility/platform/ax_platform_node_auralinux.h index 6ed9b01cedf..48fd256d64b 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_auralinux.h +++ b/chromium/ui/accessibility/platform/ax_platform_node_auralinux.h @@ -27,24 +27,24 @@ namespace ui { // Implements accessibility on Aura Linux using ATK. -class AXPlatformNodeAuraLinux : public AXPlatformNodeBase { +class AX_EXPORT AXPlatformNodeAuraLinux : public AXPlatformNodeBase { public: AXPlatformNodeAuraLinux(); ~AXPlatformNodeAuraLinux() override; // Set or get the root-level Application object that's the parent of all // top-level windows. - AX_EXPORT static void SetApplication(AXPlatformNode* application); + static void SetApplication(AXPlatformNode* application); static AXPlatformNode* application() { return application_; } static void EnsureGTypeInit(); // Do asynchronous static initialization. - AX_EXPORT static void StaticInitialize(); + static void StaticInitialize(); - AX_EXPORT void DataChanged(); + void DataChanged(); void Destroy() override; - AX_EXPORT void AddAccessibilityTreeProperties(base::DictionaryValue* dict); + void AddAccessibilityTreeProperties(base::DictionaryValue* dict); AtkRole GetAtkRole(); void GetAtkState(AtkStateSet* state_set); @@ -65,6 +65,8 @@ class AXPlatformNodeAuraLinux : public AXPlatformNodeBase { gint* x, gint* y, gint* width, gint* height, AtkCoordType coord_type); + static AXPlatformNodeAuraLinux* GetFromUniqueId(int32_t unique_id); + // AtkDocument helpers const gchar* GetDocumentAttributeValue(const gchar* attribute) const; AtkAttributeSet* GetDocumentAttributes() const; @@ -76,7 +78,13 @@ class AXPlatformNodeAuraLinux : public AXPlatformNodeBase { void GetFloatAttributeInGValue(ax::mojom::FloatAttribute attr, GValue* value); // Event helpers + void OnCheckedStateChanged(); + void OnExpandedStateChanged(bool is_expanded); void OnFocused(); + void OnSelected(); + void OnValueChanged(); + + bool SelectionAndFocusAreTheSame(); // AXPlatformNode overrides. gfx::NativeViewAccessible GetNativeViewAccessible() override; @@ -88,7 +96,8 @@ class AXPlatformNodeAuraLinux : public AXPlatformNodeBase { std::string GetTextForATK(); - AX_EXPORT void UpdateHypertext(); + void UpdateHypertext(); + const AXHypertext& GetHypertext(); protected: AXHypertext hypertext_; @@ -117,6 +126,9 @@ class AXPlatformNodeAuraLinux : public AXPlatformNodeBase { AtkObject* CreateAtkObject(); void DestroyAtkObjects(); + // The AtkStateType for a checkable node can vary depending on the role. + AtkStateType GetAtkStateTypeForCheckableNode(); + // Keep information of latest AtkInterfaces mask to refresh atk object // interfaces accordingly if needed. int interface_mask_; @@ -133,6 +145,13 @@ class AXPlatformNodeAuraLinux : public AXPlatformNodeBase { // to emit the ATK_STATE_FOCUSED change to false. static AtkObject* current_focused_; + // The last object which was selected. Tracking this is required because + // widgets in the browser UI only emit notifications upon becoming selected, + // but clients also expect notifications when items become unselected. + static base::WeakPtr<AXPlatformNodeAuraLinux> current_selected_; + + base::WeakPtrFactory<AXPlatformNodeAuraLinux> weak_factory_; + DISALLOW_COPY_AND_ASSIGN(AXPlatformNodeAuraLinux); }; diff --git a/chromium/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc b/chromium/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc index 228bd0156ce..21f086f22e0 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc +++ b/chromium/ui/accessibility/platform/ax_platform_node_auralinux_unittest.cc @@ -857,4 +857,90 @@ TEST_F(AXPlatformNodeAuraLinuxTest, TestAtkHyperlink) { g_object_unref(root_obj); } +// +// AtkText interface +// +// +TEST_F(AXPlatformNodeAuraLinuxTest, TestAtkTextCharacterGranularity) { + AXNodeData root; + root.id = 1; + root.role = ax::mojom::Role::kTextField; + root.AddStringAttribute(ax::mojom::StringAttribute::kValue, + "A decently long string \xE2\x98\xBA with an emoji."); + Init(root); + + AtkObject* root_obj(GetRootAtkObject()); + ASSERT_TRUE(ATK_IS_OBJECT(root_obj)); + g_object_ref(root_obj); + + ASSERT_TRUE(ATK_IS_TEXT(root_obj)); + AtkText* atk_text = ATK_TEXT(root_obj); + + EXPECT_EQ(static_cast<gunichar>('d'), + atk_text_get_character_at_offset(atk_text, 2)); + EXPECT_EQ(static_cast<gunichar>('A'), + atk_text_get_character_at_offset(atk_text, -1)); + EXPECT_EQ(0u, atk_text_get_character_at_offset(atk_text, 42342)); + EXPECT_EQ(0x263Au, atk_text_get_character_at_offset(atk_text, 23)); + EXPECT_EQ(static_cast<gunichar>(' '), + atk_text_get_character_at_offset(atk_text, 24)); + + auto verify_text = [&](const char* expected_text, char* text, + int expected_start, int expected_end, int start, + int end) { + EXPECT_STREQ(expected_text, text); + EXPECT_EQ(start, expected_start); + EXPECT_EQ(end, expected_end); + g_free(text); + }; + + auto verify_text_at_offset = [&](const char* expected_text, int offset, + int expected_start, int expected_end) { + int start = 0, end = 0; + char* text = atk_text_get_text_at_offset( + atk_text, offset, ATK_TEXT_BOUNDARY_CHAR, &start, &end); + verify_text(expected_text, text, expected_start, expected_end, start, end); + }; + + verify_text_at_offset("d", 2, 2, 3); + verify_text_at_offset("A", -1, 0, 1); + verify_text_at_offset("", 42342, 39, 39); + verify_text_at_offset("\xE2\x98\xBA", 23, 23, 24); + verify_text_at_offset(" ", 24, 24, 25); + + auto verify_text_after_offset = [&](const char* expected_text, int offset, + int expected_start, int expected_end) { + int start = 0, end = 0; + char* text = atk_text_get_text_after_offset( + atk_text, offset, ATK_TEXT_BOUNDARY_CHAR, &start, &end); + verify_text(expected_text, text, expected_start, expected_end, start, end); + }; + + verify_text_after_offset("d", 1, 2, 3); + verify_text_after_offset("", 42342, 39, 39); + verify_text_after_offset("\xE2\x98\xBA", 22, 23, 24); + verify_text_after_offset(" ", 23, 24, 25); + + // This boundary condition is enforced by ATK for some reason. + verify_text_after_offset(nullptr, -1, 0, 0); + + auto verify_text_before_offset = [&](const char* expected_text, int offset, + int expected_start, int expected_end) { + int start = 0, end = 0; + char* text = atk_text_get_text_before_offset( + atk_text, offset, ATK_TEXT_BOUNDARY_CHAR, &start, &end); + verify_text(expected_text, text, expected_start, expected_end, start, end); + }; + + verify_text_before_offset("d", 3, 2, 3); + verify_text_before_offset("", 42342, 39, 39); + verify_text_before_offset("\xE2\x98\xBA", 24, 23, 24); + verify_text_before_offset(" ", 25, 24, 25); + + // This boundary condition is enforced by ATK for some reason. + verify_text_after_offset(nullptr, -1, 0, 0); + + g_object_unref(root_obj); +} + } // namespace ui diff --git a/chromium/ui/accessibility/platform/ax_platform_node_base.cc b/chromium/ui/accessibility/platform/ax_platform_node_base.cc index cae47b1fc8e..e4a10976f4f 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_base.cc +++ b/chromium/ui/accessibility/platform/ax_platform_node_base.cc @@ -8,6 +8,7 @@ #include <utility> #include <vector> +#include "base/no_destructor.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" @@ -27,10 +28,10 @@ void AXPlatformNodeBase::Init(AXPlatformNodeDelegate* delegate) { } const AXNodeData& AXPlatformNodeBase::GetData() const { - CR_DEFINE_STATIC_LOCAL(AXNodeData, empty_data, ()); + static base::NoDestructor<AXNodeData> empty_data; if (delegate_) return delegate_->GetData(); - return empty_data; + return *empty_data; } gfx::NativeViewAccessible AXPlatformNodeBase::GetParent() { @@ -189,9 +190,9 @@ bool AXPlatformNodeBase::HasStringAttribute( const std::string& AXPlatformNodeBase::GetStringAttribute( ax::mojom::StringAttribute attribute) const { - CR_DEFINE_STATIC_LOCAL(std::string, empty_data, ()); + static base::NoDestructor<std::string> empty_data; if (!delegate_) - return empty_data; + return *empty_data; return GetData().GetStringAttribute(attribute); } @@ -227,9 +228,9 @@ bool AXPlatformNodeBase::HasIntListAttribute( const std::vector<int32_t>& AXPlatformNodeBase::GetIntListAttribute( ax::mojom::IntListAttribute attribute) const { - CR_DEFINE_STATIC_LOCAL(std::vector<int32_t>, empty_data, ()); + static base::NoDestructor<std::vector<int32_t>> empty_data; if (!delegate_) - return empty_data; + return *empty_data; return GetData().GetIntListAttribute(attribute); } diff --git a/chromium/ui/accessibility/platform/ax_platform_node_delegate_base.cc b/chromium/ui/accessibility/platform/ax_platform_node_delegate_base.cc index 41d04774945..f5a77c4e1dd 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_delegate_base.cc +++ b/chromium/ui/accessibility/platform/ax_platform_node_delegate_base.cc @@ -4,20 +4,20 @@ #include "ui/accessibility/platform/ax_platform_node_delegate_base.h" -#include "base/macros.h" +#include "base/no_destructor.h" #include "ui/accessibility/ax_node_data.h" #include "ui/accessibility/ax_tree_data.h" namespace ui { const AXNodeData& AXPlatformNodeDelegateBase::GetData() const { - CR_DEFINE_STATIC_LOCAL(AXNodeData, empty_data, ()); - return empty_data; + static base::NoDestructor<AXNodeData> empty_data; + return *empty_data; } const AXTreeData& AXPlatformNodeDelegateBase::GetTreeData() const { - CR_DEFINE_STATIC_LOCAL(AXTreeData, empty_data, ()); - return empty_data; + static base::NoDestructor<AXTreeData> empty_data; + return *empty_data; } gfx::NativeWindow AXPlatformNodeDelegateBase::GetTopLevelWidget() { @@ -131,8 +131,8 @@ std::set<int32_t> AXPlatformNodeDelegateBase::GetReverseRelations( } const AXUniqueId& AXPlatformNodeDelegateBase::GetUniqueId() const { - CR_DEFINE_STATIC_LOCAL(AXUniqueId, dummy_unique_id, ()); - return dummy_unique_id; + static base::NoDestructor<AXUniqueId> dummy_unique_id; + return *dummy_unique_id; } } // namespace ui diff --git a/chromium/ui/accessibility/platform/ax_platform_node_mac.mm b/chromium/ui/accessibility/platform/ax_platform_node_mac.mm index 1bf96b4b9a6..dc041e0b6ee 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_mac.mm +++ b/chromium/ui/accessibility/platform/ax_platform_node_mac.mm @@ -8,6 +8,7 @@ #include <stddef.h> #include "base/macros.h" +#include "base/no_destructor.h" #include "base/strings/sys_string_conversions.h" #include "ui/accessibility/ax_action_data.h" #include "ui/accessibility/ax_node_data.h" @@ -259,6 +260,8 @@ RoleMap BuildSubroleMap() { EventMap BuildEventMap() { const EventMap::value_type events[] = { + {ax::mojom::Event::kCheckedStateChanged, + NSAccessibilityValueChangedNotification}, {ax::mojom::Event::kFocus, NSAccessibilityFocusedUIElementChangedNotification}, {ax::mojom::Event::kFocusContext, @@ -287,8 +290,8 @@ ActionList BuildActionList() { } const ActionList& GetActionList() { - CR_DEFINE_STATIC_LOCAL(const ActionList, action_map, (BuildActionList())); - return action_map; + static const base::NoDestructor<ActionList> action_map(BuildActionList()); + return *action_map; } void PostAnnouncementNotification(NSString* announcement) { @@ -335,21 +338,21 @@ bool AlsoUseShowMenuActionForDefaultAction(const ui::AXNodeData& data) { @synthesize node = node_; + (NSString*)nativeRoleFromAXRole:(ax::mojom::Role)role { - CR_DEFINE_STATIC_LOCAL(const RoleMap, role_map, (BuildRoleMap())); - RoleMap::const_iterator it = role_map.find(role); - return it != role_map.end() ? it->second : NSAccessibilityUnknownRole; + static const base::NoDestructor<RoleMap> role_map(BuildRoleMap()); + RoleMap::const_iterator it = role_map->find(role); + return it != role_map->end() ? it->second : NSAccessibilityUnknownRole; } + (NSString*)nativeSubroleFromAXRole:(ax::mojom::Role)role { - CR_DEFINE_STATIC_LOCAL(const RoleMap, subrole_map, (BuildSubroleMap())); - RoleMap::const_iterator it = subrole_map.find(role); - return it != subrole_map.end() ? it->second : nil; + static const base::NoDestructor<RoleMap> subrole_map(BuildSubroleMap()); + RoleMap::const_iterator it = subrole_map->find(role); + return it != subrole_map->end() ? it->second : nil; } + (NSString*)nativeNotificationFromAXEvent:(ax::mojom::Event)event { - CR_DEFINE_STATIC_LOCAL(const EventMap, event_map, (BuildEventMap())); - EventMap::const_iterator it = event_map.find(event); - return it != event_map.end() ? it->second : nil; + static const base::NoDestructor<EventMap> event_map(BuildEventMap()); + EventMap::const_iterator it = event_map->find(event); + return it != event_map->end() ? it->second : nil; } - (instancetype)initWithNode:(ui::AXPlatformNodeBase*)node { @@ -560,6 +563,10 @@ bool AlsoUseShowMenuActionForDefaultAction(const ui::AXNodeData& data) { [axAttributes addObjectsFromArray:@[ NSAccessibilitySelectedAttribute ]]; } + if (ui::IsMenuItem(node_->GetData().role)) { + [axAttributes addObjectsFromArray:@[ @"AXMenuItemMarkChar" ]]; + } + return axAttributes.autorelease(); } @@ -759,6 +766,13 @@ bool AlsoUseShowMenuActionForDefaultAction(const ui::AXNodeData& data) { if (ui::IsNameExposedInAXValueForRole(role)) return [self getStringAttribute:ax::mojom::StringAttribute::kName]; + if (node_->HasIntAttribute(ax::mojom::IntAttribute::kCheckedState)) { + // Mixed checkbox state not currently supported in views, but could be. + // See browser_accessibility_cocoa.mm for details. + const auto checkedState = static_cast<ax::mojom::CheckedState>( + node_->GetIntAttribute(ax::mojom::IntAttribute::kCheckedState)); + return checkedState == ax::mojom::CheckedState::kTrue ? @1 : @0; + } return [self getStringAttribute:ax::mojom::StringAttribute::kValue]; } @@ -825,6 +839,19 @@ bool AlsoUseShowMenuActionForDefaultAction(const ui::AXNodeData& data) { return [self getStringAttribute:ax::mojom::StringAttribute::kPlaceholder]; } +- (NSString*)AXMenuItemMarkChar { + if (!ui::IsMenuItem(node_->GetData().role)) + return nil; + + const auto checkedState = static_cast<ax::mojom::CheckedState>( + node_->GetIntAttribute(ax::mojom::IntAttribute::kCheckedState)); + if (checkedState == ax::mojom::CheckedState::kTrue) { + return @"\xE2\x9C\x93"; // UTF-8 for unicode 0x2713, "check mark" + } + + return @""; +} + // Text-specific attributes. - (NSString*)AXSelectedText { @@ -976,7 +1003,7 @@ void AXPlatformNodeMac::NotifyAccessibilityEvent(ax::mojom::Event event_type) { break; case ax::mojom::Event::kSelection: // On Mac, map menu item selection to a focus event. - if (GetData().role == ax::mojom::Role::kMenuItem) { + if (ui::IsMenuItem(GetData().role)) { NotifyMacEvent(native_node_, ax::mojom::Event::kFocus); return; } diff --git a/chromium/ui/accessibility/platform/ax_platform_node_test_helper.cc b/chromium/ui/accessibility/platform/ax_platform_node_test_helper.cc new file mode 100644 index 00000000000..2dd8e81c10b --- /dev/null +++ b/chromium/ui/accessibility/platform/ax_platform_node_test_helper.cc @@ -0,0 +1,47 @@ +// 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/accessibility/platform/ax_platform_node_test_helper.h" + +#include "ui/accessibility/ax_node_data.h" + +namespace ui { + +// static +int AXPlatformNodeTestHelper::GetTreeSize(AXPlatformNode* ax_node) { + if (!ax_node) + return 0; + int count = 1; + AXPlatformNodeDelegate* delegate = ax_node->GetDelegate(); + for (int i = 0; i < delegate->GetChildCount(); ++i) { + AXPlatformNode* child_node = + AXPlatformNode::FromNativeViewAccessible(delegate->ChildAtIndex(i)); + count += GetTreeSize(child_node); + } + return count; +} + +// static +AXPlatformNode* AXPlatformNodeTestHelper::FindChildByName( + AXPlatformNode* ax_node, + const std::string& name) { + if (!ax_node) + return nullptr; + + AXPlatformNodeDelegate* delegate = ax_node->GetDelegate(); + if (delegate->GetData().GetStringAttribute( + ax::mojom::StringAttribute::kName) == name) + return ax_node; + + for (int i = 0; i < delegate->GetChildCount(); ++i) { + AXPlatformNode* result_from_child = FindChildByName( + AXPlatformNode::FromNativeViewAccessible(delegate->ChildAtIndex(i)), + name); + if (result_from_child) + return result_from_child; + } + return nullptr; +} + +} // namespace ui diff --git a/chromium/ui/accessibility/platform/ax_platform_node_test_helper.h b/chromium/ui/accessibility/platform/ax_platform_node_test_helper.h new file mode 100644 index 00000000000..14994ea4c25 --- /dev/null +++ b/chromium/ui/accessibility/platform/ax_platform_node_test_helper.h @@ -0,0 +1,22 @@ +// 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_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_TEST_HELPER_H_ +#define UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_TEST_HELPER_H_ + +#include "ui/accessibility/platform/ax_platform_node.h" +#include "ui/accessibility/platform/ax_platform_node_delegate.h" + +namespace ui { + +class AX_EXPORT AXPlatformNodeTestHelper { + public: + static int GetTreeSize(AXPlatformNode* ax_node); + static AXPlatformNode* FindChildByName(AXPlatformNode* ax_node, + const std::string& name); +}; + +} // namespace ui + +#endif // UI_ACCESSIBILITY_PLATFORM_AX_PLATFORM_NODE_TEST_HELPER_H_ diff --git a/chromium/ui/accessibility/platform/ax_platform_node_win.cc b/chromium/ui/accessibility/platform/ax_platform_node_win.cc index 65069458fa8..15faebdb9ff 100644 --- a/chromium/ui/accessibility/platform/ax_platform_node_win.cc +++ b/chromium/ui/accessibility/platform/ax_platform_node_win.cc @@ -1367,16 +1367,15 @@ IFACEMETHODIMP AXPlatformNodeWin::scrollToPoint( COM_OBJECT_VALIDATE(); WIN_ACCESSIBILITY_API_HISTOGRAM(UMA_API_SCROLL_TO_POINT); + // Convert to screen-relative coordinates if necessary. gfx::Point scroll_to(x, y); - if (coordinate_type == IA2_COORDTYPE_SCREEN_RELATIVE) { - scroll_to -= delegate_->GetUnclippedScreenBoundsRect().OffsetFromOrigin(); - } else if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) { + if (coordinate_type == IA2_COORDTYPE_PARENT_RELATIVE) { if (GetParent()) { AXPlatformNodeBase* base = FromNativeViewAccessible(GetParent()); scroll_to += base->delegate_->GetUnclippedScreenBoundsRect().OffsetFromOrigin(); } - } else { + } else if (coordinate_type != IA2_COORDTYPE_SCREEN_RELATIVE) { return E_INVALIDARG; } @@ -5559,8 +5558,7 @@ int AXPlatformNodeWin::MSAAState() { // In focused single selection UI menus and listboxes, mirror item selection // to focus. This helps NVDA read the selected option as it changes. - if ((data.role == ax::mojom::Role::kListBoxOption || - data.role == ax::mojom::Role::kMenuItem) && + if ((data.role == ax::mojom::Role::kListBoxOption || IsMenuItem(data.role)) && data.GetBoolAttribute(ax::mojom::BoolAttribute::kSelected)) { AXPlatformNodeBase* container = FromNativeViewAccessible(GetParent()); if (container && container->GetParent() == focus) { @@ -5600,6 +5598,7 @@ int AXPlatformNodeWin::MSAAEvent(ax::mojom::Event event) { switch (event) { case ax::mojom::Event::kAlert: return EVENT_SYSTEM_ALERT; + case ax::mojom::Event::kCheckedStateChanged: case ax::mojom::Event::kExpandedChanged: return EVENT_OBJECT_STATECHANGE; case ax::mojom::Event::kFocus: diff --git a/chromium/ui/accessibility/platform/ax_system_caret_win.cc b/chromium/ui/accessibility/platform/ax_system_caret_win.cc index bd475d53d84..dd91704c713 100644 --- a/chromium/ui/accessibility/platform/ax_system_caret_win.cc +++ b/chromium/ui/accessibility/platform/ax_system_caret_win.cc @@ -24,6 +24,7 @@ AXSystemCaretWin::AXSystemCaretWin(gfx::AcceleratedWidget event_target) data_.role = ax::mojom::Role::kCaret; // |get_accState| should return 0 which means that the caret is visible. data_.state = 0; + data_.AddState(ax::mojom::State::kInvisible); // According to MSDN, "Edit" should be the name of the caret object. data_.SetName(L"Edit"); data_.offset_container_id = -1; @@ -54,13 +55,43 @@ Microsoft::WRL::ComPtr<IAccessible> AXSystemCaretWin::GetCaret() const { void AXSystemCaretWin::MoveCaretTo(const gfx::Rect& bounds) { if (bounds.IsEmpty()) return; - data_.location = gfx::RectF(bounds); - if (event_target_) { + + // If the caret has non-empty bounds, assume it has been made visible. + bool newly_visible = false; + if (data_.HasState(ax::mojom::State::kInvisible)) { + newly_visible = true; + data_.RemoveState(ax::mojom::State::kInvisible); + } + + if (!event_target_) + return; + + if (newly_visible) { + ::NotifyWinEvent(EVENT_OBJECT_SHOW, event_target_, OBJID_CARET, + -caret_->GetUniqueId()); + } + + gfx::RectF new_location(bounds); + // Avoid redundant caret move events (if the location stays the same), but + // always fire when it's made visible again. + if (data_.location != new_location || newly_visible) { + data_.location = new_location; ::NotifyWinEvent(EVENT_OBJECT_LOCATIONCHANGE, event_target_, OBJID_CARET, -caret_->GetUniqueId()); } } +void AXSystemCaretWin::Hide() { + if (!data_.HasState(ax::mojom::State::kInvisible)) { + data_.AddState(ax::mojom::State::kInvisible); + data_.location.set_width(0); + if (event_target_) { + ::NotifyWinEvent(EVENT_OBJECT_HIDE, event_target_, OBJID_CARET, + -caret_->GetUniqueId()); + } + } +} + const AXNodeData& AXSystemCaretWin::GetData() const { return data_; } diff --git a/chromium/ui/accessibility/platform/ax_system_caret_win.h b/chromium/ui/accessibility/platform/ax_system_caret_win.h index 03f4bf9dc0a..a4d2392410f 100644 --- a/chromium/ui/accessibility/platform/ax_system_caret_win.h +++ b/chromium/ui/accessibility/platform/ax_system_caret_win.h @@ -31,6 +31,7 @@ class AX_EXPORT AXSystemCaretWin : private AXPlatformNodeDelegateBase { Microsoft::WRL::ComPtr<IAccessible> GetCaret() const; void MoveCaretTo(const gfx::Rect& bounds); + void Hide(); private: // |AXPlatformNodeDelegate| members. diff --git a/chromium/ui/android/BUILD.gn b/chromium/ui/android/BUILD.gn index db4b8a8ad9e..b641523e27c 100644 --- a/chromium/ui/android/BUILD.gn +++ b/chromium/ui/android/BUILD.gn @@ -233,6 +233,7 @@ java_group("ui_java") { android_library("ui_utils_java") { java_files = [ "java/src/org/chromium/ui/ContactsPickerListener.java", + "java/src/org/chromium/ui/KeyboardVisibilityDelegate.java", "java/src/org/chromium/ui/PhotoPickerListener.java", "java/src/org/chromium/ui/UiUtils.java", ] @@ -245,6 +246,7 @@ android_library("ui_full_java") { java_files = [ "java/src/org/chromium/ui/AsyncViewStub.java", "java/src/org/chromium/ui/AsyncViewProvider.java", + "java/src/org/chromium/ui/DeferredViewStubInflationProvider.java", "java/src/org/chromium/ui/DropdownAdapter.java", "java/src/org/chromium/ui/DropdownDividerDrawable.java", "java/src/org/chromium/ui/DropdownItem.java", @@ -255,6 +257,7 @@ android_library("ui_full_java") { "java/src/org/chromium/ui/DropdownPopupWindowInterface.java", "java/src/org/chromium/ui/HorizontalListDividerDrawable.java", "java/src/org/chromium/ui/OverscrollRefreshHandler.java", + "java/src/org/chromium/ui/ViewProvider.java", "java/src/org/chromium/ui/VSyncMonitor.java", "java/src/org/chromium/ui/base/ActivityAndroidPermissionDelegate.java", "java/src/org/chromium/ui/base/ActivityWindowAndroid.java", @@ -307,6 +310,7 @@ android_library("ui_full_java") { "java/src/org/chromium/ui/widget/AnchoredPopupWindow.java", "java/src/org/chromium/ui/widget/ButtonCompat.java", "java/src/org/chromium/ui/widget/CheckableImageView.java", + "java/src/org/chromium/ui/widget/ChromeBulletSpan.java", "java/src/org/chromium/ui/widget/OptimizedFrameLayout.java", "java/src/org/chromium/ui/widget/RectProvider.java", "java/src/org/chromium/ui/widget/RoundedCornerImageView.java", @@ -320,8 +324,8 @@ android_library("ui_full_java") { ":ui_java_resources", ":ui_utils_java", "//base:base_java", - "//third_party/android_tools:android_support_annotations_java", - "//third_party/android_tools:android_support_v7_appcompat_java", + "//third_party/android_deps:android_support_annotations_java", + "//third_party/android_deps:android_support_v7_appcompat_java", ] srcjar_deps = [ ":java_enums_srcjar", diff --git a/chromium/ui/android/delegated_frame_host_android.cc b/chromium/ui/android/delegated_frame_host_android.cc index b28774aaf98..ec85b8eedbe 100644 --- a/chromium/ui/android/delegated_frame_host_android.cc +++ b/chromium/ui/android/delegated_frame_host_android.cc @@ -94,7 +94,6 @@ void DelegatedFrameHostAndroid::SubmitCompositorFrame( viz::RenderPass* root_pass = frame.render_pass_list.back().get(); const bool has_transparent_background = root_pass->has_transparent_background; - const float active_device_scale_factor = frame.device_scale_factor(); const gfx::Size pending_surface_size_in_pixels = frame.size_in_pixels(); // Reset |content_layer_| only if surface-sync is not used. When surface-sync // is turned on, |content_layer_| is updated with the appropriate states (see @@ -113,7 +112,6 @@ void DelegatedFrameHostAndroid::SubmitCompositorFrame( if (!content_layer_) { active_local_surface_id_ = local_surface_id; pending_local_surface_id_ = active_local_surface_id_; - active_device_scale_factor_ = active_device_scale_factor; pending_surface_size_in_pixels_ = pending_surface_size_in_pixels; has_transparent_background_ = has_transparent_background; content_layer_ = CreateSurfaceLayer( @@ -150,6 +148,8 @@ void DelegatedFrameHostAndroid::CopyFromCompositingSurface( const gfx::Rect& src_subrect, const gfx::Size& output_size, base::OnceCallback<void(const SkBitmap&)> callback) { + DCHECK(CanCopyFromCompositingSurface()); + std::unique_ptr<viz::CopyOutputRequest> request = std::make_unique<viz::CopyOutputRequest>( viz::CopyOutputRequest::ResultFormat::RGBA_BITMAP, @@ -165,41 +165,48 @@ void DelegatedFrameHostAndroid::CopyFromCompositingSurface( if (!output_size.IsEmpty()) request->set_result_selection(gfx::Rect(output_size)); - // If there is enough information to populate the copy output request fields, - // then process it now. Otherwise, wait until the information becomes - // available. - if (CanCopyFromCompositingSurface() && - active_local_surface_id_ == pending_local_surface_id_) { - ProcessCopyOutputRequest(std::move(request)); - } else { - pending_first_frame_requests_.push_back(std::move(request)); + if (!request->has_area()) + request->set_area(gfx::Rect(pending_surface_size_in_pixels_)); + + if (request->has_result_selection()) { + const gfx::Rect& area = request->area(); + const gfx::Rect& result_selection = request->result_selection(); + if (area.IsEmpty() || result_selection.IsEmpty()) { + // Viz would normally return an empty result for an empty selection. + // However, this guard here is still necessary to protect against setting + // an illegal scaling ratio. + return; + } + request->SetScaleRatio( + gfx::Vector2d(area.width(), area.height()), + gfx::Vector2d(result_selection.width(), result_selection.height())); } + + host_frame_sink_manager_->RequestCopyOfOutput( + viz::SurfaceId(frame_sink_id_, pending_local_surface_id_), + std::move(request)); } bool DelegatedFrameHostAndroid::CanCopyFromCompositingSurface() const { - return content_layer_ && content_layer_->fallback_surface_id() && - content_layer_->fallback_surface_id()->is_valid() && - view_->GetWindowAndroid() && + return pending_local_surface_id_.is_valid() && view_->GetWindowAndroid() && view_->GetWindowAndroid()->GetCompositor(); } void DelegatedFrameHostAndroid::EvictDelegatedFrame() { if (!content_layer_) return; - viz::SurfaceId surface_id; - if (content_layer_->fallback_surface_id()) { - surface_id = *content_layer_->fallback_surface_id(); - content_layer_->SetFallbackSurfaceId(viz::SurfaceId()); - } content_layer_->SetPrimarySurfaceId(viz::SurfaceId(), cc::DeadlinePolicy::UseDefaultDeadline()); if (!enable_surface_synchronization_) { content_layer_->RemoveFromParent(); content_layer_ = nullptr; } - if (!surface_id.is_valid()) + if (!HasSavedFrame()) return; - std::vector<viz::SurfaceId> surface_ids = {surface_id}; + std::vector<viz::SurfaceId> surface_ids = { + viz::SurfaceId(frame_sink_id_, active_local_surface_id_)}; + // Reset information about the active surface because it will get destroyed. + active_local_surface_id_ = viz::LocalSurfaceId(); host_frame_sink_manager_->EvictSurfaces(surface_ids); frame_evictor_->DiscardedFrame(); } @@ -207,6 +214,16 @@ void DelegatedFrameHostAndroid::EvictDelegatedFrame() { void DelegatedFrameHostAndroid::ResetFallbackToFirstNavigationSurface() { if (!content_layer_) return; + // Don't update the fallback if it's already newer than the first id after + // navigation. + if (content_layer_->fallback_surface_id() && + content_layer_->fallback_surface_id()->frame_sink_id() == + frame_sink_id_ && + content_layer_->fallback_surface_id() + ->local_surface_id() + .IsSameOrNewerThan(first_local_surface_id_after_navigation_)) { + return; + } content_layer_->SetFallbackSurfaceId( viz::SurfaceId(frame_sink_id_, first_local_surface_id_after_navigation_)); } @@ -296,16 +313,18 @@ void DelegatedFrameHostAndroid::EmbedSurface( viz::SurfaceId current_primary_surface_id = content_layer_->primary_surface_id(); + viz::SurfaceId new_primary_surface_id(frame_sink_id_, + pending_local_surface_id_); if (!frame_evictor_->visible()) { - // If the tab is resized while hidden, reset the fallback so that the next + // If the tab is resized while hidden, advance the fallback so that the next // time user switches back to it the page is blank. This is preferred to // showing contents of old size. Don't call EvictDelegatedFrame to avoid // races when dragging tabs across displays. See https://crbug.com/813157. if (pending_surface_size_in_pixels_ != content_layer_->bounds() && content_layer_->fallback_surface_id() && content_layer_->fallback_surface_id()->is_valid()) { - content_layer_->SetFallbackSurfaceId(viz::SurfaceId()); + content_layer_->SetFallbackSurfaceId(new_primary_surface_id); } // Don't update the SurfaceLayer when invisible to avoid blocking on // renderers that do not submit CompositorFrames. Next time the renderer @@ -329,9 +348,8 @@ void DelegatedFrameHostAndroid::EmbedSurface( deadline_policy = cc::DeadlinePolicy::UseSpecifiedDeadline(0u); } } - viz::SurfaceId primary_surface_id(frame_sink_id_, - pending_local_surface_id_); - content_layer_->SetPrimarySurfaceId(primary_surface_id, deadline_policy); + content_layer_->SetPrimarySurfaceId(new_primary_surface_id, + deadline_policy); content_layer_->SetBounds(new_pending_size_in_pixels); } } @@ -397,31 +415,13 @@ void DelegatedFrameHostAndroid::OnFirstSurfaceActivation( if (!enable_surface_synchronization_) return; - // If there's no primary surface, then we don't wish to display content at - // this time (e.g. the view is hidden) and so we don't need a fallback - // surface either. Since we won't use the fallback surface, we drop the - // temporary reference here to save resources. - if (!content_layer_->primary_surface_id().is_valid()) { - host_frame_sink_manager_->DropTemporaryReference(surface_info.id()); - return; - } - - content_layer_->SetFallbackSurfaceId(surface_info.id()); active_local_surface_id_ = surface_info.id().local_surface_id(); - active_device_scale_factor_ = surface_info.device_scale_factor(); // TODO(fsamuel): "SwappedFrame" is a bad name. Also, this method doesn't // really need to take in visiblity. FrameEvictor already has the latest // visibility state. frame_evictor_->SwappedFrame(frame_evictor_->visible()); // Note: the frame may have been evicted immediately. - - if (!pending_first_frame_requests_.empty()) { - DCHECK(CanCopyFromCompositingSurface()); - for (auto& request : pending_first_frame_requests_) - ProcessCopyOutputRequest(std::move(request)); - pending_first_frame_requests_.clear(); - } } void DelegatedFrameHostAndroid::OnFrameTokenChanged(uint32_t frame_token) { @@ -441,34 +441,12 @@ void DelegatedFrameHostAndroid::CreateCompositorFrameSinkSupport() { this, frame_sink_id_, is_root, needs_sync_points); } -void DelegatedFrameHostAndroid::ProcessCopyOutputRequest( - std::unique_ptr<viz::CopyOutputRequest> request) { - if (!request->has_area()) - request->set_area(gfx::Rect(pending_surface_size_in_pixels_)); - - if (request->has_result_selection()) { - const gfx::Rect& area = request->area(); - const gfx::Rect& result_selection = request->result_selection(); - if (area.IsEmpty() || result_selection.IsEmpty()) { - // Viz would normally return an empty result for an empty selection. - // However, this guard here is still necessary to protect against setting - // an illegal scaling ratio. - return; - } - request->SetScaleRatio( - gfx::Vector2d(area.width(), area.height()), - gfx::Vector2d(result_selection.width(), result_selection.height())); - } - - host_frame_sink_manager_->RequestCopyOfOutput( - viz::SurfaceId(frame_sink_id_, pending_local_surface_id_), - std::move(request)); +viz::SurfaceId DelegatedFrameHostAndroid::SurfaceId() const { + return viz::SurfaceId(frame_sink_id_, active_local_surface_id_); } -viz::SurfaceId DelegatedFrameHostAndroid::SurfaceId() const { - return content_layer_ && content_layer_->fallback_surface_id() - ? *content_layer_->fallback_surface_id() - : viz::SurfaceId(); +bool DelegatedFrameHostAndroid::HasPrimarySurface() const { + return content_layer_ && content_layer_->primary_surface_id().is_valid(); } bool DelegatedFrameHostAndroid::HasFallbackSurface() const { @@ -478,27 +456,41 @@ bool DelegatedFrameHostAndroid::HasFallbackSurface() const { void DelegatedFrameHostAndroid::TakeFallbackContentFrom( DelegatedFrameHostAndroid* other) { - if (HasFallbackSurface() || !other->HasFallbackSurface()) + if (HasFallbackSurface() || !other->HasPrimarySurface()) return; - if (!enable_surface_synchronization_) { - if (content_layer_) { - content_layer_->SetPrimarySurfaceId( - *other->content_layer_->fallback_surface_id(), - cc::DeadlinePolicy::UseDefaultDeadline()); + if (enable_surface_synchronization_) { + const viz::SurfaceId& other_primary = + other->content_layer_->primary_surface_id(); + const base::Optional<viz::SurfaceId>& other_fallback = + other->content_layer_->fallback_surface_id(); + viz::SurfaceId desired_fallback; + if (!other->HasFallbackSurface() || + !other_primary.IsSameOrNewerThan(*other_fallback)) { + desired_fallback = other_primary.ToSmallestId(); } else { - const auto& surface_id = other->SurfaceId(); - active_local_surface_id_ = surface_id.local_surface_id(); - pending_local_surface_id_ = active_local_surface_id_; - active_device_scale_factor_ = other->active_device_scale_factor_; - pending_surface_size_in_pixels_ = other->pending_surface_size_in_pixels_; - has_transparent_background_ = other->has_transparent_background_; - content_layer_ = CreateSurfaceLayer( - surface_id, surface_id, other->content_layer_->bounds(), - cc::DeadlinePolicy::UseDefaultDeadline(), - other->content_layer_->contents_opaque()); - view_->GetLayer()->AddChild(content_layer_); + desired_fallback = *other_fallback; } + content_layer_->SetFallbackSurfaceId( + other->content_layer_->primary_surface_id().ToSmallestId()); + return; + } + + if (content_layer_) { + content_layer_->SetPrimarySurfaceId( + *other->content_layer_->fallback_surface_id(), + cc::DeadlinePolicy::UseDefaultDeadline()); + } else { + const auto& surface_id = other->SurfaceId(); + active_local_surface_id_ = surface_id.local_surface_id(); + pending_local_surface_id_ = active_local_surface_id_; + pending_surface_size_in_pixels_ = other->pending_surface_size_in_pixels_; + has_transparent_background_ = other->has_transparent_background_; + content_layer_ = CreateSurfaceLayer( + surface_id, surface_id, other->content_layer_->bounds(), + cc::DeadlinePolicy::UseDefaultDeadline(), + other->content_layer_->contents_opaque()); + view_->GetLayer()->AddChild(content_layer_); } content_layer_->SetFallbackSurfaceId( *other->content_layer_->fallback_surface_id()); @@ -509,7 +501,6 @@ void DelegatedFrameHostAndroid::DidNavigate() { return; first_local_surface_id_after_navigation_ = pending_local_surface_id_; - received_frame_after_navigation_ = false; } } // namespace ui diff --git a/chromium/ui/android/delegated_frame_host_android.h b/chromium/ui/android/delegated_frame_host_android.h index 13a83cfebae..eef5417b4eb 100644 --- a/chromium/ui/android/delegated_frame_host_android.h +++ b/chromium/ui/android/delegated_frame_host_android.h @@ -134,6 +134,7 @@ class UI_ANDROID_EXPORT DelegatedFrameHostAndroid // Returns the ID for the current Surface. Returns an invalid ID if no // surface exists (!HasDelegatedContent()). viz::SurfaceId SurfaceId() const; + bool HasPrimarySurface() const; bool HasFallbackSurface() const; void TakeFallbackContentFrom(DelegatedFrameHostAndroid* other); @@ -201,16 +202,10 @@ class UI_ANDROID_EXPORT DelegatedFrameHostAndroid // Whether we've received a frame from the renderer since navigating. // Only used when surface synchronization is on. viz::LocalSurfaceId first_local_surface_id_after_navigation_; - bool received_frame_after_navigation_ = false; - - std::vector<std::unique_ptr<viz::CopyOutputRequest>> - pending_first_frame_requests_; // The surface id that was most recently activated by // OnFirstSurfaceActivation. viz::LocalSurfaceId active_local_surface_id_; - // The scale factor of the above surface. - float active_device_scale_factor_ = 0.f; // The local surface id as of the most recent call to // EmbedSurface. This is the surface that we expect future frames to diff --git a/chromium/ui/android/delegated_frame_host_android_unittest.cc b/chromium/ui/android/delegated_frame_host_android_unittest.cc index 013ebd2205b..9248c71eb4d 100644 --- a/chromium/ui/android/delegated_frame_host_android_unittest.cc +++ b/chromium/ui/android/delegated_frame_host_android_unittest.cc @@ -8,6 +8,7 @@ #include "cc/layers/layer.h" #include "cc/layers/solid_color_layer.h" #include "cc/layers/surface_layer.h" +#include "cc/trees/layer_tree_host.h" #include "components/viz/common/hit_test/hit_test_region_list.h" #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h" #include "components/viz/host/host_frame_sink_manager.h" @@ -69,18 +70,13 @@ class MockWindowAndroidCompositor : public WindowAndroidCompositor { } }; -class MockCompositorLockManagerClient : public ui::CompositorLockManagerClient { - public: - MOCK_METHOD1(OnCompositorLockStateChanged, void(bool)); -}; - class DelegatedFrameHostAndroidTest : public testing::Test { public: DelegatedFrameHostAndroidTest() : frame_sink_manager_impl_(&shared_bitmap_manager_), frame_sink_id_(1, 1), task_runner_(new base::TestMockTimeTaskRunner()), - lock_manager_(task_runner_, &lock_manager_client_) { + lock_manager_(task_runner_) { host_frame_sink_manager_.SetLocalManager(&frame_sink_manager_impl_); frame_sink_manager_impl_.SetLocalClient(&host_frame_sink_manager_); } @@ -98,9 +94,12 @@ class DelegatedFrameHostAndroidTest : public testing::Test { ui::CompositorLock* GetLock(CompositorLockClient* client, base::TimeDelta time_delta) { - return lock_manager_.GetCompositorLock(client, time_delta).release(); + return lock_manager_.GetCompositorLock(client, time_delta, nullptr) + .release(); } + bool IsLocked() const { return lock_manager_.IsLocked(); } + void SubmitCompositorFrame(const gfx::Size& frame_size = gfx::Size(10, 10)) { viz::CompositorFrame frame = viz::CompositorFrameBuilder() @@ -115,13 +114,11 @@ class DelegatedFrameHostAndroidTest : public testing::Test { .WillOnce(Return(true)); EXPECT_CALL(compositor_, DoGetCompositorLock(frame_host_.get(), _)) .WillOnce(Invoke(this, &DelegatedFrameHostAndroidTest::GetLock)); - EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(true)) - .Times(1); frame_host_->AttachToCompositor(&compositor_); + EXPECT_TRUE(IsLocked()); - EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(false)) - .Times(1); SubmitCompositorFrame(frame_size); + EXPECT_FALSE(IsLocked()); } protected: @@ -134,7 +131,6 @@ class DelegatedFrameHostAndroidTest : public testing::Test { viz::FrameSinkId frame_sink_id_; viz::ParentLocalSurfaceIdAllocator allocator_; std::unique_ptr<DelegatedFrameHostAndroid> frame_host_; - MockCompositorLockManagerClient lock_manager_client_; scoped_refptr<base::TestMockTimeTaskRunner> task_runner_; CompositorLockManager lock_manager_; }; @@ -192,14 +188,12 @@ TEST_F(DelegatedFrameHostAndroidTest, CompositorLockDuringFirstFrame) { EXPECT_CALL(compositor_, IsDrawingFirstVisibleFrame()).WillOnce(Return(true)); EXPECT_CALL(compositor_, DoGetCompositorLock(frame_host_.get(), _)) .WillOnce(Invoke(this, &DelegatedFrameHostAndroidTest::GetLock)); - EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(true)) - .Times(1); frame_host_->AttachToCompositor(&compositor_); + EXPECT_TRUE(IsLocked()); // Lock should be released when we submit a compositor frame. - EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(false)) - .Times(1); SubmitCompositorFrame(); + EXPECT_FALSE(IsLocked()); } TEST_F(DelegatedFrameHostAndroidTest, CompositorLockDuringLaterFrame) { @@ -226,14 +220,12 @@ TEST_F(DelegatedFrameHostAndroidTest, CompositorLockReleasedWithDetach) { EXPECT_CALL(compositor_, IsDrawingFirstVisibleFrame()).WillOnce(Return(true)); EXPECT_CALL(compositor_, DoGetCompositorLock(frame_host_.get(), _)) .WillOnce(Invoke(this, &DelegatedFrameHostAndroidTest::GetLock)); - EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(true)) - .Times(1); frame_host_->AttachToCompositor(&compositor_); + EXPECT_TRUE(IsLocked()); // Lock should be released when we detach. - EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(false)) - .Times(1); frame_host_->DetachFromCompositor(); + EXPECT_FALSE(IsLocked()); } TEST_F(DelegatedFrameHostAndroidTest, ResizeLockBasic) { @@ -246,19 +238,16 @@ TEST_F(DelegatedFrameHostAndroidTest, ResizeLockBasic) { // Tell the frame host to resize, it should take a lock. EXPECT_CALL(compositor_, DoGetCompositorLock(frame_host_.get(), _)) .WillOnce(Invoke(this, &DelegatedFrameHostAndroidTest::GetLock)); - EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(true)) - .Times(1); frame_host_->PixelSizeWillChange(gfx::Size(50, 50)); + EXPECT_TRUE(IsLocked()); // Submit a frame of the wrong size, nothing should change. - EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(false)) - .Times(0); SubmitCompositorFrame(gfx::Size(20, 20)); + EXPECT_TRUE(IsLocked()); // Submit a frame with the right size, the lock should release. - EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(false)) - .Times(1); SubmitCompositorFrame(gfx::Size(50, 50)); + EXPECT_FALSE(IsLocked()); } TEST_F(DelegatedFrameHostAndroidTest, ResizeLockNotTakenIfNoSizeChange) { @@ -269,9 +258,8 @@ TEST_F(DelegatedFrameHostAndroidTest, ResizeLockNotTakenIfNoSizeChange) { SetUpValidFrame(gfx::Size(10, 10)); // Tell the frame host to resize to the existing size, nothing should happen. - EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(true)) - .Times(0); frame_host_->PixelSizeWillChange(gfx::Size(10, 10)); + EXPECT_FALSE(IsLocked()); } TEST_F(DelegatedFrameHostAndroidTest, ResizeLockReleasedWithDetach) { @@ -284,14 +272,12 @@ TEST_F(DelegatedFrameHostAndroidTest, ResizeLockReleasedWithDetach) { // Tell the frame host to resize, it should take a lock. EXPECT_CALL(compositor_, DoGetCompositorLock(frame_host_.get(), _)) .WillOnce(Invoke(this, &DelegatedFrameHostAndroidTest::GetLock)); - EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(true)) - .Times(1); frame_host_->PixelSizeWillChange(gfx::Size(50, 50)); + EXPECT_TRUE(IsLocked()); // Lock should be released when we detach. - EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(false)) - .Times(1); frame_host_->DetachFromCompositor(); + EXPECT_FALSE(IsLocked()); } TEST_F(DelegatedFrameHostAndroidTest, TestBothCompositorLocks) { @@ -303,21 +289,18 @@ TEST_F(DelegatedFrameHostAndroidTest, TestBothCompositorLocks) { EXPECT_CALL(compositor_, IsDrawingFirstVisibleFrame()).WillOnce(Return(true)); EXPECT_CALL(compositor_, DoGetCompositorLock(frame_host_.get(), _)) .WillOnce(Invoke(this, &DelegatedFrameHostAndroidTest::GetLock)); - EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(true)) - .Times(1); frame_host_->AttachToCompositor(&compositor_); + EXPECT_TRUE(IsLocked()); // Tell the frame host to resize, it should take a second lock. EXPECT_CALL(compositor_, DoGetCompositorLock(frame_host_.get(), _)) .WillOnce(Invoke(this, &DelegatedFrameHostAndroidTest::GetLock)); - EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(true)) - .Times(0); frame_host_->PixelSizeWillChange(gfx::Size(50, 50)); + EXPECT_TRUE(IsLocked()); // Submit a compositor frame of the right size, both locks should release. - EXPECT_CALL(lock_manager_client_, OnCompositorLockStateChanged(false)) - .Times(1); SubmitCompositorFrame(gfx::Size(50, 50)); + EXPECT_FALSE(IsLocked()); } } // namespace diff --git a/chromium/ui/android/display_android_manager.cc b/chromium/ui/android/display_android_manager.cc index b7c04d5e362..7352daa7127 100644 --- a/chromium/ui/android/display_android_manager.cc +++ b/chromium/ui/android/display_android_manager.cc @@ -89,7 +89,7 @@ void DisplayAndroidManager::UpdateDisplay( display::Display display(sdkDisplayId, bounds_in_dip); if (!Display::HasForceDeviceScaleFactor()) display.set_device_scale_factor(dipScale); - if (!Display::HasForceColorProfile()) { + if (!Display::HasForceDisplayColorProfile()) { // TODO(ccameron): Use CreateDisplayP3D65 if isWideColorGamut is true, once // the feature is ready to use. display.set_color_space(gfx::ColorSpace::CreateSRGB()); diff --git a/chromium/ui/android/junit/src/org/chromium/ui/AsyncViewProviderTest.java b/chromium/ui/android/junit/src/org/chromium/ui/AsyncViewProviderTest.java new file mode 100644 index 00000000000..37e04ef6b85 --- /dev/null +++ b/chromium/ui/android/junit/src/org/chromium/ui/AsyncViewProviderTest.java @@ -0,0 +1,131 @@ +// 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. + +package org.chromium.ui; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowLooper; + +import org.chromium.base.ThreadUtils; +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.ui.shadows.ShadowAsyncLayoutInflater; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Tests logic in the AsyncViewProvider class. + */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE, shadows = {ShadowAsyncLayoutInflater.class}) +public class AsyncViewProviderTest { + private LinearLayout mRoot; + private AsyncViewStub mAsyncViewStub; + private AsyncViewProvider<View> mAsyncViewProvider; + private final AtomicInteger mEventCount = new AtomicInteger(); + private static final int MAIN_LAYOUT_RESOURCE_ID = org.chromium.test.ui.R.layout.main_view; + private static final int INFLATE_LAYOUT_RESOURCE_ID = + org.chromium.test.ui.R.layout.inflated_view; + private static final int STUB_ID = org.chromium.test.ui.R.id.view_stub; + private static final int INFLATED_VIEW_ID = org.chromium.test.ui.R.id.inflated_view; + private static final int PREINFLATED_VIEW_ID = org.chromium.test.ui.R.id.pre_inflated_view; + + @Before + public void setUp() { + mRoot = (LinearLayout) LayoutInflater.from(RuntimeEnvironment.application) + .inflate(MAIN_LAYOUT_RESOURCE_ID, null); + mAsyncViewStub = mRoot.findViewById(STUB_ID); + mAsyncViewStub.setLayoutResource(INFLATE_LAYOUT_RESOURCE_ID); + mAsyncViewStub.setShouldInflateOnBackgroundThread(true); + mAsyncViewProvider = AsyncViewProvider.of(mAsyncViewStub, INFLATED_VIEW_ID); + mAsyncViewStub.setId(STUB_ID); + mEventCount.set(0); + } + + @Test + public void testCreatesUnloadedProviderIfNotInflated() { + AsyncViewProvider provider = AsyncViewProvider.of(mAsyncViewStub, INFLATED_VIEW_ID); + assertNull(provider.get()); + } + + @Test + public void testCreatesLoadedProviderIfInflated() { + mAsyncViewStub.inflate(); + ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); + AsyncViewProvider provider = AsyncViewProvider.of(mAsyncViewStub, INFLATED_VIEW_ID); + assertNotNull(provider.get()); + } + + @Test + public void testCreatesUnloadedProviderUsingResourceIds() { + AsyncViewProvider provider = AsyncViewProvider.of(mRoot, STUB_ID, INFLATED_VIEW_ID); + assertNull(provider.get()); + } + + @Test + public void testCreatesLoadedProviderUsingResourceIds() { + mAsyncViewStub.inflate(); + ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); + AsyncViewProvider provider = AsyncViewProvider.of(mRoot, STUB_ID, INFLATED_VIEW_ID); + assertNotNull(provider.get()); + } + + @Test + public void testCreatesLoadedProviderUsingResourceIdsWithoutAsyncViewStub() { + AsyncViewProvider provider = AsyncViewProvider.of(mRoot, 0, PREINFLATED_VIEW_ID); + assertNotNull(provider.get()); + assertTrue(provider.get() instanceof ImageView); + } + + @Test + public void testRunsCallbackImmediatelyIfLoaded() { + mAsyncViewStub.inflate(); + ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); + AsyncViewProvider<View> provider = AsyncViewProvider.of(mAsyncViewStub, INFLATED_VIEW_ID); + assertEquals(mEventCount.get(), 0); + provider.whenLoaded((View v) -> { mEventCount.incrementAndGet(); }); + assertEquals(mEventCount.get(), 1); + provider.whenLoaded((View v) -> { mEventCount.incrementAndGet(); }); + assertEquals(mEventCount.get(), 2); + } + + @Test + public void testCallsListenersOnUiThread() { + mAsyncViewProvider.whenLoaded((View v) -> { + assertTrue(ThreadUtils.runningOnUiThread()); + mEventCount.incrementAndGet(); + }); + mAsyncViewStub.inflate(); + ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); + // ensure callback gets called. + assertEquals(mEventCount.get(), 1); + } + + @Test + public void testCallsListenersInOrder() { + mAsyncViewProvider.whenLoaded( + (View v) -> { assertEquals(mEventCount.incrementAndGet(), 1); }); + mAsyncViewProvider.whenLoaded( + (View v) -> { assertEquals(mEventCount.incrementAndGet(), 2); }); + mAsyncViewProvider.whenLoaded( + (View v) -> { assertEquals(mEventCount.decrementAndGet(), 1); }); + assertEquals(mEventCount.get(), 0); + mAsyncViewStub.inflate(); + ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); + assertEquals(mEventCount.get(), 1); + } +}
\ No newline at end of file diff --git a/chromium/ui/android/junit/src/org/chromium/ui/AsyncViewStubTest.java b/chromium/ui/android/junit/src/org/chromium/ui/AsyncViewStubTest.java new file mode 100644 index 00000000000..4c3e9d83b1f --- /dev/null +++ b/chromium/ui/android/junit/src/org/chromium/ui/AsyncViewStubTest.java @@ -0,0 +1,75 @@ +// 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. + +package org.chromium.ui; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import android.view.LayoutInflater; +import android.view.View; +import android.widget.LinearLayout; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowLooper; + +import org.chromium.base.ThreadUtils; +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.ui.shadows.ShadowAsyncLayoutInflater; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Tests logic in the AsyncViewStub class. + */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE, shadows = {ShadowAsyncLayoutInflater.class}) +public class AsyncViewStubTest { + private AsyncViewStub mAsyncViewStub; + private final AtomicInteger mEventCount = new AtomicInteger(); + private static final int MAIN_LAYOUT_RESOURCE_ID = org.chromium.test.ui.R.layout.main_view; + private static final int INFLATE_LAYOUT_RESOURCE_ID = + org.chromium.test.ui.R.layout.inflated_view; + private static final int STUB_ID = org.chromium.test.ui.R.id.view_stub; + + @Before + public void setUp() { + LinearLayout mainView = (LinearLayout) LayoutInflater.from(RuntimeEnvironment.application) + .inflate(MAIN_LAYOUT_RESOURCE_ID, null); + mAsyncViewStub = mainView.findViewById(STUB_ID); + mAsyncViewStub.setLayoutResource(INFLATE_LAYOUT_RESOURCE_ID); + mAsyncViewStub.setShouldInflateOnBackgroundThread(true); + mEventCount.set(0); + } + + @Test + public void testCallsListenersOnUiThread() { + mAsyncViewStub.addOnInflateListener((View v) -> { + assertTrue(ThreadUtils.runningOnUiThread()); + mEventCount.incrementAndGet(); + }); + mAsyncViewStub.inflate(); + ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); + // ensure callback gets called. + assertEquals(mEventCount.get(), 1); + } + + @Test + public void testCallsListenersInOrder() { + mAsyncViewStub.addOnInflateListener( + (View v) -> { assertEquals(mEventCount.incrementAndGet(), 1); }); + mAsyncViewStub.addOnInflateListener( + (View v) -> { assertEquals(mEventCount.incrementAndGet(), 2); }); + mAsyncViewStub.addOnInflateListener( + (View v) -> { assertEquals(mEventCount.decrementAndGet(), 1); }); + assertEquals(mEventCount.get(), 0); + mAsyncViewStub.inflate(); + ShadowLooper.runUiThreadTasksIncludingDelayedTasks(); + assertEquals(mEventCount.get(), 1); + } +}
\ No newline at end of file diff --git a/chromium/ui/android/junit/src/org/chromium/ui/base/ClipboardTest.java b/chromium/ui/android/junit/src/org/chromium/ui/base/ClipboardTest.java new file mode 100644 index 00000000000..30099db7bd8 --- /dev/null +++ b/chromium/ui/android/junit/src/org/chromium/ui/base/ClipboardTest.java @@ -0,0 +1,56 @@ +// 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. + +package org.chromium.ui.base; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import android.content.ClipData; +import android.content.Intent; +import android.text.SpannableString; +import android.text.style.RelativeSizeSpan; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +import org.chromium.base.test.BaseRobolectricTestRunner; + +/** + * Tests logic in the Clipboard class. + */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class ClipboardTest { + private static final String PLAIN_TEXT = "plain"; + private static final String HTML_TEXT = "<span style=\"color: red;\">HTML</span>"; + + @Test + public void testClipDataToHtmlText() { + Clipboard clipboard = Clipboard.getInstance(); + + // HTML text + ClipData html = ClipData.newHtmlText("html", PLAIN_TEXT, HTML_TEXT); + assertEquals(HTML_TEXT, clipboard.clipDataToHtmlText(html)); + + // Plain text without span + ClipData plainTextNoSpan = ClipData.newPlainText("plain", PLAIN_TEXT); + assertNull(clipboard.clipDataToHtmlText(plainTextNoSpan)); + + // Plain text with span + SpannableString spanned = new SpannableString(PLAIN_TEXT); + spanned.setSpan(new RelativeSizeSpan(2f), 0, 5, 0); + ClipData plainTextSpan = ClipData.newPlainText("plain", spanned); + assertNotNull(clipboard.clipDataToHtmlText(plainTextSpan)); + + // Intent + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("*/*"); + ClipData intentClip = ClipData.newIntent("intent", intent); + assertNull(clipboard.clipDataToHtmlText(intentClip)); + } +} diff --git a/chromium/ui/android/junit/src/org/chromium/ui/base/SelectFileDialogTest.java b/chromium/ui/android/junit/src/org/chromium/ui/base/SelectFileDialogTest.java new file mode 100644 index 00000000000..ebcc7d363f7 --- /dev/null +++ b/chromium/ui/android/junit/src/org/chromium/ui/base/SelectFileDialogTest.java @@ -0,0 +1,147 @@ +// 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. + +package org.chromium.ui.base; + +import static org.junit.Assert.assertEquals; + +import android.net.Uri; +import android.webkit.MimeTypeMap; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.Shadows; +import org.robolectric.annotation.Config; +import org.robolectric.shadows.ShadowMimeTypeMap; + +import org.chromium.base.ContextUtils; +import org.chromium.base.test.BaseRobolectricTestRunner; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Tests logic in the SelectFileDialog class. + */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class SelectFileDialogTest { + /** + * Returns the determined scope for the accepted |fileTypes|. + */ + private int scopeForFileTypes(String... fileTypes) { + SelectFileDialog instance = SelectFileDialog.create((long) 0 /* nativeSelectFileDialog */); + instance.setFileTypesForTests(new ArrayList<String>(Arrays.asList(fileTypes))); + + return instance.determineSelectFileDialogScope(); + } + + @Test + public void testDetermineSelectFileDialogScope() { + assertEquals(SelectFileDialog.SELECT_FILE_DIALOG_SCOPE_GENERIC, scopeForFileTypes()); + assertEquals(SelectFileDialog.SELECT_FILE_DIALOG_SCOPE_GENERIC, scopeForFileTypes("*/*")); + assertEquals( + SelectFileDialog.SELECT_FILE_DIALOG_SCOPE_GENERIC, scopeForFileTypes("text/plain")); + + assertEquals( + SelectFileDialog.SELECT_FILE_DIALOG_SCOPE_IMAGES, scopeForFileTypes("image/*")); + assertEquals( + SelectFileDialog.SELECT_FILE_DIALOG_SCOPE_IMAGES, scopeForFileTypes("image/png")); + assertEquals(SelectFileDialog.SELECT_FILE_DIALOG_SCOPE_GENERIC, + scopeForFileTypes("image/*", "test/plain")); + + assertEquals( + SelectFileDialog.SELECT_FILE_DIALOG_SCOPE_VIDEOS, scopeForFileTypes("video/*")); + assertEquals( + SelectFileDialog.SELECT_FILE_DIALOG_SCOPE_VIDEOS, scopeForFileTypes("video/ogg")); + assertEquals(SelectFileDialog.SELECT_FILE_DIALOG_SCOPE_GENERIC, + scopeForFileTypes("video/*", "test/plain")); + assertEquals(SelectFileDialog.SELECT_FILE_DIALOG_SCOPE_IMAGES, + scopeForFileTypes("image/x-png", "image/gif", "image/jpeg")); + assertEquals(SelectFileDialog.SELECT_FILE_DIALOG_SCOPE_GENERIC, + scopeForFileTypes("image/x-png", "image/gif", "image/jpeg", "text/plain")); + + // Test image extensions only. + assertEquals(SelectFileDialog.SELECT_FILE_DIALOG_SCOPE_IMAGES, + scopeForFileTypes(".jpg", ".jpeg", ".png", ".gif", ".apng", ".tiff", ".tif", ".bmp", + ".pdf", ".xcf", ".webp")); + // Test image extensions mixed with image MIME types. + assertEquals(SelectFileDialog.SELECT_FILE_DIALOG_SCOPE_IMAGES, + scopeForFileTypes(".JPG", ".jpeg", "image/gif", "image/jpeg")); + // Image extensions mixed with image MIME types and other. + assertEquals(SelectFileDialog.SELECT_FILE_DIALOG_SCOPE_GENERIC, + scopeForFileTypes(".jpg", "image/gif", "text/plain")); + // Video extensions only. + assertEquals(SelectFileDialog.SELECT_FILE_DIALOG_SCOPE_VIDEOS, + scopeForFileTypes(".asf", ".avhcd", ".avi", ".flv", ".mov", ".mp4", ".mpeg", ".mpg", + ".swf", ".wmv", ".webm", ".mkv", ".divx")); + // Video extensions and video MIME types. + assertEquals(SelectFileDialog.SELECT_FILE_DIALOG_SCOPE_VIDEOS, + scopeForFileTypes(".avi", ".mp4", "video/ogg")); + // Video extensions and video MIME types and other. + assertEquals(SelectFileDialog.SELECT_FILE_DIALOG_SCOPE_GENERIC, + scopeForFileTypes(".avi", ".mp4", "video/ogg", "text/plain")); + + // Non-image, non-video extension only. + assertEquals(SelectFileDialog.SELECT_FILE_DIALOG_SCOPE_GENERIC, scopeForFileTypes(".doc")); + + assertEquals(SelectFileDialog.SELECT_FILE_DIALOG_SCOPE_IMAGES_AND_VIDEOS, + scopeForFileTypes("video/*", "image/*")); + assertEquals(SelectFileDialog.SELECT_FILE_DIALOG_SCOPE_IMAGES_AND_VIDEOS, + scopeForFileTypes("image/jpeg", "video/ogg")); + assertEquals(SelectFileDialog.SELECT_FILE_DIALOG_SCOPE_GENERIC, + scopeForFileTypes("video/*", "image/*", "text/plain")); + } + + @Test + public void testPhotoPickerLaunchAndMimeTypes() throws Throwable { + ShadowMimeTypeMap shadowMimeTypeMap = Shadows.shadowOf(MimeTypeMap.getSingleton()); + shadowMimeTypeMap.addExtensionMimeTypMapping("jpg", "image/jpeg"); + shadowMimeTypeMap.addExtensionMimeTypMapping("gif", "image/gif"); + shadowMimeTypeMap.addExtensionMimeTypMapping("txt", "text/plain"); + shadowMimeTypeMap.addExtensionMimeTypMapping("mpg", "audio/mpeg"); + + assertEquals("", SelectFileDialog.ensureMimeType("")); + assertEquals("image/jpeg", SelectFileDialog.ensureMimeType(".jpg")); + assertEquals("image/jpeg", SelectFileDialog.ensureMimeType("image/jpeg")); + // Unknown extension, expect default response: + assertEquals("application/octet-stream", SelectFileDialog.ensureMimeType(".flv")); + + assertEquals(null, SelectFileDialog.convertToImageMimeTypes(new ArrayList<>())); + assertEquals(null, SelectFileDialog.convertToImageMimeTypes(Arrays.asList(""))); + assertEquals(null, SelectFileDialog.convertToImageMimeTypes(Arrays.asList("foo/bar"))); + assertEquals(Arrays.asList("image/jpeg"), + SelectFileDialog.convertToImageMimeTypes(Arrays.asList(".jpg"))); + assertEquals(Arrays.asList("image/jpeg"), + SelectFileDialog.convertToImageMimeTypes(Arrays.asList("image/jpeg"))); + assertEquals(Arrays.asList("image/jpeg"), + SelectFileDialog.convertToImageMimeTypes(Arrays.asList(".jpg", "image/jpeg"))); + assertEquals(Arrays.asList("image/gif", "image/jpeg"), + SelectFileDialog.convertToImageMimeTypes(Arrays.asList(".gif", "image/jpeg"))); + + // Returns null because generic picker is required (due to addition of .txt file). + assertEquals(null, + SelectFileDialog.convertToImageMimeTypes( + Arrays.asList(".txt", ".jpg", "image/jpeg"))); + // Returns null because video file is included. + assertEquals(null, + SelectFileDialog.convertToImageMimeTypes( + Arrays.asList(".jpg", "image/jpeg", ".mpg"))); + } + + @Test + public void testMultipleFileSelectorWithFileUris() throws Throwable { + SelectFileDialog selectFileDialog = new SelectFileDialog(0); + Uri[] filePathArray = new Uri[] { + Uri.parse("file:///storage/emulated/0/DCIM/Camera/IMG_0.jpg"), + Uri.parse("file:///storage/emulated/0/DCIM/Camera/IMG_1.jpg")}; + SelectFileDialog.GetDisplayNameTask task = selectFileDialog.new GetDisplayNameTask( + ContextUtils.getApplicationContext(), true, filePathArray); + task.doInBackground(); + assertEquals(task.mFilePaths[0].toString(), + "///storage/emulated/0/DCIM/Camera/IMG_0.jpg"); + assertEquals(task.mFilePaths[1].toString(), + "///storage/emulated/0/DCIM/Camera/IMG_1.jpg"); + } +} diff --git a/chromium/ui/android/junit/src/org/chromium/ui/drawable/StateListDrawableBuilderTest.java b/chromium/ui/android/junit/src/org/chromium/ui/drawable/StateListDrawableBuilderTest.java new file mode 100644 index 00000000000..48911eb6a5e --- /dev/null +++ b/chromium/ui/android/junit/src/org/chromium/ui/drawable/StateListDrawableBuilderTest.java @@ -0,0 +1,62 @@ +// 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. + +package org.chromium.ui.drawable; + +import static org.junit.Assert.assertEquals; +import static org.robolectric.Shadows.shadowOf; + +import android.graphics.drawable.AnimatedStateListDrawable; +import android.graphics.drawable.StateListDrawable; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; +import org.robolectric.shadow.api.Shadow; +import org.robolectric.shadows.ShadowStateListDrawable; + +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.ui.shadows.ShadowAnimatedStateListDrawable; +import org.chromium.ui.shadows.ShadowAppCompatResources; + +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE, + shadows = {ShadowAppCompatResources.class, ShadowAnimatedStateListDrawable.class}) +public class StateListDrawableBuilderTest { + private static final int[] CHECKED_STATE = new int[] {android.R.attr.state_checked}; + private static final int[] WILDCARD_STATE = new int[0]; + private static final int CHECKED_DRAWABLE = 34567; + private static final int DEFAULT_DRAWABLE = 45678; + + @Test + @Config(sdk = 18) + public void testPreL() { + StateListDrawableBuilder b = new StateListDrawableBuilder(RuntimeEnvironment.application); + b.addState(CHECKED_DRAWABLE, android.R.attr.state_checked); + b.addState(DEFAULT_DRAWABLE, WILDCARD_STATE); + StateListDrawable result = b.build(); + assertEquals(result.getClass(), StateListDrawable.class); + ShadowStateListDrawable drawable = shadowOf(result); + assertEquals(CHECKED_DRAWABLE, + shadowOf(drawable.getDrawableForState(CHECKED_STATE)).getCreatedFromResId()); + assertEquals(DEFAULT_DRAWABLE, + shadowOf(drawable.getDrawableForState(WILDCARD_STATE)).getCreatedFromResId()); + } + + @Test + @Config(sdk = 21) + public void testPostL() { + StateListDrawableBuilder b = new StateListDrawableBuilder(RuntimeEnvironment.application); + b.addState(CHECKED_DRAWABLE, android.R.attr.state_checked); + b.addState(DEFAULT_DRAWABLE, WILDCARD_STATE); + StateListDrawable result = b.build(); + assertEquals(result.getClass(), AnimatedStateListDrawable.class); + ShadowAnimatedStateListDrawable drawable = Shadow.extract(result); + assertEquals(CHECKED_DRAWABLE, + shadowOf(drawable.getDrawableForState(CHECKED_STATE)).getCreatedFromResId()); + assertEquals(DEFAULT_DRAWABLE, + shadowOf(drawable.getDrawableForState(WILDCARD_STATE)).getCreatedFromResId()); + } +} diff --git a/chromium/ui/android/junit/src/org/chromium/ui/shadows/ShadowAnimatedStateListDrawable.java b/chromium/ui/android/junit/src/org/chromium/ui/shadows/ShadowAnimatedStateListDrawable.java new file mode 100644 index 00000000000..94120e582ce --- /dev/null +++ b/chromium/ui/android/junit/src/org/chromium/ui/shadows/ShadowAnimatedStateListDrawable.java @@ -0,0 +1,18 @@ +// 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. + +package org.chromium.ui.shadows; + +import android.graphics.drawable.AnimatedStateListDrawable; +import android.graphics.drawable.Drawable; + +import org.robolectric.annotation.Implements; +import org.robolectric.shadows.ShadowStateListDrawable; + +@Implements(AnimatedStateListDrawable.class) +public class ShadowAnimatedStateListDrawable extends ShadowStateListDrawable { + public void addState(int[] stateSet, Drawable drawable, int stateId) { + addState(stateSet, drawable); + } +} diff --git a/chromium/ui/android/junit/src/org/chromium/ui/shadows/ShadowAppCompatResources.java b/chromium/ui/android/junit/src/org/chromium/ui/shadows/ShadowAppCompatResources.java new file mode 100644 index 00000000000..036ed79a38e --- /dev/null +++ b/chromium/ui/android/junit/src/org/chromium/ui/shadows/ShadowAppCompatResources.java @@ -0,0 +1,25 @@ +// 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. + +package org.chromium.ui.shadows; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.support.annotation.DrawableRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.content.res.AppCompatResources; + +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; +import org.robolectric.shadows.ShadowDrawable; + +@Implements(AppCompatResources.class) +public class ShadowAppCompatResources { + @Implementation + @Nullable + public static Drawable getDrawable(@NonNull Context context, @DrawableRes int resId) { + return ShadowDrawable.createFromResourceId(resId); + } +} diff --git a/chromium/ui/android/junit/src/org/chromium/ui/shadows/ShadowAppCompatResourcesTest.java b/chromium/ui/android/junit/src/org/chromium/ui/shadows/ShadowAppCompatResourcesTest.java new file mode 100644 index 00000000000..c9d359dfb77 --- /dev/null +++ b/chromium/ui/android/junit/src/org/chromium/ui/shadows/ShadowAppCompatResourcesTest.java @@ -0,0 +1,32 @@ +// 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. + +package org.chromium.ui.shadows; + +import static org.junit.Assert.assertEquals; +import static org.robolectric.Shadows.shadowOf; + +import android.graphics.drawable.Drawable; +import android.support.v7.content.res.AppCompatResources; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.annotation.Config; + +import org.chromium.base.test.BaseRobolectricTestRunner; + +/** Tests logic in the ShadowAppCompatResources class. */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE, shadows = {ShadowAppCompatResources.class}) +public class ShadowAppCompatResourcesTest { + private static final int DRAWABLE_RES_ID = 34567; + + @Test + public void testShadowAppCompatResources() { + Drawable drawable = + AppCompatResources.getDrawable(RuntimeEnvironment.application, DRAWABLE_RES_ID); + assertEquals(DRAWABLE_RES_ID, shadowOf(drawable).getCreatedFromResId()); + } +} diff --git a/chromium/ui/android/junit/src/org/chromium/ui/shadows/ShadowAsyncLayoutInflater.java b/chromium/ui/android/junit/src/org/chromium/ui/shadows/ShadowAsyncLayoutInflater.java new file mode 100644 index 00000000000..0eb84725327 --- /dev/null +++ b/chromium/ui/android/junit/src/org/chromium/ui/shadows/ShadowAsyncLayoutInflater.java @@ -0,0 +1,33 @@ +// 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. + +package org.chromium.ui.shadows; + +import android.support.annotation.LayoutRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.view.AsyncLayoutInflater; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import org.robolectric.annotation.Implementation; +import org.robolectric.annotation.Implements; + +import org.chromium.base.ThreadUtils; + +/** + * Shadow implementation of AsyncLayoutInflater that inflates on the UI thread then posts the + * callback on the UI thread delayed. + */ +@Implements(AsyncLayoutInflater.class) +public class ShadowAsyncLayoutInflater { + @Implementation + public void inflate(@LayoutRes int resid, @Nullable ViewGroup parent, + @NonNull AsyncLayoutInflater.OnInflateFinishedListener callback) { + View inflatedView = LayoutInflater.from(parent.getContext()).inflate(resid, parent, false); + ThreadUtils.postOnUiThreadDelayed( + () -> callback.onInflateFinished(inflatedView, inflatedView.getId(), parent), 500); + } +}
\ No newline at end of file diff --git a/chromium/ui/android/junit/src/org/chromium/ui/text/SpanApplierTest.java b/chromium/ui/android/junit/src/org/chromium/ui/text/SpanApplierTest.java new file mode 100644 index 00000000000..7b8fc7ace75 --- /dev/null +++ b/chromium/ui/android/junit/src/org/chromium/ui/text/SpanApplierTest.java @@ -0,0 +1,179 @@ +// Copyright 2014 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. + +package org.chromium.ui.text; + +import android.text.SpannableString; +import android.text.style.BulletSpan; +import android.text.style.QuoteSpan; +import android.text.style.ScaleXSpan; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +import org.chromium.base.test.BaseRobolectricTestRunner; +import org.chromium.ui.text.SpanApplier.SpanInfo; + +/** + * Tests public methods in SpanApplier. + */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public class SpanApplierTest { + @Test + public void testApplySpan() { + String input = "Lorem ipsum <span>dolor</span> sit amet."; + String output = "Lorem ipsum dolor sit amet."; + SpanInfo span = new SpanInfo("<span>", "</span>", new QuoteSpan()); + + SpannableString expectedOutput = new SpannableString(output); + expectedOutput.setSpan(span.mSpan, 12, 17, 0); + SpannableString actualOutput = SpanApplier.applySpans(input, span); + + assertSpannableStringEquality(expectedOutput, actualOutput); + } + + @Test + public void testApplyMultipleSpans() { + String input = "Lorem <link>ipsum</link> dolor sit amet, " + + "<cons>consectetur adipiscing</cons> <elit>elit. Proin<endElit> consectetur."; + String output = "Lorem ipsum dolor sit amet, " + + "consectetur adipiscing elit. Proin consectetur."; + SpanInfo linkSpan = new SpanInfo("<link>", "</link>", new QuoteSpan()); + SpanInfo consSpan = new SpanInfo("<cons>", "</cons>", new BulletSpan()); + SpanInfo elitSpan = new SpanInfo("<elit>", "<endElit>", new ScaleXSpan(1)); + + SpannableString expectedOutput = new SpannableString(output); + expectedOutput.setSpan(linkSpan.mSpan, 6, 11, 0); + expectedOutput.setSpan(consSpan.mSpan, 28, 50, 0); + expectedOutput.setSpan(elitSpan.mSpan, 51, 62, 0); + SpannableString actualOutput = SpanApplier.applySpans(input, elitSpan, consSpan, linkSpan); + + assertSpannableStringEquality(expectedOutput, actualOutput); + } + + @Test + public void testEndTagMissingInInput() { + String input = "Lorem ipsum <span>dolor</> sit amet."; + SpanInfo span = new SpanInfo("<span>", "</span>", new QuoteSpan()); + + try { + SpanApplier.applySpans(input, span); + Assert.fail("Expected IllegalArgumentException to be thrown."); + } catch (IllegalArgumentException e) { + // success + } + } + + @Test + public void testStartTagMissingInInput() { + String input = "Lorem ipsum <>dolor</span> sit amet."; + SpanInfo span = new SpanInfo("<span>", "</span>", new QuoteSpan()); + + try { + SpanApplier.applySpans(input, span); + Assert.fail("Expected IllegalArgumentException to be thrown."); + } catch (IllegalArgumentException e) { + // success + } + } + + @Test + public void testNestedTagsInInput() { + String input = "Lorem ipsum <span>dolor<span2> sit </span2> </span> amet."; + SpanInfo span = new SpanInfo("<span>", "</span>", new QuoteSpan()); + SpanInfo span2 = new SpanInfo("<span2>", "</span2>", new QuoteSpan()); + + try { + SpanApplier.applySpans(input, span, span2); + Assert.fail("Expected IllegalArgumentException to be thrown."); + } catch (IllegalArgumentException e) { + // success + } + } + + @Test + public void testDuplicateTagsInInput() { + String input = "Lorem ipsum <span>dolor</span> <span>sit </span> amet."; + SpanInfo span = new SpanInfo("<span>", "</span>", new QuoteSpan()); + SpanInfo span2 = new SpanInfo("<span>", "</span>", new QuoteSpan()); + + try { + SpanApplier.applySpans(input, span, span2); + Assert.fail("Expected IllegalArgumentException to be thrown."); + } catch (IllegalArgumentException e) { + // success + } + } + + @Test + public void testNullSpan() { + String input = "Lorem <link>ipsum</link> dolor <span>sit</span> amet."; + SpanInfo linkSpan = new SpanInfo("<link>", "</link>", new QuoteSpan()); + SpanInfo nullSpan = new SpanInfo("<span>", "</span>", null); + + String output = "Lorem ipsum dolor sit amet."; + SpannableString expectedOutput = new SpannableString(output); + expectedOutput.setSpan(linkSpan.mSpan, 6, 11, 0); + SpannableString actualOutput = SpanApplier.applySpans(input, linkSpan, nullSpan); + + assertSpannableStringEquality(expectedOutput, actualOutput); + } + + /* + * Tests the attributes of two SpannableStrings and asserts expected equality. + * + * Prior to KitKat, there was no equals method for SpannableString, so we have to + * manually check that the objects are the same. + */ + private void assertSpannableStringEquality( + SpannableString expected, SpannableString actual) { + if (!areSpannableStringsEqual(expected, actual)) { + Assert.fail("Expected string is " + getSpannableStringDescription(expected) + + " Actual string is " + getSpannableStringDescription(actual)); + } + } + + private boolean areSpannableStringsEqual(SpannableString expected, SpannableString actual) { + Object[] expectedSpans = expected.getSpans(0, expected.length(), Object.class); + Object[] actualSpans = actual.getSpans(0, actual.length(), Object.class); + + if (!expected.toString().equals(actual.toString()) + || expectedSpans.length != actualSpans.length) { + return false; + } + + for (int i = 0; i < expectedSpans.length; i++) { + Object expectedSpan = expectedSpans[i]; + Object actualSpan = actualSpans[i]; + if (expectedSpan != actualSpan + || expected.getSpanStart(expectedSpan) != actual.getSpanStart(actualSpan) + || expected.getSpanEnd(expectedSpan) != actual.getSpanEnd(actualSpan)) { + return false; + } + } + + return true; + } + + private String getSpannableStringDescription(SpannableString spannableString) { + Object[] spans = spannableString.getSpans(0, spannableString.length(), Object.class); + StringBuilder description = new StringBuilder(); + description.append("\"" + spannableString + "\"" + " with spans: "); + for (int i = 0; i < spans.length; i++) { + Object span = spans[i]; + description.append(span.getClass().getName() + + " from " + spannableString.getSpanStart(span) + + " to " + spannableString.getSpanEnd(span)); + if (i != spans.length - 1) { + description.append(", "); + } + } + + description.append('.'); + return description.toString(); + } +} diff --git a/chromium/ui/android/junit/src/org/chromium/ui/widget/AnchoredPopupWindowTest.java b/chromium/ui/android/junit/src/org/chromium/ui/widget/AnchoredPopupWindowTest.java new file mode 100644 index 00000000000..ce165e48bb9 --- /dev/null +++ b/chromium/ui/android/junit/src/org/chromium/ui/widget/AnchoredPopupWindowTest.java @@ -0,0 +1,202 @@ +// 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. + +package org.chromium.ui.widget; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.graphics.Rect; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.robolectric.annotation.Config; + +import org.chromium.base.test.BaseRobolectricTestRunner; + +/** + * Unit tests for the static positioning methods in {@link AnchoredPopupWindow}. + */ +@RunWith(BaseRobolectricTestRunner.class) +@Config(manifest = Config.NONE) +public final class AnchoredPopupWindowTest { + private Rect mWindowRect; + int mPopupWidth; + int mPopupHeight; + + @Before + public void setUp() { + mWindowRect = new Rect(0, 0, 600, 1000); + mPopupWidth = 150; + mPopupHeight = 300; + } + + @Test + public void testGetPopupPosition_BelowRight() { + Rect anchorRect = new Rect(10, 10, 20, 20); + + int spaceLeftOfAnchor = + AnchoredPopupWindow.getSpaceLeftOfAnchor(anchorRect, mWindowRect, false); + int spaceRightOfAnchor = + AnchoredPopupWindow.getSpaceRightOfAnchor(anchorRect, mWindowRect, false); + boolean positionToLeft = AnchoredPopupWindow.shouldPositionLeftOfAnchor( + spaceLeftOfAnchor, spaceRightOfAnchor, mPopupWidth, false, false); + + assertEquals("Space left of anchor incorrect.", 10, spaceLeftOfAnchor); + assertEquals("Space right of anchor incorrect.", 580, spaceRightOfAnchor); + assertFalse("positionToLeft incorrect.", positionToLeft); + + int x = AnchoredPopupWindow.getPopupX(anchorRect, mWindowRect, mPopupWidth, 0, false, + AnchoredPopupWindow.HORIZONTAL_ORIENTATION_MAX_AVAILABLE_SPACE, false); + int y = AnchoredPopupWindow.getPopupY(anchorRect, mPopupHeight, false, true); + + assertEquals("Wrong x position.", 20, x); + assertEquals("Wrong y position.", 20, y); + } + + @Test + public void testGetPopupPosition_BelowRight_Overlap() { + Rect anchorRect = new Rect(10, 10, 20, 20); + + int spaceLeftOfAnchor = + AnchoredPopupWindow.getSpaceLeftOfAnchor(anchorRect, mWindowRect, true); + int spaceRightOfAnchor = + AnchoredPopupWindow.getSpaceRightOfAnchor(anchorRect, mWindowRect, true); + boolean positionToLeft = AnchoredPopupWindow.shouldPositionLeftOfAnchor( + spaceLeftOfAnchor, spaceRightOfAnchor, mPopupWidth, false, false); + + assertEquals("Space left of anchor incorrect.", 20, spaceLeftOfAnchor); + assertEquals("Space right of anchor incorrect.", 590, spaceRightOfAnchor); + assertFalse("positionToLeft incorrect.", positionToLeft); + + int x = AnchoredPopupWindow.getPopupX(anchorRect, mWindowRect, mPopupWidth, 0, true, + AnchoredPopupWindow.HORIZONTAL_ORIENTATION_MAX_AVAILABLE_SPACE, false); + int y = AnchoredPopupWindow.getPopupY(anchorRect, mPopupHeight, true, true); + + assertEquals("Wrong x position.", 10, x); + assertEquals("Wrong y position.", 10, y); + } + + @Test + public void testGetPopupPosition_BelowCenter() { + Rect anchorRect = new Rect(295, 10, 305, 20); + int x = AnchoredPopupWindow.getPopupX(anchorRect, mWindowRect, mPopupWidth, 0, false, + AnchoredPopupWindow.HORIZONTAL_ORIENTATION_CENTER, false); + int y = AnchoredPopupWindow.getPopupY(anchorRect, mPopupHeight, false, true); + + assertEquals("Wrong x position.", 225, x); + assertEquals("Wrong y position.", 20, y); + } + + @Test + public void getPopupPosition_AboveLeft() { + Rect anchorRect = new Rect(400, 800, 410, 820); + + int spaceLeftOfAnchor = + AnchoredPopupWindow.getSpaceLeftOfAnchor(anchorRect, mWindowRect, false); + int spaceRightOfAnchor = + AnchoredPopupWindow.getSpaceRightOfAnchor(anchorRect, mWindowRect, false); + boolean positionToLeft = AnchoredPopupWindow.shouldPositionLeftOfAnchor( + spaceLeftOfAnchor, spaceRightOfAnchor, mPopupWidth, false, false); + + assertEquals("Space left of anchor incorrect.", 400, spaceLeftOfAnchor); + assertEquals("Space right of anchor incorrect.", 190, spaceRightOfAnchor); + assertTrue("positionToLeft incorrect.", positionToLeft); + + int x = AnchoredPopupWindow.getPopupX(anchorRect, mWindowRect, mPopupWidth, 0, false, + AnchoredPopupWindow.HORIZONTAL_ORIENTATION_MAX_AVAILABLE_SPACE, positionToLeft); + int y = AnchoredPopupWindow.getPopupY(anchorRect, mPopupHeight, false, false); + + assertEquals("Wrong x position.", 250, x); + assertEquals("Wrong y position.", 500, y); + } + + @Test + public void testGetPopupPosition_AboveLeft_Overlap() { + Rect anchorRect = new Rect(400, 800, 410, 820); + + int spaceLeftOfAnchor = + AnchoredPopupWindow.getSpaceLeftOfAnchor(anchorRect, mWindowRect, true); + int spaceRightOfAnchor = + AnchoredPopupWindow.getSpaceRightOfAnchor(anchorRect, mWindowRect, true); + boolean positionToLeft = AnchoredPopupWindow.shouldPositionLeftOfAnchor( + spaceLeftOfAnchor, spaceRightOfAnchor, mPopupWidth, false, false); + + assertEquals("Space left of anchor incorrect.", 410, spaceLeftOfAnchor); + assertEquals("Space right of anchor incorrect.", 200, spaceRightOfAnchor); + assertTrue("positionToLeft incorrect.", positionToLeft); + + int x = AnchoredPopupWindow.getPopupX(anchorRect, mWindowRect, mPopupWidth, 0, true, + AnchoredPopupWindow.HORIZONTAL_ORIENTATION_MAX_AVAILABLE_SPACE, true); + int y = AnchoredPopupWindow.getPopupY(anchorRect, mPopupHeight, true, false); + + assertEquals("Wrong x position.", 260, x); + assertEquals("Wrong y position.", 520, y); + } + + @Test + public void testGetPopupPosition_ClampedLeftEdge() { + Rect anchorRect = new Rect(10, 10, 20, 20); + int x = AnchoredPopupWindow.getPopupX(anchorRect, mWindowRect, mPopupWidth, 20, false, + AnchoredPopupWindow.HORIZONTAL_ORIENTATION_MAX_AVAILABLE_SPACE, true); + + assertEquals("Wrong x position.", 20, x); + } + + @Test + public void testGetPopupPosition_ClampedRightEdge() { + Rect anchorRect = new Rect(590, 800, 600, 820); + int x = AnchoredPopupWindow.getPopupX(anchorRect, mWindowRect, mPopupWidth, 20, false, + AnchoredPopupWindow.HORIZONTAL_ORIENTATION_MAX_AVAILABLE_SPACE, true); + + assertEquals("Wrong x position.", 430, x); + } + + @Test + public void testShouldPositionLeftOfAnchor() { + Rect anchorRect = new Rect(300, 10, 310, 20); + int spaceLeftOfAnchor = + AnchoredPopupWindow.getSpaceLeftOfAnchor(anchorRect, mWindowRect, false); + int spaceRightOfAnchor = + AnchoredPopupWindow.getSpaceRightOfAnchor(anchorRect, mWindowRect, false); + boolean positionToLeft = AnchoredPopupWindow.shouldPositionLeftOfAnchor( + spaceLeftOfAnchor, spaceRightOfAnchor, mPopupWidth, false, false); + + assertEquals("Space left of anchor incorrect.", 300, spaceLeftOfAnchor); + assertEquals("Space right of anchor incorrect.", 290, spaceRightOfAnchor); + assertTrue("Should be positioned to the left.", positionToLeft); + + anchorRect = new Rect(250, 10, 260, 20); + spaceLeftOfAnchor = + AnchoredPopupWindow.getSpaceLeftOfAnchor(anchorRect, mWindowRect, false); + spaceRightOfAnchor = + AnchoredPopupWindow.getSpaceRightOfAnchor(anchorRect, mWindowRect, false); + positionToLeft = AnchoredPopupWindow.shouldPositionLeftOfAnchor( + spaceLeftOfAnchor, spaceRightOfAnchor, mPopupWidth, true, true); + + // There is more space to the right, but the popup will still fit to the left and should + // be positioned to the left. + assertEquals("Space left of anchor incorrect.", 250, spaceLeftOfAnchor); + assertEquals("Space right of anchor incorrect.", 340, spaceRightOfAnchor); + assertTrue("Should still be positioned to the left.", positionToLeft); + } + + @Test + public void testGetMaxContentWidth() { + int maxWidth = AnchoredPopupWindow.getMaxContentWidth(300, 600, 10, 10); + assertEquals("Max width should be based on desired width.", 290, maxWidth); + + maxWidth = AnchoredPopupWindow.getMaxContentWidth(300, 300, 10, 10); + assertEquals("Max width should be based on root view width.", 270, maxWidth); + + maxWidth = AnchoredPopupWindow.getMaxContentWidth(0, 600, 10, 10); + assertEquals("Max width should be based on root view width when desired with is 0.", 570, + maxWidth); + + maxWidth = AnchoredPopupWindow.getMaxContentWidth(300, 300, 10, 300); + assertEquals("Max width should be clamped at 0.", 0, maxWidth); + } +} diff --git a/chromium/ui/aura/BUILD.gn b/chromium/ui/aura/BUILD.gn index c97d5dda9cf..cba785ad544 100644 --- a/chromium/ui/aura/BUILD.gn +++ b/chromium/ui/aura/BUILD.gn @@ -47,6 +47,7 @@ jumbo_component("aura") { "mus/focus_synchronizer.h", "mus/focus_synchronizer_delegate.h", "mus/focus_synchronizer_observer.h", + "mus/gesture_synchronizer.h", "mus/in_flight_change.h", "mus/input_method_mus.h", "mus/input_method_mus_delegate.h", @@ -118,6 +119,7 @@ jumbo_component("aura") { "mus/drag_drop_controller_mus.cc", "mus/embed_root.cc", "mus/focus_synchronizer.cc", + "mus/gesture_synchronizer.cc", "mus/in_flight_change.cc", "mus/input_method_mus.cc", "mus/mus_context_factory.cc", @@ -250,6 +252,8 @@ jumbo_static_library("test_support") { "test/mus/test_window_tree_client_delegate.h", "test/mus/test_window_tree_client_setup.cc", "test/mus/test_window_tree_client_setup.h", + "test/mus/window_port_mus_test_helper.cc", + "test/mus/window_port_mus_test_helper.h", "test/mus/window_tree_client_private.cc", "test/mus/window_tree_client_private.h", "test/test_cursor_client.cc", @@ -370,6 +374,7 @@ test("aura_unittests") { "mouse_location_manager_unittest.cc", "mus/drag_drop_controller_mus_unittest.cc", "mus/focus_synchronizer_unittest.cc", + "mus/gesture_synchronizer_unittest.cc", "mus/input_method_mus_unittest.cc", "mus/os_exchange_data_provider_mus_unittest.cc", "mus/property_converter_unittest.cc", diff --git a/chromium/ui/aura/client/aura_constants.cc b/chromium/ui/aura/client/aura_constants.cc index c0b3bf9c200..1ed7c6e50d9 100644 --- a/chromium/ui/aura/client/aura_constants.cc +++ b/chromium/ui/aura/client/aura_constants.cc @@ -24,8 +24,6 @@ DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(AURA_EXPORT, void*) DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(AURA_EXPORT, SkColor) DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(AURA_EXPORT, int32_t) DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(AURA_EXPORT, int64_t) -DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(AURA_EXPORT, - aura::client::WindowEmbedType) DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(AURA_EXPORT, aura::client::FocusClient*) DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(AURA_EXPORT, aura::Window*) @@ -54,9 +52,6 @@ DEFINE_UI_CLASS_PROPERTY_KEY(Window*, kHostWindowKey, nullptr); DEFINE_UI_CLASS_PROPERTY_KEY(bool, kImmersiveFullscreenKey, false); DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(gfx::Size, kMinimumSize, nullptr); DEFINE_UI_CLASS_PROPERTY_KEY(bool, kMirroringEnabledKey, false); -DEFINE_UI_CLASS_PROPERTY_KEY(WindowEmbedType, - kEmbedType, - WindowEmbedType::NONE); DEFINE_UI_CLASS_PROPERTY_KEY(Window*, kChildModalParentKey, nullptr); DEFINE_UI_CLASS_PROPERTY_KEY(ui::ModalType, kModalKey, ui::MODAL_TYPE_NONE); DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(std::string, kNameKey, nullptr); @@ -68,6 +63,7 @@ DEFINE_UI_CLASS_PROPERTY_KEY( DEFINE_UI_CLASS_PROPERTY_KEY(int32_t, kResizeBehaviorKey, ws::mojom::kResizeBehaviorCanResize); +DEFINE_UI_CLASS_PROPERTY_KEY(int, kResizeHandleInset, 0); DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(gfx::Rect, kRestoreBoundsKey, nullptr); DEFINE_UI_CLASS_PROPERTY_KEY( ui::WindowShowState, kShowStateKey, ui::SHOW_STATE_DEFAULT); diff --git a/chromium/ui/aura/client/aura_constants.h b/chromium/ui/aura/client/aura_constants.h index a184412827a..65dc33a5477 100644 --- a/chromium/ui/aura/client/aura_constants.h +++ b/chromium/ui/aura/client/aura_constants.h @@ -23,11 +23,6 @@ namespace aura { namespace client { class FocusClient; -enum class WindowEmbedType { - NONE, - EMBED_IN_OWNER, -}; - // Alphabetical sort. // A property key to store whether accessibility focus falls back to widget or @@ -124,6 +119,12 @@ AURA_EXPORT extern const WindowProperty<ui::WindowShowState>* const // ws::mojom::kResizeBehavior values. AURA_EXPORT extern const WindowProperty<int32_t>* const kResizeBehaviorKey; +// Reserves a number of dip around the window (i.e. inset from its exterior +// border) for event routing back to the top level window. This is used for +// routing events to toplevel window resize handles. It should only be respected +// for restored windows (maximized and fullscreen can't be drag-resized). +AURA_EXPORT extern const WindowProperty<int>* const kResizeHandleInset; + // A property key to store the restore bounds in screen coordinates for a // window. AURA_EXPORT extern const WindowProperty<gfx::Rect*>* const kRestoreBoundsKey; @@ -149,9 +150,6 @@ AURA_EXPORT extern const WindowProperty<int>* const kTopViewInset; // A property key to store the window icon, typically 16x16 for title bars. AURA_EXPORT extern const WindowProperty<gfx::ImageSkia*>* const kWindowIconKey; -// Indicates the type of embedding within the given window. -AURA_EXPORT extern const WindowProperty<WindowEmbedType>* const kEmbedType; - // The corner radius of a window in DIPs. Currently only used for shadows. // Default is -1, meaning "unspecified". 0 Ensures corners are square. AURA_EXPORT extern const WindowProperty<int>* const kWindowCornerRadiusKey; diff --git a/chromium/ui/aura/env.cc b/chromium/ui/aura/env.cc index 07ff6f5bf18..bbd91404c0f 100644 --- a/chromium/ui/aura/env.cc +++ b/chromium/ui/aura/env.cc @@ -6,6 +6,7 @@ #include "base/command_line.h" #include "base/lazy_instance.h" +#include "base/memory/ptr_util.h" #include "services/ws/public/mojom/window_tree.mojom.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/env_input_state_controller.h" @@ -20,6 +21,7 @@ #include "ui/aura/mus/window_tree_client.h" #include "ui/aura/window.h" #include "ui/aura/window_event_dispatcher_observer.h" +#include "ui/aura/window_occlusion_tracker.h" #include "ui/aura/window_port_for_shutdown.h" #include "ui/base/ui_base_features.h" #include "ui/events/event_target_iterator.h" @@ -113,19 +115,8 @@ std::unique_ptr<WindowPort> Env::CreateWindowPort(Window* window) { return std::make_unique<WindowPortForShutdown>(); DCHECK(window_tree_client_); - WindowMusType window_mus_type; - switch (window->GetProperty(aura::client::kEmbedType)) { - case aura::client::WindowEmbedType::NONE: - window_mus_type = WindowMusType::LOCAL; - break; - case aura::client::WindowEmbedType::EMBED_IN_OWNER: - window_mus_type = WindowMusType::EMBED_IN_OWNER; - break; - default: - NOTREACHED(); - } - // Use LOCAL as all other cases are created by WindowTreeClient explicitly. - return std::make_unique<WindowPortMus>(window_tree_client_, window_mus_type); + return std::make_unique<WindowPortMus>(window_tree_client_, + WindowMusType::LOCAL); } void Env::AddObserver(EnvObserver* observer) { @@ -196,6 +187,17 @@ void Env::ScheduleEmbed( window_tree_client_->ScheduleEmbed(std::move(client), std::move(callback)); } +WindowOcclusionTracker* Env::GetWindowOcclusionTracker() { + // TODO(https://crbug.com/867150): DCHECK to ensure LOCAL aura after mus + // code path is wired up. + if (!window_occlusion_tracker_) { + // Use base::WrapUnique + new because of the constructor is private. + window_occlusion_tracker_ = base::WrapUnique(new WindowOcclusionTracker()); + } + + return window_occlusion_tracker_.get(); +} + //////////////////////////////////////////////////////////////////////////////// // Env, private: diff --git a/chromium/ui/aura/env.h b/chromium/ui/aura/env.h index f8fb629389f..3738281bedd 100644 --- a/chromium/ui/aura/env.h +++ b/chromium/ui/aura/env.h @@ -59,6 +59,7 @@ class MouseLocationManager; class MusMouseLocationUpdater; class Window; class WindowEventDispatcherObserver; +class WindowOcclusionTracker; class WindowPort; class WindowTreeClient; class WindowTreeHost; @@ -191,6 +192,9 @@ class AURA_EXPORT Env : public ui::EventTarget, mojo::InterfacePtr<ws::mojom::WindowTreeClient> client, base::OnceCallback<void(const base::UnguessableToken&)> callback); + // Get WindowOcclusionTracker instance. Create it if it is not yet created. + WindowOcclusionTracker* GetWindowOcclusionTracker(); + private: friend class test::EnvTestHelper; friend class test::EnvWindowTreeClientSetter; @@ -285,6 +289,9 @@ class AURA_EXPORT Env : public ui::EventTarget, // Only created if CreateMouseLocationManager() was called. std::unique_ptr<MouseLocationManager> mouse_location_manager_; + // Lazily created for LOCAL aura. + std::unique_ptr<WindowOcclusionTracker> window_occlusion_tracker_; + DISALLOW_COPY_AND_ASSIGN(Env); }; diff --git a/chromium/ui/aura/event_injector.cc b/chromium/ui/aura/event_injector.cc index fd2a7423c05..f67d1fe5721 100644 --- a/chromium/ui/aura/event_injector.cc +++ b/chromium/ui/aura/event_injector.cc @@ -13,24 +13,6 @@ #include "ui/events/event.h" #include "ui/events/event_sink.h" -namespace { -std::unique_ptr<ui::Event> MapEvent(const ui::Event& event) { - if (event.IsScrollEvent()) { - return std::make_unique<ui::PointerEvent>( - ui::MouseWheelEvent(*event.AsScrollEvent())); - } - - if (event.IsMouseEvent()) - return std::make_unique<ui::PointerEvent>(*event.AsMouseEvent()); - - if (event.IsTouchEvent()) - return std::make_unique<ui::PointerEvent>(*event.AsTouchEvent()); - - return ui::Event::Clone(event); -} - -} // namespace - namespace aura { EventInjector::EventInjector() {} @@ -60,7 +42,8 @@ ui::EventDispatchDetails EventInjector::Inject(WindowTreeHost* host, env->window_tree_client_->connector()->BindInterface( ws::mojom::kServiceName, &event_injector_); } - event_injector_->InjectEventNoAck(host->GetDisplayId(), MapEvent(*event)); + event_injector_->InjectEventNoAck(host->GetDisplayId(), + ui::Event::Clone(*event)); return ui::EventDispatchDetails(); } diff --git a/chromium/ui/aura/gestures/gesture_recognizer_unittest.cc b/chromium/ui/aura/gestures/gesture_recognizer_unittest.cc index 86b08db290d..ee72e003826 100644 --- a/chromium/ui/aura/gestures/gesture_recognizer_unittest.cc +++ b/chromium/ui/aura/gestures/gesture_recognizer_unittest.cc @@ -4705,8 +4705,7 @@ TEST_F(GestureRecognizerTest, TransferEventsToRoutesAckCorrectly) { // Transfer event sequence from previous window to the new window. aura::Env::GetInstance()->gesture_recognizer()->TransferEventsTo( - window_1.get(), window_2.get(), - ui::GestureRecognizer::ShouldCancelTouches::DontCancel); + window_1.get(), window_2.get(), ui::TransferTouchesBehavior::kDontCancel); delegate_1->Reset(); delegate_1->ReceivedAck(); diff --git a/chromium/ui/aura/hit_test_data_provider_aura_unittest.cc b/chromium/ui/aura/hit_test_data_provider_aura_unittest.cc index b120625ed1a..79f73481cad 100644 --- a/chromium/ui/aura/hit_test_data_provider_aura_unittest.cc +++ b/chromium/ui/aura/hit_test_data_provider_aura_unittest.cc @@ -8,6 +8,7 @@ #include "components/viz/common/hit_test/hit_test_region_list.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/test/aura_test_base.h" +#include "ui/aura/test/mus/window_port_mus_test_helper.h" #include "ui/aura/window.h" #include "ui/aura/window_targeter.h" #include "ui/gfx/geometry/rect.h" @@ -28,15 +29,15 @@ namespace { // | ---------- | // | hit hit | // ---------------------- -class TestHoleWindowTargeter : public aura::WindowTargeter { +class TestHoleWindowTargeter : public WindowTargeter { public: TestHoleWindowTargeter() = default; ~TestHoleWindowTargeter() override {} private: - // aura::WindowTargeter: - std::unique_ptr<aura::WindowTargeter::HitTestRects> GetExtraHitTestShapeRects( - aura::Window* target) const override { + // WindowTargeter: + std::unique_ptr<WindowTargeter::HitTestRects> GetExtraHitTestShapeRects( + Window* target) const override { gfx::Rect bounds = target->bounds(); int x0 = 0; int x1 = bounds.width() / 3; @@ -46,7 +47,7 @@ class TestHoleWindowTargeter : public aura::WindowTargeter { int y1 = bounds.height() / 3; int y2 = bounds.height() - bounds.height() / 3; int y3 = bounds.height(); - auto shape_rects = std::make_unique<aura::WindowTargeter::HitTestRects>(); + auto shape_rects = std::make_unique<WindowTargeter::HitTestRects>(); shape_rects->emplace_back(x0, y0, bounds.width(), y1 - y0); shape_rects->emplace_back(x0, y1, x1 - x0, y2 - y1); shape_rects->emplace_back(x2, y1, x3 - x2, y2 - y1); @@ -73,23 +74,17 @@ class HitTestDataProviderAuraTest : public test::AuraTestBaseMus { test::AuraTestBaseMus::SetUp(); root_ = std::make_unique<Window>(nullptr); - root_->SetProperty(aura::client::kEmbedType, - aura::client::WindowEmbedType::EMBED_IN_OWNER); root_->Init(ui::LAYER_NOT_DRAWN); root_->SetEventTargeter(std::make_unique<WindowTargeter>()); root_->SetBounds(gfx::Rect(0, 0, 300, 200)); root_->Show(); window2_ = new Window(nullptr); - window2_->SetProperty(aura::client::kEmbedType, - aura::client::WindowEmbedType::EMBED_IN_OWNER); window2_->Init(ui::LAYER_TEXTURED); window2_->SetBounds(gfx::Rect(20, 30, 40, 60)); window2_->Show(); window3_ = new Window(nullptr); - window3_->SetProperty(aura::client::kEmbedType, - aura::client::WindowEmbedType::EMBED_IN_OWNER); window3_->Init(ui::LAYER_TEXTURED); window3_->SetEventTargeter(std::make_unique<WindowTargeter>()); window3_->SetBounds(gfx::Rect(50, 60, 100, 40)); @@ -175,10 +170,14 @@ TEST_F(HitTestDataProviderAuraTest, Stacking) { TEST_F(HitTestDataProviderAuraTest, CustomTargeter) { constexpr int kMouseInset = -5; constexpr int kTouchInset = -10; - auto targeter = std::make_unique<aura::WindowTargeter>(); + auto targeter = std::make_unique<WindowTargeter>(); targeter->SetInsets(gfx::Insets(kMouseInset), gfx::Insets(kTouchInset)); window3()->SetEventTargeter(std::move(targeter)); + targeter = std::make_unique<WindowTargeter>(); + targeter->SetInsets(gfx::Insets(kMouseInset), gfx::Insets(kTouchInset)); + window4()->SetEventTargeter(std::move(targeter)); + window2()->SetEmbedFrameSinkId(viz::FrameSinkId(1, 2)); const base::Optional<viz::HitTestRegionList> hit_test_data = hit_test_data_provider()->GetHitTestData(compositor_frame_); diff --git a/chromium/ui/aura/local/window_port_local.cc b/chromium/ui/aura/local/window_port_local.cc index 56c7db037cc..454a3dea5b2 100644 --- a/chromium/ui/aura/local/window_port_local.cc +++ b/chromium/ui/aura/local/window_port_local.cc @@ -9,6 +9,7 @@ #include "components/viz/client/local_surface_id_provider.h" #include "components/viz/common/features.h" #include "components/viz/host/host_frame_sink_manager.h" +#include "services/ws/public/mojom/window_tree_constants.mojom.h" #include "ui/aura/client/cursor_client.h" #include "ui/aura/env.h" #include "ui/aura/hit_test_data_provider_aura.h" @@ -19,7 +20,9 @@ #include "ui/display/screen.h" namespace aura { + namespace { +static const char* kExo = "Exo"; class ScopedCursorHider { public: @@ -63,7 +66,9 @@ class ScopedCursorHider { } // namespace WindowPortLocal::WindowPortLocal(Window* window) - : window_(window), weak_factory_(this) {} + : WindowPort(WindowPort::Type::kLocal), + window_(window), + weak_factory_(this) {} WindowPortLocal::~WindowPortLocal() { if (frame_sink_id_.is_valid()) { @@ -156,10 +161,16 @@ WindowPortLocal::CreateLayerTreeFrameSink() { params.pipes.compositor_frame_sink_info = std::move(sink_info); params.pipes.client_request = std::move(client_request); params.enable_surface_synchronization = true; + params.client_name = kExo; if (features::IsVizHitTestingDrawQuadEnabled()) { + bool root_accepts_events = + (window_->event_targeting_policy() == + ws::mojom::EventTargetingPolicy::TARGET_ONLY) || + (window_->event_targeting_policy() == + ws::mojom::EventTargetingPolicy::TARGET_AND_DESCENDANTS); params.hit_test_data_provider = std::make_unique<viz::HitTestDataProviderDrawQuad>( - true /* should_ask_for_child_region */); + true /* should_ask_for_child_region */, root_accepts_events); } else { params.hit_test_data_provider = std::make_unique<HitTestDataProviderAura>(window_); diff --git a/chromium/ui/aura/mus/client_surface_embedder.cc b/chromium/ui/aura/mus/client_surface_embedder.cc index 21913636c50..340eef15b1f 100644 --- a/chromium/ui/aura/mus/client_surface_embedder.cc +++ b/chromium/ui/aura/mus/client_surface_embedder.cc @@ -43,6 +43,10 @@ void ClientSurfaceEmbedder::SetPrimarySurfaceId( false /* stretch_content_to_fill_bounds */); } +bool ClientSurfaceEmbedder::HasPrimarySurfaceId() const { + return surface_layer_owner_->layer()->GetPrimarySurfaceId() != nullptr; +} + void ClientSurfaceEmbedder::SetFallbackSurfaceInfo( const viz::SurfaceInfo& surface_info) { fallback_surface_info_ = surface_info; @@ -50,6 +54,16 @@ void ClientSurfaceEmbedder::SetFallbackSurfaceInfo( UpdateSizeAndGutters(); } +void ClientSurfaceEmbedder::SetClientAreaInsets( + const gfx::Insets& client_area_insets) { + if (client_area_insets_ == client_area_insets) + return; + + client_area_insets_ = client_area_insets; + if (inject_gutter_) + UpdateSizeAndGutters(); +} + void ClientSurfaceEmbedder::UpdateSizeAndGutters() { surface_layer_owner_->layer()->SetBounds(gfx::Rect(window_->bounds().size())); if (!inject_gutter_) diff --git a/chromium/ui/aura/mus/client_surface_embedder.h b/chromium/ui/aura/mus/client_surface_embedder.h index cec86d00c37..bcf1ce1bace 100644 --- a/chromium/ui/aura/mus/client_surface_embedder.h +++ b/chromium/ui/aura/mus/client_surface_embedder.h @@ -37,10 +37,15 @@ class AURA_EXPORT ClientSurfaceEmbedder { // on the provided |surface_id|. void SetPrimarySurfaceId(const viz::SurfaceId& surface_id); + bool HasPrimarySurfaceId() const; + // Sets the fallback SurfaceInfo of the surface layer. The clip layer is not // updated. void SetFallbackSurfaceInfo(const viz::SurfaceInfo& surface_info); + void SetClientAreaInsets(const gfx::Insets& client_area_insets); + const gfx::Insets& client_area_insets() const { return client_area_insets_; } + // Update the surface layer size and the right and bottom gutter layers for // the current window size. void UpdateSizeAndGutters(); diff --git a/chromium/ui/aura/mus/embed_root.cc b/chromium/ui/aura/mus/embed_root.cc index 372988eaa30..32fa6b55747 100644 --- a/chromium/ui/aura/mus/embed_root.cc +++ b/chromium/ui/aura/mus/embed_root.cc @@ -123,6 +123,7 @@ void EmbedRoot::OnEmbed(std::unique_ptr<WindowTreeHost> window_tree_host) { focus_client_ = std::make_unique<EmbeddedFocusClient>(window_tree_host->window()); window_tree_host_ = std::move(window_tree_host); + window_tree_host_->Show(); delegate_->OnEmbed(window()); } diff --git a/chromium/ui/aura/mus/gesture_synchronizer.cc b/chromium/ui/aura/mus/gesture_synchronizer.cc new file mode 100644 index 00000000000..ce216f54f35 --- /dev/null +++ b/chromium/ui/aura/mus/gesture_synchronizer.cc @@ -0,0 +1,55 @@ +// 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/aura/mus/gesture_synchronizer.h" + +#include "services/ws/public/mojom/window_tree.mojom.h" +#include "ui/aura/env.h" +#include "ui/aura/mus/window_mus.h" +#include "ui/aura/window.h" +#include "ui/events/gestures/gesture_recognizer.h" + +namespace aura { + +GestureSynchronizer::GestureSynchronizer(ws::mojom::WindowTree* window_tree) + : window_tree_(window_tree) { + Env::GetInstance()->gesture_recognizer()->AddObserver(this); +} + +GestureSynchronizer::~GestureSynchronizer() { + Env::GetInstance()->gesture_recognizer()->RemoveObserver(this); +} + +void GestureSynchronizer::OnActiveTouchesCanceledExcept( + ui::GestureConsumer* not_cancelled) { + ws::Id not_cancelled_window_id = kInvalidServerId; + if (not_cancelled) { + WindowMus* not_cancelled_window = + WindowMus::Get(static_cast<Window*>(not_cancelled)); + not_cancelled_window_id = not_cancelled_window->server_id(); + } + window_tree_->CancelActiveTouchesExcept(not_cancelled_window_id); +} + +void GestureSynchronizer::OnEventsTransferred( + ui::GestureConsumer* current_consumer, + ui::GestureConsumer* new_consumer, + ui::TransferTouchesBehavior transfer_touches_behavior) { + WindowMus* current_window = + WindowMus::Get(static_cast<Window*>(current_consumer)); + WindowMus* new_window = WindowMus::Get(static_cast<Window*>(new_consumer)); + DCHECK(current_window); + DCHECK(new_window); + window_tree_->TransferGestureEventsTo( + current_window->server_id(), new_window->server_id(), + transfer_touches_behavior == ui::TransferTouchesBehavior::kCancel); +} + +void GestureSynchronizer::OnActiveTouchesCanceled( + ui::GestureConsumer* consumer) { + WindowMus* window = WindowMus::Get(static_cast<Window*>(consumer)); + window_tree_->CancelActiveTouches(window->server_id()); +} + +} // namespace aura diff --git a/chromium/ui/aura/mus/gesture_synchronizer.h b/chromium/ui/aura/mus/gesture_synchronizer.h new file mode 100644 index 00000000000..66ab563d33f --- /dev/null +++ b/chromium/ui/aura/mus/gesture_synchronizer.h @@ -0,0 +1,45 @@ +// 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_AURA_MUS_GESTURE_SYNCHRONIZER_H_ +#define UI_AURA_MUS_GESTURE_SYNCHRONIZER_H_ + +#include "base/macros.h" +#include "ui/events/gestures/gesture_recognizer_observer.h" + +namespace ws { +namespace mojom { + +class WindowTree; + +} // namespace mojom +} // namespace ws + +namespace aura { + +// GestureSynchronizer is responsible for keeping GestureRecognizer's state +// synchronized between aura and the mus server. +class GestureSynchronizer : public ui::GestureRecognizerObserver { + public: + explicit GestureSynchronizer(ws::mojom::WindowTree* window_tree); + ~GestureSynchronizer() override; + + private: + // ui::GestureRecognizerObserver: + void OnActiveTouchesCanceledExcept( + ui::GestureConsumer* not_cancelled) override; + void OnEventsTransferred( + ui::GestureConsumer* current_consumer, + ui::GestureConsumer* new_consumer, + ui::TransferTouchesBehavior transfer_touches_behavior) override; + void OnActiveTouchesCanceled(ui::GestureConsumer* consumer) override; + + ws::mojom::WindowTree* window_tree_; + + DISALLOW_COPY_AND_ASSIGN(GestureSynchronizer); +}; + +} // namespace aura + +#endif // UI_AURA_MUS_GESTURE_SYNCHRONIZER_H_ diff --git a/chromium/ui/aura/mus/gesture_synchronizer_unittest.cc b/chromium/ui/aura/mus/gesture_synchronizer_unittest.cc new file mode 100644 index 00000000000..cd1e7e0dee8 --- /dev/null +++ b/chromium/ui/aura/mus/gesture_synchronizer_unittest.cc @@ -0,0 +1,90 @@ +// 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/aura/mus/gesture_synchronizer.h" + +#include <memory> + +#include "ui/aura/env.h" +#include "ui/aura/mus/window_mus.h" +#include "ui/aura/mus/window_tree_host_mus.h" +#include "ui/aura/mus/window_tree_host_mus_init_params.h" +#include "ui/aura/test/aura_mus_test_base.h" +#include "ui/aura/test/mus/test_window_tree.h" + +namespace aura { + +class GestureSynchronizerTest : public test::AuraMusClientTestBase { + public: + GestureSynchronizerTest() = default; + ~GestureSynchronizerTest() override = default; + + void SetUp() override { + test::AuraMusClientTestBase::SetUp(); + + window_tree_host_ = std::make_unique<WindowTreeHostMus>( + CreateInitParamsForTopLevel(window_tree_client_impl())); + window_tree_host_->InitHost(); + window_tree_host_->Show(); + } + + protected: + ui::GestureRecognizer* gesture_recognizer() { + return Env::GetInstance()->gesture_recognizer(); + } + + std::unique_ptr<Window> NewWindow() { + auto window = std::make_unique<Window>(nullptr); + window->Init(ui::LAYER_NOT_DRAWN); + window_tree_host_->window()->AddChild(window.get()); + window->Show(); + return window; + } + + private: + std::unique_ptr<WindowTreeHostMus> window_tree_host_; + + DISALLOW_COPY_AND_ASSIGN(GestureSynchronizerTest); +}; + +TEST_F(GestureSynchronizerTest, CancelActiveTouchesExcept) { + std::unique_ptr<Window> window = NewWindow(); + gesture_recognizer()->CancelActiveTouchesExcept(window.get()); + EXPECT_EQ(window_tree()->last_not_cancelled_window_id(), + WindowMus::Get(window.get())->server_id()); +} + +TEST_F(GestureSynchronizerTest, CancelActiveTouchesExceptForNullptr) { + gesture_recognizer()->CancelActiveTouchesExcept(nullptr); + EXPECT_EQ(window_tree()->last_not_cancelled_window_id(), kInvalidServerId); +} + +TEST_F(GestureSynchronizerTest, CancelActiveTouches) { + std::unique_ptr<Window> window = NewWindow(); + gesture_recognizer()->CancelActiveTouches(window.get()); + EXPECT_EQ(window_tree()->last_cancelled_window_id(), + WindowMus::Get(window.get())->server_id()); +} + +TEST_F(GestureSynchronizerTest, TransferGestureEventsTo) { + std::unique_ptr<Window> window1 = NewWindow(); + std::unique_ptr<Window> window2 = NewWindow(); + gesture_recognizer()->TransferEventsTo(window1.get(), window2.get(), + ui::TransferTouchesBehavior::kCancel); + EXPECT_EQ(window_tree()->last_transfer_current(), + WindowMus::Get(window1.get())->server_id()); + EXPECT_EQ(window_tree()->last_transfer_new(), + WindowMus::Get(window2.get())->server_id()); + EXPECT_TRUE(window_tree()->last_transfer_should_cancel()); + + gesture_recognizer()->TransferEventsTo( + window2.get(), window1.get(), ui::TransferTouchesBehavior::kDontCancel); + EXPECT_EQ(window_tree()->last_transfer_current(), + WindowMus::Get(window2.get())->server_id()); + EXPECT_EQ(window_tree()->last_transfer_new(), + WindowMus::Get(window1.get())->server_id()); + EXPECT_FALSE(window_tree()->last_transfer_should_cancel()); +} + +} // namespace aura diff --git a/chromium/ui/aura/mus/input_method_mus.cc b/chromium/ui/aura/mus/input_method_mus.cc index f840416fe0f..cf1328a6e05 100644 --- a/chromium/ui/aura/mus/input_method_mus.cc +++ b/chromium/ui/aura/mus/input_method_mus.cc @@ -19,6 +19,21 @@ using ws::mojom::EventResult; namespace aura { +namespace { + +void CallEventResultCallback(InputMethodMus::EventResultCallback ack_callback, + bool handled) { + // |ack_callback| can be null if the standard form of DispatchKeyEvent() is + // called instead of the version which provides a callback. In mus+ash we + // use the version with callback, but some unittests use the standard form. + if (!ack_callback) + return; + + std::move(ack_callback) + .Run(handled ? EventResult::HANDLED : EventResult::UNHANDLED); +} + +} // namespace //////////////////////////////////////////////////////////////////////////////// // InputMethodMus, public: @@ -34,7 +49,7 @@ InputMethodMus::~InputMethodMus() { // Mus won't dispatch the next key event until the existing one is acked. We // may have KeyEvents sent to IME and awaiting the result, we need to ack // them otherwise mus won't process the next event until it times out. - AckPendingCallbacksUnhandled(); + AckPendingCallbacks(); } void InputMethodMus::Init(service_manager::Connector* connector) { @@ -50,13 +65,9 @@ ui::EventDispatchDetails InputMethodMus::DispatchKeyEvent( // If no text input client, do nothing. if (!GetTextInputClient()) { - ui::EventDispatchDetails dispatch_details = DispatchKeyEventPostIME(event); - if (ack_callback) { - std::move(ack_callback) - .Run(event->handled() ? EventResult::HANDLED - : EventResult::UNHANDLED); - } - return dispatch_details; + return DispatchKeyEventPostIME( + event, + base::BindOnce(&CallEventResultCallback, std::move(ack_callback))); } return SendKeyEventToInputMethod(*event, std::move(ack_callback)); @@ -136,7 +147,8 @@ ui::EventDispatchDetails InputMethodMus::SendKeyEventToInputMethod( // This code path is hit in tests that don't connect to the server. DCHECK(!ack_callback); std::unique_ptr<ui::Event> event_clone = ui::Event::Clone(event); - return DispatchKeyEventPostIME(event_clone->AsKeyEvent()); + return DispatchKeyEventPostIME(event_clone->AsKeyEvent(), + base::NullCallback()); } // IME driver will notify us whether it handled the event or not by calling @@ -160,7 +172,7 @@ void InputMethodMus::OnDidChangeFocusedClient( // We are about to close the pipe with pending callbacks. Closing the pipe // results in none of the callbacks being run. We have to run the callbacks // else mus won't process the next event immediately. - AckPendingCallbacksUnhandled(); + AckPendingCallbacks(); if (!focused) { input_method_ = nullptr; @@ -200,10 +212,10 @@ void InputMethodMus::UpdateTextInputType() { } } -void InputMethodMus::AckPendingCallbacksUnhandled() { +void InputMethodMus::AckPendingCallbacks() { for (auto& callback : pending_callbacks_) { if (callback) - std::move(callback).Run(EventResult::UNHANDLED); + std::move(callback).Run(EventResult::HANDLED); } pending_callbacks_.clear(); } @@ -216,14 +228,7 @@ void InputMethodMus::ProcessKeyEventCallback( DCHECK(!pending_callbacks_.empty()); EventResultCallback ack_callback = std::move(pending_callbacks_.front()); pending_callbacks_.pop_front(); - - // |ack_callback| can be null if the standard form of DispatchKeyEvent() is - // called instead of the version which provides a callback. In mus+ash we - // use the version with callback, but some unittests use the standard form. - if (ack_callback) { - std::move(ack_callback) - .Run(handled ? EventResult::HANDLED : EventResult::UNHANDLED); - } + CallEventResultCallback(std::move(ack_callback), handled); } } // namespace aura diff --git a/chromium/ui/aura/mus/input_method_mus.h b/chromium/ui/aura/mus/input_method_mus.h index d47ed317001..d54bf4357cc 100644 --- a/chromium/ui/aura/mus/input_method_mus.h +++ b/chromium/ui/aura/mus/input_method_mus.h @@ -64,10 +64,13 @@ class AURA_EXPORT InputMethodMus : public ui::InputMethodBase { void UpdateTextInputType(); - // Runs all pending callbacks with UNHANDLED. This is called during shutdown, + // Runs all pending callbacks with HANDLED. This is called during shutdown, // or any time |input_method_ptr_| is reset to ensure we don't leave mus - // waiting for an ack. - void AckPendingCallbacksUnhandled(); + // waiting for an ack. We ack HANDLED because the input method can be reset + // due to focus changes in response to shortcuts (e.g. Ctrl-T opening a tab) + // and we don't want the window manager to try to process the accelerators. + // https://crbug.com/874098 + void AckPendingCallbacks(); // Called when the server responds to our request to process an event. void ProcessKeyEventCallback( diff --git a/chromium/ui/aura/mus/input_method_mus_unittest.cc b/chromium/ui/aura/mus/input_method_mus_unittest.cc index eaa79232a93..b70f26395ba 100644 --- a/chromium/ui/aura/mus/input_method_mus_unittest.cc +++ b/chromium/ui/aura/mus/input_method_mus_unittest.cc @@ -21,12 +21,22 @@ class TestInputMethodDelegate : public ui::internal::InputMethodDelegate { TestInputMethodDelegate() {} ~TestInputMethodDelegate() override {} + bool was_dispatch_key_event_post_ime_called() const { + return was_dispatch_key_event_post_ime_called_; + } + // ui::internal::InputMethodDelegate: - ui::EventDispatchDetails DispatchKeyEventPostIME(ui::KeyEvent* key) override { + ui::EventDispatchDetails DispatchKeyEventPostIME( + ui::KeyEvent* key, + base::OnceCallback<void(bool)> ack_callback) override { + was_dispatch_key_event_post_ime_called_ = true; + CallDispatchKeyEventPostIMEAck(key, std::move(ack_callback)); return ui::EventDispatchDetails(); } private: + bool was_dispatch_key_event_post_ime_called_ = false; + DISALLOW_COPY_AND_ASSIGN(TestInputMethodDelegate); }; @@ -94,23 +104,28 @@ using InputMethodMusTest = test::AuraTestBaseMus; namespace { // Used in closure supplied to processing the event. -void RunFunctionWithEventResult(bool* was_run, ws::mojom::EventResult result) { +void RunFunctionWithEventResult(bool* was_run, + ws::mojom::EventResult* result_out, + ws::mojom::EventResult result) { *was_run = true; + *result_out = result; } } // namespace TEST_F(InputMethodMusTest, PendingCallbackRunFromDestruction) { bool was_event_result_callback_run = false; - // Create an InputMethodMus and foward an event to it. + ws::mojom::EventResult event_result = ws::mojom::EventResult::UNHANDLED; + // Create an InputMethodMus and forward an event to it. { TestInputMethodDelegate input_method_delegate; InputMethodMus input_method_mus(&input_method_delegate, nullptr); TestInputMethod test_input_method; InputMethodMusTestApi::SetInputMethod(&input_method_mus, &test_input_method); - EventResultCallback callback = base::BindOnce( - &RunFunctionWithEventResult, &was_event_result_callback_run); + EventResultCallback callback = + base::BindOnce(&RunFunctionWithEventResult, + &was_event_result_callback_run, &event_result); ui::EventDispatchDetails details = InputMethodMusTestApi::CallSendKeyEventToInputMethod( @@ -136,14 +151,16 @@ TEST_F(InputMethodMusTest, PendingCallbackRunFromDestruction) { TEST_F(InputMethodMusTest, PendingCallbackRunFromOnDidChangeFocusedClient) { bool was_event_result_callback_run = false; + ws::mojom::EventResult event_result = ws::mojom::EventResult::UNHANDLED; ui::DummyTextInputClient test_input_client; - // Create an InputMethodMus and foward an event to it. + // Create an InputMethodMus and forward an event to it. TestInputMethodDelegate input_method_delegate; InputMethodMus input_method_mus(&input_method_delegate, nullptr); TestInputMethod test_input_method; InputMethodMusTestApi::SetInputMethod(&input_method_mus, &test_input_method); - EventResultCallback callback = base::BindOnce(&RunFunctionWithEventResult, - &was_event_result_callback_run); + EventResultCallback callback = + base::BindOnce(&RunFunctionWithEventResult, + &was_event_result_callback_run, &event_result); ui::EventDispatchDetails details = InputMethodMusTestApi::CallSendKeyEventToInputMethod( &input_method_mus, @@ -159,18 +176,21 @@ TEST_F(InputMethodMusTest, PendingCallbackRunFromOnDidChangeFocusedClient) { &input_method_mus, nullptr, &test_input_client); // Changing the focused client should trigger running the callback. EXPECT_TRUE(was_event_result_callback_run); + EXPECT_EQ(ws::mojom::EventResult::HANDLED, event_result); } TEST_F(InputMethodMusTest, PendingCallbackRunFromOnDidChangeFocusedClientToNull) { bool was_event_result_callback_run = false; - // Create an InputMethodMus and foward an event to it. + ws::mojom::EventResult event_result = ws::mojom::EventResult::UNHANDLED; + // Create an InputMethodMus and forward an event to it. TestInputMethodDelegate input_method_delegate; InputMethodMus input_method_mus(&input_method_delegate, nullptr); TestInputMethod test_input_method; InputMethodMusTestApi::SetInputMethod(&input_method_mus, &test_input_method); - EventResultCallback callback = base::BindOnce(&RunFunctionWithEventResult, - &was_event_result_callback_run); + EventResultCallback callback = + base::BindOnce(&RunFunctionWithEventResult, + &was_event_result_callback_run, &event_result); ui::EventDispatchDetails details = InputMethodMusTestApi::CallSendKeyEventToInputMethod( &input_method_mus, @@ -186,6 +206,7 @@ TEST_F(InputMethodMusTest, nullptr, nullptr); // Changing the focused client should trigger running the callback. EXPECT_TRUE(was_event_result_callback_run); + EXPECT_EQ(ws::mojom::EventResult::HANDLED, event_result); } // See description of ChangeTextInputTypeWhileProcessingCallback for details. @@ -205,9 +226,12 @@ class TestInputMethodDelegate2 : public ui::internal::InputMethodDelegate { } // ui::internal::InputMethodDelegate: - ui::EventDispatchDetails DispatchKeyEventPostIME(ui::KeyEvent* key) override { + ui::EventDispatchDetails DispatchKeyEventPostIME( + ui::KeyEvent* key, + base::OnceCallback<void(bool)> ack_callback) override { was_dispatch_key_event_post_ime_called_ = true; input_method_mus_->SetFocusedTextInputClient(text_input_client_); + CallDispatchKeyEventPostIMEAck(key, std::move(ack_callback)); return ui::EventDispatchDetails(); } @@ -224,16 +248,18 @@ class TestInputMethodDelegate2 : public ui::internal::InputMethodDelegate { // scenario and the callback is correctly called. TEST_F(InputMethodMusTest, ChangeTextInputTypeWhileProcessingCallback) { bool was_event_result_callback_run = false; + ws::mojom::EventResult event_result = ws::mojom::EventResult::UNHANDLED; ui::DummyTextInputClient test_input_client; - // Create an InputMethodMus and foward an event to it. + // Create an InputMethodMus and forward an event to it. TestInputMethodDelegate2 input_method_delegate; InputMethodMus input_method_mus(&input_method_delegate, nullptr); input_method_delegate.SetInputMethodAndClient(&input_method_mus, &test_input_client); TestInputMethod test_input_method; InputMethodMusTestApi::SetInputMethod(&input_method_mus, &test_input_method); - EventResultCallback callback = base::BindOnce(&RunFunctionWithEventResult, - &was_event_result_callback_run); + EventResultCallback callback = + base::BindOnce(&RunFunctionWithEventResult, + &was_event_result_callback_run, &event_result); const ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_RETURN, 0); ui::EventDispatchDetails details = InputMethodMusTestApi::CallSendKeyEventToInputMethod( @@ -243,10 +269,13 @@ TEST_F(InputMethodMusTest, ChangeTextInputTypeWhileProcessingCallback) { ASSERT_EQ(1u, test_input_method.process_key_event_callbacks()->size()); // Callback should not have been run yet. EXPECT_FALSE(was_event_result_callback_run); - std::move((*test_input_method.process_key_event_callbacks())[0]).Run(false); + const bool handled = true; + std::move((*test_input_method.process_key_event_callbacks())[0]).Run(handled); // Callback should have been run. EXPECT_TRUE(was_event_result_callback_run); + EXPECT_EQ(ws::mojom::EventResult::HANDLED, event_result); + EXPECT_FALSE(input_method_delegate.was_dispatch_key_event_post_ime_called()); } // Calling OnTextInputTypeChanged from unfocused client should @@ -317,4 +346,37 @@ TEST_F(InputMethodMusTest, ShowVirtualKeyboardIfEnabled) { EXPECT_TRUE(test_input_method.was_show_virtual_keyboard_if_enabled_called()); } +TEST_F(InputMethodMusTest, AckUnhandledCallsDispatchKeyEventPostUnhandled) { + bool was_event_result_callback_run = false; + ws::mojom::EventResult event_result = ws::mojom::EventResult::UNHANDLED; + // Create an InputMethodMus and forward an event to it. + TestInputMethodDelegate input_method_delegate; + InputMethodMus input_method_mus(&input_method_delegate, nullptr); + TestInputMethod test_input_method; + InputMethodMusTestApi::SetInputMethod(&input_method_mus, &test_input_method); + EventResultCallback callback = + base::BindOnce(&RunFunctionWithEventResult, + &was_event_result_callback_run, &event_result); + const ui::KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_RETURN, 0); + ignore_result(InputMethodMusTestApi::CallSendKeyEventToInputMethod( + &input_method_mus, key_event, std::move(callback))); + // The event should have been queued. + ASSERT_EQ(1u, test_input_method.process_key_event_callbacks()->size()); + // Callback should not have been run yet. + EXPECT_FALSE(was_event_result_callback_run); + const bool handled = false; + std::move((*test_input_method.process_key_event_callbacks())[0]).Run(handled); + test_input_method.process_key_event_callbacks()->clear(); + + // Callback should have been run. + EXPECT_TRUE(was_event_result_callback_run); + EXPECT_EQ(ws::mojom::EventResult::UNHANDLED, event_result); + + // DispatchKeyEventPostIME() should not have beeen called. In production + // the following calls result: + // InputMethodChromeOS -> RemoteTextInputClient -> TextInputClientImpl -> + // InputMethodMus's delegate. + EXPECT_FALSE(input_method_delegate.was_dispatch_key_event_post_ime_called()); +} + } // namespace aura diff --git a/chromium/ui/aura/mus/mus_mouse_location_updater.cc b/chromium/ui/aura/mus/mus_mouse_location_updater.cc index 32aa588cb61..ce61eab444f 100644 --- a/chromium/ui/aura/mus/mus_mouse_location_updater.cc +++ b/chromium/ui/aura/mus/mus_mouse_location_updater.cc @@ -38,27 +38,17 @@ MusMouseLocationUpdater::~MusMouseLocationUpdater() { } void MusMouseLocationUpdater::OnEventProcessingStarted(const ui::Event& event) { - if (!IsMouseEventWithLocation(event) || - Env::GetInstance()->always_use_last_mouse_location_) { + Env* env = Env::GetInstance(); + if (!IsMouseEventWithLocation(event) || env->always_use_last_mouse_location_) return; - } is_processing_trigger_event_ = true; - gfx::Point location_in_screen = event.AsMouseEvent()->root_location(); // event.target() may not exist in some tests. - if (event.target()) { - aura::Window* root_window = - static_cast<aura::Window*>(event.target())->GetRootWindow(); - auto* screen_position_client = - aura::client::GetScreenPositionClient(root_window); - // screen_position_client may not exist in tests. - if (screen_position_client) { - screen_position_client->ConvertPointToScreen(root_window, - &location_in_screen); - } - } - Env::GetInstance()->SetLastMouseLocation(location_in_screen); - Env::GetInstance()->get_last_mouse_location_from_mus_ = false; + const gfx::Point screen_location = + event.target() ? event.target()->GetScreenLocation(*event.AsMouseEvent()) + : event.AsMouseEvent()->root_location(); + env->SetLastMouseLocation(screen_location); + env->get_last_mouse_location_from_mus_ = false; } void MusMouseLocationUpdater::OnEventProcessingFinished() { diff --git a/chromium/ui/aura/mus/mus_types.h b/chromium/ui/aura/mus/mus_types.h index 4f406261d73..3a58e55eeb9 100644 --- a/chromium/ui/aura/mus/mus_types.h +++ b/chromium/ui/aura/mus/mus_types.h @@ -20,17 +20,9 @@ enum class WindowMusType { // The window is an embed root in the embedded client. That is, the client // received this window by way of another client calling Embed(). In other // words, this is the embedded side of an embedding. - // NOTE: in the client that called Embed() the window type is LOCAL (or - // EMBED_IN_OWNER). - // TODO(sky): ensure when Embed() is called type is always set to - // EMBED_IN_OWNER, and if the embedding is removed it goes back to LOCAL. - // https://crbug.com/834487 + // NOTE: in the client that called Embed() the window type is LOCAL. EMBED, - // Embed() was called on the window by the local client. In other words, this - // is the embedder side of an embedding. - EMBED_IN_OWNER, - // The window was created by requesting a top level // (WindowTree::NewTopLevel()). TOP_LEVEL, diff --git a/chromium/ui/aura/mus/os_exchange_data_provider_mus.cc b/chromium/ui/aura/mus/os_exchange_data_provider_mus.cc index 9e41787ea96..f8e327cd09b 100644 --- a/chromium/ui/aura/mus/os_exchange_data_provider_mus.cc +++ b/chromium/ui/aura/mus/os_exchange_data_provider_mus.cc @@ -113,8 +113,7 @@ void OSExchangeDataProviderMus::SetFilename(const base::FilePath& path) { void OSExchangeDataProviderMus::SetFilenames( const std::vector<ui::FileInfo>& file_names) { std::vector<std::string> paths; - for (std::vector<ui::FileInfo>::const_iterator it = file_names.begin(); - it != file_names.end(); ++it) { + for (auto it = file_names.begin(); it != file_names.end(); ++it) { std::string url_spec = net::FilePathToFileURL(it->path).spec(); if (!url_spec.empty()) paths.push_back(url_spec); diff --git a/chromium/ui/aura/mus/property_converter.cc b/chromium/ui/aura/mus/property_converter.cc index 51cd00e1d81..0d0e345318e 100644 --- a/chromium/ui/aura/mus/property_converter.cc +++ b/chromium/ui/aura/mus/property_converter.cc @@ -4,6 +4,7 @@ #include "ui/aura/mus/property_converter.h" +#include "base/time/time.h" #include "base/unguessable_token.h" #include "mojo/public/cpp/bindings/type_converter.h" #include "services/ws/public/cpp/property_type_converters.h" @@ -108,10 +109,58 @@ PropertyConverter::PropertyConverter() { client::kAnimationsDisabledKey, ws::mojom::WindowManager::kAnimationsDisabled_Property, CreateAcceptAnyValueCallback()); + RegisterWindowPtrProperty( + client::kChildModalParentKey, + ws::mojom::WindowManager::kChildModalParent_Property); } PropertyConverter::~PropertyConverter() {} +const void* PropertyConverter::GetPropertyKeyFromTransportName( + const std::string& transport_name) { + for (const auto& primitive_property : primitive_properties_) { + if (primitive_property.second.transport_name == transport_name) + return primitive_property.first; + } + + for (const auto& image_property : image_properties_) { + if (image_property.second == transport_name) + return image_property.first->name; + } + + for (const auto& rect_property : rect_properties_) { + if (rect_property.second == transport_name) + return rect_property.first->name; + } + + for (const auto& size_property : size_properties_) { + if (size_property.second == transport_name) + return size_property.first->name; + } + + for (const auto& string_property : string_properties_) { + if (string_property.second == transport_name) + return string_property.first->name; + } + + for (const auto& string16_property : string16_properties_) { + if (string16_property.second == transport_name) + return string16_property.first->name; + } + + for (const auto& unguessable_token_property : unguessable_token_properties_) { + if (unguessable_token_property.second == transport_name) + return unguessable_token_property.first->name; + } + + for (const auto& window_ptr_property : window_ptr_properties_) { + if (window_ptr_property.second == transport_name) + return window_ptr_property.first->name; + } + + return nullptr; +} + bool PropertyConverter::IsTransportNameRegistered( const std::string& name) const { return transport_names_.count(name) > 0; @@ -131,7 +180,7 @@ bool PropertyConverter::ConvertPropertyForTransport( const gfx::ImageSkia* value = window->GetProperty(image_key); if (value) { // TODO(crbug.com/667566): Support additional scales or gfx::Image[Skia]. - SkBitmap bitmap = value->GetRepresentation(1.f).sk_bitmap(); + SkBitmap bitmap = value->GetRepresentation(1.f).GetBitmap(); *transport_value = std::make_unique<std::vector<uint8_t>>( mojo::ConvertTo<std::vector<uint8_t>>(bitmap)); } else { @@ -171,6 +220,14 @@ bool PropertyConverter::ConvertPropertyForTransport( return true; } + // window_ptr_properties_ aren't processed here since Window* values aren't + // transferrable. A post processing step in WindowTree and WindowTreeClient + // takes care of the conversion. + if (IsWindowPtrPropertyRegistered( + static_cast<const WindowProperty<Window*>*>(key))) { + return true; + } + // Handle primitive property types generically. DCHECK_GT(primitive_properties_.count(key), 0u); PrimitiveType default_value = primitive_properties_[key].default_value; @@ -210,6 +267,10 @@ std::string PropertyConverter::GetTransportNameForPropertyKey(const void* key) { if (unguessable_token_properties_.count(unguessable_token_key) > 0) return unguessable_token_properties_[unguessable_token_key]; + auto* window_ptr_key = static_cast<const WindowProperty<Window*>*>(key); + if (window_ptr_properties_.count(window_ptr_key) > 0) + return window_ptr_properties_[window_ptr_key]; + return std::string(); } @@ -307,6 +368,20 @@ void PropertyConverter::SetPropertyFromTransportValue( } } + // window_ptr_properties_ aren't processed here since Window* values aren't + // transferrable. A post processing step in WindowTree and WindowTreeClient + // takes care of the conversion. + for (const auto& window_ptr_property : window_ptr_properties_) { + if (window_ptr_property.second == transport_name) { + LOG(ERROR) << transport_name << " is a registered window property but " + << "should not be processed here."; + return; + } + } + + // WARNING: Adding a new map, be sure and update + // GetPropertyKeyFromTransportName() as well. + DVLOG(2) << "Unknown mus property name: " << transport_name; } @@ -379,6 +454,15 @@ void PropertyConverter::RegisterString16Property( transport_names_.insert(transport_name); } +void PropertyConverter::RegisterTimeDeltaProperty( + const WindowProperty<base::TimeDelta>* property, + const char* transport_name) { + // TimeDelta is internally handled (by class_property) as a primitive + // value (int64_t) . See ClassPropertyCaster<base::TimeDelta> for details. + RegisterPrimitiveProperty(property, transport_name, + CreateAcceptAnyValueCallback()); +} + void PropertyConverter::RegisterUnguessableTokenProperty( const WindowProperty<base::UnguessableToken*>* property, const char* transport_name) { @@ -388,6 +472,29 @@ void PropertyConverter::RegisterUnguessableTokenProperty( transport_names_.insert(transport_name); } +void PropertyConverter::RegisterWindowPtrProperty( + const WindowProperty<Window*>* property, + const char* transport_name) { + DCHECK(!IsTransportNameRegistered(transport_name)) + << "Property already registered: " << transport_name; + window_ptr_properties_[property] = transport_name; + transport_names_.insert(transport_name); +} + +const WindowProperty<Window*>* PropertyConverter::GetWindowPtrProperty( + const std::string& transport_name) const { + for (const auto& iter : window_ptr_properties_) { + if (transport_name == iter.second) + return iter.first; + } + return nullptr; +} + +bool PropertyConverter::IsWindowPtrPropertyRegistered( + const WindowProperty<Window*>* property) const { + return window_ptr_properties_.find(property) != window_ptr_properties_.end(); +} + base::flat_map<std::string, std::vector<uint8_t>> PropertyConverter::GetTransportProperties(Window* window) { base::flat_map<std::string, std::vector<uint8_t>> properties; diff --git a/chromium/ui/aura/mus/property_converter.h b/chromium/ui/aura/mus/property_converter.h index 8d13b2021cd..82650db1321 100644 --- a/chromium/ui/aura/mus/property_converter.h +++ b/chromium/ui/aura/mus/property_converter.h @@ -18,6 +18,7 @@ #include "ui/aura/window.h" namespace base { +class TimeDelta; class UnguessableToken; } @@ -45,6 +46,10 @@ class AURA_EXPORT PropertyConverter { // accept any value. static base::RepeatingCallback<bool(int64_t)> CreateAcceptAnyValueCallback(); + // Returns the key for the window property registered against the specified + // transport name. + const void* GetPropertyKeyFromTransportName(const std::string& name); + // Returns true if RegisterProperty() has been called with the specified // transport name. bool IsTransportNameRegistered(const std::string& name) const; @@ -111,9 +116,23 @@ class AURA_EXPORT PropertyConverter { const char* transport_name); void RegisterString16Property(const WindowProperty<base::string16*>* property, const char* transport_name); + void RegisterTimeDeltaProperty( + const WindowProperty<base::TimeDelta>* property, + const char* transport_name); void RegisterUnguessableTokenProperty( const WindowProperty<base::UnguessableToken*>* property, const char* transport_name); + void RegisterWindowPtrProperty(const WindowProperty<Window*>* property, + const char* transport_name); + + // Returns the window property key of Window* value which is registered with + // the transport_name. If the name is not registered or registered with a + // different type, returns nullptr. + const WindowProperty<Window*>* GetWindowPtrProperty( + const std::string& transport_name) const; + + bool IsWindowPtrPropertyRegistered( + const WindowProperty<Window*>* property) const; // Get a flat map of the window's registered properties, to use for transport. base::flat_map<std::string, std::vector<uint8_t>> GetTransportProperties( @@ -151,6 +170,7 @@ class AURA_EXPORT PropertyConverter { string16_properties_; std::map<const WindowProperty<base::UnguessableToken*>*, const char*> unguessable_token_properties_; + std::map<const WindowProperty<Window*>*, const char*> window_ptr_properties_; // Set of transport names supplied to RegisterProperty(). std::set<std::string> transport_names_; diff --git a/chromium/ui/aura/mus/property_converter_unittest.cc b/chromium/ui/aura/mus/property_converter_unittest.cc index 50838071d5f..2d0382cf08f 100644 --- a/chromium/ui/aura/mus/property_converter_unittest.cc +++ b/chromium/ui/aura/mus/property_converter_unittest.cc @@ -43,6 +43,10 @@ DEFINE_UI_CLASS_PROPERTY_KEY(int16_t, kTestPropertyKey7, 1); DEFINE_UI_CLASS_PROPERTY_KEY(int32_t, kTestPropertyKey8, -1); DEFINE_UI_CLASS_PROPERTY_KEY(int64_t, kTestPropertyKey9, 777); +DEFINE_UI_CLASS_PROPERTY_KEY(base::TimeDelta, + kTestTimeDeltaKey, + base::TimeDelta()); + DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(gfx::ImageSkia, kTestImageSkiaPropertyKey, nullptr); @@ -53,6 +57,9 @@ DEFINE_OWNED_UI_CLASS_PROPERTY_KEY( DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(base::string16, kTestString16PropertyKey, nullptr); +DEFINE_UI_CLASS_PROPERTY_KEY(Window*, kTestWindowPtrPropertyKey, nullptr); +DEFINE_UI_CLASS_PROPERTY_KEY(Window*, kTestWindowPtrUnregisteredKey, nullptr); + const char kTestPropertyServerKey0[] = "test-property-server0"; const char kTestPropertyServerKey1[] = "test-property-server1"; const char kTestPropertyServerKey2[] = "test-property-server2"; @@ -69,6 +76,10 @@ const char kTestRectPropertyServerKey[] = "test-rect-property-server"; const char kTestSizePropertyServerKey[] = "test-size-property-server"; const char kTestStringPropertyServerKey[] = "test-string-property-server"; const char kTestString16PropertyServerKey[] = "test-string16-property-server"; +const char kTestTimeDeltaPropertyServerKey[] = + "test-time-delta-property-server"; +const char kTestWindowPtrPropertyServerKey[] = + "test-window-ptr-property-server"; // Test registration, naming and value conversion for primitive property types. template <typename T> @@ -369,4 +380,84 @@ TEST_F(PropertyConverterTest, String16Property) { EXPECT_EQ(value_2, *window->GetProperty(kTestString16PropertyKey)); } +TEST_F(PropertyConverterTest, TimeDeltaProperty) { + PropertyConverter property_converter; + property_converter.RegisterTimeDeltaProperty(kTestTimeDeltaKey, + kTestTimeDeltaPropertyServerKey); + EXPECT_EQ( + kTestTimeDeltaPropertyServerKey, + property_converter.GetTransportNameForPropertyKey(kTestTimeDeltaKey)); + EXPECT_TRUE(property_converter.IsTransportNameRegistered( + kTestTimeDeltaPropertyServerKey)); + + std::unique_ptr<Window> window(CreateNormalWindow(1, root_window(), nullptr)); + const base::TimeDelta time_delta = + base::TimeDelta::FromMilliseconds(123456789); + window->SetProperty(kTestTimeDeltaKey, time_delta); + + std::string transport_name_out; + std::unique_ptr<std::vector<uint8_t>> transport_value_out; + EXPECT_TRUE(property_converter.ConvertPropertyForTransport( + window.get(), kTestTimeDeltaKey, &transport_name_out, + &transport_value_out)); + EXPECT_EQ(kTestTimeDeltaPropertyServerKey, transport_name_out); + const int64_t storage_value_1 = time_delta.InMicroseconds(); + std::vector<uint8_t> transport_value = + mojo::ConvertTo<std::vector<uint8_t>>(storage_value_1); + EXPECT_EQ(transport_value, *transport_value_out.get()); + + int64_t decoded_value_1 = 0; + EXPECT_TRUE(property_converter.GetPropertyValueFromTransportValue( + kTestTimeDeltaPropertyServerKey, *transport_value_out, &decoded_value_1)); + EXPECT_EQ(time_delta.InMicroseconds(), decoded_value_1); + + window->SetProperty(kTestTimeDeltaKey, base::TimeDelta()); + property_converter.SetPropertyFromTransportValue( + window.get(), kTestTimeDeltaPropertyServerKey, &transport_value); + EXPECT_EQ(time_delta, window->GetProperty(kTestTimeDeltaKey)); +} + +TEST_F(PropertyConverterTest, WindowPtrProperty) { + PropertyConverter property_converter; + property_converter.RegisterString16Property(kTestString16PropertyKey, + kTestString16PropertyServerKey); + property_converter.RegisterWindowPtrProperty(kTestWindowPtrPropertyKey, + kTestWindowPtrPropertyServerKey); + EXPECT_EQ(kTestWindowPtrPropertyServerKey, + property_converter.GetTransportNameForPropertyKey( + kTestWindowPtrPropertyKey)); + EXPECT_TRUE(property_converter.IsTransportNameRegistered( + kTestWindowPtrPropertyServerKey)); + + EXPECT_TRUE(property_converter.IsWindowPtrPropertyRegistered( + kTestWindowPtrPropertyKey)); + EXPECT_EQ(kTestWindowPtrPropertyKey, property_converter.GetWindowPtrProperty( + kTestWindowPtrPropertyServerKey)); + EXPECT_FALSE(property_converter.IsWindowPtrPropertyRegistered( + kTestWindowPtrUnregisteredKey)); + EXPECT_FALSE( + property_converter.GetWindowPtrProperty(kTestString16PropertyServerKey)); + + std::unique_ptr<Window> window1( + CreateNormalWindow(1, root_window(), nullptr)); + std::unique_ptr<Window> window2( + CreateNormalWindow(2, root_window(), nullptr)); + window1->SetProperty(kTestWindowPtrPropertyKey, window2.get()); + + std::string transport_name_out; + std::unique_ptr<std::vector<uint8_t>> transport_value_out; + EXPECT_TRUE(property_converter.ConvertPropertyForTransport( + window1.get(), kTestWindowPtrPropertyKey, &transport_name_out, + &transport_value_out)); + EXPECT_EQ(kTestWindowPtrPropertyServerKey, transport_name_out); + EXPECT_FALSE(transport_value_out); + + window1->ClearProperty(kTestWindowPtrPropertyKey); + std::vector<uint8_t> transport_value = mojo::ConvertTo<std::vector<uint8_t>>( + reinterpret_cast<uint64_t>(window2.get())); + property_converter.SetPropertyFromTransportValue( + window1.get(), kTestWindowPtrPropertyServerKey, &transport_value); + EXPECT_FALSE(window1->GetProperty(kTestWindowPtrPropertyKey)); +} + } // namespace aura diff --git a/chromium/ui/aura/mus/text_input_client_impl.cc b/chromium/ui/aura/mus/text_input_client_impl.cc index 0ce8eb10c23..29121c53a7e 100644 --- a/chromium/ui/aura/mus/text_input_client_impl.cc +++ b/chromium/ui/aura/mus/text_input_client_impl.cc @@ -52,9 +52,8 @@ void TextInputClientImpl::DispatchKeyEventPostIME( std::unique_ptr<ui::Event> event, DispatchKeyEventPostIMECallback callback) { if (delegate_) { - delegate_->DispatchKeyEventPostIME(event->AsKeyEvent()); - if (callback && !callback.is_null()) - std::move(callback).Run(event->stopped_propagation()); + delegate_->DispatchKeyEventPostIME(event->AsKeyEvent(), + std::move(callback)); } } diff --git a/chromium/ui/aura/mus/window_mus.h b/chromium/ui/aura/mus/window_mus.h index bac124e8b37..3ee4b65d25a 100644 --- a/chromium/ui/aura/mus/window_mus.h +++ b/chromium/ui/aura/mus/window_mus.h @@ -28,7 +28,6 @@ enum class OrderDirection; namespace viz { class FrameSinkId; class LocalSurfaceId; -class SurfaceInfo; } namespace aura { @@ -95,7 +94,6 @@ class AURA_EXPORT WindowMus { const viz::FrameSinkId& frame_sink_id) = 0; virtual const viz::LocalSurfaceId& GetOrAllocateLocalSurfaceId( const gfx::Size& new_size) = 0; - virtual void SetFallbackSurfaceInfo(const viz::SurfaceInfo& surface_info) = 0; // The window was deleted on the server side. DestroyFromServer() should // result in deleting |this|. virtual void DestroyFromServer() = 0; diff --git a/chromium/ui/aura/mus/window_port_mus.cc b/chromium/ui/aura/mus/window_port_mus.cc index 9db986531be..2eb6ad5647c 100644 --- a/chromium/ui/aura/mus/window_port_mus.cc +++ b/chromium/ui/aura/mus/window_port_mus.cc @@ -4,13 +4,15 @@ #include "ui/aura/mus/window_port_mus.h" +#include "base/auto_reset.h" #include "cc/mojo_embedder/async_layer_tree_frame_sink.h" +#include "components/viz/client/hit_test_data_provider_draw_quad.h" #include "components/viz/client/local_surface_id_provider.h" #include "components/viz/host/host_frame_sink_manager.h" +#include "services/ws/public/mojom/window_tree_constants.mojom.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/client/transient_window_client.h" #include "ui/aura/env.h" -#include "ui/aura/hit_test_data_provider_aura.h" #include "ui/aura/mus/client_surface_embedder.h" #include "ui/aura/mus/property_converter.h" #include "ui/aura/mus/window_tree_client.h" @@ -27,6 +29,10 @@ namespace aura { +namespace { +static const char* kMus = "Mus"; +} // namespace + WindowPortMus::WindowMusChangeDataImpl::WindowMusChangeDataImpl() = default; WindowPortMus::WindowMusChangeDataImpl::~WindowMusChangeDataImpl() = default; @@ -38,7 +44,8 @@ WindowMus* WindowMus::Get(Window* window) { WindowPortMus::WindowPortMus(WindowTreeClient* client, WindowMusType window_mus_type) - : WindowMus(window_mus_type), + : WindowPort(WindowPort::Type::kMus), + WindowMus(window_mus_type), window_tree_client_(client), weak_ptr_factory_(this) {} @@ -56,7 +63,10 @@ WindowPortMus::~WindowPortMus() { // static WindowPortMus* WindowPortMus::Get(Window* window) { - return static_cast<WindowPortMus*>(WindowPort::Get(window)); + WindowPort* port = WindowPort::Get(window); + return port && port->type() == WindowPort::Type::kMus + ? static_cast<WindowPortMus*>(port) + : nullptr; } void WindowPortMus::SetTextInputState(ui::mojom::TextInputStatePtr state) { @@ -85,23 +95,36 @@ void WindowPortMus::SetCanAcceptDrops(bool can_accept_drops) { window_tree_client_->SetCanAcceptDrops(this, can_accept_drops); } -void WindowPortMus::SetHitTestMask(const base::Optional<gfx::Rect>& mask) { - window_tree_client_->SetHitTestMask(this, mask); +void WindowPortMus::SetHitTestInsets(const gfx::Insets& mouse, + const gfx::Insets& touch) { + window_tree_client_->SetHitTestInsets(this, mouse, touch); } void WindowPortMus::Embed(ws::mojom::WindowTreeClientPtr client, uint32_t flags, ws::mojom::WindowTree::EmbedCallback callback) { - window_tree_client_->Embed(window_, std::move(client), flags, - std::move(callback)); + if (!PrepareForEmbed()) { + std::move(callback).Run(false); + return; + } + window_tree_client_->tree_->Embed( + server_id(), std::move(client), flags, + base::BindOnce(&WindowPortMus::OnEmbedAck, weak_ptr_factory_.GetWeakPtr(), + std::move(callback))); } void WindowPortMus::EmbedUsingToken( const base::UnguessableToken& token, uint32_t flags, ws::mojom::WindowTree::EmbedCallback callback) { - window_tree_client_->EmbedUsingToken(window_, token, flags, - std::move(callback)); + if (!PrepareForEmbed()) { + std::move(callback).Run(false); + return; + } + window_tree_client_->tree_->EmbedUsingToken( + server_id(), token, flags, + base::BindOnce(&WindowPortMus::OnEmbedAck, weak_ptr_factory_.GetWeakPtr(), + std::move(callback))); } std::unique_ptr<cc::mojo_embedder::AsyncLayerTreeFrameSink> @@ -119,11 +142,18 @@ WindowPortMus::RequestLayerTreeFrameSink( params.gpu_memory_buffer_manager = gpu_memory_buffer_manager; params.pipes.compositor_frame_sink_info = std::move(sink_info); params.pipes.client_request = std::move(client_request); + bool root_accepts_events = + (window_->event_targeting_policy() == + ws::mojom::EventTargetingPolicy::TARGET_ONLY) || + (window_->event_targeting_policy() == + ws::mojom::EventTargetingPolicy::TARGET_AND_DESCENDANTS); params.hit_test_data_provider = - std::make_unique<HitTestDataProviderAura>(window_); + std::make_unique<viz::HitTestDataProviderDrawQuad>( + true /* should_ask_for_child_region */, root_accepts_events); params.local_surface_id_provider = std::make_unique<viz::DefaultLocalSurfaceIdProvider>(); params.enable_surface_synchronization = true; + params.client_name = kMus; auto layer_tree_frame_sink = std::make_unique<cc::mojo_embedder::AsyncLayerTreeFrameSink>( @@ -213,6 +243,43 @@ WindowPortMus::ServerChanges::iterator WindowPortMus::FindChangeByTypeAndData( return iter; } +bool WindowPortMus::PrepareForEmbed() { + // Window::Init() must be called before Embed() (otherwise the server hasn't + // been told about the window). + DCHECK(window_->layer()); + + // The window server removes all children before embedding. In other words, + // it's generally an error to Embed() with existing children. So, fail early. + if (!window_->children().empty()) + return false; + + // Can only embed in windows created by this client. + if (window_mus_type() != WindowMusType::LOCAL) + return false; + + // Don't allow an embed when one exists. This could be handled, if the + // callback was converted to OnChangeCompleted(). To attempt to handle it + // without routing the callback over the WindowTreeClient pipe would result + // in problemcs because of ordering. The ordering problem is because there is + // the Embed() request, the callback, and OnEmbeddedAppDisconnected() (which + // originates from the server side). + if (has_embedding_) + return false; + + has_embedding_ = true; + return true; +} + +// static +void WindowPortMus::OnEmbedAck( + base::WeakPtr<WindowPortMus> window, + ws::mojom::WindowTree::EmbedCallback real_callback, + bool result) { + if (window && !result) + window->has_embedding_ = false; + std::move(real_callback).Run(window && result); +} + PropertyConverter* WindowPortMus::GetPropertyConverter() { return window_tree_client_->delegate_->GetPropertyConverter(); } @@ -305,9 +372,12 @@ void WindowPortMus::SetPropertyFromServer( void WindowPortMus::SetFrameSinkIdFromServer( const viz::FrameSinkId& frame_sink_id) { - DCHECK(window_mus_type() == WindowMusType::EMBED_IN_OWNER); - window_->SetEmbedFrameSinkId(frame_sink_id); - UpdatePrimarySurfaceId(); + embed_frame_sink_id_ = frame_sink_id; + window_->SetEmbedFrameSinkId(embed_frame_sink_id_); + // We may not have allocated a LocalSurfaceId. Call OnWindowMusBoundsChanged() + // to trigger updating the LocalSurfaceId *and* notifying the server. + window_tree_client_->OnWindowMusBoundsChanged(this, window_->bounds(), + window_->bounds()); } const viz::LocalSurfaceId& WindowPortMus::GetOrAllocateLocalSurfaceId( @@ -331,25 +401,6 @@ const viz::LocalSurfaceId& WindowPortMus::GetOrAllocateLocalSurfaceId( return local_surface_id_; } -void WindowPortMus::SetFallbackSurfaceInfo( - const viz::SurfaceInfo& surface_info) { - if (!window_->IsEmbeddingClient()) { - // |primary_surface_id_| shold not be valid, since we didn't know the - // |window_->frame_sink_id()|. - DCHECK(!primary_surface_id_.is_valid()); - window_->SetEmbedFrameSinkId(surface_info.id().frame_sink_id()); - UpdatePrimarySurfaceId(); - } - - // The frame sink id should never be changed. - DCHECK_EQ(surface_info.id().frame_sink_id(), window_->GetFrameSinkId()); - - fallback_surface_info_ = surface_info; - UpdateClientSurfaceEmbedder(); - if (window_->delegate()) - window_->delegate()->OnFirstSurfaceActivation(fallback_surface_info_); -} - void WindowPortMus::DestroyFromServer() { std::unique_ptr<ScopedServerChange> remove_from_parent_change; if (window_->parent()) { @@ -408,6 +459,8 @@ WindowPortMus::ChangeSource WindowPortMus::OnTransientChildRemoved( void WindowPortMus::AllocateLocalSurfaceId() { local_surface_id_ = parent_local_surface_id_allocator_.GenerateId(); UpdatePrimarySurfaceId(); + if (local_layer_tree_frame_sink_) + local_layer_tree_frame_sink_->SetLocalSurfaceId(local_surface_id_); } bool WindowPortMus::IsLocalSurfaceIdAllocationSuppressed() const { @@ -426,6 +479,12 @@ void WindowPortMus::UpdateLocalSurfaceIdFromEmbeddedClient( embedded_client_local_surface_id); local_surface_id_ = parent_local_surface_id_allocator_.GetCurrentLocalSurfaceId(); + UpdatePrimarySurfaceId(); + + // OnWindowMusBoundsChanged() triggers notifying the server of the new + // LocalSurfaceId. + window_tree_client_->OnWindowMusBoundsChanged(this, window_->bounds(), + window_->bounds()); } const viz::LocalSurfaceId& WindowPortMus::GetLocalSurfaceId() { @@ -459,6 +518,7 @@ void WindowPortMus::PrepareForDestroy() { } void WindowPortMus::NotifyEmbeddedAppDisconnected() { + has_embedding_ = false; for (WindowObserver& observer : *GetObservers(window_)) observer.OnEmbeddedAppDisconnected(window_); } @@ -575,20 +635,18 @@ WindowPortMus::CreateLayerTreeFrameSink() { DCHECK_EQ(window_mus_type(), WindowMusType::LOCAL); DCHECK(!local_layer_tree_frame_sink_); - std::unique_ptr<cc::LayerTreeFrameSink> frame_sink; auto client_layer_tree_frame_sink = RequestLayerTreeFrameSink( - nullptr, - aura::Env::GetInstance()->context_factory()->GetGpuMemoryBufferManager()); + nullptr, window_->env()->context_factory()->GetGpuMemoryBufferManager()); local_layer_tree_frame_sink_ = client_layer_tree_frame_sink->GetWeakPtr(); - frame_sink = std::move(client_layer_tree_frame_sink); - window_->SetEmbedFrameSinkId(GenerateFrameSinkIdFromServerId()); + embed_frame_sink_id_ = GenerateFrameSinkIdFromServerId(); + window_->SetEmbedFrameSinkId(embed_frame_sink_id_); gfx::Size size_in_pixel = gfx::ConvertSizeToPixel(GetDeviceScaleFactor(), window_->bounds().size()); // Make sure |local_surface_id_| and |last_surface_size_in_pixels_| are // correct for the new created |local_layer_tree_frame_sink_|. GetOrAllocateLocalSurfaceId(size_in_pixel); - return frame_sink; + return client_layer_tree_frame_sink; } void WindowPortMus::OnEventTargetingPolicyChanged() { @@ -599,25 +657,30 @@ bool WindowPortMus::ShouldRestackTransientChildren() { return should_restack_transient_children_; } +void WindowPortMus::RegisterFrameSinkId(const viz::FrameSinkId& frame_sink_id) { + if (frame_sink_id == embed_frame_sink_id_) + return; + + window_tree_client_->RegisterFrameSinkId(this, frame_sink_id); +} + +void WindowPortMus::UnregisterFrameSinkId( + const viz::FrameSinkId& frame_sink_id) { + if (frame_sink_id == embed_frame_sink_id_) + return; + + window_tree_client_->UnregisterFrameSinkId(this); +} + void WindowPortMus::UpdatePrimarySurfaceId() { - if (window_mus_type() != WindowMusType::EMBED_IN_OWNER && - window_mus_type() != WindowMusType::LOCAL) { + if (window_mus_type() != WindowMusType::LOCAL) return; - } if (!window_->IsEmbeddingClient() || !local_surface_id_.is_valid()) return; primary_surface_id_ = viz::SurfaceId(window_->GetFrameSinkId(), local_surface_id_); - UpdateClientSurfaceEmbedder(); -} - -void WindowPortMus::UpdateClientSurfaceEmbedder() { - if (window_mus_type() != WindowMusType::EMBED_IN_OWNER && - window_mus_type() != WindowMusType::LOCAL) { - return; - } if (!client_surface_embedder_) { client_surface_embedder_ = std::make_unique<ClientSurfaceEmbedder>( @@ -625,7 +688,7 @@ void WindowPortMus::UpdateClientSurfaceEmbedder() { } client_surface_embedder_->SetPrimarySurfaceId(primary_surface_id_); - client_surface_embedder_->SetFallbackSurfaceInfo(fallback_surface_info_); + client_surface_embedder_->UpdateSizeAndGutters(); } } // namespace aura diff --git a/chromium/ui/aura/mus/window_port_mus.h b/chromium/ui/aura/mus/window_port_mus.h index e6ec469c098..428b05a2732 100644 --- a/chromium/ui/aura/mus/window_port_mus.h +++ b/chromium/ui/aura/mus/window_port_mus.h @@ -45,7 +45,6 @@ namespace aura { class ClientSurfaceEmbedder; class PropertyConverter; class Window; -class WindowPortMusTest; class WindowTreeClient; class WindowTreeClientPrivate; class WindowTreeHostMus; @@ -87,7 +86,7 @@ class AURA_EXPORT WindowPortMus : public WindowPort, public WindowMus { void SetCanAcceptDrops(bool can_accept_drops); // See description in mojom for details on this. - void SetHitTestMask(const base::Optional<gfx::Rect>& mask); + void SetHitTestInsets(const gfx::Insets& mouse, const gfx::Insets& touch); // Embeds a new client in this Window. See WindowTreeClient::Embed() for // details on arguments. @@ -106,7 +105,7 @@ class AURA_EXPORT WindowPortMus : public WindowPort, public WindowMus { viz::FrameSinkId GenerateFrameSinkIdFromServerId() const; private: - friend class WindowPortMusTest; + friend class WindowPortMusTestHelper; friend class WindowTreeClient; friend class WindowTreeClientPrivate; friend class WindowTreeHostMus; @@ -116,7 +115,7 @@ class AURA_EXPORT WindowPortMus : public WindowPort, public WindowMus { // Changes to the underlying Window originating from the server must be done // in such a way that the same change is not applied back to the server. To - // accomplish this every changes from the server is associated with at least + // accomplish this every change from the server is associated with at least // one ServerChange. If the underlying Window ends up calling back to this // class and the change is expected then the change is ignored and not sent to // the server. For example, here's the flow when the server changes the @@ -221,6 +220,16 @@ class AURA_EXPORT WindowPortMus : public WindowPort, public WindowMus { ServerChanges::iterator FindChangeByTypeAndData(const ServerChangeType type, const ServerChangeData& data); + // Called to setup state necessary for an embedding. Returns false if an + // embedding is not allowed in this window. + bool PrepareForEmbed(); + + // Called from OnEmbed() with the result of the embedding. |real_callback| is + // the callback supplied to the embed call. + static void OnEmbedAck(base::WeakPtr<WindowPortMus> window, + ws::mojom::WindowTree::EmbedCallback real_callback, + bool result); + PropertyConverter* GetPropertyConverter(); // WindowMus: @@ -245,7 +254,6 @@ class AURA_EXPORT WindowPortMus : public WindowPort, public WindowMus { const gfx::Size& surface_size_in_pixels) override; void UpdateLocalSurfaceIdFromEmbeddedClient( const viz::LocalSurfaceId& embedded_client_local_surface_id) override; - void SetFallbackSurfaceInfo(const viz::SurfaceInfo& surface_info) override; void DestroyFromServer() override; void AddTransientChildFromServer(WindowMus* child) override; void RemoveTransientChildFromServer(WindowMus* child) override; @@ -285,9 +293,10 @@ class AURA_EXPORT WindowPortMus : public WindowPort, public WindowMus { const viz::LocalSurfaceId& GetLocalSurfaceId() override; void OnEventTargetingPolicyChanged() override; bool ShouldRestackTransientChildren() override; + void RegisterFrameSinkId(const viz::FrameSinkId& frame_sink_id) override; + void UnregisterFrameSinkId(const viz::FrameSinkId& frame_sink_id) override; void UpdatePrimarySurfaceId(); - void UpdateClientSurfaceEmbedder(); WindowTreeClient* window_tree_client_; @@ -300,7 +309,6 @@ class AURA_EXPORT WindowPortMus : public WindowPort, public WindowMus { ServerChanges server_changes_; viz::SurfaceId primary_surface_id_; - viz::SurfaceInfo fallback_surface_info_; viz::LocalSurfaceId local_surface_id_; // TODO(sad, fsamuel): For 'mash' mode, where the embedder is responsible for @@ -311,9 +319,15 @@ class AURA_EXPORT WindowPortMus : public WindowPort, public WindowMus { ui::CursorData cursor_; + // Set if this class calls SetEmbedFrameSinkId() on the associated window. + viz::FrameSinkId embed_frame_sink_id_; + // See description in single place that changes the value for details. bool should_restack_transient_children_ = true; + // True if this window has an embedding. + bool has_embedding_ = false; + // When a frame sink is created // for a local aura::Window, we need keep a weak ptr of it, so we can update // the local surface id when necessary. diff --git a/chromium/ui/aura/mus/window_port_mus_unittest.cc b/chromium/ui/aura/mus/window_port_mus_unittest.cc index 0e05b5886d5..fe616b2dc28 100644 --- a/chromium/ui/aura/mus/window_port_mus_unittest.cc +++ b/chromium/ui/aura/mus/window_port_mus_unittest.cc @@ -7,26 +7,16 @@ #include "cc/mojo_embedder/async_layer_tree_frame_sink.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/aura/mus/client_surface_embedder.h" +#include "ui/aura/test/aura_mus_test_base.h" #include "ui/aura/test/aura_test_base.h" +#include "ui/aura/test/mus/test_window_tree.h" +#include "ui/aura/test/mus/window_port_mus_test_helper.h" #include "ui/aura/window.h" #include "ui/base/ui_base_features.h" namespace aura { -class WindowPortMusTest : public test::AuraTestBase { - public: - WindowPortMusTest() { EnableMusWithTestWindowTree(); } - - ~WindowPortMusTest() override = default; - - base::WeakPtr<cc::LayerTreeFrameSink> GetFrameSinkFor(Window* window) { - auto* window_mus = WindowPortMus::Get(window); - return window_mus->local_layer_tree_frame_sink_; - } - - private: - DISALLOW_COPY_AND_ASSIGN(WindowPortMusTest); -}; +using WindowPortMusTest = test::AuraMusClientTestBase; // TODO(sadrul): https://crbug.com/842361. TEST_F(WindowPortMusTest, @@ -45,7 +35,7 @@ TEST_F(WindowPortMusTest, window.CreateLayerTreeFrameSink()); EXPECT_TRUE(frame_sink.get()); - auto mus_frame_sink = GetFrameSinkFor(&window); + auto mus_frame_sink = WindowPortMusTestHelper(&window).GetFrameSink(); ASSERT_TRUE(mus_frame_sink); auto frame_sink_local_surface_id = static_cast<cc::mojo_embedder::AsyncLayerTreeFrameSink*>( @@ -71,4 +61,46 @@ TEST_F(WindowPortMusTest, ClientSurfaceEmbedderUpdatesLayer) { EXPECT_EQ(local_surface_id, primary_surface_id.local_surface_id()); } +TEST_F(WindowPortMusTest, + UpdateLocalSurfaceIdFromEmbeddedClientUpdateClientSurfaceEmbedder) { + Window window(nullptr); + window.Init(ui::LAYER_NOT_DRAWN); + window.set_owned_by_parent(false); + window.SetBounds(gfx::Rect(300, 300)); + // Simulate an embedding. + window.SetEmbedFrameSinkId(viz::FrameSinkId(0, 1)); + root_window()->AddChild(&window); + + // AckAllChanges() so that can verify a bounds change happens from + // UpdateLocalSurfaceIdFromEmbeddedClient(). + window_tree()->AckAllChanges(); + + // Update the LocalSurfaceId. + viz::LocalSurfaceId current_id = window.GetSurfaceId().local_surface_id(); + ASSERT_TRUE(current_id.is_valid()); + viz::ParentLocalSurfaceIdAllocator* parent_allocator = + WindowPortMusTestHelper(&window).GetParentLocalSurfaceIdAllocator(); + parent_allocator->Reset(current_id); + viz::LocalSurfaceId updated_id = parent_allocator->GenerateId(); + ASSERT_TRUE(updated_id.is_valid()); + EXPECT_NE(updated_id, current_id); + window.UpdateLocalSurfaceIdFromEmbeddedClient(updated_id); + + // Updating the LocalSurfaceId should propagate to the ClientSurfaceEmbedder. + auto* window_mus = WindowPortMus::Get(&window); + ASSERT_TRUE(window_mus); + ASSERT_TRUE(window_mus->client_surface_embedder()); + EXPECT_EQ(updated_id, window_mus->client_surface_embedder() + ->GetPrimarySurfaceIdForTesting() + .local_surface_id()); + + // The server is notified of a bounds change, so that it sees the new + // LocalSurfaceId. + ASSERT_EQ(1u, + window_tree()->GetChangeCountForType(WindowTreeChangeType::BOUNDS)); + ASSERT_TRUE(window_tree()->last_local_surface_id()); + EXPECT_EQ(window_mus->server_id(), window_tree()->window_id()); + EXPECT_EQ(updated_id, *(window_tree()->last_local_surface_id())); +} + } // namespace aura diff --git a/chromium/ui/aura/mus/window_tree_client.cc b/chromium/ui/aura/mus/window_tree_client.cc index 517bd131eaf..f28829be913 100644 --- a/chromium/ui/aura/mus/window_tree_client.cc +++ b/chromium/ui/aura/mus/window_tree_client.cc @@ -38,6 +38,7 @@ #include "ui/aura/mus/embed_root.h" #include "ui/aura/mus/embed_root_delegate.h" #include "ui/aura/mus/focus_synchronizer.h" +#include "ui/aura/mus/gesture_synchronizer.h" #include "ui/aura/mus/in_flight_change.h" #include "ui/aura/mus/input_method_mus.h" #include "ui/aura/mus/mus_context_factory.h" @@ -121,32 +122,7 @@ WindowTreeHostMus* GetWindowTreeHostMus(WindowMus* window) { } bool IsInternalProperty(const void* key) { - return key == client::kModalKey || key == client::kChildModalParentKey; -} - -// Create and return a MouseEvent or TouchEvent from |event| if |event| is a -// PointerEvent, otherwise return the copy of |event|. -std::unique_ptr<ui::Event> MapEvent(const ui::Event& event) { - if (event.IsPointerEvent()) { - const ui::PointerEvent& pointer_event = *event.AsPointerEvent(); - // Use a switch statement in case more pointer types are added. - switch (pointer_event.pointer_details().pointer_type) { - case ui::EventPointerType::POINTER_TYPE_MOUSE: - if (event.type() == ui::ET_POINTER_WHEEL_CHANGED) - return std::make_unique<ui::MouseWheelEvent>(pointer_event); - return std::make_unique<ui::MouseEvent>(pointer_event); - case ui::EventPointerType::POINTER_TYPE_TOUCH: - case ui::EventPointerType::POINTER_TYPE_PEN: - return std::make_unique<ui::TouchEvent>(pointer_event); - case ui::EventPointerType::POINTER_TYPE_ERASER: - NOTIMPLEMENTED(); - break; - case ui::EventPointerType::POINTER_TYPE_UNKNOWN: - NOTREACHED(); - break; - } - } - return ui::Event::Clone(event); + return key == client::kModalKey; } } // namespace @@ -273,31 +249,26 @@ void WindowTreeClient::SetImeVisibility(WindowMus* window, tree_->SetImeVisibility(window->server_id(), visible, std::move(state)); } -void WindowTreeClient::SetHitTestMask( - WindowMus* window, - const base::Optional<gfx::Rect>& mask_rect) { +void WindowTreeClient::SetHitTestInsets(WindowMus* window, + const gfx::Insets& mouse, + const gfx::Insets& touch) { DCHECK(tree_); - tree_->SetHitTestMask(window->server_id(), mask_rect); + tree_->SetHitTestInsets(window->server_id(), mouse, touch); } -void WindowTreeClient::Embed(Window* window, - ws::mojom::WindowTreeClientPtr client, - uint32_t flags, - ws::mojom::WindowTree::EmbedCallback callback) { - DCHECK(tree_); - // Window::Init() must be called before Embed() (otherwise the server hasn't - // been told about the window). - DCHECK(window->layer()); - if (!window->children().empty()) { - // The window server removes all children before embedding. In other words, - // it's generally an error to Embed() with existing children. So, fail - // early. - std::move(callback).Run(false); - return; - } +void WindowTreeClient::RegisterFrameSinkId( + WindowMus* window, + const viz::FrameSinkId& frame_sink_id) { + tree_->AttachFrameSinkId(window->server_id(), frame_sink_id); + + // Call OnWindowMusBoundsChanged() to force allocation of a LocalSurfaceId as + // well as notifying the server of the LocalSurfaceId. + const gfx::Rect bounds = window->GetWindow()->bounds(); + OnWindowMusBoundsChanged(window, bounds, bounds); +} - tree_->Embed(WindowMus::Get(window)->server_id(), std::move(client), flags, - std::move(callback)); +void WindowTreeClient::UnregisterFrameSinkId(WindowMus* window) { + tree_->UnattachFrameSinkId(window->server_id()); } void WindowTreeClient::ScheduleEmbed( @@ -306,27 +277,6 @@ void WindowTreeClient::ScheduleEmbed( tree_->ScheduleEmbed(std::move(client), std::move(callback)); } -void WindowTreeClient::EmbedUsingToken( - Window* window, - const base::UnguessableToken& token, - uint32_t flags, - ws::mojom::WindowTree::EmbedCallback callback) { - DCHECK(tree_); - // Window::Init() must be called before Embed() (otherwise the server hasn't - // been told about the window). - DCHECK(window->layer()); - if (!window->children().empty()) { - // The window server removes all children before embedding. In other words, - // it's generally an error to Embed() with existing children. So, fail - // early. - std::move(callback).Run(false); - return; - } - - tree_->EmbedUsingToken(WindowMus::Get(window)->server_id(), token, flags, - std::move(callback)); -} - void WindowTreeClient::AttachCompositorFrameSink( ws::Id window_id, viz::mojom::CompositorFrameSinkRequest compositor_frame_sink, @@ -413,7 +363,9 @@ WindowMus* WindowTreeClient::GetWindowByServerId(ws::Id id) { bool WindowTreeClient::IsWindowKnown(aura::Window* window) { WindowMus* window_mus = WindowMus::Get(window); - return windows_.count(window_mus->server_id()) > 0; + // NOTE: this function explicitly checks for a null WindowMus as it may be + // called from global observers that may see other types. + return window_mus && windows_.count(window_mus->server_id()) > 0; } InFlightChange* WindowTreeClient::GetOldestInFlightChangeMatching( @@ -584,6 +536,7 @@ void WindowTreeClient::WindowTreeConnectionEstablished( drag_drop_controller_ = std::make_unique<DragDropControllerMus>(this, tree_); capture_synchronizer_ = std::make_unique<CaptureSynchronizer>(this, tree_); focus_synchronizer_ = std::make_unique<FocusSynchronizer>(this, tree_); + gesture_synchronizer_ = std::make_unique<GestureSynchronizer>(tree_); } void WindowTreeClient::OnConnectionLost() { @@ -601,18 +554,6 @@ bool WindowTreeClient::HandleInternalPropertyChanged(WindowMus* window, window->GetWindow()->GetProperty(client::kModalKey)); return true; } - if (key == client::kChildModalParentKey) { - const uint32_t change_id = - ScheduleInFlightChange(std::make_unique<CrashInFlightChange>( - window, ChangeType::CHILD_MODAL_PARENT)); - Window* child_modal_parent = - window->GetWindow()->GetProperty(client::kChildModalParentKey); - tree_->SetChildModalParent( - change_id, window->server_id(), - child_modal_parent ? WindowMus::Get(child_modal_parent)->server_id() - : kInvalidServerId); - return true; - } return false; } @@ -736,16 +677,17 @@ void WindowTreeClient::ScheduleInFlightBoundsChange( ScheduleInFlightChange(std::make_unique<InFlightBoundsChange>( this, window, old_bounds, window->GetLocalSurfaceId())); base::Optional<viz::LocalSurfaceId> local_surface_id; - if (window->window_mus_type() == WindowMusType::EMBED_IN_OWNER || + if (window->GetWindow()->IsEmbeddingClient() || window->HasLocalLayerTreeFrameSink()) { // Do not use ConvertRectToPixel, enclosing rects cause problems. const gfx::Size size = gfx::ScaleToCeiledSize( new_bounds.size(), window->GetDeviceScaleFactor()); local_surface_id = window->GetOrAllocateLocalSurfaceId(size); // |window_tree_host| may be null if this is called during creation of - // the window associated with the WindowTreeHostMus. + // the window associated with the WindowTreeHostMus, or if there is an + // embedding. WindowTreeHost* window_tree_host = window->GetWindow()->GetHost(); - if (window_tree_host) + if (window_tree_host && window_tree_host->window() == window->GetWindow()) window_tree_host->compositor()->OnChildResizing(); } tree_->SetWindowBounds(change_id, window->server_id(), new_bounds, @@ -904,13 +846,24 @@ void WindowTreeClient::OnWindowMusPropertyChanged( WindowPortPropertyDataMus* data_mus = static_cast<WindowPortPropertyDataMus*>(data.get()); + PropertyConverter* property_converter = delegate_->GetPropertyConverter(); std::string transport_name; std::unique_ptr<std::vector<uint8_t>> transport_value; - if (!delegate_->GetPropertyConverter()->ConvertPropertyForTransport( + if (!property_converter->ConvertPropertyForTransport( window->GetWindow(), key, &transport_name, &transport_value)) { return; } DCHECK_EQ(transport_name, data_mus->transport_name); + const auto* window_ptr_key = static_cast<const WindowProperty<Window*>*>(key); + if (property_converter->IsWindowPtrPropertyRegistered(window_ptr_key)) { + DCHECK(!transport_value); + Window* value = window->GetWindow()->GetProperty(window_ptr_key); + WindowMus* window_mus = WindowMus::Get(value); + if (window_mus) { + transport_value = std::make_unique<std::vector<uint8_t>>( + mojo::ConvertTo<std::vector<uint8_t>>(window_mus->server_id())); + } + } base::Optional<std::vector<uint8_t>> transport_value_mojo; if (transport_value) @@ -1273,6 +1226,14 @@ void WindowTreeClient::OnWindowOpacityChanged(ws::Id window_id, window->SetOpacityFromServer(new_opacity); } +void WindowTreeClient::OnWindowDisplayChanged(ws::Id window_id, + int64_t display_id) { + WindowMus* window = GetWindowByServerId(window_id); + if (!window) + return; + GetWindowTreeHostMus(window->GetWindow())->set_display_id(display_id); +} + void WindowTreeClient::OnWindowParentDrawnStateChanged(ws::Id window_id, bool drawn) { // TODO: route to WindowTreeHost. @@ -1311,10 +1272,22 @@ void WindowTreeClient::OnWindowInputEvent(uint32_t event_id, DCHECK(event); WindowMus* window = GetWindowByServerId(window_id); // May be null. + DCHECK(!event->IsPointerEvent()); + if (matches_pointer_watcher && has_pointer_watcher_) { - DCHECK(event->IsPointerEvent()); - std::unique_ptr<ui::Event> event_in_dip(ui::Event::Clone(*event)); - NotifyPointerEventObserved(event_in_dip->AsPointerEvent(), display_id, + // TODO(sky): remove this once PointerWatcher doesn't need PointerEvent. + // https://crbug.com/865781 + std::unique_ptr<ui::Event> pointer_event; + if (event->IsMouseEvent()) { + pointer_event = + std::make_unique<ui::PointerEvent>(*event->AsMouseEvent()); + } else if (event->IsTouchEvent()) { + pointer_event = + std::make_unique<ui::PointerEvent>(*event->AsTouchEvent()); + } else { + NOTREACHED(); + } + NotifyPointerEventObserved(pointer_event->AsPointerEvent(), display_id, window); } @@ -1323,13 +1296,10 @@ void WindowTreeClient::OnWindowInputEvent(uint32_t event_id, if (!window || !window->GetWindow()->GetHost()) { EnvInputStateController* env_controller = Env::GetInstance()->env_controller(); - std::unique_ptr<ui::Event> mapped_event = MapEvent(*event.get()); - if (mapped_event->IsMouseEvent()) { - env_controller->UpdateStateForMouseEvent(nullptr, - *mapped_event->AsMouseEvent()); - } else if (mapped_event->IsTouchEvent()) { - env_controller->UpdateStateForTouchEvent(*mapped_event->AsTouchEvent()); - } + if (event->IsMouseEvent()) + env_controller->UpdateStateForMouseEvent(nullptr, *event->AsMouseEvent()); + else if (event->IsTouchEvent()) + env_controller->UpdateStateForTouchEvent(*event->AsTouchEvent()); tree_->OnWindowInputEventAck(event_id, ws::mojom::EventResult::UNHANDLED); return; } @@ -1343,10 +1313,6 @@ void WindowTreeClient::OnWindowInputEvent(uint32_t event_id, } } - // TODO(moshayedi): crbug.com/617222. No need to convert to ui::MouseEvent or - // ui::TouchEvent once we have proper support for pointer events. - std::unique_ptr<ui::Event> mapped_event = MapEvent(*event.get()); - ui::Event* event_to_dispatch = mapped_event.get(); // |ack_handler| may use |event_to_dispatch| from its destructor, so it needs // to be destroyed after |event_to_dispatch| is destroyed. EventAckHandler ack_handler(CreateEventResultCallback(event_id)); @@ -1354,12 +1320,12 @@ void WindowTreeClient::OnWindowInputEvent(uint32_t event_id, if (!event->IsKeyEvent()) { // Set |window| as the target, except for key events. Key events go to the // focused window, which may have changed by the time we process the event. - ui::Event::DispatcherApi(event_to_dispatch).set_target(window->GetWindow()); + ui::Event::DispatcherApi(event.get()).set_target(window->GetWindow()); } - GetWindowTreeHostMus(window)->SendEventToSink(event_to_dispatch); + GetWindowTreeHostMus(window)->SendEventToSink(event.get()); - ack_handler.set_handled(event_to_dispatch->handled()); + ack_handler.set_handled(event->handled()); } void WindowTreeClient::OnPointerEventObserved(std::unique_ptr<ui::Event> event, @@ -1397,21 +1363,6 @@ void WindowTreeClient::OnWindowCursorChanged(ws::Id window_id, window->SetCursorFromServer(cursor); } -void WindowTreeClient::OnWindowSurfaceChanged( - ws::Id window_id, - const viz::SurfaceInfo& surface_info) { - WindowMus* window = GetWindowByServerId(window_id); - if (!window) - return; - - // If the parent is informed of a child's surface then that surface ID is - // guaranteed to be available in the display compositor so we set it as the - // fallback. If surface synchronization is enabled, the primary SurfaceInfo - // is created by the embedder, and the LocalSurfaceId is allocated by the - // embedder. - window->SetFallbackSurfaceInfo(surface_info); -} - void WindowTreeClient::OnDragDropStart( const base::flat_map<std::string, std::vector<uint8_t>>& mime_data) { drag_drop_controller_->OnDragDropStart(mojo::FlatMapToMap(mime_data)); @@ -1605,13 +1556,6 @@ void WindowTreeClient::OnWindowTreeHostStackAtTop( tree_->StackAtTop(change_id, window->server_id()); } -void WindowTreeClient::OnWindowTreeHostPerformWmAction( - WindowTreeHostMus* window_tree_host, - const std::string& action) { - WindowMus* window = WindowMus::Get(window_tree_host->window()); - tree_->PerformWmAction(window->server_id(), action); -} - void WindowTreeClient::OnWindowTreeHostPerformWindowMove( WindowTreeHostMus* window_tree_host, ws::mojom::MoveLoopSource source, diff --git a/chromium/ui/aura/mus/window_tree_client.h b/chromium/ui/aura/mus/window_tree_client.h index f4d0931ed3e..9183c781309 100644 --- a/chromium/ui/aura/mus/window_tree_client.h +++ b/chromium/ui/aura/mus/window_tree_client.h @@ -61,6 +61,7 @@ class DragDropControllerMus; class EmbedRoot; class EmbedRootDelegate; class FocusSynchronizer; +class GestureSynchronizer; class InFlightBoundsChange; class InFlightChange; class InFlightFocusChange; @@ -136,20 +137,14 @@ class AURA_EXPORT WindowTreeClient void SetImeVisibility(WindowMus* window, bool visible, ui::mojom::TextInputStatePtr state); - void SetHitTestMask(WindowMus* window, const base::Optional<gfx::Rect>& rect); - - // Embeds a new client in |window|. |flags| is a bitmask of the values defined - // by kEmbedFlag*; 0 gives default behavior. |callback| is called to indicate - // whether the embedding succeeded or failed and may be called immediately if - // the embedding is known to fail. - void Embed(Window* window, - ws::mojom::WindowTreeClientPtr client, - uint32_t flags, - ws::mojom::WindowTree::EmbedCallback callback); - void EmbedUsingToken(Window* window, - const base::UnguessableToken& token, - uint32_t flags, - ws::mojom::WindowTree::EmbedCallback callback); + void SetHitTestInsets(WindowMus* window, + const gfx::Insets& mouse, + const gfx::Insets& touch); + + // See WindowPort for details on these. + void RegisterFrameSinkId(WindowMus* window, + const viz::FrameSinkId& child_frame_sink_id); + void UnregisterFrameSinkId(WindowMus* window); // Schedules an embed of a client. See // ws::mojom::WindowTreeClient::ScheduleEmbed() for details. @@ -177,6 +172,17 @@ class AURA_EXPORT WindowTreeClient ws::mojom::MoveLoopSource source, aura::Window* initial_target); + // See mojom for details. + template <typename Interface> + mojo::AssociatedInterfacePtr<Interface> BindWindowManagerInterface() { + ws::mojom::WindowManagerAssociatedPtr interface_ptr; + tree_->BindWindowManagerInterface(Interface::Name_, + mojo::MakeRequest(&interface_ptr)); + return mojo::AssociatedInterfacePtr<Interface>( + mojo::AssociatedInterfacePtrInfo<Interface>( + interface_ptr.PassInterface().PassHandle(), Interface::Version_)); + } + // Returns true if the specified window was created by this client. bool WasCreatedByThisClient(const WindowMus* window) const; @@ -403,6 +409,7 @@ class AURA_EXPORT WindowTreeClient void OnWindowOpacityChanged(ws::Id window_id, float old_opacity, float new_opacity) override; + void OnWindowDisplayChanged(ws::Id window_id, int64_t display_id) override; void OnWindowParentDrawnStateChanged(ws::Id window_id, bool drawn) override; void OnWindowSharedPropertyChanged( ws::Id window_id, @@ -418,8 +425,6 @@ class AURA_EXPORT WindowTreeClient int64_t display_id) override; void OnWindowFocused(ws::Id focused_window_id) override; void OnWindowCursorChanged(ws::Id window_id, ui::CursorData cursor) override; - void OnWindowSurfaceChanged(ws::Id window_id, - const viz::SurfaceInfo& surface_info) override; void OnDragDropStart(const base::flat_map<std::string, std::vector<uint8_t>>& mime_data) override; void OnDragEnter(ws::Id window_id, @@ -469,8 +474,6 @@ class AURA_EXPORT WindowTreeClient void OnWindowTreeHostStackAbove(WindowTreeHostMus* window_tree_host, Window* window) override; void OnWindowTreeHostStackAtTop(WindowTreeHostMus* window_tree_host) override; - void OnWindowTreeHostPerformWmAction(WindowTreeHostMus* window_tree_host, - const std::string& action) override; void OnWindowTreeHostPerformWindowMove( WindowTreeHostMus* window_tree_host, ws::mojom::MoveLoopSource mus_source, @@ -526,6 +529,8 @@ class AURA_EXPORT WindowTreeClient std::unique_ptr<FocusSynchronizer> focus_synchronizer_; + std::unique_ptr<GestureSynchronizer> gesture_synchronizer_; + mojo::Binding<ws::mojom::WindowTreeClient> binding_; ws::mojom::WindowTreePtr tree_ptr_; // Typically this is the value contained in |tree_ptr_|, but tests may diff --git a/chromium/ui/aura/mus/window_tree_client_unittest.cc b/chromium/ui/aura/mus/window_tree_client_unittest.cc index e8c841cb632..2ca4cce0c21 100644 --- a/chromium/ui/aura/mus/window_tree_client_unittest.cc +++ b/chromium/ui/aura/mus/window_tree_client_unittest.cc @@ -39,6 +39,7 @@ #include "ui/aura/mus/window_tree_host_mus_init_params.h" #include "ui/aura/test/aura_mus_test_base.h" #include "ui/aura/test/mus/test_window_tree.h" +#include "ui/aura/test/mus/window_port_mus_test_helper.h" #include "ui/aura/test/mus/window_tree_client_private.h" #include "ui/aura/test/test_screen.h" #include "ui/aura/test/test_window_delegate.h" @@ -68,10 +69,12 @@ namespace { DEFINE_UI_CLASS_PROPERTY_KEY(uint8_t, kTestPropertyKey1, 0); DEFINE_UI_CLASS_PROPERTY_KEY(uint16_t, kTestPropertyKey2, 0); DEFINE_UI_CLASS_PROPERTY_KEY(bool, kTestPropertyKey3, false); +DEFINE_UI_CLASS_PROPERTY_KEY(Window*, kTestPropertyKey4, nullptr); const char kTestPropertyServerKey1[] = "test-property-server1"; const char kTestPropertyServerKey2[] = "test-property-server2"; const char kTestPropertyServerKey3[] = "test-property-server3"; +const char kTestPropertyServerKey4[] = "test-property-server4"; ws::Id server_id(Window* window) { return window ? WindowMus::Get(window)->server_id() : 0; @@ -113,6 +116,8 @@ void RegisterTestProperties(PropertyConverter* converter) { converter->RegisterPrimitiveProperty( kTestPropertyKey3, kTestPropertyServerKey3, PropertyConverter::CreateAcceptAnyValueCallback()); + converter->RegisterWindowPtrProperty(kTestPropertyKey4, + kTestPropertyServerKey4); } // Convert a primitive aura property value to a mus transport value. @@ -269,11 +274,8 @@ TEST_F(WindowTreeClientTest, SetBoundsFailed) { // reverted if the server replied that the change failed. TEST_F(WindowTreeClientTest, SetBoundsFailedLocalSurfaceId) { Window window(nullptr); - // TOP_LEVEL_IN_WM and EMBED_IN_OWNER windows allocate viz::LocalSurfaceIds - // when their sizes change. - window.SetProperty(aura::client::kEmbedType, - aura::client::WindowEmbedType::EMBED_IN_OWNER); window.Init(ui::LAYER_NOT_DRAWN); + WindowPortMusTestHelper(&window).SimulateEmbedding(); const gfx::Rect original_bounds(window.bounds()); const gfx::Rect new_bounds(gfx::Rect(0, 0, 100, 100)); @@ -295,111 +297,29 @@ INSTANTIATE_TEST_CASE_P(/* no prefix */, WindowTreeClientTestSurfaceSync, ::testing::Bool()); -namespace { - -class FirstSurfaceActivationWindowDelegate : public test::TestWindowDelegate { - public: - FirstSurfaceActivationWindowDelegate() = default; - ~FirstSurfaceActivationWindowDelegate() override = default; - - const viz::SurfaceInfo& last_surface_info() const { - return last_surface_info_; - } - - void OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info) override { - last_surface_info_ = surface_info; - } - - private: - viz::SurfaceInfo last_surface_info_; - - DISALLOW_COPY_AND_ASSIGN(FirstSurfaceActivationWindowDelegate); -}; - -} // namespace - -// Verifies that a ClientSurfaceEmbedder is created for a window once it has -// a bounds, and a valid FrameSinkId. -TEST_P(WindowTreeClientTestSurfaceSync, ClientSurfaceEmbedderOnValidEmbedding) { - FirstSurfaceActivationWindowDelegate delegate; - Window window(&delegate); - // EMBED_IN_OWNER windows allocate viz::LocalSurfaceIds when their sizes - // change. - window.SetProperty(aura::client::kEmbedType, - aura::client::WindowEmbedType::EMBED_IN_OWNER); +// Verifies that windows with an embedding create a ClientSurfaceEmbedder. +TEST_P(WindowTreeClientTestSurfaceSync, ClientSurfaceEmbedderCreated) { + Window window(nullptr); window.Init(ui::LAYER_NOT_DRAWN); + WindowPortMusTestHelper(&window).SimulateEmbedding(); // The window will allocate a viz::LocalSurfaceId once it has a bounds. - WindowMus* window_mus = WindowMus::Get(&window); - ASSERT_NE(nullptr, window_mus); - EXPECT_FALSE(window_mus->GetLocalSurfaceId().is_valid()); - gfx::Rect new_bounds(gfx::Rect(0, 0, 100, 100)); - ASSERT_NE(new_bounds, window.bounds()); - window.SetBounds(new_bounds); - EXPECT_EQ(new_bounds, window.bounds()); - EXPECT_TRUE(window_mus->GetLocalSurfaceId().is_valid()); - - // An ClientSurfaceEmbedder isn't created UNTIL the window has a bounds and - // a valid FrameSinkId. WindowPortMus* window_port_mus = WindowPortMus::Get(&window); ASSERT_NE(nullptr, window_port_mus); + EXPECT_FALSE(WindowMus::Get(&window)->GetLocalSurfaceId().is_valid()); + // A ClientSurfaceEmbedder is only created once there is bounds and a + // FrameSinkId. EXPECT_EQ(nullptr, window_port_mus->client_surface_embedder()); - - // Now that the window has a valid FrameSinkId, it can embed the client in a - // CompositorFrame. - window_tree_client()->OnFrameSinkIdAllocated(server_id(&window), - viz::FrameSinkId(1, 1)); - ClientSurfaceEmbedder* client_surface_embedder = - window_port_mus->client_surface_embedder(); - ASSERT_NE(nullptr, client_surface_embedder); - EXPECT_FALSE(delegate.last_surface_info().is_valid()); - - // When a SurfaceInfo arrives from the window server, we use it as the - // fallback SurfaceInfo. Here we issue the primary SurfaceId back to the - // client lib. This should cause the gutter to go away, eliminating overdraw. - window_tree_client()->OnWindowSurfaceChanged( - server_id(&window), - viz::SurfaceInfo(window_port_mus->PrimarySurfaceIdForTesting(), 1.0f, - gfx::Size(100, 100))); - EXPECT_TRUE(delegate.last_surface_info().is_valid()); - EXPECT_EQ(delegate.last_surface_info().id(), - window_port_mus->PrimarySurfaceIdForTesting()); -} - -// Verifies that EMBED_IN_OWNER windows do not gutter. -TEST_P(WindowTreeClientTestSurfaceSync, NoEmbedInOwnerGutter) { - FirstSurfaceActivationWindowDelegate delegate; - Window window(&delegate); - // TOP_LEVEL_IN_WM and EMBED_IN_OWNER windows allocate viz::LocalSurfaceIds - // when their sizes change. - window.SetProperty(aura::client::kEmbedType, - aura::client::WindowEmbedType::EMBED_IN_OWNER); - window.Init(ui::LAYER_NOT_DRAWN); - - // The window will allocate a viz::LocalSurfaceId once it has a bounds. - WindowMus* window_mus = WindowMus::Get(&window); - ASSERT_NE(nullptr, window_mus); - EXPECT_FALSE(window_mus->GetLocalSurfaceId().is_valid()); gfx::Rect new_bounds(gfx::Rect(0, 0, 100, 100)); ASSERT_NE(new_bounds, window.bounds()); window.SetBounds(new_bounds); EXPECT_EQ(new_bounds, window.bounds()); - EXPECT_TRUE(window_mus->GetLocalSurfaceId().is_valid()); + EXPECT_TRUE(WindowMus::Get(&window)->GetLocalSurfaceId().is_valid()); - // An ClientSurfaceEmbedder isn't created UNTIL the window has a bounds and - // a valid FrameSinkId. - WindowPortMus* window_port_mus = WindowPortMus::Get(&window); - ASSERT_NE(nullptr, window_port_mus); - EXPECT_EQ(nullptr, window_port_mus->client_surface_embedder()); - - // Now that the window has a valid FrameSinkId, it can embed the client in a - // CompositorFrame. - window_tree_client()->OnFrameSinkIdAllocated(server_id(&window), - viz::FrameSinkId(1, 1)); + // Once the bounds have been set, the ClientSurfaceEmbedder should be created. ClientSurfaceEmbedder* client_surface_embedder = window_port_mus->client_surface_embedder(); ASSERT_NE(nullptr, client_surface_embedder); - EXPECT_FALSE(delegate.last_surface_info().is_valid()); EXPECT_EQ(nullptr, client_surface_embedder->BottomGutterForTesting()); EXPECT_EQ(nullptr, client_surface_embedder->RightGutterForTesting()); @@ -410,11 +330,8 @@ TEST_P(WindowTreeClientTestSurfaceSync, NoEmbedInOwnerGutter) { TEST_P(WindowTreeClientTestSurfaceSync, SetBoundsLocalSurfaceIdChanges) { ASSERT_EQ(base::nullopt, window_tree()->last_local_surface_id()); Window window(nullptr); - // TOP_LEVEL_IN_WM and EMBED_IN_OWNER windows allocate viz::LocalSurfaceIds - // when their sizes change. - window.SetProperty(aura::client::kEmbedType, - aura::client::WindowEmbedType::EMBED_IN_OWNER); window.Init(ui::LAYER_NOT_DRAWN); + WindowPortMusTestHelper(&window).SimulateEmbedding(); // Resize the window and verify that we've allocated a viz::LocalSurfaceId. const gfx::Rect new_bounds(0, 0, 100, 100); @@ -800,6 +717,26 @@ TEST_F(WindowTreeClientTest, SetStringProperty) { EXPECT_EQ(example, *root_window()->GetProperty(client::kNameKey)); } +TEST_F(WindowTreeClientTest, SetWindowPointerProperty) { + PropertyConverter* property_converter = GetPropertyConverter(); + RegisterTestProperties(property_converter); + + Window window(nullptr); + window.Init(ui::LAYER_NOT_DRAWN); + window.Show(); + root_window()->SetProperty(kTestPropertyKey4, &window); + base::Optional<std::vector<uint8_t>> value = + window_tree()->GetLastPropertyValue(); + ASSERT_TRUE(value.has_value()); + EXPECT_EQ(WindowMus::Get(&window)->server_id(), + mojo::ConvertTo<ws::Id>(*value)); + window_tree()->AckAllChanges(); + + root_window()->ClearProperty(kTestPropertyKey4); + value = window_tree()->GetLastPropertyValue(); + EXPECT_FALSE(value.has_value()); +} + // Verifies visible is reverted if the server replied that the change failed. TEST_F(WindowTreeClientTest, SetVisibleFailed) { const bool original_visible = root_window()->TargetVisibility(); @@ -1010,13 +947,13 @@ TEST_F(WindowTreeClientTest, InputEventPointerEvent) { const gfx::Point event_location(2, 3); const uint32_t event_id = 1; window_delegate.set_event_id(event_id); - ui::PointerEvent pointer_event( - ui::ET_POINTER_MOVED, event_location, gfx::Point(), ui::EF_NONE, 0, - ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_MOUSE, 0), - base::TimeTicks()); + ui::MouseEvent mouse_event( + ui::ET_MOUSE_MOVED, event_location, event_location, ui::EventTimeForNow(), + ui::EF_NONE, ui::EF_NONE, + ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_MOUSE, 0)); window_tree_client()->OnWindowInputEvent(event_id, server_id(&child), window_tree_host.display_id(), - ui::Event::Clone(pointer_event), 0); + ui::Event::Clone(mouse_event), 0); EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ws::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); @@ -1046,13 +983,13 @@ TEST_F(WindowTreeClientTest, InputEventPen) { const gfx::Point event_location(2, 3); const uint32_t event_id = 1; window_delegate.set_event_id(event_id); - ui::PointerEvent pointer_event( - ui::ET_POINTER_DOWN, event_location, gfx::Point(), ui::EF_NONE, 0, - ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_PEN, 0), - ui::EventTimeForNow()); + ui::MouseEvent mouse_event( + ui::ET_MOUSE_PRESSED, event_location, event_location, + ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE, + ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_PEN, 0)); window_tree_client()->OnWindowInputEvent(event_id, server_id(&child), window_tree_host.display_id(), - ui::Event::Clone(pointer_event), 0); + ui::Event::Clone(mouse_event), 0); // Pen event was handled. EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); @@ -1346,14 +1283,13 @@ TEST_F(WindowTreeClientTest, InputMouseEventNoWindow) { const gfx::Point event_location(2, 3); uint32_t event_id = 1; window_delegate.set_event_id(event_id); - ui::PointerEvent pointer_event_down( - ui::ET_POINTER_DOWN, event_location, event_location, - ui::EF_LEFT_MOUSE_BUTTON, 0, - ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_MOUSE, 0), - ui::EventTimeForNow()); + ui::MouseEvent mouse_event_down( + ui::ET_MOUSE_PRESSED, event_location, event_location, + ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0, + ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_MOUSE, 0)); window_tree_client()->OnWindowInputEvent( event_id, server_id(&child), window_tree_host.display_id(), - ui::Event::Clone(pointer_event_down), 0); + ui::Event::Clone(mouse_event_down), 0); EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ws::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); @@ -1366,14 +1302,13 @@ TEST_F(WindowTreeClientTest, InputMouseEventNoWindow) { const gfx::Point event_location1(4, 5); event_id = 2; window_delegate.set_event_id(event_id); - ui::PointerEvent pointer_event_up( - ui::ET_POINTER_UP, event_location1, event_location, - ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON, - ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_MOUSE, 0), - ui::EventTimeForNow()); - window_tree_client()->OnWindowInputEvent( - event_id, kInvalidServerId, window_tree_host.display_id(), - ui::Event::Clone(pointer_event_up), 0); + ui::MouseEvent mouse_event_up( + ui::ET_MOUSE_RELEASED, event_location1, event_location, + ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_LEFT_MOUSE_BUTTON, + ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_MOUSE, 0)); + window_tree_client()->OnWindowInputEvent(event_id, kInvalidServerId, + window_tree_host.display_id(), + ui::Event::Clone(mouse_event_up), 0); EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); // WindowTreeClient::OnWindowInputEvent cannot find a target window with // kInvalidServerId but should use the event to update event states kept in @@ -1410,13 +1345,12 @@ TEST_F(WindowTreeClientTest, InputTouchEventNoWindow) { const gfx::Point event_location(2, 3); uint32_t event_id = 1; window_delegate.set_event_id(event_id); - ui::PointerEvent pointer_event_down( - ui::ET_POINTER_DOWN, event_location, gfx::Point(), 0, 0, - ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 0), - ui::EventTimeForNow()); + ui::TouchEvent touch_event_down( + ui::ET_TOUCH_PRESSED, event_location, ui::EventTimeForNow(), + ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 0)); window_tree_client()->OnWindowInputEvent( event_id, server_id(&child), window_tree_host.display_id(), - ui::Event::Clone(pointer_event_down), 0); + ui::Event::Clone(touch_event_down), 0); EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); EXPECT_EQ(ws::mojom::EventResult::HANDLED, window_tree()->GetEventResult(event_id)); @@ -1426,13 +1360,12 @@ TEST_F(WindowTreeClientTest, InputTouchEventNoWindow) { event_id = 2; window_delegate.set_event_id(event_id); - ui::PointerEvent pointer_event_up( - ui::ET_POINTER_UP, event_location, gfx::Point(), 0, 0, - ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 0), - ui::EventTimeForNow()); - window_tree_client()->OnWindowInputEvent( - event_id, kInvalidServerId, window_tree_host.display_id(), - ui::Event::Clone(pointer_event_up), 0); + ui::TouchEvent touch_event_up( + ui::ET_TOUCH_RELEASED, event_location, ui::EventTimeForNow(), + ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 0)); + window_tree_client()->OnWindowInputEvent(event_id, kInvalidServerId, + window_tree_host.display_id(), + ui::Event::Clone(touch_event_up), 0); EXPECT_TRUE(window_tree()->WasEventAcked(event_id)); // WindowTreeClient::OnWindowInputEvent cannot find a target window with // kInvalidServerId but should use the event to update event states kept in @@ -1543,12 +1476,12 @@ TEST_F(WindowTreeClientPointerObserverTest, window_tree_client_impl()->StartPointerWatcher(false /* want_moves */); // Simulate the server dispatching an event that also matched the observer. - std::unique_ptr<ui::PointerEvent> pointer_event_down(new ui::PointerEvent( - ui::ET_POINTER_DOWN, gfx::Point(), gfx::Point(), ui::EF_CONTROL_DOWN, 0, + ui::TouchEvent touch_event_down( + ui::ET_TOUCH_PRESSED, gfx::Point(), ui::EventTimeForNow(), ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 1), - base::TimeTicks::Now())); - window_tree_client()->OnWindowInputEvent(1, server_id(top_level), 0, - std::move(pointer_event_down), true); + ui::EF_CONTROL_DOWN); + window_tree_client()->OnWindowInputEvent( + 1, server_id(top_level), 0, ui::Event::Clone(touch_event_down), true); // Delegate sensed the event. const ui::Event* last_event = last_event_observed(); diff --git a/chromium/ui/aura/mus/window_tree_host_mus.cc b/chromium/ui/aura/mus/window_tree_host_mus.cc index 4714427d756..47ff9645626 100644 --- a/chromium/ui/aura/mus/window_tree_host_mus.cc +++ b/chromium/ui/aura/mus/window_tree_host_mus.cc @@ -146,10 +146,6 @@ void WindowTreeHostMus::StackAtTop() { delegate_->OnWindowTreeHostStackAtTop(this); } -void WindowTreeHostMus::PerformWmAction(const std::string& action) { - delegate_->OnWindowTreeHostPerformWmAction(this, action); -} - void WindowTreeHostMus::PerformWindowMove( ws::mojom::MoveLoopSource mus_source, const gfx::Point& cursor_location, diff --git a/chromium/ui/aura/mus/window_tree_host_mus.h b/chromium/ui/aura/mus/window_tree_host_mus.h index c023265a716..eaafd008187 100644 --- a/chromium/ui/aura/mus/window_tree_host_mus.h +++ b/chromium/ui/aura/mus/window_tree_host_mus.h @@ -68,9 +68,6 @@ class AURA_EXPORT WindowTreeHostMus : public WindowTreeHostPlatform, // windows which we might not own. void StackAtTop(); - // Requests that the window manager perform |action| on the window. - void PerformWmAction(const std::string& action); - // Tells the window manager to take control of moving the window. Returns // true if the move wasn't canceled. void PerformWindowMove(ws::mojom::MoveLoopSource mus_source, diff --git a/chromium/ui/aura/mus/window_tree_host_mus_delegate.h b/chromium/ui/aura/mus/window_tree_host_mus_delegate.h index 636d7018f35..0c17643cca5 100644 --- a/chromium/ui/aura/mus/window_tree_host_mus_delegate.h +++ b/chromium/ui/aura/mus/window_tree_host_mus_delegate.h @@ -54,11 +54,6 @@ class AURA_EXPORT WindowTreeHostMusDelegate { virtual void OnWindowTreeHostStackAtTop( WindowTreeHostMus* window_tree_host) = 0; - // Called to signal to the window manager to take an action. - virtual void OnWindowTreeHostPerformWmAction( - WindowTreeHostMus* window_tree_host, - const std::string& action) = 0; - // Called to start a move loop, where the window manager will take over // moving a window during a drag. virtual void OnWindowTreeHostPerformWindowMove( diff --git a/chromium/ui/aura/mus/window_tree_host_mus_unittest.cc b/chromium/ui/aura/mus/window_tree_host_mus_unittest.cc index 39a7302ed1d..90c5bc823a2 100644 --- a/chromium/ui/aura/mus/window_tree_host_mus_unittest.cc +++ b/chromium/ui/aura/mus/window_tree_host_mus_unittest.cc @@ -23,30 +23,4 @@ TEST_F(WindowTreeHostMusTest, UpdateClientArea) { EXPECT_EQ(new_insets, window_tree()->last_client_area()); } -TEST_F(WindowTreeHostMusTest, SetHitTestMask) { - std::unique_ptr<WindowTreeHostMus> window_tree_host_mus = - std::make_unique<WindowTreeHostMus>( - CreateInitParamsForTopLevel(window_tree_client_impl())); - - EXPECT_FALSE(window_tree()->last_hit_test_mask().has_value()); - gfx::Rect mask(10, 10, 10, 10); - WindowPortMus::Get(window_tree_host_mus->window())->SetHitTestMask(mask); - ASSERT_TRUE(window_tree()->last_hit_test_mask().has_value()); - EXPECT_EQ(mask, window_tree()->last_hit_test_mask()); - - WindowPortMus::Get(window_tree_host_mus->window()) - ->SetHitTestMask(base::nullopt); - ASSERT_FALSE(window_tree()->last_hit_test_mask().has_value()); -} - -TEST_F(WindowTreeHostMusTest, PerformWmAction) { - std::unique_ptr<WindowTreeHostMus> window_tree_host_mus = - std::make_unique<WindowTreeHostMus>( - CreateInitParamsForTopLevel(window_tree_client_impl())); - - const std::string test_action("test-action"); - window_tree_host_mus->PerformWmAction(test_action); - EXPECT_EQ(test_action, window_tree()->last_wm_action()); -} - } // namespace aura diff --git a/chromium/ui/aura/screen_ozone.h b/chromium/ui/aura/screen_ozone.h index 132ea591702..7c26999f515 100644 --- a/chromium/ui/aura/screen_ozone.h +++ b/chromium/ui/aura/screen_ozone.h @@ -5,6 +5,8 @@ #ifndef UI_AURA_SCREEN_OZONE_H_ #define UI_AURA_SCREEN_OZONE_H_ +#include <memory> + #include "base/macros.h" #include "ui/aura/aura_export.h" #include "ui/display/screen.h" diff --git a/chromium/ui/aura/test/ui_controls_factory_ozone.cc b/chromium/ui/aura/test/ui_controls_factory_ozone.cc index 8538cfb0893..24ef109d467 100644 --- a/chromium/ui/aura/test/ui_controls_factory_ozone.cc +++ b/chromium/ui/aura/test/ui_controls_factory_ozone.cc @@ -6,12 +6,12 @@ #include "base/location.h" #include "base/logging.h" #include "base/macros.h" +#include "base/optional.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "services/service_manager/public/cpp/connector.h" #include "services/ws/public/mojom/constants.mojom.h" #include "services/ws/public/mojom/event_injector.mojom.h" -#include "ui/aura/client/screen_position_client.h" #include "ui/aura/env.h" #include "ui/aura/mus/window_tree_client.h" #include "ui/aura/test/aura_test_utils.h" @@ -19,6 +19,8 @@ #include "ui/aura/test/ui_controls_factory_aura.h" #include "ui/aura/window_tree_host.h" #include "ui/base/test/ui_controls_aura.h" +#include "ui/display/display.h" +#include "ui/display/screen.h" #include "ui/events/event_utils.h" #include "ui/events/test/events_test_utils.h" @@ -122,16 +124,19 @@ class UIControlsOzone : public ui_controls::UIControlsAura { bool SendMouseMoveNotifyWhenDone(long screen_x, long screen_y, base::OnceClosure closure) override { - gfx::Point root_location(screen_x, screen_y); - aura::client::ScreenPositionClient* screen_position_client = - aura::client::GetScreenPositionClient(host_->window()); - if (screen_position_client) { - screen_position_client->ConvertPointFromScreen(host_->window(), - &root_location); + // The location needs to be in display's coordinate. + gfx::Point display_location(screen_x, screen_y); + display::Display display; + if (!display::Screen::GetScreen()->GetDisplayWithDisplayId( + host_->GetDisplayId(), &display)) { + LOG(ERROR) << "Failed to see the display for " << host_->GetDisplayId(); + return false; } + display_location -= display.bounds().OffsetFromOrigin(); - gfx::Point host_location = root_location; + gfx::Point host_location = display_location; host_->ConvertDIPToPixels(&host_location); + last_mouse_location_ = host_location; ui::EventType event_type; @@ -155,16 +160,24 @@ class UIControlsOzone : public ui_controls::UIControlsAura { int button_state, base::OnceClosure closure, int accelerator_state) override { - gfx::Point root_location = host_->window()->env()->last_mouse_location(); - aura::client::ScreenPositionClient* screen_position_client = - aura::client::GetScreenPositionClient(host_->window()); - if (screen_position_client) { - screen_position_client->ConvertPointFromScreen(host_->window(), - &root_location); - } + gfx::Point host_location; + if (last_mouse_location_.has_value()) { + host_location = last_mouse_location_.value(); + } else { + // The location needs to be in display's coordinate. + gfx::Point display_location = + host_->window()->env()->last_mouse_location(); + display::Display display; + if (!display::Screen::GetScreen()->GetDisplayWithDisplayId( + host_->GetDisplayId(), &display)) { + LOG(ERROR) << "Failed to see the display for " << host_->GetDisplayId(); + return false; + } + display_location -= display.bounds().OffsetFromOrigin(); - gfx::Point host_location = root_location; - host_->ConvertDIPToPixels(&host_location); + host_location = display_location; + host_->ConvertDIPToPixels(&host_location); + } int changed_button_flag = 0; @@ -219,18 +232,8 @@ class UIControlsOzone : public ui_controls::UIControlsAura { private: void SendEventToSink(ui::Event* event, base::OnceClosure closure) { if (host_->window()->env()->mode() == aura::Env::Mode::MUS) { - std::unique_ptr<ui::Event> event_to_send; - if (event->IsMouseEvent()) { - // WindowService expects MouseEvents as PointerEvents. - // See http://crbug.com/617222. - event_to_send = - std::make_unique<ui::PointerEvent>(*event->AsMouseEvent()); - } else { - event_to_send = ui::Event::Clone(*event); - } - GetEventInjector()->InjectEvent( - host_->GetDisplayId(), std::move(event_to_send), + host_->GetDisplayId(), ui::Event::Clone(*event), base::BindOnce(&OnWindowServiceProcessedEvent, std::move(closure))); return; } @@ -311,6 +314,11 @@ class UIControlsOzone : public ui_controls::UIControlsAura { WindowTreeHost* host_; ws::mojom::EventInjectorPtr event_injector_; + // The mouse location for the last SendMouseEventsNotifyWhenDone call. This is + // used rather than Env::last_mouse_location() as Env::last_mouse_location() + // is updated asynchronously with mus. + base::Optional<gfx::Point> last_mouse_location_; + // Mask of the mouse buttons currently down. This is static as it needs to // track the state globally for all displays. A UIControlsOzone instance is // created for each display host. diff --git a/chromium/ui/aura/window.cc b/chromium/ui/aura/window.cc index 3ebec72c8db..18829840094 100644 --- a/chromium/ui/aura/window.cc +++ b/chromium/ui/aura/window.cc @@ -92,7 +92,7 @@ Window::Window(WindowDelegate* delegate, } Window::~Window() { - WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion_tracking; + WindowOcclusionTracker::ScopedPause pause_occlusion_tracking(env_); if (layer()->owner() == this) layer()->CompleteAllAnimations(); @@ -160,7 +160,7 @@ Window::~Window() { } void Window::Init(ui::LayerType layer_type) { - WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion_tracking; + WindowOcclusionTracker::ScopedPause pause_occlusion_tracking(env_); if (!port_owner_) { port_owner_ = env_->CreateWindowPort(this); @@ -285,7 +285,7 @@ gfx::Rect Window::GetBoundsInScreen() const { } void Window::SetTransform(const gfx::Transform& transform) { - WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion_tracking; + WindowOcclusionTracker::ScopedPause pause_occlusion_tracking(env_); for (WindowObserver& observer : observers_) observer.OnWindowTargetTransformChanging(this, transform); layer()->SetTransform(transform); @@ -308,7 +308,11 @@ void Window::SetLayoutManager(LayoutManager* layout_manager) { std::unique_ptr<WindowTargeter> Window::SetEventTargeter( std::unique_ptr<WindowTargeter> targeter) { std::unique_ptr<WindowTargeter> old_targeter = std::move(targeter_); + if (old_targeter) + old_targeter->OnInstalled(nullptr); targeter_ = std::move(targeter); + if (targeter_) + targeter_->OnInstalled(this); return old_targeter; } @@ -369,7 +373,7 @@ void Window::StackChildBelow(Window* child, Window* target) { } void Window::AddChild(Window* child) { - WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion_tracking; + WindowOcclusionTracker::ScopedPause pause_occlusion_tracking(env_); DCHECK(layer()) << "Parent has not been Init()ed yet."; DCHECK(child->layer()) << "Child has not been Init()ed yt."; @@ -411,7 +415,7 @@ void Window::AddChild(Window* child) { } void Window::RemoveChild(Window* child) { - WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion_tracking; + WindowOcclusionTracker::ScopedPause pause_occlusion_tracking(env_); WindowObserver::HierarchyChangeParams params; params.target = child; @@ -547,6 +551,20 @@ bool Window::HasObserver(const WindowObserver* observer) const { } void Window::SetEventTargetingPolicy(ws::mojom::EventTargetingPolicy policy) { +#if DCHECK_IS_ON() + const bool old_window_accepts_events = + (event_targeting_policy_ == + ws::mojom::EventTargetingPolicy::TARGET_ONLY) || + (event_targeting_policy_ == + ws::mojom::EventTargetingPolicy::TARGET_AND_DESCENDANTS); + const bool new_window_accepts_events = + (policy == ws::mojom::EventTargetingPolicy::TARGET_ONLY) || + (policy == ws::mojom::EventTargetingPolicy::TARGET_AND_DESCENDANTS); + if (new_window_accepts_events != old_window_accepts_events) { + DCHECK(!created_layer_tree_frame_sink_); + } +#endif + if (event_targeting_policy_ == policy) return; @@ -833,7 +851,7 @@ void Window::SetVisible(bool visible) { if (visible == layer()->GetTargetVisibility()) return; // No change. - WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion_tracking; + WindowOcclusionTracker::ScopedPause pause_occlusion_tracking(env_); for (WindowObserver& observer : observers_) observer.OnWindowVisibilityChanging(this, visible); @@ -886,7 +904,7 @@ void Window::RemoveChildImpl(Window* child, Window* new_parent) { if (child->OwnsLayer()) layer()->Remove(child->layer()); child->parent_ = NULL; - Windows::iterator i = std::find(children_.begin(), children_.end(), child); + auto i = std::find(children_.begin(), children_.end(), child); DCHECK(i != children_.end()); children_.erase(i); child->OnParentChanged(); @@ -908,7 +926,7 @@ void Window::StackChildRelativeTo(Window* child, DCHECK_EQ(this, child->parent()); DCHECK_EQ(this, target->parent()); - WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion_tracking; + WindowOcclusionTracker::ScopedPause pause_occlusion_tracking(env_); client::WindowStackingClient* stacking_client = client::GetWindowStackingClient(); @@ -1074,9 +1092,7 @@ bool Window::CleanupGestureState() { bool state_modified = false; state_modified |= env_->gesture_recognizer()->CancelActiveTouches(this); state_modified |= env_->gesture_recognizer()->CleanupStateForConsumer(this); - for (Window::Windows::iterator iter = children_.begin(); - iter != children_.end(); - ++iter) { + for (auto iter = children_.begin(); iter != children_.end(); ++iter) { state_modified |= (*iter)->CleanupGestureState(); } return state_modified; @@ -1087,6 +1103,7 @@ std::unique_ptr<cc::LayerTreeFrameSink> Window::CreateLayerTreeFrameSink() { DCHECK(frame_sink_id_.is_valid()); DCHECK(embeds_external_client_); DCHECK(GetLocalSurfaceId().is_valid()); + created_layer_tree_frame_sink_ = true; return sink; } @@ -1133,6 +1150,8 @@ const viz::FrameSinkId& Window::GetFrameSinkId() const { } void Window::SetEmbedFrameSinkId(const viz::FrameSinkId& frame_sink_id) { + UnregisterFrameSinkId(); + DCHECK(frame_sink_id.is_valid()); frame_sink_id_ = frame_sink_id; embeds_external_client_ = true; @@ -1143,6 +1162,10 @@ bool Window::IsEmbeddingClient() const { return embeds_external_client_; } +void Window::TrackOcclusionState() { + env_->GetWindowOcclusionTracker()->Track(this); +} + bool Window::RequiresDoubleTapGestureEvents() const { return delegate_ && delegate_->RequiresDoubleTapGestureEvents(); } @@ -1160,7 +1183,7 @@ void Window::OnPaintLayer(const ui::PaintContext& context) { void Window::OnLayerBoundsChanged(const gfx::Rect& old_bounds, ui::PropertyChangeReason reason) { - WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion_tracking; + WindowOcclusionTracker::ScopedPause pause_occlusion_tracking(env_); bounds_ = layer()->bounds(); @@ -1177,13 +1200,13 @@ void Window::OnLayerBoundsChanged(const gfx::Rect& old_bounds, } void Window::OnLayerOpacityChanged(ui::PropertyChangeReason reason) { - WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion_tracking; + WindowOcclusionTracker::ScopedPause pause_occlusion_tracking(env_); for (WindowObserver& observer : observers_) observer.OnWindowOpacitySet(this, reason); } void Window::OnLayerAlphaShapeChanged() { - WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion_tracking; + WindowOcclusionTracker::ScopedPause pause_occlusion_tracking(env_); for (WindowObserver& observer : observers_) observer.OnWindowAlphaShapeSet(this); } @@ -1191,7 +1214,7 @@ void Window::OnLayerAlphaShapeChanged() { void Window::OnLayerTransformed(const gfx::Transform& old_transform, ui::PropertyChangeReason reason) { port_->OnDidChangeTransform(old_transform, layer()->transform()); - WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion_tracking; + WindowOcclusionTracker::ScopedPause pause_occlusion_tracking(env_); for (WindowObserver& observer : observers_) observer.OnWindowTransformed(this, reason); } @@ -1246,8 +1269,18 @@ void Window::ConvertEventToTarget(ui::EventTarget* target, static_cast<Window*>(target)); } +gfx::PointF Window::GetScreenLocationF(const ui::LocatedEvent& event) const { + DCHECK_EQ(this, event.target()); + gfx::PointF screen_location(event.root_location_f()); + const Window* root = GetRootWindow(); + auto* screen_position_client = aura::client::GetScreenPositionClient(root); + if (screen_position_client) + screen_position_client->ConvertPointToScreen(root, &screen_location); + return screen_location; +} + std::unique_ptr<ui::Layer> Window::RecreateLayer() { - WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion_tracking; + WindowOcclusionTracker::ScopedPause pause_occlusion_tracking(env_); ui::LayerAnimator* const animator = layer()->GetAnimator(); const bool was_animating_opacity = @@ -1307,6 +1340,7 @@ void Window::RegisterFrameSinkId() { if (auto* compositor = layer()->GetCompositor()) { compositor->AddChildFrameSink(frame_sink_id_); registered_frame_sink_id_ = true; + port_->RegisterFrameSinkId(frame_sink_id_); } } @@ -1314,6 +1348,7 @@ void Window::UnregisterFrameSinkId() { if (!registered_frame_sink_id_) return; registered_frame_sink_id_ = false; + port_->UnregisterFrameSinkId(frame_sink_id_); if (auto* compositor = layer()->GetCompositor()) compositor->RemoveChildFrameSink(frame_sink_id_); } diff --git a/chromium/ui/aura/window.h b/chromium/ui/aura/window.h index 18a6a3601e6..6175dcd10ba 100644 --- a/chromium/ui/aura/window.h +++ b/chromium/ui/aura/window.h @@ -95,8 +95,8 @@ class AURA_EXPORT Window : public ui::LayerDelegate, STACK_BELOW }; enum class OcclusionState { - // The window's occlusion state isn't tracked - // (WindowOcclusionTracker::Track) or hasn't been computed yet. + // The window's occlusion state isn't tracked (Window::TrackOcclusionState) + // or hasn't been computed yet. UNKNOWN, // The window or one of its descendants IsVisible() [1] and: // - Its bounds aren't completely covered by fully opaque windows [2], or, @@ -194,9 +194,9 @@ class AURA_EXPORT Window : public ui::LayerDelegate, // whether Show() without a Hide() has been invoked. bool TargetVisibility() const { return visible_; } // Returns the occlusion state of this window. Is UNKNOWN if the occlusion - // state of this window isn't tracked (WindowOcclusionTracker::Track) or + // state of this window isn't tracked (Window::TrackOcclusionState) or // hasn't been computed yet. Is stale if called within the scope of a - // WindowOcclusionTracker::ScopedPauseOcclusionTracking. + // WindowOcclusionTracker::ScopedPause. OcclusionState occlusion_state() const { return occlusion_state_; } // Returns the window's bounds in root window's coordinates. @@ -221,6 +221,7 @@ class AURA_EXPORT Window : public ui::LayerDelegate, std::unique_ptr<WindowTargeter> SetEventTargeter( std::unique_ptr<WindowTargeter> targeter); WindowTargeter* targeter() { return targeter_.get(); } + const WindowTargeter* targeter() const { return targeter_.get(); } // Changes the bounds of the window. If present, the window's parent's // LayoutManager may adjust the bounds. @@ -430,6 +431,9 @@ class AURA_EXPORT Window : public ui::LayerDelegate, // Returns whether this window is embedding another client. bool IsEmbeddingClient() const; + // Starts occlusion state tracking. + void TrackOcclusionState(); + Env* env() { return env_; } const Env* env() const { return env_; } @@ -558,6 +562,7 @@ class AURA_EXPORT Window : public ui::LayerDelegate, ui::EventTargeter* GetEventTargeter() override; void ConvertEventToTarget(ui::EventTarget* target, ui::LocatedEvent* event) override; + gfx::PointF GetScreenLocationF(const ui::LocatedEvent& event) const override; // Updates the layer name based on the window's name and id. void UpdateLayerName(); @@ -572,6 +577,8 @@ class AURA_EXPORT Window : public ui::LayerDelegate, bool registered_frame_sink_id_ = false; bool disable_frame_sink_id_registration_ = false; + bool created_layer_tree_frame_sink_ = false; + // Window owns its corresponding WindowPort, but the ref is held as a raw // pointer in |port_| so that it can still be accessed during destruction. // This is important as deleting the WindowPort may result in trying to lookup diff --git a/chromium/ui/aura/window_delegate.h b/chromium/ui/aura/window_delegate.h index fae8ea9c7ee..7911cb92df7 100644 --- a/chromium/ui/aura/window_delegate.h +++ b/chromium/ui/aura/window_delegate.h @@ -24,10 +24,6 @@ namespace ui { class PaintContext; } -namespace viz { -class SurfaceInfo; -} - namespace aura { // Delegate interface for aura::Window. @@ -106,10 +102,6 @@ class AURA_EXPORT WindowDelegate : public ui::EventHandler { // above returns true. virtual void GetHitTestMask(gfx::Path* mask) const = 0; - // Called when a child submits a CompositorFrame to a surface with the given - // |surface_info| for the first time. - virtual void OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info) {} - // Returns whether the window wants to receive and handle double tap gesture // events. Defaults to false. virtual bool RequiresDoubleTapGestureEvents() const; diff --git a/chromium/ui/aura/window_event_dispatcher.cc b/chromium/ui/aura/window_event_dispatcher.cc index b5ce6203d1a..f6ea825d7e2 100644 --- a/chromium/ui/aura/window_event_dispatcher.cc +++ b/chromium/ui/aura/window_event_dispatcher.cc @@ -836,15 +836,19 @@ ui::EventDispatchDetails WindowEventDispatcher::DispatchHeldEvents() { } if (held_move_event_) { + // |held_move_event_| should be cleared here. Some event handler can + // create its own run loop on an event (e.g. WindowMove loop for + // tab-dragging), which means the other move events need to be processed + // before this OnEventFromSource() finishes. See also b/119260190. + std::unique_ptr<ui::LocatedEvent> event = std::move(held_move_event_); + // If a mouse move has been synthesized, the target location is suspect, // so drop the held mouse event. - if (held_move_event_->IsTouchEvent() || - (held_move_event_->IsMouseEvent() && !synthesize_mouse_move_)) { - dispatching_held_event_ = held_move_event_.get(); - dispatch_details = OnEventFromSource(held_move_event_.get()); + if (event->IsTouchEvent() || + (event->IsMouseEvent() && !synthesize_mouse_move_)) { + dispatching_held_event_ = event.get(); + dispatch_details = OnEventFromSource(event.get()); } - if (!dispatch_details.dispatcher_destroyed) - held_move_event_.reset(); } if (!dispatch_details.dispatcher_destroyed) { diff --git a/chromium/ui/aura/window_event_dispatcher.h b/chromium/ui/aura/window_event_dispatcher.h index 5a9e099cf7a..c66b2a1d2a7 100644 --- a/chromium/ui/aura/window_event_dispatcher.h +++ b/chromium/ui/aura/window_event_dispatcher.h @@ -64,6 +64,8 @@ class AURA_EXPORT WindowEventDispatcher : public ui::EventProcessor, WindowEventDispatcher(WindowTreeHost* host, bool are_events_in_pixels); ~WindowEventDispatcher() override; + bool are_events_in_pixels() const { return are_events_in_pixels_; } + // Stops dispatching/synthesizing mouse events. void Shutdown(); diff --git a/chromium/ui/aura/window_event_dispatcher_unittest.cc b/chromium/ui/aura/window_event_dispatcher_unittest.cc index 27deae028d1..fc0967392b9 100644 --- a/chromium/ui/aura/window_event_dispatcher_unittest.cc +++ b/chromium/ui/aura/window_event_dispatcher_unittest.cc @@ -14,6 +14,7 @@ #include "base/run_loop.h" #include "base/single_thread_task_runner.h" #include "base/stl_util.h" +#include "base/test/bind_test_util.h" #include "base/test/metrics/histogram_tester.h" #include "base/threading/thread_task_runner_handle.h" #include "build/build_config.h" @@ -1010,9 +1011,6 @@ class HoldPointerOnScrollHandler : public ui::test::TestEventHandler { // Tests that touch-move events don't contribute to an in-progress scroll // gesture if touch-move events are being held by the dispatcher. TEST_P(WindowEventDispatcherTest, TouchMovesHeldOnScroll) { - // TODO(sky): fails with mus. https://crbug.com/866502 - if (GetParam() == Env::Mode::MUS) - return; EventFilterRecorder recorder; root_window()->AddPreTargetHandler(&recorder); test::TestWindowDelegate delegate; @@ -2179,6 +2177,77 @@ TEST_P(WindowEventDispatcherTest, CaptureWindowDestroyed) { EXPECT_EQ(NULL, capture_window_tracker.capture_window()); } +namespace { + +class RunLoopHandler : public ui::EventHandler { + public: + explicit RunLoopHandler(aura::Window* target) + : run_loop_(base::RunLoop::Type::kNestableTasksAllowed), target_(target) { + target_->AddPreTargetHandler(this); + } + ~RunLoopHandler() override { target_->RemovePreTargetHandler(this); } + int num_scroll_updates() const { return num_scroll_updates_; } + + private: + // ui::EventHandler: + void OnGestureEvent(ui::GestureEvent* event) override { + if (event->type() != ui::ET_GESTURE_SCROLL_UPDATE) + return; + num_scroll_updates_++; + if (running_) { + run_loop_.QuitWhenIdle(); + } else { + running_ = true; + run_loop_.Run(); + } + } + + base::RunLoop run_loop_; + bool running_ = false; + int num_scroll_updates_ = 0; + + aura::Window* target_; + + DISALLOW_COPY_AND_ASSIGN(RunLoopHandler); +}; + +} // namespace + +TEST_P(WindowEventDispatcherTest, HeldTouchMoveWithRunLoop) { + RunLoopHandler handler(root_window()); + + host()->dispatcher()->HoldPointerMoves(); + + gfx::Point point = root_window()->GetBoundsInScreen().CenterPoint(); + ui::TouchEvent ev0(ui::ET_TOUCH_PRESSED, point, ui::EventTimeForNow(), + ui::PointerDetails()); + DispatchEventUsingWindowDispatcher(&ev0); + + point.Offset(10, 10); + ui::TouchEvent ev1(ui::ET_TOUCH_MOVED, point, ui::EventTimeForNow(), + ui::PointerDetails()); + DispatchEventUsingWindowDispatcher(&ev1); + // The move event is held, so SCROLL_UPDATE does not happen yet. + EXPECT_EQ(0, handler.num_scroll_updates()); + + // ReleasePointerMoves() will post DispatchHeldEvent() asynchronously. + host()->dispatcher()->ReleasePointerMoves(); + point.Offset(10, 10); + // Schedule another move event which should cause another SCROLL_UPDATE and + // quit the run_loop within the handler. + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, base::BindLambdaForTesting([&]() { + ui::TouchEvent ev2(ui::ET_TOUCH_MOVED, point, base::TimeTicks::Now(), + ui::PointerDetails()); + DispatchEventUsingWindowDispatcher(&ev2); + })); + // Wait for both DispatchHeldEvent() and dispatch of |ev2|. + base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed).RunUntilIdle(); + + // Makes sure that the run_loop ran and then ended. + EXPECT_EQ(2, handler.num_scroll_updates()); +} + class ExitMessageLoopOnMousePress : public ui::test::TestEventHandler { public: ExitMessageLoopOnMousePress() {} @@ -2282,7 +2351,9 @@ class WindowEventDispatcherTestInHighDPI : public WindowEventDispatcherTest { }; TEST_P(WindowEventDispatcherTestInHighDPI, EventLocationTransform) { - // TODO(sky): fails with mus. https://crbug.com/866502 + // This test is only applicable to LOCAL mode as it's setting a device scale + // factor and expecting events to be transformed while routing the event + // directly through host(). In MUS mode the window-service does the scaling. if (GetParam() == Env::Mode::MUS) return; @@ -2322,9 +2393,12 @@ TEST_P(WindowEventDispatcherTestInHighDPI, EventLocationTransform) { } TEST_P(WindowEventDispatcherTestInHighDPI, TouchMovesHeldOnScroll) { - // TODO(sky): fails with mus. https://crbug.com/866502 + // This test is only applicable to LOCAL mode as it's setting a device scale + // factor and expecting events to be transformed while routing the event + // directly through host(). In MUS mode the window-service does the scaling. if (GetParam() == Env::Mode::MUS) return; + EventFilterRecorder recorder; root_window()->AddPreTargetHandler(&recorder); test::TestWindowDelegate delegate; @@ -2394,9 +2468,12 @@ class TriggerNestedLoopOnRightMousePress : public ui::test::TestEventHandler { // correctly. TEST_P(WindowEventDispatcherTestInHighDPI, EventsTransformedInRepostedEventTriggeredNestedLoop) { - // TODO(sky): fails with mus. https://crbug.com/866502 + // This test is only applicable to LOCAL mode as it's setting a device scale + // factor and expecting events to be transformed while routing the event + // directly through host(). In MUS mode the window-service does the scaling. if (GetParam() == Env::Mode::MUS) return; + std::unique_ptr<Window> window(CreateNormalWindow(1, root_window(), NULL)); // Make sure the window is visible. RunAllPendingInMessageLoop(); @@ -2851,7 +2928,9 @@ TEST_P(WindowEventDispatcherTest, TouchMovesMarkedWhenCausingScroll) { // cursor's position in root coordinates has changed (e.g. when the displays's // scale factor changed). Test that hover effects are properly updated. TEST_P(WindowEventDispatcherTest, OnCursorMovedToRootLocationUpdatesHover) { - // TODO(sky): fails with mus. https://crbug.com/866502 + // This test is only applicable to LOCAL mode as it's setting a device scale + // factor and expecting events to be transformed while routing the event + // directly through host(). In MUS mode the window-service does the scaling. if (GetParam() == Env::Mode::MUS) return; diff --git a/chromium/ui/aura/window_occlusion_tracker.cc b/chromium/ui/aura/window_occlusion_tracker.cc index 89f7a5b412f..16a1d228990 100644 --- a/chromium/ui/aura/window_occlusion_tracker.cc +++ b/chromium/ui/aura/window_occlusion_tracker.cc @@ -9,6 +9,7 @@ #include "base/stl_util.h" #include "third_party/skia/include/core/SkRect.h" #include "third_party/skia/include/core/SkRegion.h" +#include "ui/aura/env.h" #include "ui/aura/window_tracker.h" #include "ui/gfx/geometry/safe_integer_conversions.h" #include "ui/gfx/transform.h" @@ -32,10 +33,6 @@ constexpr ui::LayerAnimationElement::AnimatableProperties // RenderWidgetHostViewAura. https://crbug.com/827268 constexpr int kMaxRecomputeOcclusion = 3; -WindowOcclusionTracker* g_tracker = nullptr; - -int g_num_pause_occlusion_tracking = 0; - bool WindowOrParentHasShape(Window* window) { if (window->layer()->alpha_shape()) return true; @@ -112,46 +109,36 @@ bool OcclusionStatesMatch( } // namespace -WindowOcclusionTracker::ScopedPauseOcclusionTracking:: - ScopedPauseOcclusionTracking() { - ++g_num_pause_occlusion_tracking; +WindowOcclusionTracker::ScopedPause::ScopedPause(Env* env) + : tracker_(env->GetWindowOcclusionTracker()) { + ++tracker_->num_pause_occlusion_tracking_; } -WindowOcclusionTracker::ScopedPauseOcclusionTracking:: - ~ScopedPauseOcclusionTracking() { - --g_num_pause_occlusion_tracking; - DCHECK_GE(g_num_pause_occlusion_tracking, 0); - if (g_tracker) - g_tracker->MaybeComputeOcclusion(); +WindowOcclusionTracker::ScopedPause::~ScopedPause() { + --tracker_->num_pause_occlusion_tracking_; + DCHECK_GE(tracker_->num_pause_occlusion_tracking_, 0); + tracker_->MaybeComputeOcclusion(); } void WindowOcclusionTracker::Track(Window* window) { DCHECK(window); DCHECK(window != window->GetRootWindow()); - if (!g_tracker) - g_tracker = new WindowOcclusionTracker(); - - auto insert_result = g_tracker->tracked_windows_.insert( - {window, Window::OcclusionState::UNKNOWN}); + auto insert_result = + tracked_windows_.insert({window, Window::OcclusionState::UNKNOWN}); DCHECK(insert_result.second); - if (!window->HasObserver(g_tracker)) - window->AddObserver(g_tracker); + if (!window_observer_.IsObserving(window)) + window_observer_.Add(window); if (window->GetRootWindow()) - g_tracker->TrackedWindowAddedToRoot(window); + TrackedWindowAddedToRoot(window); } WindowOcclusionTracker::WindowOcclusionTracker() = default; WindowOcclusionTracker::~WindowOcclusionTracker() = default; -WindowOcclusionTracker* WindowOcclusionTracker::GetInstance() { - DCHECK(g_tracker); - return g_tracker; -} - void WindowOcclusionTracker::MaybeComputeOcclusion() { - if (g_num_pause_occlusion_tracking || + if (num_pause_occlusion_tracking_ || num_times_occlusion_recomputed_in_current_step_ != 0) { return; } @@ -459,9 +446,10 @@ void WindowOcclusionTracker::TrackedWindowRemovedFromRoot(Window* window) { void WindowOcclusionTracker::RemoveObserverFromWindowAndDescendants( Window* window) { if (WindowIsTracked(window)) { - DCHECK(window->HasObserver(this)); + DCHECK(window_observer_.IsObserving(window)); } else { - window->RemoveObserver(this); + if (window_observer_.IsObserving(window)) + window_observer_.Remove(window); window->layer()->GetAnimator()->RemoveObserver(this); animated_windows_.erase(window); } @@ -470,10 +458,12 @@ void WindowOcclusionTracker::RemoveObserverFromWindowAndDescendants( } void WindowOcclusionTracker::AddObserverToWindowAndDescendants(Window* window) { - if (WindowIsTracked(window)) - DCHECK(window->HasObserver(this)); - else - window->AddObserver(this); + if (WindowIsTracked(window)) { + DCHECK(window_observer_.IsObserving(window)); + } else { + DCHECK(!window_observer_.IsObserving(window)); + window_observer_.Add(window); + } for (Window* child_window : window->children()) AddObserverToWindowAndDescendants(child_window); } @@ -498,7 +488,7 @@ void WindowOcclusionTracker::OnWindowHierarchyChanged( Window* const window = params.target; Window* const root_window = window->GetRootWindow(); if (root_window && base::ContainsKey(root_windows_, root_window) && - !window->HasObserver(this)) { + !window_observer_.IsObserving(window)) { AddObserverToWindowAndDescendants(window); } } @@ -582,6 +572,7 @@ void WindowOcclusionTracker::OnWindowStackingChanged(Window* window) { void WindowOcclusionTracker::OnWindowDestroyed(Window* window) { DCHECK(!window->GetRootWindow() || (window == window->GetRootWindow())); tracked_windows_.erase(window); + window_observer_.Remove(window); // Animations should be completed or aborted before a window is destroyed. DCHECK(!window->layer()->GetAnimator()->IsAnimatingOnePropertyOf( kSkipWindowWhenPropertiesAnimated)); diff --git a/chromium/ui/aura/window_occlusion_tracker.h b/chromium/ui/aura/window_occlusion_tracker.h index 2e6d557b31b..2b546a5a8b6 100644 --- a/chromium/ui/aura/window_occlusion_tracker.h +++ b/chromium/ui/aura/window_occlusion_tracker.h @@ -5,9 +5,12 @@ #ifndef UI_AURA_WINDOW_OCCLUSION_TRACKER_H_ #define UI_AURA_WINDOW_OCCLUSION_TRACKER_H_ +#include <memory> + #include "base/containers/flat_map.h" #include "base/containers/flat_set.h" #include "base/macros.h" +#include "base/scoped_observer.h" #include "ui/aura/aura_export.h" #include "ui/aura/window.h" #include "ui/aura/window_observer.h" @@ -26,10 +29,12 @@ namespace test { class WindowOcclusionTrackerTestApi; } +class Env; + // Notifies tracked Windows when their occlusion state change. // // To start tracking the occlusion state of a Window, call -// WindowOcclusionTracker::Track(). +// aura::Window::TrackOcclusionState() // // A Window is occluded if its bounds and transform are not animated and one of // these conditions is true: @@ -43,22 +48,25 @@ class AURA_EXPORT WindowOcclusionTracker : public ui::LayerAnimationObserver, public: // Prevents window occlusion state computations within its scope. If an event // that could cause window occlusion states to change occurs within the scope - // of a ScopedPauseOcclusionTracking, window occlusion state computations are - // delayed until all ScopedPauseOcclusionTracking objects have been destroyed. - class AURA_EXPORT ScopedPauseOcclusionTracking { + // of a ScopedPause, window occlusion state computations are delayed until all + // ScopedPause objects have been destroyed. + class AURA_EXPORT ScopedPause { public: - ScopedPauseOcclusionTracking(); - ~ScopedPauseOcclusionTracking(); + explicit ScopedPause(Env* env); + ~ScopedPause(); private: - DISALLOW_COPY_AND_ASSIGN(ScopedPauseOcclusionTracking); + WindowOcclusionTracker* const tracker_; + DISALLOW_COPY_AND_ASSIGN(ScopedPause); }; // Start tracking the occlusion state of |window|. - static void Track(Window* window); + void Track(Window* window); private: friend class test::WindowOcclusionTrackerTestApi; + friend class Env; + friend std::unique_ptr<WindowOcclusionTracker>::deleter_type; struct RootWindowState { // Number of Windows whose occlusion state is tracked under this root @@ -72,11 +80,8 @@ class AURA_EXPORT WindowOcclusionTracker : public ui::LayerAnimationObserver, WindowOcclusionTracker(); ~WindowOcclusionTracker() override; - static WindowOcclusionTracker* GetInstance(); - // Recomputes the occlusion state of tracked windows under roots marked as - // dirty in |root_windows_| if there are no active - // ScopedPauseOcclusionTracking instance. + // dirty in |root_windows_| if there are no active ScopedPause instance. void MaybeComputeOcclusion(); // Recomputes the occlusion state of |window| and its descendants. @@ -211,6 +216,12 @@ class AURA_EXPORT WindowOcclusionTracker : public ui::LayerAnimationObserver, // recomputed occlusion states. Always 0 when not in MaybeComputeOcclusion(). int num_times_occlusion_recomputed_in_current_step_ = 0; + // Counter of the current occlusion tracking pause. + int num_pause_occlusion_tracking_ = 0; + + // Tracks the observed windows. + ScopedObserver<Window, WindowObserver> window_observer_{this}; + DISALLOW_COPY_AND_ASSIGN(WindowOcclusionTracker); }; diff --git a/chromium/ui/aura/window_occlusion_tracker_unittest.cc b/chromium/ui/aura/window_occlusion_tracker_unittest.cc index 27caa0959a2..ee9247f9d99 100644 --- a/chromium/ui/aura/window_occlusion_tracker_unittest.cc +++ b/chromium/ui/aura/window_occlusion_tracker_unittest.cc @@ -76,7 +76,7 @@ class WindowOcclusionTrackerTest : public test::AuraTestBase { window->Show(); parent = parent ? parent : root_window(); parent->AddChild(window); - WindowOcclusionTracker::Track(window); + window->TrackOcclusionState(); return window; } @@ -958,9 +958,8 @@ TEST_F(WindowOcclusionTrackerTest, ResizeChildFromObserver) { } // Verify that the bounds of windows are changed multiple times within the scope -// of a ScopedPauseOcclusionTracking, occlusion states are updated once at the -// end of the scope. -TEST_F(WindowOcclusionTrackerTest, ScopedPauseOcclusionTracking) { +// of a ScopedPause, occlusion states are updated once at the end of the scope. +TEST_F(WindowOcclusionTrackerTest, ScopedPause) { // Create window a. Expect it to be non-occluded. MockWindowDelegate* delegate_a = new MockWindowDelegate(); delegate_a->set_expectation(Window::OcclusionState::VISIBLE); @@ -977,8 +976,8 @@ TEST_F(WindowOcclusionTrackerTest, ScopedPauseOcclusionTracking) { // Change bounds multiple times. At the end of the scope, expect window a to // be occluded. { - WindowOcclusionTracker::ScopedPauseOcclusionTracking - pause_occlusion_tracking; + WindowOcclusionTracker::ScopedPause pause_occlusion_tracking( + root_window()->env()); window_b->SetBounds(window_a->bounds()); window_a->SetBounds(gfx::Rect(0, 10, 5, 5)); window_b->SetBounds(window_a->bounds()); @@ -988,8 +987,8 @@ TEST_F(WindowOcclusionTrackerTest, ScopedPauseOcclusionTracking) { EXPECT_FALSE(delegate_a->is_expecting_call()); } -// Same as the previous test, but with nested ScopedPauseOcclusionTracking. -TEST_F(WindowOcclusionTrackerTest, NestedScopedPauseOcclusionTracking) { +// Same as the previous test, but with nested ScopedPause. +TEST_F(WindowOcclusionTrackerTest, NestedScopedPause) { // Create window a. Expect it to be non-occluded. MockWindowDelegate* delegate_a = new MockWindowDelegate(); delegate_a->set_expectation(Window::OcclusionState::VISIBLE); @@ -1006,22 +1005,22 @@ TEST_F(WindowOcclusionTrackerTest, NestedScopedPauseOcclusionTracking) { // Change bounds multiple times. At the end of the scope, expect window a to // be occluded. { - WindowOcclusionTracker::ScopedPauseOcclusionTracking - pause_occlusion_tracking_a; + WindowOcclusionTracker::ScopedPause pause_occlusion_tracking_a( + root_window()->env()); { - WindowOcclusionTracker::ScopedPauseOcclusionTracking - pause_occlusion_tracking_b; + WindowOcclusionTracker::ScopedPause pause_occlusion_tracking_b( + root_window()->env()); window_b->SetBounds(window_a->bounds()); } { - WindowOcclusionTracker::ScopedPauseOcclusionTracking - pause_occlusion_tracking_c; + WindowOcclusionTracker::ScopedPause pause_occlusion_tracking_c( + root_window()->env()); window_a->SetBounds(gfx::Rect(0, 10, 5, 5)); } { - WindowOcclusionTracker::ScopedPauseOcclusionTracking - pause_occlusion_tracking_d; + WindowOcclusionTracker::ScopedPause pause_occlusion_tracking_d( + root_window()->env()); window_b->SetBounds(window_a->bounds()); } @@ -1451,7 +1450,7 @@ class WindowDelegateChangingWindowVisibility : public MockWindowDelegate { // Verify that if a window changes its visibility every time it is notified that // its occlusion state changed, a DCHECK occurs. TEST_F(WindowOcclusionTrackerTest, OcclusionStatesDontBecomeStable) { - test::WindowOcclusionTrackerTestApi test_api; + test::WindowOcclusionTrackerTestApi test_api(root_window()->env()); // Create 2 superposed tracked windows. MockWindowDelegate* delegate_a = new MockWindowDelegate(); @@ -1651,7 +1650,7 @@ class WindowDelegateAddingAndHidingChild : public MockWindowDelegate { // to be recomputed. TEST_F(WindowOcclusionTrackerTest, HideWindowWithHiddenParentOnOcclusionChange) { - test::WindowOcclusionTrackerTestApi test_api; + test::WindowOcclusionTrackerTestApi test_api(root_window()->env()); auto* delegate_a = new WindowDelegateAddingAndHidingChild(this); delegate_a->set_expectation(Window::OcclusionState::VISIBLE); diff --git a/chromium/ui/aura/window_port.cc b/chromium/ui/aura/window_port.cc index a710f70587b..2c3b71651ba 100644 --- a/chromium/ui/aura/window_port.cc +++ b/chromium/ui/aura/window_port.cc @@ -8,6 +8,8 @@ namespace aura { +WindowPort::WindowPort(Type type) : type_(type) {} + // static WindowPort* WindowPort::Get(Window* window) { return window ? window->port_ : nullptr; diff --git a/chromium/ui/aura/window_port.h b/chromium/ui/aura/window_port.h index 99f07292d6f..8e058542b48 100644 --- a/chromium/ui/aura/window_port.h +++ b/chromium/ui/aura/window_port.h @@ -40,8 +40,22 @@ class WindowObserver; // Env::CreateWindowPort() is used to create the WindowPort. class AURA_EXPORT WindowPort { public: + // Corresponds to the concrete implementation of this interface. + enum class Type { + // WindowPortLocal. + kLocal, + + // WindowPortMus. + kMus, + + // WindowPortForShutdown. + kShutdown, + }; + virtual ~WindowPort() {} + Type type() const { return type_; } + // Called from Window::Init(). virtual void OnPreInit(Window* window) = 0; @@ -116,13 +130,23 @@ class AURA_EXPORT WindowPort { // See description of function with same name in transient_window_client. virtual bool ShouldRestackTransientChildren() = 0; + // Called to register/unregister an embedded FramesSinkId. This is only called + // if SetEmbedFrameSinkId() is called on the associated Window. + virtual void RegisterFrameSinkId(const viz::FrameSinkId& frame_sink_id) {} + virtual void UnregisterFrameSinkId(const viz::FrameSinkId& frame_sink_id) {} + protected: + explicit WindowPort(Type type); + // Returns the WindowPort associated with a Window. static WindowPort* Get(Window* window); // Returns the ObserverList of a Window. static base::ObserverList<WindowObserver, true>::Unchecked* GetObservers( Window* window); + + private: + const Type type_; }; } // namespace aura diff --git a/chromium/ui/aura/window_port_for_shutdown.cc b/chromium/ui/aura/window_port_for_shutdown.cc index 998764cceed..a30583c51cd 100644 --- a/chromium/ui/aura/window_port_for_shutdown.cc +++ b/chromium/ui/aura/window_port_for_shutdown.cc @@ -9,7 +9,8 @@ namespace aura { -WindowPortForShutdown::WindowPortForShutdown() {} +WindowPortForShutdown::WindowPortForShutdown() + : WindowPort(WindowPort::Type::kShutdown) {} WindowPortForShutdown::~WindowPortForShutdown() {} diff --git a/chromium/ui/aura/window_targeter.cc b/chromium/ui/aura/window_targeter.cc index 9555a1d7373..19a77c27a51 100644 --- a/chromium/ui/aura/window_targeter.cc +++ b/chromium/ui/aura/window_targeter.cc @@ -10,6 +10,8 @@ #include "ui/aura/client/focus_client.h" #include "ui/aura/client/screen_position_client.h" #include "ui/aura/env.h" +#include "ui/aura/mus/window_port_mus.h" +#include "ui/aura/mus/window_tree_client.h" #include "ui/aura/window.h" #include "ui/aura/window_delegate.h" #include "ui/aura/window_event_dispatcher.h" @@ -20,6 +22,14 @@ #include "ui/events/event_target_iterator.h" namespace aura { +namespace { + +bool AreInsetsEmptyOrPositive(const gfx::Insets& insets) { + return insets.left() >= 0 && insets.right() >= 0 && insets.top() >= 0 && + insets.bottom() >= 0; +} + +} // namespace WindowTargeter::WindowTargeter() {} WindowTargeter::~WindowTargeter() {} @@ -60,11 +70,9 @@ void WindowTargeter::SetInsets(const gfx::Insets& mouse_extend, if (mouse_extend_ == mouse_extend && touch_extend_ == touch_extend) return; - const gfx::Insets last_mouse_extend_ = mouse_extend_; - const gfx::Insets last_touch_extend_ = touch_extend_; mouse_extend_ = mouse_extend; touch_extend_ = touch_extend; - OnSetInsets(last_mouse_extend_, last_touch_extend_); + UpdateMusIfNecessary(); } Window* WindowTargeter::GetPriorityTargetInRootWindow( @@ -125,10 +133,12 @@ Window* WindowTargeter::FindTargetInRootWindow(Window* root_window, if (consumer) return static_cast<Window*>(consumer); +#if defined(OS_CHROMEOS) // If the initial touch is outside the window's display, target the root. // This is used for bezel gesture events (eg. swiping in from screen edge). display::Display display = display::Screen::GetScreen()->GetDisplayNearestWindow(root_window); + // The window target may be null, so use the root's ScreenPositionClient. gfx::Point screen_location = event.root_location(); if (client::GetScreenPositionClient(root_window)) { client::GetScreenPositionClient(root_window) @@ -136,6 +146,12 @@ Window* WindowTargeter::FindTargetInRootWindow(Window* root_window, } if (!display.bounds().Contains(screen_location)) return root_window; +#else + // If the initial touch is outside the root window, target the root. + // TODO: this code is likely not necessarily and will be removed. + if (!root_window->bounds().Contains(event.location())) + return root_window; +#endif } return nullptr; @@ -189,6 +205,11 @@ ui::EventTarget* WindowTargeter::FindNextBestTarget( return nullptr; } +void WindowTargeter::OnInstalled(Window* window) { + window_ = window; + UpdateMusIfNecessary(); +} + Window* WindowTargeter::FindTargetForLocatedEvent(Window* window, ui::LocatedEvent* event) { if (!window->parent()) { @@ -278,12 +299,34 @@ bool WindowTargeter::EventLocationInsideBounds( return false; } -bool WindowTargeter::ShouldUseExtendedBounds(const aura::Window* window) const { - return true; +bool WindowTargeter::ShouldUseExtendedBounds(const aura::Window* w) const { + // window() is null when this is used as the default targeter (by + // WindowEventDispatcher). Insets should never be set in this case, so the + // return should not matter. + if (!window()) { + DCHECK(mouse_extend_.IsEmpty()); + DCHECK(touch_extend_.IsEmpty()); + return false; + } + + // Insets should only apply to the window. Subclasses may enforce other + // policies. + return window() == w; } -void WindowTargeter::OnSetInsets(const gfx::Insets& last_mouse_extend, - const gfx::Insets& last_touch_extend) {} +// TODO: this function should go away once https://crbug.com/879308 is fixed. +void WindowTargeter::UpdateMusIfNecessary() { + if (!window_ || window_->env()->mode() != Env::Mode::MUS) + return; + + // Negative insets are used solely to extend the hit-test region of child + // windows, which is not needed by code using MUS (negative insets are only + // used in the server). + if (AreInsetsEmptyOrPositive(mouse_extend_) && + AreInsetsEmptyOrPositive(touch_extend_)) { + WindowPortMus::Get(window_)->SetHitTestInsets(mouse_extend_, touch_extend_); + } +} Window* WindowTargeter::FindTargetForKeyEvent(Window* window, const ui::KeyEvent& key) { diff --git a/chromium/ui/aura/window_targeter.h b/chromium/ui/aura/window_targeter.h index 5f38c4828f8..3d251b8a37e 100644 --- a/chromium/ui/aura/window_targeter.h +++ b/chromium/ui/aura/window_targeter.h @@ -88,6 +88,12 @@ class AURA_EXPORT WindowTargeter : public ui::EventTargeter { ui::Event* event) override; protected: + aura::Window* window() { return window_; } + const aura::Window* window() const { return window_; } + + // This is called by Window when the targeter is set on a window. + virtual void OnInstalled(Window* window); + // Same as FindTargetForEvent(), but used for positional events. The location // etc. of |event| are in |window|'s coordinate system. When finding the // target for the event, the targeter can mutate the |event| (e.g. change the @@ -114,22 +120,26 @@ class AURA_EXPORT WindowTargeter : public ui::EventTargeter { // Returns true if the hit testing (GetHitTestRects()) should use the // extended bounds. - virtual bool ShouldUseExtendedBounds(const aura::Window* window) const; - - // Called after the hit-test area has been extended with SetInsets(). The - // supplied insets are the values before the call to SetInsets(). - virtual void OnSetInsets(const gfx::Insets& last_mouse_extend, - const gfx::Insets& last_touch_extend); + virtual bool ShouldUseExtendedBounds(const aura::Window* w) const; const gfx::Insets& mouse_extend() const { return mouse_extend_; } const gfx::Insets& touch_extend() const { return touch_extend_; } private: + // To call OnInstalled(). + friend class Window; + + void UpdateMusIfNecessary(); + Window* FindTargetForKeyEvent(Window* root_window, const ui::KeyEvent& event); Window* FindTargetForNonKeyEvent(Window* root_window, ui::Event* event); Window* FindTargetForLocatedEventRecursively(Window* root_window, ui::LocatedEvent* event); + // The Window this WindowTargeter is installed on. Null if not attached to a + // Window. + aura::Window* window_ = nullptr; + gfx::Insets mouse_extend_; gfx::Insets touch_extend_; diff --git a/chromium/ui/aura/window_targeter_unittest.cc b/chromium/ui/aura/window_targeter_unittest.cc index daeb8529d05..1085c08c32c 100644 --- a/chromium/ui/aura/window_targeter_unittest.cc +++ b/chromium/ui/aura/window_targeter_unittest.cc @@ -8,7 +8,9 @@ #include "base/macros.h" #include "ui/aura/scoped_window_targeter.h" +#include "ui/aura/test/aura_mus_test_base.h" #include "ui/aura/test/aura_test_base.h" +#include "ui/aura/test/mus/test_window_tree.h" #include "ui/aura/test/test_window_delegate.h" #include "ui/aura/window.h" #include "ui/display/display.h" @@ -332,4 +334,33 @@ INSTANTIATE_TEST_CASE_P(/* no prefix */, WindowTargeterTest, ::testing::Values(Env::Mode::LOCAL, Env::Mode::MUS)); +using WindowTargeterMus = aura::test::AuraMusClientTestBase; + +TEST_F(WindowTargeterMus, SetInsets) { + aura::Window window(nullptr); + window.Init(ui::LAYER_NOT_DRAWN); + std::unique_ptr<WindowTargeter> window_targeter_ptr = + std::make_unique<WindowTargeter>(); + WindowTargeter* window_targeter = window_targeter_ptr.get(); + window.SetEventTargeter(std::move(window_targeter_ptr)); + const gfx::Insets insets1(1, 2, 3, 4); + const gfx::Insets insets2(11, 12, 13, 14); + window_targeter->SetInsets(insets1, insets2); + EXPECT_EQ(insets1, window_tree()->last_mouse_hit_test_insets()); + EXPECT_EQ(insets2, window_tree()->last_touch_hit_test_insets()); +} + +TEST_F(WindowTargeterMus, SetInsetsBeforeInstall) { + aura::Window window(nullptr); + window.Init(ui::LAYER_NOT_DRAWN); + std::unique_ptr<WindowTargeter> window_targeter = + std::make_unique<WindowTargeter>(); + const gfx::Insets insets1(1, 2, 3, 4); + const gfx::Insets insets2(11, 12, 13, 14); + window_targeter->SetInsets(insets1, insets2); + window.SetEventTargeter(std::move(window_targeter)); + EXPECT_EQ(insets1, window_tree()->last_mouse_hit_test_insets()); + EXPECT_EQ(insets2, window_tree()->last_touch_hit_test_insets()); +} + } // namespace aura diff --git a/chromium/ui/aura/window_tree_host.cc b/chromium/ui/aura/window_tree_host.cc index 4c3ea6cb9d1..eb205785534 100644 --- a/chromium/ui/aura/window_tree_host.cc +++ b/chromium/ui/aura/window_tree_host.cc @@ -243,7 +243,8 @@ void WindowTreeHost::SetSharedInputMethod(ui::InputMethod* input_method) { } ui::EventDispatchDetails WindowTreeHost::DispatchKeyEventPostIME( - ui::KeyEvent* event) { + ui::KeyEvent* event, + base::OnceCallback<void(bool)> ack_callback) { // If dispatch to IME is already disabled we shouldn't reach here. DCHECK(!dispatcher_->should_skip_ime()); dispatcher_->set_skip_ime(true); @@ -252,6 +253,7 @@ ui::EventDispatchDetails WindowTreeHost::DispatchKeyEventPostIME( event_sink()->OnEventFromSource(event); if (!dispatch_details.dispatcher_destroyed) dispatcher_->set_skip_ime(false); + CallDispatchKeyEventPostIMEAck(event, std::move(ack_callback)); return dispatch_details; } @@ -300,6 +302,11 @@ WindowTreeHost::WindowTreeHost(std::unique_ptr<Window> window) display::Screen::GetScreen()->AddObserver(this); } +void WindowTreeHost::IntializeDeviceScaleFactor(float device_scale_factor) { + DCHECK(!compositor_->root_layer()) << "Only call this before InitHost()"; + device_scale_factor_ = device_scale_factor; +} + void WindowTreeHost::DestroyCompositor() { if (compositor_) { compositor_->RemoveObserver(this); @@ -442,10 +449,6 @@ ui::EventSink* WindowTreeHost::GetEventSink() { return dispatcher_.get(); } -void WindowTreeHost::OnDisplayAdded(const display::Display& new_display) {} - -void WindowTreeHost::OnDisplayRemoved(const display::Display& old_display) {} - void WindowTreeHost::OnDisplayMetricsChanged(const display::Display& display, uint32_t metrics) { if (metrics & DisplayObserver::DISPLAY_METRIC_COLOR_SPACE) { diff --git a/chromium/ui/aura/window_tree_host.h b/chromium/ui/aura/window_tree_host.h index d600ca4869e..8eec7d205ea 100644 --- a/chromium/ui/aura/window_tree_host.h +++ b/chromium/ui/aura/window_tree_host.h @@ -45,12 +45,13 @@ struct PlatformWindowInitProperties; } namespace aura { -class ScopedKeyboardHook; namespace test { class WindowTreeHostTestApi; } +class Env; +class ScopedKeyboardHook; class WindowEventDispatcher; class WindowTreeHostObserver; @@ -64,9 +65,11 @@ class AURA_EXPORT WindowTreeHost : public ui::internal::InputMethodDelegate, public: ~WindowTreeHost() override; - // Creates a new WindowTreeHost with the specified |properties|. + // Creates a new WindowTreeHost with the specified |properties| and an + // optional |env|. If |env| is null, the default Env::GetInstance() is used. static std::unique_ptr<WindowTreeHost> Create( - ui::PlatformWindowInitProperties properties); + ui::PlatformWindowInitProperties properties, + Env* env = nullptr); // Returns the WindowTreeHost for the specified accelerated widget, or NULL // if there is none associated. @@ -168,7 +171,9 @@ class AURA_EXPORT WindowTreeHost : public ui::internal::InputMethodDelegate, void SetSharedInputMethod(ui::InputMethod* input_method); // Overridden from ui::internal::InputMethodDelegate: - ui::EventDispatchDetails DispatchKeyEventPostIME(ui::KeyEvent* event) final; + ui::EventDispatchDetails DispatchKeyEventPostIME( + ui::KeyEvent* event, + base::OnceCallback<void(bool)> ack_callback) final; // Returns the id of the display. Default implementation queries Screen. virtual int64_t GetDisplayId(); @@ -224,6 +229,10 @@ class AURA_EXPORT WindowTreeHost : public ui::internal::InputMethodDelegate, explicit WindowTreeHost(std::unique_ptr<Window> window = nullptr); + // Set the cached display device scale factor. This should only be called + // during subclass initialization, when the value is needed before InitHost(). + void IntializeDeviceScaleFactor(float device_scale_factor); + void DestroyCompositor(); void DestroyDispatcher(); @@ -273,8 +282,6 @@ class AURA_EXPORT WindowTreeHost : public ui::internal::InputMethodDelegate, ui::EventSink* GetEventSink() override; // display::DisplayObserver implementation. - void OnDisplayAdded(const display::Display& new_display) override; - void OnDisplayRemoved(const display::Display& old_display) override; void OnDisplayMetricsChanged(const display::Display& display, uint32_t metrics) override; diff --git a/chromium/ui/aura/window_tree_host_platform.cc b/chromium/ui/aura/window_tree_host_platform.cc index 76865a51750..c16bbb1477a 100644 --- a/chromium/ui/aura/window_tree_host_platform.cc +++ b/chromium/ui/aura/window_tree_host_platform.cc @@ -10,6 +10,7 @@ #include "base/run_loop.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" +#include "ui/aura/client/cursor_client.h" #include "ui/aura/env.h" #include "ui/aura/window.h" #include "ui/aura/window_event_dispatcher.h" @@ -42,11 +43,12 @@ namespace aura { // static std::unique_ptr<WindowTreeHost> WindowTreeHost::Create( - ui::PlatformWindowInitProperties properties) { + ui::PlatformWindowInitProperties properties, + Env* env) { return std::make_unique<WindowTreeHostPlatform>( std::move(properties), std::make_unique<aura::Window>(nullptr, client::WINDOW_TYPE_UNKNOWN, - Env::GetInstance())); + env ? env : Env::GetInstance())); } WindowTreeHostPlatform::WindowTreeHostPlatform( @@ -215,8 +217,23 @@ void WindowTreeHostPlatform::OnDamageRect(const gfx::Rect& damage_rect) { void WindowTreeHostPlatform::DispatchEvent(ui::Event* event) { TRACE_EVENT0("input", "WindowTreeHostPlatform::DispatchEvent"); ui::EventDispatchDetails details = SendEventToSink(event); - if (details.dispatcher_destroyed) + if (details.dispatcher_destroyed) { event->SetHandled(); + return; + } + + // Reset the cursor on ET_MOUSE_EXITED, so that when the mouse re-enters the + // window, the cursor is updated correctly. + if (event->type() == ui::ET_MOUSE_EXITED) { + client::CursorClient* cursor_client = client::GetCursorClient(window()); + if (cursor_client) { + // The cursor-change needs to happen through the CursorClient so that + // other external states are updated correctly, instead of just changing + // |current_cursor_| here. + cursor_client->SetCursor(ui::CursorType::kNone); + DCHECK_EQ(ui::CursorType::kNone, current_cursor_.native_type()); + } + } } void WindowTreeHostPlatform::OnCloseRequest() { diff --git a/chromium/ui/aura/window_tree_host_platform.h b/chromium/ui/aura/window_tree_host_platform.h index 5ea1727398f..91fde9bcf2a 100644 --- a/chromium/ui/aura/window_tree_host_platform.h +++ b/chromium/ui/aura/window_tree_host_platform.h @@ -84,6 +84,9 @@ class AURA_EXPORT WindowTreeHostPlatform : public WindowTreeHost, bool IsKeyLocked(ui::DomCode dom_code) override; base::flat_map<std::string, std::string> GetKeyboardLayoutMap() override; + // This function is only for test purpose. + gfx::NativeCursor* GetCursorNative() { return ¤t_cursor_; } + private: gfx::AcceleratedWidget widget_; std::unique_ptr<ui::PlatformWindow> platform_window_; diff --git a/chromium/ui/aura/window_tree_host_unittest.cc b/chromium/ui/aura/window_tree_host_unittest.cc index 8207d3ba42d..6d40482c6d6 100644 --- a/chromium/ui/aura/window_tree_host_unittest.cc +++ b/chromium/ui/aura/window_tree_host_unittest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "ui/aura/test/aura_test_base.h" +#include "ui/aura/test/test_cursor_client.h" #include "ui/aura/test/test_screen.h" #include "ui/aura/test/window_event_dispatcher_test_api.h" #include "ui/aura/window.h" @@ -10,6 +11,7 @@ #include "ui/base/ime/input_method.h" #include "ui/compositor/layer.h" #include "ui/compositor/test/draw_waiter_for_test.h" +#include "ui/events/base_event_utils.h" #include "ui/events/event_rewriter.h" #include "ui/events/keycodes/dom/dom_code.h" #include "ui/platform_window/stub/stub_window.h" @@ -141,12 +143,51 @@ class TestWindowTreeHost : public WindowTreeHostPlatform { CreateCompositor(); } + ui::CursorType GetCursorType() { return GetCursorNative()->native_type(); } + void DispatchEventForTest(ui::Event* event) { DispatchEvent(event); } + private: DISALLOW_COPY_AND_ASSIGN(TestWindowTreeHost); }; +class TestCursorClient : public test::TestCursorClient { + public: + explicit TestCursorClient(aura::Window* root_window) + : test::TestCursorClient(root_window) { + window_ = root_window; + } + ~TestCursorClient() override {} + + // Overridden from test::TestCursorClient: + void SetCursor(gfx::NativeCursor cursor) override { + WindowTreeHost* host = window_->GetHost(); + if (host) + host->SetCursor(cursor); + } + + private: + aura::Window* window_; + DISALLOW_COPY_AND_ASSIGN(TestCursorClient); +}; + TEST_F(WindowTreeHostTest, LostCaptureDuringTearDown) { TestWindowTreeHost host; } +// Tests if the cursor type is reset after ET_MOUSE_EXITED event. +TEST_F(WindowTreeHostTest, ResetCursorOnExit) { + TestWindowTreeHost host; + aura::TestCursorClient cursor_client(host.window()); + + // Set the cursor with the specific type to check if it's reset after + // ET_MOUSE_EXITED event. + host.SetCursorNative(ui::CursorType::kCross); + + ui::MouseEvent exit_event(ui::ET_MOUSE_EXITED, gfx::Point(), gfx::Point(), + ui::EventTimeForNow(), 0, 0); + + host.DispatchEventForTest(&exit_event); + EXPECT_EQ(host.GetCursorType(), ui::CursorType::kNone); +} + } // namespace aura diff --git a/chromium/ui/aura/window_unittest.cc b/chromium/ui/aura/window_unittest.cc index a61fc13a4de..7edb1e5aced 100644 --- a/chromium/ui/aura/window_unittest.cc +++ b/chromium/ui/aura/window_unittest.cc @@ -1638,9 +1638,13 @@ TEST_P(WindowTest, Transform) { } TEST_P(WindowTest, TransformGesture) { - // TODO(sky): fails with mus. https://crbug.com/866502 + // This test is only applicable to LOCAL mode as it's setting a transform on + // host() and expecting events to be transformed while routing the event + // directly through host(). In MUS mode the window-service does the + // transformation. if (GetParam() == Env::Mode::MUS) return; + gfx::Size size = host()->GetBoundsInPixels().size(); std::unique_ptr<GestureTrackPositionDelegate> delegate( diff --git a/chromium/ui/base/BUILD.gn b/chromium/ui/base/BUILD.gn index 07d7594628f..1e2170ec644 100644 --- a/chromium/ui/base/BUILD.gn +++ b/chromium/ui/base/BUILD.gn @@ -4,6 +4,7 @@ import("//build/buildflag_header.gni") import("//build/config/compiler/compiler.gni") +import("//build/config/jumbo.gni") import("//build/config/linux/pangocairo/pangocairo.gni") import("//build/config/sanitizers/sanitizers.gni") import("//build/config/ui.gni") @@ -27,7 +28,7 @@ build_ime = !is_ios # # Introduce a standalone target that can build on both 'host' and 'target' # toolchains that just builds the support to load datapacks. The dependencies -# should be kept minimal to have to build too many targets with multiple +# should be kept minimal to not have to build too many targets with multiple # toolchains. component("ui_data_pack") { sources = [ @@ -45,8 +46,11 @@ component("ui_data_pack") { defines = [ "UI_DATA_PACK_IMPLEMENTATION" ] - # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. - configs += [ "//build/config/compiler:no_size_t_to_int_warning" ] + configs += [ + # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. + "//build/config/compiler:no_size_t_to_int_warning", + "//build/config/compiler:wexit_time_destructors", + ] } buildflag_header("ui_features") { @@ -57,12 +61,11 @@ buildflag_header("ui_features") { "ENABLE_MUS=$enable_mus", "USE_ATK=$use_atk", "USE_XKBCOMMON=$use_xkbcommon", - "MAC_VIEWS_BROWSER=$mac_views_browser", "HAS_NATIVE_ACCESSIBILITY=$has_native_accessibility", ] } -component("base") { +jumbo_component("base") { output_name = "ui_base" sources = [ @@ -88,9 +91,6 @@ component("base") { "clipboard/clipboard_win.cc", "clipboard/clipboard_win.h", "clipboard/custom_data_helper_mac.mm", - "cocoa/a11y_util.h", - "cocoa/a11y_util.mm", - "cocoa/accessibility_hostable.h", "cocoa/animation_utils.h", "cocoa/appkit_utils.h", "cocoa/appkit_utils.mm", @@ -102,20 +102,8 @@ component("base") { "cocoa/command_dispatcher.mm", "cocoa/constrained_window/constrained_window_animation.h", "cocoa/constrained_window/constrained_window_animation.mm", - "cocoa/controls/blue_label_button.h", - "cocoa/controls/blue_label_button.mm", "cocoa/controls/button_utils.h", "cocoa/controls/button_utils.mm", - "cocoa/controls/hover_image_menu_button.h", - "cocoa/controls/hover_image_menu_button.mm", - "cocoa/controls/hover_image_menu_button_cell.h", - "cocoa/controls/hover_image_menu_button_cell.mm", - "cocoa/controls/hyperlink_button_cell.h", - "cocoa/controls/hyperlink_button_cell.mm", - "cocoa/controls/hyperlink_text_view.h", - "cocoa/controls/hyperlink_text_view.mm", - "cocoa/controls/imageview_utils.h", - "cocoa/controls/imageview_utils.mm", "cocoa/controls/textfield_utils.h", "cocoa/controls/textfield_utils.mm", "cocoa/defaults_utils.h", @@ -136,22 +124,16 @@ component("base") { "cocoa/menu_controller.mm", "cocoa/nib_loading.h", "cocoa/nib_loading.mm", - "cocoa/nsgraphics_context_additions.h", - "cocoa/nsgraphics_context_additions.mm", - "cocoa/nsview_additions.h", - "cocoa/nsview_additions.mm", + "cocoa/ns_view_ids.h", + "cocoa/ns_view_ids.mm", "cocoa/quartz_util.h", "cocoa/quartz_util.mm", "cocoa/remote_layer_api.h", "cocoa/remote_layer_api.mm", - "cocoa/scoped_cg_context_smooth_fonts.h", - "cocoa/scoped_cg_context_smooth_fonts.mm", "cocoa/secure_password_input.h", "cocoa/secure_password_input.mm", "cocoa/text_services_context_menu.cc", "cocoa/text_services_context_menu.h", - "cocoa/three_part_image.h", - "cocoa/three_part_image.mm", "cocoa/tool_tip_base_view.h", "cocoa/tool_tip_base_view.mm", "cocoa/touch_bar_forward_declarations.h", @@ -163,8 +145,7 @@ component("base") { "cocoa/underlay_opengl_hosting_window.h", "cocoa/underlay_opengl_hosting_window.mm", "cocoa/user_interface_item_command_handler.h", - "cocoa/view_description.h", - "cocoa/view_description.mm", + "cocoa/views_hostable.h", "cocoa/weak_ptr_nsobject.h", "cocoa/weak_ptr_nsobject.mm", "cocoa/window_size_constants.h", @@ -178,7 +159,6 @@ component("base") { "cursor/cursor_type.h", "cursor/cursor_win.cc", "default_style.h", - "default_theme_provider_mac.mm", "device_form_factor.h", "device_form_factor_android.cc", "device_form_factor_desktop.cc", @@ -223,6 +203,7 @@ component("base") { "layout.h", "material_design/material_design_controller.cc", "material_design/material_design_controller.h", + "material_design/material_design_controller_observer.h", "models/button_menu_item_model.cc", "models/button_menu_item_model.h", "models/combobox_model.cc", @@ -344,7 +325,6 @@ component("base") { "accelerators/accelerator_history.h", "accelerators/accelerator_manager.cc", "accelerators/accelerator_manager.h", - "accelerators/accelerator_manager_delegate.h", "base_window.cc", "base_window.h", "clipboard/clipboard.cc", @@ -422,8 +402,11 @@ component("base") { ] } - # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. - configs += [ "//build/config/compiler:no_size_t_to_int_warning" ] + configs += [ + # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. + "//build/config/compiler:no_size_t_to_int_warning", + "//build/config/compiler:wexit_time_destructors", + ] defines = [ "UI_BASE_IMPLEMENTATION" ] @@ -682,7 +665,7 @@ component("base") { } } -static_library("test_support") { +jumbo_static_library("test_support") { testonly = true sources = [ "test/material_design_controller_test_api.cc", @@ -820,7 +803,6 @@ test("ui_base_unittests") { "clipboard/clipboard_util_mac_unittest.mm", "l10n/l10n_util_mac_unittest.mm", "l10n/l10n_util_unittest.cc", - "l10n/l10n_util_win_unittest.cc", "l10n/time_format_unittest.cc", "layout_unittest.cc", "material_design/material_design_controller_unittest.cc", @@ -873,17 +855,10 @@ test("ui_base_unittests") { "cocoa/bubble_closer_unittest.mm", "cocoa/cocoa_base_utils_unittest.mm", "cocoa/constrained_window/constrained_window_animation_unittest.mm", - "cocoa/controls/blue_label_button_unittest.mm", - "cocoa/controls/hover_image_menu_button_unittest.mm", - "cocoa/controls/hyperlink_button_cell_unittest.mm", - "cocoa/controls/hyperlink_text_view_unittest.mm", "cocoa/focus_tracker_unittest.mm", "cocoa/hover_button_unittest.mm", "cocoa/hover_image_button_unittest.mm", "cocoa/menu_controller_unittest.mm", - "cocoa/nsgraphics_context_additions_unittest.mm", - "cocoa/nsview_additions_unittest.mm", - "cocoa/three_part_image_unittest.mm", "cocoa/touch_bar_util_unittest.mm", "cocoa/tracking_area_unittest.mm", "cocoa/weak_ptr_nsobject_unittest.mm", @@ -912,7 +887,7 @@ test("ui_base_unittests") { "ime/win/tsf_text_store_unittest.cc", ] if (is_linux && use_aura && !is_chromeos) { - if (use_x11) { + if (use_x11 || use_ozone) { sources += [ "ime/input_method_auralinux_unittest.cc" ] } else { sources += [ "ime/input_method_minimal_unittest.cc" ] diff --git a/chromium/ui/base/accelerators/accelerator.cc b/chromium/ui/base/accelerators/accelerator.cc index d2f31e0add2..d56e87be04c 100644 --- a/chromium/ui/base/accelerators/accelerator.cc +++ b/chromium/ui/base/accelerators/accelerator.cc @@ -239,6 +239,8 @@ base::string16 Accelerator::ApplyLongFormModifiers( shortcut = ApplyModifierToAcceleratorString(shortcut, IDS_APP_COMMAND_KEY); #elif defined(OS_CHROMEOS) shortcut = ApplyModifierToAcceleratorString(shortcut, IDS_APP_SEARCH_KEY); +#elif defined(OS_WIN) + shortcut = ApplyModifierToAcceleratorString(shortcut, IDS_APP_WINDOWS_KEY); #else NOTREACHED(); #endif diff --git a/chromium/ui/base/accelerators/accelerator.h b/chromium/ui/base/accelerators/accelerator.h index 1ea2afeca16..f5304b8e1ae 100644 --- a/chromium/ui/base/accelerators/accelerator.h +++ b/chromium/ui/base/accelerators/accelerator.h @@ -38,6 +38,11 @@ class UI_BASE_EXPORT Accelerator { }; Accelerator(); + // |modifiers| consists of ui::EventFlags bitwise-or-ed together, + // for example: + // Accelerator(ui::VKEY_Z, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN) + // would correspond to the shortcut "ctrl + shift + z". + // // NOTE: this constructor strips out non key related flags. Accelerator(KeyboardCode key_code, int modifiers, diff --git a/chromium/ui/base/accelerators/accelerator_history.h b/chromium/ui/base/accelerators/accelerator_history.h index bc944cc9a6b..331389ae6b8 100644 --- a/chromium/ui/base/accelerators/accelerator_history.h +++ b/chromium/ui/base/accelerators/accelerator_history.h @@ -31,6 +31,10 @@ class UI_BASE_EXPORT AcceleratorHistory { return previous_accelerator_; } + const std::set<KeyboardCode>& currently_pressed_keys() const { + return currently_pressed_keys_; + } + // Stores the given |accelerator| only if it's different than the currently // stored one. void StoreCurrentAccelerator(const Accelerator& accelerator); diff --git a/chromium/ui/base/accelerators/accelerator_manager.cc b/chromium/ui/base/accelerators/accelerator_manager.cc index b604c019ccc..919326f1a9d 100644 --- a/chromium/ui/base/accelerators/accelerator_manager.cc +++ b/chromium/ui/base/accelerators/accelerator_manager.cc @@ -8,15 +8,12 @@ #include "base/logging.h" #include "base/stl_util.h" -#include "ui/base/accelerators/accelerator_manager_delegate.h" namespace ui { -AcceleratorManager::AcceleratorManager(AcceleratorManagerDelegate* delegate) - : delegate_(delegate) {} +AcceleratorManager::AcceleratorManager() = default; -AcceleratorManager::~AcceleratorManager() { -} +AcceleratorManager::~AcceleratorManager() = default; void AcceleratorManager::Register( const std::vector<ui::Accelerator>& accelerators, @@ -24,14 +21,10 @@ void AcceleratorManager::Register( AcceleratorTarget* target) { DCHECK(target); - // Accelerators which haven't already been registered with any target. - std::vector<ui::Accelerator> new_accelerators; - for (const ui::Accelerator& accelerator : accelerators) { AcceleratorTargetList& targets = accelerators_[accelerator].second; DCHECK(!base::ContainsValue(targets, target)) << "Registering the same target multiple times"; - const bool is_first_target_for_accelerator = targets.empty(); // All priority accelerators go to the front of the line. if (priority == kHighPriority) { @@ -50,16 +43,12 @@ void AcceleratorManager::Register( else targets.insert(++targets.begin(), target); } - if (is_first_target_for_accelerator) - new_accelerators.push_back(accelerator); } - if (delegate_ && !new_accelerators.empty()) - delegate_->OnAcceleratorsRegistered(new_accelerators); } void AcceleratorManager::Unregister(const Accelerator& accelerator, AcceleratorTarget* target) { - AcceleratorMap::iterator map_iter = accelerators_.find(accelerator); + auto map_iter = accelerators_.find(accelerator); if (map_iter == accelerators_.end()) { NOTREACHED() << "Unregistering non-existing accelerator"; return; @@ -69,7 +58,7 @@ void AcceleratorManager::Unregister(const Accelerator& accelerator, } void AcceleratorManager::UnregisterAll(AcceleratorTarget* target) { - for (AcceleratorMap::iterator map_iter = accelerators_.begin(); + for (auto map_iter = accelerators_.begin(); map_iter != accelerators_.end();) { AcceleratorTargetList* targets = &map_iter->second.second; if (!base::ContainsValue(*targets, target)) { @@ -83,20 +72,19 @@ void AcceleratorManager::UnregisterAll(AcceleratorTarget* target) { } bool AcceleratorManager::IsRegistered(const Accelerator& accelerator) const { - AcceleratorMap::const_iterator map_iter = accelerators_.find(accelerator); + auto map_iter = accelerators_.find(accelerator); return map_iter != accelerators_.end() && !map_iter->second.second.empty(); } bool AcceleratorManager::Process(const Accelerator& accelerator) { - AcceleratorMap::iterator map_iter = accelerators_.find(accelerator); + auto map_iter = accelerators_.find(accelerator); if (map_iter == accelerators_.end()) return false; // We have to copy the target list here, because an AcceleratorPressed // event handler may modify the list. AcceleratorTargetList targets(map_iter->second.second); - for (AcceleratorTargetList::iterator iter = targets.begin(); - iter != targets.end(); ++iter) { + for (auto iter = targets.begin(); iter != targets.end(); ++iter) { if ((*iter)->CanHandleAccelerators() && (*iter)->AcceleratorPressed(accelerator)) { return true; @@ -108,7 +96,7 @@ bool AcceleratorManager::Process(const Accelerator& accelerator) { bool AcceleratorManager::HasPriorityHandler( const Accelerator& accelerator) const { - AcceleratorMap::const_iterator map_iter = accelerators_.find(accelerator); + auto map_iter = accelerators_.find(accelerator); if (map_iter == accelerators_.end() || map_iter->second.second.empty()) return false; @@ -124,8 +112,7 @@ bool AcceleratorManager::HasPriorityHandler( void AcceleratorManager::UnregisterImpl(AcceleratorMap::iterator map_iter, AcceleratorTarget* target) { AcceleratorTargetList* targets = &map_iter->second.second; - AcceleratorTargetList::iterator target_iter = - std::find(targets->begin(), targets->end(), target); + auto target_iter = std::find(targets->begin(), targets->end(), target); if (target_iter == targets->end()) { NOTREACHED() << "Unregistering accelerator for wrong target"; return; @@ -139,10 +126,7 @@ void AcceleratorManager::UnregisterImpl(AcceleratorMap::iterator map_iter, targets->remove(target); if (!targets->empty()) return; - const ui::Accelerator accelerator = map_iter->first; accelerators_.erase(map_iter); - if (delegate_) - delegate_->OnAcceleratorUnregistered(accelerator); } } // namespace ui diff --git a/chromium/ui/base/accelerators/accelerator_manager.h b/chromium/ui/base/accelerators/accelerator_manager.h index 621454eb9ed..9eeec5ce99e 100644 --- a/chromium/ui/base/accelerators/accelerator_manager.h +++ b/chromium/ui/base/accelerators/accelerator_manager.h @@ -17,8 +17,6 @@ namespace ui { -class AcceleratorManagerDelegate; - // AcceleratorManger handles processing of accelerators. A delegate may be // supplied which is notified as unique accelerators are added and removed. class UI_BASE_EXPORT AcceleratorManager { @@ -28,7 +26,7 @@ class UI_BASE_EXPORT AcceleratorManager { kHighPriority, }; - explicit AcceleratorManager(AcceleratorManagerDelegate* = nullptr); + AcceleratorManager(); ~AcceleratorManager(); // Register keyboard accelerators for the specified target. If multiple @@ -93,7 +91,6 @@ class UI_BASE_EXPORT AcceleratorManager { void UnregisterImpl(AcceleratorMap::iterator map_iter, AcceleratorTarget* target); - AcceleratorManagerDelegate* delegate_; AcceleratorMap accelerators_; DISALLOW_COPY_AND_ASSIGN(AcceleratorManager); diff --git a/chromium/ui/base/accelerators/accelerator_manager_delegate.h b/chromium/ui/base/accelerators/accelerator_manager_delegate.h deleted file mode 100644 index 7056be18941..00000000000 --- a/chromium/ui/base/accelerators/accelerator_manager_delegate.h +++ /dev/null @@ -1,35 +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_ACCELERATORS_ACCELERATOR_MANAGER_DELEGATE_H_ -#define UI_BASE_ACCELERATORS_ACCELERATOR_MANAGER_DELEGATE_H_ - -#include "ui/base/ui_base_export.h" - -namespace ui { - -class Accelerator; - -// TODO: this should no longer be necessary, remove. https://crbug.com/842365 -class UI_BASE_EXPORT AcceleratorManagerDelegate { - public: - // Called when new accelerators are registered. This is only called with - // newly registered accelerators. For example, if Register() is - // called with A and B, then OnAcceleratorsRegistered() is called with A and - // B. If Register() is subsequently called with A and C, then - // OnAcceleratorsRegistered() is only called with C, as A was already - // registered. - virtual void OnAcceleratorsRegistered( - const std::vector<ui::Accelerator>& accelerators) = 0; - - // Called when there no more targets are registered for |accelerator|. - virtual void OnAcceleratorUnregistered(const Accelerator& accelerator) = 0; - - protected: - virtual ~AcceleratorManagerDelegate() {} -}; - -} // namespace ui - -#endif // UI_BASE_ACCELERATORS_ACCELERATOR_MANAGER_DELEGATE_H_ diff --git a/chromium/ui/base/accelerators/accelerator_manager_unittest.cc b/chromium/ui/base/accelerators/accelerator_manager_unittest.cc index 7ef855296d7..4711295de00 100644 --- a/chromium/ui/base/accelerators/accelerator_manager_unittest.cc +++ b/chromium/ui/base/accelerators/accelerator_manager_unittest.cc @@ -4,19 +4,14 @@ #include "ui/base/accelerators/accelerator_manager.h" -#include <map> - -#include "base/compiler_specific.h" #include "base/macros.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/base/accelerators/accelerator_manager_delegate.h" #include "ui/base/accelerators/test_accelerator_target.h" #include "ui/events/event_constants.h" #include "ui/events/keycodes/keyboard_codes.h" namespace ui { namespace test { - namespace { Accelerator GetAccelerator(KeyboardCode code, int mask) { @@ -38,81 +33,24 @@ int BuildAcceleratorModifier(int id) { return result; } -// AcceleratorManagerDelegate implementation that records calls to interface -// using the following format. -// . OnAcceleratorsRegistered() -> A list of "'Register ' + <id>" separated by -// whitespaces. -// . OnAcceleratorUnregistered() -> 'Unregister' + id -// where the id is specified using SetIdForAccelerator(). -class TestAcceleratorManagerDelegate : public AcceleratorManagerDelegate { - public: - TestAcceleratorManagerDelegate() {} - ~TestAcceleratorManagerDelegate() override {} - - void SetIdForAccelerator(const Accelerator& accelerator, - const std::string& id) { - accelerator_to_id_[accelerator] = id; - } - - std::string GetAndClearCommands() { - std::string commands; - std::swap(commands, commands_); - return commands; - } - - // AcceleratorManagerDelegate: - void OnAcceleratorsRegistered( - const std::vector<Accelerator>& accelerators) override { - for (const Accelerator& accelerator : accelerators) { - if (!commands_.empty()) - commands_ += " "; - commands_ += "Register " + accelerator_to_id_[accelerator]; - } - } - void OnAcceleratorUnregistered(const Accelerator& accelerator) override { - if (!commands_.empty()) - commands_ += " "; - commands_ += "Unregister " + accelerator_to_id_[accelerator]; - } - - private: - std::map<Accelerator, std::string> accelerator_to_id_; - std::string commands_; - - DISALLOW_COPY_AND_ASSIGN(TestAcceleratorManagerDelegate); -}; - -} // namespace - class AcceleratorManagerTest : public testing::Test { public: - AcceleratorManagerTest() : manager_(&delegate_) {} - ~AcceleratorManagerTest() override {} + AcceleratorManagerTest() = default; + ~AcceleratorManagerTest() override = default; protected: - TestAcceleratorManagerDelegate delegate_; AcceleratorManager manager_; }; TEST_F(AcceleratorManagerTest, Register) { TestAcceleratorTarget target; const Accelerator accelerator_a(VKEY_A, EF_NONE); - delegate_.SetIdForAccelerator(accelerator_a, "a"); - const Accelerator accelerator_b(VKEY_B, EF_NONE); - delegate_.SetIdForAccelerator(accelerator_b, "b"); - const Accelerator accelerator_c(VKEY_C, EF_NONE); - delegate_.SetIdForAccelerator(accelerator_c, "c"); - const Accelerator accelerator_d(VKEY_D, EF_NONE); - delegate_.SetIdForAccelerator(accelerator_d, "d"); - manager_.Register( {accelerator_a, accelerator_b, accelerator_c, accelerator_d}, AcceleratorManager::kNormalPriority, &target); - EXPECT_EQ("Register a Register b Register c Register d", - delegate_.GetAndClearCommands()); // The registered accelerators are processed. EXPECT_TRUE(manager_.Process(accelerator_a)); @@ -124,16 +62,12 @@ TEST_F(AcceleratorManagerTest, Register) { TEST_F(AcceleratorManagerTest, RegisterMultipleTarget) { const Accelerator accelerator_a(VKEY_A, EF_NONE); - delegate_.SetIdForAccelerator(accelerator_a, "a"); TestAcceleratorTarget target1; manager_.Register({accelerator_a}, AcceleratorManager::kNormalPriority, &target1); - EXPECT_EQ("Register a", delegate_.GetAndClearCommands()); TestAcceleratorTarget target2; manager_.Register({accelerator_a}, AcceleratorManager::kNormalPriority, &target2); - // Registering the same command shouldn't notify the delegate. - EXPECT_TRUE(delegate_.GetAndClearCommands().empty()); // If multiple targets are registered with the same accelerator, the target // registered later processes the accelerator. @@ -144,51 +78,37 @@ TEST_F(AcceleratorManagerTest, RegisterMultipleTarget) { TEST_F(AcceleratorManagerTest, Unregister) { const Accelerator accelerator_a(VKEY_A, EF_NONE); - delegate_.SetIdForAccelerator(accelerator_a, "a"); TestAcceleratorTarget target; const Accelerator accelerator_b(VKEY_B, EF_NONE); - delegate_.SetIdForAccelerator(accelerator_b, "b"); manager_.Register({accelerator_a, accelerator_b}, AcceleratorManager::kNormalPriority, &target); - EXPECT_EQ("Register a Register b", delegate_.GetAndClearCommands()); // Unregistering a different accelerator does not affect the other // accelerator. manager_.Unregister(accelerator_b, &target); - EXPECT_EQ("Unregister b", delegate_.GetAndClearCommands()); EXPECT_TRUE(manager_.Process(accelerator_a)); EXPECT_EQ(1, target.accelerator_count()); // The unregistered accelerator is no longer processed. target.ResetCounts(); manager_.Unregister(accelerator_a, &target); - EXPECT_EQ("Unregister a", delegate_.GetAndClearCommands()); EXPECT_FALSE(manager_.Process(accelerator_a)); EXPECT_EQ(0, target.accelerator_count()); } TEST_F(AcceleratorManagerTest, UnregisterAll) { const Accelerator accelerator_a(VKEY_A, EF_NONE); - delegate_.SetIdForAccelerator(accelerator_a, "a"); TestAcceleratorTarget target1; const Accelerator accelerator_b(VKEY_B, EF_NONE); - delegate_.SetIdForAccelerator(accelerator_b, "b"); manager_.Register({accelerator_a, accelerator_b}, AcceleratorManager::kNormalPriority, &target1); + const Accelerator accelerator_c(VKEY_C, EF_NONE); - delegate_.SetIdForAccelerator(accelerator_c, "c"); TestAcceleratorTarget target2; manager_.Register({accelerator_c}, AcceleratorManager::kNormalPriority, &target2); - EXPECT_EQ("Register a Register b Register c", - delegate_.GetAndClearCommands()); + manager_.UnregisterAll(&target1); - { - const std::string commands = delegate_.GetAndClearCommands(); - // Ordering is not guaranteed. - EXPECT_TRUE(commands == "Unregister a Unregister b" || - commands == "Unregister b Unregister a"); - } // All the accelerators registered for |target1| are no longer processed. EXPECT_FALSE(manager_.Process(accelerator_a)); @@ -242,21 +162,6 @@ TEST_F(AcceleratorManagerTest, Process) { } } -// Verifies delegate is notifed correctly when unregistering and registering -// with the same accelerator. -TEST_F(AcceleratorManagerTest, Reregister) { - const Accelerator accelerator_a(VKEY_A, EF_NONE); - TestAcceleratorTarget target; - delegate_.SetIdForAccelerator(accelerator_a, "a"); - manager_.Register({accelerator_a}, AcceleratorManager::kNormalPriority, - &target); - EXPECT_EQ("Register a", delegate_.GetAndClearCommands()); - manager_.UnregisterAll(&target); - EXPECT_EQ("Unregister a", delegate_.GetAndClearCommands()); - manager_.Register({accelerator_a}, AcceleratorManager::kNormalPriority, - &target); - EXPECT_EQ("Register a", delegate_.GetAndClearCommands()); -} - +} // namespace } // namespace test } // namespace ui diff --git a/chromium/ui/base/class_property.cc b/chromium/ui/base/class_property.cc index 8b13aa1426e..01941edfd31 100644 --- a/chromium/ui/base/class_property.cc +++ b/chromium/ui/base/class_property.cc @@ -54,7 +54,7 @@ void PropertyHandler::ClearProperties() { int64_t PropertyHandler::GetPropertyInternal(const void* key, int64_t default_value) const { - std::map<const void*, Value>::const_iterator iter = prop_map_.find(key); + auto iter = prop_map_.find(key); if (iter == prop_map_.end()) return default_value; return iter->second.value; diff --git a/chromium/ui/base/clipboard/clipboard.cc b/chromium/ui/base/clipboard/clipboard.cc index 6697be9cd4c..38665261399 100644 --- a/chromium/ui/base/clipboard/clipboard.cc +++ b/chromium/ui/base/clipboard/clipboard.cc @@ -80,7 +80,7 @@ void Clipboard::DestroyClipboardForCurrentThread() { ClipboardMap* clipboard_map = clipboard_map_.Pointer(); base::PlatformThreadId id = base::PlatformThread::CurrentId(); - ClipboardMap::iterator it = clipboard_map->find(id); + auto it = clipboard_map->find(id); if (it != clipboard_map->end()) clipboard_map->erase(it); } diff --git a/chromium/ui/base/clipboard/clipboard.h b/chromium/ui/base/clipboard/clipboard.h index 94236adca28..d76a66d657b 100644 --- a/chromium/ui/base/clipboard/clipboard.h +++ b/chromium/ui/base/clipboard/clipboard.h @@ -16,6 +16,7 @@ #include "base/containers/flat_map.h" #include "base/lazy_instance.h" #include "base/macros.h" +#include "base/no_destructor.h" #include "base/process/process.h" #include "base/strings/string16.h" #include "base/synchronization/lock.h" @@ -82,6 +83,7 @@ class UI_BASE_EXPORT Clipboard : public base::ThreadChecker { bool Equals(const FormatType& other) const; private: + friend class base::NoDestructor<FormatType>; friend class Clipboard; // Platform-specific glue used internally by the Clipboard class. Each diff --git a/chromium/ui/base/clipboard/clipboard_android.cc b/chromium/ui/base/clipboard/clipboard_android.cc index 0cb4357b64b..c1fada750f6 100644 --- a/chromium/ui/base/clipboard/clipboard_android.cc +++ b/chromium/ui/base/clipboard/clipboard_android.cc @@ -11,6 +11,7 @@ #include "base/android/scoped_java_ref.h" #include "base/callback.h" #include "base/lazy_instance.h" +#include "base/no_destructor.h" #include "base/stl_util.h" #include "base/strings/utf_string_conversions.h" #include "base/synchronization/lock.h" @@ -269,56 +270,56 @@ Clipboard::FormatType Clipboard::GetFormatType( // static const Clipboard::FormatType& Clipboard::GetUrlWFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kURLFormat)); - return type; + static base::NoDestructor<FormatType> type(kURLFormat); + return *type; } // static const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kPlainTextFormat)); - return type; + static base::NoDestructor<FormatType> type(kPlainTextFormat); + return *type; } // static const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kPlainTextFormat)); - return type; + static base::NoDestructor<FormatType> type(kPlainTextFormat); + return *type; } // static const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kWebKitSmartPasteFormat)); - return type; + static base::NoDestructor<FormatType> type(kWebKitSmartPasteFormat); + return *type; } // static const Clipboard::FormatType& Clipboard::GetHtmlFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kHTMLFormat)); - return type; + static base::NoDestructor<FormatType> type(kHTMLFormat); + return *type; } // static const Clipboard::FormatType& Clipboard::GetRtfFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kRTFFormat)); - return type; + static base::NoDestructor<FormatType> type(kRTFFormat); + return *type; } // static const Clipboard::FormatType& Clipboard::GetBitmapFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kBitmapFormat)); - return type; + static base::NoDestructor<FormatType> type(kBitmapFormat); + return *type; } // static const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData)); - return type; + static base::NoDestructor<FormatType> type(kMimeTypeWebCustomData); + return *type; } // static const Clipboard::FormatType& Clipboard::GetPepperCustomDataFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypePepperCustomData)); - return type; + static base::NoDestructor<FormatType> type(kMimeTypePepperCustomData); + return *type; } // Clipboard factory method. diff --git a/chromium/ui/base/clipboard/clipboard_aura.cc b/chromium/ui/base/clipboard/clipboard_aura.cc index 9e08f7a1f9c..f185182cb36 100644 --- a/chromium/ui/base/clipboard/clipboard_aura.cc +++ b/chromium/ui/base/clipboard/clipboard_aura.cc @@ -15,6 +15,7 @@ #include "base/logging.h" #include "base/macros.h" #include "base/memory/ptr_util.h" +#include "base/no_destructor.h" #include "base/strings/utf_string_conversions.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/clipboard/clipboard_monitor.h" @@ -453,8 +454,8 @@ Clipboard::FormatType Clipboard::GetFormatType( // static const Clipboard::FormatType& Clipboard::GetUrlFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeURIList)); - return type; + static base::NoDestructor<FormatType> type(kMimeTypeURIList); + return *type; } // static @@ -464,14 +465,14 @@ const Clipboard::FormatType& Clipboard::GetUrlWFormatType() { // static const Clipboard::FormatType& Clipboard::GetMozUrlFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeMozillaURL)); - return type; + static base::NoDestructor<FormatType> type(kMimeTypeMozillaURL); + return *type; } // static const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeText)); - return type; + static base::NoDestructor<FormatType> type(kMimeTypeText); + return *type; } // static @@ -481,8 +482,8 @@ const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() { // static const Clipboard::FormatType& Clipboard::GetFilenameFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeFilename)); - return type; + static base::NoDestructor<FormatType> type(kMimeTypeFilename); + return *type; } // static @@ -492,38 +493,38 @@ const Clipboard::FormatType& Clipboard::GetFilenameWFormatType() { // static const Clipboard::FormatType& Clipboard::GetHtmlFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeHTML)); - return type; + static base::NoDestructor<FormatType> type(kMimeTypeHTML); + return *type; } // static const Clipboard::FormatType& Clipboard::GetRtfFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeRTF)); - return type; + static base::NoDestructor<FormatType> type(kMimeTypeRTF); + return *type; } // static const Clipboard::FormatType& Clipboard::GetBitmapFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeBitmap)); - return type; + static base::NoDestructor<FormatType> type(kMimeTypeBitmap); + return *type; } // static const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebkitSmartPaste)); - return type; + static base::NoDestructor<FormatType> type(kMimeTypeWebkitSmartPaste); + return *type; } // static const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData)); - return type; + static base::NoDestructor<FormatType> type(kMimeTypeWebCustomData); + return *type; } // static const Clipboard::FormatType& Clipboard::GetPepperCustomDataFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypePepperCustomData)); - return type; + static base::NoDestructor<FormatType> type(kMimeTypePepperCustomData); + return *type; } // Clipboard factory method. diff --git a/chromium/ui/base/clipboard/clipboard_aurax11.cc b/chromium/ui/base/clipboard/clipboard_aurax11.cc index d071d155a65..4e767fcd9d9 100644 --- a/chromium/ui/base/clipboard/clipboard_aurax11.cc +++ b/chromium/ui/base/clipboard/clipboard_aurax11.cc @@ -17,6 +17,7 @@ #include "base/memory/ref_counted_memory.h" #include "base/memory/singleton.h" #include "base/metrics/histogram_macros.h" +#include "base/no_destructor.h" #include "base/stl_util.h" #include "base/strings/utf_string_conversions.h" #include "third_party/skia/include/core/SkBitmap.h" @@ -378,9 +379,8 @@ SelectionData ClipboardAuraX11::AuraX11Details::RequestAndWaitForTypes( // with the X server. const SelectionFormatMap& format_map = LookupStorageForAtom(selection_name); - for (std::vector< ::Atom>::const_iterator it = types.begin(); - it != types.end(); ++it) { - SelectionFormatMap::const_iterator format_map_it = format_map.find(*it); + for (auto it = types.begin(); it != types.end(); ++it) { + auto format_map_it = format_map.find(*it); if (format_map_it != format_map.end()) return SelectionData(format_map_it->first, format_map_it->second); } @@ -405,8 +405,7 @@ TargetList ClipboardAuraX11::AuraX11Details::WaitAndGetTargetsList( // We can local fastpath and return the list of local targets. const SelectionFormatMap& format_map = LookupStorageForAtom(selection_name); - for (SelectionFormatMap::const_iterator it = format_map.begin(); - it != format_map.end(); ++it) { + for (auto it = format_map.begin(); it != format_map.end(); ++it) { out.push_back(it->first); } } else { @@ -557,8 +556,8 @@ Clipboard::FormatType Clipboard::GetFormatType( // static const Clipboard::FormatType& Clipboard::GetUrlFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeURIList)); - return type; + static base::NoDestructor<FormatType> type(kMimeTypeURIList); + return *type; } // static @@ -568,14 +567,14 @@ const Clipboard::FormatType& Clipboard::GetUrlWFormatType() { // static const Clipboard::FormatType& Clipboard::GetMozUrlFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeMozillaURL)); - return type; + static base::NoDestructor<FormatType> type(kMimeTypeMozillaURL); + return *type; } // static const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeText)); - return type; + static base::NoDestructor<FormatType> type(kMimeTypeText); + return *type; } // static @@ -585,8 +584,8 @@ const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() { // static const Clipboard::FormatType& Clipboard::GetFilenameFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeFilename)); - return type; + static base::NoDestructor<FormatType> type(kMimeTypeFilename); + return *type; } // static @@ -596,38 +595,38 @@ const Clipboard::FormatType& Clipboard::GetFilenameWFormatType() { // static const Clipboard::FormatType& Clipboard::GetHtmlFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeHTML)); - return type; + static base::NoDestructor<FormatType> type(kMimeTypeHTML); + return *type; } // static const Clipboard::FormatType& Clipboard::GetRtfFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeRTF)); - return type; + static base::NoDestructor<FormatType> type(kMimeTypeRTF); + return *type; } // static const Clipboard::FormatType& Clipboard::GetBitmapFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypePNG)); - return type; + static base::NoDestructor<FormatType> type(kMimeTypePNG); + return *type; } // static const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebkitSmartPaste)); - return type; + static base::NoDestructor<FormatType> type(kMimeTypeWebkitSmartPaste); + return *type; } // static const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypeWebCustomData)); - return type; + static base::NoDestructor<FormatType> type(kMimeTypeWebCustomData); + return *type; } // static const Clipboard::FormatType& Clipboard::GetPepperCustomDataFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kMimeTypePepperCustomData)); - return type; + static base::NoDestructor<FormatType> type(kMimeTypePepperCustomData); + return *type; } /////////////////////////////////////////////////////////////////////////////// @@ -813,14 +812,13 @@ void ClipboardAuraX11::WriteObjects(ClipboardType type, DCHECK(IsSupportedClipboardType(type)); aurax11_details_->CreateNewClipboardData(); - for (ObjectMap::const_iterator iter = objects.begin(); iter != objects.end(); - ++iter) { + for (auto iter = objects.begin(); iter != objects.end(); ++iter) { DispatchObject(static_cast<ObjectType>(iter->first), iter->second); } aurax11_details_->TakeOwnershipOfSelection(type); if (type == CLIPBOARD_TYPE_COPY_PASTE) { - ObjectMap::const_iterator text_iter = objects.find(CBF_TEXT); + auto text_iter = objects.find(CBF_TEXT); if (text_iter != objects.end()) { aurax11_details_->CreateNewClipboardData(); const ObjectMapParams& params_vector = text_iter->second; diff --git a/chromium/ui/base/clipboard/clipboard_mac.mm b/chromium/ui/base/clipboard/clipboard_mac.mm index 845c1a3427b..e9abab846ec 100644 --- a/chromium/ui/base/clipboard/clipboard_mac.mm +++ b/chromium/ui/base/clipboard/clipboard_mac.mm @@ -14,6 +14,7 @@ #include "base/mac/mac_util.h" #include "base/mac/scoped_cftyperef.h" #include "base/mac/scoped_nsobject.h" +#include "base/no_destructor.h" #include "base/stl_util.h" #include "base/strings/sys_string_conversions.h" #include "base/strings/utf_string_conversions.h" @@ -99,8 +100,8 @@ Clipboard::FormatType Clipboard::GetFormatType( // static const Clipboard::FormatType& Clipboard::GetUrlFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (NSURLPboardType)); - return type; + static base::NoDestructor<FormatType> type(NSURLPboardType); + return *type; } // static @@ -110,8 +111,8 @@ const Clipboard::FormatType& Clipboard::GetUrlWFormatType() { // static const Clipboard::FormatType& Clipboard::GetPlainTextFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (NSPasteboardTypeString)); - return type; + static base::NoDestructor<FormatType> type(NSPasteboardTypeString); + return *type; } // static @@ -121,8 +122,8 @@ const Clipboard::FormatType& Clipboard::GetPlainTextWFormatType() { // static const Clipboard::FormatType& Clipboard::GetFilenameFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (NSFilenamesPboardType)); - return type; + static base::NoDestructor<FormatType> type(NSFilenamesPboardType); + return *type; } // static @@ -132,38 +133,38 @@ const Clipboard::FormatType& Clipboard::GetFilenameWFormatType() { // static const Clipboard::FormatType& Clipboard::GetHtmlFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (NSHTMLPboardType)); - return type; + static base::NoDestructor<FormatType> type(NSHTMLPboardType); + return *type; } // static const Clipboard::FormatType& Clipboard::GetRtfFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (NSRTFPboardType)); - return type; + static base::NoDestructor<FormatType> type(NSRTFPboardType); + return *type; } // static const Clipboard::FormatType& Clipboard::GetBitmapFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (NSTIFFPboardType)); - return type; + static base::NoDestructor<FormatType> type(NSTIFFPboardType); + return *type; } // static const Clipboard::FormatType& Clipboard::GetWebKitSmartPasteFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kWebSmartPastePboardType)); - return type; + static base::NoDestructor<FormatType> type(kWebSmartPastePboardType); + return *type; } // static const Clipboard::FormatType& Clipboard::GetWebCustomDataFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kWebCustomDataPboardType)); - return type; + static base::NoDestructor<FormatType> type(kWebCustomDataPboardType); + return *type; } // static const Clipboard::FormatType& Clipboard::GetPepperCustomDataFormatType() { - CR_DEFINE_STATIC_LOCAL(FormatType, type, (kPepperCustomDataPboardType)); - return type; + static base::NoDestructor<FormatType> type(kPepperCustomDataPboardType); + return *type; } // Clipboard factory method. diff --git a/chromium/ui/base/clipboard/clipboard_util_mac.h b/chromium/ui/base/clipboard/clipboard_util_mac.h index 1c4ff0ad99d..31c459b1b08 100644 --- a/chromium/ui/base/clipboard/clipboard_util_mac.h +++ b/chromium/ui/base/clipboard/clipboard_util_mac.h @@ -9,10 +9,15 @@ #include "base/mac/scoped_nsobject.h" #include "base/memory/ref_counted.h" +#include "ui/base/clipboard/clipboard_types.h" #include "ui/base/ui_base_export.h" namespace ui { +// A publicly-used UTI for the name of a URL. It really should be in a system +// header but isn't. +UI_BASE_EXPORT extern NSString* const kUTTypeURLName; + class UI_BASE_EXPORT UniquePasteboard : public base::RefCounted<UniquePasteboard> { public: @@ -64,6 +69,9 @@ class UI_BASE_EXPORT ClipboardUtil { static bool URLsAndTitlesFromPasteboard(NSPasteboard* pboard, NSArray** urls, NSArray** titles); + + // Gets the NSPasteboard specified from the clipboard type. + static NSPasteboard* PasteboardFromType(ui::ClipboardType type); }; } diff --git a/chromium/ui/base/clipboard/clipboard_util_mac.mm b/chromium/ui/base/clipboard/clipboard_util_mac.mm index 0db36b8f58a..b7bf6229dda 100644 --- a/chromium/ui/base/clipboard/clipboard_util_mac.mm +++ b/chromium/ui/base/clipboard/clipboard_util_mac.mm @@ -10,10 +10,10 @@ namespace ui { +NSString* const kUTTypeURLName = @"public.url-name"; + namespace { NSString* const kWebURLsWithTitlesPboardType = @"WebURLsWithTitlesPboardType"; -NSString* const kPublicUrl = @"public.url"; -NSString* const kPublicUrlName = @"public.url-name"; // It's much more convenient to return an NSString than a // base::ScopedCFTypeRef<CFStringRef>, since the methods on NSPasteboardItem @@ -75,8 +75,8 @@ base::scoped_nsobject<NSPasteboardItem> ClipboardUtil::PasteboardItemFromUrl( } [item setString:urlString forType:NSPasteboardTypeString]; - [item setString:urlString forType:kPublicUrl]; - [item setString:title forType:kPublicUrlName]; + [item setString:urlString forType:base::mac::CFToNSCast(kUTTypeURL)]; + [item setString:title forType:kUTTypeURLName]; return item; } @@ -104,12 +104,12 @@ base::scoped_nsobject<NSPasteboardItem> ClipboardUtil::PasteboardItemFromString( //static NSString* ClipboardUtil::GetTitleFromPasteboardURL(NSPasteboard* pboard) { - return [pboard stringForType:kPublicUrlName]; + return [pboard stringForType:kUTTypeURLName]; } //static NSString* ClipboardUtil::GetURLFromPasteboardURL(NSPasteboard* pboard) { - return [pboard stringForType:kPublicUrl]; + return [pboard stringForType:base::mac::CFToNSCast(kUTTypeURL)]; } // static @@ -177,4 +177,22 @@ bool ClipboardUtil::URLsAndTitlesFromPasteboard(NSPasteboard* pboard, return true; } +// static +NSPasteboard* ClipboardUtil::PasteboardFromType(ui::ClipboardType type) { + NSString* type_string = nil; + switch (type) { + case ui::CLIPBOARD_TYPE_COPY_PASTE: + type_string = NSGeneralPboard; + break; + case ui::CLIPBOARD_TYPE_DRAG: + type_string = NSDragPboard; + break; + case ui::CLIPBOARD_TYPE_SELECTION: + NOTREACHED(); + break; + } + + return [NSPasteboard pasteboardWithName:type_string]; +} + } // namespace ui diff --git a/chromium/ui/base/cocoa/a11y_util.h b/chromium/ui/base/cocoa/a11y_util.h deleted file mode 100644 index a304b77c647..00000000000 --- a/chromium/ui/base/cocoa/a11y_util.h +++ /dev/null @@ -1,28 +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_COCOA_A11Y_UTIL_H -#define UI_BASE_COCOA_A11Y_UTIL_H - -#import <Cocoa/Cocoa.h> - -#include "ui/base/ui_base_export.h" - -namespace ui { -namespace a11y_util { - -// Hides the given |view| from the accessibility order for Voice Over. This -// should be used when the view provides no additional information with -// voice over (i.e., an icon next to a written description of the icon). -UI_BASE_EXPORT void HideImageFromAccessibilityOrder(NSImageView* view); - -// Ask VoiceOver to play a sound for |object|, generally a view or window -// (undocumented). Built-in apps seem to use this to indicate that something -// interesting has happened, like a failed download or available completions. -UI_BASE_EXPORT void PlayElementUpdatedSound(id source); - -} // namespace a11y_util -} // namespace ui - -#endif // UI_BASE_COCOA_A11Y_UTIL_H diff --git a/chromium/ui/base/cocoa/a11y_util.mm b/chromium/ui/base/cocoa/a11y_util.mm deleted file mode 100644 index e6a27ad26c3..00000000000 --- a/chromium/ui/base/cocoa/a11y_util.mm +++ /dev/null @@ -1,25 +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. - -#import "ui/base/cocoa/a11y_util.h" - -namespace ui { -namespace a11y_util { - -void HideImageFromAccessibilityOrder(NSImageView* view) { - // This is the minimum change necessary to get VoiceOver to skip the image - // (instead of reading the word "image"). Accessibility mechanisms in OSX - // change once in a while, so this may be fragile. - [[view cell] accessibilitySetOverrideValue:@"" - forAttribute:NSAccessibilityRoleAttribute]; -} - -void PlayElementUpdatedSound(id source) { - NSAccessibilityPostNotificationWithUserInfo( - source, @"AXPlaySound", - @{@"AXSoundIdentifier" : @"AXElementUpdatedSound"}); -} - -} // namespace a11y_util -} // namespace ui diff --git a/chromium/ui/base/cocoa/accessibility_hostable.h b/chromium/ui/base/cocoa/accessibility_hostable.h deleted file mode 100644 index 63a4a2424b2..00000000000 --- a/chromium/ui/base/cocoa/accessibility_hostable.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_ACCESSIBILITY_HOSTABLE_H_ -#define UI_BASE_COCOA_ACCESSIBILITY_HOSTABLE_H_ - -#import <objc/objc.h> - -// An object that can be hosted in another accessibility hierarchy. -// This allows for stitching together heterogenous accessibility -// hierarchies, for example the AXPlatformNodeCocoa-based views -// toolkit hierarchy and the BrowserAccessibilityCocoa-based -// web content hierarchy. -@protocol AccessibilityHostable - -// Sets |accessibilityParent| as the object returned when the -// receiver is queried for its accessibility parent. -// TODO(lgrey/ellyjones): Remove this in favor of setAccessibilityParent: -// when we switch to the new accessibility API. -- (void)setAccessibilityParentElement:(id)accessibilityParent; - -@end - -#endif // UI_BASE_COCOA_ACCESSIBILITY_HOSTABLE_H_ diff --git a/chromium/ui/base/cocoa/appkit_utils.h b/chromium/ui/base/cocoa/appkit_utils.h index 8f900e9326b..14f6c55070e 100644 --- a/chromium/ui/base/cocoa/appkit_utils.h +++ b/chromium/ui/base/cocoa/appkit_utils.h @@ -11,37 +11,6 @@ namespace ui { -struct NinePartImageIds { - int top_left; - int top; - int top_right; - int left; - int center; - int right; - int bottom_left; - int bottom; - int bottom_right; -}; - -// A macro to define arrays of IDR constants used with DrawNinePartImage. -#define IMAGE_GRID(x) { x ## _TOP_LEFT, x ## _TOP, x ## _TOP_RIGHT, \ - x ## _LEFT, x ## _CENTER, x ## _RIGHT, \ - x ## _BOTTOM_LEFT, x ## _BOTTOM, x ## _BOTTOM_RIGHT, } - -// Utility method to draw a nine part image using image ids. -UI_BASE_EXPORT void DrawNinePartImage(NSRect frame, - const NinePartImageIds& image_ids, - NSCompositingOperation operation, - CGFloat alpha, - BOOL flipped); - -// Minimizes or zooms the window, or does nothing, depending on OS version and -// system configuration. -// |window| is the receiver of the double click. -// |sender| is the object that sends the action, if one is sent. -UI_BASE_EXPORT void WindowTitlebarReceivedDoubleClick(NSWindow* window, - id sender); - // Whether a force-click event on the touchpad should invoke Quick Look. UI_BASE_EXPORT bool ForceClickInvokesQuickLook(); diff --git a/chromium/ui/base/cocoa/appkit_utils.mm b/chromium/ui/base/cocoa/appkit_utils.mm index 6c3e7e2d9cb..c82a222e096 100644 --- a/chromium/ui/base/cocoa/appkit_utils.mm +++ b/chromium/ui/base/cocoa/appkit_utils.mm @@ -25,86 +25,10 @@ enum class ForceTouchAction { QUICK_LOOK = 1, // Set to "Force Click with one finger". }; -// Gets an NSImage given an image id. -NSImage* GetImage(int image_id) { - return ui::ResourceBundle::GetSharedInstance() - .GetNativeImageNamed(image_id) - .ToNSImage(); -} - -// The action to take when the user double-clicks in the window title bar. -DoubleClickAction WindowTitleBarDoubleClickAction() { - // El Capitan introduced a Dock preference to configure the window title bar - // double-click action (Minimize, Maximize, or nothing). - if (base::mac::IsAtLeastOS10_11()) { - NSString* doubleClickAction = [[NSUserDefaults standardUserDefaults] - objectForKey:@"AppleActionOnDoubleClick"]; - - if ([doubleClickAction isEqualToString:@"Minimize"]) { - return DoubleClickAction::MINIMIZE; - } else if ([doubleClickAction isEqualToString:@"Maximize"]) { - return DoubleClickAction::MAXIMIZE; - } - - return DoubleClickAction::NONE; - } - - // Determine minimize using an undocumented method in Cocoa. If we're - // running on an earlier version of the OS that doesn't implement it, - // just default to the minimize action. - BOOL methodImplemented = - [NSWindow respondsToSelector:@selector(_shouldMiniaturizeOnDoubleClick)]; - if (!methodImplemented || - [NSWindow performSelector:@selector(_shouldMiniaturizeOnDoubleClick)]) { - return DoubleClickAction::MINIMIZE; - } - - // At this point _shouldMiniaturizeOnDoubleClick has returned |NO|. On - // Yosemite, that means a double-click should Maximize the window, and on - // all prior OSes a double-click should do nothing. - return base::mac::IsOS10_10() ? DoubleClickAction::MAXIMIZE - : DoubleClickAction::NONE; -} - } // namespace namespace ui { -void DrawNinePartImage(NSRect frame, - const NinePartImageIds& image_ids, - NSCompositingOperation operation, - CGFloat alpha, - BOOL flipped) { - NSDrawNinePartImage(frame, - GetImage(image_ids.top_left), - GetImage(image_ids.top), - GetImage(image_ids.top_right), - GetImage(image_ids.left), - GetImage(image_ids.center), - GetImage(image_ids.right), - GetImage(image_ids.bottom_left), - GetImage(image_ids.bottom), - GetImage(image_ids.bottom_right), - operation, - alpha, - flipped); -} - -void WindowTitlebarReceivedDoubleClick(NSWindow* window, id sender) { - switch (WindowTitleBarDoubleClickAction()) { - case DoubleClickAction::MINIMIZE: - [window performMiniaturize:sender]; - break; - - case DoubleClickAction::MAXIMIZE: - [window performZoom:sender]; - break; - - case DoubleClickAction::NONE: - break; - } -} - bool ForceClickInvokesQuickLook() { return [[NSUserDefaults standardUserDefaults] integerForKey:@"com.apple.trackpad.forceClick"] == diff --git a/chromium/ui/base/cocoa/constrained_window/constrained_window_animation.mm b/chromium/ui/base/cocoa/constrained_window/constrained_window_animation.mm index e467e0c85d4..b681b77ca68 100644 --- a/chromium/ui/base/cocoa/constrained_window/constrained_window_animation.mm +++ b/chromium/ui/base/cocoa/constrained_window/constrained_window_animation.mm @@ -5,6 +5,7 @@ #import "ui/base/cocoa/constrained_window/constrained_window_animation.h" #include <stdint.h> +#include <stdlib.h> #include "base/files/file_path.h" #include "base/location.h" @@ -189,6 +190,15 @@ void UpdateWindowShowHideAnimationState(NSWindow* window, CGFloat value) { SetWindowWarp(window, y_offset, scale, perspective_offset); } +bool AreWindowServerEffectsDisabled() { + // If the CHROME_HEADLESS env variable is set, this code is running in a + // test environment. The custom constrained window animations may be + // causing the WindowServer to crash (https://crbug.com/828031), so use the + // simple animations. + static bool is_headless = getenv("CHROME_HEADLESS") != nullptr; + return is_headless; +} + } // namespace @interface ConstrainedWindowAnimationBase () @@ -256,14 +266,26 @@ void UpdateWindowShowHideAnimationState(NSWindow* window, CGFloat value) { @implementation ConstrainedWindowAnimationShow - (void)setWindowStateForStart { + if (AreWindowServerEffectsDisabled()) { + [window_ setAlphaValue:0.0]; + return; + } SetWindowAlpha(window_, 0.0); } - (void)setWindowStateForValue:(float)value { + if (AreWindowServerEffectsDisabled()) { + [window_ setAlphaValue:value]; + return; + } UpdateWindowShowHideAnimationState(window_, value); } - (void)setWindowStateForEnd { + if (AreWindowServerEffectsDisabled()) { + [window_ setAlphaValue:1.0]; + return; + } SetWindowAlpha(window_, 1.0); ClearWindowWarp(window_); } @@ -273,10 +295,18 @@ void UpdateWindowShowHideAnimationState(NSWindow* window, CGFloat value) { @implementation ConstrainedWindowAnimationHide - (void)setWindowStateForValue:(float)value { + if (AreWindowServerEffectsDisabled()) { + [window_ setAlphaValue:1.0 - value]; + return; + } UpdateWindowShowHideAnimationState(window_, 1.0 - value); } - (void)setWindowStateForEnd { + if (AreWindowServerEffectsDisabled()) { + [window_ setAlphaValue:0.0]; + return; + } SetWindowAlpha(window_, 0.0); ClearWindowWarp(window_); } @@ -287,6 +317,9 @@ void UpdateWindowShowHideAnimationState(NSWindow* window, CGFloat value) { // Sets the window scale based on the animation progress. - (void)setWindowStateForValue:(float)value { + if (AreWindowServerEffectsDisabled()) + return; + KeyFrame frames[] = { {0.00, 1.0}, {0.40, 1.02}, {0.60, 1.02}, {1.00, 1.0}, }; @@ -306,6 +339,11 @@ void UpdateWindowShowHideAnimationState(NSWindow* window, CGFloat value) { } - (void)setWindowStateForEnd { + if (AreWindowServerEffectsDisabled()) { + NSBeep(); + return; + } + SetWindowScale(window_, 1.0); } diff --git a/chromium/ui/base/cocoa/controls/blue_label_button.h b/chromium/ui/base/cocoa/controls/blue_label_button.h deleted file mode 100644 index 0712131a791..00000000000 --- a/chromium/ui/base/cocoa/controls/blue_label_button.h +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2013 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_CONTROLS_BLUE_LABEL_BUTTON_H_ -#define UI_BASE_COCOA_CONTROLS_BLUE_LABEL_BUTTON_H_ - -#import <Cocoa/Cocoa.h> - -#import "ui/base/cocoa/hover_button.h" -#include "ui/base/ui_base_export.h" - -// A rectangular blue NSButton that reacts to hover, focus and lit states. It -// can contain an arbitrary single-line text label, and will be sized to fit the -// font height and label width. -UI_BASE_EXPORT -@interface BlueLabelButton : HoverButtonCocoa -@end - -#endif // UI_BASE_COCOA_CONTROLS_BLUE_LABEL_BUTTON_H_ diff --git a/chromium/ui/base/cocoa/controls/blue_label_button.mm b/chromium/ui/base/cocoa/controls/blue_label_button.mm deleted file mode 100644 index 5a620175889..00000000000 --- a/chromium/ui/base/cocoa/controls/blue_label_button.mm +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2013 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/controls/blue_label_button.h" - -#include "base/mac/foundation_util.h" -#include "skia/ext/skia_utils_mac.h" -#include "third_party/skia/include/core/SkColor.h" -#include "ui/base/cocoa/scoped_cg_context_smooth_fonts.h" -#include "ui/base/resource/resource_bundle.h" -#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" - -const CGFloat kCornerRadius = 2; - -const CGFloat kTopBottomTextPadding = 7; -const CGFloat kLeftRightTextPadding = 15; -const SkColor kTextShadowColor = SkColorSetRGB(0x53, 0x8c, 0xea); - -const SkColor kShadowColor = SkColorSetRGB(0xe9, 0xe9, 0xe9); -const SkColor kDefaultColor = SkColorSetRGB(0x5a, 0x97, 0xff); -const SkColor kHoverColor = SkColorSetRGB(0x55, 0x8f, 0xf3); -const SkColor kPressedColor = SkColorSetRGB(0x42, 0x79, 0xd8); - -const SkColor kInnerRingColor = SkColorSetRGB(0x64, 0x9e, 0xff); -const SkColor kFocusInnerRingColor = SkColorSetRGB(0xad, 0xcc, 0xff); -const SkColor kPressInnerRingColor = SkColorSetRGB(0x3f, 0x73, 0xcd); - -const SkColor kOuterRingColor = SkColorSetRGB(0x2b, 0x67, 0xce); -const SkColor kPressOuterRingColor = SkColorSetRGB(0x23, 0x52, 0xa2); - -const int kFontSizeDelta = ui::ResourceBundle::kSmallFontDelta; - -@interface BlueLabelButtonCell : NSButtonCell - -+ (NSAttributedString*)generateAttributedString:(NSString*)buttonText; - -@end - -@implementation BlueLabelButton - -+ (Class)cellClass { - return [BlueLabelButtonCell class]; -} - -- (id)initWithFrame:(NSRect)frameRect { - if ((self = [super initWithFrame:frameRect])) { - [self setBezelStyle:NSSmallSquareBezelStyle]; - } - return self; -} - -@end - -@implementation BlueLabelButtonCell - -+ (NSAttributedString*)generateAttributedString:(NSString*)buttonText { - ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); - NSFont* buttonFont = rb.GetFontWithDelta(kFontSizeDelta).GetNativeFont(); - base::scoped_nsobject<NSMutableParagraphStyle> buttonTextParagraphStyle( - [[NSMutableParagraphStyle alloc] init]); - [buttonTextParagraphStyle setAlignment:NSCenterTextAlignment]; - - base::scoped_nsobject<NSShadow> shadow([[NSShadow alloc] init]); - [shadow setShadowOffset:NSMakeSize(0, -1)]; - [shadow setShadowBlurRadius:0]; - [shadow setShadowColor:skia::SkColorToSRGBNSColor(kTextShadowColor)]; - - NSDictionary* buttonTextAttributes = @{ - NSParagraphStyleAttributeName : buttonTextParagraphStyle, - NSFontAttributeName : buttonFont, - NSForegroundColorAttributeName : [NSColor whiteColor], - NSShadowAttributeName : shadow.get() - }; - base::scoped_nsobject<NSAttributedString> attributedButtonText( - [[NSAttributedString alloc] initWithString:buttonText - attributes:buttonTextAttributes]); - return attributedButtonText.autorelease(); -} - -- (NSSize)cellSize { - NSAttributedString* attributedTitle = - [[self class] generateAttributedString:[self title]]; - NSSize textSize = [attributedTitle size]; - - // Add 1 to maintain identical height w/ previous drawing code. - textSize.height += 2 * kTopBottomTextPadding + 1; - textSize.width += 2 * kLeftRightTextPadding; - return textSize; -} - -- (NSRect)drawTitle:(NSAttributedString*)title - withFrame:(NSRect)frame - inView:(NSView*)controlView { - // Fuzz factor to adjust for the drop shadow. Based on visual inspection. - frame.origin.y -= 1; - - ui::ScopedCGContextSmoothFonts fontSmoothing; - NSAttributedString* attributedTitle = - [[self class] generateAttributedString:[self title]]; - [attributedTitle drawInRect:frame]; - return frame; -} - -- (void)drawBezelWithFrame:(NSRect)frame - inView:(NSView*)controlView { - NSColor* centerColor; - NSColor* innerColor; - NSColor* outerColor; - CloseButtonHoverState hoverState = - [base::mac::ObjCCastStrict<HoverButtonCocoa>(controlView) hoverState]; - // Leave a sliver of height 1 for the button drop shadow. - frame.size.height -= 1; - - if (hoverState == kHoverStateMouseDown && [self isHighlighted]) { - centerColor = skia::SkColorToSRGBNSColor(kPressedColor); - innerColor = skia::SkColorToSRGBNSColor(kPressInnerRingColor); - outerColor = skia::SkColorToSRGBNSColor(kPressOuterRingColor); - } else { - centerColor = hoverState == kHoverStateMouseOver ? - skia::SkColorToSRGBNSColor(kHoverColor) : - skia::SkColorToSRGBNSColor(kDefaultColor); - innerColor = [self showsFirstResponder] ? - skia::SkColorToSRGBNSColor(kFocusInnerRingColor) : - skia::SkColorToSRGBNSColor(kInnerRingColor); - outerColor = skia::SkColorToSRGBNSColor(kOuterRingColor); - } - { - gfx::ScopedNSGraphicsContextSaveGState context; - base::scoped_nsobject<NSShadow> shadow([[NSShadow alloc] init]); - [shadow setShadowOffset:NSMakeSize(0, -1)]; - [shadow setShadowBlurRadius:1.0]; - [shadow setShadowColor:skia::SkColorToSRGBNSColor(kShadowColor)]; - [shadow set]; - [outerColor set]; - - [[NSBezierPath bezierPathWithRoundedRect:frame - xRadius:kCornerRadius - yRadius:kCornerRadius] fill]; - } - - [innerColor set]; - [[NSBezierPath bezierPathWithRoundedRect:NSInsetRect(frame, 1, 1) - xRadius:kCornerRadius - yRadius:kCornerRadius] fill]; - [centerColor set]; - [[NSBezierPath bezierPathWithRoundedRect:NSInsetRect(frame, 2, 2) - xRadius:kCornerRadius - yRadius:kCornerRadius] fill]; -} - -@end diff --git a/chromium/ui/base/cocoa/controls/blue_label_button_unittest.mm b/chromium/ui/base/cocoa/controls/blue_label_button_unittest.mm deleted file mode 100644 index 33200cb11e6..00000000000 --- a/chromium/ui/base/cocoa/controls/blue_label_button_unittest.mm +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2013 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/controls/blue_label_button.h" - -#include "base/mac/scoped_nsobject.h" -#include "base/macros.h" -#import "testing/gtest_mac.h" -#import "ui/base/test/cocoa_helper.h" - -namespace ui { -namespace test { - -class BlueLabelButtonTest : public ui::CocoaTest { - public: - BlueLabelButtonTest() {} - - // ui::CocoaTest override: - void SetUp() override; - - protected: - base::scoped_nsobject<BlueLabelButton> blue_label_button_; - - private: - DISALLOW_COPY_AND_ASSIGN(BlueLabelButtonTest); -}; - -void BlueLabelButtonTest::SetUp() { - blue_label_button_.reset([[BlueLabelButton alloc] initWithFrame:NSZeroRect]); - [blue_label_button_ setTitle:@"Test Title"]; - [blue_label_button_ sizeToFit]; - ui::CocoaTest::SetUp(); - [[test_window() contentView] addSubview:blue_label_button_]; -} - -TEST_VIEW(BlueLabelButtonTest, blue_label_button_); - -} // namespace test -} // namespace ui diff --git a/chromium/ui/base/cocoa/controls/button_utils.h b/chromium/ui/base/cocoa/controls/button_utils.h index 8759436e803..740c914140a 100644 --- a/chromium/ui/base/cocoa/controls/button_utils.h +++ b/chromium/ui/base/cocoa/controls/button_utils.h @@ -22,9 +22,6 @@ UI_BASE_EXPORT + (NSButton*)checkboxWithTitle:(NSString*)title; -+ (NSButton*)linkWithTitle:(NSString*)title - action:(SEL)action - target:(id)target; @end #endif // UI_BASE_COCOA_CONTROLS_BUTTON_UTILS_H_ diff --git a/chromium/ui/base/cocoa/controls/button_utils.mm b/chromium/ui/base/cocoa/controls/button_utils.mm index 51a3b44609b..0a74df2d7da 100644 --- a/chromium/ui/base/cocoa/controls/button_utils.mm +++ b/chromium/ui/base/cocoa/controls/button_utils.mm @@ -4,8 +4,6 @@ #import "ui/base/cocoa/controls/button_utils.h" -#import "ui/base/cocoa/controls/hyperlink_button_cell.h" - @implementation ButtonUtils + (NSButton*)buttonWithTitle:(NSString*)title @@ -31,18 +29,4 @@ return button; } -+ (NSButton*)linkWithTitle:(NSString*)title - action:(SEL)action - target:(id)target { - NSButton* button = [[[NSButton alloc] initWithFrame:NSZeroRect] autorelease]; - // The cell has to be replaced before doing any of the other setup, or it will - // not get the new configuration. - [button setCell:[[[HyperlinkButtonCell alloc] init] autorelease]]; - [button setAction:action]; - [button setFont:[NSFont systemFontOfSize:[NSFont systemFontSize]]]; - [button setTarget:target]; - [button setTitle:title]; - return button; -} - @end diff --git a/chromium/ui/base/cocoa/controls/hover_image_menu_button.h b/chromium/ui/base/cocoa/controls/hover_image_menu_button.h deleted file mode 100644 index 86914192e02..00000000000 --- a/chromium/ui/base/cocoa/controls/hover_image_menu_button.h +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2013 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_CONTROLS_HOVER_IMAGE_MENU_BUTTON_H_ -#define UI_BASE_COCOA_CONTROLS_HOVER_IMAGE_MENU_BUTTON_H_ - -#import <Cocoa/Cocoa.h> - -#import "ui/base/cocoa/tracking_area.h" -#include "ui/base/ui_base_export.h" - -@class HoverImageMenuButtonCell; - -// An NSPopUpButton that additionally tracks mouseover state and calls -// [[self cell] setHovered:flag] when the hover state changes. Uses -// HoverImageMenuButtonCell as the default cellClass. Note that the menu item at -// index 0 is ignored and client code should populate it with a dummy item. -UI_BASE_EXPORT -@interface HoverImageMenuButton : NSPopUpButton { - @private - ui::ScopedCrTrackingArea trackingArea_; -} - -- (HoverImageMenuButtonCell*)hoverImageMenuButtonCell; - -@end - -#endif // UI_BASE_COCOA_CONTROLS_HOVER_IMAGE_MENU_BUTTON_H_ diff --git a/chromium/ui/base/cocoa/controls/hover_image_menu_button.mm b/chromium/ui/base/cocoa/controls/hover_image_menu_button.mm deleted file mode 100644 index 85a311885bd..00000000000 --- a/chromium/ui/base/cocoa/controls/hover_image_menu_button.mm +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2013 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/controls/hover_image_menu_button.h" - -#include "base/mac/foundation_util.h" -#import "ui/base/cocoa/controls/hover_image_menu_button_cell.h" - -@implementation HoverImageMenuButton - -+ (Class)cellClass { - return [HoverImageMenuButtonCell class]; -} - -- (id)initWithFrame:(NSRect)frameRect - pullsDown:(BOOL)flag { - if ((self = [super initWithFrame:frameRect - pullsDown:flag])) { - trackingArea_.reset( - [[CrTrackingArea alloc] initWithRect:NSZeroRect - options:NSTrackingInVisibleRect | - NSTrackingMouseEnteredAndExited | - NSTrackingActiveInKeyWindow - owner:self - userInfo:nil]); - [self addTrackingArea:trackingArea_.get()]; - } - return self; -} - -- (HoverImageMenuButtonCell*)hoverImageMenuButtonCell { - return base::mac::ObjCCastStrict<HoverImageMenuButtonCell>([self cell]); -} - -- (void)mouseEntered:(NSEvent*)theEvent { - [[self cell] setHovered:YES]; -} - -- (void)mouseExited:(NSEvent*)theEvent { - [[self cell] setHovered:NO]; -} - -@end diff --git a/chromium/ui/base/cocoa/controls/hover_image_menu_button_cell.h b/chromium/ui/base/cocoa/controls/hover_image_menu_button_cell.h deleted file mode 100644 index 249a74d78e4..00000000000 --- a/chromium/ui/base/cocoa/controls/hover_image_menu_button_cell.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2013 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_CONTROLS_HOVER_IMAGE_MENU_BUTTON_CELL_H_ -#define UI_BASE_COCOA_CONTROLS_HOVER_IMAGE_MENU_BUTTON_CELL_H_ - -#import <Cocoa/Cocoa.h> - -#include "base/mac/scoped_nsobject.h" -#include "ui/base/ui_base_export.h" - -// A custom NSPopUpButtonCell that permits a hover image, and draws only an -// image in its frame; no border, bezel or drop-down arrow. Use setDefaultImage: -// to set the default image, setAlternateImage: to set the button shown while -// the menu is active, and setHoverImage: for the mouseover hover image. -UI_BASE_EXPORT -@interface HoverImageMenuButtonCell : NSPopUpButtonCell { - @private - base::scoped_nsobject<NSImage> hoverImage_; - BOOL hovered_; -} - -@property(retain, nonatomic) NSImage* hoverImage; -@property(assign, nonatomic, getter=isHovered) BOOL hovered; - -// Return the image that would be drawn based on the current state flags. -- (NSImage*)imageToDraw; - -// Set the default image to show on the menu button. -- (void)setDefaultImage:(NSImage*)defaultImage; - -@end - -#endif // UI_BASE_COCOA_CONTROLS_HOVER_IMAGE_MENU_BUTTON_CELL_H_ diff --git a/chromium/ui/base/cocoa/controls/hover_image_menu_button_cell.mm b/chromium/ui/base/cocoa/controls/hover_image_menu_button_cell.mm deleted file mode 100644 index 10e93fb0b27..00000000000 --- a/chromium/ui/base/cocoa/controls/hover_image_menu_button_cell.mm +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2013 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/controls/hover_image_menu_button_cell.h" - -@implementation HoverImageMenuButtonCell - -@synthesize hovered = hovered_; - -- (id)initTextCell:(NSString*)stringValue - pullsDown:(BOOL)pullDown { - if ((self = [super initTextCell:stringValue - pullsDown:pullDown])) { - [self setUsesItemFromMenu:NO]; - } - return self; -} - -- (void)setHoverImage:(NSImage*)newImage { - if ([hoverImage_ isEqual:newImage]) - return; - - hoverImage_.reset([newImage retain]); - if (hovered_) - [[self controlView] setNeedsDisplay:YES]; -} - -- (NSImage*)hoverImage { - return hoverImage_; -} - -- (void)setHovered:(BOOL)hovered { - if (hovered_ == hovered) - return; - - hovered_ = hovered; - [[self controlView] setNeedsDisplay:YES]; -} - -- (NSImage*)imageToDraw { - if ([self isHighlighted] && [self alternateImage]) - return [self alternateImage]; - - if ([self isHovered] && [self hoverImage]) - return [self hoverImage]; - - // Note that NSPopUpButtonCell updates the cell image when the [self menuItem] - // changes. - return [self image]; -} - -- (void)setDefaultImage:(NSImage*)defaultImage { - base::scoped_nsobject<NSMenuItem> buttonMenuItem([[NSMenuItem alloc] init]); - [buttonMenuItem setImage:defaultImage]; - [self setMenuItem:buttonMenuItem]; -} - -- (void)drawWithFrame:(NSRect)cellFrame - inView:(NSView*)controlView { - [[self imageToDraw] drawInRect:cellFrame - fromRect:NSZeroRect - operation:NSCompositeSourceOver - fraction:1.0 - respectFlipped:YES - hints:nil]; -} - -@end diff --git a/chromium/ui/base/cocoa/controls/hover_image_menu_button_unittest.mm b/chromium/ui/base/cocoa/controls/hover_image_menu_button_unittest.mm deleted file mode 100644 index ea6bd91f18a..00000000000 --- a/chromium/ui/base/cocoa/controls/hover_image_menu_button_unittest.mm +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright 2013 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/controls/hover_image_menu_button.h" - -#include "base/mac/foundation_util.h" -#include "base/macros.h" -#import "testing/gtest_mac.h" -#import "ui/base/cocoa/controls/hover_image_menu_button_cell.h" -#import "ui/base/test/cocoa_helper.h" -#include "ui/events/test/cocoa_test_event_utils.h" - -namespace ui { - -namespace { - -// Test initialization and display of the NSPopUpButton that shows the drop- -// down menu. Don't try to show the menu, since it will block the thread. -class HoverImageMenuButtonTest : public CocoaTest { - public: - HoverImageMenuButtonTest() {} - - // CocoaTest override: - void SetUp() override; - - protected: - base::scoped_nsobject<HoverImageMenuButton> menu_button_; - base::scoped_nsobject<NSImage> normal_; - base::scoped_nsobject<NSImage> pressed_; - base::scoped_nsobject<NSImage> hovered_; - - DISALLOW_COPY_AND_ASSIGN(HoverImageMenuButtonTest); -}; - -void HoverImageMenuButtonTest::SetUp() { - menu_button_.reset( - [[HoverImageMenuButton alloc] initWithFrame:NSMakeRect(0, 0, 50, 30) - pullsDown:YES]); - - normal_.reset([base::mac::ObjCCastStrict<NSImage>( - [NSImage imageNamed:NSImageNameStatusAvailable]) retain]); - pressed_.reset([base::mac::ObjCCastStrict<NSImage>( - [NSImage imageNamed:NSImageNameStatusUnavailable]) retain]); - hovered_.reset([base::mac::ObjCCastStrict<NSImage>( - [NSImage imageNamed:NSImageNameStatusPartiallyAvailable]) retain]); - [[menu_button_ hoverImageMenuButtonCell] setDefaultImage:normal_]; - [[menu_button_ hoverImageMenuButtonCell] setAlternateImage:pressed_]; - [[menu_button_ hoverImageMenuButtonCell] setHoverImage:hovered_]; - - CocoaTest::SetUp(); - [[test_window() contentView] addSubview:menu_button_]; -} - -} // namespace - -TEST_VIEW(HoverImageMenuButtonTest, menu_button_); - -// Tests that the correct image is chosen, depending on the cell's state flags. -TEST_F(HoverImageMenuButtonTest, CheckImagesForState) { - EXPECT_FALSE([[menu_button_ cell] isHovered]); - EXPECT_FALSE([[menu_button_ cell] isHighlighted]); - EXPECT_NSEQ(normal_, [[menu_button_ cell] imageToDraw]); - [menu_button_ display]; - - [[menu_button_ cell] setHovered:YES]; - EXPECT_TRUE([[menu_button_ cell] isHovered]); - EXPECT_FALSE([[menu_button_ cell] isHighlighted]); - EXPECT_NSEQ(hovered_, [[menu_button_ cell] imageToDraw]); - [menu_button_ display]; - - // Highlighted takes precendece over hover. - [[menu_button_ cell] setHighlighted:YES]; - EXPECT_TRUE([[menu_button_ cell] isHovered]); - EXPECT_TRUE([[menu_button_ cell] isHighlighted]); - EXPECT_NSEQ(pressed_, [[menu_button_ cell] imageToDraw]); - [menu_button_ display]; - - [[menu_button_ cell] setHovered:NO]; - EXPECT_FALSE([[menu_button_ cell] isHovered]); - EXPECT_TRUE([[menu_button_ cell] isHighlighted]); - EXPECT_NSEQ(pressed_, [[menu_button_ cell] imageToDraw]); - [menu_button_ display]; - - [[menu_button_ cell] setHighlighted:NO]; - EXPECT_FALSE([[menu_button_ cell] isHovered]); - EXPECT_FALSE([[menu_button_ cell] isHighlighted]); - EXPECT_NSEQ(normal_, [[menu_button_ cell] imageToDraw]); - [menu_button_ display]; -} - -// Tests that calling the various setXImage functions calls setNeedsDisplay. -TEST_F(HoverImageMenuButtonTest, NewImageCausesDisplay) { - [menu_button_ display]; - EXPECT_FALSE([menu_button_ needsDisplay]); - - // Uses setDefaultImage rather than setImage to ensure the image goes into the - // NSPopUpButtonCell's menuItem. It is then accessible using [NSCell image]. - EXPECT_NSEQ(normal_, [[menu_button_ cell] image]); - [[menu_button_ cell] setDefaultImage:pressed_]; - EXPECT_NSEQ(pressed_, [[menu_button_ cell] image]); - EXPECT_TRUE([menu_button_ needsDisplay]); - [menu_button_ display]; - EXPECT_FALSE([menu_button_ needsDisplay]); - - // Highlighting the cell requires a redisplay. - [[menu_button_ cell] setHighlighted:YES]; - EXPECT_TRUE([menu_button_ needsDisplay]); - [menu_button_ display]; - EXPECT_FALSE([menu_button_ needsDisplay]); - - // setAlternateImage comes from NSButtonCell. Ensure the added setHover* - // behaviour matches. - [[menu_button_ cell] setAlternateImage:normal_]; - EXPECT_TRUE([menu_button_ needsDisplay]); - [menu_button_ display]; - EXPECT_FALSE([menu_button_ needsDisplay]); - - // Setting the same image should not cause a redisplay. - [[menu_button_ cell] setAlternateImage:normal_]; - EXPECT_FALSE([menu_button_ needsDisplay]); - - // Unhighlighting requires a redisplay. - [[menu_button_ cell] setHighlighted:NO]; - EXPECT_TRUE([menu_button_ needsDisplay]); - [menu_button_ display]; - EXPECT_FALSE([menu_button_ needsDisplay]); - - // Changing hover state requires a redisplay. - [[menu_button_ cell] setHovered:YES]; - EXPECT_TRUE([menu_button_ needsDisplay]); - [menu_button_ display]; - EXPECT_FALSE([menu_button_ needsDisplay]); - - // setHoverImage comes directly from storage in HoverImageMenuButtonCell. - [[menu_button_ cell] setHoverImage:normal_]; - EXPECT_TRUE([menu_button_ needsDisplay]); - [menu_button_ display]; - EXPECT_FALSE([menu_button_ needsDisplay]); - - // Setting the same image should not cause a redisplay. - [[menu_button_ cell] setHoverImage:normal_]; - EXPECT_FALSE([menu_button_ needsDisplay]); - - // Unhover requires a redisplay. - [[menu_button_ cell] setHovered:NO]; - EXPECT_TRUE([menu_button_ needsDisplay]); - [menu_button_ display]; - EXPECT_FALSE([menu_button_ needsDisplay]); - - // Changing the image while not hovered should not require a redisplay. - [[menu_button_ cell] setHoverImage:pressed_]; - EXPECT_FALSE([menu_button_ needsDisplay]); -} - -// Test that the mouse enter and exit is properly handled, to set hover state. -TEST_F(HoverImageMenuButtonTest, SimulateMouseEnterExit) { - [menu_button_ display]; - EXPECT_FALSE([menu_button_ needsDisplay]); - EXPECT_NSEQ(normal_, [[menu_button_ cell] imageToDraw]); - - [menu_button_ mouseEntered:cocoa_test_event_utils::EnterEvent()]; - EXPECT_TRUE([menu_button_ needsDisplay]); - EXPECT_NSEQ(hovered_, [[menu_button_ cell] imageToDraw]); - [menu_button_ display]; - EXPECT_FALSE([menu_button_ needsDisplay]); - - [menu_button_ mouseExited:cocoa_test_event_utils::ExitEvent()]; - EXPECT_TRUE([menu_button_ needsDisplay]); - EXPECT_NSEQ(normal_, [[menu_button_ cell] imageToDraw]); - [menu_button_ display]; - EXPECT_FALSE([menu_button_ needsDisplay]); -} - -} // namespace ui diff --git a/chromium/ui/base/cocoa/controls/hyperlink_button_cell.h b/chromium/ui/base/cocoa/controls/hyperlink_button_cell.h deleted file mode 100644 index f8753e48660..00000000000 --- a/chromium/ui/base/cocoa/controls/hyperlink_button_cell.h +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2013 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_CONTROLS_HYPERLINK_BUTTON_CELL_H_ -#define UI_BASE_COCOA_CONTROLS_HYPERLINK_BUTTON_CELL_H_ - -#import <Cocoa/Cocoa.h> - -#include "base/mac/scoped_nsobject.h" -#include "ui/base/ui_base_export.h" - -// A HyperlinkButtonCell is used to create an NSButton that looks and acts -// like a hyperlink. The default styling is to look like blue (#3367D6) text -// with no underline and to have the pointingHand cursor on mouse over. -// -// To use in Interface Builder: -// 1. Drag out an NSButton. -// 2. Double click on the button so you have the cell component selected. -// 3. In the Identity panel of the inspector, set the custom class to this. -// 4. In the Attributes panel, change the Bezel to Square. -// 5. In the Size panel, set the Height to 16. -// -// Use this if all of your text is a link. If you need text that contains -// embedded links but also regular text, use HyperlinkTextView. - -namespace hyperlink_button_cell { - -enum class UI_BASE_EXPORT UnderlineBehavior { - ALWAYS, - NEVER, - ON_HOVER, -}; - -} // namespace hyperlink_button_cell - -UI_BASE_EXPORT -@interface HyperlinkButtonCell : NSButtonCell { - base::scoped_nsobject<NSColor> textColor_; - hyperlink_button_cell::UnderlineBehavior underlineBehavior_; - BOOL mouseIsInside_; -} -@property(nonatomic, retain) NSColor* textColor; -@property(nonatomic, assign) hyperlink_button_cell::UnderlineBehavior - underlineBehavior; - -+ (NSColor*)defaultTextColor; - -// Helper function to create a button with HyperLinkButtonCell as its cell. -+ (NSButton*)buttonWithString:(NSString*)string; - -@end - -@interface HyperlinkButtonCell (ExposedForTesting) -- (NSDictionary*)linkAttributes; -@end - -#endif // UI_BASE_COCOA_CONTROLS_HYPERLINK_BUTTON_CELL_H_ diff --git a/chromium/ui/base/cocoa/controls/hyperlink_button_cell.mm b/chromium/ui/base/cocoa/controls/hyperlink_button_cell.mm deleted file mode 100644 index 754be72f613..00000000000 --- a/chromium/ui/base/cocoa/controls/hyperlink_button_cell.mm +++ /dev/null @@ -1,167 +0,0 @@ -// Copyright 2013 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/controls/hyperlink_button_cell.h" - -using hyperlink_button_cell::UnderlineBehavior; - -@interface HyperlinkButtonCell () -- (void)customizeButtonCell; -@end - -@implementation HyperlinkButtonCell - -@dynamic textColor; -@synthesize underlineBehavior = underlineBehavior_; - -+ (NSColor*)defaultTextColor { - // Equates to rgb(51, 103, 214) or #3367D6. - return [NSColor colorWithCalibratedRed:51.0/255.0 - green:103.0/255.0 - blue:214.0/255.0 - alpha:1.0]; -} - -+ (NSButton*)buttonWithString:(NSString*)string { - NSButton* button = [[[NSButton alloc] initWithFrame:NSZeroRect] autorelease]; - base::scoped_nsobject<HyperlinkButtonCell> cell( - [[HyperlinkButtonCell alloc] initTextCell:string]); - [cell setAlignment:NSLeftTextAlignment]; - [button setCell:cell.get()]; - [button setBezelStyle:NSRegularSquareBezelStyle]; - return button; -} - -// Designated initializer. -- (id)init { - if ((self = [super init])) { - [self customizeButtonCell]; - } - return self; -} - -// Initializer called when the cell is loaded from the NIB. -- (id)initWithCoder:(NSCoder*)aDecoder { - if ((self = [super initWithCoder:aDecoder])) { - [self customizeButtonCell]; - } - return self; -} - -// Initializer for code-based creation. -- (id)initTextCell:(NSString*)title { - if ((self = [super initTextCell:title])) { - [self customizeButtonCell]; - } - return self; -} - -- (id)copyWithZone:(NSZone*)zone { - NSColor* color = textColor_.release(); - HyperlinkButtonCell* cell = [super copyWithZone:zone]; - cell->textColor_.reset([color copy]); - textColor_.reset(color); - return cell; -} - -// Because an NSButtonCell has multiple initializers, this method performs the -// common cell customization code. -- (void)customizeButtonCell { - [self setBordered:NO]; - [self setTextColor:[HyperlinkButtonCell defaultTextColor]]; - [self setUnderlineBehavior:UnderlineBehavior::NEVER]; - - CGFloat fontSize = [NSFont systemFontSizeForControlSize:[self controlSize]]; - NSFont* font = [NSFont controlContentFontOfSize:fontSize]; - [self setFont:font]; - - // Do not change button appearance when we are pushed. - [self setHighlightsBy:NSNoCellMask]; - - // We need to set this so that we can override |-mouseEntered:| and - // |-mouseExited:| to change the cursor style on hover states. - [self setShowsBorderOnlyWhileMouseInside:YES]; -} - -- (void)setControlSize:(NSControlSize)size { - [super setControlSize:size]; - [self customizeButtonCell]; // recompute |font|. -} - -// Creates the NSDictionary of attributes for the attributed string. -- (NSDictionary*)linkAttributes { - NSUInteger underlineMask = NSUnderlineStyleNone; - if (underlineBehavior_ == UnderlineBehavior::ALWAYS || - (mouseIsInside_ && [self isEnabled] && - underlineBehavior_ == UnderlineBehavior::ON_HOVER)) { - underlineMask = NSUnderlinePatternSolid | NSUnderlineStyleSingle; - } - - base::scoped_nsobject<NSMutableParagraphStyle> paragraphStyle( - [[NSParagraphStyle defaultParagraphStyle] mutableCopy]); - [paragraphStyle setAlignment:[self alignment]]; - [paragraphStyle setLineBreakMode:[self lineBreakMode]]; - - return [NSDictionary dictionaryWithObjectsAndKeys: - [self textColor], NSForegroundColorAttributeName, - [NSNumber numberWithInt:underlineMask], NSUnderlineStyleAttributeName, - [self font], NSFontAttributeName, - [NSCursor pointingHandCursor], NSCursorAttributeName, - paragraphStyle.get(), NSParagraphStyleAttributeName, - nil - ]; -} - -// Override the drawing for the cell so that the custom style attributes -// can always be applied and so that ellipses will appear when appropriate. -- (NSRect)drawTitle:(NSAttributedString*)title - withFrame:(NSRect)frame - inView:(NSView*)controlView { - NSDictionary* linkAttributes = [self linkAttributes]; - NSString* plainTitle = [title string]; - [plainTitle drawWithRect:frame - options:(NSStringDrawingUsesLineFragmentOrigin | - NSStringDrawingTruncatesLastVisibleLine) - attributes:linkAttributes]; - return frame; -} - -// Override the default behavior to draw the border. Instead, change the cursor. -- (void)mouseEntered:(NSEvent*)event { - mouseIsInside_ = YES; - if ([self isEnabled]) - [[NSCursor pointingHandCursor] push]; - else - [[NSCursor currentCursor] push]; - if (underlineBehavior_ == UnderlineBehavior::ON_HOVER) - [[self controlView] setNeedsDisplay:YES]; -} - -- (void)mouseExited:(NSEvent*)event { - mouseIsInside_ = NO; - [NSCursor pop]; - if (underlineBehavior_ == UnderlineBehavior::ON_HOVER) - [[self controlView] setNeedsDisplay:YES]; -} - -// Setters and getters. -- (NSColor*)textColor { - if ([self isEnabled]) - return textColor_.get(); - else - return [NSColor disabledControlTextColor]; -} - -- (void)setTextColor:(NSColor*)color { - textColor_.reset([color retain]); -} - -// Override so that |-sizeToFit| works better with this type of cell. -- (NSSize)cellSize { - NSSize size = [super cellSize]; - size.width += 2; - return size; -} - -@end diff --git a/chromium/ui/base/cocoa/controls/hyperlink_button_cell_unittest.mm b/chromium/ui/base/cocoa/controls/hyperlink_button_cell_unittest.mm deleted file mode 100644 index 2caf8a6690f..00000000000 --- a/chromium/ui/base/cocoa/controls/hyperlink_button_cell_unittest.mm +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2013 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/controls/hyperlink_button_cell.h" - -#import <Cocoa/Cocoa.h> - -#include "base/mac/foundation_util.h" -#include "base/mac/scoped_nsobject.h" -#include "testing/gtest/include/gtest/gtest.h" -#import "testing/gtest_mac.h" -#include "testing/platform_test.h" -#import "ui/base/test/cocoa_helper.h" -#include "ui/events/test/cocoa_test_event_utils.h" - -namespace ui { - -namespace { - -class HyperlinkButtonCellTest : public CocoaTest { - public: - HyperlinkButtonCellTest() { - NSRect frame = NSMakeRect(0, 0, 50, 30); - base::scoped_nsobject<NSButton> view( - [[NSButton alloc] initWithFrame:frame]); - view_ = view.get(); - base::scoped_nsobject<HyperlinkButtonCell> cell( - [[HyperlinkButtonCell alloc] initTextCell:@"Testing"]); - cell_ = cell.get(); - [view_ setCell:cell_]; - [[test_window() contentView] addSubview:view_]; - } - - void TestCellCustomization(HyperlinkButtonCell* cell) { - EXPECT_FALSE([cell isBordered]); - EXPECT_EQ(NSNoCellMask, [cell_ highlightsBy]); - EXPECT_TRUE([cell showsBorderOnlyWhileMouseInside]); - EXPECT_TRUE([cell textColor]); - } - - protected: - bool HasUnderlineAttribute(NSDictionary* attributes) { - NSNumber* number = base::mac::ObjCCastStrict<NSNumber>( - [attributes objectForKey:NSUnderlineStyleAttributeName]); - return [number unsignedIntegerValue] != 0; - } - - NSButton* view_; - HyperlinkButtonCell* cell_; -}; - -TEST_VIEW(HyperlinkButtonCellTest, view_) - -// Tests the three designated intializers. -TEST_F(HyperlinkButtonCellTest, Initializers) { - TestCellCustomization(cell_); // |-initTextFrame:| - base::scoped_nsobject<HyperlinkButtonCell> cell( - [[HyperlinkButtonCell alloc] init]); - TestCellCustomization(cell.get()); - - // Need to create a dummy archiver to test |-initWithCoder:|. - NSData* emptyData = [NSKeyedArchiver archivedDataWithRootObject:@""]; - NSCoder* coder = - [[[NSKeyedUnarchiver alloc] initForReadingWithData:emptyData] autorelease]; - cell.reset([[HyperlinkButtonCell alloc] initWithCoder:coder]); - TestCellCustomization(cell); -} - -// Test set color. -TEST_F(HyperlinkButtonCellTest, SetTextColor) { - NSColor* textColor = [NSColor redColor]; - EXPECT_NE(textColor, [cell_ textColor]); - [cell_ setTextColor:textColor]; - EXPECT_EQ(textColor, [cell_ textColor]); -} - -// Test mouse events. -TEST_F(HyperlinkButtonCellTest, MouseHover) { - [[NSCursor disappearingItemCursor] push]; // Set a known state. - [cell_ mouseEntered:cocoa_test_event_utils::EnterEvent()]; - EXPECT_EQ([NSCursor pointingHandCursor], [NSCursor currentCursor]); - [cell_ mouseExited:cocoa_test_event_utils::ExitEvent()]; - EXPECT_EQ([NSCursor disappearingItemCursor], [NSCursor currentCursor]); - [NSCursor pop]; -} - -// Test mouse events when button is disabled. { -TEST_F(HyperlinkButtonCellTest, MouseHoverWhenDisabled) { - [cell_ setEnabled:NO]; - - [[NSCursor disappearingItemCursor] push]; // Set a known state. - [cell_ mouseEntered:cocoa_test_event_utils::EnterEvent()]; - EXPECT_EQ([NSCursor disappearingItemCursor], [NSCursor currentCursor]); - - [cell_ mouseExited:cocoa_test_event_utils::ExitEvent()]; - EXPECT_EQ([NSCursor disappearingItemCursor], [NSCursor currentCursor]); - [NSCursor pop]; - [NSCursor pop]; -} - -// Test underline on hover. -TEST_F(HyperlinkButtonCellTest, UnderlineOnHover) { - // Default is for no underline. - EXPECT_FALSE(HasUnderlineAttribute([cell_ linkAttributes])); - [cell_ mouseEntered:cocoa_test_event_utils::EnterEvent()]; - EXPECT_FALSE(HasUnderlineAttribute([cell_ linkAttributes])); - [cell_ mouseExited:cocoa_test_event_utils::ExitEvent()]; - EXPECT_FALSE(HasUnderlineAttribute([cell_ linkAttributes])); - - // Setting behavior to ALWAYS will result in always having an underline. - [cell_ setUnderlineBehavior:hyperlink_button_cell::UnderlineBehavior::ALWAYS]; - EXPECT_TRUE(HasUnderlineAttribute([cell_ linkAttributes])); - [cell_ mouseEntered:cocoa_test_event_utils::EnterEvent()]; - EXPECT_TRUE(HasUnderlineAttribute([cell_ linkAttributes])); - [cell_ mouseExited:cocoa_test_event_utils::ExitEvent()]; - EXPECT_TRUE(HasUnderlineAttribute([cell_ linkAttributes])); - - // Setting behavior to ON_HOVER will result in only underlining when the - // mouse is hovering over the link. - [cell_ setUnderlineBehavior: - hyperlink_button_cell::UnderlineBehavior::ON_HOVER]; - EXPECT_FALSE(HasUnderlineAttribute([cell_ linkAttributes])); - [cell_ mouseEntered:cocoa_test_event_utils::EnterEvent()]; - EXPECT_TRUE(HasUnderlineAttribute([cell_ linkAttributes])); - [cell_ mouseExited:cocoa_test_event_utils::ExitEvent()]; - EXPECT_FALSE(HasUnderlineAttribute([cell_ linkAttributes])); -} - -TEST_F(HyperlinkButtonCellTest, Copy) { - base::scoped_nsobject<HyperlinkButtonCell> cell1([[HyperlinkButtonCell alloc] - initTextCell:@"Cell"]); - [cell1 setTextColor:[NSColor redColor]]; - - base::scoped_nsobject<HyperlinkButtonCell> cell2([cell1 copy]); - EXPECT_NSEQ([cell1 textColor], [cell2 textColor]); - [cell1 setTextColor:[NSColor purpleColor]]; - [cell2 setTextColor:[NSColor greenColor]]; -} - -} // namespace - -} // namespace ui diff --git a/chromium/ui/base/cocoa/controls/hyperlink_text_view.h b/chromium/ui/base/cocoa/controls/hyperlink_text_view.h deleted file mode 100644 index 0a0cb4406bd..00000000000 --- a/chromium/ui/base/cocoa/controls/hyperlink_text_view.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2011 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 <Cocoa/Cocoa.h> - -#include "ui/base/ui_base_export.h" - -@class NSColor; - -// HyperlinkTextView is an NSTextView subclass for unselectable, linkable text. -// This subclass doesn't show the text caret or IBeamCursor, whereas the base -// class NSTextView displays both with full keyboard accessibility enabled. -UI_BASE_EXPORT -@interface HyperlinkTextView : NSTextView { - @private - BOOL refusesFirstResponder_; - BOOL drawsBackgroundUsingSuperview_; - BOOL isValidLink_; -} - -@property(nonatomic, assign) BOOL drawsBackgroundUsingSuperview; - -// Set the |message| displayed by the HyperlinkTextView, using |font| and -// |messageColor|. -- (void)setMessage:(NSString*)message - withFont:(NSFont*)font - messageColor:(NSColor*)messageColor; - -// Marks a |range| within the given message as a link. Pass nil as the url to -// create a link that can neither be copied nor dragged. -- (void)addLinkRange:(NSRange)range - withURL:(NSString*)url - linkColor:(NSColor*)linkColor; - -// This is NO (by default) if the view rejects first responder status. -- (void)setRefusesFirstResponder:(BOOL)refusesFirstResponder; - -@end diff --git a/chromium/ui/base/cocoa/controls/hyperlink_text_view.mm b/chromium/ui/base/cocoa/controls/hyperlink_text_view.mm deleted file mode 100644 index 86204e99e57..00000000000 --- a/chromium/ui/base/cocoa/controls/hyperlink_text_view.mm +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright (c) 2011 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/controls/hyperlink_text_view.h" - -#include "base/logging.h" -#include "base/mac/scoped_nsobject.h" -#include "ui/base/cocoa/nsview_additions.h" - -// The baseline shift for text in the NSTextView. -const float kTextBaselineShift = -1.0; - -@interface HyperlinkTextView(Private) -// Initialize the NSTextView properties for this subclass. -- (void)configureTextView; - -// Change the current IBeamCursor to an arrowCursor. -- (void)fixupCursor; -@end - -@implementation HyperlinkTextView - -@synthesize drawsBackgroundUsingSuperview = drawsBackgroundUsingSuperview_; - -- (id)initWithCoder:(NSCoder*)decoder { - if ((self = [super initWithCoder:decoder])) - [self configureTextView]; - return self; -} - -- (id)initWithFrame:(NSRect)frameRect { - if ((self = [super initWithFrame:frameRect])) - [self configureTextView]; - return self; -} - -- (BOOL)acceptsFirstResponder { - return !refusesFirstResponder_; -} - -- (void)drawViewBackgroundInRect:(NSRect)rect { - if (drawsBackgroundUsingSuperview_) - [self cr_drawUsingAncestor:[self superview] inRect:rect]; - else - [super drawViewBackgroundInRect:rect]; -} - -// Never draw the insertion point (otherwise, it shows up without any user -// action if full keyboard accessibility is enabled). -- (BOOL)shouldDrawInsertionPoint { - return NO; -} - -- (NSRange)selectionRangeForProposedRange:(NSRange)proposedSelRange - granularity:(NSSelectionGranularity)granularity { - // Return a range of length 0 to prevent text selection. Note that the start - // of the range (the first argument) is treated as the position of the - // subsequent click so it must not be 0. If it is, links that begin at a - // non-zero position in the text will not function correctly when they are - // clicked in such a way as to look like a possible text selection. - return NSMakeRange(proposedSelRange.location, 0); -} - -// Convince NSTextView to not show an I-Beam cursor when the cursor is over the -// text view but not over actual text. -// -// http://www.mail-archive.com/cocoa-dev@lists.apple.com/msg10791.html -// "NSTextView sets the cursor over itself dynamically, based on considerations -// including the text under the cursor. It does so in -mouseEntered:, -// -mouseMoved:, and -cursorUpdate:, so those would be points to consider -// overriding." -- (void)mouseMoved:(NSEvent*)e { - [super mouseMoved:e]; - [self fixupCursor]; -} - -- (void)mouseEntered:(NSEvent*)e { - [super mouseEntered:e]; - [self fixupCursor]; -} - -- (void)cursorUpdate:(NSEvent*)e { - [super cursorUpdate:e]; - [self fixupCursor]; -} - -- (void)configureTextView { - [self setEditable:NO]; - [self setDrawsBackground:NO]; - [self setHorizontallyResizable:NO]; - [self setVerticallyResizable:NO]; - - // When text is rendered, linkTextAttributes override anything set via - // addAttributes for text that has NSLinkAttributeName. Set to nil to allow - // custom attributes to take precedence. - [self setLinkTextAttributes:nil]; - [self setDisplaysLinkToolTips:NO]; - - refusesFirstResponder_ = NO; - drawsBackgroundUsingSuperview_ = NO; - isValidLink_ = NO; -} - -- (void)fixupCursor { - if ([[NSCursor currentCursor] isEqual:[NSCursor IBeamCursor]]) - [[NSCursor arrowCursor] set]; -} - -// Only allow contextual menus (which allow copying of the link URL) if the link -// is a valid one. -- (NSMenu*)menuForEvent:(NSEvent*)e { - if (isValidLink_) - return [super menuForEvent:e]; - - return nil; -} - -// Only allow dragging of valid links. -- (BOOL)dragSelectionWithEvent:(NSEvent*)event - offset:(NSSize)mouseOffset - slideBack:(BOOL)slideBack { - if (isValidLink_) { - return [super dragSelectionWithEvent:event - offset:mouseOffset - slideBack:slideBack]; - } - - return NO; -} - -- (void)setMessage:(NSString*)message - withFont:(NSFont*)font - messageColor:(NSColor*)messageColor { - // Create an attributes dictionary for the message and link. - NSDictionary* attributes = @{ - NSForegroundColorAttributeName : messageColor, - NSCursorAttributeName : [NSCursor arrowCursor], - NSFontAttributeName : font, - NSBaselineOffsetAttributeName : @(kTextBaselineShift) - }; - - // Create the attributed string for the message. - base::scoped_nsobject<NSAttributedString> attributedMessage( - [[NSMutableAttributedString alloc] initWithString:message - attributes:attributes]); - - // Update the text view with the new text. - [[self textStorage] setAttributedString:attributedMessage]; -} - -- (void)addLinkRange:(NSRange)range - withURL:(NSString*)url - linkColor:(NSColor*)linkColor { - // If a URL is provided, make sure it is a valid one. - if (url) { - DCHECK_GT([url length], 0u); - DCHECK([NSURL URLWithString:url]); - isValidLink_ = YES; - } else { - url = @""; - isValidLink_ = NO; - } - NSDictionary* attributes = @{ - NSForegroundColorAttributeName : linkColor, - NSUnderlineStyleAttributeName : @(YES), - NSCursorAttributeName : [NSCursor pointingHandCursor], - NSLinkAttributeName : url, - NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle) - }; - - [[self textStorage] addAttributes:attributes range:range]; -} - -- (void)setRefusesFirstResponder:(BOOL)refusesFirstResponder { - refusesFirstResponder_ = refusesFirstResponder; -} - -@end diff --git a/chromium/ui/base/cocoa/controls/hyperlink_text_view_unittest.mm b/chromium/ui/base/cocoa/controls/hyperlink_text_view_unittest.mm deleted file mode 100644 index d4fbd072453..00000000000 --- a/chromium/ui/base/cocoa/controls/hyperlink_text_view_unittest.mm +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright (c) 2011 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/controls/hyperlink_text_view.h" - -#include "base/mac/scoped_nsobject.h" -#include "testing/gtest_mac.h" -#import "ui/base/test/cocoa_helper.h" - -namespace { - -class HyperlinkTextViewTest : public ui::CocoaTest { - public: - HyperlinkTextViewTest() { - NSRect frame = NSMakeRect(0, 0, 50, 50); - base::scoped_nsobject<HyperlinkTextView> view( - [[HyperlinkTextView alloc] initWithFrame:frame]); - view_ = view.get(); - [[test_window() contentView] addSubview:view_]; - } - - NSFont* GetDefaultFont() { - return [NSFont labelFontOfSize: - [NSFont systemFontSizeForControlSize:NSRegularControlSize]]; - } - - NSDictionary* GetDefaultTextAttributes() { - const float kTextBaselineShift = -1.0; - return @{ - NSForegroundColorAttributeName : [NSColor blackColor], - NSCursorAttributeName : [NSCursor arrowCursor], - NSFontAttributeName : GetDefaultFont(), - NSBaselineOffsetAttributeName : @(kTextBaselineShift) - }; - } - - NSMutableDictionary* GetDefaultLinkAttributes() { - if (!linkAttributes_.get()) { - linkAttributes_.reset( - [[NSMutableDictionary dictionaryWithDictionary: - GetDefaultTextAttributes()] retain]); - [linkAttributes_ addEntriesFromDictionary:@{ - NSForegroundColorAttributeName : [NSColor blueColor], - NSUnderlineStyleAttributeName : @(YES), - NSCursorAttributeName : [NSCursor pointingHandCursor], - NSUnderlineStyleAttributeName : @(NSUnderlineStyleSingle), - NSLinkAttributeName : @"" - }]; - } - return [NSMutableDictionary dictionaryWithDictionary:linkAttributes_]; - } - - HyperlinkTextView* view_; - - private: - base::scoped_nsobject<NSMutableDictionary> linkAttributes_; -}; - -TEST_VIEW(HyperlinkTextViewTest, view_); - -TEST_F(HyperlinkTextViewTest, TestSelectionRange) { - NSRange actualRange; - - // The length of the selection range should be 0. - actualRange = [view_ selectionRangeForProposedRange:NSMakeRange(0, 20) - granularity:NSSelectByCharacter]; - EXPECT_TRUE(NSEqualRanges(NSMakeRange(0, 0), actualRange)); - - // While the location should always match the location of the proposed range. - actualRange = [view_ selectionRangeForProposedRange:NSMakeRange(50, 100) - granularity:NSSelectByCharacter]; - EXPECT_TRUE(NSEqualRanges(NSMakeRange(50, 0), actualRange)); -} - -TEST_F(HyperlinkTextViewTest, TestViewConfiguration) { - EXPECT_FALSE([view_ isEditable]); - EXPECT_FALSE([view_ drawsBackground]); - EXPECT_FALSE([view_ isHorizontallyResizable]); - EXPECT_FALSE([view_ isVerticallyResizable]); -} - -TEST_F(HyperlinkTextViewTest, TestSetMessage) { - // Verifies setMessage sets text and attributes properly. - NSString* message = @"Test message"; - [view_ setMessage:message - withFont:GetDefaultFont() - messageColor:[NSColor blackColor]]; - EXPECT_NSEQ(@"Test message", [[view_ textStorage] string]); - - NSDictionary* attributes; - NSRange rangeLimit = NSMakeRange(0, [message length]); - NSRange range; - attributes = [[view_ textStorage] attributesAtIndex:0 - longestEffectiveRange:&range - inRange:rangeLimit]; - EXPECT_EQ(0U, range.location); - EXPECT_EQ([message length], range.length); - EXPECT_NSEQ(GetDefaultTextAttributes(), attributes); -} - -TEST_F(HyperlinkTextViewTest, TestAddLinkRange) { - NSString* message = @"One Two Three Four"; - [view_ setMessage:message - withFont:GetDefaultFont() - messageColor:[NSColor blackColor]]; - - NSColor* blue = [NSColor blueColor]; - [view_ addLinkRange:NSMakeRange(4,3) withURL:@"http://2" linkColor:blue]; - [view_ addLinkRange:NSMakeRange(14,4) withURL:@"http://4" linkColor:blue]; - - NSDictionary* attributes; - NSRange rangeLimit = NSMakeRange(0, [message length]); - NSRange range; - attributes = [[view_ textStorage] attributesAtIndex:0 - longestEffectiveRange:&range - inRange:rangeLimit]; - EXPECT_EQ(0U, range.location); - EXPECT_EQ(4U, range.length); - EXPECT_NSEQ(GetDefaultTextAttributes(), attributes); - - NSMutableDictionary* linkAttributes = GetDefaultLinkAttributes(); - [linkAttributes setObject:@"http://2" forKey:NSLinkAttributeName]; - attributes = [[view_ textStorage] attributesAtIndex:4 - longestEffectiveRange:&range - inRange:rangeLimit]; - EXPECT_EQ(4U, range.location); - EXPECT_EQ(3U, range.length); - EXPECT_NSEQ(linkAttributes, attributes); - - attributes = [[view_ textStorage] attributesAtIndex:7 - longestEffectiveRange:&range - inRange:rangeLimit]; - EXPECT_EQ(7U, range.location); - EXPECT_EQ(7U, range.length); - EXPECT_NSEQ(GetDefaultTextAttributes(), attributes); - - [linkAttributes setObject:@"http://4" forKey:NSLinkAttributeName]; - attributes = [[view_ textStorage] attributesAtIndex:14 - longestEffectiveRange:&range - inRange:rangeLimit]; - EXPECT_EQ(14U, range.location); - EXPECT_EQ(4U, range.length); - EXPECT_NSEQ(linkAttributes, attributes); -} - -TEST_F(HyperlinkTextViewTest, FirstResponderBehavior) { - // By default, accept. - EXPECT_TRUE([view_ acceptsFirstResponder]); - - [view_ setRefusesFirstResponder:YES]; - EXPECT_FALSE([view_ acceptsFirstResponder]); -} - -} // namespace diff --git a/chromium/ui/base/cocoa/controls/imageview_utils.h b/chromium/ui/base/cocoa/controls/imageview_utils.h deleted file mode 100644 index b0e35705310..00000000000 --- a/chromium/ui/base/cocoa/controls/imageview_utils.h +++ /dev/null @@ -1,23 +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_COCOA_CONTROLS_IMAGEVIEW_UTILS_H_ -#define UI_BASE_COCOA_CONTROLS_IMAGEVIEW_UTILS_H_ - -#include "ui/base/ui_base_export.h" - -#include <Cocoa/Cocoa.h> - -UI_BASE_EXPORT -@interface ImageViewUtils : NSObject - -// These methods are a polyfill for convenience constructors that exist on -// NSImageView in macOS 10.12+. -// TODO(ellyjones): once we target only 10.12+, delete these and migrate -// callers over to NSImageView directly. -+ (NSImageView*)imageViewWithImage:(NSImage*)image; - -@end - -#endif // UI_BASE_COCOA_CONTROLS_IMAGEVIEW_UTILS_H_ diff --git a/chromium/ui/base/cocoa/controls/imageview_utils.mm b/chromium/ui/base/cocoa/controls/imageview_utils.mm deleted file mode 100644 index a12d2b4c88f..00000000000 --- a/chromium/ui/base/cocoa/controls/imageview_utils.mm +++ /dev/null @@ -1,15 +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. - -#import "ui/base/cocoa/controls/imageview_utils.h" - -@implementation ImageViewUtils - -+ (NSImageView*)imageViewWithImage:(NSImage*)image { - NSImageView* imageView = [[[NSImageView alloc] init] autorelease]; - [imageView setImage:image]; - return imageView; -} - -@end diff --git a/chromium/ui/base/cocoa/menu_controller.h b/chromium/ui/base/cocoa/menu_controller.h index 95cf7fcaad7..56cdd4a63a7 100644 --- a/chromium/ui/base/cocoa/menu_controller.h +++ b/chromium/ui/base/cocoa/menu_controller.h @@ -15,9 +15,6 @@ namespace ui { class MenuModel; } -UI_BASE_EXPORT extern NSString* const kMenuControllerMenuWillOpenNotification; -UI_BASE_EXPORT extern NSString* const kMenuControllerMenuDidCloseNotification; - // A controller for the cross-platform menu model. The menu that's created // has the tag and represented object set for each menu item. The object is a // NSValue holding a pointer to the model for that level of the menu (to @@ -25,12 +22,10 @@ UI_BASE_EXPORT extern NSString* const kMenuControllerMenuDidCloseNotification; // that particular item. It is important that the model outlives this object // as it only maintains weak references. UI_BASE_EXPORT -@interface MenuControllerCocoa : NSObject<NSMenuDelegate> { - @protected - ui::MenuModel* model_; // Weak. - base::scoped_nsobject<NSMenu> menu_; -} +@interface MenuControllerCocoa + : NSObject<NSMenuDelegate, NSUserInterfaceValidations> +// The Model passed in to -initWithModel:. @property(nonatomic, assign) ui::MenuModel* model; // Whether to activate selected menu items via a posted task. This may allow the @@ -41,7 +36,7 @@ UI_BASE_EXPORT // Note that changing this will have no effect if you use // |-initWithModel:useWithPopUpButtonCell:| or after the first call to |-menu|. -@property(nonatomic) BOOL useWithPopUpButtonCell; +@property(nonatomic, assign) BOOL useWithPopUpButtonCell; + (base::string16)elideMenuTitle:(const base::string16&)title toWidth:(int)width; @@ -69,35 +64,6 @@ UI_BASE_EXPORT // Whether the menu is currently open. - (BOOL)isMenuOpen; -// NSMenuDelegate methods this class implements. Subclasses should call super -// if extending the behavior. -- (void)menuWillOpen:(NSMenu*)menu; -- (void)menuDidClose:(NSMenu*)menu; - -@end - -// Protected methods that subclassers can override and/or invoke. -@interface MenuControllerCocoa (Protected) - -// Called before the menu is to be displayed to update the state (enabled, -// radio, etc) of each item in the menu. Also will update the title if the item -// is marked as "dynamic". -- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item; - -// Adds the item at |index| in |model| as an NSMenuItem at |index| of |menu|. -// Associates a submenu if the MenuModel::ItemType is TYPE_SUBMENU. -- (void)addItemToMenu:(NSMenu*)menu - atIndex:(NSInteger)index - fromModel:(ui::MenuModel*)model; - -// Creates a NSMenu from the given model. If the model has submenus, this can -// be invoked recursively. -- (NSMenu*)menuFromModel:(ui::MenuModel*)model; - -// Returns the maximum width for the menu item. Returns -1 to indicate that -// there's no maximum width. -- (int)maxWidthForMenuModel:(ui::MenuModel*)model - modelIndex:(int)modelIndex; @end #endif // UI_BASE_COCOA_MENU_CONTROLLER_H_ diff --git a/chromium/ui/base/cocoa/menu_controller.mm b/chromium/ui/base/cocoa/menu_controller.mm index 089448f28e2..7462cfc934f 100644 --- a/chromium/ui/base/cocoa/menu_controller.mm +++ b/chromium/ui/base/cocoa/menu_controller.mm @@ -45,13 +45,23 @@ bool MenuHasVisibleItems(const ui::MenuModel* model) { } // namespace -NSString* const kMenuControllerMenuWillOpenNotification = - @"MenuControllerMenuWillOpen"; -NSString* const kMenuControllerMenuDidCloseNotification = - @"MenuControllerMenuDidClose"; - // Internal methods. @interface MenuControllerCocoa () +// Called before the menu is to be displayed to update the state (enabled, +// radio, etc) of each item in the menu. Also will update the title if the item +// is marked as "dynamic". +- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item; + +// Adds the item at |index| in |model| as an NSMenuItem at |index| of |menu|. +// Associates a submenu if the MenuModel::ItemType is TYPE_SUBMENU. +- (void)addItemToMenu:(NSMenu*)menu + atIndex:(NSInteger)index + fromModel:(ui::MenuModel*)model; + +// Creates a NSMenu from the given model. If the model has submenus, this can +// be invoked recursively. +- (NSMenu*)menuFromModel:(ui::MenuModel*)model; + // Adds a separator item at the given index. As the separator doesn't need // anything from the model, this method doesn't need the model index as the // other method below does. @@ -74,6 +84,8 @@ NSString* const kMenuControllerMenuDidCloseNotification = @end @implementation MenuControllerCocoa { + ui::MenuModel* model_; // Weak. + base::scoped_nsobject<NSMenu> menu_; BOOL useWithPopUpButtonCell_; // If YES, 0th item is blank BOOL isMenuOpen_; BOOL postItemSelectedAsTask_; @@ -139,11 +151,6 @@ NSString* const kMenuControllerMenuDidCloseNotification = return menu; } -- (int)maxWidthForMenuModel:(ui::MenuModel*)model - modelIndex:(int)modelIndex { - return -1; -} - - (void)addSeparatorToMenu:(NSMenu*)menu atIndex:(int)index { NSMenuItem* separator = [NSMenuItem separatorItem]; @@ -153,12 +160,7 @@ NSString* const kMenuControllerMenuDidCloseNotification = - (void)addItemToMenu:(NSMenu*)menu atIndex:(NSInteger)index fromModel:(ui::MenuModel*)model { - base::string16 label16 = model->GetLabelAt(index); - int maxWidth = [self maxWidthForMenuModel:model modelIndex:index]; - if (maxWidth != -1) - label16 = [MenuControllerCocoa elideMenuTitle:label16 toWidth:maxWidth]; - - NSString* label = l10n_util::FixUpWindowsStyleLabel(label16); + NSString* label = l10n_util::FixUpWindowsStyleLabel(model->GetLabelAt(index)); base::scoped_nsobject<NSMenuItem> item([[ResponsiveNSMenuItem alloc] initWithTitle:label action:@selector(itemSelected:) @@ -335,16 +337,10 @@ NSString* const kMenuControllerMenuDidCloseNotification = - (void)menuWillOpen:(NSMenu*)menu { isMenuOpen_ = YES; - [[NSNotificationCenter defaultCenter] - postNotificationName:kMenuControllerMenuWillOpenNotification - object:self]; model_->MenuWillShow(); // Note: |model_| may trigger -[self dealloc]. } - (void)menuDidClose:(NSMenu*)menu { - [[NSNotificationCenter defaultCenter] - postNotificationName:kMenuControllerMenuDidCloseNotification - object:self]; if (isMenuOpen_) { isMenuOpen_ = NO; model_->MenuWillClose(); // Note: |model_| may trigger -[self dealloc]. diff --git a/chromium/ui/base/cocoa/ns_view_ids.h b/chromium/ui/base/cocoa/ns_view_ids.h new file mode 100644 index 00000000000..749fdd14dc0 --- /dev/null +++ b/chromium/ui/base/cocoa/ns_view_ids.h @@ -0,0 +1,49 @@ +// 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 new file mode 100644 index 00000000000..3af9ba81e47 --- /dev/null +++ b/chromium/ui/base/cocoa/ns_view_ids.mm @@ -0,0 +1,49 @@ +// 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/nsgraphics_context_additions.h b/chromium/ui/base/cocoa/nsgraphics_context_additions.h deleted file mode 100644 index 7a373ef67c0..00000000000 --- a/chromium/ui/base/cocoa/nsgraphics_context_additions.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) 2013 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_NSGRAPHICS_CONTEXT_ADDITIONS_H_ -#define UI_BASE_COCOA_NSGRAPHICS_CONTEXT_ADDITIONS_H_ - -#import <Cocoa/Cocoa.h> - -@interface NSGraphicsContext (CrAdditions) - -// When a view is not layer backed the pattern phase is relative to the origin -// of the window's content view. With a layer backed view the pattern phase is -// relative to the origin of the view. -// -// For layer backed view this method offsets the pattern phase to match the -// behavior of non layer backed views. -- (void)cr_setPatternPhase:(NSPoint)phase - forView:(NSView*)view; - -@end - -#endif // UI_BASE_COCOA_NSGRAPHICS_CONTEXT_ADDITIONS_H_ diff --git a/chromium/ui/base/cocoa/nsgraphics_context_additions.mm b/chromium/ui/base/cocoa/nsgraphics_context_additions.mm deleted file mode 100644 index c29a76227ee..00000000000 --- a/chromium/ui/base/cocoa/nsgraphics_context_additions.mm +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2013 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/nsgraphics_context_additions.h" - -#include "base/logging.h" - -@implementation NSGraphicsContext (CrAdditions) - -- (void)cr_setPatternPhase:(NSPoint)phase - forView:(NSView*)view { - // TODO(sdy): Remove once we no longer add the tab background view to - // [[window contentView] superview] *or* no longer make the content view - // smaller than the window while exiting fullscreen. These two things - // together can result in the tab background view being drawn while it's - // outside of contentView, with a pattern phase that assumes it's inside. - NSView* contentView = [[view window] contentView]; - if (![view isDescendantOf:contentView]) { - NSView* frameView = [contentView superview]; - DCHECK([view isDescendantOf:frameView]); - // Convert phase into an offset from the top left corner of contentView so - // that it will be aligned correctly at the end of the transition. - phase.x += NSMinX([frameView frame]) - NSMinX([contentView frame]); - phase.y += NSMaxY([frameView frame]) - NSMaxY([contentView frame]); - } - - NSView* ancestorWithLayer = view; - while (ancestorWithLayer && ![ancestorWithLayer layer]) - ancestorWithLayer = [ancestorWithLayer superview]; - if (ancestorWithLayer) { - NSPoint bottomLeft = NSZeroPoint; - if ([ancestorWithLayer isFlipped]) - bottomLeft.y = NSMaxY([ancestorWithLayer bounds]); - NSPoint offset = [ancestorWithLayer convertPoint:bottomLeft toView:nil]; - phase.x -= offset.x; - phase.y -= offset.y; - } - [self setPatternPhase:phase]; -} - -@end diff --git a/chromium/ui/base/cocoa/nsgraphics_context_additions_unittest.mm b/chromium/ui/base/cocoa/nsgraphics_context_additions_unittest.mm deleted file mode 100644 index 632add77e27..00000000000 --- a/chromium/ui/base/cocoa/nsgraphics_context_additions_unittest.mm +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) 2013 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/nsgraphics_context_additions.h" - -#include "base/mac/scoped_nsobject.h" -#import "ui/base/test/cocoa_helper.h" - -typedef ui::CocoaTest NSGraphicsContextCrAdditionsTest; - -TEST_F(NSGraphicsContextCrAdditionsTest, PatternPhase) { - NSRect frame = NSMakeRect(50, 50, 100, 100); - base::scoped_nsobject<NSView> view([[NSView alloc] initWithFrame:frame]); - [[test_window() contentView] addSubview:view]; - - [view lockFocus]; - NSGraphicsContext* context = [NSGraphicsContext currentContext]; - - [context cr_setPatternPhase:NSZeroPoint forView:view]; - EXPECT_EQ(0, [context patternPhase].y); - - [view setWantsLayer:YES]; - [context cr_setPatternPhase:NSZeroPoint forView:view]; - EXPECT_EQ(-NSMinY(frame), [context patternPhase].y); - - [view unlockFocus]; -} diff --git a/chromium/ui/base/cocoa/nsview_additions.h b/chromium/ui/base/cocoa/nsview_additions.h deleted file mode 100644 index 836a21a261b..00000000000 --- a/chromium/ui/base/cocoa/nsview_additions.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2011 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_NSVIEW_ADDITIONS_H_ -#define UI_BASE_COCOA_NSVIEW_ADDITIONS_H_ - -#import <Cocoa/Cocoa.h> - -@interface NSView (ChromeAdditions) - -// Returns the line width that will generate a 1 pixel wide line. -- (CGFloat)cr_lineWidth; - -// Checks if the mouse is currently in this view. -- (BOOL)cr_isMouseInView; - -// Returns YES if this view is below |otherView|. -- (BOOL)cr_isBelowView:(NSView*)otherView; - -// Returns YES if this view is aobve |otherView|. -- (BOOL)cr_isAboveView:(NSView*)otherView; - -// Ensures that the z-order of |subview| is correct relative to |otherView|. -- (void)cr_ensureSubview:(NSView*)subview - isPositioned:(NSWindowOrderingMode)place - relativeTo:(NSView *)otherView; - -// Return best color for keyboard focus ring. -- (NSColor*)cr_keyboardFocusIndicatorColor; - -// Invoke |block| on this view and all descendants. -- (void)cr_recursivelyInvokeBlock:(void (^)(id view))block; - -// Set needsDisplay for this view and all descendants. -- (void)cr_recursivelySetNeedsDisplay:(BOOL)flag; - -// Draw using ancestorView's drawRect function into this view's rect. Do any -// required translating or flipping to transform between the two coordinate -// systems, and optionally clip to the ancestor view's bounds. -- (void)cr_drawUsingAncestor:(NSView*)ancestorView inRect:(NSRect)dirtyRect - clippedToAncestorBounds:(BOOL)clipToAncestorBounds; - -// Same as cr_drawUsingAncestor:inRect:clippedToAncestorBounds: except always -// clips to the ancestor view's bounds. -- (void)cr_drawUsingAncestor:(NSView*)ancestorView inRect:(NSRect)dirtyRect; - -// Used by ancestorView in the above draw call, to look up the child view that -// it is actually drawing to. -- (NSView*)cr_viewBeingDrawnTo; - -// Set a view's accessibilityLabel in a way that's compatible with 10.9. -- (void)cr_setAccessibilityLabel:(NSString*)label; - -@end - -#endif // UI_BASE_COCOA_NSVIEW_ADDITIONS_H_ diff --git a/chromium/ui/base/cocoa/nsview_additions.mm b/chromium/ui/base/cocoa/nsview_additions.mm deleted file mode 100644 index 4f37d9e3989..00000000000 --- a/chromium/ui/base/cocoa/nsview_additions.mm +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "base/mac/sdk_forward_declarations.h" -#import "ui/base/cocoa/nsview_additions.h" -#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" - -#include "base/logging.h" - -@implementation NSView (ChromeAdditions) - -- (CGFloat)cr_lineWidth { - // All shipping retina macs run at least 10.7. - if (![self respondsToSelector:@selector(convertSizeFromBacking:)]) - return 1; - return [self convertSizeFromBacking:NSMakeSize(1, 1)].width; -} - -- (BOOL)cr_isMouseInView { - NSPoint mouseLoc = [[self window] mouseLocationOutsideOfEventStream]; - mouseLoc = [[self superview] convertPoint:mouseLoc fromView:nil]; - return [self hitTest:mouseLoc] == self; -} - -- (BOOL)cr_isBelowView:(NSView*)otherView { - NSArray* subviews = [[self superview] subviews]; - - NSUInteger selfIndex = [subviews indexOfObject:self]; - DCHECK(NSNotFound != selfIndex); - - NSUInteger otherIndex = [subviews indexOfObject:otherView]; - DCHECK(NSNotFound != otherIndex); - - return selfIndex < otherIndex; -} - -- (BOOL)cr_isAboveView:(NSView*)otherView { - return ![self cr_isBelowView:otherView]; -} - -- (void)cr_ensureSubview:(NSView*)subview - isPositioned:(NSWindowOrderingMode)place - relativeTo:(NSView *)otherView { - DCHECK(place == NSWindowAbove || place == NSWindowBelow); - BOOL isAbove = place == NSWindowAbove; - if ([[subview superview] isEqual:self] && - [subview cr_isAboveView:otherView] == isAbove) { - return; - } - - [subview removeFromSuperview]; - [self addSubview:subview - positioned:place - relativeTo:otherView]; -} - -- (NSColor*)cr_keyboardFocusIndicatorColor { - return [[NSColor keyboardFocusIndicatorColor] - colorWithAlphaComponent:0.5 / [self cr_lineWidth]]; -} - -- (void)cr_recursivelyInvokeBlock:(void (^)(id view))block { - block(self); - for (NSView* subview in [self subviews]) - [subview cr_recursivelyInvokeBlock:block]; -} - -- (void)cr_recursivelySetNeedsDisplay:(BOOL)flag { - [self setNeedsDisplay:YES]; - for (NSView* child in [self subviews]) - [child cr_recursivelySetNeedsDisplay:flag]; -} - -static NSView* g_ancestorBeingDrawnFrom = nil; -static NSView* g_childBeingDrawnTo = nil; - -- (void)cr_drawUsingAncestor:(NSView*)ancestorView inRect:(NSRect)dirtyRect - clippedToAncestorBounds:(BOOL)clipToAncestorBounds { - gfx::ScopedNSGraphicsContextSaveGState scopedGSState; - NSRect frame = [self convertRect:[self bounds] toView:ancestorView]; - NSAffineTransform* transform = [NSAffineTransform transform]; - if ([self isFlipped] == [ancestorView isFlipped]) { - [transform translateXBy:-NSMinX(frame) yBy:-NSMinY(frame)]; - } else { - [transform translateXBy:-NSMinX(frame) yBy:NSMaxY(frame)]; - [transform scaleXBy:1.0 yBy:-1.0]; - } - [transform concat]; - - // This can be made robust to recursive calls, but is as of yet unneeded. - DCHECK(!g_ancestorBeingDrawnFrom && !g_childBeingDrawnTo); - g_ancestorBeingDrawnFrom = ancestorView; - g_childBeingDrawnTo = self; - NSRect drawRect = [self convertRect:dirtyRect toView:ancestorView]; - if (clipToAncestorBounds) { - drawRect = NSIntersectionRect([ancestorView bounds], drawRect); - } - [ancestorView drawRect:drawRect]; - g_childBeingDrawnTo = nil; - g_ancestorBeingDrawnFrom = nil; -} - -- (void)cr_drawUsingAncestor:(NSView*)ancestorView inRect:(NSRect)dirtyRect { - [self cr_drawUsingAncestor:ancestorView - inRect:dirtyRect - clippedToAncestorBounds:YES]; -} - -- (NSView*)cr_viewBeingDrawnTo { - if (!g_ancestorBeingDrawnFrom) - return self; - DCHECK(g_ancestorBeingDrawnFrom == self); - return g_childBeingDrawnTo; -} - -- (void)cr_setAccessibilityLabel:(NSString*)label { - if (@available(macOS 10.10, *)) { - self.accessibilityLabel = label; - } else { - [self accessibilitySetOverrideValue:label - forAttribute:NSAccessibilityDescriptionAttribute]; - } -} - -@end diff --git a/chromium/ui/base/cocoa/nsview_additions_unittest.mm b/chromium/ui/base/cocoa/nsview_additions_unittest.mm deleted file mode 100644 index 9d1f66f6633..00000000000 --- a/chromium/ui/base/cocoa/nsview_additions_unittest.mm +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) 2013 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/nsview_additions.h" - -#include "base/mac/scoped_nsobject.h" -#include "testing/gtest/include/gtest/gtest.h" -#import "testing/gtest_mac.h" -#import "ui/base/test/cocoa_helper.h" - -typedef ui::CocoaTest NSViewChromeAdditionsTest; - -@interface ParentView : NSView { - @private - int removeCount_; - int addCount_; -} - -@property(readonly, nonatomic) int removeCount; -@property(readonly, nonatomic) int addCount; - -@end - -@implementation ParentView - -@synthesize removeCount = removeCount_; -@synthesize addCount = addCount_; - -- (void)willRemoveSubview:(NSView*)view { - ++removeCount_; -} - -- (void)didAddSubview:(NSView*)view { - ++addCount_; -} - -@end - -TEST_F(NSViewChromeAdditionsTest, BelowAboveView) { - base::scoped_nsobject<NSView> parent( - [[NSView alloc] initWithFrame:NSZeroRect]); - base::scoped_nsobject<NSView> child1( - [[NSView alloc] initWithFrame:NSZeroRect]); - base::scoped_nsobject<NSView> child2( - [[NSView alloc] initWithFrame:NSZeroRect]); - - [parent addSubview:child1]; - [parent addSubview:child2]; - EXPECT_TRUE([child1 cr_isBelowView:child2]); - EXPECT_FALSE([child1 cr_isAboveView:child2]); - EXPECT_FALSE([child2 cr_isBelowView:child1]); - EXPECT_TRUE([child2 cr_isAboveView:child1]); - - [child1 removeFromSuperview]; - [child2 removeFromSuperview]; - [parent addSubview:child2]; - [parent addSubview:child1]; - EXPECT_FALSE([child1 cr_isBelowView:child2]); - EXPECT_TRUE([child1 cr_isAboveView:child2]); - EXPECT_TRUE([child2 cr_isBelowView:child1]); - EXPECT_FALSE([child2 cr_isAboveView:child1]); -} - -TEST_F(NSViewChromeAdditionsTest, EnsurePosition) { - base::scoped_nsobject<NSView> parent( - [[NSView alloc] initWithFrame:NSZeroRect]); - base::scoped_nsobject<NSView> child1( - [[NSView alloc] initWithFrame:NSZeroRect]); - base::scoped_nsobject<NSView> child2( - [[NSView alloc] initWithFrame:NSZeroRect]); - - [parent addSubview:child1]; - [parent cr_ensureSubview:child2 - isPositioned:NSWindowAbove - relativeTo:child1]; - EXPECT_NSEQ([[parent subviews] objectAtIndex:0], child1); - EXPECT_NSEQ([[parent subviews] objectAtIndex:1], child2); - - [child2 removeFromSuperview]; - [parent cr_ensureSubview:child2 - isPositioned:NSWindowBelow - relativeTo:child1]; - EXPECT_NSEQ([[parent subviews] objectAtIndex:0], child2); - EXPECT_NSEQ([[parent subviews] objectAtIndex:1], child1); -} - -// Verify that no view is removed or added when no change is needed. -TEST_F(NSViewChromeAdditionsTest, EnsurePositionNoChange) { - base::scoped_nsobject<ParentView> parent( - [[ParentView alloc] initWithFrame:NSZeroRect]); - base::scoped_nsobject<NSView> child1( - [[NSView alloc] initWithFrame:NSZeroRect]); - base::scoped_nsobject<NSView> child2( - [[NSView alloc] initWithFrame:NSZeroRect]); - [parent addSubview:child1]; - [parent addSubview:child2]; - - EXPECT_EQ(0, [parent removeCount]); - EXPECT_EQ(2, [parent addCount]); - [parent cr_ensureSubview:child2 - isPositioned:NSWindowAbove - relativeTo:child1]; - EXPECT_EQ(0, [parent removeCount]); - EXPECT_EQ(2, [parent addCount]); -} diff --git a/chromium/ui/base/cocoa/scoped_cg_context_smooth_fonts.h b/chromium/ui/base/cocoa/scoped_cg_context_smooth_fonts.h deleted file mode 100644 index f080d78e199..00000000000 --- a/chromium/ui/base/cocoa/scoped_cg_context_smooth_fonts.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2015 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_SCOPED_CG_CONTEXT_SMOOTH_FONTS_H_ -#define UI_BASE_COCOA_SCOPED_CG_CONTEXT_SMOOTH_FONTS_H_ - -#include "base/macros.h" -#include "ui/base/ui_base_export.h" -#include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" - -namespace ui { - -// Ensures LCD font smoothing is enabled before drawing text. This allows Cocoa -// drawing code to override a decision made by AppKit to disable font smoothing. -// E.g. this occurs when a view is layer-backed or, since 10.8, when a view -// returns NO from -[NSView isOpaque]. For this to look nice, there must be an -// opaque background already drawn in the graphics context at the location of -// the text (but it doesn't need to fill the view bounds, which is required when -// -[NSView isOpaque] returns YES). -class UI_BASE_EXPORT ScopedCGContextSmoothFonts { - public: - ScopedCGContextSmoothFonts(); - ~ScopedCGContextSmoothFonts(); - - private: - gfx::ScopedNSGraphicsContextSaveGState save_state_; - - DISALLOW_COPY_AND_ASSIGN(ScopedCGContextSmoothFonts); -}; - -} // namespace ui - -#endif // UI_BASE_COCOA_SCOPED_CG_CONTEXT_SMOOTH_FONTS_H_ diff --git a/chromium/ui/base/cocoa/scoped_cg_context_smooth_fonts.mm b/chromium/ui/base/cocoa/scoped_cg_context_smooth_fonts.mm deleted file mode 100644 index 1129a19abc8..00000000000 --- a/chromium/ui/base/cocoa/scoped_cg_context_smooth_fonts.mm +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2015 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/scoped_cg_context_smooth_fonts.h" - -#import <AppKit/AppKit.h> - -namespace ui { - -ScopedCGContextSmoothFonts::ScopedCGContextSmoothFonts() { - NSGraphicsContext* context = [NSGraphicsContext currentContext]; - CGContextRef cg_context = static_cast<CGContextRef>([context graphicsPort]); - CGContextSetShouldSmoothFonts(cg_context, true); -} - -ScopedCGContextSmoothFonts::~ScopedCGContextSmoothFonts() { -} - -} // namespace ui diff --git a/chromium/ui/base/cocoa/three_part_image.h b/chromium/ui/base/cocoa/three_part_image.h deleted file mode 100644 index 72da5ca918a..00000000000 --- a/chromium/ui/base/cocoa/three_part_image.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2015 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_THREE_PART_IMAGE_H_ -#define UI_BASE_COCOA_THREE_PART_IMAGE_H_ - -#import <Cocoa/Cocoa.h> - -#include "base/mac/scoped_nsobject.h" -#include "base/macros.h" -#include "ui/base/ui_base_export.h" - -namespace ui { - -// A wrapper around NSDrawThreePartImage that caches the images. -// The middle image is optional. -// Vertical orientation is not currently supported. -class UI_BASE_EXPORT ThreePartImage { - public: - // Create a ThreePartImage from existing NSImages. Specify nil for |middle| if - // there is no middle image. - ThreePartImage(NSImage* left, NSImage* middle, NSImage* right); - ~ThreePartImage(); - - // Returns the image rects if drawn in |bounds|. - NSRect GetLeftRect(NSRect bounds) const; - NSRect GetMiddleRect(NSRect bounds) const; - NSRect GetRightRect(NSRect bounds) const; - - // Draws the three part image inside |rect|. - void DrawInRect(NSRect rect, NSCompositingOperation op, CGFloat alpha) const; - - // Returns YES if |point| is in a non-transparent part of the images. - // Returns YES if |point| is inside the middle rect and there is no middle - // image. - BOOL HitTest(NSPoint point, NSRect bounds) const; - - private: - // Returns YES if |point| is in a non-transparent part of |image|. - BOOL HitTestImage(NSPoint point, NSImage* image, NSRect imageRect) const; - - base::scoped_nsobject<NSImage> leftImage_; - base::scoped_nsobject<NSImage> middleImage_; - base::scoped_nsobject<NSImage> rightImage_; - NSSize leftSize_; - NSSize rightSize_; - - DISALLOW_COPY_AND_ASSIGN(ThreePartImage); -}; - -} // namespace ui - -#endif // UI_BASE_COCOA_THREE_PART_IMAGE_H_ diff --git a/chromium/ui/base/cocoa/three_part_image.mm b/chromium/ui/base/cocoa/three_part_image.mm deleted file mode 100644 index 18ec2ddbfd0..00000000000 --- a/chromium/ui/base/cocoa/three_part_image.mm +++ /dev/null @@ -1,82 +0,0 @@ -// Copyright 2015 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/three_part_image.h" - -#include "base/logging.h" -#include "ui/base/resource/resource_bundle.h" - -namespace ui { - -ThreePartImage::ThreePartImage(NSImage* left, NSImage* middle, NSImage* right) { - DCHECK(left); - DCHECK(right); - leftImage_.reset([left retain]); - rightImage_.reset([right retain]); - leftSize_ = [leftImage_ size]; - rightSize_ = [rightImage_ size]; - - if (middle) { - middleImage_.reset([middle retain]); - } -} - -ThreePartImage::~ThreePartImage() { -} - -NSRect ThreePartImage::GetLeftRect(NSRect bounds) const { - NSRect left, right; - NSDivideRect(bounds, &left, &right, leftSize_.width, NSMinXEdge); - return left; -} - -NSRect ThreePartImage::GetMiddleRect(NSRect bounds) const { - NSRect left, middle, right; - NSDivideRect(bounds, &left, &middle, leftSize_.width, NSMinXEdge); - NSDivideRect(middle, &right, &middle, rightSize_.width, NSMaxXEdge); - return middle; -} - -NSRect ThreePartImage::GetRightRect(NSRect bounds) const { - NSRect left, right; - NSDivideRect(bounds, &right, &left, rightSize_.width, NSMaxXEdge); - return right; -} - -void ThreePartImage::DrawInRect(NSRect rect, - NSCompositingOperation op, - CGFloat alpha) const { - rect.size.height = leftSize_.height; - NSDrawThreePartImage(rect, leftImage_, middleImage_, rightImage_, - NO, op, alpha, NO); -} - -BOOL ThreePartImage::HitTest(NSPoint point, NSRect bounds) const { - NSRect middleRect = GetMiddleRect(bounds); - if (NSPointInRect(point, middleRect)) - return middleImage_ ? HitTestImage(point, middleImage_, middleRect) : YES; - - NSRect leftRect = GetLeftRect(bounds); - if (NSPointInRect(point, leftRect)) - return HitTestImage(point, leftImage_, leftRect); - - NSRect rightRect = GetRightRect(bounds); - if (NSPointInRect(point, rightRect)) - return HitTestImage(point, rightImage_, rightRect); - - return NO; -} - -BOOL ThreePartImage::HitTestImage(NSPoint point, - NSImage* image, - NSRect imageRect) const { - NSRect pointRect = NSMakeRect(point.x, point.y, 1, 1); - return [image hitTestRect:pointRect - withImageDestinationRect:imageRect - context:nil - hints:nil - flipped:NO]; -} - -} // namespace ui diff --git a/chromium/ui/base/cocoa/three_part_image_unittest.mm b/chromium/ui/base/cocoa/three_part_image_unittest.mm deleted file mode 100644 index 223d5e6f235..00000000000 --- a/chromium/ui/base/cocoa/three_part_image_unittest.mm +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2015 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/three_part_image.h" - -#include <memory> - -#include "testing/gtest_mac.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "third_party/skia/include/core/SkColor.h" -#include "third_party/skia/include/core/SkRect.h" -#include "ui/base/resource/resource_bundle.h" -#import "ui/base/test/cocoa_helper.h" -#include "ui/gfx/image/image.h" -#include "ui/gfx/image/image_unittest_util.h" - -namespace ui { -namespace test { - -TEST(ThreePartImageTest, GetRects) { - const int kHeight = 11; - const int kLeftWidth = 3; - const int kMiddleWidth = 5; - const int kRightWidth = 7; - base::scoped_nsobject<NSImage> leftImage( - gfx::test::CreateImage(kLeftWidth, kHeight).CopyNSImage()); - base::scoped_nsobject<NSImage> middleImage( - gfx::test::CreateImage(kMiddleWidth, kHeight).CopyNSImage()); - base::scoped_nsobject<NSImage> rightImage( - gfx::test::CreateImage(kRightWidth, kHeight).CopyNSImage()); - ThreePartImage image(leftImage, middleImage, rightImage); - NSRect bounds = - NSMakeRect(0, 0, kLeftWidth + kMiddleWidth + kRightWidth, kHeight); - EXPECT_NSRECT_EQ(NSMakeRect(0, 0, kLeftWidth, kHeight), - image.GetLeftRect(bounds)); - EXPECT_NSRECT_EQ(NSMakeRect(kLeftWidth, 0, kMiddleWidth, kHeight), - image.GetMiddleRect(bounds)); - EXPECT_NSRECT_EQ( - NSMakeRect(kLeftWidth + kMiddleWidth, 0, kRightWidth, kHeight), - image.GetRightRect(bounds)); - - ThreePartImage image2(leftImage, nullptr, rightImage); - EXPECT_NSRECT_EQ(NSMakeRect(0, 0, kLeftWidth, kHeight), - image.GetLeftRect(bounds)); - EXPECT_NSRECT_EQ(NSMakeRect(kLeftWidth, 0, kMiddleWidth, kHeight), - image.GetMiddleRect(bounds)); - EXPECT_NSRECT_EQ( - NSMakeRect(kLeftWidth + kMiddleWidth, 0, kRightWidth, kHeight), - image.GetRightRect(bounds)); -} - -TEST(ThreePartImageTest, HitTest) { - // Create a bitmap with transparent top and bottom. - const int size = 128; - const int corner_size = 8; - SkBitmap bitmap = gfx::test::CreateBitmap(size, size); - // Clear top and bottom. - bitmap.erase(SK_ColorTRANSPARENT, SkIRect::MakeXYWH(0, 0, size, corner_size)); - bitmap.erase(SK_ColorTRANSPARENT, - SkIRect::MakeXYWH(0, size - corner_size, size, corner_size)); - gfx::Image part_image = gfx::Image::CreateFrom1xBitmap(bitmap); - - // Create a three-part image. - base::scoped_nsobject<NSImage> ns_image(part_image.CopyNSImage()); - ThreePartImage image(ns_image, nullptr, ns_image); - NSRect bounds = NSMakeRect(0, 0, 4 * size, size); - - // The middle of the left and right parts are hits. - EXPECT_TRUE(image.HitTest(NSMakePoint(size / 2, size / 2), bounds)); - EXPECT_TRUE(image.HitTest(NSMakePoint(7 * size / 2, size / 2), bounds)); - - // No middle image means the middle rect is a hit. - EXPECT_TRUE(image.HitTest(NSMakePoint(2 * size, size / 2), bounds)); - - // The corners are transparent. - EXPECT_FALSE(image.HitTest(NSMakePoint(0, 0), bounds)); - EXPECT_FALSE(image.HitTest(NSMakePoint(0, size - 1), bounds)); - EXPECT_FALSE(image.HitTest(NSMakePoint(4 * size - 1, 0), bounds)); - EXPECT_FALSE(image.HitTest(NSMakePoint(4 * size - 1, size - 1), bounds)); -} - -} // namespace test -} // namespace ui diff --git a/chromium/ui/base/cocoa/view_description.h b/chromium/ui/base/cocoa/view_description.h deleted file mode 100644 index 727ad15db0a..00000000000 --- a/chromium/ui/base/cocoa/view_description.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) 2013 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_VIEW_DESCRIPTION_H_ -#define UI_BASE_COCOA_VIEW_DESCRIPTION_H_ - -#import <Cocoa/Cocoa.h> - -#if !NDEBUG - -@interface NSView (CrDebugging) - -// Returns a description of all the subviews and each's frame for debugging. -- (NSString*)cr_recursiveDescription; - -@end - -#endif // !NDEBUG - -#endif // UI_BASE_COCOA_VIEW_DESCRIPTION_H_ diff --git a/chromium/ui/base/cocoa/view_description.mm b/chromium/ui/base/cocoa/view_description.mm deleted file mode 100644 index 1734f8817be..00000000000 --- a/chromium/ui/base/cocoa/view_description.mm +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2013 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/view_description.h" - -#if !NDEBUG - -@implementation NSView (CrDebugging) - -- (NSString*)cr_recursiveDescriptionWithPrefix:(NSString*)prefix { - NSString* description = - [NSString stringWithFormat:@"%@ <%@ %p, frame=%@, hidden=%d>\n", - prefix, [self class], self, NSStringFromRect([self frame]), - [self isHidden]]; - prefix = [prefix stringByAppendingString:@"--"]; - - for (NSView* subview in [self subviews]) { - description = [description stringByAppendingString: - [subview cr_recursiveDescriptionWithPrefix:prefix]]; - } - return description; -} - -- (NSString*)cr_recursiveDescription { - return [self cr_recursiveDescriptionWithPrefix:@""]; -} - -@end - -#endif // !NDEBUG diff --git a/chromium/ui/base/cocoa/views_hostable.h b/chromium/ui/base/cocoa/views_hostable.h new file mode 100644 index 00000000000..68c5cc56f19 --- /dev/null +++ b/chromium/ui/base/cocoa/views_hostable.h @@ -0,0 +1,78 @@ +// 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_VIEWS_HOSTABLE_H_ +#define UI_BASE_COCOA_VIEWS_HOSTABLE_H_ + +#import <objc/objc.h> + +#include "ui/base/ui_base_export.h" +#include "ui/gfx/geometry/rect.h" + +namespace ui { + +class Layer; + +// Interface that it used to stitch a content::WebContentsView into a +// views::View. +class ViewsHostableView { + public: + // Host interface through which the WebContentsView may indicate that its C++ + // object is destroying. + class Host { + public: + // 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; + + // The id for the views::View's NSView. Used to add the + // content::WebContentsView's NSView as a child view. + virtual uint64_t GetNSViewId() const = 0; + + // Query the parent accessibility element of the host. + virtual id GetAccessibilityElement() const = 0; + + // Called when the hostable view will be destroyed. + virtual void OnHostableViewDestroying() = 0; + }; + + // Called when the content::WebContentsView's NSView is added as a subview of + // the views::View's NSView (note that these are the browser-side NSViews). + // This is responsible for: + // - Adding the WebContentsView's ui::Layer to the parent's ui::Layer tree. + // - Stitching together the accessibility tree between the views::View and + // the WebContentsView. + // - Stitching together any app-shim-side NSViews. + virtual void OnViewsHostableAttached(Host* host) = 0; + + // Called when the WebContentsView's NSView has been removed from the + // views::View's NSView. This is responsible for un-doing all of the actions + // taken when attaching. + virtual void OnViewsHostableDetached() = 0; + + // Called when the WebContentsView's NSView is to be shown or resized. + virtual void OnViewsHostableShow(const gfx::Rect& bounds_in_window) = 0; + + // Called when the WebContentsView's NSView is to be hidden. + virtual void OnViewsHostableHide() = 0; + + // Called when the WebContentsView's NSView is to be made a first responder. + virtual void OnViewsHostableMakeFirstResponder() = 0; +}; + +} // namespace ui + +// The protocol through which an NSView indicates support for the +// ViewsHostableView interface. +@protocol ViewsHostable + +- (ui::ViewsHostableView*)viewsHostableView; + +@end + +#endif // UI_BASE_COCOA_VIEWS_HOSTABLE_H_ diff --git a/chromium/ui/base/cursor/cursor_util.cc b/chromium/ui/base/cursor/cursor_util.cc index 5a672aa921a..3735bf69664 100644 --- a/chromium/ui/base/cursor/cursor_util.cc +++ b/chromium/ui/base/cursor/cursor_util.cc @@ -108,7 +108,7 @@ void GetImageCursorBitmap(int resource_id, const gfx::ImageSkiaRep& image_rep = image->GetRepresentation(scale); // TODO(oshima): The cursor should use resource scale factor when // fractional scale factor is enabled. crbug.com/372212 - (*bitmap) = image_rep.sk_bitmap(); + (*bitmap) = image_rep.GetBitmap(); ScaleAndRotateCursorBitmapAndHotpoint( scale / image_rep.scale(), rotation, bitmap, hotspot); // |image_rep| is owned by the resource bundle. So we do not need to free it. @@ -123,7 +123,7 @@ void GetAnimatedCursorBitmaps(int resource_id, const gfx::ImageSkia* image = ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id); const gfx::ImageSkiaRep& image_rep = image->GetRepresentation(scale); - SkBitmap bitmap = image_rep.sk_bitmap(); + SkBitmap bitmap = image_rep.GetBitmap(); int frame_width = bitmap.height(); int frame_height = frame_width; int total_width = bitmap.width(); diff --git a/chromium/ui/base/cursor/ozone/bitmap_cursor_factory_ozone.cc b/chromium/ui/base/cursor/ozone/bitmap_cursor_factory_ozone.cc index 809a927ca4f..a64e32e9185 100644 --- a/chromium/ui/base/cursor/ozone/bitmap_cursor_factory_ozone.cc +++ b/chromium/ui/base/cursor/ozone/bitmap_cursor_factory_ozone.cc @@ -22,6 +22,10 @@ PlatformCursor ToPlatformCursor(BitmapCursorOzone* cursor) { scoped_refptr<BitmapCursorOzone> CreateDefaultBitmapCursor(CursorType type) { Cursor cursor(type); + // Ozone must honor the lowest possible scale value, which is 1.0f. Otherwise, + // it can happen that cursor chooses wrong hotspots if max scaling value is + // set to 200p, for example. + cursor.set_device_scale_factor(1.0f); SkBitmap bitmap = cursor.GetBitmap(); gfx::Point hotspot = cursor.GetHotspot(); if (!bitmap.isNull()) diff --git a/chromium/ui/base/default_theme_provider.h b/chromium/ui/base/default_theme_provider.h index 1f976131f47..c5bf36835b6 100644 --- a/chromium/ui/base/default_theme_provider.h +++ b/chromium/ui/base/default_theme_provider.h @@ -9,7 +9,6 @@ #include "base/compiler_specific.h" #include "base/macros.h" -#include "build/build_config.h" #include "ui/base/theme_provider.h" #include "ui/base/ui_base_export.h" @@ -31,17 +30,6 @@ class UI_BASE_EXPORT DefaultThemeProvider : public ThemeProvider { base::RefCountedMemory* GetRawData(int id, ui::ScaleFactor scale_factor) const override; -#if defined(OS_MACOSX) - bool UsingSystemTheme() const override; - bool InIncognitoMode() const override; - NSImage* GetNSImageNamed(int id) const override; - NSColor* GetNSImageColorNamed(int id) const override; - NSColor* GetNSColor(int id) const override; - NSColor* GetNSColorTint(int id) const override; - NSGradient* GetNSGradient(int id) const override; - bool ShouldIncreaseContrast() const override; -#endif - private: DISALLOW_COPY_AND_ASSIGN(DefaultThemeProvider); }; diff --git a/chromium/ui/base/default_theme_provider_mac.mm b/chromium/ui/base/default_theme_provider_mac.mm deleted file mode 100644 index 21db59a98bf..00000000000 --- a/chromium/ui/base/default_theme_provider_mac.mm +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) 2013 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/default_theme_provider.h" - -#import <Cocoa/Cocoa.h> - -#include "ui/base/resource/resource_bundle.h" - -namespace ui { - -bool DefaultThemeProvider::UsingSystemTheme() const { - return true; -} - -bool DefaultThemeProvider::InIncognitoMode() const { - return false; -} - -NSImage* DefaultThemeProvider::GetNSImageNamed(int id) const { - return ResourceBundle::GetSharedInstance(). - GetNativeImageNamed(id).ToNSImage(); -} - -NSColor* DefaultThemeProvider::GetNSImageColorNamed(int id) const { - NSImage* image = GetNSImageNamed(id); - return [NSColor colorWithPatternImage:image]; -} - -NSColor* DefaultThemeProvider::GetNSColor(int id) const { - return [NSColor redColor]; -} - -NSColor* DefaultThemeProvider::GetNSColorTint(int id) const { - return [NSColor redColor]; -} - -NSGradient* DefaultThemeProvider::GetNSGradient(int id) const { - return nil; -} - -bool DefaultThemeProvider::ShouldIncreaseContrast() const { - return false; -} - -} // namespace ui diff --git a/chromium/ui/base/dragdrop/file_info.cc b/chromium/ui/base/dragdrop/file_info.cc index e5b894a020f..e0f63dc582a 100644 --- a/chromium/ui/base/dragdrop/file_info.cc +++ b/chromium/ui/base/dragdrop/file_info.cc @@ -14,4 +14,8 @@ FileInfo::FileInfo(const base::FilePath& path, FileInfo::~FileInfo() {} +bool FileInfo::operator==(const FileInfo& other) const { + return path == other.path && display_name == other.display_name; +} + } // namespace ui diff --git a/chromium/ui/base/dragdrop/file_info.h b/chromium/ui/base/dragdrop/file_info.h index 6e4f456147a..4c8bd3cb131 100644 --- a/chromium/ui/base/dragdrop/file_info.h +++ b/chromium/ui/base/dragdrop/file_info.h @@ -15,6 +15,7 @@ struct UI_BASE_EXPORT FileInfo { FileInfo(); FileInfo(const base::FilePath& path, const base::FilePath& display_name); ~FileInfo(); + bool operator==(const FileInfo& other) const; base::FilePath path; base::FilePath display_name; // Optional. diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider_aurax11.cc b/chromium/ui/base/dragdrop/os_exchange_data_provider_aurax11.cc index 1a6e9a1c789..6666737fd2d 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_provider_aurax11.cc +++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_aurax11.cc @@ -173,9 +173,7 @@ void OSExchangeDataProviderAuraX11::SetFilename(const base::FilePath& path) { void OSExchangeDataProviderAuraX11::SetFilenames( const std::vector<FileInfo>& filenames) { std::vector<std::string> paths; - for (std::vector<FileInfo>::const_iterator it = filenames.begin(); - it != filenames.end(); - ++it) { + for (auto it = filenames.begin(); it != filenames.end(); ++it) { std::string url_spec = net::FilePathToFileURL(it->path).spec(); if (!url_spec.empty()) paths.push_back(url_spec); diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider_mac.h b/chromium/ui/base/dragdrop/os_exchange_data_provider_mac.h index 7ee25905b85..edc52f42540 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_provider_mac.h +++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_mac.h @@ -57,6 +57,9 @@ class UI_BASE_EXPORT OSExchangeDataProviderMac // Returns the data for the specified type in the pasteboard. NSData* GetNSDataForType(NSString* type) const; + // Gets the underlying pasteboard. + NSPasteboard* GetPasteboard() const; + // Returns the union of SupportedPasteboardTypes() and the types in the // current pasteboard. NSArray* GetAvailableTypes() const; diff --git a/chromium/ui/base/dragdrop/os_exchange_data_provider_mac.mm b/chromium/ui/base/dragdrop/os_exchange_data_provider_mac.mm index 334bc9cad88..c55c78ca720 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_provider_mac.mm +++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_mac.mm @@ -15,6 +15,7 @@ #import "ui/base/clipboard/clipboard_util_mac.h" #include "ui/base/clipboard/custom_data_helper.h" #import "ui/base/dragdrop/cocoa_dnd_util.h" +#include "ui/base/dragdrop/file_info.h" #include "url/gurl.h" namespace ui { @@ -64,7 +65,16 @@ void OSExchangeDataProviderMac::SetFilename(const base::FilePath& path) { void OSExchangeDataProviderMac::SetFilenames( const std::vector<FileInfo>& filenames) { - NOTIMPLEMENTED(); + if (filenames.empty()) + return; + + NSMutableArray* paths = [NSMutableArray arrayWithCapacity:filenames.size()]; + + for (const auto& filename : filenames) { + NSString* path = base::SysUTF8ToNSString(filename.path.value()); + [paths addObject:path]; + } + [pasteboard_->get() setPropertyList:paths forType:NSFilenamesPboardType]; } void OSExchangeDataProviderMac::SetPickledData( @@ -137,8 +147,16 @@ bool OSExchangeDataProviderMac::GetFilename(base::FilePath* path) const { bool OSExchangeDataProviderMac::GetFilenames( std::vector<FileInfo>* filenames) const { - NOTIMPLEMENTED(); - return false; + NSArray* paths = + [pasteboard_->get() propertyListForType:NSFilenamesPboardType]; + if ([paths count] == 0) + return false; + + for (NSString* path in paths) + filenames->push_back( + {base::FilePath(base::SysNSStringToUTF8(path)), base::FilePath()}); + + return true; } bool OSExchangeDataProviderMac::GetPickledData( @@ -194,6 +212,10 @@ NSData* OSExchangeDataProviderMac::GetNSDataForType(NSString* type) const { return [pasteboard_->get() dataForType:type]; } +NSPasteboard* OSExchangeDataProviderMac::GetPasteboard() const { + return pasteboard_->get(); +} + NSArray* OSExchangeDataProviderMac::GetAvailableTypes() const { NSSet* supportedTypes = [NSSet setWithArray:SupportedPasteboardTypes()]; NSMutableSet* availableTypes = 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 2aa2920a069..7f7270b6319 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_provider_win.cc +++ b/chromium/ui/base/dragdrop/os_exchange_data_provider_win.cc @@ -18,6 +18,7 @@ #include "base/i18n/file_util_icu.h" #include "base/logging.h" #include "base/macros.h" +#include "base/no_destructor.h" #include "base/pickle.h" #include "base/strings/utf_string_conversions.h" #include "base/win/scoped_hdc.h" @@ -38,11 +39,9 @@ namespace ui { static const Clipboard::FormatType& GetRendererTaintFormatType() { - CR_DEFINE_STATIC_LOCAL( - Clipboard::FormatType, - format, - (ui::Clipboard::GetFormatType("chromium/x-renderer-taint"))); - return format; + static base::NoDestructor<Clipboard::FormatType> format( + ui::Clipboard::GetFormatType("chromium/x-renderer-taint")); + return *format; } // Creates a new STGMEDIUM object to hold the specified text. The caller @@ -57,8 +56,9 @@ static void GetInternetShortcutFileContents(const GURL& url, std::string* data); static void CreateValidFileNameFromTitle(const GURL& url, const base::string16& title, base::string16* validated); -// Creates a new STGMEDIUM object to hold a file. -static STGMEDIUM* GetStorageForFileName(const base::FilePath& path); +// Creates a new STGMEDIUM object to hold files. +static STGMEDIUM* GetStorageForFileNames( + const std::vector<FileInfo>& filenames); static STGMEDIUM* GetIDListStorageForFileName(const base::FilePath& path); // Creates a File Descriptor for the creation of a file to the given URL and // returns a handle to it. @@ -346,7 +346,7 @@ void OSExchangeDataProviderWin::SetURL(const GURL& url, Clipboard::GetUrlFormatType().ToFormatEtc(), storage)); // TODO(beng): add CF_HTML. - // http://code.google.com/p/chromium/issues/detail?id=6767 + // http://code.google.com/p/chromium/issues/detail?id=6767GetIDListStorageForFileName // Also add text representations (these should be last since they're the // least preferable). @@ -354,11 +354,9 @@ void OSExchangeDataProviderWin::SetURL(const GURL& url, } void OSExchangeDataProviderWin::SetFilename(const base::FilePath& path) { - STGMEDIUM* storage = GetStorageForFileName(path); - data_->contents_.push_back(std::make_unique<DataObjectImpl::StoredDataInfo>( - Clipboard::GetCFHDropFormatType().ToFormatEtc(), storage)); + SetFilenames({FileInfo(path, base::FilePath())}); - storage = GetIDListStorageForFileName(path); + STGMEDIUM* storage = GetIDListStorageForFileName(path); if (!storage) return; data_->contents_.push_back(std::make_unique<DataObjectImpl::StoredDataInfo>( @@ -367,11 +365,12 @@ void OSExchangeDataProviderWin::SetFilename(const base::FilePath& path) { void OSExchangeDataProviderWin::SetFilenames( const std::vector<FileInfo>& filenames) { - for (size_t i = 0; i < filenames.size(); ++i) { - STGMEDIUM* storage = GetStorageForFileName(filenames[i].path); - data_->contents_.push_back(std::make_unique<DataObjectImpl::StoredDataInfo>( - Clipboard::GetCFHDropFormatType().ToFormatEtc(), storage)); - } + STGMEDIUM* storage = GetStorageForFileNames(filenames); + if (!storage) + return; + + data_->contents_.push_back(std::make_unique<DataObjectImpl::StoredDataInfo>( + Clipboard::GetCFHDropFormatType().ToFormatEtc(), storage)); } void OSExchangeDataProviderWin::SetPickledData( @@ -537,7 +536,7 @@ void OSExchangeDataProviderWin::SetDownloadFileInfo( // think we always synthesize one in WebContentsDragWin. STGMEDIUM* storage = NULL; if (!download.filename.empty()) - storage = GetStorageForFileName(download.filename); + GetStorageForFileNames({FileInfo(download.filename, base::FilePath())}); // Add CF_HDROP. auto info = std::make_unique<DataObjectImpl::StoredDataInfo>( @@ -752,8 +751,12 @@ void DataObjectImpl::OnDownloadCompleted(const base::FilePath& file_path) { } // Update the storage. - (*iter)->owns_medium = true; - (*iter)->medium = GetStorageForFileName(file_path); + STGMEDIUM* storage = + GetStorageForFileNames({FileInfo(file_path, base::FilePath())}); + if (storage) { + (*iter)->owns_medium = true; + (*iter)->medium = storage; + } break; } @@ -1003,26 +1006,52 @@ static void CreateValidFileNameFromTitle(const GURL& url, *validated += extension; } -static STGMEDIUM* GetStorageForFileName(const base::FilePath& path) { - const size_t kDropSize = sizeof(DROPFILES); - const size_t kTotalBytes = - kDropSize + (path.value().length() + 2) * sizeof(wchar_t); - HANDLE hdata = GlobalAlloc(GMEM_MOVEABLE, kTotalBytes); +static STGMEDIUM* GetStorageForFileNames( + const std::vector<FileInfo>& filenames) { + // CF_HDROP clipboard format consists of DROPFILES structure, a series of file + // names including the terminating null character and the additional null + // character at the tail to terminate the array. + // For example, + //| DROPFILES | FILENAME 1 | NULL | ... | FILENAME n | NULL | NULL | + // For more details, please refer to + // https://docs.microsoft.com/ko-kr/windows/desktop/shell/clipboard#cf_hdrop + + if (filenames.empty()) + return nullptr; + + const size_t kDropFilesHeaderSizeInBytes = sizeof(DROPFILES); + size_t total_bytes = kDropFilesHeaderSizeInBytes; + for (const auto& filename : filenames) { + // Allocate memory of the filename's length including the null + // character. + total_bytes += (filename.path.value().length() + 1) * sizeof(wchar_t); + } + // |data| needs to be terminated by an additional null character. + total_bytes += sizeof(wchar_t); + + // GHND combines GMEM_MOVEABLE and GMEM_ZEROINIT, and GMEM_ZEROINIT + // initializes memory contents to zero. + HANDLE hdata = GlobalAlloc(GHND, total_bytes); base::win::ScopedHGlobal<DROPFILES*> locked_mem(hdata); DROPFILES* drop_files = locked_mem.get(); drop_files->pFiles = sizeof(DROPFILES); drop_files->fWide = TRUE; + wchar_t* data = reinterpret_cast<wchar_t*>( - reinterpret_cast<BYTE*>(drop_files) + kDropSize); - const size_t copy_size = (path.value().length() + 1) * sizeof(wchar_t); - memcpy(data, path.value().c_str(), copy_size); - data[path.value().length() + 1] = L'\0'; // Double NULL + reinterpret_cast<BYTE*>(drop_files) + kDropFilesHeaderSizeInBytes); + + size_t next_filename_offset = 0; + for (const auto& filename : filenames) { + wcscpy(data + next_filename_offset, filename.path.value().c_str()); + // Skip the terminating null character of the filename. + next_filename_offset += filename.path.value().length() + 1; + } STGMEDIUM* storage = new STGMEDIUM; storage->tymed = TYMED_HGLOBAL; storage->hGlobal = hdata; - storage->pUnkForRelease = NULL; + storage->pUnkForRelease = nullptr; return storage; } diff --git a/chromium/ui/base/dragdrop/os_exchange_data_unittest.cc b/chromium/ui/base/dragdrop/os_exchange_data_unittest.cc index 62d98d1b77a..9291ca57f51 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_unittest.cc +++ b/chromium/ui/base/dragdrop/os_exchange_data_unittest.cc @@ -12,6 +12,7 @@ #include "net/base/filename_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/platform_test.h" +#include "ui/base/dragdrop/file_info.h" #include "ui/base/dragdrop/os_exchange_data.h" #include "ui/events/platform/platform_event_source.h" #include "url/gurl.h" @@ -164,6 +165,30 @@ TEST_F(OSExchangeDataTest, TestPickledData) { EXPECT_EQ(2, value); } +TEST_F(OSExchangeDataTest, TestFilenames) { +#if defined(OS_WIN) + 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()}, + }; +#else + const std::vector<FileInfo> kTestFilenames = { + {base::FilePath(FILE_PATH_LITERAL("/tmp/test_file1")), base::FilePath()}, + {base::FilePath(FILE_PATH_LITERAL("/tmp/test_file2")), base::FilePath()}, + }; +#endif + OSExchangeData data; + data.SetFilenames(kTestFilenames); + + OSExchangeData copy(data.provider().Clone()); + std::vector<FileInfo> dropped_filenames; + + EXPECT_TRUE(copy.GetFilenames(&dropped_filenames)); + EXPECT_EQ(kTestFilenames, dropped_filenames); +} + #if defined(USE_AURA) TEST_F(OSExchangeDataTest, TestHTML) { OSExchangeData data; 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 536c4d37d7d..09bf9b0d6a3 100644 --- a/chromium/ui/base/dragdrop/os_exchange_data_win_unittest.cc +++ b/chromium/ui/base/dragdrop/os_exchange_data_win_unittest.cc @@ -11,6 +11,7 @@ #include "base/win/scoped_hglobal.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/clipboard/clipboard.h" +#include "ui/base/dragdrop/file_info.h" #include "ui/base/dragdrop/os_exchange_data_provider_win.h" #include "url/gurl.h" diff --git a/chromium/ui/base/ime/BUILD.gn b/chromium/ui/base/ime/BUILD.gn index f143cd4db17..df6079261fd 100644 --- a/chromium/ui/base/ime/BUILD.gn +++ b/chromium/ui/base/ime/BUILD.gn @@ -4,6 +4,7 @@ import("//build/config/jumbo.gni") import("//build/config/linux/pangocairo/pangocairo.gni") +import("//build/config/jumbo.gni") import("//build/config/ui.gni") import("//testing/test.gni") @@ -15,56 +16,48 @@ source_set("text_input_types") { ] } -jumbo_component("ime") { - output_name = "ui_base_ime" +jumbo_component("ime_types") { + output_name = "ui_base_ime_types" sources = [ "candidate_window.cc", "candidate_window.h", - "chromeos/character_composer.cc", - "chromeos/character_composer.h", - "chromeos/component_extension_ime_manager.cc", - "chromeos/component_extension_ime_manager.h", - "chromeos/extension_ime_util.cc", - "chromeos/extension_ime_util.h", - "chromeos/fake_ime_keyboard.cc", - "chromeos/fake_ime_keyboard.h", - "chromeos/fake_input_method_delegate.cc", - "chromeos/fake_input_method_delegate.h", - "chromeos/ime_candidate_window_handler_interface.h", - "chromeos/ime_keyboard.cc", - "chromeos/ime_keyboard.h", - "chromeos/ime_keymap.cc", - "chromeos/ime_keymap.h", - "chromeos/input_method_delegate.h", - "chromeos/input_method_descriptor.cc", - "chromeos/input_method_descriptor.h", - "chromeos/input_method_manager.cc", - "chromeos/input_method_manager.h", - "chromeos/input_method_util.cc", - "chromeos/input_method_util.h", - "chromeos/mock_component_extension_ime_manager.cc", - "chromeos/mock_component_extension_ime_manager.h", - "chromeos/mock_component_extension_ime_manager_delegate.cc", - "chromeos/mock_component_extension_ime_manager_delegate.h", - "chromeos/mock_ime_candidate_window_handler.cc", - "chromeos/mock_ime_candidate_window_handler.h", - "chromeos/mock_ime_engine_handler.cc", - "chromeos/mock_ime_engine_handler.h", "composition_text.cc", "composition_text.h", - "ime_bridge.cc", - "ime_bridge.h", - "ime_engine_handler_interface.h", - "ime_input_context_handler_interface.h", "ime_text_span.cc", "ime_text_span.h", "infolist_entry.cc", "infolist_entry.h", + "ui_base_ime_types_export.h", + ] + + defines = [ "UI_BASE_IME_TYPES_IMPLEMENTATION" ] + + deps = [ + "//base", + "//ui/gfx/range", + ] + + public_deps = [ + ":text_input_types", + "//skia", + ] +} + +jumbo_component("ime") { + output_name = "ui_base_ime" + sources = [ + "constants.cc", + "constants.h", + "ime_bridge.cc", + "ime_bridge.h", + "ime_engine_handler_interface.h", + "ime_input_context_handler_interface.h", "input_method.h", "input_method_base.cc", "input_method_base.h", "input_method_chromeos.cc", "input_method_chromeos.h", + "input_method_delegate.cc", "input_method_delegate.h", "input_method_factory.cc", "input_method_factory.h", @@ -79,13 +72,6 @@ jumbo_component("ime") { "input_method_minimal.cc", "input_method_minimal.h", "input_method_observer.h", - "linux/fake_input_method_context.cc", - "linux/fake_input_method_context.h", - "linux/fake_input_method_context_factory.cc", - "linux/fake_input_method_context_factory.h", - "linux/linux_input_method_context.h", - "linux/linux_input_method_context_factory.cc", - "linux/linux_input_method_context_factory.h", "mock_ime_input_context_handler.cc", "mock_ime_input_context_handler.h", "mock_input_method.cc", @@ -110,6 +96,41 @@ jumbo_component("ime") { "win/tsf_text_store.h", ] + if (is_chromeos) { + sources += [ + "chromeos/character_composer.cc", + "chromeos/character_composer.h", + "chromeos/component_extension_ime_manager.cc", + "chromeos/component_extension_ime_manager.h", + "chromeos/extension_ime_util.cc", + "chromeos/extension_ime_util.h", + "chromeos/fake_ime_keyboard.cc", + "chromeos/fake_ime_keyboard.h", + "chromeos/fake_input_method_delegate.cc", + "chromeos/fake_input_method_delegate.h", + "chromeos/ime_candidate_window_handler_interface.h", + "chromeos/ime_keyboard.cc", + "chromeos/ime_keyboard.h", + "chromeos/ime_keymap.cc", + "chromeos/ime_keymap.h", + "chromeos/input_method_delegate.h", + "chromeos/input_method_descriptor.cc", + "chromeos/input_method_descriptor.h", + "chromeos/input_method_manager.cc", + "chromeos/input_method_manager.h", + "chromeos/input_method_util.cc", + "chromeos/input_method_util.h", + "chromeos/mock_component_extension_ime_manager.cc", + "chromeos/mock_component_extension_ime_manager.h", + "chromeos/mock_component_extension_ime_manager_delegate.cc", + "chromeos/mock_component_extension_ime_manager_delegate.h", + "chromeos/mock_ime_candidate_window_handler.cc", + "chromeos/mock_ime_candidate_window_handler.h", + "chromeos/mock_ime_engine_handler.cc", + "chromeos/mock_ime_engine_handler.h", + ] + } + # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. configs += [ "//build/config/compiler:no_size_t_to_int_warning" ] @@ -132,8 +153,7 @@ jumbo_component("ime") { ] public_deps = [ - ":text_input_types", - "//skia", + ":ime_types", ] if (is_desktop_linux) { @@ -143,6 +163,10 @@ jumbo_component("ime") { ] } + if (is_linux) { + public_deps += [ "//ui/base/ime/linux" ] + } + if (!is_chromeos && is_linux) { sources += [ "linux/text_edit_command_auralinux.cc", @@ -211,4 +235,19 @@ jumbo_component("ime") { if (is_mac) { libs = [ "AppKit.framework" ] } + + if (is_fuchsia) { + sources += [ + "fuchsia/input_method_keyboard_controller_fuchsia.cc", + "fuchsia/input_method_keyboard_controller_fuchsia.h", + "input_method_fuchsia.cc", + "input_method_fuchsia.h", + ] + + deps += [ + "//third_party/fuchsia-sdk/sdk:input", + "//ui/events", + "//ui/events:dom_keycode_converter", + ] + } } diff --git a/chromium/ui/base/ime/candidate_window.h b/chromium/ui/base/ime/candidate_window.h index f27bfe29b70..60b8f68e409 100644 --- a/chromium/ui/base/ime/candidate_window.h +++ b/chromium/ui/base/ime/candidate_window.h @@ -13,19 +13,19 @@ #include "base/macros.h" #include "ui/base/ime/infolist_entry.h" -#include "ui/base/ime/ui_base_ime_export.h" +#include "ui/base/ime/ui_base_ime_types_export.h" namespace ui { // CandidateWindow represents the structure of candidates generated from IME. -class UI_BASE_IME_EXPORT CandidateWindow { +class UI_BASE_IME_TYPES_EXPORT CandidateWindow { public: enum Orientation { HORIZONTAL = 0, VERTICAL = 1, }; - struct UI_BASE_IME_EXPORT CandidateWindowProperty { + struct UI_BASE_IME_TYPES_EXPORT CandidateWindowProperty { CandidateWindowProperty(); virtual ~CandidateWindowProperty(); int page_size; @@ -41,7 +41,7 @@ class UI_BASE_IME_EXPORT CandidateWindow { }; // Represents a candidate entry. - struct UI_BASE_IME_EXPORT Entry { + struct UI_BASE_IME_TYPES_EXPORT Entry { Entry(); Entry(const Entry& other); virtual ~Entry(); diff --git a/chromium/ui/base/ime/composition_text.h b/chromium/ui/base/ime/composition_text.h index 272535ba3e6..6d05f15bce5 100644 --- a/chromium/ui/base/ime/composition_text.h +++ b/chromium/ui/base/ime/composition_text.h @@ -9,13 +9,13 @@ #include "base/strings/string16.h" #include "ui/base/ime/ime_text_span.h" -#include "ui/base/ime/ui_base_ime_export.h" +#include "ui/base/ime/ui_base_ime_types_export.h" #include "ui/gfx/range/range.h" namespace ui { // A struct represents the status of an ongoing composition text. -struct UI_BASE_IME_EXPORT CompositionText { +struct UI_BASE_IME_TYPES_EXPORT CompositionText { CompositionText(); CompositionText(const CompositionText& other); ~CompositionText(); diff --git a/chromium/ui/base/ime/constants.cc b/chromium/ui/base/ime/constants.cc new file mode 100644 index 00000000000..e9556078fc3 --- /dev/null +++ b/chromium/ui/base/ime/constants.cc @@ -0,0 +1,11 @@ +// 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/ime/constants.h" + +namespace ui { + +const char kPropertyFromVK[] = "from_vk"; + +} // namespace ui diff --git a/chromium/ui/base/ime/constants.h b/chromium/ui/base/ime/constants.h new file mode 100644 index 00000000000..f5e812a7b44 --- /dev/null +++ b/chromium/ui/base/ime/constants.h @@ -0,0 +1,21 @@ +// 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_IME_CONSTANTS_H_ +#define UI_BASE_IME_CONSTANTS_H_ + +#include "ui/base/ime/ui_base_ime_export.h" + +namespace ui { + +// The name of the property that is attach to the key event and indicates +// whether it was from the virtual keyboard. +// This is used where the key event is simulated by the virtual keyboard +// (e.g. IME extension API) as well as the input field implementation (e.g. +// Textfield). +UI_BASE_IME_EXPORT extern const char kPropertyFromVK[]; + +} // namespace ui + +#endif // UI_BASE_IME_CONSTANTS_H_ diff --git a/chromium/ui/base/ime/fuchsia/input_method_keyboard_controller_fuchsia.cc b/chromium/ui/base/ime/fuchsia/input_method_keyboard_controller_fuchsia.cc new file mode 100644 index 00000000000..b2f5b9fd580 --- /dev/null +++ b/chromium/ui/base/ime/fuchsia/input_method_keyboard_controller_fuchsia.cc @@ -0,0 +1,96 @@ +// 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/ime/fuchsia/input_method_keyboard_controller_fuchsia.h" + +#include <utility> + +#include "base/fuchsia/component_context.h" +#include "base/logging.h" +#include "ui/base/ime/text_input_client.h" +#include "ui/events/base_event_utils.h" +#include "ui/events/keycodes/dom/dom_code.h" +#include "ui/events/keycodes/dom/keycode_converter.h" + +namespace ui { + +InputMethodKeyboardControllerFuchsia::InputMethodKeyboardControllerFuchsia( + InputMethodFuchsia* input_method) + : event_converter_(input_method), + ime_client_binding_(this), + ime_service_(base::fuchsia::ComponentContext::GetDefault() + ->ConnectToService<fuchsia::ui::input::ImeService>()), + ime_visibility_( + base::fuchsia::ComponentContext::GetDefault() + ->ConnectToService<fuchsia::ui::input::ImeVisibilityService>()), + input_method_(input_method) { + DCHECK(ime_service_); + DCHECK(input_method_); + + ime_service_.set_error_handler([this]() { + LOG(ERROR) << "Lost connection to IME service."; + ime_.Unbind(); + ime_client_binding_.Unbind(); + }); + + ime_visibility_.events().OnKeyboardVisibilityChanged = [this](bool visible) { + keyboard_visible_ = visible; + }; +} + +InputMethodKeyboardControllerFuchsia::~InputMethodKeyboardControllerFuchsia() = + default; + +bool InputMethodKeyboardControllerFuchsia::DisplayVirtualKeyboard() { + if (!ime_) { + // TODO(crbug.com/876934): Instantiate the IME with details about the + // current composition. + fuchsia::ui::input::TextInputState state = {}; + state.text = ""; + ime_service_->GetInputMethodEditor( + fuchsia::ui::input::KeyboardType::TEXT, + fuchsia::ui::input::InputMethodAction::UNSPECIFIED, std::move(state), + ime_client_binding_.NewBinding(), ime_.NewRequest()); + } + + if (!keyboard_visible_) { + ime_service_->ShowKeyboard(); + } + + return true; +} + +void InputMethodKeyboardControllerFuchsia::DismissVirtualKeyboard() { + if (keyboard_visible_) { + ime_service_->HideKeyboard(); + } +} + +void InputMethodKeyboardControllerFuchsia::AddObserver( + InputMethodKeyboardControllerObserver* observer) {} + +void InputMethodKeyboardControllerFuchsia::RemoveObserver( + InputMethodKeyboardControllerObserver* observer) {} + +bool InputMethodKeyboardControllerFuchsia::IsKeyboardVisible() { + return keyboard_visible_; +} + +void InputMethodKeyboardControllerFuchsia::DidUpdateState( + fuchsia::ui::input::TextInputState state, + std::unique_ptr<fuchsia::ui::input::InputEvent> input_event) { + if (input_event->is_keyboard()) + event_converter_.ProcessEvent(*input_event); +} + +void InputMethodKeyboardControllerFuchsia::OnAction( + fuchsia::ui::input::InputMethodAction action) { + // Synthesize an ENTER keypress and send it to the Window. + KeyEvent key_event(ET_KEY_PRESSED, KeyboardCode::VKEY_RETURN, + ui::DomCode::ENTER, ui::EF_NONE, ui::DomKey::ENTER, + ui::EventTimeForNow()); + input_method_->DispatchKeyEvent(&key_event); +} + +} // namespace ui diff --git a/chromium/ui/base/ime/fuchsia/input_method_keyboard_controller_fuchsia.h b/chromium/ui/base/ime/fuchsia/input_method_keyboard_controller_fuchsia.h new file mode 100644 index 00000000000..01b53ea2704 --- /dev/null +++ b/chromium/ui/base/ime/fuchsia/input_method_keyboard_controller_fuchsia.h @@ -0,0 +1,59 @@ +// 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_IME_FUCHSIA_INPUT_METHOD_KEYBOARD_CONTROLLER_FUCHSIA_H_ +#define UI_BASE_IME_FUCHSIA_INPUT_METHOD_KEYBOARD_CONTROLLER_FUCHSIA_H_ + +#include <fuchsia/ui/input/cpp/fidl.h> +#include <lib/fidl/cpp/binding.h> +#include <memory> + +#include "base/macros.h" +#include "ui/base/ime/input_method_fuchsia.h" +#include "ui/base/ime/input_method_keyboard_controller.h" +#include "ui/base/ime/ui_base_ime_export.h" +#include "ui/events/fuchsia/input_event_dispatcher.h" + +namespace ui { + +// Handles input events from the Fuchsia on-screen keyboard. +class UI_BASE_IME_EXPORT InputMethodKeyboardControllerFuchsia + : public InputMethodKeyboardController, + public fuchsia::ui::input::InputMethodEditorClient { + public: + // |input_method|: Pointer to the parent InputMethod which owns |this|. + explicit InputMethodKeyboardControllerFuchsia( + InputMethodFuchsia* input_method); + ~InputMethodKeyboardControllerFuchsia() override; + + // InputMethodKeyboardController implementation. + bool DisplayVirtualKeyboard() override; + void DismissVirtualKeyboard() override; + void AddObserver(InputMethodKeyboardControllerObserver* observer) override; + void RemoveObserver(InputMethodKeyboardControllerObserver* observer) override; + bool IsKeyboardVisible() override; + + private: + // InputMethodEditorClient implementation. + void DidUpdateState( + fuchsia::ui::input::TextInputState state, + std::unique_ptr<fuchsia::ui::input::InputEvent> input_event) override; + void OnAction(fuchsia::ui::input::InputMethodAction action) override; + + bool keyboard_visible_ = false; + InputEventDispatcher event_converter_; + fidl::Binding<fuchsia::ui::input::InputMethodEditorClient> + ime_client_binding_; + fuchsia::ui::input::ImeServicePtr ime_service_; + fuchsia::ui::input::InputMethodEditorPtr ime_; + fuchsia::ui::input::ImeVisibilityServicePtr ime_visibility_; + + InputMethodFuchsia* input_method_; + + DISALLOW_COPY_AND_ASSIGN(InputMethodKeyboardControllerFuchsia); +}; + +} // namespace ui + +#endif // UI_BASE_IME_FUCHSIA_INPUT_METHOD_KEYBOARD_CONTROLLER_FUCHSIA_H_ diff --git a/chromium/ui/base/ime/ime_text_span.h b/chromium/ui/base/ime/ime_text_span.h index e47064a1a63..52011f3c067 100644 --- a/chromium/ui/base/ime/ime_text_span.h +++ b/chromium/ui/base/ime/ime_text_span.h @@ -11,14 +11,14 @@ #include <vector> #include "third_party/skia/include/core/SkColor.h" -#include "ui/base/ime/ui_base_ime_export.h" +#include "ui/base/ime/ui_base_ime_types_export.h" namespace ui { // Intentionally keep sync with blink::WebImeTextSpan defined in: // third_party/WebKit/public/web/WebImeTextSpan.h -struct UI_BASE_IME_EXPORT ImeTextSpan { +struct UI_BASE_IME_TYPES_EXPORT ImeTextSpan { enum class Type { // Creates a composition marker. kComposition, diff --git a/chromium/ui/base/ime/infolist_entry.h b/chromium/ui/base/ime/infolist_entry.h index a19d8541077..1e16e5e938f 100644 --- a/chromium/ui/base/ime/infolist_entry.h +++ b/chromium/ui/base/ime/infolist_entry.h @@ -6,12 +6,12 @@ #define UI_BASE_IME_INFOLIST_ENTRY_H_ #include "base/strings/string16.h" -#include "ui/base/ime/ui_base_ime_export.h" +#include "ui/base/ime/ui_base_ime_types_export.h" namespace ui { // The data model of infolist window. -struct UI_BASE_IME_EXPORT InfolistEntry { +struct UI_BASE_IME_TYPES_EXPORT InfolistEntry { base::string16 title; base::string16 body; bool highlighted; diff --git a/chromium/ui/base/ime/input_method_auralinux.cc b/chromium/ui/base/ime/input_method_auralinux.cc index 699792009be..f7343b30c07 100644 --- a/chromium/ui/base/ime/input_method_auralinux.cc +++ b/chromium/ui/base/ime/input_method_auralinux.cc @@ -6,6 +6,7 @@ #include "base/auto_reset.h" #include "base/bind.h" +#include "base/bind_helpers.h" #include "base/environment.h" #include "ui/base/ime/ime_bridge.h" #include "ui/base/ime/ime_engine_handler_interface.h" @@ -52,11 +53,12 @@ ui::EventDispatchDetails InputMethodAuraLinux::DispatchKeyEvent( // If no text input client, do nothing. if (!GetTextInputClient()) - return DispatchKeyEventPostIME(event); + return DispatchKeyEventPostIME(event, base::NullCallback()); if (!event->HasNativeEvent() && sending_key_event()) { // Faked key events that are sent from input.ime.sendKeyEvents. - ui::EventDispatchDetails details = DispatchKeyEventPostIME(event); + ui::EventDispatchDetails details = + DispatchKeyEventPostIME(event, base::NullCallback()); if (details.dispatcher_destroyed || details.target_destroyed || event->stopped_propagation()) { return details; @@ -135,7 +137,7 @@ ui::EventDispatchDetails InputMethodAuraLinux::ProcessKeyEventDone( ui::EventDispatchDetails details; if (event->type() == ui::ET_KEY_PRESSED && filtered) { if (NeedInsertChar()) - details = DispatchKeyEventPostIME(event); + details = DispatchKeyEventPostIME(event, base::NullCallback()); else if (HasInputMethodResult()) details = SendFakeProcessKeyEvent(event); if (details.dispatcher_destroyed) @@ -194,7 +196,7 @@ ui::EventDispatchDetails InputMethodAuraLinux::ProcessKeyEventDone( composition_ = CompositionText(); if (!filtered) { - details = DispatchKeyEventPostIME(event); + details = DispatchKeyEventPostIME(event, base::NullCallback()); if (details.dispatcher_destroyed) { if (should_stop_propagation) event->StopPropagation(); @@ -275,6 +277,14 @@ void InputMethodAuraLinux::OnCaretBoundsChanged(const TextInputClient* client) { NotifyTextInputCaretBoundsChanged(client); context_->SetCursorLocation(GetTextInputClient()->GetCaretBounds()); + gfx::Range text_range, selection_range; + base::string16 text; + if (client->GetTextRange(&text_range) && + client->GetTextFromRange(text_range, &text) && + client->GetSelectionRange(&selection_range)) { + context_->SetSurroundingText(text, selection_range); + } + if (!IsTextInputTypeNone() && text_input_type_ != TEXT_INPUT_TYPE_PASSWORD && GetEngine()) GetEngine()->SetCompositionBounds(GetCompositionBounds(client)); @@ -307,13 +317,6 @@ void InputMethodAuraLinux::ResetContext() { context_->Reset(); context_simple_->Reset(); - // Some input methods may not honour the reset call. Focusing out/in the - // |context_| to make sure it gets reset correctly. - if (text_input_type_ != TEXT_INPUT_TYPE_NONE) { - context_->Blur(); - context_->Focus(); - } - composition_ = CompositionText(); result_text_.clear(); is_sync_mode_ = false; @@ -353,6 +356,14 @@ void InputMethodAuraLinux::OnCommit(const base::string16& text) { } } +void InputMethodAuraLinux::OnDeleteSurroundingText(int32_t index, + uint32_t length) { + if (GetTextInputClient() && composition_.text.empty()) { + uint32_t before = index >= 0 ? 0U : static_cast<uint32_t>(-1 * index); + GetTextInputClient()->ExtendSelectionAndDelete(before, length - before); + } +} + void InputMethodAuraLinux::OnPreeditChanged( const CompositionText& composition_text) { if (IgnoringNonKeyInput() || IsTextInputTypeNone()) @@ -432,7 +443,8 @@ bool InputMethodAuraLinux::NeedInsertChar() const { ui::EventDispatchDetails InputMethodAuraLinux::SendFakeProcessKeyEvent( ui::KeyEvent* event) const { KeyEvent key_event(ui::ET_KEY_PRESSED, ui::VKEY_PROCESSKEY, event->flags()); - ui::EventDispatchDetails details = DispatchKeyEventPostIME(&key_event); + ui::EventDispatchDetails details = + DispatchKeyEventPostIME(&key_event, base::NullCallback()); if (key_event.stopped_propagation()) event->StopPropagation(); return details; diff --git a/chromium/ui/base/ime/input_method_auralinux.h b/chromium/ui/base/ime/input_method_auralinux.h index ef4985b95c6..8963ab945ad 100644 --- a/chromium/ui/base/ime/input_method_auralinux.h +++ b/chromium/ui/base/ime/input_method_auralinux.h @@ -35,6 +35,7 @@ class UI_BASE_IME_EXPORT InputMethodAuraLinux // Overriden from ui::LinuxInputMethodContextDelegate void OnCommit(const base::string16& text) override; + void OnDeleteSurroundingText(int32_t index, uint32_t length) override; void OnPreeditChanged(const CompositionText& composition_text) override; void OnPreeditEnd() override; void OnPreeditStart() override {} diff --git a/chromium/ui/base/ime/input_method_auralinux_unittest.cc b/chromium/ui/base/ime/input_method_auralinux_unittest.cc index 7424f5f5024..2496391504e 100644 --- a/chromium/ui/base/ime/input_method_auralinux_unittest.cc +++ b/chromium/ui/base/ime/input_method_auralinux_unittest.cc @@ -125,6 +125,19 @@ class LinuxInputMethodContextForTesting : public LinuxInputMethodContext { cursor_position_ = rect; } + void SetSurroundingText(const base::string16& text, + const gfx::Range& selection_range) override { + TestResult::GetInstance()->RecordAction( + base::ASCIIToUTF16("surroundingtext:") + text); + + std::stringstream rs; + rs << "selectionrangestart:" << selection_range.start(); + std::stringstream re; + re << "selectionrangeend:" << selection_range.end(); + TestResult::GetInstance()->RecordAction(base::ASCIIToUTF16(rs.str())); + TestResult::GetInstance()->RecordAction(base::ASCIIToUTF16(re.str())); + }; + private: LinuxInputMethodContextDelegate* delegate_; std::vector<base::string16> actions_; @@ -158,7 +171,8 @@ class InputMethodDelegateForTesting : public internal::InputMethodDelegate { ~InputMethodDelegateForTesting() override {} ui::EventDispatchDetails DispatchKeyEventPostIME( - ui::KeyEvent* key_event) override { + ui::KeyEvent* key_event, + base::OnceCallback<void(bool)> ack_callback) override { std::string action; switch (key_event->type()) { case ET_KEY_PRESSED: @@ -174,6 +188,7 @@ class InputMethodDelegateForTesting : public internal::InputMethodDelegate { ss << key_event->key_code(); action += std::string(ss.str()); TestResult::GetInstance()->RecordAction(base::ASCIIToUTF16(action)); + CallDispatchKeyEventPostIMEAck(key_event, std::move(ack_callback)); return ui::EventDispatchDetails(); } @@ -187,6 +202,9 @@ class TextInputClientForTesting : public DummyTextInputClient { : DummyTextInputClient(text_input_type){}; base::string16 composition_text; + gfx::Range text_range; + gfx::Range selection_range; + base::string16 surrounding_text; protected: void SetCompositionText(const CompositionText& composition) override { @@ -229,6 +247,22 @@ class TextInputClientForTesting : public DummyTextInputClient { TestResult::GetInstance()->RecordAction(base::ASCIIToUTF16("keypress:") + base::ASCIIToUTF16(ss.str())); } + + bool GetTextRange(gfx::Range* range) const override { + *range = text_range; + return true; + } + bool GetSelectionRange(gfx::Range* range) const override { + *range = selection_range; + return true; + } + bool GetTextFromRange(const gfx::Range& range, + base::string16* text) const override { + if (surrounding_text.empty()) + return false; + *text = surrounding_text.substr(range.GetMin(), range.length()); + return true; + } }; class InputMethodAuraLinuxTest : public testing::Test { @@ -760,5 +794,59 @@ TEST_F(InputMethodAuraLinuxTest, ReleaseKeyTest) { test_result_->Verify(); } +TEST_F(InputMethodAuraLinuxTest, SurroundingText_NoSelectionTest) { + std::unique_ptr<TextInputClientForTesting> client( + new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT)); + input_method_auralinux_->SetFocusedTextInputClient(client.get()); + input_method_auralinux_->OnTextInputTypeChanged(client.get()); + + client->surrounding_text = base::ASCIIToUTF16("abcdef"); + client->text_range = gfx::Range(0, 6); + client->selection_range = gfx::Range(3, 3); + + input_method_auralinux_->OnCaretBoundsChanged(client.get()); + + test_result_->ExpectAction("surroundingtext:abcdef"); + test_result_->ExpectAction("selectionrangestart:3"); + test_result_->ExpectAction("selectionrangeend:3"); + test_result_->Verify(); +} + +TEST_F(InputMethodAuraLinuxTest, SurroundingText_SelectionTest) { + std::unique_ptr<TextInputClientForTesting> client( + new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT)); + input_method_auralinux_->SetFocusedTextInputClient(client.get()); + input_method_auralinux_->OnTextInputTypeChanged(client.get()); + + client->surrounding_text = base::ASCIIToUTF16("abcdef"); + client->text_range = gfx::Range(0, 6); + client->selection_range = gfx::Range(2, 5); + + input_method_auralinux_->OnCaretBoundsChanged(client.get()); + + test_result_->ExpectAction("surroundingtext:abcdef"); + test_result_->ExpectAction("selectionrangestart:2"); + test_result_->ExpectAction("selectionrangeend:5"); + test_result_->Verify(); +} + +TEST_F(InputMethodAuraLinuxTest, SurroundingText_PartialText) { + std::unique_ptr<TextInputClientForTesting> client( + new TextInputClientForTesting(TEXT_INPUT_TYPE_TEXT)); + input_method_auralinux_->SetFocusedTextInputClient(client.get()); + input_method_auralinux_->OnTextInputTypeChanged(client.get()); + + client->surrounding_text = base::ASCIIToUTF16("abcdefghij"); + client->text_range = gfx::Range(5, 10); + client->selection_range = gfx::Range(7, 9); + + input_method_auralinux_->OnCaretBoundsChanged(client.get()); + + test_result_->ExpectAction("surroundingtext:fghij"); + test_result_->ExpectAction("selectionrangestart:7"); + test_result_->ExpectAction("selectionrangeend:9"); + test_result_->Verify(); +} + } // namespace } // namespace ui diff --git a/chromium/ui/base/ime/input_method_base.cc b/chromium/ui/base/ime/input_method_base.cc index df1e94c65b7..76f14cac5af 100644 --- a/chromium/ui/base/ime/input_method_base.cc +++ b/chromium/ui/base/ime/input_method_base.cc @@ -5,6 +5,7 @@ #include "ui/base/ime/input_method_base.h" #include "base/bind.h" +#include "base/bind_helpers.h" #include "base/logging.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" @@ -129,7 +130,8 @@ bool InputMethodBase::GetClientShouldDoLearning() { void InputMethodBase::ShowVirtualKeyboardIfEnabled() { for (InputMethodObserver& observer : observer_list_) observer.OnShowVirtualKeyboardIfEnabled(); - GetInputMethodKeyboardController()->DisplayVirtualKeyboard(); + if (auto* keyboard = GetInputMethodKeyboardController()) + keyboard->DisplayVirtualKeyboard(); } void InputMethodBase::AddObserver(InputMethodObserver* observer) { @@ -142,11 +144,6 @@ void InputMethodBase::RemoveObserver(InputMethodObserver* observer) { InputMethodKeyboardController* InputMethodBase::GetInputMethodKeyboardController() { - if (!keyboard_controller_) { - NOTIMPLEMENTED() << "Using InputMethodKeyboardControllerStub"; - keyboard_controller_ = - std::make_unique<InputMethodKeyboardControllerStub>(); - } return keyboard_controller_.get(); } @@ -165,23 +162,10 @@ void InputMethodBase::OnInputMethodChanged() const { } ui::EventDispatchDetails InputMethodBase::DispatchKeyEventPostIME( - ui::KeyEvent* event) const { - ui::EventDispatchDetails details; - if (delegate_) - details = delegate_->DispatchKeyEventPostIME(event); - return details; -} - -ui::EventDispatchDetails InputMethodBase::DispatchKeyEventPostIME( ui::KeyEvent* event, base::OnceCallback<void(bool)> ack_callback) const { - if (delegate_) { - ui::EventDispatchDetails details = - delegate_->DispatchKeyEventPostIME(event); - if (ack_callback) - std::move(ack_callback).Run(event->stopped_propagation()); - return details; - } + if (delegate_) + return delegate_->DispatchKeyEventPostIME(event, std::move(ack_callback)); if (ack_callback) std::move(ack_callback).Run(false); @@ -235,7 +219,7 @@ std::vector<gfx::Rect> InputMethodBase::GetCompositionBounds( bool InputMethodBase::SendFakeProcessKeyEvent(bool pressed) const { KeyEvent evt(pressed ? ET_KEY_PRESSED : ET_KEY_RELEASED, pressed ? VKEY_PROCESSKEY : VKEY_UNKNOWN, EF_IME_FABRICATED_KEY); - ignore_result(DispatchKeyEventPostIME(&evt)); + ignore_result(DispatchKeyEventPostIME(&evt, base::NullCallback())); return evt.stopped_propagation(); } diff --git a/chromium/ui/base/ime/input_method_base.h b/chromium/ui/base/ime/input_method_base.h index 31fa2742395..afd0342cc42 100644 --- a/chromium/ui/base/ime/input_method_base.h +++ b/chromium/ui/base/ime/input_method_base.h @@ -106,11 +106,6 @@ class UI_BASE_IME_EXPORT InputMethodBase // input type is not TEXT_INPUT_TYPE_NONE. void OnInputMethodChanged() const; - // Convenience method to call delegate_->DispatchKeyEventPostIME(). - // Returns true if the event was processed - ui::EventDispatchDetails DispatchKeyEventPostIME(ui::KeyEvent* event) const - WARN_UNUSED_RESULT; - virtual ui::EventDispatchDetails DispatchKeyEventPostIME( ui::KeyEvent* event, base::OnceCallback<void(bool)> ack_callback) const WARN_UNUSED_RESULT; diff --git a/chromium/ui/base/ime/input_method_chromeos.cc b/chromium/ui/base/ime/input_method_chromeos.cc index 3478bcd0c76..918bd6eca9a 100644 --- a/chromium/ui/base/ime/input_method_chromeos.cc +++ b/chromium/ui/base/ime/input_method_chromeos.cc @@ -474,7 +474,7 @@ void InputMethodChromeOS::PostProcessUnfilteredKeyPressEvent( if (stopped_propagation) { ResetContext(); if (ack_callback) - std::move(ack_callback).Run(false); + std::move(ack_callback).Run(true); // true matches |stopped_propagation|. return; } diff --git a/chromium/ui/base/ime/input_method_chromeos_unittest.cc b/chromium/ui/base/ime/input_method_chromeos_unittest.cc index 6be9f339ffb..5b4a72a861f 100644 --- a/chromium/ui/base/ime/input_method_chromeos_unittest.cc +++ b/chromium/ui/base/ime/input_method_chromeos_unittest.cc @@ -237,10 +237,12 @@ class InputMethodChromeOSTest : public internal::InputMethodDelegate, // Overridden from ui::internal::InputMethodDelegate: ui::EventDispatchDetails DispatchKeyEventPostIME( - ui::KeyEvent* event) override { + ui::KeyEvent* event, + base::OnceCallback<void(bool)> ack_callback) override { dispatched_key_event_ = *event; if (stop_propagation_post_ime_) event->StopPropagation(); + CallDispatchKeyEventPostIMEAck(event, std::move(ack_callback)); return ui::EventDispatchDetails(); } diff --git a/chromium/ui/base/ime/input_method_delegate.cc b/chromium/ui/base/ime/input_method_delegate.cc new file mode 100644 index 00000000000..f2578a51479 --- /dev/null +++ b/chromium/ui/base/ime/input_method_delegate.cc @@ -0,0 +1,22 @@ +// 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/ime/input_method_delegate.h" + +#include "base/callback.h" +#include "ui/events/event.h" + +namespace ui { +namespace internal { + +// static +void InputMethodDelegate::CallDispatchKeyEventPostIMEAck( + KeyEvent* key_event, + base::OnceCallback<void(bool)> ack_callback) { + if (ack_callback) + std::move(ack_callback).Run(key_event->stopped_propagation()); +} + +} // namespace internal +} // namespace ui diff --git a/chromium/ui/base/ime/input_method_delegate.h b/chromium/ui/base/ime/input_method_delegate.h index 7bfa01a242a..4bfae0343ab 100644 --- a/chromium/ui/base/ime/input_method_delegate.h +++ b/chromium/ui/base/ime/input_method_delegate.h @@ -5,13 +5,15 @@ #ifndef UI_BASE_IME_INPUT_METHOD_DELEGATE_H_ #define UI_BASE_IME_INPUT_METHOD_DELEGATE_H_ +#include "base/callback_forward.h" #include "ui/base/ime/ui_base_ime_export.h" -#include "ui/events/event_dispatcher.h" namespace ui { class KeyEvent; +struct EventDispatchDetails; + namespace internal { // An interface implemented by the object that handles events sent back from an @@ -20,10 +22,19 @@ class UI_BASE_IME_EXPORT InputMethodDelegate { public: virtual ~InputMethodDelegate() {} - // Dispatch a key event already processed by the input method. - // Returns true if the event was processed. - virtual ui::EventDispatchDetails DispatchKeyEventPostIME( - ui::KeyEvent* key_event) = 0; + // Dispatch a key event already processed by the input method. Returns the + // status of processing, as well as running the callback |ack_callback| with + // the result of processing. |ack_callback| may be run asynchronously (if the + // delegate does processing async). |ack_callback| may not be null. + // Subclasses can use CallDispatchKeyEventPostIMEAck() to run the callback. + virtual EventDispatchDetails DispatchKeyEventPostIME( + KeyEvent* key_event, + base::OnceCallback<void(bool)> ack_callback) = 0; + + protected: + static void CallDispatchKeyEventPostIMEAck( + KeyEvent* key_event, + base::OnceCallback<void(bool)> ack_callback); }; } // namespace internal diff --git a/chromium/ui/base/ime/input_method_factory.cc b/chromium/ui/base/ime/input_method_factory.cc index 45546b353fa..2cd9b7911a3 100644 --- a/chromium/ui/base/ime/input_method_factory.cc +++ b/chromium/ui/base/ime/input_method_factory.cc @@ -18,7 +18,9 @@ #include "ui/base/ime/input_method_win_tsf.h" #elif defined(OS_MACOSX) #include "ui/base/ime/input_method_mac.h" -#elif defined(USE_AURA) && defined(USE_X11) +#elif defined(OS_FUCHSIA) +#include "ui/base/ime/input_method_fuchsia.h" +#elif defined(USE_AURA) && (defined(USE_X11) || defined(USE_OZONE)) #include "ui/base/ime/input_method_auralinux.h" #else #include "ui/base/ime/input_method_minimal.h" @@ -62,7 +64,9 @@ std::unique_ptr<InputMethod> CreateInputMethod( return std::make_unique<InputMethodWinImm32>(delegate, widget); #elif defined(OS_MACOSX) return std::make_unique<InputMethodMac>(delegate); -#elif defined(USE_AURA) && defined(USE_X11) +#elif defined(OS_FUCHSIA) + return std::make_unique<InputMethodFuchsia>(delegate); +#elif defined(USE_AURA) && (defined(USE_X11) || defined(USE_OZONE)) return std::make_unique<InputMethodAuraLinux>(delegate); #else return std::make_unique<InputMethodMinimal>(delegate); diff --git a/chromium/ui/base/ime/input_method_fuchsia.cc b/chromium/ui/base/ime/input_method_fuchsia.cc new file mode 100644 index 00000000000..83f18d4df01 --- /dev/null +++ b/chromium/ui/base/ime/input_method_fuchsia.cc @@ -0,0 +1,66 @@ +// 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/ime/input_method_fuchsia.h" + +#include <fuchsia/ui/input/cpp/fidl.h> +#include <memory> +#include <utility> + +#include "base/bind_helpers.h" +#include "base/fuchsia/component_context.h" +#include "ui/base/ime/fuchsia/input_method_keyboard_controller_fuchsia.h" +#include "ui/base/ime/text_input_client.h" + +namespace ui { + +InputMethodFuchsia::InputMethodFuchsia(internal::InputMethodDelegate* delegate) + : InputMethodBase(delegate) { + virtual_keyboard_controller_ = + std::make_unique<InputMethodKeyboardControllerFuchsia>(this); +} + +InputMethodFuchsia::~InputMethodFuchsia() {} + +InputMethodKeyboardController* +InputMethodFuchsia::GetInputMethodKeyboardController() { + return virtual_keyboard_controller_.get(); +} + +void InputMethodFuchsia::DispatchEvent(ui::Event* event) { + DCHECK(event->IsKeyEvent()); + DispatchKeyEvent(event->AsKeyEvent()); +} + +ui::EventDispatchDetails InputMethodFuchsia::DispatchKeyEvent( + ui::KeyEvent* event) { + DCHECK(event->type() == ET_KEY_PRESSED || event->type() == ET_KEY_RELEASED); + + // If no text input client, do nothing. + if (!GetTextInputClient()) + return DispatchKeyEventPostIME(event, base::NullCallback()); + + // Insert the character. + ui::EventDispatchDetails dispatch_details = + DispatchKeyEventPostIME(event, base::NullCallback()); + if (!event->stopped_propagation() && !dispatch_details.dispatcher_destroyed && + event->type() == ET_KEY_PRESSED && GetTextInputClient()) { + const uint16_t ch = event->GetCharacter(); + if (ch) { + GetTextInputClient()->InsertChar(*event); + event->StopPropagation(); + } + } + return dispatch_details; +} + +void InputMethodFuchsia::OnCaretBoundsChanged(const TextInputClient* client) {} + +void InputMethodFuchsia::CancelComposition(const TextInputClient* client) {} + +bool InputMethodFuchsia::IsCandidatePopupOpen() const { + return false; +} + +} // namespace ui diff --git a/chromium/ui/base/ime/input_method_fuchsia.h b/chromium/ui/base/ime/input_method_fuchsia.h new file mode 100644 index 00000000000..0c72098c283 --- /dev/null +++ b/chromium/ui/base/ime/input_method_fuchsia.h @@ -0,0 +1,49 @@ +// 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_IME_INPUT_METHOD_FUCHSIA_H_ +#define UI_BASE_IME_INPUT_METHOD_FUCHSIA_H_ + +#include <fuchsia/ui/input/cpp/fidl.h> +#include <memory> + +#include "base/macros.h" +#include "ui/base/ime/input_method_base.h" +#include "ui/base/ime/input_method_delegate.h" +#include "ui/base/ime/ui_base_ime_export.h" +#include "ui/events/fuchsia/input_event_dispatcher_delegate.h" +#include "ui/gfx/native_widget_types.h" + +namespace ui { + +class InputMethodKeyboardControllerFuchsia; + +// Hnadles input from physical keyboards and the IME service. +class UI_BASE_IME_EXPORT InputMethodFuchsia + : public InputMethodBase, + public InputEventDispatcherDelegate { + public: + explicit InputMethodFuchsia(internal::InputMethodDelegate* delegate); + ~InputMethodFuchsia() override; + + // InputMethodBase interface implementation. + InputMethodKeyboardController* GetInputMethodKeyboardController() override; + ui::EventDispatchDetails DispatchKeyEvent(ui::KeyEvent* event) override; + void OnCaretBoundsChanged(const TextInputClient* client) override; + void CancelComposition(const TextInputClient* client) override; + bool IsCandidatePopupOpen() const override; + + private: + // InputEventDispatcherDelegate interface implementation. + void DispatchEvent(ui::Event* event) override; + + std::unique_ptr<InputMethodKeyboardControllerFuchsia> + virtual_keyboard_controller_; + + DISALLOW_COPY_AND_ASSIGN(InputMethodFuchsia); +}; + +} // namespace ui + +#endif // UI_BASE_IME_INPUT_METHOD_FUCHSIA_H_ diff --git a/chromium/ui/base/ime/input_method_mac.mm b/chromium/ui/base/ime/input_method_mac.mm index e42a251afe1..f23bce99cbc 100644 --- a/chromium/ui/base/ime/input_method_mac.mm +++ b/chromium/ui/base/ime/input_method_mac.mm @@ -6,6 +6,8 @@ #import <Cocoa/Cocoa.h> +#include "base/bind_helpers.h" + namespace ui { InputMethodMac::InputMethodMac(internal::InputMethodDelegate* delegate) @@ -16,7 +18,7 @@ InputMethodMac::~InputMethodMac() { ui::EventDispatchDetails InputMethodMac::DispatchKeyEvent(ui::KeyEvent* event) { // This is used on Mac only to dispatch events post-IME. - return DispatchKeyEventPostIME(event); + return DispatchKeyEventPostIME(event, base::NullCallback()); } void InputMethodMac::OnCaretBoundsChanged(const TextInputClient* client) { diff --git a/chromium/ui/base/ime/input_method_minimal.cc b/chromium/ui/base/ime/input_method_minimal.cc index 98b1cd0fe2b..fb008796579 100644 --- a/chromium/ui/base/ime/input_method_minimal.cc +++ b/chromium/ui/base/ime/input_method_minimal.cc @@ -6,6 +6,7 @@ #include <stdint.h> +#include "base/bind_helpers.h" #include "ui/base/ime/text_input_client.h" #include "ui/events/event.h" #include "ui/events/event_constants.h" @@ -23,10 +24,11 @@ ui::EventDispatchDetails InputMethodMinimal::DispatchKeyEvent( // If no text input client, do nothing. if (!GetTextInputClient()) - return DispatchKeyEventPostIME(event); + return DispatchKeyEventPostIME(event, base::NullCallback()); // Insert the character. - ui::EventDispatchDetails dispatch_details = DispatchKeyEventPostIME(event); + ui::EventDispatchDetails dispatch_details = + DispatchKeyEventPostIME(event, base::NullCallback()); if (!event->stopped_propagation() && !dispatch_details.dispatcher_destroyed && event->type() == ET_KEY_PRESSED && GetTextInputClient()) { const uint16_t ch = event->GetCharacter(); diff --git a/chromium/ui/base/ime/input_method_minimal_unittest.cc b/chromium/ui/base/ime/input_method_minimal_unittest.cc index 2b9df961250..9eadb6ab1f7 100644 --- a/chromium/ui/base/ime/input_method_minimal_unittest.cc +++ b/chromium/ui/base/ime/input_method_minimal_unittest.cc @@ -21,9 +21,11 @@ class InputMethodDelegateForTesting : public internal::InputMethodDelegate { ~InputMethodDelegateForTesting() override {} ui::EventDispatchDetails DispatchKeyEventPostIME( - ui::KeyEvent* key_event) override { + ui::KeyEvent* key_event, + base::OnceCallback<void(bool)> ack_callback) override { if (!propagation_post_ime_) key_event->StopPropagation(); + CallDispatchKeyEventPostIMEAck(key_event, std::move(ack_callback)); return ui::EventDispatchDetails(); } diff --git a/chromium/ui/base/ime/input_method_win_base.cc b/chromium/ui/base/ime/input_method_win_base.cc index 046f0d05268..333607e76ea 100644 --- a/chromium/ui/base/ime/input_method_win_base.cc +++ b/chromium/ui/base/ime/input_method_win_base.cc @@ -10,6 +10,7 @@ #include "base/auto_reset.h" #include "base/bind.h" +#include "base/bind_helpers.h" #include "base/command_line.h" #include "base/memory/ptr_util.h" #include "base/win/windows_version.h" @@ -498,7 +499,8 @@ ui::EventDispatchDetails InputMethodWinBase::ProcessUnhandledKeyEvent( ui::KeyEvent* event, const std::vector<MSG>* char_msgs) { DCHECK(event); - ui::EventDispatchDetails details = DispatchKeyEventPostIME(event); + ui::EventDispatchDetails details = + DispatchKeyEventPostIME(event, base::NullCallback()); if (details.dispatcher_destroyed || details.target_destroyed || event->stopped_propagation()) { return details; diff --git a/chromium/ui/base/ime/input_method_win_imm32.cc b/chromium/ui/base/ime/input_method_win_imm32.cc index 162cf77e59b..723408b20bb 100644 --- a/chromium/ui/base/ime/input_method_win_imm32.cc +++ b/chromium/ui/base/ime/input_method_win_imm32.cc @@ -89,6 +89,7 @@ bool InputMethodWinImm32::OnUntranslatedIMEMessage( void InputMethodWinImm32::OnTextInputTypeChanged( const TextInputClient* client) { + InputMethodBase::OnTextInputTypeChanged(client); if (!IsTextInputClientFocused(client) || !IsWindowFocused(client)) return; imm32_manager_.CancelIME(toplevel_window_handle_); diff --git a/chromium/ui/base/ime/input_method_win_tsf.cc b/chromium/ui/base/ime/input_method_win_tsf.cc index 5e723f12e56..0bc1842527e 100644 --- a/chromium/ui/base/ime/input_method_win_tsf.cc +++ b/chromium/ui/base/ime/input_method_win_tsf.cc @@ -79,6 +79,7 @@ bool InputMethodWinTSF::OnUntranslatedIMEMessage( } void InputMethodWinTSF::OnTextInputTypeChanged(const TextInputClient* client) { + InputMethodBase::OnTextInputTypeChanged(client); if (!IsTextInputClientFocused(client) || !IsWindowFocused(client)) return; ui::TSFBridge::GetInstance()->CancelComposition(); diff --git a/chromium/ui/base/ime/linux/BUILD.gn b/chromium/ui/base/ime/linux/BUILD.gn new file mode 100644 index 00000000000..bdbc26b7df1 --- /dev/null +++ b/chromium/ui/base/ime/linux/BUILD.gn @@ -0,0 +1,29 @@ +# 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. + +import("//build/config/jumbo.gni") + +jumbo_component("linux") { + output_name = "ui_base_ime_linux" + sources = [ + "fake_input_method_context.cc", + "fake_input_method_context.h", + "fake_input_method_context_factory.cc", + "fake_input_method_context_factory.h", + "linux_input_method_context.h", + "linux_input_method_context_factory.cc", + "linux_input_method_context_factory.h", + "ui_base_ime_linux_export.h", + ] + + defines = [ "UI_BASE_IME_LINUX_IMPLEMENTATION" ] + + deps = [ + "//base", + ] + + public_deps = [ + "//ui/base/ime:ime_types", + ] +} diff --git a/chromium/ui/base/ime/linux/fake_input_method_context.cc b/chromium/ui/base/ime/linux/fake_input_method_context.cc index 5053aa1cbc4..c6c0479897d 100644 --- a/chromium/ui/base/ime/linux/fake_input_method_context.cc +++ b/chromium/ui/base/ime/linux/fake_input_method_context.cc @@ -27,4 +27,9 @@ void FakeInputMethodContext::Blur() { void FakeInputMethodContext::SetCursorLocation(const gfx::Rect& rect) { } +void FakeInputMethodContext::SetSurroundingText( + const base::string16& text, + const gfx::Range& selection_range) { +} + } // namespace ui diff --git a/chromium/ui/base/ime/linux/fake_input_method_context.h b/chromium/ui/base/ime/linux/fake_input_method_context.h index ec9ac7e445a..0fd8fdf548a 100644 --- a/chromium/ui/base/ime/linux/fake_input_method_context.h +++ b/chromium/ui/base/ime/linux/fake_input_method_context.h @@ -21,6 +21,8 @@ class FakeInputMethodContext : public LinuxInputMethodContext { void Focus() override; void Blur() override; void SetCursorLocation(const gfx::Rect& rect) override; + void SetSurroundingText(const base::string16& text, + const gfx::Range& selection_range) override; private: DISALLOW_COPY_AND_ASSIGN(FakeInputMethodContext); diff --git a/chromium/ui/base/ime/linux/fake_input_method_context_factory.h b/chromium/ui/base/ime/linux/fake_input_method_context_factory.h index 4d57b96f79e..12bb7661b1e 100644 --- a/chromium/ui/base/ime/linux/fake_input_method_context_factory.h +++ b/chromium/ui/base/ime/linux/fake_input_method_context_factory.h @@ -12,7 +12,8 @@ namespace ui { // An implementation of LinuxInputMethodContextFactory, which creates and // returns FakeInputMethodContext's. -class FakeInputMethodContextFactory : public LinuxInputMethodContextFactory { +class UI_BASE_IME_LINUX_EXPORT FakeInputMethodContextFactory + : public LinuxInputMethodContextFactory { public: FakeInputMethodContextFactory(); diff --git a/chromium/ui/base/ime/linux/linux_input_method_context.h b/chromium/ui/base/ime/linux/linux_input_method_context.h index 4611501d062..ca80623fbb6 100644 --- a/chromium/ui/base/ime/linux/linux_input_method_context.h +++ b/chromium/ui/base/ime/linux/linux_input_method_context.h @@ -6,12 +6,13 @@ #define UI_BASE_IME_LINUX_LINUX_INPUT_METHOD_CONTEXT_H_ #include "base/strings/string16.h" +#include "ui/base/ime/linux/ui_base_ime_linux_export.h" #include "ui/base/ime/text_input_type.h" -#include "ui/base/ime/ui_base_ime_export.h" namespace gfx { class Rect; -} +class Range; +} // namespace gfx namespace ui { @@ -20,7 +21,7 @@ class KeyEvent; // An interface of input method context for input method frameworks on // GNU/Linux and likes. -class UI_BASE_IME_EXPORT LinuxInputMethodContext { +class UI_BASE_IME_LINUX_EXPORT LinuxInputMethodContext { public: virtual ~LinuxInputMethodContext() {} @@ -33,6 +34,10 @@ class UI_BASE_IME_EXPORT LinuxInputMethodContext { // client window rect. virtual void SetCursorLocation(const gfx::Rect& rect) = 0; + // Tells the system IME the surrounding text around the cursor location. + virtual void SetSurroundingText(const base::string16& text, + const gfx::Range& selection_range) = 0; + // Resets the context. A client needs to call OnTextInputTypeChanged() again // before calling DispatchKeyEvent(). virtual void Reset() = 0; @@ -45,13 +50,16 @@ class UI_BASE_IME_EXPORT LinuxInputMethodContext { }; // An interface of callback functions called from LinuxInputMethodContext. -class UI_BASE_IME_EXPORT LinuxInputMethodContextDelegate { +class UI_BASE_IME_LINUX_EXPORT LinuxInputMethodContextDelegate { public: virtual ~LinuxInputMethodContextDelegate() {} // Commits the |text| to the text input client. virtual void OnCommit(const base::string16& text) = 0; + // Deletes the surrounding text at |index| for given |length|. + virtual void OnDeleteSurroundingText(int32_t index, uint32_t length) = 0; + // Sets the composition text to the text input client. virtual void OnPreeditChanged(const CompositionText& composition_text) = 0; diff --git a/chromium/ui/base/ime/linux/linux_input_method_context_factory.h b/chromium/ui/base/ime/linux/linux_input_method_context_factory.h index 65d873a02ae..2429067d660 100644 --- a/chromium/ui/base/ime/linux/linux_input_method_context_factory.h +++ b/chromium/ui/base/ime/linux/linux_input_method_context_factory.h @@ -7,7 +7,7 @@ #include <memory> -#include "ui/base/ime/ui_base_ime_export.h" +#include "ui/base/ime/linux/ui_base_ime_linux_export.h" namespace ui { @@ -17,7 +17,7 @@ class LinuxInputMethodContextDelegate; // An interface that lets different Linux platforms override the // CreateInputMethodContext function declared here to return native input method // contexts. -class UI_BASE_IME_EXPORT LinuxInputMethodContextFactory { +class UI_BASE_IME_LINUX_EXPORT LinuxInputMethodContextFactory { public: // Returns the current active factory or NULL. static const LinuxInputMethodContextFactory* instance(); diff --git a/chromium/ui/base/ime/linux/ui_base_ime_linux_export.h b/chromium/ui/base/ime/linux/ui_base_ime_linux_export.h new file mode 100644 index 00000000000..e59f303866b --- /dev/null +++ b/chromium/ui/base/ime/linux/ui_base_ime_linux_export.h @@ -0,0 +1,34 @@ +// 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_IME_LINUX_UI_BASE_IME_LINUX_EXPORT_H_ +#define UI_BASE_IME_LINUX_UI_BASE_IME_LINUX_EXPORT_H_ + +#if defined(COMPONENT_BUILD) + +#if defined(WIN32) + +#if defined(UI_BASE_IME_LINUX_IMPLEMENTATION) +#define UI_BASE_IME_LINUX_EXPORT __declspec(dllexport) +#else +#define UI_BASE_IME_LINUX_EXPORT __declspec(dllimport) +#endif + +#else // !defined(WIN32) + +#if defined(UI_BASE_IME_LINUX_IMPLEMENTATION) +#define UI_BASE_IME_LINUX_EXPORT __attribute__((visibility("default"))) +#else +#define UI_BASE_IME_LINUX_EXPORT +#endif + +#endif + +#else // !defined(COMPONENT_BUILD) + +#define UI_BASE_IME_LINUX_EXPORT + +#endif + +#endif // UI_BASE_IME_LINUX_UI_BASE_IME_LINUX_EXPORT_H_ diff --git a/chromium/ui/base/ime/mock_input_method.cc b/chromium/ui/base/ime/mock_input_method.cc index 09cde9c326b..04ff47bed46 100644 --- a/chromium/ui/base/ime/mock_input_method.cc +++ b/chromium/ui/base/ime/mock_input_method.cc @@ -3,8 +3,10 @@ // found in the LICENSE file. #include "ui/base/ime/mock_input_method.h" -#include "build/build_config.h" +#include "base/bind_helpers.h" +#include "base/callback.h" +#include "build/build_config.h" #include "ui/base/ime/input_method_delegate.h" #include "ui/events/event.h" @@ -43,7 +45,7 @@ TextInputClient* MockInputMethod::GetTextInputClient() const { ui::EventDispatchDetails MockInputMethod::DispatchKeyEvent( ui::KeyEvent* event) { - return delegate_->DispatchKeyEventPostIME(event); + return delegate_->DispatchKeyEventPostIME(event, base::NullCallback()); } void MockInputMethod::OnFocus() { diff --git a/chromium/ui/base/ime/text_input_flags.h b/chromium/ui/base/ime/text_input_flags.h index f2d7c49ec9d..ed3c851107b 100644 --- a/chromium/ui/base/ime/text_input_flags.h +++ b/chromium/ui/base/ime/text_input_flags.h @@ -8,7 +8,7 @@ namespace ui { // Intentionally keep in sync with blink::WebTextInputFlags defined in: -// third_party/WebKit/public/platform/WebTextInputType.h +// third_party/blink/public/platform/web_text_input_type.h enum TextInputFlags { TEXT_INPUT_FLAG_NONE = 0, TEXT_INPUT_FLAG_AUTOCOMPLETE_ON = 1 << 0, diff --git a/chromium/ui/base/ime/ui_base_ime_types_export.h b/chromium/ui/base/ime/ui_base_ime_types_export.h new file mode 100644 index 00000000000..354a8adc3f0 --- /dev/null +++ b/chromium/ui/base/ime/ui_base_ime_types_export.h @@ -0,0 +1,34 @@ +// 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_IME_UI_BASE_IME_TYPES_EXPORT_H_ +#define UI_BASE_IME_UI_BASE_IME_TYPES_EXPORT_H_ + +#if defined(COMPONENT_BUILD) + +#if defined(WIN32) + +#if defined(UI_BASE_IME_TYPES_IMPLEMENTATION) +#define UI_BASE_IME_TYPES_EXPORT __declspec(dllexport) +#else +#define UI_BASE_IME_TYPES_EXPORT __declspec(dllimport) +#endif + +#else // !defined(WIN32) + +#if defined(UI_BASE_IME_TYPES_IMPLEMENTATION) +#define UI_BASE_IME_TYPES_EXPORT __attribute__((visibility("default"))) +#else +#define UI_BASE_IME_TYPES_EXPORT +#endif + +#endif + +#else // !defined(COMPONENT_BUILD) + +#define UI_BASE_IME_TYPES_EXPORT + +#endif + +#endif // UI_BASE_IME_UI_BASE_IME_TYPES_EXPORT_H_ diff --git a/chromium/ui/base/l10n/l10n_util.cc b/chromium/ui/base/l10n/l10n_util.cc index 265c80edd26..506c5c68781 100644 --- a/chromium/ui/base/l10n/l10n_util.cc +++ b/chromium/ui/base/l10n/l10n_util.cc @@ -268,15 +268,7 @@ bool IsLocaleAvailable(const std::string& locale) { if (!l10n_util::IsLocaleSupportedByOS(locale)) return false; - // If the ResourceBundle is not yet initialized, return false to avoid the - // CHECK failure in ResourceBundle::GetSharedInstance(). - if (!ui::ResourceBundle::HasSharedInstance()) - return false; - - // TODO(hshi): make ResourceBundle::LocaleDataPakExists() a static function - // so that this can be invoked without initializing the global instance. - // See crbug.com/230432: CHECK failure in GetUserDataDir(). - return ui::ResourceBundle::GetSharedInstance().LocaleDataPakExists(locale); + return ui::ResourceBundle::LocaleDataPakExists(locale); } #endif diff --git a/chromium/ui/base/l10n/l10n_util_win.cc b/chromium/ui/base/l10n/l10n_util_win.cc index 8acfde020ee..8b3850c2441 100644 --- a/chromium/ui/base/l10n/l10n_util_win.cc +++ b/chromium/ui/base/l10n/l10n_util_win.cc @@ -4,7 +4,6 @@ #include "ui/base/l10n/l10n_util_win.h" -#include <windowsx.h> #include <algorithm> #include <iterator> @@ -22,30 +21,6 @@ namespace { -void AdjustLogFont(const base::string16& font_family, - double font_size_scaler, - double dpi_scale, - LOGFONT* logfont) { - DCHECK(font_size_scaler > 0); - font_size_scaler = std::max(std::min(font_size_scaler, 2.0), 0.7); - // Font metrics are computed in pixels and scale in high-DPI mode. - // Normalized by the DPI scale factor in order to work in DIP with - // Views/Aura. Call with dpi_scale=1 to keep the size in pixels. - font_size_scaler /= dpi_scale; - logfont->lfHeight = static_cast<long>(font_size_scaler * - static_cast<double>(abs(logfont->lfHeight)) + 0.5) * - (logfont->lfHeight > 0 ? 1 : -1); - - // TODO(jungshik): We may want to check the existence of the font. - // If it's not installed, we shouldn't adjust the font. - if (font_family != L"default") { - int name_len = std::min(static_cast<int>(font_family.size()), - LF_FACESIZE -1); - memcpy(logfont->lfFaceName, font_family.data(), name_len * sizeof(WORD)); - logfont->lfFaceName[name_len] = 0; - } -} - class OverrideLocaleHolder { public: OverrideLocaleHolder() {} @@ -113,44 +88,14 @@ bool NeedOverrideDefaultUIFont(base::string16* override_font_family, if ((ui_font_family == L"default" && scaler100 == 100) || ui_font_family.empty()) return false; - if (override_font_family && font_size_scaler) { + + if (override_font_family && ui_font_family != L"default") override_font_family->swap(ui_font_family); + if (font_size_scaler) *font_size_scaler = scaler100 / 100.0; - } return true; } -void AdjustUIFont(LOGFONT* logfont) { - // Use the unforced scale so the font will be normalized to the correct DIP - // value. That way it'll appear the right size when the forced scale is - // applied later (when coverting to pixels). - AdjustUIFontForDIP(display::win::ScreenWin::GetSystemScaleFactor(), logfont); -} - -void AdjustUIFontForDIP(float dpi_scale, LOGFONT* logfont) { - base::string16 ui_font_family = L"default"; - double ui_font_size_scaler = 1; - if (NeedOverrideDefaultUIFont(&ui_font_family, &ui_font_size_scaler) || - dpi_scale != 1) { - AdjustLogFont(ui_font_family, ui_font_size_scaler, dpi_scale, logfont); - } -} - -void AdjustUIFontForWindow(HWND hwnd) { - base::string16 ui_font_family; - double ui_font_size_scaler; - if (NeedOverrideDefaultUIFont(&ui_font_family, &ui_font_size_scaler)) { - LOGFONT logfont; - if (GetObject(GetWindowFont(hwnd), sizeof(logfont), &logfont)) { - double dpi_scale = 1; - AdjustLogFont(ui_font_family, ui_font_size_scaler, dpi_scale, &logfont); - HFONT hfont = CreateFontIndirect(&logfont); - if (hfont) - SetWindowFont(hwnd, hfont, FALSE); - } - } -} - void OverrideLocaleWithUILanguageList() { std::vector<base::string16> ui_languages; if (base::win::i18n::GetThreadPreferredUILanguageList(&ui_languages)) { diff --git a/chromium/ui/base/l10n/l10n_util_win.h b/chromium/ui/base/l10n/l10n_util_win.h index 75bfe5db03a..a245ec91764 100644 --- a/chromium/ui/base/l10n/l10n_util_win.h +++ b/chromium/ui/base/l10n/l10n_util_win.h @@ -44,25 +44,6 @@ UI_BASE_EXPORT bool NeedOverrideDefaultUIFont( base::string16* override_font_family, double* font_size_scaler); -// If the default UI font stored in |logfont| is not suitable, its family -// and size are replaced with those stored in the per-locale resource. The -// font size is adjusted based on the font scale setting in the OS preferences. -// Windows 8 supports scale factors of 1, 1.4 and 1.8. -UI_BASE_EXPORT void AdjustUIFont(LOGFONT* logfont); - -// If the default UI font stored in |logfont| is not suitable, its family -// and size are replaced with those stored in the per-locale resource. The -// |dpi_scale| is the ratio of the OS setting for dots per inch relative to the -// baseline of 96 DPI. This ratio is also used for converting sizes from -// device independent pixels (DIP) to physical pixels. The font size is scaled -// for use with Views and Aura which work in DIP. -UI_BASE_EXPORT void AdjustUIFontForDIP(float dpi_scale, LOGFONT* logfont); - -// If the font for a given window (pointed to by HWND) is not suitable for the -// UI in the current UI langauge, its family and size are replaced with those -// stored in the per-locale resource. -UI_BASE_EXPORT void AdjustUIFontForWindow(HWND hwnd); - // Allow processes to override the configured locale with the user's Windows UI // languages. This function should generally be called once early in // Application startup. @@ -70,7 +51,7 @@ UI_BASE_EXPORT void OverrideLocaleWithUILanguageList(); // Retrieve the locale override, or an empty vector if the locale has not been // or failed to be overridden. -const std::vector<std::string>& GetLocaleOverrides(); +UI_BASE_EXPORT const std::vector<std::string>& GetLocaleOverrides(); } // namespace l10n_util diff --git a/chromium/ui/base/l10n/l10n_util_win_unittest.cc b/chromium/ui/base/l10n/l10n_util_win_unittest.cc deleted file mode 100644 index 3dfa8533722..00000000000 --- a/chromium/ui/base/l10n/l10n_util_win_unittest.cc +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) 2013 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/l10n/l10n_util_win.h" - -#include <windows.h> - -#include "base/command_line.h" -#include "base/win/win_client_metrics.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "testing/platform_test.h" -#include "ui/display/display.h" -#include "ui/display/display_switches.h" -#include "ui/display/win/screen_win.h" - -typedef PlatformTest L10nUtilWinTest; - -TEST_F(L10nUtilWinTest, TestDPIScaling) { - // Baseline font for comparison. - NONCLIENTMETRICS_XP metrics; - base::win::GetNonClientMetrics(&metrics); - LOGFONT lf = metrics.lfMessageFont; - l10n_util::AdjustUIFont(&lf); - int size = lf.lfHeight; - float rounding = size < 0 ? -0.5f : 0.5f; - - // Test that font size is properly normalized for DIP. In high-DPI mode, the - // font metrics are scaled based on the DPI scale factor. For Windows 8, 140% - // and 180% font scaling are supported. Simulate size normalization for a DPI- - // aware process by manually scaling up the font and checking that it returns - // to the expected size. - lf.lfHeight = static_cast<int>(1.4 * size + rounding); - l10n_util::AdjustUIFontForDIP(1.4f, &lf); - EXPECT_NEAR(size, lf.lfHeight, 1); - - lf.lfHeight = static_cast<int>(1.8 * size + rounding); - l10n_util::AdjustUIFontForDIP(1.8f, &lf); - EXPECT_NEAR(size, lf.lfHeight, 1); -} - -// Test for crbug.com/675933. Since font size is a Windows metric we need to -// normalize to DIPs based on the real device scale factor, not the forced one. -TEST_F(L10nUtilWinTest, TestForcedScaling) { - base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( - switches::kForceDeviceScaleFactor, "4"); - display::Display::ResetForceDeviceScaleFactorForTesting(); - - NONCLIENTMETRICS_XP metrics; - base::win::GetNonClientMetrics(&metrics); - LOGFONT lf = metrics.lfMessageFont; - - // Set the font size to an arbitrary value and make sure it comes through - // correctly on the other end. - constexpr int test_font_size = 18; - lf.lfHeight = test_font_size; - l10n_util::AdjustUIFont(&lf); - EXPECT_EQ(test_font_size / display::win::ScreenWin::GetSystemScaleFactor(), - lf.lfHeight); -} diff --git a/chromium/ui/base/material_design/material_design_controller.cc b/chromium/ui/base/material_design/material_design_controller.cc index caf62d06d79..d23a4df67be 100644 --- a/chromium/ui/base/material_design/material_design_controller.cc +++ b/chromium/ui/base/material_design/material_design_controller.cc @@ -9,10 +9,13 @@ #include "base/command_line.h" #include "base/feature_list.h" #include "base/logging.h" +#include "base/no_destructor.h" +#include "base/observer_list.h" #include "base/strings/string_number_conversions.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" #include "build/buildflag.h" +#include "ui/base/material_design/material_design_controller_observer.h" #include "ui/base/ui_base_features.h" #include "ui/base/ui_base_switches.h" #include "ui/base/ui_features.h" @@ -39,19 +42,6 @@ namespace ui { namespace { -#if defined(OS_CHROMEOS) || defined(OS_WIN) || defined(OS_LINUX) - -// Whether Material Refresh should be used by default. -// Material refresh is controlled by both --top-chrome-md and this feature. -// --top-chrome-md should take precedence over what this feature may indicate. -bool IsMaterialRefreshEnabled() { - static constexpr base::Feature kMaterialRefreshEnabledFeature = { - "MaterialRefresh", base::FEATURE_ENABLED_BY_DEFAULT}; - return base::FeatureList::IsEnabled(kMaterialRefreshEnabledFeature); -} - -#endif - #if defined(OS_CHROMEOS) // Whether to use touchable UI. @@ -60,18 +50,11 @@ const base::Feature kTouchOptimizedUi = {"TouchOptimizedUi", base::FEATURE_DISABLED_BY_DEFAULT}; MaterialDesignController::Mode GetDefaultTouchDeviceMode() { - bool material_refresh_enabled = IsMaterialRefreshEnabled(); bool touch_optimized_ui_enabled = base::FeatureList::IsEnabled(kTouchOptimizedUi); - if (material_refresh_enabled) { - return touch_optimized_ui_enabled - ? MaterialDesignController::MATERIAL_TOUCH_REFRESH - : MaterialDesignController::MATERIAL_REFRESH; - } - return touch_optimized_ui_enabled - ? MaterialDesignController::MATERIAL_TOUCH_OPTIMIZED - : MaterialDesignController::MATERIAL_HYBRID; + ? MaterialDesignController::MATERIAL_TOUCH_REFRESH + : MaterialDesignController::MATERIAL_REFRESH; } bool HasTouchscreen() { @@ -107,45 +90,27 @@ bool MaterialDesignController::is_mode_initialized_ = false; MaterialDesignController::Mode MaterialDesignController::mode_ = MaterialDesignController::MATERIAL_NORMAL; +bool MaterialDesignController::is_refresh_dynamic_ui_ = false; + // static void MaterialDesignController::Initialize() { TRACE_EVENT0("startup", "MaterialDesignController::InitializeMode"); CHECK(!is_mode_initialized_); + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); const std::string switch_value = command_line->GetSwitchValueASCII(switches::kTopChromeMD); - bool force_material_refresh = false; -#if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_LINUX) - force_material_refresh = - base::FeatureList::IsEnabled(features::kExperimentalUi); -#endif - if (switch_value == switches::kTopChromeMDMaterialRefresh) { SetMode(MATERIAL_REFRESH); } else if (switch_value == switches::kTopChromeMDMaterialRefreshTouchOptimized) { SetMode(MATERIAL_TOUCH_REFRESH); - } else if (force_material_refresh) { - bool has_touchscreen = false; -#if defined(OS_CHROMEOS) - has_touchscreen = HasTouchscreen(); -#endif - SetMode(has_touchscreen ? MATERIAL_TOUCH_REFRESH : MATERIAL_REFRESH); - } else if (switch_value == switches::kTopChromeMDMaterial) { - SetMode(MATERIAL_NORMAL); - } else if (switch_value == switches::kTopChromeMDMaterialHybrid) { - SetMode(MATERIAL_HYBRID); - } else if (switch_value == switches::kTopChromeMDMaterialTouchOptimized) { - SetMode(MATERIAL_TOUCH_OPTIMIZED); - } else if (switch_value == switches::kTopChromeMDMaterialAuto) { -#if defined(OS_WIN) - // TODO(girard): add support for switching between modes when - // the device switches to "tablet mode". - if (base::win::IsTabletDevice(nullptr, ui::GetHiddenWindow())) - SetMode(MATERIAL_HYBRID); -#endif - SetMode(DefaultMode()); + } else if (switch_value == switches::kTopChromeMDMaterialRefreshDynamic) { + is_refresh_dynamic_ui_ = true; + + // TabletModeClient's default state is in non-tablet mode. + SetMode(MATERIAL_REFRESH); } else { if (!switch_value.empty()) { LOG(ERROR) << "Invalid value='" << switch_value @@ -158,8 +123,7 @@ void MaterialDesignController::Initialize() { // Ideally, there would be a more general, "initialize random stuff here" // function into which these things and a call to this function can be placed. // TODO(crbug.com/864544) - if (IsRefreshUi()) - color_utils::SetDarkestColor(gfx::kGoogleGrey900); + color_utils::SetDarkestColor(gfx::kGoogleGrey900); double animation_duration_scale; if (base::StringToDouble( @@ -182,16 +146,6 @@ bool MaterialDesignController::IsTouchOptimizedUiEnabled() { } // static -bool MaterialDesignController::IsNewerMaterialUi() { - return IsTouchOptimizedUiEnabled() || IsRefreshUi(); -} - -// static -bool MaterialDesignController::IsRefreshUi() { - return GetMode() == MATERIAL_REFRESH || GetMode() == MATERIAL_TOUCH_REFRESH; -} - -// static MaterialDesignController::Mode MaterialDesignController::DefaultMode() { #if defined(OS_CHROMEOS) // This is called (once) early in device startup to initialize core UI, so @@ -199,28 +153,48 @@ MaterialDesignController::Mode MaterialDesignController::DefaultMode() { base::ScopedAllowBlocking allow_io; if (HasTouchscreen()) return GetDefaultTouchDeviceMode(); - - return IsMaterialRefreshEnabled() ? MATERIAL_REFRESH : MATERIAL_NORMAL; #endif // defined(OS_CHROMEOS) -#if defined(OS_WIN) || defined(OS_LINUX) - return IsMaterialRefreshEnabled() ? MATERIAL_REFRESH : MATERIAL_NORMAL; -#elif defined(OS_MACOSX) && BUILDFLAG(MAC_VIEWS_BROWSER) - return features::IsViewsBrowserCocoa() ? MATERIAL_NORMAL : MATERIAL_REFRESH; -#else - return MATERIAL_NORMAL; -#endif + return MATERIAL_REFRESH; +} + +// static +void MaterialDesignController::OnTabletModeToggled(bool enabled) { + if (is_refresh_dynamic_ui_) + SetMode(enabled ? MATERIAL_TOUCH_REFRESH : MATERIAL_REFRESH); } // static +MaterialDesignController* MaterialDesignController::GetInstance() { + static base::NoDestructor<MaterialDesignController> instance; + return instance.get(); +} + +void MaterialDesignController::AddObserver( + MaterialDesignControllerObserver* observer) { + observers_.AddObserver(observer); +} + +void MaterialDesignController::RemoveObserver( + MaterialDesignControllerObserver* observer) { + observers_.RemoveObserver(observer); +} + +MaterialDesignController::MaterialDesignController() = default; + +// static void MaterialDesignController::Uninitialize() { is_mode_initialized_ = false; } // static void MaterialDesignController::SetMode(MaterialDesignController::Mode mode) { - mode_ = mode; - is_mode_initialized_ = true; + if (!is_mode_initialized_ || mode_ != mode) { + is_mode_initialized_ = true; + mode_ = mode; + for (auto& observer : GetInstance()->observers_) + observer.OnMdModeChanged(); + } } } // namespace ui diff --git a/chromium/ui/base/material_design/material_design_controller.h b/chromium/ui/base/material_design/material_design_controller.h index 4605134569e..d66f2624b43 100644 --- a/chromium/ui/base/material_design/material_design_controller.h +++ b/chromium/ui/base/material_design/material_design_controller.h @@ -6,10 +6,18 @@ #define UI_BASE_MATERIAL_DESIGN_MATERIAL_DESIGN_CONTROLLER_H_ #include "base/macros.h" +#include "base/observer_list.h" #include "ui/base/ui_base_export.h" +namespace base { +template <typename T> +class NoDestructor; +} + namespace ui { +class MaterialDesignControllerObserver; + namespace test { class MaterialDesignControllerTestAPI; } // namespace test @@ -41,31 +49,27 @@ class UI_BASE_EXPORT MaterialDesignController { // Returns true if the touch-optimized UI material design mode is enabled. static bool IsTouchOptimizedUiEnabled(); - // Returns true if the Material Refresh or touch-optimized UI is enabled. - static bool IsNewerMaterialUi(); - - // Returns true if any Material Refresh mode is enabled. - static bool IsRefreshUi(); - // Returns the per-platform default material design variant. static Mode DefaultMode(); + // Exposed for TabletModeClient on ChromeOS + ash. + static void OnTabletModeToggled(bool enabled); + static bool is_mode_initialized() { return is_mode_initialized_; } - private: - friend class test::MaterialDesignControllerTestAPI; + static MaterialDesignController* GetInstance(); - // Tracks whether |mode_| has been initialized. This is necessary to avoid - // checking the |mode_| early in initialization before a call to Initialize(). - // Tests can use it to reset the state back to a clean state during tear down. - static bool is_mode_initialized_; + void AddObserver(MaterialDesignControllerObserver* observer); - // The current Mode to be used by the system. - static Mode mode_; + void RemoveObserver(MaterialDesignControllerObserver* observer); + + private: + friend class base::NoDestructor<MaterialDesignController>; + friend class test::MaterialDesignControllerTestAPI; - // Declarations only. Do not allow construction of an object. MaterialDesignController(); - ~MaterialDesignController(); + + ~MaterialDesignController() = delete; // Resets the initialization state to uninitialized. To be used by tests to // allow calling Initialize() more than once. @@ -75,6 +79,20 @@ class UI_BASE_EXPORT MaterialDesignController { // used by tests to directly set the mode. static void SetMode(Mode mode); + // Tracks whether |mode_| has been initialized. This is necessary to avoid + // checking the |mode_| early in initialization before a call to Initialize(). + // Tests can use it to reset the state back to a clean state during tear down. + static bool is_mode_initialized_; + + // The current Mode to be used by the system. + static Mode mode_; + + // Whether |mode_| should toggle between MATERIAL_REFRESH and + // MATERIAL_TOUCH_REFRESH depending on the tablet state. + static bool is_refresh_dynamic_ui_; + + base::ObserverList<MaterialDesignControllerObserver> observers_; + DISALLOW_COPY_AND_ASSIGN(MaterialDesignController); }; diff --git a/chromium/ui/base/material_design/material_design_controller_observer.h b/chromium/ui/base/material_design/material_design_controller_observer.h new file mode 100644 index 00000000000..5b7eba4bbc5 --- /dev/null +++ b/chromium/ui/base/material_design/material_design_controller_observer.h @@ -0,0 +1,24 @@ +// 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_MATERIAL_DESIGN_MATERIAL_DESIGN_CONTROLLER_OBSERVER_H_ +#define UI_BASE_MATERIAL_DESIGN_MATERIAL_DESIGN_CONTROLLER_OBSERVER_H_ + +#include "base/observer_list_types.h" +#include "ui/base/ui_base_export.h" + +namespace ui { + +class UI_BASE_EXPORT MaterialDesignControllerObserver + : public base::CheckedObserver { + public: + virtual void OnMdModeChanged() = 0; + + protected: + ~MaterialDesignControllerObserver() override {} +}; + +} // namespace ui + +#endif // UI_BASE_MATERIAL_DESIGN_MATERIAL_DESIGN_CONTROLLER_OBSERVER_H_ diff --git a/chromium/ui/base/material_design/material_design_controller_unittest.cc b/chromium/ui/base/material_design/material_design_controller_unittest.cc index 6da42af5445..c5a9075033f 100644 --- a/chromium/ui/base/material_design/material_design_controller_unittest.cc +++ b/chromium/ui/base/material_design/material_design_controller_unittest.cc @@ -6,137 +6,180 @@ #include "base/command_line.h" #include "base/macros.h" +#include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/base/material_design/material_design_controller.h" +#include "ui/base/material_design/material_design_controller_observer.h" #include "ui/base/test/material_design_controller_test_api.h" #include "ui/base/ui_base_switches.h" namespace ui { + +TEST(MaterialDesignControllerDeathTest, CrashesWithoutInitialization) { + ASSERT_FALSE(MaterialDesignController::is_mode_initialized()); + EXPECT_DEATH_IF_SUPPORTED( + MaterialDesignController::IsTouchOptimizedUiEnabled(), ""); +} + namespace { // Test fixture for the MaterialDesignController class. class MaterialDesignControllerTest : public testing::Test { public: - MaterialDesignControllerTest(); - ~MaterialDesignControllerTest() override; + MaterialDesignControllerTest() = default; + ~MaterialDesignControllerTest() override = default; protected: // testing::Test: - void SetUp() override; - void TearDown() override; - void SetCommandLineSwitch(const std::string& value_string); + void SetUp() override { + testing::Test::SetUp(); + MaterialDesignController::Initialize(); + } + + void TearDown() override { + test::MaterialDesignControllerTestAPI::Uninitialize(); + testing::Test::TearDown(); + } + + void SetCommandLineSwitch(const std::string& value_string) { + base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( + switches::kTopChromeMD, value_string); + } private: DISALLOW_COPY_AND_ASSIGN(MaterialDesignControllerTest); }; -MaterialDesignControllerTest::MaterialDesignControllerTest() { -} - -MaterialDesignControllerTest::~MaterialDesignControllerTest() { -} - -void MaterialDesignControllerTest::SetUp() { - testing::Test::SetUp(); - MaterialDesignController::Initialize(); -} +} // namespace -void MaterialDesignControllerTest::TearDown() { - test::MaterialDesignControllerTestAPI::Uninitialize(); - testing::Test::TearDown(); +#if !defined(OS_CHROMEOS) +// Verify that non-touch is the default. +TEST_F(MaterialDesignControllerTest, NoCommandLineFlagIsRefresh) { + ASSERT_FALSE(base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kTopChromeMD)); + EXPECT_FALSE(MaterialDesignController::IsTouchOptimizedUiEnabled()); } +#endif -void MaterialDesignControllerTest::SetCommandLineSwitch( - const std::string& value_string) { - base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( - switches::kTopChromeMD, value_string); -} +namespace { -class MaterialDesignControllerTestMaterial : - public MaterialDesignControllerTest { +class MaterialDesignControllerTestCommandLineRefresh + : public MaterialDesignControllerTest { public: - MaterialDesignControllerTestMaterial() { - SetCommandLineSwitch("material"); + MaterialDesignControllerTestCommandLineRefresh() { + SetCommandLineSwitch(switches::kTopChromeMDMaterialRefresh); } private: - DISALLOW_COPY_AND_ASSIGN(MaterialDesignControllerTestMaterial); + DISALLOW_COPY_AND_ASSIGN(MaterialDesignControllerTestCommandLineRefresh); }; -class MaterialDesignControllerTestHybrid : public MaterialDesignControllerTest { - public: - MaterialDesignControllerTestHybrid() { - SetCommandLineSwitch("material-hybrid"); - } +} // namespace - private: - DISALLOW_COPY_AND_ASSIGN(MaterialDesignControllerTestHybrid); -}; +// Verify switches::kTopChromeMDMaterialRefresh maps to non-touch (the default). +TEST_F(MaterialDesignControllerTestCommandLineRefresh, CheckApiReturns) { + EXPECT_FALSE(MaterialDesignController::IsTouchOptimizedUiEnabled()); +} + +namespace { -class MaterialDesignControllerTestDefault : - public MaterialDesignControllerTest { +class MaterialDesignControllerTestCommandLineForceTouchRefresh + : public MaterialDesignControllerTest { public: - MaterialDesignControllerTestDefault() { - SetCommandLineSwitch(std::string()); + MaterialDesignControllerTestCommandLineForceTouchRefresh() { + SetCommandLineSwitch(switches::kTopChromeMDMaterialRefreshTouchOptimized); } private: - DISALLOW_COPY_AND_ASSIGN(MaterialDesignControllerTestDefault); + DISALLOW_COPY_AND_ASSIGN( + MaterialDesignControllerTestCommandLineForceTouchRefresh); }; -class MaterialDesignControllerTestInvalid : - public MaterialDesignControllerTest { +} // namespace + +// Verify switches::kTopChromeMDMaterialRefreshTouchOptimized maps to touch. +TEST_F(MaterialDesignControllerTestCommandLineForceTouchRefresh, + CheckApiReturns) { + EXPECT_TRUE(MaterialDesignController::IsTouchOptimizedUiEnabled()); +} + +namespace { + +class TestObserver : public ui::MaterialDesignControllerObserver { public: - MaterialDesignControllerTestInvalid() { - const std::string kInvalidValue = "1nvalid-valu3"; - SetCommandLineSwitch(kInvalidValue); - } + TestObserver() = default; + ~TestObserver() override = default; + + bool on_md_mode_changed_called() { return on_md_mode_changed_called_; } private: - DISALLOW_COPY_AND_ASSIGN(MaterialDesignControllerTestInvalid); + // ui::MaterialDesignControllerObserver: + void OnMdModeChanged() override { on_md_mode_changed_called_ = true; } + + bool on_md_mode_changed_called_ = false; + + DISALLOW_COPY_AND_ASSIGN(TestObserver); }; -// Verify command line value "material" maps to Mode::MATERIAL when the compile -// time flag is defined. -TEST_F(MaterialDesignControllerTestMaterial, - EnabledCommandLineValueMapsToMaterialModeWhenCompileTimeFlagEnabled) { - EXPECT_EQ(MaterialDesignController::Mode::MATERIAL_NORMAL, - MaterialDesignController::GetMode()); -} +} // namespace -// Verify command line value "material-hybrid" maps to Mode::MATERIAL_HYBRID -// when the compile time flag is defined. -TEST_F( - MaterialDesignControllerTestHybrid, - EnabledHybridCommandLineValueMapsToMaterialHybridModeWhenCompileTimeFlagEnabled) { - EXPECT_EQ(MaterialDesignController::Mode::MATERIAL_HYBRID, - MaterialDesignController::GetMode()); -} +TEST(MaterialDesignControllerObserver, InitializationOnMdModeChanged) { + // Verifies that the MaterialDesignControllerObserver gets called back when + // the mode changes at startup. + TestObserver observer; + ASSERT_FALSE(observer.on_md_mode_changed_called()); + MaterialDesignController::GetInstance()->AddObserver(&observer); -// Verify command line value "" maps to the default mode when the compile time -// flag is defined. -TEST_F( - MaterialDesignControllerTestDefault, - DisabledCommandLineValueMapsToNonMaterialModeWhenCompileTimeFlagEnabled) { - EXPECT_EQ(MaterialDesignController::DefaultMode(), - MaterialDesignController::GetMode()); -} + // Trigger a mode change by setting it for the first time. + MaterialDesignController::Initialize(); -// Verify the current mode is reported as the default mode when no command line -// flag is defined. -TEST_F(MaterialDesignControllerTest, - NoCommandLineValueMapsToNonMaterialModeWhenCompileTimeFlagEnabled) { - ASSERT_FALSE(base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kTopChromeMD)); - EXPECT_EQ(MaterialDesignController::DefaultMode(), - MaterialDesignController::GetMode()); + EXPECT_TRUE(observer.on_md_mode_changed_called()); + + test::MaterialDesignControllerTestAPI::Uninitialize(); + MaterialDesignController::GetInstance()->RemoveObserver(&observer); } -// Verify an invalid command line value uses the default mode. -TEST_F(MaterialDesignControllerTestInvalid, InvalidCommandLineValue) { - EXPECT_EQ(MaterialDesignController::DefaultMode(), - MaterialDesignController::GetMode()); +TEST(MaterialDesignControllerObserver, TabletOnMdModeChanged) { + // Verifies that the MaterialDesignControllerObserver gets called back when + // the tablet mode toggles. + test::MaterialDesignControllerTestAPI::SetDynamicRefreshUi(true); + + MaterialDesignController::Initialize(); + TestObserver tablet_enabled_observer; + MaterialDesignController::GetInstance()->AddObserver( + &tablet_enabled_observer); + ASSERT_FALSE(tablet_enabled_observer.on_md_mode_changed_called()); + +#if !defined(OS_CHROMEOS) + EXPECT_FALSE(MaterialDesignController::IsTouchOptimizedUiEnabled()); +#endif + + MaterialDesignController::OnTabletModeToggled(true); + + EXPECT_TRUE(MaterialDesignController::IsTouchOptimizedUiEnabled()); + + EXPECT_TRUE(tablet_enabled_observer.on_md_mode_changed_called()); + + TestObserver tablet_disabled_observer; + MaterialDesignController::GetInstance()->AddObserver( + &tablet_disabled_observer); + ASSERT_FALSE(tablet_disabled_observer.on_md_mode_changed_called()); + + MaterialDesignController::OnTabletModeToggled(false); + +#if !defined(OS_CHROMEOS) + EXPECT_FALSE(MaterialDesignController::IsTouchOptimizedUiEnabled()); +#endif + + EXPECT_TRUE(tablet_disabled_observer.on_md_mode_changed_called()); + + test::MaterialDesignControllerTestAPI::Uninitialize(); + MaterialDesignController::GetInstance()->RemoveObserver( + &tablet_disabled_observer); + MaterialDesignController::GetInstance()->RemoveObserver( + &tablet_enabled_observer); + + test::MaterialDesignControllerTestAPI::SetDynamicRefreshUi(false); } -} // namespace } // namespace ui diff --git a/chromium/ui/base/models/list_selection_model.cc b/chromium/ui/base/models/list_selection_model.cc index 1456c0f4f01..d8e23ba1340 100644 --- a/chromium/ui/base/models/list_selection_model.cc +++ b/chromium/ui/base/models/list_selection_model.cc @@ -76,8 +76,7 @@ bool ListSelectionModel::operator!=(const ListSelectionModel& other) const { void ListSelectionModel::IncrementFrom(int index) { // Shift the selection to account for a newly inserted item at |index|. - for (SelectedIndices::iterator i = selected_indices_.begin(); - i != selected_indices_.end(); ++i) { + for (auto i = selected_indices_.begin(); i != selected_indices_.end(); ++i) { IncrementFromImpl(index, &(*i)); } IncrementFromImpl(index, &anchor_); @@ -85,8 +84,7 @@ void ListSelectionModel::IncrementFrom(int index) { } void ListSelectionModel::DecrementFrom(int index) { - for (SelectedIndices::iterator i = selected_indices_.begin(); - i != selected_indices_.end(); ) { + for (auto i = selected_indices_.begin(); i != selected_indices_.end();) { if (DecrementFromImpl(index, &(*i))) i = selected_indices_.erase(i); else @@ -115,8 +113,7 @@ void ListSelectionModel::AddIndexToSelection(int index) { } void ListSelectionModel::RemoveIndexFromSelection(int index) { - SelectedIndices::iterator i = std::find(selected_indices_.begin(), - selected_indices_.end(), index); + auto i = std::find(selected_indices_.begin(), selected_indices_.end(), index); if (i != selected_indices_.end()) selected_indices_.erase(i); } diff --git a/chromium/ui/base/models/simple_menu_model.cc b/chromium/ui/base/models/simple_menu_model.cc index 65435087568..5561c413a29 100644 --- a/chromium/ui/base/models/simple_menu_model.cc +++ b/chromium/ui/base/models/simple_menu_model.cc @@ -295,7 +295,7 @@ void SimpleMenuModel::Clear() { } int SimpleMenuModel::GetIndexOfCommandId(int command_id) const { - for (ItemVector::const_iterator i = items_.begin(); i != items_.end(); ++i) { + for (auto i = items_.begin(); i != items_.end(); ++i) { if (i->command_id == command_id) return static_cast<int>(std::distance(items_.begin(), i)); } @@ -306,7 +306,7 @@ int SimpleMenuModel::GetIndexOfCommandId(int command_id) const { // SimpleMenuModel, MenuModel implementation: bool SimpleMenuModel::HasIcons() const { - for (ItemVector::const_iterator i = items_.begin(); i != items_.end(); ++i) { + for (auto i = items_.begin(); i != items_.end(); ++i) { if (!i->icon.IsEmpty()) return true; } diff --git a/chromium/ui/base/mojo/clipboard_host.h b/chromium/ui/base/mojo/clipboard_host.h index bab35e05ed9..b36e591e82a 100644 --- a/chromium/ui/base/mojo/clipboard_host.h +++ b/chromium/ui/base/mojo/clipboard_host.h @@ -58,7 +58,7 @@ class ClipboardHost : public mojom::ClipboardHost { void WriteBitmap(const SkBitmap& bitmap) override; void WriteData(const std::string& type, const std::string& data) override; void CommitWrite(ClipboardType type) override; -#if defined(OS_MACOSX) +#if defined(OS_MACOSX) && !defined(OS_IOS) void WriteStringToFindPboard(const base::string16& text) override; #endif diff --git a/chromium/ui/base/mojo/ui_base_types.mojom b/chromium/ui/base/mojo/ui_base_types.mojom index e2b12c00164..82bc476b639 100644 --- a/chromium/ui/base/mojo/ui_base_types.mojom +++ b/chromium/ui/base/mojo/ui_base_types.mojom @@ -4,6 +4,13 @@ module ui.mojom; +// Dialog button identifiers used to specify which buttons to show the user. +enum DialogButton { + NONE, + OK, + CANCEL, +}; + // Specifies the type of modality applied to a window. Different modal // treatments may be handled differently by the window manager. enum ModalType { diff --git a/chromium/ui/base/mojo/ui_base_types.typemap b/chromium/ui/base/mojo/ui_base_types.typemap index a8a3b6f2643..9157ed1fa08 100644 --- a/chromium/ui/base/mojo/ui_base_types.typemap +++ b/chromium/ui/base/mojo/ui_base_types.typemap @@ -9,4 +9,7 @@ public_deps = [ "//ui/base", ] traits_headers = [ "//ui/base/mojo/ui_base_types_struct_traits.h" ] -type_mappings = [ "ui.mojom.ModalType=ui::ModalType" ] +type_mappings = [ + "ui.mojom.DialogButton=ui::DialogButton", + "ui.mojom.ModalType=ui::ModalType", +] diff --git a/chromium/ui/base/mojo/ui_base_types_struct_traits.h b/chromium/ui/base/mojo/ui_base_types_struct_traits.h index 55d57df2d56..35bf814112e 100644 --- a/chromium/ui/base/mojo/ui_base_types_struct_traits.h +++ b/chromium/ui/base/mojo/ui_base_types_struct_traits.h @@ -12,6 +12,41 @@ namespace mojo { template <> +struct EnumTraits<ui::mojom::DialogButton, ui::DialogButton> { + static ui::mojom::DialogButton ToMojom(ui::DialogButton modal_type) { + switch (modal_type) { + case ui::DIALOG_BUTTON_NONE: + return ui::mojom::DialogButton::NONE; + case ui::DIALOG_BUTTON_OK: + return ui::mojom::DialogButton::OK; + case ui::DIALOG_BUTTON_CANCEL: + return ui::mojom::DialogButton::CANCEL; + default: + NOTREACHED(); + return ui::mojom::DialogButton::NONE; + } + } + + static bool FromMojom(ui::mojom::DialogButton modal_type, + ui::DialogButton* out) { + switch (modal_type) { + case ui::mojom::DialogButton::NONE: + *out = ui::DIALOG_BUTTON_NONE; + return true; + case ui::mojom::DialogButton::OK: + *out = ui::DIALOG_BUTTON_OK; + return true; + case ui::mojom::DialogButton::CANCEL: + *out = ui::DIALOG_BUTTON_CANCEL; + return true; + default: + NOTREACHED(); + return false; + } + } +}; + +template <> struct EnumTraits<ui::mojom::ModalType, ui::ModalType> { static ui::mojom::ModalType ToMojom(ui::ModalType modal_type) { switch (modal_type) { diff --git a/chromium/ui/base/resource/resource_bundle.cc b/chromium/ui/base/resource/resource_bundle.cc index 2a00d4e851a..d6d21eeb8ef 100644 --- a/chromium/ui/base/resource/resource_bundle.cc +++ b/chromium/ui/base/resource/resource_bundle.cc @@ -242,23 +242,12 @@ void ResourceBundle::LoadSecondaryLocaleDataWithPakFileRegion( } #if !defined(OS_ANDROID) +// static bool ResourceBundle::LocaleDataPakExists(const std::string& locale) { return !GetLocaleFilePath(locale, true).empty(); } #endif // !defined(OS_ANDROID) -void ResourceBundle::AddDataPack(std::unique_ptr<DataPack> data_pack) { -#if DCHECK_IS_ON() - data_pack->CheckForDuplicateResources(data_packs_); -#endif - - if (GetScaleForScaleFactor(data_pack->GetScaleFactor()) > - GetScaleForScaleFactor(max_scale_factor_)) - max_scale_factor_ = data_pack->GetScaleFactor(); - - data_packs_.push_back(std::move(data_pack)); -} - void ResourceBundle::AddDataPackFromPath(const base::FilePath& path, ScaleFactor scale_factor) { AddDataPackFromPathInternal(path, scale_factor, false); @@ -300,6 +289,7 @@ void ResourceBundle::AddDataPackFromFileRegion( } #if !defined(OS_MACOSX) +// static base::FilePath ResourceBundle::GetLocaleFilePath(const std::string& app_locale, bool test_file_exists) { if (app_locale.empty()) @@ -328,9 +318,14 @@ base::FilePath ResourceBundle::GetLocaleFilePath(const std::string& app_locale, #endif } - if (delegate_) { - locale_file_path = - delegate_->GetPathForLocalePack(locale_file_path, app_locale); + // Note: The delegate GetPathForLocalePack() override is currently only used + // by CastResourceDelegate, which does not call this function prior to + // initializing the ResourceBundle. This called earlier than that by the + // variations code which also has a CHECK that an inconsistent value does not + // get returned via VariationsService::EnsureLocaleEquals(). + if (HasSharedInstance() && GetSharedInstance().delegate_) { + locale_file_path = GetSharedInstance().delegate_->GetPathForLocalePack( + locale_file_path, app_locale); } // Don't try to load empty values or values that are not absolute paths. @@ -402,9 +397,9 @@ void ResourceBundle::OverrideLocalePakForTest(const base::FilePath& pak_path) { } void ResourceBundle::OverrideLocaleStringResource( - int message_id, + int resource_id, const base::string16& string) { - overridden_locale_strings_[message_id] = string; + overridden_locale_strings_[resource_id] = string; } const base::FilePath& ResourceBundle::GetOverriddenPakPath() { @@ -556,62 +551,16 @@ base::StringPiece ResourceBundle::GetRawDataResourceForScale( return base::StringPiece(); } -base::string16 ResourceBundle::GetLocalizedString(int message_id) { - base::string16 string; - if (delegate_ && delegate_->GetLocalizedString(message_id, &string)) - return MaybeMangleLocalizedString(string); - - // Ensure that ReloadLocaleResources() doesn't drop the resources while - // we're using them. - base::AutoLock lock_scope(*locale_resources_data_lock_); - - IdToStringMap::const_iterator it = - overridden_locale_strings_.find(message_id); - if (it != overridden_locale_strings_.end()) - return MaybeMangleLocalizedString(it->second); - - // If for some reason we were unable to load the resources , return an empty - // string (better than crashing). - if (!locale_resources_data_.get()) { - LOG(WARNING) << "locale resources are not loaded"; - return base::string16(); - } - - base::StringPiece data; - ResourceHandle::TextEncodingType encoding = - locale_resources_data_->GetTextEncodingType(); - if (!locale_resources_data_->GetStringPiece(static_cast<uint16_t>(message_id), - &data)) { - if (secondary_locale_resources_data_.get() && - secondary_locale_resources_data_->GetStringPiece( - static_cast<uint16_t>(message_id), &data)) { - // Fall back on the secondary locale pak if it exists. - encoding = secondary_locale_resources_data_->GetTextEncodingType(); - } else { - // Fall back on the main data pack (shouldn't be any strings here except - // in unittests). - data = GetRawDataResource(message_id); - if (data.empty()) { - LOG(WARNING) << "unable to find resource: " << message_id; - NOTREACHED(); - return base::string16(); - } - } - } - - // Strings should not be loaded from a data pack that contains binary data. - DCHECK(encoding == ResourceHandle::UTF16 || encoding == ResourceHandle::UTF8) - << "requested localized string from binary pack file"; - - // Data pack encodes strings as either UTF8 or UTF16. - base::string16 msg; - if (encoding == ResourceHandle::UTF16) { - msg = base::string16(reinterpret_cast<const base::char16*>(data.data()), - data.length() / 2); - } else if (encoding == ResourceHandle::UTF8) { - msg = base::UTF8ToUTF16(data); +base::string16 ResourceBundle::GetLocalizedString(int resource_id) { +#if DCHECK_IS_ON() + { + base::AutoLock lock_scope(*locale_resources_data_lock_); + // Overriding locale strings isn't supported if the first string resource + // has already been queried. + can_override_locale_string_resources_ = false; } - return MaybeMangleLocalizedString(msg); +#endif + return GetLocalizedStringImpl(resource_id); } base::RefCountedMemory* ResourceBundle::LoadLocalizedResourceBytes( @@ -733,6 +682,13 @@ bool ResourceBundle::IsScaleFactorSupported(ScaleFactor scale_factor) { return base::ContainsValue(supported_scale_factors, scale_factor); } +void ResourceBundle::CheckCanOverrideStringResources() { +#if DCHECK_IS_ON() + base::AutoLock lock_scope(*locale_resources_data_lock_); + DCHECK(can_override_locale_string_resources_); +#endif +} + ResourceBundle::ResourceBundle(Delegate* delegate) : delegate_(delegate), locale_resources_data_lock_(new base::Lock), @@ -750,7 +706,7 @@ ResourceBundle::~ResourceBundle() { void ResourceBundle::InitSharedInstance(Delegate* delegate) { DCHECK(g_shared_instance_ == NULL) << "ResourceBundle initialized twice"; g_shared_instance_ = new ResourceBundle(delegate); - static std::vector<ScaleFactor> supported_scale_factors; + std::vector<ScaleFactor> supported_scale_factors; #if defined(OS_IOS) display::Display display = display::Screen::GetScreen()->GetPrimaryDisplay(); if (display.device_scale_factor() > 2.0) { @@ -817,10 +773,26 @@ void ResourceBundle::AddDataPackFromPathInternal( } } +void ResourceBundle::AddDataPack(std::unique_ptr<DataPack> data_pack) { +#if DCHECK_IS_ON() + data_pack->CheckForDuplicateResources(data_packs_); +#endif + + if (GetScaleForScaleFactor(data_pack->GetScaleFactor()) > + GetScaleForScaleFactor(max_scale_factor_)) + max_scale_factor_ = data_pack->GetScaleFactor(); + + data_packs_.push_back(std::move(data_pack)); +} + void ResourceBundle::InitDefaultFontList() { #if defined(OS_CHROMEOS) - std::string font_family = base::UTF16ToUTF8( - GetLocalizedString(IDS_UI_FONT_FAMILY_CROS)); + // InitDefaultFontList() is called earlier than overriding the locale strings. + // So we call the |GetLocalizedStringImpl()| which doesn't set the flag + // |can_override_locale_string_resources_| to false. This is okay, because the + // font list doesn't need to be overridden by variations. + std::string font_family = + base::UTF16ToUTF8(GetLocalizedStringImpl(IDS_UI_FONT_FAMILY_CROS)); gfx::FontList::SetDefaultFontDescription(font_family); // TODO(yukishiino): Remove SetDefaultFontDescription() once the migration to @@ -907,6 +879,64 @@ gfx::Image& ResourceBundle::GetEmptyImage() { return empty_image_; } +base::string16 ResourceBundle::GetLocalizedStringImpl(int resource_id) { + base::string16 string; + if (delegate_ && delegate_->GetLocalizedString(resource_id, &string)) + return MaybeMangleLocalizedString(string); + + // Ensure that ReloadLocaleResources() doesn't drop the resources while + // we're using them. + base::AutoLock lock_scope(*locale_resources_data_lock_); + + IdToStringMap::const_iterator it = + overridden_locale_strings_.find(resource_id); + if (it != overridden_locale_strings_.end()) + return MaybeMangleLocalizedString(it->second); + + // If for some reason we were unable to load the resources , return an empty + // string (better than crashing). + if (!locale_resources_data_.get()) { + LOG(WARNING) << "locale resources are not loaded"; + return base::string16(); + } + + base::StringPiece data; + ResourceHandle::TextEncodingType encoding = + locale_resources_data_->GetTextEncodingType(); + if (!locale_resources_data_->GetStringPiece( + static_cast<uint16_t>(resource_id), &data)) { + if (secondary_locale_resources_data_.get() && + secondary_locale_resources_data_->GetStringPiece( + static_cast<uint16_t>(resource_id), &data)) { + // Fall back on the secondary locale pak if it exists. + encoding = secondary_locale_resources_data_->GetTextEncodingType(); + } else { + // Fall back on the main data pack (shouldn't be any strings here except + // in unittests). + data = GetRawDataResource(resource_id); + if (data.empty()) { + LOG(WARNING) << "unable to find resource: " << resource_id; + NOTREACHED(); + return base::string16(); + } + } + } + + // Strings should not be loaded from a data pack that contains binary data. + DCHECK(encoding == ResourceHandle::UTF16 || encoding == ResourceHandle::UTF8) + << "requested localized string from binary pack file"; + + // Data pack encodes strings as either UTF8 or UTF16. + base::string16 msg; + if (encoding == ResourceHandle::UTF16) { + msg = base::string16(reinterpret_cast<const base::char16*>(data.data()), + data.length() / 2); + } else if (encoding == ResourceHandle::UTF8) { + msg = base::UTF8ToUTF16(data); + } + return MaybeMangleLocalizedString(msg); +} + // static bool ResourceBundle::PNGContainsFallbackMarker(const unsigned char* buf, size_t size) { diff --git a/chromium/ui/base/resource/resource_bundle.h b/chromium/ui/base/resource/resource_bundle.h index 422d84bc266..c24fcf0ba3c 100644 --- a/chromium/ui/base/resource/resource_bundle.h +++ b/chromium/ui/base/resource/resource_bundle.h @@ -156,11 +156,7 @@ class UI_BASE_EXPORT ResourceBundle { const base::MemoryMappedFile::Region& region); // Check if the .pak for the given locale exists. - bool LocaleDataPakExists(const std::string& locale); - - // Inserts |data_pack| to |data_pack_| and updates |max_scale_factor_| - // accordingly. - void AddDataPack(std::unique_ptr<DataPack> data_pack); + static bool LocaleDataPakExists(const std::string& locale); // Registers additional data pack files with this ResourceBundle. When // looking for a DataResource, we will search these files after searching the @@ -241,9 +237,9 @@ class UI_BASE_EXPORT ResourceBundle { base::StringPiece GetRawDataResourceForScale(int resource_id, ScaleFactor scale_factor) const; - // Get a localized string given a message id. Returns an empty - // string if the message_id is not found. - base::string16 GetLocalizedString(int message_id); + // Get a localized string given a message id. Returns an empty string if the + // resource_id is not found. + base::string16 GetLocalizedString(int resource_id); // Get a localized resource (for example, localized image logo) given a // resource id. @@ -276,19 +272,19 @@ class UI_BASE_EXPORT ResourceBundle { // Overrides a localized string resource with the given string. If no delegate // is present, the |string| will be returned when getting the localized string - // |message_id|. If |ReloadLocaleResources| is called, all overrides are + // |resource_id|. If |ReloadLocaleResources| is called, all overrides are // cleared. This is intended to be used in conjunction with field trials and // the variations service to experiment with different UI strings. This method // is not thread safe! - void OverrideLocaleStringResource(int message_id, + void OverrideLocaleStringResource(int resource_id, const base::string16& string); // Returns the full pathname of the locale file to load. May return an empty // string if no locale data files are found and |test_file_exists| is true. // Used on Android to load the local file in the browser process and pass it // to the sandboxed renderer process. - base::FilePath GetLocaleFilePath(const std::string& app_locale, - bool test_file_exists); + static base::FilePath GetLocaleFilePath(const std::string& app_locale, + bool test_file_exists); // Returns the maximum scale factor currently loaded. // Returns SCALE_FACTOR_100P if no resource is loaded. @@ -297,11 +293,22 @@ class UI_BASE_EXPORT ResourceBundle { // Returns true if |scale_factor| is supported by this platform. static bool IsScaleFactorSupported(ScaleFactor scale_factor); + // Checks whether overriding locale strings is supported. This will fail with + // a DCHECK if the first string resource has already been queried. + void CheckCanOverrideStringResources(); + // Sets whether this ResourceBundle should mangle localized strings or not. void set_mangle_localized_strings_for_test(bool mangle) { mangle_localized_strings_ = mangle; } +#if DCHECK_IS_ON() + // Gets whether overriding locale strings is supported. + bool get_can_override_locale_string_resources_for_test() { + return can_override_locale_string_resources_; + } +#endif + private: FRIEND_TEST_ALL_PREFIXES(ResourceBundleTest, DelegateGetPathForLocalePack); FRIEND_TEST_ALL_PREFIXES(ResourceBundleTest, DelegateGetImageNamed); @@ -341,6 +348,10 @@ class UI_BASE_EXPORT ResourceBundle { ScaleFactor scale_factor, bool optional); + // Inserts |data_pack| to |data_pack_| and updates |max_scale_factor_| + // accordingly. + void AddDataPack(std::unique_ptr<DataPack> data_pack); + // Try to load the locale specific strings from an external data module. // Returns the locale that is loaded. std::string LoadLocaleResources(const std::string& pref_locale); @@ -404,6 +415,13 @@ class UI_BASE_EXPORT ResourceBundle { // visible and returns the mangled string. If not, returns |str|. base::string16 MaybeMangleLocalizedString(const base::string16& str); + // An internal implementation of |GetLocalizedString()| without setting the + // flag of whether overriding locale strings is supported to false. We don't + // update this flag only in |InitDefaultFontList()| which is called earlier + // than the overriding. This is okay, because the font list doesn't need to be + // overridden by variations. + base::string16 GetLocalizedStringImpl(int resource_id); + // This pointer is guaranteed to outlive the ResourceBundle instance and may // be NULL. Delegate* delegate_; @@ -436,6 +454,10 @@ class UI_BASE_EXPORT ResourceBundle { IdToStringMap overridden_locale_strings_; +#if DCHECK_IS_ON() + bool can_override_locale_string_resources_ = true; +#endif + bool is_test_resources_ = false; bool mangle_localized_strings_ = false; diff --git a/chromium/ui/base/resource/resource_bundle_android.cc b/chromium/ui/base/resource/resource_bundle_android.cc index 6ce54d115ad..5fdfaeae0e7 100644 --- a/chromium/ui/base/resource/resource_bundle_android.cc +++ b/chromium/ui/base/resource/resource_bundle_android.cc @@ -11,6 +11,7 @@ #include "base/path_service.h" #include "jni/ResourceBundle_jni.h" #include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/data_pack.h" #include "ui/base/resource/resource_bundle.h" #include "ui/base/ui_base_paths.h" @@ -91,6 +92,7 @@ void ResourceBundle::LoadCommonResources() { } } +// static bool ResourceBundle::LocaleDataPakExists(const std::string& locale) { if (g_locale_paks_in_apk) { return !GetPathForAndroidLocalePakWithinApk(locale).empty(); @@ -175,21 +177,6 @@ void LoadMainAndroidPackFile(const char* path_within_apk, } } -std::unique_ptr<DataPack> GetDataPackFromPackFile( - const char* path_within_apk, - const base::FilePath& disk_file_path) { - if (LoadFromApkOrFile(path_within_apk, &disk_file_path, &g_resources_pack_fd, - &g_resources_pack_region)) { - std::unique_ptr<DataPack> data_pack = - std::make_unique<DataPack>(SCALE_FACTOR_NONE); - if (data_pack->LoadFromFileRegion(base::File(g_resources_pack_fd), - g_resources_pack_region)) { - return data_pack; - } - } - return nullptr; -} - int GetMainAndroidPackFd(base::MemoryMappedFile::Region* out_region) { DCHECK_GE(g_resources_pack_fd, 0); *out_region = g_resources_pack_region; diff --git a/chromium/ui/base/resource/resource_bundle_android.h b/chromium/ui/base/resource/resource_bundle_android.h index 2913731131f..57059b0ecbc 100644 --- a/chromium/ui/base/resource/resource_bundle_android.h +++ b/chromium/ui/base/resource/resource_bundle_android.h @@ -9,7 +9,6 @@ #include <string> #include "base/files/memory_mapped_file.h" -#include "ui/base/resource/data_pack.h" #include "ui/base/ui_base_export.h" namespace ui { @@ -20,10 +19,6 @@ UI_BASE_EXPORT void LoadMainAndroidPackFile( const char* path_within_apk, const base::FilePath& disk_file_path); -UI_BASE_EXPORT std::unique_ptr<DataPack> GetDataPackFromPackFile( - const char* path_within_apk, - const base::FilePath& disk_file_path); - // Returns the file descriptor and region for resources.pak. UI_BASE_EXPORT int GetMainAndroidPackFd( base::MemoryMappedFile::Region* out_region); diff --git a/chromium/ui/base/resource/resource_bundle_ios.mm b/chromium/ui/base/resource/resource_bundle_ios.mm index 74d9a97c700..02ba7fc1616 100644 --- a/chromium/ui/base/resource/resource_bundle_ios.mm +++ b/chromium/ui/base/resource/resource_bundle_ios.mm @@ -59,6 +59,7 @@ void ResourceBundle::LoadCommonResources() { } } +// static base::FilePath ResourceBundle::GetLocaleFilePath(const std::string& app_locale, bool test_file_exists) { NSString* mac_locale = base::SysUTF8ToNSString(app_locale); @@ -74,9 +75,9 @@ base::FilePath ResourceBundle::GetLocaleFilePath(const std::string& app_locale, base::FilePath locale_file_path = GetResourcesPakFilePath(@"locale", mac_locale); - if (delegate_) { - locale_file_path = - delegate_->GetPathForLocalePack(locale_file_path, app_locale); + if (HasSharedInstance() && GetSharedInstance().delegate_) { + locale_file_path = GetSharedInstance().delegate_->GetPathForLocalePack( + locale_file_path, app_locale); } // Don't try to load empty values or values that are not absolute paths. diff --git a/chromium/ui/base/resource/resource_bundle_mac.mm b/chromium/ui/base/resource/resource_bundle_mac.mm index 3cf1847860a..873989c5017 100644 --- a/chromium/ui/base/resource/resource_bundle_mac.mm +++ b/chromium/ui/base/resource/resource_bundle_mac.mm @@ -59,6 +59,7 @@ void ResourceBundle::LoadCommonResources() { } } +// static base::FilePath ResourceBundle::GetLocaleFilePath(const std::string& app_locale, bool test_file_exists) { NSString* mac_locale = base::SysUTF8ToNSString(app_locale); @@ -74,9 +75,9 @@ base::FilePath ResourceBundle::GetLocaleFilePath(const std::string& app_locale, base::FilePath locale_file_path = GetResourcesPakFilePath(@"locale", mac_locale); - if (delegate_) { - locale_file_path = - delegate_->GetPathForLocalePack(locale_file_path, app_locale); + if (HasSharedInstance() && GetSharedInstance().delegate_) { + locale_file_path = GetSharedInstance().delegate_->GetPathForLocalePack( + locale_file_path, app_locale); } // Don't try to load empty values or values that are not absolute paths. diff --git a/chromium/ui/base/resource/resource_bundle_unittest.cc b/chromium/ui/base/resource/resource_bundle_unittest.cc index 363a0e9d03d..1d61db04987 100644 --- a/chromium/ui/base/resource/resource_bundle_unittest.cc +++ b/chromium/ui/base/resource/resource_bundle_unittest.cc @@ -92,8 +92,7 @@ void AddCustomChunk(const base::StringPiece& custom_chunk, bitmap_data->begin(), bitmap_data->begin() + arraysize(kPngMagic), kPngMagic)); - std::vector<unsigned char>::iterator ihdr_start = - bitmap_data->begin() + arraysize(kPngMagic); + auto ihdr_start = bitmap_data->begin() + arraysize(kPngMagic); char ihdr_length_data[sizeof(uint32_t)]; for (size_t i = 0; i < sizeof(uint32_t); ++i) ihdr_length_data[i] = *(ihdr_start + i); @@ -135,8 +134,7 @@ void CreateDataPackWithSingleBitmap(const base::FilePath& path, class ResourceBundleTest : public testing::Test { public: - ResourceBundleTest() : resource_bundle_(NULL) { - } + ResourceBundleTest() : resource_bundle_(nullptr) {} ~ResourceBundleTest() override {} @@ -184,27 +182,31 @@ TEST_F(ResourceBundleTest, DelegateGetPathForResourcePack) { #define MAYBE_DelegateGetPathForLocalePack DelegateGetPathForLocalePack #endif TEST_F(ResourceBundleTest, MAYBE_DelegateGetPathForLocalePack) { + ResourceBundle::CleanupSharedInstance(); + MockResourceBundleDelegate delegate; - ResourceBundle* resource_bundle = CreateResourceBundle(&delegate); + ResourceBundle::InitSharedInstance(&delegate); std::string locale = "en-US"; // Cancel the load. - EXPECT_CALL(delegate, GetPathForLocalePack(_, locale)) - .Times(2) + EXPECT_CALL(delegate, GetPathForLocalePack(_, _)) .WillRepeatedly(Return(base::FilePath())) .RetiresOnSaturation(); - EXPECT_FALSE(resource_bundle->LocaleDataPakExists(locale)); - EXPECT_EQ("", resource_bundle->LoadLocaleResources(locale)); + EXPECT_FALSE(ResourceBundle::LocaleDataPakExists(locale)); + EXPECT_EQ("", + ResourceBundle::GetSharedInstance().LoadLocaleResources(locale)); // Allow the load to proceed. - EXPECT_CALL(delegate, GetPathForLocalePack(_, locale)) - .Times(2) + EXPECT_CALL(delegate, GetPathForLocalePack(_, _)) .WillRepeatedly(ReturnArg<0>()); - EXPECT_TRUE(resource_bundle->LocaleDataPakExists(locale)); - EXPECT_EQ(locale, resource_bundle->LoadLocaleResources(locale)); + EXPECT_TRUE(ResourceBundle::LocaleDataPakExists(locale)); + EXPECT_EQ(locale, + ResourceBundle::GetSharedInstance().LoadLocaleResources(locale)); + + ResourceBundle::CleanupSharedInstance(); } TEST_F(ResourceBundleTest, DelegateGetImageNamed) { @@ -298,7 +300,7 @@ TEST_F(ResourceBundleTest, DelegateGetLocalizedString) { } TEST_F(ResourceBundleTest, OverrideStringResource) { - ResourceBundle* resource_bundle = CreateResourceBundle(NULL); + ResourceBundle* resource_bundle = CreateResourceBundle(nullptr); base::string16 data = base::ASCIIToUTF16("My test data"); int resource_id = 5; @@ -312,6 +314,21 @@ TEST_F(ResourceBundleTest, OverrideStringResource) { EXPECT_EQ(data, result); } +#if DCHECK_IS_ON() +TEST_F(ResourceBundleTest, CanOverrideStringResources) { + ResourceBundle* resource_bundle = CreateResourceBundle(nullptr); + + base::string16 data = base::ASCIIToUTF16("My test data"); + int resource_id = 5; + + EXPECT_TRUE( + resource_bundle->get_can_override_locale_string_resources_for_test()); + resource_bundle->GetLocalizedString(resource_id); + EXPECT_FALSE( + resource_bundle->get_can_override_locale_string_resources_for_test()); +} +#endif + TEST_F(ResourceBundleTest, DelegateGetLocalizedStringWithOverride) { MockResourceBundleDelegate delegate; ResourceBundle* resource_bundle = CreateResourceBundle(&delegate); @@ -329,11 +346,9 @@ TEST_F(ResourceBundleTest, DelegateGetLocalizedStringWithOverride) { } TEST_F(ResourceBundleTest, LocaleDataPakExists) { - ResourceBundle* resource_bundle = CreateResourceBundle(NULL); - // Check that ResourceBundle::LocaleDataPakExists returns the correct results. - EXPECT_TRUE(resource_bundle->LocaleDataPakExists("en-US")); - EXPECT_FALSE(resource_bundle->LocaleDataPakExists("not_a_real_locale")); + EXPECT_TRUE(ResourceBundle::LocaleDataPakExists("en-US")); + EXPECT_FALSE(ResourceBundle::LocaleDataPakExists("not_a_real_locale")); } class ResourceBundleImageTest : public ResourceBundleTest { @@ -355,7 +370,7 @@ class ResourceBundleImageTest : public ResourceBundleTest { EXPECT_EQ(base::WriteFile(locale_path, kEmptyPakContents, kEmptyPakSize), static_cast<int>(kEmptyPakSize)); - ui::ResourceBundle* resource_bundle = CreateResourceBundle(NULL); + ui::ResourceBundle* resource_bundle = CreateResourceBundle(nullptr); // Load the empty locale data pak. resource_bundle->LoadTestResources(base::FilePath(), locale_path); @@ -392,15 +407,15 @@ TEST_F(ResourceBundleImageTest, LoadDataResourceBytes) { resource_bundle->AddDataPackFromPath(data_path, SCALE_FACTOR_100P); const int kUnfoundResourceId = 10000; - EXPECT_EQ(NULL, resource_bundle->LoadDataResourceBytes( - kUnfoundResourceId)); + EXPECT_EQ(nullptr, + resource_bundle->LoadDataResourceBytes(kUnfoundResourceId)); // Give a .pak file that doesn't exist so we will fail to load it. resource_bundle->AddDataPackFromPath( base::FilePath(FILE_PATH_LITERAL("non-existant-file.pak")), ui::SCALE_FACTOR_NONE); - EXPECT_EQ(NULL, resource_bundle->LoadDataResourceBytes( - kUnfoundResourceId)); + EXPECT_EQ(nullptr, + resource_bundle->LoadDataResourceBytes(kUnfoundResourceId)); } TEST_F(ResourceBundleImageTest, GetRawDataResource) { diff --git a/chromium/ui/base/template_expressions.cc b/chromium/ui/base/template_expressions.cc index 9618dc7b4d7..1aec20cb5b9 100644 --- a/chromium/ui/base/template_expressions.cc +++ b/chromium/ui/base/template_expressions.cc @@ -93,7 +93,7 @@ std::string ReplaceTemplateExpressions( source.substr(current_pos, key_end - current_pos).as_string(); CHECK(!key.empty()); - TemplateReplacements::const_iterator value = replacements.find(key); + auto value = replacements.find(key); CHECK(value != replacements.end()) << "$i18n replacement key \"" << key << "\" not found"; diff --git a/chromium/ui/base/theme_provider.h b/chromium/ui/base/theme_provider.h index 3a19618ce89..514a9ae4545 100644 --- a/chromium/ui/base/theme_provider.h +++ b/chromium/ui/base/theme_provider.h @@ -5,23 +5,10 @@ #ifndef UI_BASE_THEME_PROVIDER_H_ #define UI_BASE_THEME_PROVIDER_H_ -#include "build/build_config.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/base/layout.h" #include "ui/base/ui_base_export.h" -#if defined(OS_MACOSX) -#ifdef __OBJC__ -@class NSColor; -@class NSGradient; -@class NSImage; -#else -class NSColor; -class NSGradient; -class NSImage; -#endif // __OBJC__ -#endif // OS_* - namespace base { class RefCountedMemory; } @@ -80,36 +67,6 @@ class UI_BASE_EXPORT ThemeProvider { virtual base::RefCountedMemory* GetRawData( int id, ui::ScaleFactor scale_factor) const = 0; - -#if defined(OS_MACOSX) - // Whether we're using the system theme (which may or may not be the - // same as the default theme). - // TODO(estade): this should probably just be part of ThemeService and not - // ThemeProvider, but it's used in many places on OSX. - virtual bool UsingSystemTheme() const = 0; - - // Returns whether or not theme is in Incognito mode. - virtual bool InIncognitoMode() const = 0; - - // Gets the NSImage with the specified |id|. - virtual NSImage* GetNSImageNamed(int id) const = 0; - - // Gets the NSImage that GetNSImageNamed (above) would return, but returns it - // as a pattern color. - virtual NSColor* GetNSImageColorNamed(int id) const = 0; - - // Gets the NSColor with the specified |id|. - virtual NSColor* GetNSColor(int id) const = 0; - - // Gets the NSColor for tinting with the specified |id|. - virtual NSColor* GetNSColorTint(int id) const = 0; - - // Gets the NSGradient with the specified |id|. - virtual NSGradient* GetNSGradient(int id) const = 0; - - // Whether the "increase contrast" accessibility setting is enabled. - virtual bool ShouldIncreaseContrast() const = 0; -#endif }; } // namespace ui diff --git a/chromium/ui/base/touch/touch_editing_controller.h b/chromium/ui/base/touch/touch_editing_controller.h index 513835e227a..bfa17bcdc87 100644 --- a/chromium/ui/base/touch/touch_editing_controller.h +++ b/chromium/ui/base/touch/touch_editing_controller.h @@ -92,13 +92,12 @@ class UI_BASE_EXPORT TouchEditingControllerDeprecated { class UI_BASE_EXPORT TouchEditingControllerFactory { public: + virtual ~TouchEditingControllerFactory() {} + static void SetInstance(TouchEditingControllerFactory* instance); virtual TouchEditingControllerDeprecated* Create(TouchEditable* client_view) = 0; - - protected: - virtual ~TouchEditingControllerFactory() {} }; } // namespace ui diff --git a/chromium/ui/base/ui_base_features.cc b/chromium/ui/base/ui_base_features.cc index 95139912ce8..674998b13af 100644 --- a/chromium/ui/base/ui_base_features.cc +++ b/chromium/ui/base/ui_base_features.cc @@ -67,7 +67,7 @@ bool IsNotificationIndicatorEnabled() { // Enables GPU rasterization for all UI drawing (where not blacklisted). const base::Feature kUiGpuRasterization = {"UiGpuRasterization", -#if defined(OS_MACOSX) +#if defined(OS_MACOSX) || defined(OS_CHROMEOS) base::FEATURE_ENABLED_BY_DEFAULT #else base::FEATURE_DISABLED_BY_DEFAULT @@ -153,20 +153,6 @@ const base::Feature kHostWindowsInAppShimProcess{ bool HostWindowsInAppShimProcess() { return base::FeatureList::IsEnabled(kHostWindowsInAppShimProcess); } - -#if BUILDFLAG(MAC_VIEWS_BROWSER) -// Causes Views browser builds to use Views browser windows by default rather -// than Cocoa browser windows. -const base::Feature kViewsBrowserWindows{"ViewsBrowserWindows", - base::FEATURE_ENABLED_BY_DEFAULT}; - -// Returns whether a Views-capable browser build should use the Cocoa browser -// UI. -bool IsViewsBrowserCocoa() { - return !base::FeatureList::IsEnabled(kViewsBrowserWindows) && - !base::FeatureList::IsEnabled(kExperimentalUi); -} -#endif // BUILDFLAG(MAC_VIEWS_BROWSER) #endif // defined(OS_MACOSX) const base::Feature kEnableOzoneDrmMojo = {"OzoneDrmMojo", @@ -177,4 +163,6 @@ bool IsOzoneDrmMojo() { IsMultiProcessMash(); } +const base::Feature kDarkMode = {"DarkMode", base::FEATURE_DISABLED_BY_DEFAULT}; + } // namespace features diff --git a/chromium/ui/base/ui_base_features.h b/chromium/ui/base/ui_base_features.h index c9799a2bda0..4a799dda3c5 100644 --- a/chromium/ui/base/ui_base_features.h +++ b/chromium/ui/base/ui_base_features.h @@ -68,17 +68,11 @@ UI_BASE_EXPORT bool IsMultiProcessMash(); UI_BASE_EXPORT bool IsSingleProcessMash(); #if defined(OS_MACOSX) +UI_BASE_EXPORT extern const base::Feature kHostWindowsInAppShimProcess; + // Returns true if the NSWindows for apps will be created in the app's process, // and will forward input to the browser process. UI_BASE_EXPORT bool HostWindowsInAppShimProcess(); - -#if BUILDFLAG(MAC_VIEWS_BROWSER) -UI_BASE_EXPORT extern const base::Feature kViewsBrowserWindows; - -// Returns whether a Views-capable browser build should use the Cocoa browser -// UI. -UI_BASE_EXPORT bool IsViewsBrowserCocoa(); -#endif // BUILDFLAG(MAC_VIEWS_BROWSER) #endif // defined(OS_MACOSX) // Use mojo communication in the drm platform instead of paramtraits. Remove @@ -88,6 +82,10 @@ UI_BASE_EXPORT bool IsViewsBrowserCocoa(); UI_BASE_EXPORT extern const base::Feature kEnableOzoneDrmMojo; UI_BASE_EXPORT bool IsOzoneDrmMojo(); +// Whether default UI should use a dark mode color scheme, if enabled on +// macOS Mojave/Windows 10. +UI_BASE_EXPORT extern const base::Feature kDarkMode; + } // namespace features #endif // UI_BASE_UI_BASE_FEATURES_H_ diff --git a/chromium/ui/base/ui_base_switches.cc b/chromium/ui/base/ui_base_switches.cc index 4165ac52387..c14baff0863 100644 --- a/chromium/ui/base/ui_base_switches.cc +++ b/chromium/ui/base/ui_base_switches.cc @@ -48,6 +48,9 @@ const char kEnableTouchDragDrop[] = "enable-touch-drag-drop"; const char kEnableTouchableAppContextMenu[] = "enable-touchable-app-context-menus"; +// Forces dark mode in UI for platforms that support it. +const char kForceDarkMode[] = "force-dark-mode"; + // Forces high-contrast mode in native UI drawing, regardless of system // settings. Note that this has limited effect on Windows: only Aura colors will // be switched to high contrast, not other system colors. @@ -70,21 +73,6 @@ const char kMaterialDesignInkDropAnimationSpeedSlow[] = "slow"; // Enables top Chrome material design elements. const char kTopChromeMD[] = "top-chrome-md"; -// Material design mode for the |kTopChromeMD| switch. -const char kTopChromeMDMaterial[] = "material"; - -// Auto-switching mode |kTopChromeMD| switch. This mode toggles between -// material and material-hybrid depending on device. -const char kTopChromeMDMaterialAuto[] = "material-auto"; - -// Material design hybrid mode for the |kTopChromeMD| switch. Targeted for -// mouse/touch hybrid devices. -const char kTopChromeMDMaterialHybrid[] = "material-hybrid"; - -// Material design mode that is more optimized for touch devices for the -// |kTopChromeMD| switch. -const char kTopChromeMDMaterialTouchOptimized[] = "material-touch-optimized"; - // Material design mode that represents a refresh of the Chrome UI for the // |kTopChromeMD| switch. const char kTopChromeMDMaterialRefresh[] = "material-refresh"; @@ -94,6 +82,10 @@ const char kTopChromeMDMaterialRefresh[] = "material-refresh"; const char kTopChromeMDMaterialRefreshTouchOptimized[] = "material-refresh-touch-optimized"; +// Switches between material refresh and touchable material refresh depending on +// the tablet mode. +const char kTopChromeMDMaterialRefreshDynamic[] = "material-refresh-dynamic"; + // Disable partial swap which is needed for some OpenGL drivers / emulators. const char kUIDisablePartialSwap[] = "ui-disable-partial-swap"; diff --git a/chromium/ui/base/ui_base_switches.h b/chromium/ui/base/ui_base_switches.h index 4ddc0e56899..0c8c5957d48 100644 --- a/chromium/ui/base/ui_base_switches.h +++ b/chromium/ui/base/ui_base_switches.h @@ -27,6 +27,7 @@ UI_BASE_EXPORT extern const char kDisableTouchAdjustment[]; UI_BASE_EXPORT extern const char kDisableTouchDragDrop[]; UI_BASE_EXPORT extern const char kEnableTouchDragDrop[]; UI_BASE_EXPORT extern const char kEnableTouchableAppContextMenu[]; +UI_BASE_EXPORT extern const char kForceDarkMode[]; UI_BASE_EXPORT extern const char kForceHighContrast[]; UI_BASE_EXPORT extern const char kLang[]; UI_BASE_EXPORT extern const char kMaterialDesignInkDropAnimationSpeed[]; @@ -36,12 +37,9 @@ UI_BASE_EXPORT extern const char kShowOverdrawFeedback[]; UI_BASE_EXPORT extern const char kSlowDownCompositingScaleFactor[]; UI_BASE_EXPORT extern const char kTintGlCompositedContent[]; UI_BASE_EXPORT extern const char kTopChromeMD[]; -UI_BASE_EXPORT extern const char kTopChromeMDMaterial[]; -UI_BASE_EXPORT extern const char kTopChromeMDMaterialAuto[]; -UI_BASE_EXPORT extern const char kTopChromeMDMaterialHybrid[]; -UI_BASE_EXPORT extern const char kTopChromeMDMaterialTouchOptimized[]; UI_BASE_EXPORT extern const char kTopChromeMDMaterialRefresh[]; UI_BASE_EXPORT extern const char kTopChromeMDMaterialRefreshTouchOptimized[]; +UI_BASE_EXPORT extern const char kTopChromeMDMaterialRefreshDynamic[]; UI_BASE_EXPORT extern const char kUIDisablePartialSwap[]; // Test related. diff --git a/chromium/ui/base/ui_features.gni b/chromium/ui/base/ui_features.gni index 2691c6fbef5..6ecd398d5f1 100644 --- a/chromium/ui/base/ui_features.gni +++ b/chromium/ui/base/ui_features.gni @@ -8,9 +8,6 @@ declare_args() { # Optional system library. use_xkbcommon = false - # Whether the entire browser uses toolkit-views on Mac instead of Cocoa. - mac_views_browser = is_mac - # Whether the platform provides a native accessibility toolkit. has_native_accessibility = use_atk || is_win || is_mac diff --git a/chromium/ui/base/view_prop.cc b/chromium/ui/base/view_prop.cc index 2ff403e2e31..45185f5ffbe 100644 --- a/chromium/ui/base/view_prop.cc +++ b/chromium/ui/base/view_prop.cc @@ -21,7 +21,7 @@ class ViewProp::Data : public base::RefCounted<ViewProp::Data> { if (!data_set_) data_set_ = new DataSet; scoped_refptr<Data> new_data(new Data(view, key)); - DataSet::const_iterator i = data_set_->find(new_data.get()); + auto i = data_set_->find(new_data.get()); if (i != data_set_->end()) { *data = *i; return; @@ -58,7 +58,7 @@ class ViewProp::Data : public base::RefCounted<ViewProp::Data> { data_(NULL) {} ~Data() { - DataSet::iterator i = data_set_->find(this); + auto i = data_set_->find(this); // Also check for equality using == as |Get| creates dummy values in order // to look up a value. if (i != data_set_->end() && *i == this) diff --git a/chromium/ui/base/webui/i18n_source_stream_unittest.cc b/chromium/ui/base/webui/i18n_source_stream_unittest.cc index 3484f649d73..5ef13bc2b53 100644 --- a/chromium/ui/base/webui/i18n_source_stream_unittest.cc +++ b/chromium/ui/base/webui/i18n_source_stream_unittest.cc @@ -87,7 +87,7 @@ class I18nSourceStreamTest : public ::testing::TestWithParam<I18nTestParam> { // Helpful function to initialize the test fixture. void Init() { - output_buffer_ = new net::IOBuffer(output_buffer_size_); + output_buffer_ = base::MakeRefCounted<net::IOBuffer>(output_buffer_size_); std::unique_ptr<net::MockSourceStream> source(new net::MockSourceStream()); source_ = source.get(); diff --git a/chromium/ui/base/x/selection_owner.cc b/chromium/ui/base/x/selection_owner.cc index 17b750b6e9f..1804eaedee8 100644 --- a/chromium/ui/base/x/selection_owner.cc +++ b/chromium/ui/base/x/selection_owner.cc @@ -100,8 +100,7 @@ SelectionOwner::~SelectionOwner() { } void SelectionOwner::RetrieveTargets(std::vector<XAtom>* targets) { - for (SelectionFormatMap::const_iterator it = format_map_.begin(); - it != format_map_.end(); ++it) { + for (auto it = format_map_.begin(); it != format_map_.end(); ++it) { targets->push_back(it->first); } } @@ -187,8 +186,7 @@ bool SelectionOwner::CanDispatchPropertyEvent(const XEvent& event) { } void SelectionOwner::OnPropertyEvent(const XEvent& event) { - std::vector<IncrementalTransfer>::iterator it = - FindIncrementalTransferForEvent(event); + auto it = FindIncrementalTransferForEvent(event); if (it == incremental_transfers_.end()) return; @@ -233,7 +231,7 @@ bool SelectionOwner::ProcessTarget(XAtom target, } // Try to find the data type in map. - SelectionFormatMap::const_iterator it = format_map_.find(target); + auto it = format_map_.find(target); if (it != format_map_.end()) { if (it->second->size() > max_request_size_) { // We must send the data back in several chunks due to a limitation in @@ -326,10 +324,8 @@ void SelectionOwner::CompleteIncrementalTransfer( std::vector<SelectionOwner::IncrementalTransfer>::iterator SelectionOwner::FindIncrementalTransferForEvent(const XEvent& event) { - for (std::vector<IncrementalTransfer>::iterator it = - incremental_transfers_.begin(); - it != incremental_transfers_.end(); - ++it) { + for (auto it = incremental_transfers_.begin(); + it != incremental_transfers_.end(); ++it) { if (it->window == event.xproperty.window && it->property == event.xproperty.atom) { return it; diff --git a/chromium/ui/base/x/selection_requestor.cc b/chromium/ui/base/x/selection_requestor.cc index ccd65a4d86b..8fb3bda7711 100644 --- a/chromium/ui/base/x/selection_requestor.cc +++ b/chromium/ui/base/x/selection_requestor.cc @@ -82,8 +82,7 @@ bool SelectionRequestor::PerformBlockingConvertSelection( ConvertSelectionForCurrentRequest(); BlockTillSelectionNotifyForRequest(&request); - std::vector<Request*>::iterator request_it = std::find( - requests_.begin(), requests_.end(), &request); + auto request_it = std::find(requests_.begin(), requests_.end(), &request); CHECK(request_it != requests_.end()); if (static_cast<int>(current_request_index_) > request_it - requests_.begin()) { @@ -116,8 +115,7 @@ void SelectionRequestor::PerformBlockingConvertSelectionWithParameter( SelectionData SelectionRequestor::RequestAndWaitForTypes( XAtom selection, const std::vector<XAtom>& types) { - for (std::vector<XAtom>::const_iterator it = types.begin(); - it != types.end(); ++it) { + for (auto it = types.begin(); it != types.end(); ++it) { scoped_refptr<base::RefCountedMemory> data; XAtom type = x11::None; if (PerformBlockingConvertSelection(selection, diff --git a/chromium/ui/base/x/selection_utils.cc b/chromium/ui/base/x/selection_utils.cc index 8a413428fac..c0947ea7821 100644 --- a/chromium/ui/base/x/selection_utils.cc +++ b/chromium/ui/base/x/selection_utils.cc @@ -51,8 +51,7 @@ std::vector<::Atom> GetURIListAtomsFrom() { void GetAtomIntersection(const std::vector< ::Atom>& desired, const std::vector< ::Atom>& offered, std::vector< ::Atom>* output) { - for (std::vector< ::Atom>::const_iterator it = desired.begin(); - it != desired.end(); ++it) { + for (auto it = desired.begin(); it != desired.end(); ++it) { if (base::ContainsValue(offered, *it)) output->push_back(*it); } @@ -121,9 +120,8 @@ void SelectionFormatMap::Insert( ui::SelectionData SelectionFormatMap::GetFirstOf( const std::vector< ::Atom>& requested_types) const { - for (std::vector< ::Atom>::const_iterator it = requested_types.begin(); - it != requested_types.end(); ++it) { - const_iterator data_it = data_.find(*it); + for (auto it = requested_types.begin(); it != requested_types.end(); ++it) { + auto data_it = data_.find(*it); if (data_it != data_.end()) { return SelectionData(data_it->first, data_it->second); } @@ -134,7 +132,7 @@ ui::SelectionData SelectionFormatMap::GetFirstOf( std::vector< ::Atom> SelectionFormatMap::GetTypes() const { std::vector< ::Atom> atoms; - for (const_iterator it = data_.begin(); it != data_.end(); ++it) + for (auto it = data_.begin(); it != data_.end(); ++it) atoms.push_back(it->first); return atoms; diff --git a/chromium/ui/base/x/x11_menu_list.cc b/chromium/ui/base/x/x11_menu_list.cc index 876865b41d2..83a5522437b 100644 --- a/chromium/ui/base/x/x11_menu_list.cc +++ b/chromium/ui/base/x/x11_menu_list.cc @@ -34,8 +34,7 @@ void XMenuList::MaybeRegisterMenu(XID menu) { } void XMenuList::MaybeUnregisterMenu(XID menu) { - std::vector<XID>::iterator iter = std::find(menus_.begin(), menus_.end(), - menu); + auto iter = std::find(menus_.begin(), menus_.end(), menu); if (iter == menus_.end()) return; menus_.erase(iter); diff --git a/chromium/ui/base/x/x11_util.cc b/chromium/ui/base/x/x11_util.cc index 3b304881885..311b10e3d67 100644 --- a/chromium/ui/base/x/x11_util.cc +++ b/chromium/ui/base/x/x11_util.cc @@ -25,6 +25,7 @@ #include "base/memory/singleton.h" #include "base/message_loop/message_loop_current.h" #include "base/metrics/histogram_macros.h" +#include "base/no_destructor.h" #include "base/single_thread_task_runner.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" @@ -421,10 +422,10 @@ int CoalescePendingMotionEvents(const XEvent* xev, XEvent* last_event) { } void HideHostCursor() { - CR_DEFINE_STATIC_LOCAL(XScopedCursor, invisible_cursor, - (CreateInvisibleCursor(), gfx::GetXDisplay())); + static base::NoDestructor<XScopedCursor> invisible_cursor( + CreateInvisibleCursor(), gfx::GetXDisplay()); XDefineCursor(gfx::GetXDisplay(), DefaultRootWindow(gfx::GetXDisplay()), - invisible_cursor.get()); + invisible_cursor->get()); } ::Cursor CreateInvisibleCursor() { diff --git a/chromium/ui/chromeos/BUILD.gn b/chromium/ui/chromeos/BUILD.gn index bccdfb49937..7c4de18b330 100644 --- a/chromium/ui/chromeos/BUILD.gn +++ b/chromium/ui/chromeos/BUILD.gn @@ -34,6 +34,8 @@ component("chromeos") { "//components/device_event_log", "//components/onc", "//mojo/public/cpp/bindings", + "//services/device/public/mojom", + "//services/service_manager/public/cpp", "//services/ws/public/cpp", "//services/ws/public/mojom", "//skia", diff --git a/chromium/ui/compositor/BUILD.gn b/chromium/ui/compositor/BUILD.gn index e7f9be1ace3..ab830a68656 100644 --- a/chromium/ui/compositor/BUILD.gn +++ b/chromium/ui/compositor/BUILD.gn @@ -115,7 +115,7 @@ jumbo_component("compositor") { } } -static_library("test_support") { +jumbo_static_library("test_support") { testonly = true sources = [ "test/context_factories_for_test.cc", diff --git a/chromium/ui/compositor/compositor.cc b/chromium/ui/compositor/compositor.cc index 03ef3bf7315..8ae7efefd62 100644 --- a/chromium/ui/compositor/compositor.cc +++ b/chromium/ui/compositor/compositor.cc @@ -75,7 +75,7 @@ Compositor::Compositor(const viz::FrameSinkId& frame_sink_id, force_software_compositor_(force_software_compositor), layer_animator_collection_(this), is_pixel_canvas_(enable_pixel_canvas), - lock_manager_(task_runner, this), + lock_manager_(task_runner), context_creation_weak_ptr_factory_(this) { if (context_factory_private) { auto* host_frame_sink_manager = @@ -201,7 +201,7 @@ Compositor::Compositor(const viz::FrameSinkId& frame_sink_id, params.settings = &settings; params.main_task_runner = task_runner_; params.mutator_host = animation_host_.get(); - host_ = cc::LayerTreeHost::CreateSingleThreaded(this, ¶ms); + host_ = cc::LayerTreeHost::CreateSingleThreaded(this, std::move(params)); if (base::FeatureList::IsEnabled(features::kUiCompositorScrollWithLayers) && host_->GetInputHandler()) { @@ -445,16 +445,6 @@ bool Compositor::GetScrollOffsetForLayer(cc::ElementId element_id, input_handler->GetScrollOffsetForLayer(element_id, offset); } -void Compositor::SetAuthoritativeVSyncInterval( - const base::TimeDelta& interval) { - DCHECK_GT(interval.InMillisecondsF(), 0); - refresh_rate_ = - base::Time::kMillisecondsPerSecond / interval.InMillisecondsF(); - if (context_factory_private_) - context_factory_private_->SetAuthoritativeVSyncInterval(this, interval); - vsync_manager_->SetAuthoritativeVSyncInterval(interval); -} - void Compositor::SetDisplayVSyncParameters(base::TimeTicks timebase, base::TimeDelta interval) { static bool is_frame_rate_limit_disabled = @@ -596,8 +586,8 @@ static void SendDamagedRectsRecursive(ui::Layer* layer) { SendDamagedRectsRecursive(child); } -void Compositor::UpdateLayerTreeHost(VisualStateUpdate requested_update) { - if (!root_layer() || requested_update == VisualStateUpdate::kPrePaint) +void Compositor::UpdateLayerTreeHost() { + if (!root_layer()) return; SendDamagedRectsRecursive(root_layer()); } @@ -661,10 +651,6 @@ void Compositor::SetLayerTreeDebugState( host_->SetDebugState(debug_state); } -void Compositor::OnCompositorLockStateChanged(bool locked) { - host_->SetDeferCommits(locked); -} - void Compositor::RequestPresentationTimeForNextFrame( PresentationTimeCallback callback) { host_->RequestPresentationTimeForNextFrame(std::move(callback)); diff --git a/chromium/ui/compositor/compositor.h b/chromium/ui/compositor/compositor.h index a1b6716fc79..b3905bdfa65 100644 --- a/chromium/ui/compositor/compositor.h +++ b/chromium/ui/compositor/compositor.h @@ -19,6 +19,7 @@ #include "base/time/time.h" #include "build/build_config.h" #include "cc/trees/element_id.h" +#include "cc/trees/layer_tree_host.h" #include "cc/trees/layer_tree_host_client.h" #include "cc/trees/layer_tree_host_single_thread_client.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" @@ -49,7 +50,6 @@ class AnimationTimeline; class Layer; class LayerTreeDebugState; class LayerTreeFrameSink; -class LayerTreeHost; class TaskGraphRunner; } @@ -151,8 +151,6 @@ class COMPOSITOR_EXPORT ContextFactoryPrivate { const gfx::ColorSpace& blending_color_space, const gfx::ColorSpace& output_color_space) = 0; - virtual void SetAuthoritativeVSyncInterval(ui::Compositor* compositor, - base::TimeDelta interval) = 0; // Mac path for transporting vsync parameters to the display. Other platforms // update it via the BrowserCompositorLayerTreeFrameSink directly. virtual void SetDisplayVSyncParameters(ui::Compositor* compositor, @@ -207,7 +205,6 @@ class COMPOSITOR_EXPORT ContextFactory { // view hierarchy. class COMPOSITOR_EXPORT Compositor : public cc::LayerTreeHostClient, public cc::LayerTreeHostSingleThreadClient, - public CompositorLockManagerClient, public viz::HostFrameSinkClient, public ExternalBeginFrameClient { public: @@ -310,14 +307,6 @@ class COMPOSITOR_EXPORT Compositor : public cc::LayerTreeHostClient, gfx::ScrollOffset* offset) const; bool ScrollLayerTo(cc::ElementId element_id, const gfx::ScrollOffset& offset); - // The "authoritative" vsync interval, if provided, will override interval - // reported from 3D context. This is typically the value reported by a more - // reliable source, e.g, the platform display configuration. - // In the particular case of ChromeOS -- this is the value queried through - // XRandR, which is more reliable than the value queried through the 3D - // context. - void SetAuthoritativeVSyncInterval(const base::TimeDelta& interval); - // Most platforms set their vsync info via // BrowerCompositorLayerTreeFrameSink::OnUpdateVSyncParametersFromGpu(), but // Mac routes vsync info via the browser compositor instead through this path. @@ -376,7 +365,8 @@ class COMPOSITOR_EXPORT Compositor : public cc::LayerTreeHostClient, CompositorLockClient* client, base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(kCompositorLockTimeoutMs)) { - return lock_manager_.GetCompositorLock(client, timeout); + return lock_manager_.GetCompositorLock(client, timeout, + host_->DeferCommits()); } // Registers a callback that is run when the next frame successfully makes it @@ -398,12 +388,9 @@ class COMPOSITOR_EXPORT Compositor : public cc::LayerTreeHostClient, void BeginMainFrame(const viz::BeginFrameArgs& args) override; void BeginMainFrameNotExpectedSoon() override; void BeginMainFrameNotExpectedUntil(base::TimeTicks time) override; - void UpdateLayerTreeHost(VisualStateUpdate requested_update) override; - void ApplyViewportDeltas(const gfx::Vector2dF& inner_delta, - const gfx::Vector2dF& outer_delta, - const gfx::Vector2dF& elastic_overscroll_delta, - float page_scale, - float top_controls_delta) override {} + void UpdateLayerTreeHost() override; + void ApplyViewportChanges(const cc::ApplyViewportChangesArgs& args) override { + } void RecordWheelAndTouchScrollingCount(bool has_scrolled_by_wheel, bool has_scrolled_by_touch) override {} void RequestNewLayerTreeFrameSink() override; @@ -417,6 +404,7 @@ class COMPOSITOR_EXPORT Compositor : public cc::LayerTreeHostClient, void DidPresentCompositorFrame( uint32_t frame_token, const gfx::PresentationFeedback& feedback) override {} + void RecordEndOfFrameMetrics(base::TimeTicks frame_begin_time) override {} // cc::LayerTreeHostSingleThreadClient implementation. void DidSubmitCompositorFrame() override; @@ -426,9 +414,6 @@ class COMPOSITOR_EXPORT Compositor : public cc::LayerTreeHostClient, void OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info) override; void OnFrameTokenChanged(uint32_t frame_token) override; - // CompositorLockManagerClient implementation. - void OnCompositorLockStateChanged(bool locked) override; - bool IsLocked() { return lock_manager_.IsLocked(); } void SetOutputIsSecure(bool output_is_secure); diff --git a/chromium/ui/compositor/compositor_lock.cc b/chromium/ui/compositor/compositor_lock.cc index 02c609b7359..69a34775bfd 100644 --- a/chromium/ui/compositor/compositor_lock.cc +++ b/chromium/ui/compositor/compositor_lock.cc @@ -6,14 +6,13 @@ #include "base/bind.h" #include "base/stl_util.h" +#include "cc/trees/layer_tree_host.h" namespace ui { CompositorLockManager::CompositorLockManager( - scoped_refptr<base::SingleThreadTaskRunner> task_runner, - CompositorLockManagerClient* client) + scoped_refptr<base::SingleThreadTaskRunner> task_runner) : task_runner_(std::move(task_runner)), - client_(client), weak_ptr_factory_(this), lock_timeout_weak_ptr_factory_(this) {} @@ -21,11 +20,12 @@ CompositorLockManager::~CompositorLockManager() = default; std::unique_ptr<CompositorLock> CompositorLockManager::GetCompositorLock( CompositorLockClient* client, - base::TimeDelta timeout) { + base::TimeDelta timeout, + std::unique_ptr<cc::ScopedDeferCommits> scoped_defer_commits) { // This uses the main WeakPtrFactory to break the connection from the lock to // the CompositorLockManager when the CompositorLockManager is destroyed. - auto lock = - std::make_unique<CompositorLock>(client, weak_ptr_factory_.GetWeakPtr()); + auto lock = std::make_unique<CompositorLock>( + client, weak_ptr_factory_.GetWeakPtr(), std::move(scoped_defer_commits)); bool was_empty = active_locks_.empty(); active_locks_.push_back(lock.get()); @@ -42,9 +42,6 @@ std::unique_ptr<CompositorLock> CompositorLockManager::GetCompositorLock( } } - if (was_empty) - client_->OnCompositorLockStateChanged(true); - if (should_extend_timeout) { // The timeout task uses an independent WeakPtrFactory that is invalidated // when all locks are ended to prevent the timeout from leaking into @@ -61,7 +58,6 @@ std::unique_ptr<CompositorLock> CompositorLockManager::GetCompositorLock( void CompositorLockManager::RemoveCompositorLock(CompositorLock* lock) { base::Erase(active_locks_, lock); if (active_locks_.empty()) { - client_->OnCompositorLockStateChanged(false); lock_timeout_weak_ptr_factory_.InvalidateWeakPtrs(); scheduled_timeout_ = base::TimeTicks(); } @@ -75,16 +71,22 @@ void CompositorLockManager::TimeoutLocks() { DCHECK(active_locks_.empty()); } -CompositorLock::CompositorLock(CompositorLockClient* client, - base::WeakPtr<CompositorLockManager> manager) - : client_(client), manager_(std::move(manager)) {} +CompositorLock::CompositorLock( + CompositorLockClient* client, + base::WeakPtr<CompositorLockManager> manager, + std::unique_ptr<cc::ScopedDeferCommits> scoped_defer_commits) + : client_(client), + scoped_defer_commits_(std::move(scoped_defer_commits)), + manager_(std::move(manager)) {} CompositorLock::~CompositorLock() { + scoped_defer_commits_ = nullptr; if (manager_) manager_->RemoveCompositorLock(this); } void CompositorLock::TimeoutLock() { + scoped_defer_commits_ = nullptr; manager_->RemoveCompositorLock(this); manager_ = nullptr; if (client_) diff --git a/chromium/ui/compositor/compositor_lock.h b/chromium/ui/compositor/compositor_lock.h index 370724c7ccc..2f08a190619 100644 --- a/chromium/ui/compositor/compositor_lock.h +++ b/chromium/ui/compositor/compositor_lock.h @@ -12,6 +12,10 @@ #include "base/single_thread_task_runner.h" #include "ui/compositor/compositor_export.h" +namespace cc { +class ScopedDeferCommits; +} + namespace ui { class Compositor; @@ -27,22 +31,12 @@ class CompositorLockClient { virtual void CompositorLockTimedOut() = 0; }; -// Implemented by clients which are locked by a compositor lock. Used by the -// CompositorLockManager to notify their parent that lock state has changed. -class CompositorLockManagerClient { - public: - virtual ~CompositorLockManagerClient() {} - - // Called when the lock state changes. - virtual void OnCompositorLockStateChanged(bool locked) = 0; -}; - // A helper class used to manage compositor locks. Should be created/used by // classes which want to provide out compositor locking. class COMPOSITOR_EXPORT CompositorLockManager { public: - CompositorLockManager(scoped_refptr<base::SingleThreadTaskRunner> task_runner, - CompositorLockManagerClient* client); + CompositorLockManager( + scoped_refptr<base::SingleThreadTaskRunner> task_runner); ~CompositorLockManager(); // Creates a compositor lock. Returns NULL if it is not possible to lock at @@ -50,7 +44,8 @@ class COMPOSITOR_EXPORT CompositorLockManager { // timeout is null, then no timeout is used. std::unique_ptr<CompositorLock> GetCompositorLock( CompositorLockClient* client, - base::TimeDelta timeout); + base::TimeDelta timeout, + std::unique_ptr<cc::ScopedDeferCommits> scoped_defer_commits); void set_allow_locks_to_extend_timeout(bool allowed) { allow_locks_to_extend_timeout_ = allowed; @@ -71,8 +66,6 @@ class COMPOSITOR_EXPORT CompositorLockManager { // The TaskRunner on which timeouts are run. scoped_refptr<base::SingleThreadTaskRunner> task_runner_; - // A client which is notified about lock state changes. - CompositorLockManagerClient* client_ = nullptr; // The estimated time that the locks will timeout. base::TimeTicks scheduled_timeout_; // If true, the |scheduled_timeout_| might be recalculated and extended. @@ -99,8 +92,10 @@ class COMPOSITOR_EXPORT CompositorLock { // The |client| is informed about events from the CompositorLock. The // |delegate| is used to perform actual unlocking. If |timeout| is zero then // no timeout is scheduled, else a timeout is scheduled on the |task_runner|. - explicit CompositorLock(CompositorLockClient* client, - base::WeakPtr<CompositorLockManager> manager); + explicit CompositorLock( + CompositorLockClient* client, + base::WeakPtr<CompositorLockManager> manager, + std::unique_ptr<cc::ScopedDeferCommits> scoped_defer_commits); ~CompositorLock(); private: @@ -110,6 +105,7 @@ class COMPOSITOR_EXPORT CompositorLock { void TimeoutLock(); CompositorLockClient* const client_; + std::unique_ptr<cc::ScopedDeferCommits> scoped_defer_commits_; base::WeakPtr<CompositorLockManager> manager_; DISALLOW_COPY_AND_ASSIGN(CompositorLock); diff --git a/chromium/ui/compositor/compositor_lock_unittest.cc b/chromium/ui/compositor/compositor_lock_unittest.cc index 6ca8cf842a7..4589fd63f08 100644 --- a/chromium/ui/compositor/compositor_lock_unittest.cc +++ b/chromium/ui/compositor/compositor_lock_unittest.cc @@ -5,6 +5,7 @@ #include <stdint.h> #include "base/test/test_mock_time_task_runner.h" +#include "cc/trees/layer_tree_host.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/compositor/compositor_lock.h" @@ -16,15 +17,14 @@ namespace ui { namespace { // For tests that control time. -class CompositorLockTest : public testing::Test, - public ui::CompositorLockManagerClient { +class CompositorLockTest : public testing::Test { protected: CompositorLockTest() {} ~CompositorLockTest() override {} void SetUp() override { task_runner_ = new base::TestMockTimeTaskRunner; - lock_manager_ = std::make_unique<CompositorLockManager>(task_runner_, this); + lock_manager_ = std::make_unique<CompositorLockManager>(task_runner_); } base::TestMockTimeTaskRunner* task_runner() { return task_runner_.get(); } @@ -33,11 +33,6 @@ class CompositorLockTest : public testing::Test, void DestroyLockManager() { lock_manager_.reset(); } - // ui::CompositorLockManagerClient implementation. - void OnCompositorLockStateChanged(bool locked) override { - EXPECT_EQ(locked, lock_manager_->IsLocked()); - } - protected: scoped_refptr<base::TestMockTimeTaskRunner> task_runner_; std::unique_ptr<CompositorLockManager> lock_manager_; @@ -59,7 +54,7 @@ TEST_F(CompositorLockTest, LocksTimeOut) { { testing::StrictMock<MockCompositorLockClient> lock_client; // This lock has a timeout. - lock = lock_manager()->GetCompositorLock(&lock_client, timeout); + lock = lock_manager()->GetCompositorLock(&lock_client, timeout, nullptr); EXPECT_TRUE(lock_manager()->IsLocked()); EXPECT_CALL(lock_client, CompositorLockTimedOut()).Times(1); task_runner()->FastForwardBy(timeout); @@ -70,7 +65,8 @@ TEST_F(CompositorLockTest, LocksTimeOut) { { testing::StrictMock<MockCompositorLockClient> lock_client; // This lock has no timeout. - lock = lock_manager()->GetCompositorLock(&lock_client, base::TimeDelta()); + lock = lock_manager()->GetCompositorLock(&lock_client, base::TimeDelta(), + nullptr); EXPECT_TRUE(lock_manager()->IsLocked()); EXPECT_CALL(lock_client, CompositorLockTimedOut()).Times(0); task_runner()->FastForwardBy(timeout); @@ -87,8 +83,8 @@ TEST_F(CompositorLockTest, MultipleLockClients) { base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(1); // Both locks are grabbed from the Compositor with a separate client. - lock1 = lock_manager()->GetCompositorLock(&lock_client1, timeout); - lock2 = lock_manager()->GetCompositorLock(&lock_client2, timeout); + lock1 = lock_manager()->GetCompositorLock(&lock_client1, timeout, nullptr); + lock2 = lock_manager()->GetCompositorLock(&lock_client2, timeout, nullptr); EXPECT_TRUE(lock_manager()->IsLocked()); // Both clients get notified of timeout. EXPECT_CALL(lock_client1, CompositorLockTimedOut()).Times(1); @@ -108,11 +104,11 @@ TEST_F(CompositorLockTest, ExtendingLifeOfLockDoesntUseDeadClient) { // One lock is grabbed from the compositor with a client. The other // extends its lifetime past that of the first. - lock1 = lock_manager()->GetCompositorLock(&lock_client1, timeout); + lock1 = lock_manager()->GetCompositorLock(&lock_client1, timeout, nullptr); EXPECT_TRUE(lock_manager()->IsLocked()); // This also locks the compositor and will do so past |lock1| ending. - lock2 = lock_manager()->GetCompositorLock(&lock_client2, timeout); + lock2 = lock_manager()->GetCompositorLock(&lock_client2, timeout, nullptr); // |lock1| is destroyed, so it won't timeout but |lock2| will. lock1 = nullptr; @@ -134,12 +130,12 @@ TEST_F(CompositorLockTest, AddingLocksDoesNotExtendTimeout) { base::TimeDelta timeout2 = base::TimeDelta::FromMilliseconds(10); // The first lock has a short timeout. - lock1 = lock_manager()->GetCompositorLock(&lock_client1, timeout1); + lock1 = lock_manager()->GetCompositorLock(&lock_client1, timeout1, nullptr); EXPECT_TRUE(lock_manager()->IsLocked()); // The second lock has a longer timeout, but since a lock is active, // the first one is used for both. - lock2 = lock_manager()->GetCompositorLock(&lock_client2, timeout2); + lock2 = lock_manager()->GetCompositorLock(&lock_client2, timeout2, nullptr); EXPECT_CALL(lock_client1, CompositorLockTimedOut()).Times(1); EXPECT_CALL(lock_client2, CompositorLockTimedOut()).Times(1); @@ -158,13 +154,13 @@ TEST_F(CompositorLockTest, AllowAndExtendTimeout) { base::TimeDelta timeout2 = base::TimeDelta::FromMilliseconds(10); // The first lock has a short timeout. - lock1 = lock_manager()->GetCompositorLock(&lock_client1, timeout1); + lock1 = lock_manager()->GetCompositorLock(&lock_client1, timeout1, nullptr); EXPECT_TRUE(lock_manager()->IsLocked()); // Allow locks to extend timeout. lock_manager()->set_allow_locks_to_extend_timeout(true); // The second lock has a longer timeout, so the second one is used for both. - lock2 = lock_manager()->GetCompositorLock(&lock_client2, timeout2); + lock2 = lock_manager()->GetCompositorLock(&lock_client2, timeout2, nullptr); lock_manager()->set_allow_locks_to_extend_timeout(false); EXPECT_CALL(lock_client1, CompositorLockTimedOut()).Times(0); @@ -190,7 +186,7 @@ TEST_F(CompositorLockTest, ExtendingTimeoutStartingCreatedTime) { base::TimeDelta timeout2 = base::TimeDelta::FromMilliseconds(10); // The first lock has a short timeout. - lock1 = lock_manager()->GetCompositorLock(&lock_client1, timeout1); + lock1 = lock_manager()->GetCompositorLock(&lock_client1, timeout1, nullptr); EXPECT_TRUE(lock_manager()->IsLocked()); base::TimeDelta time_elapse = base::TimeDelta::FromMilliseconds(1); @@ -201,7 +197,7 @@ TEST_F(CompositorLockTest, ExtendingTimeoutStartingCreatedTime) { lock_manager()->set_allow_locks_to_extend_timeout(true); // The second lock has a longer timeout, so the second one is used for both // and start from the time second lock created. - lock2 = lock_manager()->GetCompositorLock(&lock_client2, timeout2); + lock2 = lock_manager()->GetCompositorLock(&lock_client2, timeout2, nullptr); lock_manager()->set_allow_locks_to_extend_timeout(false); EXPECT_CALL(lock_client1, CompositorLockTimedOut()).Times(0); @@ -227,13 +223,13 @@ TEST_F(CompositorLockTest, AllowButNotExtendTimeout) { base::TimeDelta timeout2 = base::TimeDelta::FromMilliseconds(1); // The first lock has a longer timeout. - lock1 = lock_manager()->GetCompositorLock(&lock_client1, timeout1); + lock1 = lock_manager()->GetCompositorLock(&lock_client1, timeout1, nullptr); EXPECT_TRUE(lock_manager()->IsLocked()); // Allow locks to extend timeout. lock_manager()->set_allow_locks_to_extend_timeout(true); // The second lock has a short timeout, so the first one is used for both. - lock2 = lock_manager()->GetCompositorLock(&lock_client2, timeout2); + lock2 = lock_manager()->GetCompositorLock(&lock_client2, timeout2, nullptr); lock_manager()->set_allow_locks_to_extend_timeout(false); EXPECT_CALL(lock_client1, CompositorLockTimedOut()).Times(0); @@ -258,7 +254,7 @@ TEST_F(CompositorLockTest, AllowingExtendDoesNotUseDeadClient) { base::TimeDelta timeout1 = base::TimeDelta::FromMilliseconds(1); base::TimeDelta timeout2 = base::TimeDelta::FromMilliseconds(10); - lock1 = lock_manager()->GetCompositorLock(&lock_client1, timeout1); + lock1 = lock_manager()->GetCompositorLock(&lock_client1, timeout1, nullptr); EXPECT_TRUE(lock_manager()->IsLocked()); EXPECT_CALL(lock_client1, CompositorLockTimedOut()).Times(1); EXPECT_CALL(lock_client2, CompositorLockTimedOut()).Times(0); @@ -269,7 +265,7 @@ TEST_F(CompositorLockTest, AllowingExtendDoesNotUseDeadClient) { // Allow locks to extend timeout. lock_manager()->set_allow_locks_to_extend_timeout(true); // |lock1| is timed out already. The second lock can timeout on its own. - lock2 = lock_manager()->GetCompositorLock(&lock_client2, timeout2); + lock2 = lock_manager()->GetCompositorLock(&lock_client2, timeout2, nullptr); lock_manager()->set_allow_locks_to_extend_timeout(false); EXPECT_TRUE(lock_manager()->IsLocked()); EXPECT_CALL(lock_client1, CompositorLockTimedOut()).Times(0); @@ -284,7 +280,7 @@ TEST_F(CompositorLockTest, LockIsDestroyedDoesntTimeout) { testing::StrictMock<MockCompositorLockClient> lock_client1; std::unique_ptr<CompositorLock> lock1; - lock1 = lock_manager()->GetCompositorLock(&lock_client1, timeout); + lock1 = lock_manager()->GetCompositorLock(&lock_client1, timeout, nullptr); EXPECT_TRUE(lock_manager()->IsLocked()); // The CompositorLockClient is destroyed when |lock1| is released. lock1 = nullptr; @@ -305,7 +301,7 @@ TEST_F(CompositorLockTest, TimeoutEndsWhenLockEnds) { base::TimeDelta timeout2 = base::TimeDelta::FromMilliseconds(10); // The first lock has a short timeout. - lock1 = lock_manager()->GetCompositorLock(&lock_client1, timeout1); + lock1 = lock_manager()->GetCompositorLock(&lock_client1, timeout1, nullptr); EXPECT_TRUE(lock_manager()->IsLocked()); // But the first lock is ended before timeout. lock1 = nullptr; @@ -313,7 +309,7 @@ TEST_F(CompositorLockTest, TimeoutEndsWhenLockEnds) { // The second lock has a longer timeout, and it should use that timeout, // since the first lock is done. - lock2 = lock_manager()->GetCompositorLock(&lock_client2, timeout2); + lock2 = lock_manager()->GetCompositorLock(&lock_client2, timeout2, nullptr); EXPECT_TRUE(lock_manager()->IsLocked()); { @@ -337,7 +333,8 @@ TEST_F(CompositorLockTest, CompositorLockOutlivesManager) { testing::StrictMock<MockCompositorLockClient> lock_client1; std::unique_ptr<CompositorLock> lock1; - lock1 = lock_manager()->GetCompositorLock(&lock_client1, base::TimeDelta()); + lock1 = lock_manager()->GetCompositorLock(&lock_client1, base::TimeDelta(), + nullptr); // The compositor is destroyed before the lock. DestroyLockManager(); // This doesn't crash. diff --git a/chromium/ui/compositor/compositor_unittest.cc b/chromium/ui/compositor/compositor_unittest.cc index 2abd4a84eca..c308e92d9a5 100644 --- a/chromium/ui/compositor/compositor_unittest.cc +++ b/chromium/ui/compositor/compositor_unittest.cc @@ -7,6 +7,7 @@ #include "base/macros.h" #include "base/run_loop.h" #include "base/single_thread_task_runner.h" +#include "base/test/scoped_task_environment.h" #include "base/test/test_mock_time_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "components/viz/common/frame_sinks/begin_frame_args.h" @@ -80,6 +81,12 @@ class CompositorTestWithMockedTime : public CompositorTest { // For tests that run on a real MessageLoop with real time. class CompositorTestWithMessageLoop : public CompositorTest { + public: + CompositorTestWithMessageLoop() + : scoped_task_environment_( + base::test::ScopedTaskEnvironment::MainThreadType::UI) {} + ~CompositorTestWithMessageLoop() override = default; + protected: scoped_refptr<base::SingleThreadTaskRunner> CreateTaskRunner() override { task_runner_ = base::ThreadTaskRunnerHandle::Get(); @@ -89,6 +96,7 @@ class CompositorTestWithMessageLoop : public CompositorTest { base::SequencedTaskRunner* task_runner() { return task_runner_.get(); } private: + base::test::ScopedTaskEnvironment scoped_task_environment_; scoped_refptr<base::SingleThreadTaskRunner> task_runner_; }; diff --git a/chromium/ui/compositor/compositor_vsync_manager.cc b/chromium/ui/compositor/compositor_vsync_manager.cc index 2bc25fc0275..6c537887b62 100644 --- a/chromium/ui/compositor/compositor_vsync_manager.cc +++ b/chromium/ui/compositor/compositor_vsync_manager.cc @@ -6,23 +6,12 @@ namespace ui { -CompositorVSyncManager::CompositorVSyncManager() - : authoritative_vsync_interval_(base::TimeDelta::FromSeconds(0)) { -} +CompositorVSyncManager::CompositorVSyncManager() = default; CompositorVSyncManager::~CompositorVSyncManager() {} -void CompositorVSyncManager::SetAuthoritativeVSyncInterval( - base::TimeDelta interval) { - authoritative_vsync_interval_ = interval; - last_interval_ = interval; - NotifyObservers(last_timebase_, last_interval_); -} - void CompositorVSyncManager::UpdateVSyncParameters(base::TimeTicks timebase, base::TimeDelta interval) { - if (authoritative_vsync_interval_ != base::TimeDelta::FromSeconds(0)) - interval = authoritative_vsync_interval_; last_timebase_ = timebase; last_interval_ = interval; NotifyObservers(timebase, interval); diff --git a/chromium/ui/compositor/compositor_vsync_manager.h b/chromium/ui/compositor/compositor_vsync_manager.h index 28769074026..3c4989bfc22 100644 --- a/chromium/ui/compositor/compositor_vsync_manager.h +++ b/chromium/ui/compositor/compositor_vsync_manager.h @@ -29,17 +29,8 @@ class COMPOSITOR_EXPORT CompositorVSyncManager CompositorVSyncManager(); - // The "authoritative" vsync interval, if provided, will override |interval| - // as reported by UpdateVSyncParameters() whenever it is called. This is - // typically the value reported by a more reliable source, e.g. the platform - // display configuration. In the particular case of ChromeOS -- this is the - // value queried through XRandR, which is more reliable than the value - // queried through the 3D context. - void SetAuthoritativeVSyncInterval(base::TimeDelta interval); - // The vsync parameters consist of |timebase|, which is the platform timestamp // of the last vsync, and |interval|, which is the interval between vsyncs. - // |interval| may be overriden by SetAuthoritativeVSyncInterval() above. void UpdateVSyncParameters(base::TimeTicks timebase, base::TimeDelta interval); @@ -57,7 +48,6 @@ class COMPOSITOR_EXPORT CompositorVSyncManager base::TimeTicks last_timebase_; base::TimeDelta last_interval_; - base::TimeDelta authoritative_vsync_interval_; DISALLOW_COPY_AND_ASSIGN(CompositorVSyncManager); }; diff --git a/chromium/ui/compositor/host/host_context_factory_private.cc b/chromium/ui/compositor/host/host_context_factory_private.cc index b6010d7b7c6..952525b7f82 100644 --- a/chromium/ui/compositor/host/host_context_factory_private.cc +++ b/chromium/ui/compositor/host/host_context_factory_private.cc @@ -4,11 +4,13 @@ #include "ui/compositor/host/host_context_factory_private.h" +#include "base/command_line.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" #include "cc/mojo_embedder/async_layer_tree_frame_sink.h" #include "components/viz/client/hit_test_data_provider_draw_quad.h" #include "components/viz/client/local_surface_id_provider.h" +#include "components/viz/common/switches.h" #include "components/viz/host/host_display_client.h" #include "components/viz/host/host_frame_sink_manager.h" #include "components/viz/host/renderer_settings_creation.h" @@ -23,6 +25,10 @@ namespace ui { +namespace { +static const char* kBrowser = "Browser"; +} // namespace + HostContextFactoryPrivate::HostContextFactoryPrivate( uint32_t client_id, viz::HostFrameSinkManager* host_frame_sink_manager, @@ -36,14 +42,14 @@ HostContextFactoryPrivate::HostContextFactoryPrivate( HostContextFactoryPrivate::~HostContextFactoryPrivate() = default; +void HostContextFactoryPrivate::AddCompositor(Compositor* compositor) { + compositor_data_map_.try_emplace(compositor); +} + void HostContextFactoryPrivate::ConfigureCompositor( - base::WeakPtr<Compositor> compositor_weak_ptr, + Compositor* compositor, scoped_refptr<viz::ContextProvider> context_provider, scoped_refptr<viz::RasterContextProvider> worker_context_provider) { - Compositor* compositor = compositor_weak_ptr.get(); - if (!compositor) - return; - bool gpu_compositing = !is_gpu_compositing_disabled_ && !compositor->force_software_compositor(); @@ -69,13 +75,6 @@ void HostContextFactoryPrivate::ConfigureCompositor( compositor_data.display_client->GetBoundPtr(resize_task_runner_) .PassInterface(); -#if defined(GPU_SURFACE_HANDLE_IS_ACCELERATED_WINDOW) - gpu::SurfaceHandle surface_handle = compositor->widget(); -#else - // TODO(kylechar): Fix this when we support macOS. - gpu::SurfaceHandle surface_handle = gpu::kNullSurfaceHandle; -#endif - // Initialize ExternalBeginFrameController client if enabled. compositor_data.external_begin_frame_controller_client.reset(); if (compositor->external_begin_frames_enabled()) { @@ -90,16 +89,24 @@ void HostContextFactoryPrivate::ConfigureCompositor( } root_params->frame_sink_id = compositor->frame_sink_id(); - root_params->widget = surface_handle; +#if defined(GPU_SURFACE_HANDLE_IS_ACCELERATED_WINDOW) + root_params->widget = compositor->widget(); +#endif root_params->gpu_compositing = gpu_compositing; root_params->renderer_settings = renderer_settings_; + base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(switches::kDisableFrameRateLimit)) + root_params->disable_frame_rate_limit = true; + // Connects the viz process end of CompositorFrameSink message pipes. The // browser compositor may request a new CompositorFrameSink on context loss, // which will destroy the existing CompositorFrameSink. GetHostFrameSinkManager()->CreateRootCompositorFrameSink( std::move(root_params)); compositor_data.display_private->Resize(compositor->size()); + compositor_data.display_private->SetOutputIsSecure( + compositor_data.output_is_secure); // Create LayerTreeFrameSink with the browser end of CompositorFrameSink. cc::mojo_embedder::AsyncLayerTreeFrameSink::InitParams params; @@ -113,7 +120,9 @@ void HostContextFactoryPrivate::ConfigureCompositor( params.enable_surface_synchronization = true; params.hit_test_data_provider = std::make_unique<viz::HitTestDataProviderDrawQuad>( - /*should_ask_for_child_region=*/false); + false /* should_ask_for_child_region */, + true /* root_accepts_events */); + params.client_name = kBrowser; compositor->SetLayerTreeFrameSink( std::make_unique<cc::mojo_embedder::AsyncLayerTreeFrameSink>( std::move(context_provider), std::move(worker_context_provider), @@ -122,8 +131,6 @@ void HostContextFactoryPrivate::ConfigureCompositor( void HostContextFactoryPrivate::UnconfigureCompositor(Compositor* compositor) { #if defined(OS_WIN) - // TODO(crbug.com/791660): Make sure that GpuProcessHost::SetChildSurface() - // doesn't crash the GPU process after parent is unregistered. gfx::RenderingWindowManager::GetInstance()->UnregisterParent( compositor->widget()); #endif @@ -205,15 +212,6 @@ void HostContextFactoryPrivate::SetDisplayColorSpace( output_color_space); } -void HostContextFactoryPrivate::SetAuthoritativeVSyncInterval( - Compositor* compositor, - base::TimeDelta interval) { - auto iter = compositor_data_map_.find(compositor); - if (iter == compositor_data_map_.end() || !iter->second.display_private) - return; - iter->second.display_private->SetAuthoritativeVSyncInterval(interval); -} - void HostContextFactoryPrivate::SetDisplayVSyncParameters( Compositor* compositor, base::TimeTicks timebase, @@ -239,9 +237,12 @@ void HostContextFactoryPrivate::IssueExternalBeginFrame( void HostContextFactoryPrivate::SetOutputIsSecure(Compositor* compositor, bool secure) { auto iter = compositor_data_map_.find(compositor); - if (iter == compositor_data_map_.end() || !iter->second.display_private) + if (iter == compositor_data_map_.end()) return; - iter->second.display_private->SetOutputIsSecure(secure); + iter->second.output_is_secure = secure; + + if (iter->second.display_private) + iter->second.display_private->SetOutputIsSecure(secure); } viz::FrameSinkManagerImpl* HostContextFactoryPrivate::GetFrameSinkManager() { diff --git a/chromium/ui/compositor/host/host_context_factory_private.h b/chromium/ui/compositor/host/host_context_factory_private.h index 76319deaa6d..69a441e9fbc 100644 --- a/chromium/ui/compositor/host/host_context_factory_private.h +++ b/chromium/ui/compositor/host/host_context_factory_private.h @@ -38,8 +38,13 @@ class HostContextFactoryPrivate : public ContextFactoryPrivate { scoped_refptr<base::SingleThreadTaskRunner> resize_task_runner); ~HostContextFactoryPrivate() override; + // Call this when a compositor is created to ensure a data map entry exists + // for it, so that the data can be accessed before the compositor is + // configured. Could be called twice, e.g. if the GPU process crashes. + void AddCompositor(Compositor* compositor); + void ConfigureCompositor( - base::WeakPtr<Compositor> compositor_weak_ptr, + Compositor* compositor, scoped_refptr<viz::ContextProvider> context_provider, scoped_refptr<viz::RasterContextProvider> worker_context_provider); @@ -59,8 +64,6 @@ class HostContextFactoryPrivate : public ContextFactoryPrivate { void SetDisplayColorSpace(Compositor* compositor, const gfx::ColorSpace& blending_color_space, const gfx::ColorSpace& output_color_space) override; - void SetAuthoritativeVSyncInterval(Compositor* compositor, - base::TimeDelta interval) override; void SetDisplayVSyncParameters(Compositor* compositor, base::TimeTicks timebase, base::TimeDelta interval) override; @@ -100,6 +103,10 @@ class HostContextFactoryPrivate : public ContextFactoryPrivate { std::unique_ptr<ExternalBeginFrameControllerClientImpl> external_begin_frame_controller_client; + // SetOutputIsSecure is called before the compositor is ready, so remember + // the status and apply it during configuration. + bool output_is_secure = false; + private: DISALLOW_COPY_AND_ASSIGN(CompositorData); }; diff --git a/chromium/ui/compositor/layer.cc b/chromium/ui/compositor/layer.cc index 22c2dfe0125..f8f3f8cafdb 100644 --- a/chromium/ui/compositor/layer.cc +++ b/chromium/ui/compositor/layer.cc @@ -113,6 +113,7 @@ Layer::Layer() device_scale_factor_(1.0f), cache_render_surface_requests_(0), deferred_paint_requests_(0), + backdrop_filter_quality_(1.0f), trilinear_filtering_request_(0), weak_ptr_factory_(this) { CreateCcLayer(); @@ -141,6 +142,7 @@ Layer::Layer(LayerType type) device_scale_factor_(1.0f), cache_render_surface_requests_(0), deferred_paint_requests_(0), + backdrop_filter_quality_(1.0f), trilinear_filtering_request_(0), weak_ptr_factory_(this) { CreateCcLayer(); @@ -187,16 +189,14 @@ std::unique_ptr<Layer> Layer::Clone() const { // cc::Layer state. if (surface_layer_) { - if (surface_layer_->primary_surface_id().is_valid()) { - clone->SetShowPrimarySurface( - surface_layer_->primary_surface_id(), frame_size_in_dip_, - surface_layer_->background_color(), - surface_layer_->deadline_in_frames() - ? cc::DeadlinePolicy::UseSpecifiedDeadline( - *surface_layer_->deadline_in_frames()) - : cc::DeadlinePolicy::UseDefaultDeadline(), - surface_layer_->stretch_content_to_fill_bounds()); - } + clone->SetShowPrimarySurface( + surface_layer_->primary_surface_id(), frame_size_in_dip_, + surface_layer_->background_color(), + surface_layer_->deadline_in_frames() + ? cc::DeadlinePolicy::UseSpecifiedDeadline( + *surface_layer_->deadline_in_frames()) + : cc::DeadlinePolicy::UseDefaultDeadline(), + surface_layer_->stretch_content_to_fill_bounds()); if (surface_layer_->fallback_surface_id()) clone->SetFallbackSurfaceId(*surface_layer_->fallback_surface_id()); } else if (type_ == LAYER_SOLID_COLOR) { @@ -286,8 +286,7 @@ void Layer::Remove(Layer* child) { if (compositor) child->ResetCompositorForAnimatorsInTree(compositor); - std::vector<Layer*>::iterator i = - std::find(children_.begin(), children_.end(), child); + auto i = std::find(children_.begin(), children_.end(), child); DCHECK(i != children_.end()); children_.erase(i); child->parent_ = nullptr; @@ -641,6 +640,7 @@ void Layer::SwitchToLayer(scoped_refptr<cc::Layer> new_layer) { cc_layer_->SetContentsOpaque(fills_bounds_opaquely_); cc_layer_->SetIsDrawable(type_ != LAYER_NOT_DRAWN); cc_layer_->SetHideLayerAndSubtree(!visible_); + cc_layer_->SetBackdropFilterQuality(backdrop_filter_quality_); cc_layer_->SetElementId(cc::ElementId(cc_layer_->id())); SetLayerFilters(); @@ -676,6 +676,10 @@ void Layer::RemoveCacheRenderSurfaceRequest() { cc_layer_->SetCacheRenderSurface(false); } +void Layer::SetBackdropFilterQuality(const float quality) { + backdrop_filter_quality_ = quality / GetDeviceScaleFactor(); + cc_layer_->SetBackdropFilterQuality(backdrop_filter_quality_); +} void Layer::AddDeferredPaintRequest() { ++deferred_paint_requests_; TRACE_COUNTER_ID1("ui", "DeferredPaintRequests", this, @@ -850,7 +854,7 @@ void Layer::UpdateNinePatchLayerImage(const gfx::ImageSkia& image) { // we don't need/want to, but we should address this in the future if it // becomes an issue. nine_patch_layer_->SetBitmap( - image.GetRepresentation(device_scale_factor_).sk_bitmap()); + image.GetRepresentation(device_scale_factor_).GetBitmap()); } void Layer::UpdateNinePatchLayerAperture(const gfx::Rect& aperture_in_dip) { diff --git a/chromium/ui/compositor/layer.h b/chromium/ui/compositor/layer.h index 47c36b6dc4e..bda5eb12b82 100644 --- a/chromium/ui/compositor/layer.h +++ b/chromium/ui/compositor/layer.h @@ -428,6 +428,14 @@ class COMPOSITOR_EXPORT Layer : public LayerAnimationDelegate, void AddDeferredPaintRequest(); void RemoveDeferredPaintRequest(); + // |quality| is used as a multiplier to scale the temporary surface + // that might be created by the compositor to apply the backdrop filters. + // The filter will be applied on a surface |quality|^2 times the area of the + // original background. + // |quality| lower than one will decrease memory usage and increase + // performance. + void SetBackdropFilterQuality(const float quality); + bool IsPaintDeferredForTesting() const { return deferred_paint_requests_; } // Request trilinear filtering for layer. @@ -622,6 +630,8 @@ class COMPOSITOR_EXPORT Layer : public LayerAnimationDelegate, // means we should paint the layer. unsigned deferred_paint_requests_; + float backdrop_filter_quality_; + // The counter to maintain how many trilinear filtering requests we have. If // the value > 0, means we need to perform trilinear filtering on the layer. // If the value == 0, means we should not perform trilinear filtering on the diff --git a/chromium/ui/compositor/layer_animation_observer.cc b/chromium/ui/compositor/layer_animation_observer.cc index e5db5ecd5e5..2c77493b76d 100644 --- a/chromium/ui/compositor/layer_animation_observer.cc +++ b/chromium/ui/compositor/layer_animation_observer.cc @@ -159,8 +159,7 @@ void ImplicitAnimationObserver::UpdatePropertyAnimationStatus( ImplicitAnimationObserver::AnimationStatus ImplicitAnimationObserver::AnimationStatusForProperty( LayerAnimationElement::AnimatableProperty property) const { - PropertyAnimationStatusMap::const_iterator iter = - property_animation_status_.find(property); + auto iter = property_animation_status_.find(property); return iter == property_animation_status_.end() ? ANIMATION_STATUS_UNKNOWN : iter->second; } diff --git a/chromium/ui/compositor/layer_animator.cc b/chromium/ui/compositor/layer_animator.cc index d64ac0e6c1c..3c3efd6959c 100644 --- a/chromium/ui/compositor/layer_animator.cc +++ b/chromium/ui/compositor/layer_animator.cc @@ -414,7 +414,7 @@ void LayerAnimator::OnThreadedAnimationStarted( // The call to GetRunningAnimation made above already purged deleted // animations, so we are guaranteed that all the animations we iterate // over now are alive. - for (RunningAnimations::iterator iter = running_animations_.begin(); + for (auto iter = running_animations_.begin(); iter != running_animations_.end(); ++iter) { // Ensure that each sequence is only Started once, regardless of the // number of sequences in the group that have threaded first elements. @@ -541,7 +541,7 @@ LayerAnimationSequence* LayerAnimator::RemoveAnimation( bool is_running = false; // First remove from running animations - for (RunningAnimations::iterator iter = running_animations_.begin(); + for (auto iter = running_animations_.begin(); iter != running_animations_.end(); ++iter) { if ((*iter).sequence() == sequence) { running_animations_.erase(iter); @@ -638,7 +638,7 @@ void LayerAnimator::ClearAnimations() { LayerAnimator::RunningAnimation* LayerAnimator::GetRunningAnimation( LayerAnimationElement::AnimatableProperty property) { PurgeDeletedAnimations(); - for (RunningAnimations::iterator iter = running_animations_.begin(); + for (auto iter = running_animations_.begin(); iter != running_animations_.end(); ++iter) { if ((*iter).sequence()->properties() & property) return &(*iter); diff --git a/chromium/ui/compositor/layer_animator_collection.cc b/chromium/ui/compositor/layer_animator_collection.cc index 54951f0c2a3..fd9873e6104 100644 --- a/chromium/ui/compositor/layer_animator_collection.cc +++ b/chromium/ui/compositor/layer_animator_collection.cc @@ -46,9 +46,7 @@ bool LayerAnimatorCollection::HasActiveAnimators() const { void LayerAnimatorCollection::OnAnimationStep(base::TimeTicks now) { last_tick_time_ = now; std::set<scoped_refptr<LayerAnimator> > list = animators_; - for (std::set<scoped_refptr<LayerAnimator> >::iterator iter = list.begin(); - iter != list.end(); - ++iter) { + for (auto iter = list.begin(); iter != list.end(); ++iter) { // Make sure the animator is still valid. if (animators_.count(*iter) > 0) (*iter)->Step(now); diff --git a/chromium/ui/compositor/layer_animator_unittest.cc b/chromium/ui/compositor/layer_animator_unittest.cc index dc643869939..d735d2df611 100644 --- a/chromium/ui/compositor/layer_animator_unittest.cc +++ b/chromium/ui/compositor/layer_animator_unittest.cc @@ -10,7 +10,10 @@ #include "base/compiler_specific.h" #include "base/macros.h" +#include "base/run_loop.h" #include "base/strings/stringprintf.h" +#include "base/test/scoped_mock_clock_override.h" +#include "base/test/scoped_task_environment.h" #include "base/time/time.h" #include "cc/trees/layer_tree_host.h" #include "cc/trees/mutator_host.h" @@ -3250,6 +3253,8 @@ TEST(LayerAnimatorTest, AnimatorRemovedFromCollectionWhenLayerIsDestroyed) { } TEST(LayerAnimatorTest, LayerMovedBetweenCompositorsDuringAnimation) { + base::test::ScopedTaskEnvironment scoped_task_environment_( + base::test::ScopedTaskEnvironment::MainThreadType::UI); bool enable_pixel_output = false; ui::ContextFactory* context_factory = nullptr; ui::ContextFactoryPrivate* context_factory_private = nullptr; @@ -3314,6 +3319,8 @@ TEST(LayerAnimatorTest, LayerMovedBetweenCompositorsDuringAnimation) { } TEST(LayerAnimatorTest, ThreadedAnimationSurvivesIfLayerRemovedAdded) { + base::test::ScopedTaskEnvironment scoped_task_environment_( + base::test::ScopedTaskEnvironment::MainThreadType::UI); bool enable_pixel_output = false; ui::ContextFactory* context_factory = nullptr; ui::ContextFactoryPrivate* context_factory_private = nullptr; @@ -3355,6 +3362,115 @@ TEST(LayerAnimatorTest, ThreadedAnimationSurvivesIfLayerRemovedAdded) { TerminateContextFactoryForTests(); } +// A simple AnimationMetricsReporter class that remembers smoothness metric +// when animation completes. +class TestMetricsReporter : public ui::AnimationMetricsReporter { + public: + TestMetricsReporter() {} + ~TestMetricsReporter() override {} + + bool report_called() { return report_called_; } + int value() const { return value_; } + + protected: + void Report(int value) override { + value_ = value; + report_called_ = true; + } + + private: + bool report_called_ = false; + int value_ = -1; + + DISALLOW_COPY_AND_ASSIGN(TestMetricsReporter); +}; + +// Starts an animation and tests that incrementing compositor frame count can +// be used to report animation smoothness metrics. This verifies that when an +// animation is smooth (frame count matches refresh rate) that we receive a +// smoothness value of 100. +TEST(LayerAnimatorTest, ReportMetricsSmooth) { + base::ScopedMockClockOverride mock_clock_; + const base::TimeDelta kAnimationDuration = + base::TimeDelta::FromMilliseconds(100); + + std::unique_ptr<Layer> root(new Layer(LAYER_SOLID_COLOR)); + TestLayerAnimationDelegate delegate; + scoped_refptr<LayerAnimator> animator(CreateImplicitTestAnimator(&delegate)); + + std::unique_ptr<ui::LayerAnimationElement> animation_element = + ui::LayerAnimationElement::CreateColorElement(SK_ColorRED, + kAnimationDuration); + ui::LayerAnimationSequence* animation_sequence = + new ui::LayerAnimationSequence(std::move(animation_element)); + TestMetricsReporter reporter; + + animation_sequence->SetAnimationMetricsReporter(&reporter); + animator->StartAnimation(animation_sequence); + // Advances the frame count, simulating having produced 6 frames, and received + // an ack for each one. This implicitly simulates calling LayerAnimator::Step + // with an increased clock 6 times. + delegate.SetFrameNumber(6); + + // Advancing the clock does not implicitly advance frame count. It allows the + // next call of LayerAnimator::Step to detect that the animation is finished, + // thus triggering the processing of metrics reporting. + mock_clock_.Advance(kAnimationDuration); + animator->Step(mock_clock_.NowTicks()); + + CHECK(reporter.report_called()); + // With the clock being controlled, the animations should be smooth regardless + // of the test bots. Smoothness is based on a calculated duration of the + // animation when compared to the requested duration, as an integer + // percentage. With 100 being 100%. When the number of frames match the + // refresh rate, reporter should be notified of a smoothness of 100. + EXPECT_EQ(reporter.value(), 100); +} + +// Starts an animation and tests that incrementing compositor frame count can +// be used to report animation smoothness metrics. This verifies that when an +// animation is not smooth (frame count doesn't match refresh rate) that we +// receive a smoothness value that is not 100. +TEST(LayerAnimatorTest, ReportMetricsNotSmooth) { + base::ScopedMockClockOverride mock_clock_; + const base::TimeDelta kAnimationDuration = + base::TimeDelta::FromMilliseconds(100); + + std::unique_ptr<Layer> root(new Layer(LAYER_SOLID_COLOR)); + TestLayerAnimationDelegate delegate; + scoped_refptr<LayerAnimator> animator(CreateImplicitTestAnimator(&delegate)); + + std::unique_ptr<ui::LayerAnimationElement> animation_element = + ui::LayerAnimationElement::CreateColorElement(SK_ColorRED, + kAnimationDuration); + ui::LayerAnimationSequence* animation_sequence = + new ui::LayerAnimationSequence(std::move(animation_element)); + TestMetricsReporter reporter; + + animation_sequence->SetAnimationMetricsReporter(&reporter); + animator->StartAnimation(animation_sequence); + // Advances the frame count, simulating having produced 1 frame, and received + // an ack for it. This implicitly simulates calling LayerAnimator::Step with + // an increased clock 1 time. + delegate.SetFrameNumber(1); + + // Advancing the clock does not implicitly advance frame count. It allows the + // next call of LayerAnimator::Step to detect that the animation is finished, + // thus triggering the processing of metrics reporting. + mock_clock_.Advance(kAnimationDuration); + animator->Step(mock_clock_.NowTicks()); + + CHECK(reporter.report_called()); + // With the clock being controlled, we have direct control of the smoothness + // regardless of the test bots. Smoothness is based on a calculated duration + // of the animation when compared to the requested duration, as an integer + // percentage. With 100 being 100%. With a single frame being generated over + // 100ms, the reporter should be notified of a smoothness matching the + // interval between refreshes. + int expected = base::Time::kMillisecondsPerSecond / delegate.GetRefreshRate(); + EXPECT_EQ(reporter.value(), expected); +} + class LayerOwnerAnimationObserver : public LayerAnimationObserver { public: explicit LayerOwnerAnimationObserver(LayerAnimator* animator) diff --git a/chromium/ui/compositor/layer_unittest.cc b/chromium/ui/compositor/layer_unittest.cc index 57951bf6f4f..190cf162069 100644 --- a/chromium/ui/compositor/layer_unittest.cc +++ b/chromium/ui/compositor/layer_unittest.cc @@ -20,6 +20,7 @@ #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/scoped_task_environment.h" #include "base/trace_event/trace_event.h" #include "build/build_config.h" #include "cc/animation/animation_events.h" @@ -129,7 +130,9 @@ class DrawFadedStringLayerDelegate : public LayerDelegate { class LayerWithRealCompositorTest : public testing::Test { public: - LayerWithRealCompositorTest() { + LayerWithRealCompositorTest() + : scoped_task_environment_( + base::test::ScopedTaskEnvironment::MainThreadType::UI) { if (base::PathService::Get(gfx::DIR_TEST_DATA, &test_data_directory_)) { test_data_directory_ = test_data_directory_.AppendASCII("compositor"); } else { @@ -271,6 +274,7 @@ class LayerWithRealCompositorTest : public testing::Test { std::unique_ptr<base::RunLoop> run_loop_; }; + base::test::ScopedTaskEnvironment scoped_task_environment_; std::unique_ptr<TestCompositorHost> compositor_host_; // The root directory for test files. @@ -489,7 +493,9 @@ TEST_F(LayerWithRealCompositorTest, Hierarchy) { class LayerWithDelegateTest : public testing::Test { public: - LayerWithDelegateTest() {} + LayerWithDelegateTest() + : scoped_task_environment_( + base::test::ScopedTaskEnvironment::MainThreadType::UI) {} ~LayerWithDelegateTest() override {} // Overridden from testing::Test: @@ -556,6 +562,7 @@ class LayerWithDelegateTest : public testing::Test { } private: + base::test::ScopedTaskEnvironment scoped_task_environment_; std::unique_ptr<TestCompositorHost> compositor_host_; DISALLOW_COPY_AND_ASSIGN(LayerWithDelegateTest); @@ -903,34 +910,22 @@ TEST_F(LayerWithDelegateTest, SurfaceLayerCloneAndMirror) { layer->SetShowPrimarySurface(surface_id_one, gfx::Size(10, 10), SK_ColorWHITE, cc::DeadlinePolicy::UseDefaultDeadline(), false); EXPECT_FALSE(layer->StretchContentToFillBounds()); - EXPECT_TRUE(static_cast<cc::SurfaceLayer*>(layer->cc_layer_for_testing()) - ->surface_hit_testable()); auto clone = layer->Clone(); EXPECT_FALSE(clone->StretchContentToFillBounds()); - EXPECT_TRUE(static_cast<cc::SurfaceLayer*>(clone->cc_layer_for_testing()) - ->surface_hit_testable()); auto mirror = layer->Mirror(); EXPECT_FALSE(mirror->StretchContentToFillBounds()); - EXPECT_TRUE(static_cast<cc::SurfaceLayer*>(mirror->cc_layer_for_testing()) - ->surface_hit_testable()); local_surface_id = allocator.GenerateId(); viz::SurfaceId surface_id_two(arbitrary_frame_sink, local_surface_id); layer->SetShowPrimarySurface(surface_id_two, gfx::Size(10, 10), SK_ColorWHITE, cc::DeadlinePolicy::UseDefaultDeadline(), true); EXPECT_TRUE(layer->StretchContentToFillBounds()); - EXPECT_TRUE(static_cast<cc::SurfaceLayer*>(layer->cc_layer_for_testing()) - ->surface_hit_testable()); clone = layer->Clone(); EXPECT_TRUE(clone->StretchContentToFillBounds()); - EXPECT_TRUE(static_cast<cc::SurfaceLayer*>(clone->cc_layer_for_testing()) - ->surface_hit_testable()); mirror = layer->Mirror(); EXPECT_TRUE(mirror->StretchContentToFillBounds()); - EXPECT_TRUE(static_cast<cc::SurfaceLayer*>(mirror->cc_layer_for_testing()) - ->surface_hit_testable()); } class LayerWithNullDelegateTest : public LayerWithDelegateTest { @@ -2675,53 +2670,6 @@ TEST_F(LayerWithRealCompositorTest, CompositorAnimationObserverTest) { EXPECT_TRUE(animation_observer.shutdown()); } -// A simple AnimationMetricsReporter class that remembers smoothness metric -// when animation completes. -class TestMetricsReporter : public ui::AnimationMetricsReporter { - public: - TestMetricsReporter() {} - ~TestMetricsReporter() override {} - - bool report_called() { return report_called_; } - int value() const { return value_; } - - protected: - void Report(int value) override { - value_ = value; - report_called_ = true; - } - - private: - bool report_called_ = false; - int value_ = -1; - - DISALLOW_COPY_AND_ASSIGN(TestMetricsReporter); -}; - -// Starts an animation and tests that incrementing compositor frame count can -// be used to report animation smoothness metrics. -// Flaky test crbug.com/709080 -TEST_F(LayerWithRealCompositorTest, DISABLED_ReportMetrics) { - std::unique_ptr<Layer> root(CreateLayer(LAYER_SOLID_COLOR)); - GetCompositor()->SetRootLayer(root.get()); - LayerAnimator* animator = root->GetAnimator(); - std::unique_ptr<ui::LayerAnimationElement> animation_element = - ui::LayerAnimationElement::CreateColorElement( - SK_ColorRED, base::TimeDelta::FromMilliseconds(100)); - ui::LayerAnimationSequence* animation_sequence = - new ui::LayerAnimationSequence(std::move(animation_element)); - TestMetricsReporter reporter; - animation_sequence->SetAnimationMetricsReporter(&reporter); - animator->StartAnimation(animation_sequence); - while (!reporter.report_called()) - WaitForSwap(); - ResetCompositor(); - // Even though most of the time 100% smooth animations are expected, on the - // test bots this cannot be guaranteed. Therefore simply check that some - // value was reported. - EXPECT_GT(reporter.value(), 0); -} - TEST(LayerDebugInfoTest, LayerNameDoesNotClobber) { Layer layer(LAYER_NOT_DRAWN); layer.set_name("foo"); diff --git a/chromium/ui/display/BUILD.gn b/chromium/ui/display/BUILD.gn index bd7de89cf5a..e5185ec1c71 100644 --- a/chromium/ui/display/BUILD.gn +++ b/chromium/ui/display/BUILD.gn @@ -2,11 +2,12 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +import("//build/config/jumbo.gni") import("//build/config/ui.gni") import("//testing/test.gni") import("//ui/display/display.gni") -component("display") { +jumbo_component("display") { sources = [ "display.cc", "display.h", @@ -47,12 +48,17 @@ component("display") { "win/screen_win.h", "win/screen_win_display.cc", "win/screen_win_display.h", + "win/uwp_text_scale_factor.cc", + "win/uwp_text_scale_factor.h", ] + configs += [ "//build/config/compiler:wexit_time_destructors" ] + defines = [ "DISPLAY_IMPLEMENTATION" ] public_deps = [ "//ui/display/types", + "//ui/gfx:color_space", "//ui/gfx:gfx", ] @@ -101,7 +107,7 @@ component("display_manager_test_api") { ] } -static_library("test_support") { +jumbo_static_library("test_support") { testonly = true sources = [ "test/display_matchers.cc", diff --git a/chromium/ui/display/display.cc b/chromium/ui/display/display.cc index 324148f8469..68a5b3a9cc2 100644 --- a/chromium/ui/display/display.cc +++ b/chromium/ui/display/display.cc @@ -58,6 +58,33 @@ float GetForcedDeviceScaleFactorImpl() { int64_t internal_display_id_ = -1; +gfx::ColorSpace ForcedColorProfileStringToColorSpace(const std::string& value) { + if (value == "srgb") + return gfx::ColorSpace::CreateSRGB(); + if (value == "display-p3-d65") + return gfx::ColorSpace::CreateDisplayP3D65(); + if (value == "scrgb-linear") + return gfx::ColorSpace::CreateSCRGBLinear(); + if (value == "extended-srgb") + return gfx::ColorSpace::CreateExtendedSRGB(); + if (value == "generic-rgb") { + return gfx::ColorSpace(gfx::ColorSpace::PrimaryID::APPLE_GENERIC_RGB, + gfx::ColorSpace::TransferID::GAMMA18); + } + if (value == "color-spin-gamma24") { + // Run this color profile through an ICC profile. The resulting color space + // is slightly different from the input color space, and removing the ICC + // profile would require rebaselineing many layout tests. + gfx::ColorSpace color_space( + gfx::ColorSpace::PrimaryID::WIDE_GAMUT_COLOR_SPIN, + gfx::ColorSpace::TransferID::GAMMA24); + return gfx::ICCProfile::FromParametricColorSpace(color_space) + .GetColorSpace(); + } + LOG(ERROR) << "Invalid forced color profile: \"" << value << "\""; + return gfx::ColorSpace::CreateSRGB(); +} + } // namespace bool CompareDisplayIds(int64_t id1, int64_t id2) { @@ -103,42 +130,33 @@ void Display::SetForceDeviceScaleFactor(double dsf) { } // static -gfx::ColorSpace Display::GetForcedColorProfile() { - DCHECK(HasForceColorProfile()); +gfx::ColorSpace Display::GetForcedDisplayColorProfile() { + DCHECK(HasForceDisplayColorProfile()); std::string value = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( - switches::kForceColorProfile); - if (value == "srgb") { - return gfx::ColorSpace::CreateSRGB(); - } else if (value == "display-p3-d65") { - return gfx::ColorSpace::CreateDisplayP3D65(); - } else if (value == "scrgb-linear") { - return gfx::ColorSpace::CreateSCRGBLinear(); - } else if (value == "extended-srgb") { - return gfx::ColorSpace::CreateExtendedSRGB(); - } else if (value == "generic-rgb") { - return gfx::ColorSpace(gfx::ColorSpace::PrimaryID::APPLE_GENERIC_RGB, - gfx::ColorSpace::TransferID::GAMMA18); - } else if (value == "color-spin-gamma24") { - // Run this color profile through an ICC profile. The resulting color space - // is slightly different from the input color space, and removing the ICC - // profile would require rebaselineing many layout tests. - gfx::ColorSpace color_space( - gfx::ColorSpace::PrimaryID::WIDE_GAMUT_COLOR_SPIN, - gfx::ColorSpace::TransferID::GAMMA24); - return gfx::ICCProfile::FromParametricColorSpace(color_space) - .GetColorSpace(); - } - LOG(ERROR) << "Invalid forced color profile"; - return gfx::ColorSpace::CreateSRGB(); + switches::kForceDisplayColorProfile); + return ForcedColorProfileStringToColorSpace(value); } // static -bool Display::HasForceColorProfile() { - static bool has_force_color_profile = - base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kForceColorProfile); - return has_force_color_profile; +bool Display::HasForceDisplayColorProfile() { + return base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kForceDisplayColorProfile); +} + +// static +gfx::ColorSpace Display::GetForcedRasterColorProfile() { + DCHECK(HasForceRasterColorProfile()); + std::string value = + base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII( + switches::kForceRasterColorProfile); + return ForcedColorProfileStringToColorSpace(value); +} + +// static +bool Display::HasForceRasterColorProfile() { + return base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kForceRasterColorProfile); } // static @@ -196,8 +214,8 @@ Display::Display(int64_t id, const gfx::Rect& bounds) color_space_(gfx::ColorSpace::CreateSRGB()), color_depth_(DEFAULT_BITS_PER_PIXEL), depth_per_component_(DEFAULT_BITS_PER_COMPONENT) { - if (HasForceColorProfile()) - SetColorSpaceAndDepth(GetForcedColorProfile()); + if (HasForceDisplayColorProfile()) + SetColorSpaceAndDepth(GetForcedDisplayColorProfile()); #if defined(USE_AURA) SetScaleAndBounds(device_scale_factor_, bounds); #endif diff --git a/chromium/ui/display/display.h b/chromium/ui/display/display.h index a788c0e64e7..53f3e95c824 100644 --- a/chromium/ui/display/display.h +++ b/chromium/ui/display/display.h @@ -94,11 +94,19 @@ class DISPLAY_EXPORT Display final { // Returns the forced display color profile, which is given by // "--force-color-profile". - static gfx::ColorSpace GetForcedColorProfile(); + static gfx::ColorSpace GetForcedDisplayColorProfile(); // Indicates if a display color profile is being explicitly enforced from the // command line via "--force-color-profile". - static bool HasForceColorProfile(); + static bool HasForceDisplayColorProfile(); + + // Returns the forced raster color profile, which is given by + // "--force-raster-color-profile". + static gfx::ColorSpace GetForcedRasterColorProfile(); + + // Indicates if a raster color profile is being explicitly enforced from the + // command line via "--force-raster-color-profile". + static bool HasForceRasterColorProfile(); // Indicates if the display color profile being forced should be ensured to // be in use by the operating system as well. diff --git a/chromium/ui/display/display_change_notifier.cc b/chromium/ui/display/display_change_notifier.cc index 3a5cfd0c830..9c1e6eca641 100644 --- a/chromium/ui/display/display_change_notifier.cc +++ b/chromium/ui/display/display_change_notifier.cc @@ -44,7 +44,7 @@ void DisplayChangeNotifier::NotifyDisplaysChanged( const std::vector<Display>& old_displays, const std::vector<Display>& new_displays) { // Display present in old_displays but not in new_displays has been removed. - std::vector<Display>::const_iterator old_it = old_displays.begin(); + auto old_it = old_displays.begin(); for (; old_it != old_displays.end(); ++old_it) { if (std::find_if(new_displays.begin(), new_displays.end(), DisplayComparator(*old_it)) == new_displays.end()) { @@ -55,10 +55,10 @@ void DisplayChangeNotifier::NotifyDisplaysChanged( // Display present in new_displays but not in old_displays has been added. // Display present in both might have been modified. - for (std::vector<Display>::const_iterator new_it = new_displays.begin(); - new_it != new_displays.end(); ++new_it) { - std::vector<Display>::const_iterator old_it = std::find_if( - old_displays.begin(), old_displays.end(), DisplayComparator(*new_it)); + for (auto new_it = new_displays.begin(); new_it != new_displays.end(); + ++new_it) { + auto old_it = std::find_if(old_displays.begin(), old_displays.end(), + DisplayComparator(*new_it)); if (old_it == old_displays.end()) { for (DisplayObserver& observer : observer_list_) diff --git a/chromium/ui/display/display_change_notifier.h b/chromium/ui/display/display_change_notifier.h index 5c61896bd76..6e3c436c67d 100644 --- a/chromium/ui/display/display_change_notifier.h +++ b/chromium/ui/display/display_change_notifier.h @@ -32,7 +32,7 @@ class DISPLAY_EXPORT DisplayChangeNotifier { private: // The observers that need to be notified when a display is modified, added // or removed. - base::ObserverList<DisplayObserver>::Unchecked observer_list_; + base::ObserverList<DisplayObserver> observer_list_; DISALLOW_COPY_AND_ASSIGN(DisplayChangeNotifier); }; diff --git a/chromium/ui/display/display_list.h b/chromium/ui/display/display_list.h index 91f0042594a..295dc3fb57a 100644 --- a/chromium/ui/display/display_list.h +++ b/chromium/ui/display/display_list.h @@ -82,9 +82,7 @@ class DISPLAY_EXPORT DisplayList { // Removes the Display with the specified id. void RemoveDisplay(int64_t id); - base::ObserverList<DisplayObserver>::Unchecked* observers() { - return &observers_; - } + base::ObserverList<DisplayObserver>* observers() { return &observers_; } private: friend class DisplayListObserverLock; @@ -101,7 +99,7 @@ class DISPLAY_EXPORT DisplayList { std::vector<Display> displays_; int primary_display_index_ = -1; - base::ObserverList<DisplayObserver>::Unchecked observers_; + base::ObserverList<DisplayObserver> observers_; int observer_suspend_lock_count_ = 0; diff --git a/chromium/ui/display/display_observer.h b/chromium/ui/display/display_observer.h index 568852db6af..e487ce3fe5b 100644 --- a/chromium/ui/display/display_observer.h +++ b/chromium/ui/display/display_observer.h @@ -7,6 +7,7 @@ #include <stdint.h> +#include "base/observer_list_types.h" #include "ui/display/display_export.h" namespace display { @@ -15,7 +16,7 @@ class Display; // Observers for display configuration changes. // TODO(oshima): consolidate |WorkAreaWatcherObserver| and // |DisplaySettingsProvier|. crbug.com/122863. -class DISPLAY_EXPORT DisplayObserver { +class DISPLAY_EXPORT DisplayObserver : public base::CheckedObserver { public: enum DisplayMetric { DISPLAY_METRIC_NONE = 0, @@ -48,7 +49,7 @@ class DISPLAY_EXPORT DisplayObserver { uint32_t changed_metrics); protected: - virtual ~DisplayObserver(); + ~DisplayObserver() override; }; } // namespace display diff --git a/chromium/ui/display/display_switches.cc b/chromium/ui/display/display_switches.cc index de1e0577e5d..40b7fa176db 100644 --- a/chromium/ui/display/display_switches.cc +++ b/chromium/ui/display/display_switches.cc @@ -25,7 +25,11 @@ const char kEnsureForcedColorProfile[] = "ensure-forced-color-profile"; // Force all monitors to be treated as though they have the specified color // profile. Accepted values are "srgb" and "generic-rgb" (currently used by Mac // layout tests) and "color-spin-gamma24" (used by layout tests). -const char kForceColorProfile[] = "force-color-profile"; +const char kForceDisplayColorProfile[] = "force-color-profile"; + +// Force rastering to take place in the specified color profile. Accepted values +// are the same as for the kForceDisplayColorProfile case above. +const char kForceRasterColorProfile[] = "force-raster-color-profile"; // Overrides the device scale factor for the browser UI and the contents. const char kForceDeviceScaleFactor[] = "force-device-scale-factor"; diff --git a/chromium/ui/display/display_switches.h b/chromium/ui/display/display_switches.h index da146eb5359..0079182e025 100644 --- a/chromium/ui/display/display_switches.h +++ b/chromium/ui/display/display_switches.h @@ -16,8 +16,9 @@ namespace switches { DISPLAY_EXPORT extern const char kDisableMultiMirroring[]; DISPLAY_EXPORT extern const char kEnableSoftwareMirroring[]; DISPLAY_EXPORT extern const char kEnsureForcedColorProfile[]; -DISPLAY_EXPORT extern const char kForceColorProfile[]; DISPLAY_EXPORT extern const char kForceDeviceScaleFactor[]; +DISPLAY_EXPORT extern const char kForceDisplayColorProfile[]; +DISPLAY_EXPORT extern const char kForceRasterColorProfile[]; // TODO(kylechar): This overlaps with --screen-config. Unify flags and remove. DISPLAY_EXPORT extern const char kHostWindowBounds[]; DISPLAY_EXPORT extern const char kScreenConfig[]; diff --git a/chromium/ui/display/mac/screen_mac.mm b/chromium/ui/display/mac/screen_mac.mm index 99a12851125..ec1fc4edf55 100644 --- a/chromium/ui/display/mac/screen_mac.mm +++ b/chromium/ui/display/mac/screen_mac.mm @@ -93,7 +93,7 @@ Display BuildDisplayForScreen(NSScreen* screen) { } icc_profile.HistogramDisplay(display.id()); gfx::ColorSpace screen_color_space = icc_profile.GetColorSpace(); - if (Display::HasForceColorProfile()) { + if (Display::HasForceDisplayColorProfile()) { if (Display::HasEnsureForcedColorProfile()) { CHECK_EQ(screen_color_space, display.color_space()) << "The display's color space does not match the color space that " diff --git a/chromium/ui/display/manager/BUILD.gn b/chromium/ui/display/manager/BUILD.gn index f43c0dea56d..fda805f36f3 100644 --- a/chromium/ui/display/manager/BUILD.gn +++ b/chromium/ui/display/manager/BUILD.gn @@ -52,6 +52,8 @@ jumbo_component("manager") { ] } + configs += [ "//build/config/compiler:wexit_time_destructors" ] + public_deps = [ "//ui/display", ] diff --git a/chromium/ui/display/manager/display_manager.cc b/chromium/ui/display/manager/display_manager.cc index 06770862f57..06e2cff23c5 100644 --- a/chromium/ui/display/manager/display_manager.cc +++ b/chromium/ui/display/manager/display_manager.cc @@ -18,6 +18,7 @@ #include "base/command_line.h" #include "base/logging.h" #include "base/metrics/histogram_macros.h" +#include "base/no_destructor.h" #include "base/run_loop.h" #include "base/stl_util.h" #include "base/strings/string_number_conversions.h" @@ -394,9 +395,9 @@ const DisplayLayout& DisplayManager::GetCurrentDisplayLayout() const { } DLOG(ERROR) << "DisplayLayout is requested for single display"; // On release build, just fallback to default instead of blowing up. - static DisplayLayout layout; - layout.primary_id = active_display_list_[0].id(); - return layout; + static base::NoDestructor<DisplayLayout> layout; + layout->primary_id = active_display_list_[0].id(); + return *layout; } const DisplayLayout& DisplayManager::GetCurrentResolvedDisplayLayout() const { @@ -714,8 +715,7 @@ void DisplayManager::RegisterDisplayRotationProperties( bool DisplayManager::GetSelectedModeForDisplayId( int64_t display_id, ManagedDisplayMode* mode) const { - std::map<int64_t, ManagedDisplayMode>::const_iterator iter = - display_modes_.find(display_id); + auto iter = display_modes_.find(display_id); if (iter == display_modes_.end()) return false; *mode = iter->second; @@ -741,8 +741,7 @@ bool DisplayManager::IsDisplayUIScalingEnabled() const { } gfx::Insets DisplayManager::GetOverscanInsets(int64_t display_id) const { - std::map<int64_t, ManagedDisplayInfo>::const_iterator it = - display_info_.find(display_id); + auto it = display_info_.find(display_id); return (it != display_info_.end()) ? it->second.overscan_insets_in_dip() : gfx::Insets(); } @@ -943,7 +942,7 @@ void DisplayManager::UpdateDisplaysWith( std::map<size_t, uint32_t> display_changes; std::vector<size_t> added_display_indices; - Displays::iterator curr_iter = active_display_list_.begin(); + auto curr_iter = active_display_list_.begin(); DisplayInfoList::const_iterator new_info_iter = new_display_info_list.begin(); while (curr_iter != active_display_list_.end() || @@ -1068,8 +1067,8 @@ void DisplayManager::UpdateDisplaysWith( bool notify_primary_change = delegate_ ? old_primary.id() != screen_->GetPrimaryDisplay().id() : false; - for (std::map<size_t, uint32_t>::iterator iter = display_changes.begin(); - iter != display_changes.end(); ++iter) { + for (auto iter = display_changes.begin(); iter != display_changes.end(); + ++iter) { uint32_t metrics = iter->second; const Display& updated_display = active_display_list_[iter->first]; @@ -1240,8 +1239,7 @@ const ManagedDisplayInfo& DisplayManager::GetDisplayInfo( int64_t display_id) const { DCHECK_NE(kInvalidDisplayId, display_id); - std::map<int64_t, ManagedDisplayInfo>::const_iterator iter = - display_info_.find(display_id); + auto iter = display_info_.find(display_id); CHECK(iter != display_info_.end()) << display_id; return iter->second; } @@ -1260,8 +1258,7 @@ std::string DisplayManager::GetDisplayNameForId(int64_t id) const { if (id == kInvalidDisplayId) return l10n_util::GetStringUTF8(IDS_DISPLAY_NAME_UNKNOWN); - std::map<int64_t, ManagedDisplayInfo>::const_iterator iter = - display_info_.find(id); + auto iter = display_info_.find(id); if (iter != display_info_.end() && !iter->second.name().empty()) return iter->second.name(); diff --git a/chromium/ui/display/manager/display_manager.h b/chromium/ui/display/manager/display_manager.h index d9fc6ef7f1a..6f0d784463d 100644 --- a/chromium/ui/display/manager/display_manager.h +++ b/chromium/ui/display/manager/display_manager.h @@ -655,7 +655,7 @@ class DISPLAY_MANAGER_EXPORT DisplayManager base::Closure created_mirror_window_; - base::ObserverList<DisplayObserver>::Unchecked observers_; + base::ObserverList<DisplayObserver> observers_; // Not empty if mixed mirror mode should be turned on (the specified source // display is mirrored to the specified destination displays). Empty if mixed diff --git a/chromium/ui/display/manager/touch_device_manager.cc b/chromium/ui/display/manager/touch_device_manager.cc index b71cfa51475..41606b4eeb9 100644 --- a/chromium/ui/display/manager/touch_device_manager.cc +++ b/chromium/ui/display/manager/touch_device_manager.cc @@ -715,8 +715,10 @@ std::ostream& operator<<(std::ostream& os, bool HasExternalTouchscreenDevice() { for (const auto& device : ui::InputDeviceManager::GetInstance()->GetTouchscreenDevices()) { - if (device.type == ui::InputDeviceType::INPUT_DEVICE_EXTERNAL) + if (device.type == ui::InputDeviceType::INPUT_DEVICE_USB || + device.type == ui::InputDeviceType::INPUT_DEVICE_BLUETOOTH) { return true; + } } return false; } diff --git a/chromium/ui/display/manager/touch_device_manager_unittest.cc b/chromium/ui/display/manager/touch_device_manager_unittest.cc index acc60f539f8..54c57f50460 100644 --- a/chromium/ui/display/manager/touch_device_manager_unittest.cc +++ b/chromium/ui/display/manager/touch_device_manager_unittest.cc @@ -142,7 +142,7 @@ TEST_F(TouchAssociationTest, ManyTouchscreens) { std::vector<ui::TouchscreenDevice> devices; for (int i = 0; i < 5; ++i) { devices.push_back(CreateTouchscreenDevice( - i, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(256, 256))); + i, ui::InputDeviceType::INPUT_DEVICE_USB, gfx::Size(256, 256))); } DisplayInfoList displays; @@ -159,9 +159,9 @@ TEST_F(TouchAssociationTest, ManyTouchscreens) { TEST_F(TouchAssociationTest, OneToOneMapping) { std::vector<ui::TouchscreenDevice> devices; devices.push_back(CreateTouchscreenDevice( - 1, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(800, 600))); + 1, ui::InputDeviceType::INPUT_DEVICE_USB, gfx::Size(800, 600))); devices.push_back(CreateTouchscreenDevice( - 2, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(1024, 768))); + 2, ui::InputDeviceType::INPUT_DEVICE_USB, gfx::Size(1024, 768))); test::ScopedSetInternalDisplayId set_internal(display_manager(), displays_[0].id()); @@ -178,7 +178,7 @@ TEST_F(TouchAssociationTest, OneToOneMapping) { TEST_F(TouchAssociationTest, MapToCorrectDisplaySize) { std::vector<ui::TouchscreenDevice> devices; devices.push_back(CreateTouchscreenDevice( - 2, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(1024, 768))); + 2, ui::InputDeviceType::INPUT_DEVICE_USB, gfx::Size(1024, 768))); test::ScopedSetInternalDisplayId set_internal(display_manager(), displays_[0].id()); @@ -194,9 +194,9 @@ TEST_F(TouchAssociationTest, MapToCorrectDisplaySize) { TEST_F(TouchAssociationTest, MapWhenSizeDiffersByOne) { std::vector<ui::TouchscreenDevice> devices; devices.push_back(CreateTouchscreenDevice( - 1, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(801, 600))); + 1, ui::InputDeviceType::INPUT_DEVICE_USB, gfx::Size(801, 600))); devices.push_back(CreateTouchscreenDevice( - 2, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(1023, 768))); + 2, ui::InputDeviceType::INPUT_DEVICE_USB, gfx::Size(1023, 768))); test::ScopedSetInternalDisplayId set_internal(display_manager(), displays_[0].id()); @@ -213,9 +213,9 @@ TEST_F(TouchAssociationTest, MapWhenSizeDiffersByOne) { TEST_F(TouchAssociationTest, MapWhenSizesDoNotMatch) { std::vector<ui::TouchscreenDevice> devices; devices.push_back(CreateTouchscreenDevice( - 1, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(1022, 768))); + 1, ui::InputDeviceType::INPUT_DEVICE_USB, gfx::Size(1022, 768))); devices.push_back(CreateTouchscreenDevice( - 2, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(802, 600))); + 2, ui::InputDeviceType::INPUT_DEVICE_USB, gfx::Size(802, 600))); DisplayInfoList displays; displays.push_back(displays_[0]); @@ -236,7 +236,7 @@ TEST_F(TouchAssociationTest, MapWhenSizesDoNotMatch) { TEST_F(TouchAssociationTest, MapInternalTouchscreen) { std::vector<ui::TouchscreenDevice> devices; devices.push_back(CreateTouchscreenDevice( - 1, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(1920, 1080))); + 1, ui::InputDeviceType::INPUT_DEVICE_USB, gfx::Size(1920, 1080))); devices.push_back(CreateTouchscreenDevice( 2, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, gfx::Size(9999, 888))); @@ -279,7 +279,7 @@ TEST_F(TouchAssociationTest, MultipleInternalAndExternal) { devices.push_back(CreateTouchscreenDevice( 2, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, gfx::Size(1920, 1080))); devices.push_back(CreateTouchscreenDevice( - 3, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(1024, 768))); + 3, ui::InputDeviceType::INPUT_DEVICE_USB, gfx::Size(1024, 768))); test::ScopedSetInternalDisplayId set_internal(display_manager(), displays_[0].id()); @@ -298,7 +298,7 @@ TEST_F(TouchAssociationTest, MultipleInternalAndExternal) { TEST_F(TouchAssociationTest, TestWithNoInternalDisplay) { std::vector<ui::TouchscreenDevice> devices; devices.push_back(CreateTouchscreenDevice( - 1, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(1920, 1080))); + 1, ui::InputDeviceType::INPUT_DEVICE_USB, gfx::Size(1920, 1080))); devices.push_back(CreateTouchscreenDevice( 2, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, gfx::Size(9999, 888))); @@ -315,11 +315,11 @@ TEST_F(TouchAssociationTest, TestWithNoInternalDisplay) { TEST_F(TouchAssociationTest, MatchRemainingDevicesToInternalDisplay) { std::vector<ui::TouchscreenDevice> devices; devices.push_back(CreateTouchscreenDevice( - 1, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(123, 456))); + 1, ui::InputDeviceType::INPUT_DEVICE_USB, gfx::Size(123, 456))); devices.push_back(CreateTouchscreenDevice( - 2, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(234, 567))); + 2, ui::InputDeviceType::INPUT_DEVICE_USB, gfx::Size(234, 567))); devices.push_back(CreateTouchscreenDevice( - 3, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(345, 678))); + 3, ui::InputDeviceType::INPUT_DEVICE_USB, gfx::Size(345, 678))); test::ScopedSetInternalDisplayId set_internal(display_manager(), displays_[0].id()); @@ -338,11 +338,11 @@ TEST_F(TouchAssociationTest, MatchRemainingDevicesWithNoInternalDisplayPresent) { std::vector<ui::TouchscreenDevice> devices; devices.push_back(CreateTouchscreenDevice( - 1, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(123, 456))); + 1, ui::InputDeviceType::INPUT_DEVICE_USB, gfx::Size(123, 456))); devices.push_back(CreateTouchscreenDevice( - 2, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(234, 567))); + 2, ui::InputDeviceType::INPUT_DEVICE_USB, gfx::Size(234, 567))); devices.push_back(CreateTouchscreenDevice( - 3, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(345, 678))); + 3, ui::InputDeviceType::INPUT_DEVICE_USB, gfx::Size(345, 678))); touch_device_manager()->AssociateTouchscreens(&displays_, devices); @@ -364,11 +364,11 @@ class TouchAssociationFromPrefTest : public TouchAssociationTest { TouchDeviceManager::TouchAssociationMap touch_associations; devices_.push_back(CreateTouchscreenDevice( - 1, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(1920, 1080))); + 1, ui::InputDeviceType::INPUT_DEVICE_USB, gfx::Size(1920, 1080))); devices_.push_back(CreateTouchscreenDevice( - 2, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(1024, 768))); + 2, ui::InputDeviceType::INPUT_DEVICE_USB, gfx::Size(1024, 768))); devices_.push_back(CreateTouchscreenDevice( - 3, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(640, 480))); + 3, ui::InputDeviceType::INPUT_DEVICE_USB, gfx::Size(640, 480))); devices_.push_back(CreateTouchscreenDevice( 4, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, gfx::Size(800, 600))); @@ -600,7 +600,7 @@ class TouchAssociationWithDuplicateDeviceTest : public TouchAssociationTest { // Create a device with name |device_name_1| connected to |ports[0]|. devices_.push_back(CreateTouchscreenDevice( - 1, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(1920, 1080))); + 1, ui::InputDeviceType::INPUT_DEVICE_USB, gfx::Size(1920, 1080))); devices_.back().name = device_name_1; devices_.back().phys = ports[0]; @@ -608,7 +608,7 @@ class TouchAssociationWithDuplicateDeviceTest : public TouchAssociationTest { int product_id = devices_.back().product_id; devices_.push_back(CreateTouchscreenDevice( - 2, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(1920, 1080))); + 2, ui::InputDeviceType::INPUT_DEVICE_USB, gfx::Size(1920, 1080))); // Create another device with the same name but different port. Ensure that // the touch device idnetifier is the same by setting the same vendor id, @@ -619,7 +619,7 @@ class TouchAssociationWithDuplicateDeviceTest : public TouchAssociationTest { devices_.back().product_id = product_id; devices_.push_back(CreateTouchscreenDevice( - 3, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(1920, 1080))); + 3, ui::InputDeviceType::INPUT_DEVICE_USB, gfx::Size(1920, 1080))); devices_.back().name = device_name_1; devices_.back().phys = ports[2]; devices_.back().vendor_id = vendor_id; @@ -629,7 +629,7 @@ class TouchAssociationWithDuplicateDeviceTest : public TouchAssociationTest { 4, ui::InputDeviceType::INPUT_DEVICE_INTERNAL, gfx::Size(800, 600))); devices_.push_back(CreateTouchscreenDevice( - 5, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(4096, 4096))); + 5, ui::InputDeviceType::INPUT_DEVICE_USB, gfx::Size(4096, 4096))); devices_.back().name = device_name_2; devices_.back().phys = ports[3]; @@ -637,7 +637,7 @@ class TouchAssociationWithDuplicateDeviceTest : public TouchAssociationTest { product_id = devices_.back().product_id; devices_.push_back(CreateTouchscreenDevice( - 6, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, gfx::Size(4096, 4096))); + 6, ui::InputDeviceType::INPUT_DEVICE_USB, gfx::Size(4096, 4096))); devices_.back().name = device_name_2; devices_.back().phys = ports[4]; devices_.back().vendor_id = vendor_id; diff --git a/chromium/ui/display/manager/touch_transform_controller_unittest.cc b/chromium/ui/display/manager/touch_transform_controller_unittest.cc index 131bb543942..ba7cdd1d88c 100644 --- a/chromium/ui/display/manager/touch_transform_controller_unittest.cc +++ b/chromium/ui/display/manager/touch_transform_controller_unittest.cc @@ -41,7 +41,7 @@ ui::TouchDeviceTransform CreateTouchDeviceTransform( ui::TouchscreenDevice CreateTouchscreenDevice(unsigned int id, const gfx::Size& size) { - return ui::TouchscreenDevice(id, ui::InputDeviceType::INPUT_DEVICE_EXTERNAL, + return ui::TouchscreenDevice(id, ui::InputDeviceType::INPUT_DEVICE_USB, std::string(), size, 0); } diff --git a/chromium/ui/display/util/BUILD.gn b/chromium/ui/display/util/BUILD.gn index e7870883a11..ebf6b16c1c2 100644 --- a/chromium/ui/display/util/BUILD.gn +++ b/chromium/ui/display/util/BUILD.gn @@ -16,6 +16,8 @@ jumbo_component("util") { "edid_parser.h", ] + configs += [ "//build/config/compiler:wexit_time_destructors" ] + defines = [ "DISPLAY_UTIL_IMPLEMENTATION" ] deps = [ diff --git a/chromium/ui/display/util/edid_parser.h b/chromium/ui/display/util/edid_parser.h index 5323f527da5..20516a29767 100644 --- a/chromium/ui/display/util/edid_parser.h +++ b/chromium/ui/display/util/edid_parser.h @@ -11,6 +11,7 @@ #include <vector> #include "base/compiler_specific.h" +#include "base/macros.h" #include "base/optional.h" #include "third_party/skia/include/core/SkColorSpace.h" #include "ui/display/util/display_util_export.h" diff --git a/chromium/ui/display/win/dpi.cc b/chromium/ui/display/win/dpi.cc index 45e0aad18e4..5014d34cf12 100644 --- a/chromium/ui/display/win/dpi.cc +++ b/chromium/ui/display/win/dpi.cc @@ -8,6 +8,7 @@ #include "base/win/scoped_hdc.h" #include "ui/display/display.h" +#include "ui/display/win/uwp_text_scale_factor.h" namespace display { namespace win { @@ -18,7 +19,30 @@ const float kDefaultDPI = 96.f; float g_device_scale_factor = 0.f; -gfx::Size GetDPI() { +} // namespace + +void SetDefaultDeviceScaleFactor(float scale) { + DCHECK_NE(0.f, scale); + g_device_scale_factor = scale; +} + +float GetDPIScale() { + if (Display::HasForceDeviceScaleFactor()) + return Display::GetForcedDeviceScaleFactor(); + return display::win::internal::GetUnforcedDeviceScaleFactor(); +} + +int GetDPIFromScalingFactor(float device_scaling_factor) { + return kDefaultDPI * device_scaling_factor; +} + +double GetAccessibilityFontScale() { + return 1.0 / UwpTextScaleFactor::Instance()->GetTextScaleFactor(); +} + +namespace internal { + +int GetDefaultSystemDPI() { static int dpi_x = 0; static int dpi_y = 0; static bool should_initialize = true; @@ -31,35 +55,20 @@ gfx::Size GetDPI() { // to all screens. dpi_x = GetDeviceCaps(screen_dc, LOGPIXELSX); dpi_y = GetDeviceCaps(screen_dc, LOGPIXELSY); + DCHECK_EQ(dpi_x, dpi_y); } - return gfx::Size(dpi_x, dpi_y); -} - -} // namespace - -void SetDefaultDeviceScaleFactor(float scale) { - DCHECK_NE(0.f, scale); - g_device_scale_factor = scale; -} - -float GetDPIScale() { - if (Display::HasForceDeviceScaleFactor()) - return Display::GetForcedDeviceScaleFactor(); - return GetUnforcedDeviceScaleFactor(); + return dpi_x; } float GetUnforcedDeviceScaleFactor() { return g_device_scale_factor ? g_device_scale_factor - : GetScalingFactorFromDPI(GetDPI().width()); -} - -int GetDPIFromScalingFactor(float device_scaling_factor) { - return kDefaultDPI * device_scaling_factor; + : GetScalingFactorFromDPI(GetDefaultSystemDPI()); } float GetScalingFactorFromDPI(int dpi) { return static_cast<float>(dpi) / kDefaultDPI; } +} // namespace internal } // namespace win } // namespace display diff --git a/chromium/ui/display/win/dpi.h b/chromium/ui/display/win/dpi.h index 96c62506155..9da504e32ce 100644 --- a/chromium/ui/display/win/dpi.h +++ b/chromium/ui/display/win/dpi.h @@ -6,7 +6,6 @@ #define UI_DISPLAY_WIN_DPI_H_ #include "ui/display/display_export.h" -#include "ui/gfx/geometry/size.h" namespace display { namespace win { @@ -23,15 +22,36 @@ DISPLAY_EXPORT void SetDefaultDeviceScaleFactor(float scale); // scale factor is 1.0. This does not handle per-monitor DPI. DISPLAY_EXPORT float GetDPIScale(); -// Equivalent to GetDPIScale() but ignores the --force-device-scale-factor flag. -float GetUnforcedDeviceScaleFactor(); - +// Deprecated. Use win::ScreenWin::GetDPIForHWND instead. +// // Returns the equivalent DPI for |device_scaling_factor|. DISPLAY_EXPORT int GetDPIFromScalingFactor(float device_scaling_factor); +// Returns a factor to adjust a system font's height by, to adjust for +// accessibility measures already built into the font, in order to prevent +// applying the same scale factor twice. Value should be in the range +// 0.0 (exclusive) to 1.0 (inclusive). +// +// Windows will add text scaling factor into the logical size of its default +// system fonts (which it does *not* do for DPI scaling). Since we're scaling +// the entire UI by a combination of text scale and DPI scale, this results in +// double scaling. Call this function to unscale the font before using it in +// any of our rendering code. +DISPLAY_EXPORT double GetAccessibilityFontScale(); + +namespace internal { +// Note: These methods do not take accessibility adjustments into account. + +// Equivalent to GetDPIScale() but ignores the --force-device-scale-factor flag. +float GetUnforcedDeviceScaleFactor(); + // Returns the equivalent scaling factor for |dpi|. -DISPLAY_EXPORT float GetScalingFactorFromDPI(int dpi); +float GetScalingFactorFromDPI(int dpi); + +// Gets the default DPI for the system. +int GetDefaultSystemDPI(); +} // namespace internal } // namespace win } // namespace display diff --git a/chromium/ui/display/win/screen_win.cc b/chromium/ui/display/win/screen_win.cc index d3e2267b23a..7fd3b5b4a78 100644 --- a/chromium/ui/display/win/screen_win.cc +++ b/chromium/ui/display/win/screen_win.cc @@ -35,32 +35,70 @@ namespace { // resolved with Desktop Aura and WindowTreeHost. ScreenWin* g_screen_win_instance = nullptr; -float GetMonitorScaleFactor(HMONITOR monitor) { +// Gets the DPI for a particular monitor, or 0 if per-monitor DPI is nuot +// supported or can't be read. +int GetPerMonitorDPI(HMONITOR monitor) { + // Most versions of Windows we will encounter are DPI-aware. + if (!base::win::IsProcessPerMonitorDpiAware()) + return 0; + + static auto get_dpi_for_monitor_func = []() { + using GetDpiForMonitorPtr = decltype(::GetDpiForMonitor)*; + HMODULE shcore_dll = ::LoadLibrary(L"shcore.dll"); + if (shcore_dll) { + return reinterpret_cast<GetDpiForMonitorPtr>( + ::GetProcAddress(shcore_dll, "GetDpiForMonitor")); + } + return static_cast<GetDpiForMonitorPtr>(nullptr); + }(); + + if (!get_dpi_for_monitor_func) + return 0; + + UINT dpi_x; + UINT dpi_y; + if (!SUCCEEDED(get_dpi_for_monitor_func(monitor, MDT_EFFECTIVE_DPI, &dpi_x, + &dpi_y))) { + return 0; + } + + DCHECK_EQ(dpi_x, dpi_y); + return int{dpi_x}; +} + +// Gets the raw monitor scale factor. +// +// Respects the forced device scale factor, and will fall back to the global +// scale factor if per-monitor DPI is not supported. +float GetMonitorScaleFactorImpl(HMONITOR monitor, bool include_accessibility) { DCHECK(monitor); if (Display::HasForceDeviceScaleFactor()) return Display::GetForcedDeviceScaleFactor(); - if (base::win::IsProcessPerMonitorDpiAware()) { - static auto get_dpi_for_monitor_func = []() { - using GetDpiForMonitorPtr = decltype(::GetDpiForMonitor)*; - HMODULE shcore_dll = ::LoadLibrary(L"shcore.dll"); - if (shcore_dll) { - return reinterpret_cast<GetDpiForMonitorPtr>( - ::GetProcAddress(shcore_dll, "GetDpiForMonitor")); - } - return static_cast<GetDpiForMonitorPtr>(nullptr); - }(); + int dpi = GetPerMonitorDPI(monitor); + if (!dpi) + return GetDPIScale(); - UINT dpi_x; - UINT dpi_y; - if (get_dpi_for_monitor_func && - SUCCEEDED(get_dpi_for_monitor_func(monitor, MDT_EFFECTIVE_DPI, &dpi_x, - &dpi_y))) { - DCHECK_EQ(dpi_x, dpi_y); - return GetScalingFactorFromDPI(dpi_x); - } + float scale_factor = display::win::internal::GetScalingFactorFromDPI(dpi); + if (include_accessibility) { + float text_scale_factor = + UwpTextScaleFactor::Instance()->GetTextScaleFactor(); + scale_factor *= text_scale_factor; } - return GetDPIScale(); + return scale_factor; +} + +// Rounds a scale factor to one we can display safely in pixels without +// smearing. +double RoundToNearestSafeScaleFactor(float scale_factor) { + return std::max(1.0f, std::round(4.0f * scale_factor) * 0.25f); +} + +// Returns a pixel safe monitor scale factor rounded to a pixel-safe value. +float GetSafeMonitorScaleFactor(HMONITOR monitor, + bool include_accessibility = true) { + return RoundToNearestSafeScaleFactor( + GetMonitorScaleFactorImpl(monitor, include_accessibility)); } bool GetPathInfo(HMONITOR monitor, DISPLAYCONFIG_PATH_INFO* path_info) { @@ -153,7 +191,7 @@ Display CreateDisplayFromDisplayInfo(const DisplayInfo& display_info, display.set_bounds(gfx::ScaleToEnclosingRect(display_info.screen_rect(), 1.0f / scale_factor)); display.set_rotation(display_info.rotation()); - if (!Display::HasForceColorProfile()) { + if (!Display::HasForceDisplayColorProfile()) { if (hdr_enabled) { display.SetColorSpaceAndDepth( gfx::ColorSpace::CreateSCRGBLinear().GetScaledColorSpace( @@ -254,7 +292,7 @@ BOOL CALLBACK EnumMonitorForDisplayInfoCallback(HMONITOR monitor, reinterpret_cast<std::vector<DisplayInfo>*>(data); DCHECK(display_infos); display_infos->push_back(DisplayInfo(MonitorInfoFromHMONITOR(monitor), - GetMonitorScaleFactor(monitor), + GetSafeMonitorScaleFactor(monitor), GetMonitorSDRWhiteLevel(monitor))); return TRUE; } @@ -294,6 +332,9 @@ ScreenWin::ScreenWin(bool initialize) ScreenWin::~ScreenWin() { DCHECK_EQ(g_screen_win_instance, this); + if (uwp_text_scale_factor_) + uwp_text_scale_factor_->RemoveObserver(this); + g_screen_win_instance = nullptr; } @@ -414,29 +455,36 @@ gfx::Size ScreenWin::DIPToScreenSize(HWND hwnd, const gfx::Size& dip_size) { } // static -int ScreenWin::GetSystemMetricsForHwnd(HWND hwnd, int metric) { +int ScreenWin::GetSystemMetricsForMonitor(HMONITOR monitor, int metric) { if (!g_screen_win_instance) return ::GetSystemMetrics(metric); - Display primary_display(g_screen_win_instance->GetPrimaryDisplay()); - - return GetSystemMetricsForScaleFactor( - hwnd ? GetScaleFactorForHWND(hwnd) - : primary_display.device_scale_factor(), - metric); -} + // We don't include fudge factors stemming from accessiblility features when + // dealing with system metrics associated with window elements drawn by the + // operating system, since we will not be doing scaling of those metrics + // ourselves. + bool include_accessibility; + switch (metric) { + case SM_CXSIZEFRAME: + case SM_CYSIZEFRAME: + case SM_CXPADDEDBORDER: + include_accessibility = false; + break; + default: + include_accessibility = true; + break; + } -// static -int ScreenWin::GetSystemMetricsForMonitor(HMONITOR monitor, int metric) { - if (!g_screen_win_instance) - return ::GetSystemMetrics(metric); + // We'll want to use GetSafeMonitorScaleFactor(), so if the monitor is not + // specified pull up the primary display's HMONITOR. + if (!monitor) + monitor = MonitorFromWindow(nullptr, MONITOR_DEFAULTTOPRIMARY); - Display primary_display(g_screen_win_instance->GetPrimaryDisplay()); + float scale_factor = + GetSafeMonitorScaleFactor(monitor, include_accessibility); - return GetSystemMetricsForScaleFactor( - monitor ? GetMonitorScaleFactor(monitor) - : primary_display.device_scale_factor(), - metric); + // We'll then pull up the system metrics scaled by the appropriate amount. + return GetSystemMetricsForScaleFactor(scale_factor, metric); } // static @@ -460,8 +508,25 @@ float ScreenWin::GetScaleFactorForHWND(HWND hwnd) { } // static +int ScreenWin::GetDPIForHWND(HWND hwnd) { + if (Display::HasForceDeviceScaleFactor()) + return GetDPIFromScalingFactor(Display::GetForcedDeviceScaleFactor()); + + HMONITOR monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST); + int dpi = GetPerMonitorDPI(monitor); + return dpi ? dpi : display::win::internal::GetDefaultSystemDPI(); +} + +// static +float ScreenWin::GetScaleFactorForDPI(int dpi) { + return RoundToNearestSafeScaleFactor( + display::win::internal::GetScalingFactorFromDPI(dpi) * + UwpTextScaleFactor::Instance()->GetTextScaleFactor()); +} + +// static float ScreenWin::GetSystemScaleFactor() { - return GetUnforcedDeviceScaleFactor(); + return display::win::internal::GetUnforcedDeviceScaleFactor(); } // static @@ -495,6 +560,17 @@ gfx::NativeWindow ScreenWin::GetNativeWindowFromHWND(HWND hwnd) const { return nullptr; } +void ScreenWin::OnUwpTextScaleFactorChanged() { + UpdateAllDisplaysAndNotify(); +} + +void ScreenWin::OnUwpTextScaleFactorCleanup(UwpTextScaleFactor* source) { + if (source == uwp_text_scale_factor_) + uwp_text_scale_factor_ = nullptr; + + UwpTextScaleFactor::Observer::OnUwpTextScaleFactorCleanup(source); +} + gfx::Point ScreenWin::GetCursorScreenPoint() { POINT pt; ::GetCursorPos(&pt); @@ -588,6 +664,12 @@ void ScreenWin::Initialize() { base::Bind(&ScreenWin::OnWndProc, base::Unretained(this)))); UpdateFromDisplayInfos(GetDisplayInfosFromSystem()); RecordDisplayScaleFactors(); + + // We want to remember that we've observed a screen metrics object so that we + // can remove ourselves as an observer at some later point (either when the + // metrics object notifies us it's going away or when we are destructed). + uwp_text_scale_factor_ = UwpTextScaleFactor::Instance(); + uwp_text_scale_factor_->AddObserver(this); } MONITORINFOEX ScreenWin::MonitorInfoFromScreenPoint( diff --git a/chromium/ui/display/win/screen_win.h b/chromium/ui/display/win/screen_win.h index 6a20812bf89..9a31579b8e3 100644 --- a/chromium/ui/display/win/screen_win.h +++ b/chromium/ui/display/win/screen_win.h @@ -15,6 +15,7 @@ #include "ui/display/display_export.h" #include "ui/display/screen.h" #include "ui/display/win/color_profile_reader.h" +#include "ui/display/win/uwp_text_scale_factor.h" #include "ui/gfx/native_widget_types.h" #include "ui/gfx/win/singleton_hwnd_observer.h" @@ -33,7 +34,8 @@ class DisplayInfo; class ScreenWinDisplay; class DISPLAY_EXPORT ScreenWin : public Screen, - public ColorProfileReader::Client { + public ColorProfileReader::Client, + public UwpTextScaleFactor::Observer { public: ScreenWin(); ~ScreenWin() override; @@ -92,15 +94,14 @@ class DISPLAY_EXPORT ScreenWin : public Screen, // The DPI scale is performed relative to the display nearest to |hwnd|. static gfx::Size DIPToScreenSize(HWND hwnd, const gfx::Size& dip_size); - // Returns the result of GetSystemMetrics for |metric| scaled to |hwnd|'s DPI. - // Use this function if you're already working with screen pixels, as this - // helps reduce any cascading rounding errors from DIP to the |hwnd|'s DPI. - static int GetSystemMetricsForHwnd(HWND hwnd, int metric); - // Returns the result of GetSystemMetrics for |metric| scaled to |monitor|'s // DPI. Use this function if you're already working with screen pixels, as // this helps reduce any cascading rounding errors from DIP to the |monitor|'s // DPI. + // + // Note that metrics which correspond to elements drawn by Windows + // (specifically frame and resize handles) will be scaled by DPI only and not + // by Text Zoom or other accessibility features. static int GetSystemMetricsForMonitor(HMONITOR monitor, int metric); // Returns the result of GetSystemMetrics for |metric| in DIP. @@ -108,9 +109,16 @@ class DISPLAY_EXPORT ScreenWin : public Screen, // rounding errors towards screen pixels. static int GetSystemMetricsInDIP(int metric); - // Returns |hwnd|'s scale factor. + // Returns |hwnd|'s scale factor, including accessibility adjustments. static float GetScaleFactorForHWND(HWND hwnd); + // Returns the unmodified DPI for a particular |hwnd|, without accessibility + // adjustments. + static int GetDPIForHWND(HWND hwnd); + + // Converts dpi to scale factor, including accessibility adjustments. + static float GetScaleFactorForDPI(int dpi); + // Returns the system's global scale factor, ignoring the value of // --force-device-scale-factor. Only use this if you are working with Windows // metrics global to the system. Otherwise you should call @@ -214,6 +222,12 @@ class DISPLAY_EXPORT ScreenWin : public Screen, void RecordDisplayScaleFactors() const; + //----------------------------------------------------------------- + // UwpTextScaleFactor::Observer: + + void OnUwpTextScaleFactorChanged() override; + void OnUwpTextScaleFactorCleanup(UwpTextScaleFactor* source) override; + // Helper implementing the DisplayObserver handling. DisplayChangeNotifier change_notifier_; @@ -236,6 +250,8 @@ class DISPLAY_EXPORT ScreenWin : public Screen, // advanced color" setting. bool hdr_enabled_ = false; + UwpTextScaleFactor* uwp_text_scale_factor_ = nullptr; + DISALLOW_COPY_AND_ASSIGN(ScreenWin); }; diff --git a/chromium/ui/display/win/screen_win_unittest.cc b/chromium/ui/display/win/screen_win_unittest.cc index 4cf8e183fd0..4e420cdeec3 100644 --- a/chromium/ui/display/win/screen_win_unittest.cc +++ b/chromium/ui/display/win/screen_win_unittest.cc @@ -329,14 +329,6 @@ TEST_F(ScreenWinTestSingleDisplay1x, DIPToScreenSize) { EXPECT_EQ(size, ScreenWin::DIPToScreenSize(hwnd, size)); } -TEST_F(ScreenWinTestSingleDisplay1x, GetSystemMetricsForHwnd) { - HWND hwnd = GetFakeHwnd(); - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(hwnd, 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(hwnd, 42)); - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(nullptr, 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(nullptr, 42)); -} - TEST_F(ScreenWinTestSingleDisplay1x, GetSystemMetricsInDIP) { EXPECT_EQ(31, ScreenWin::GetSystemMetricsInDIP(31)); EXPECT_EQ(42, ScreenWin::GetSystemMetricsInDIP(42)); @@ -497,14 +489,6 @@ TEST_F(ScreenWinTestSingleDisplay1_25x, DIPToScreenSize) { ScreenWin::DIPToScreenSize(GetFakeHwnd(), gfx::Size(28, 88))); } -TEST_F(ScreenWinTestSingleDisplay1_25x, GetSystemMetricsForHwnd) { - HWND hwnd = GetFakeHwnd(); - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(hwnd, 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(hwnd, 42)); - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(nullptr, 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(nullptr, 42)); -} - TEST_F(ScreenWinTestSingleDisplay1_25x, GetSystemMetricsInDIP) { EXPECT_EQ(25, ScreenWin::GetSystemMetricsInDIP(31)); EXPECT_EQ(34, ScreenWin::GetSystemMetricsInDIP(42)); @@ -654,14 +638,6 @@ TEST_F(ScreenWinTestSingleDisplay1_5x, DIPToScreenSize) { ScreenWin::DIPToScreenSize(GetFakeHwnd(), gfx::Size(28, 88))); } -TEST_F(ScreenWinTestSingleDisplay1_5x, GetSystemMetricsForHwnd) { - HWND hwnd = GetFakeHwnd(); - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(hwnd, 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(hwnd, 42)); - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(nullptr, 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(nullptr, 42)); -} - TEST_F(ScreenWinTestSingleDisplay1_5x, GetSystemMetricsInDIP) { EXPECT_EQ(21, ScreenWin::GetSystemMetricsInDIP(31)); EXPECT_EQ(28, ScreenWin::GetSystemMetricsInDIP(42)); @@ -811,14 +787,6 @@ TEST_F(ScreenWinTestSingleDisplay2x, DIPToScreenSize) { ScreenWin::DIPToScreenSize(GetFakeHwnd(), gfx::Size(21, 66))); } -TEST_F(ScreenWinTestSingleDisplay2x, GetSystemMetricsForHwnd) { - HWND hwnd = GetFakeHwnd(); - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(hwnd, 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(hwnd, 42)); - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(nullptr, 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(nullptr, 42)); -} - TEST_F(ScreenWinTestSingleDisplay2x, GetSystemMetricsInDIP) { EXPECT_EQ(16, ScreenWin::GetSystemMetricsInDIP(31)); EXPECT_EQ(21, ScreenWin::GetSystemMetricsInDIP(42)); @@ -1036,17 +1004,6 @@ TEST_F(ScreenWinTestTwoDisplays1x, DIPToScreenSize) { EXPECT_EQ(size, ScreenWin::DIPToScreenSize(right_hwnd, size)); } -TEST_F(ScreenWinTestTwoDisplays1x, GetSystemMetricsForHwnd) { - HWND left_hwnd = GetLeftFakeHwnd(); - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(left_hwnd, 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(left_hwnd, 42)); - HWND right_hwnd = GetRightFakeHwnd(); - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(right_hwnd, 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(right_hwnd, 42)); - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(nullptr, 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(nullptr, 42)); -} - TEST_F(ScreenWinTestTwoDisplays1x, GetSystemMetricsInDIP) { EXPECT_EQ(31, ScreenWin::GetSystemMetricsInDIP(31)); EXPECT_EQ(42, ScreenWin::GetSystemMetricsInDIP(42)); @@ -1314,17 +1271,6 @@ TEST_F(ScreenWinTestTwoDisplays2x, DIPToScreenSize) { ScreenWin::DIPToScreenSize(right_hwnd, gfx::Size(21, 66))); } -TEST_F(ScreenWinTestTwoDisplays2x, GetSystemMetricsForHwnd) { - HWND left_hwnd = GetLeftFakeHwnd(); - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(left_hwnd, 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(left_hwnd, 42)); - HWND right_hwnd = GetRightFakeHwnd(); - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(right_hwnd, 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(right_hwnd, 42)); - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(nullptr, 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(nullptr, 42)); -} - TEST_F(ScreenWinTestTwoDisplays2x, GetSystemMetricsInDIP) { EXPECT_EQ(16, ScreenWin::GetSystemMetricsInDIP(31)); EXPECT_EQ(21, ScreenWin::GetSystemMetricsInDIP(42)); @@ -1692,16 +1638,6 @@ TEST_F(ScreenWinTestManyDisplays1x, DIPToScreenSize) { } } -TEST_F(ScreenWinTestManyDisplays1x, GetSystemMetricsForHwnd) { - for (size_t i = 0; i < 5u; ++i) { - SCOPED_TRACE(base::StringPrintf("i=%zu", i)); - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(GetFakeHwnd(i), 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(GetFakeHwnd(i), 42)); - } - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(nullptr, 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(nullptr, 42)); -} - TEST_F(ScreenWinTestManyDisplays1x, GetSystemMetricsInDIP) { EXPECT_EQ(31, ScreenWin::GetSystemMetricsInDIP(31)); EXPECT_EQ(42, ScreenWin::GetSystemMetricsInDIP(42)); @@ -2131,16 +2067,6 @@ TEST_F(ScreenWinTestManyDisplays2x, DIPToScreenSize) { } } -TEST_F(ScreenWinTestManyDisplays2x, GetSystemMetricsForHwnd) { - for (size_t i = 0; i < 5u; ++i) { - SCOPED_TRACE(base::StringPrintf("i=%zu", i)); - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(GetFakeHwnd(i), 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(GetFakeHwnd(i), 42)); - } - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(nullptr, 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(nullptr, 42)); -} - TEST_F(ScreenWinTestManyDisplays2x, GetSystemMetricsInDIP) { EXPECT_EQ(16, ScreenWin::GetSystemMetricsInDIP(31)); EXPECT_EQ(21, ScreenWin::GetSystemMetricsInDIP(42)); @@ -2441,17 +2367,6 @@ TEST_F(ScreenWinTestTwoDisplays1x2x, DIPToScreenSize) { ScreenWin::DIPToScreenSize(right_hwnd, gfx::Size(21, 66))); } -TEST_F(ScreenWinTestTwoDisplays1x2x, GetSystemMetricsForHwnd) { - HWND left_hwnd = GetLeftFakeHwnd(); - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(left_hwnd, 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(left_hwnd, 42)); - HWND right_hwnd = GetRightFakeHwnd(); - EXPECT_EQ(62, ScreenWin::GetSystemMetricsForHwnd(right_hwnd, 31)); - EXPECT_EQ(84, ScreenWin::GetSystemMetricsForHwnd(right_hwnd, 42)); - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(nullptr, 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(nullptr, 42)); -} - TEST_F(ScreenWinTestTwoDisplays1x2x, GetSystemMetricsInDIP) { EXPECT_EQ(31, ScreenWin::GetSystemMetricsInDIP(31)); EXPECT_EQ(42, ScreenWin::GetSystemMetricsInDIP(42)); @@ -2727,17 +2642,6 @@ TEST_F(ScreenWinTestTwoDisplays1_5x1x, DIPToScreenSize) { ScreenWin::DIPToScreenSize(right_hwnd, gfx::Size(42, 131))); } -TEST_F(ScreenWinTestTwoDisplays1_5x1x, GetSystemMetricsForHwnd) { - HWND left_hwnd = GetLeftFakeHwnd(); - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(left_hwnd, 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(left_hwnd, 42)); - HWND right_hwnd = GetRightFakeHwnd(); - EXPECT_EQ(21, ScreenWin::GetSystemMetricsForHwnd(right_hwnd, 31)); - EXPECT_EQ(28, ScreenWin::GetSystemMetricsForHwnd(right_hwnd, 42)); - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(nullptr, 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(nullptr, 42)); -} - TEST_F(ScreenWinTestTwoDisplays1_5x1x, GetSystemMetricsInDIP) { EXPECT_EQ(21, ScreenWin::GetSystemMetricsInDIP(31)); EXPECT_EQ(28, ScreenWin::GetSystemMetricsInDIP(42)); @@ -3009,17 +2913,6 @@ TEST_F(ScreenWinTestTwoDisplays2x1x, DIPToScreenSize) { ScreenWin::DIPToScreenSize(right_hwnd, gfx::Size(42, 131))); } -TEST_F(ScreenWinTestTwoDisplays2x1x, GetSystemMetricsForHwnd) { - HWND left_hwnd = GetLeftFakeHwnd(); - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(left_hwnd, 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(left_hwnd, 42)); - HWND right_hwnd = GetRightFakeHwnd(); - EXPECT_EQ(16, ScreenWin::GetSystemMetricsForHwnd(right_hwnd, 31)); - EXPECT_EQ(21, ScreenWin::GetSystemMetricsForHwnd(right_hwnd, 42)); - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(nullptr, 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(nullptr, 42)); -} - TEST_F(ScreenWinTestTwoDisplays2x1x, GetSystemMetricsInDIP) { EXPECT_EQ(16, ScreenWin::GetSystemMetricsInDIP(31)); EXPECT_EQ(21, ScreenWin::GetSystemMetricsInDIP(42)); @@ -3296,17 +3189,6 @@ TEST_F(ScreenWinTestTwoDisplays2x1xVirtualized, DIPToScreenSize) { ScreenWin::DIPToScreenSize(right_hwnd, gfx::Size(21, 66))); } -TEST_F(ScreenWinTestTwoDisplays2x1xVirtualized, GetSystemMetricsForHwnd) { - HWND left_hwnd = GetLeftFakeHwnd(); - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(left_hwnd, 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(left_hwnd, 42)); - HWND right_hwnd = GetRightFakeHwnd(); - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(right_hwnd, 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(right_hwnd, 42)); - EXPECT_EQ(31, ScreenWin::GetSystemMetricsForHwnd(nullptr, 31)); - EXPECT_EQ(42, ScreenWin::GetSystemMetricsForHwnd(nullptr, 42)); -} - TEST_F(ScreenWinTestTwoDisplays2x1xVirtualized, GetSystemMetricsInDIP) { EXPECT_EQ(16, ScreenWin::GetSystemMetricsInDIP(31)); EXPECT_EQ(21, ScreenWin::GetSystemMetricsInDIP(42)); @@ -3485,12 +3367,6 @@ TEST_F(ScreenWinUninitializedForced1x, DIPToScreenSize) { EXPECT_EQ(size, ScreenWin::DIPToScreenSize(nullptr, size)); } -TEST_F(ScreenWinUninitializedForced1x, GetSystemMetricsForHwnd) { - // GetSystemMetricsForHwnd falls back to the system's GetSystemMetrics, so - // this test is to make sure we don't crash. - ScreenWin::GetSystemMetricsForHwnd(nullptr, SM_CXSIZEFRAME); -} - TEST_F(ScreenWinUninitializedForced1x, GetSystemMetricsInDIP) { // GetSystemMetricsInDIP falls back to the system's GetSystemMetrics, so this // test is to make sure we don't crash. @@ -3598,14 +3474,8 @@ TEST_F(ScreenWinUninitializedForced2x, DIPToScreenSize) { ScreenWin::DIPToScreenSize(nullptr, gfx::Size(21, 66))); } -TEST_F(ScreenWinUninitializedForced2x, GetSystemMetricsForHwnd) { - // GetSystemMetricsForHwnd falls back to the system's GetSystemMetrics, so - // this test is to make sure we don't crash. - ScreenWin::GetSystemMetricsForHwnd(nullptr, SM_CXSIZEFRAME); -} - TEST_F(ScreenWinUninitializedForced2x, GetSystemMetricsInDIP) { - // GetSystemMetricsForHwnd falls back to the system's GetSystemMetrics, so + // This falls back to the system's GetSystemMetrics, so // this test is to make sure we don't crash. ScreenWin::GetSystemMetricsInDIP(SM_CXSIZEFRAME); } diff --git a/chromium/ui/display/win/uwp_text_scale_factor.cc b/chromium/ui/display/win/uwp_text_scale_factor.cc new file mode 100644 index 00000000000..6b661bdbed8 --- /dev/null +++ b/chromium/ui/display/win/uwp_text_scale_factor.cc @@ -0,0 +1,236 @@ +// Copyright (c) 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/display/win/uwp_text_scale_factor.h" + +#include <windows.h> + +#include <windows.ui.viewmanagement.h> +#include <wrl/client.h> +#include <wrl/event.h> + +#include "base/lazy_instance.h" +#include "base/strings/string_piece.h" +#include "base/threading/thread_checker.h" +#include "base/win/core_winrt_util.h" +#include "base/win/scoped_com_initializer.h" +#include "base/win/scoped_hstring.h" +#include "base/win/windows_version.h" + +namespace display { +namespace win { + +namespace { + +using Microsoft::WRL::ComPtr; +using Microsoft::WRL::Callback; +using ABI::Windows::Foundation::ITypedEventHandler; +using ABI::Windows::UI::ViewManagement::UISettings; +using ABI::Windows::UI::ViewManagement::IUISettings; +using ABI::Windows::UI::ViewManagement::IUISettings2; + +typedef ITypedEventHandler<UISettings*, IInspectable*> + TextScaleChangedEventHandler; + +// A zero value indicates an invalid token. +constexpr EventRegistrationToken kInvalidEventRegistrationToken{0LL}; + +// Override the default instance for testing purposes. +UwpTextScaleFactor* g_implementation_for_testing = nullptr; + +// Sanity check for access after destruction. +bool g_default_instance_cleaned_up = false; + +// Constructs the UWP UI Settings COM object, or fails with as useful of a log +// message as possible given Windows error reporting. +// +// Lots of things could potentially go wrong so we want to be able to bail out +// when creating the UWP UI Settings object, so we've moved the initialization +// to a separate function. +bool CreateUiSettingsComObject(ComPtr<IUISettings2>& ptr) { + DCHECK(!ptr); + + // This is required setup before using ScopedHString. + if (!(base::win::ResolveCoreWinRTDelayload() && + base::win::ScopedHString::ResolveCoreWinRTStringDelayload())) { + DLOG(ERROR) << "Failed loading functions from combase.dll"; + return false; + } + + // Create the COM object. + auto hstring = base::win::ScopedHString::Create( + RuntimeClass_Windows_UI_ViewManagement_UISettings); + if (!hstring.is_valid()) { + return false; + } + ComPtr<IInspectable> inspectable; + HRESULT hr = base::win::RoActivateInstance(hstring.get(), &inspectable); + if (FAILED(hr)) { + VLOG(2) << "RoActivateInstance failed: " + << logging::SystemErrorCodeToString(hr); + return false; + } + + // Verify that it supports the correct interface. + hr = inspectable.As(&ptr); + if (FAILED(hr)) { + VLOG(2) << "As IUISettings2 failed: " + << logging::SystemErrorCodeToString(hr); + return false; + } + + return true; +} + +// Implements the actual logic for getting screen metrics. +class UwpTextScaleFactorImpl : public UwpTextScaleFactor { + public: + UwpTextScaleFactorImpl() + : text_scale_factor_changed_token_(kInvalidEventRegistrationToken) { + // There's no point in doing this initialization if we're earlier than + // Windows 10, since UWP is a Win10 feature. + if (base::win::GetVersion() < base::win::VERSION_WIN10) + return; + + // We want to bracket all use of our COM object with COM initialization + // in order to be sure we don't leak COM listeners into the OS. This may + // extend the lifetime of COM on this thread but we do not expect it to be + // a problem. + scoped_com_initializer_ = + std::make_unique<base::win::ScopedCOMInitializer>(); + if (!scoped_com_initializer_->Succeeded()) + return; + + // Create our COM object. Again, if we fail, there's no reason to proceed. + if (!CreateUiSettingsComObject(ui_settings_com_object_)) + return; + + // Set up a listener for the TextScaleChanged event. We'll do this here + // so that we can use a member function as a callback. + auto text_scale_changed_handler = Callback<TextScaleChangedEventHandler>( + this, &UwpTextScaleFactorImpl::OnTextScaleFactorChanged); + if (text_scale_changed_handler) { + HRESULT hr = ui_settings_com_object_->add_TextScaleFactorChanged( + text_scale_changed_handler.Get(), &text_scale_factor_changed_token_); + if (FAILED(hr)) { + VLOG(2) << "Register text scale changed callback failed: " + << logging::SystemErrorCodeToString(hr); + } + } + } + + ~UwpTextScaleFactorImpl() override { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + // Release the callback if we've registered one and free our COM objects. + if (ui_settings_com_object_) { + if (text_scale_factor_changed_token_.value) { + HRESULT hr = ui_settings_com_object_->remove_TextScaleFactorChanged( + text_scale_factor_changed_token_); + if (FAILED(hr)) { + VLOG(2) << "Failed to remove TextScaleFactorChanged listener: " + << logging::SystemErrorCodeToString(hr); + } + } + ui_settings_com_object_.Reset(); + } + + g_default_instance_cleaned_up = true; + } + + float GetTextScaleFactor() const override { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + double result = 1.0; + + // This is just a null check, so if we don't have access to the text + // scaling / service for any reason we'll just use 1x. + if (ui_settings_com_object_) { + HRESULT hr = ui_settings_com_object_->get_TextScaleFactor(&result); + if (FAILED(hr)) { + VLOG(2) << "IUISettings2::TextScaleFactor failed: " + << logging::SystemErrorCodeToString(hr); + // COM calls overwrite their out-params, typically by zeroing them out. + // Since we can't rely on this being a valid value on failure, we'll + // reset it. + result = 1.0; + } + } + + // Windows documents this property to always have a value greater than or + // equal to 1. Let's make sure that's the case - if we don't, we could get + // bizarre behavior and divide-by-zeros later on. + DCHECK_GE(result, 1.0); + return float{result}; + } + + private: + HRESULT OnTextScaleFactorChanged(IUISettings*, IInspectable*) { + NotifyUwpTextScaleFactorChanged(); + return S_OK; + } + + std::unique_ptr<base::win::ScopedCOMInitializer> scoped_com_initializer_; + ComPtr<IUISettings2> ui_settings_com_object_; + EventRegistrationToken text_scale_factor_changed_token_; + + THREAD_CHECKER(thread_checker_); +}; + +} // namespace + +//--------------------------------------------------------- +// Base UwpScreenMetrics implementation: + +UwpTextScaleFactor::UwpTextScaleFactor() = default; + +UwpTextScaleFactor::~UwpTextScaleFactor() { + for (auto& observer : observer_list_) { + observer.OnUwpTextScaleFactorCleanup(this); + } +} + +float UwpTextScaleFactor::GetTextScaleFactor() const { + return 1.0f; +} + +void UwpTextScaleFactor::AddObserver(Observer* observer) { + observer_list_.AddObserver(observer); +} + +void UwpTextScaleFactor::RemoveObserver(Observer* observer) { + observer_list_.RemoveObserver(observer); +} + +void UwpTextScaleFactor::NotifyUwpTextScaleFactorChanged() { + for (auto& observer : observer_list_) { + observer.OnUwpTextScaleFactorChanged(); + } +} + +void UwpTextScaleFactor::SetImplementationForTesting( + UwpTextScaleFactor* mock_impl) { + g_implementation_for_testing = mock_impl; +} + +UwpTextScaleFactor* UwpTextScaleFactor::Instance() { + static base::LazyInstance<UwpTextScaleFactorImpl>::DestructorAtExit instance; + + DCHECK(!g_default_instance_cleaned_up) + << "Attempting to access UwpScreenMetrics after AtExit cleanup!"; + + return g_implementation_for_testing ? g_implementation_for_testing + : &instance.Get(); +} + +//--------------------------------------------------------- +// UwpScreenMetrics::Observer implementation: + +void UwpTextScaleFactor::Observer::OnUwpTextScaleFactorCleanup( + UwpTextScaleFactor* source) { + source->RemoveObserver(this); +} + +} // namespace win +} // namespace display diff --git a/chromium/ui/display/win/uwp_text_scale_factor.h b/chromium/ui/display/win/uwp_text_scale_factor.h new file mode 100644 index 00000000000..5ad22d22add --- /dev/null +++ b/chromium/ui/display/win/uwp_text_scale_factor.h @@ -0,0 +1,73 @@ +// Copyright (c) 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_DISPLAY_WIN_UWP_TEXT_SCALE_FACTOR_H_ +#define UI_DISPLAY_WIN_UWP_TEXT_SCALE_FACTOR_H_ + +#include "base/observer_list.h" +#include "ui/display/display_export.h" + +namespace display { +namespace win { + +// Provides access to UWP TextScaleFactor, an accessibility feature. +// +// TODO(dfried): In the future we may need to expand this class to capture +// other UWP-only metrics and functionality. At that point, we probably want to +// rename the class and/or put it in base/win. +class DISPLAY_EXPORT UwpTextScaleFactor { + public: + // Observer for UWP screen metrics and accessibility events. + class DISPLAY_EXPORT Observer : public base::CheckedObserver { + public: + // Notifies the observer that the text scale has changed. Will be called on + // the main thread. + virtual void OnUwpTextScaleFactorChanged() = 0; + + // Notifies the observer that this object is about to go out of scope and + // that it should release any references to this object. The base + // implementation removes this object as a listener, so remember to call + // it when you're done with derived class cleanup. + virtual void OnUwpTextScaleFactorCleanup(UwpTextScaleFactor* source); + }; + + // Retrieves the one global instance. Lazily-created unless you want to mock + // it out with SetImplementationForTesting(). + static UwpTextScaleFactor* Instance(); + + virtual ~UwpTextScaleFactor(); + + // Retrieves the Windows Text Zoom scale factor. Guaranteed to be >= 1. + virtual float GetTextScaleFactor() const; + + // Registers and observer that will be notified of any changes to UWP screen + // or accessibility metrics. + void AddObserver(Observer* observer); + + // Removes an observer that has been added. Observers should remove + // themselves either during OnUwpScreenMetricsCleanup() or at destruction + // (whichever is first). + void RemoveObserver(Observer* observer); + + // Override creation of the default UwpTextScaleFactor implementation, using + // a mock or other stub implementation of your choice. The caller is + // responsible for cleaning up this object. + static void SetImplementationForTesting(UwpTextScaleFactor* mock_impl); + + protected: + UwpTextScaleFactor(); + void NotifyUwpTextScaleFactorChanged(); + + // The observers that need to be notified when a display is modified, added + // or removed. + base::ObserverList<Observer> observer_list_; + + private: + DISALLOW_COPY_AND_ASSIGN(UwpTextScaleFactor); +}; + +} // namespace win +} // namespace display + +#endif // UI_DISPLAY_WIN_UWP_TEXT_SCALE_FACTOR_H_ diff --git a/chromium/ui/events/BUILD.gn b/chromium/ui/events/BUILD.gn index 25b3dc39f49..75bd864ef4b 100644 --- a/chromium/ui/events/BUILD.gn +++ b/chromium/ui/events/BUILD.gn @@ -3,6 +3,7 @@ # found in the LICENSE file. import("//build/config/features.gni") +import("//build/config/jumbo.gni") import("//build/config/ui.gni") import("//testing/test.gni") import("//ui/base/ui_features.gni") @@ -16,7 +17,7 @@ if (is_ios) { import("//ios/build/config.gni") } -static_library("dom_keycode_converter") { +jumbo_static_library("dom_keycode_converter") { public = [ "keycodes/dom/dom_code.h", "keycodes/dom/dom_codes.h", @@ -92,7 +93,7 @@ source_set("platform_event") { ] } -component("events_base") { +jumbo_component("events_base") { sources = [ "base_event_utils.cc", "base_event_utils.h", @@ -153,62 +154,71 @@ component("events_base") { } } -component("events") { - sources = [ +jumbo_component("events") { + public = [ "cocoa/cocoa_event_utils.h", - "cocoa/cocoa_event_utils.mm", - "cocoa/events_mac.mm", - "event.cc", "event.h", - "event_dispatcher.cc", "event_dispatcher.h", - "event_handler.cc", "event_handler.h", - "event_modifiers.cc", "event_modifiers.h", - "event_processor.cc", "event_processor.h", - "event_rewriter.cc", "event_rewriter.h", "event_sink.h", - "event_source.cc", "event_source.h", - "event_target.cc", "event_target.h", "event_target_iterator.h", "event_targeter.h", - "event_utils.cc", "event_utils.h", "events_export.h", - "events_stub.cc", - "gestures/gesture_recognizer_impl_mac.cc", + "gestures/gesture_recognizer.h", "gestures/gesture_recognizer_impl_mac.h", - "gestures/gesture_types.cc", + "gestures/gesture_recognizer_observer.h", "gestures/gesture_types.h", "keyboard_hook.h", + "null_event_targeter.h", + "scoped_target_handler.h", + "system_input_injector.h", + "win/events_win_utils.h", + "win/system_event_state_lookup.h", + ] + + sources = [ + "cocoa/cocoa_event_utils.mm", + "cocoa/events_mac.mm", + "event.cc", + "event_dispatcher.cc", + "event_handler.cc", + "event_modifiers.cc", + "event_processor.cc", + "event_rewriter.cc", + "event_source.cc", + "event_target.cc", + "event_utils.cc", + "events_stub.cc", + "gestures/gesture_recognizer.cc", + "gestures/gesture_recognizer_impl_mac.cc", + "gestures/gesture_recognizer_observer.cc", + "gestures/gesture_types.cc", "keyboard_hook_base.cc", "keyboard_hook_base.h", "keycodes/platform_key_map_win.cc", "keycodes/platform_key_map_win.h", "mac/keyboard_hook_mac.mm", "null_event_targeter.cc", - "null_event_targeter.h", "scoped_target_handler.cc", - "scoped_target_handler.h", "system_input_injector.cc", - "system_input_injector.h", "win/events_win.cc", "win/events_win_utils.cc", - "win/events_win_utils.h", "win/keyboard_hook_win.cc", + "win/keyboard_hook_win.h", "win/system_event_state_lookup.cc", - "win/system_event_state_lookup.h", ] defines = [ "EVENTS_IMPLEMENTATION" ] public_deps = [ ":events_base", + "//ui/display", "//ui/latency", ] deps = [ @@ -216,11 +226,16 @@ component("events") { ":gesture_detection", "//base/third_party/dynamic_annotations", "//skia", - "//ui/display", "//ui/gfx", "//ui/gfx/geometry", ] + # Expose the internals of this target to other packages in this BUILD file + # so the unit tests can access the private header files. + # Note: Only 'events_unittests' needs access in this file, however it uses a + # template which generates different target names on different platforms. + friend = [ ":*" ] + if (use_x11) { sources += [ "x/events_x.cc", @@ -248,23 +263,24 @@ component("events") { } if (use_ozone) { + public += [ "ozone/events_ozone.h" ] sources += [ "ozone/events_ozone.cc", - "ozone/events_ozone.h", "ozone/keyboard_hook_ozone.cc", ] deps += [ "//ui/events/ozone:events_ozone_layout" ] } if (use_aura) { + public += [ + "gestures/gesture_provider_aura.h", + "gestures/gesture_recognizer_impl.h", + "gestures/motion_event_aura.h", + ] sources += [ "gestures/gesture_provider_aura.cc", - "gestures/gesture_provider_aura.h", - "gestures/gesture_recognizer.h", "gestures/gesture_recognizer_impl.cc", - "gestures/gesture_recognizer_impl.h", "gestures/motion_event_aura.cc", - "gestures/motion_event_aura.h", ] } @@ -273,21 +289,23 @@ component("events") { } if (is_android) { - sources += [ - "android/drag_event_android.cc", + public += [ "android/drag_event_android.h", - "android/event_handler_android.cc", "android/event_handler_android.h", - "android/gesture_event_android.cc", "android/gesture_event_android.h", "android/gesture_event_type.h", - "android/key_event_android.cc", "android/key_event_android.h", - "android/key_event_utils.cc", "android/key_event_utils.h", + "android/motion_event_android.h", + ] + sources += [ + "android/drag_event_android.cc", + "android/event_handler_android.cc", + "android/gesture_event_android.cc", + "android/key_event_android.cc", + "android/key_event_utils.cc", "android/keyboard_hook_android.cc", "android/motion_event_android.cc", - "android/motion_event_android.h", ] deps += [ ":keyevent_jni_headers", @@ -298,9 +316,18 @@ component("events") { if (is_mac) { libs = [ "AppKit.framework" ] } + + if (is_fuchsia) { + public += [ + "fuchsia/input_event_dispatcher.h", + "fuchsia/input_event_dispatcher_delegate.h", + ] + sources += [ "fuchsia/input_event_dispatcher.cc" ] + public_deps += [ "//third_party/fuchsia-sdk/sdk:input" ] + } } -component("gesture_detection") { +jumbo_component("gesture_detection") { sources = [ "gesture_detection/bitset_32.h", "gesture_detection/filtered_gesture_provider.cc", @@ -364,7 +391,7 @@ component("gesture_detection") { } } -static_library("test_support") { +jumbo_static_library("test_support") { sources = [ "test/cocoa_test_event_utils.h", "test/cocoa_test_event_utils.mm", @@ -442,6 +469,8 @@ if (!is_ios) { "blink/fling_booster_unittest.cc", "blink/input_handler_proxy_unittest.cc", "blink/input_scroll_elasticity_controller_unittest.cc", + "blink/prediction/input_predictor_unittest_helpers.cc", + "blink/prediction/input_predictor_unittest_helpers.h", "blink/prediction/kalman_predictor_unittest.cc", "blink/prediction/least_squares_predictor_unittest.cc", "blink/scroll_predictor_unittest.cc", @@ -476,6 +505,7 @@ if (!is_ios) { "platform/platform_event_source_unittest.cc", "scoped_target_handler_unittest.cc", "win/event_utils_win_unittest.cc", + "win/keyboard_hook_win_unittest.cc", ] deps = [ @@ -491,6 +521,7 @@ if (!is_ios) { "//ipc:test_support", "//mojo/core/test:run_all_unittests", "//mojo/public/cpp/bindings", + "//mojo/public/cpp/test_support:test_utils", "//skia", "//testing/gmock", "//testing/gtest", @@ -501,8 +532,8 @@ if (!is_ios) { "//ui/events/devices", "//ui/events/devices/mojo:test_interfaces", "//ui/events/gestures/blink", - "//ui/events/mojo:test_interfaces", "//ui/events/platform", + "//ui/gfx/geometry/mojo:struct_traits", "//ui/gfx/ipc/geometry", ] @@ -565,6 +596,7 @@ if (!is_ios) { if (use_aura) { sources += [ "gestures/gesture_provider_aura_unittest.cc", + "gestures/gesture_recognizer_impl_unittest.cc", "gestures/motion_event_aura_unittest.cc", ] } @@ -576,6 +608,10 @@ if (!is_ios) { if (is_chromecast && !is_android) { sources += [ "chromecast/scroller_unittest.cc" ] } + + if (is_fuchsia) { + sources += [ "fuchsia/input_event_dispatcher_unittest.cc" ] + } } } diff --git a/chromium/ui/events/blink/blink_features.cc b/chromium/ui/events/blink/blink_features.cc index 30255a53905..a88686acbcd 100644 --- a/chromium/ui/events/blink/blink_features.cc +++ b/chromium/ui/events/blink/blink_features.cc @@ -16,4 +16,10 @@ const base::Feature kResamplingScrollEvents{"ResamplingScrollEvents", const base::Feature kSendMouseLeaveEvents{"SendMouseLeaveEvents", base::FEATURE_ENABLED_BY_DEFAULT}; + +const base::Feature kNoHoverAfterLayoutChange{ + "NoHoverAfterLayoutChange", base::FEATURE_DISABLED_BY_DEFAULT}; + +const base::Feature kNoHoverDuringScroll{"NoHoverDuringScroll", + base::FEATURE_DISABLED_BY_DEFAULT}; } diff --git a/chromium/ui/events/blink/blink_features.h b/chromium/ui/events/blink/blink_features.h index 64e887e0e42..c7ee6ef5495 100644 --- a/chromium/ui/events/blink/blink_features.h +++ b/chromium/ui/events/blink/blink_features.h @@ -19,6 +19,16 @@ extern const base::Feature kResamplingScrollEvents; // converted to mouse move events due to a number of inconsistencies on // the native platforms. crbug.com/450631 extern const base::Feature kSendMouseLeaveEvents; + +// When enabled, this feature prevents Blink from changing the hover state and +// dispatching mouse enter/exit events for elements under the mouse after the +// layout under the mouse cursor is changed. +extern const base::Feature kNoHoverAfterLayoutChange; + +// When enabled, this feature prevents Blink from changing the hover state and +// dispatching mouse enter/exit events for elements under the mouse as the page +// is scrolled. +extern const base::Feature kNoHoverDuringScroll; } #endif // UI_EVENTS_BLINK_BLINK_FEATURES_H_ diff --git a/chromium/ui/events/blink/input_handler_proxy.cc b/chromium/ui/events/blink/input_handler_proxy.cc index 420df485950..d37158e58e1 100644 --- a/chromium/ui/events/blink/input_handler_proxy.cc +++ b/chromium/ui/events/blink/input_handler_proxy.cc @@ -466,6 +466,17 @@ void InputHandlerProxy::RecordMainThreadScrollingReasons( DCHECK( !cc::MainThreadScrollingReason::HasNonCompositedScrollReasons(reasons)); + int32_t event_disposition_result = + (device == blink::kWebGestureDeviceTouchpad ? mouse_wheel_result_ + : touch_result_); + if (event_disposition_result == DID_NOT_HANDLE) { + // We should also collect main thread scrolling reasons if a scroll event + // scrolls on impl thread but is blocked by main thread event handlers. + reasons |= (device == blink::kWebGestureDeviceTouchpad + ? cc::MainThreadScrollingReason::kWheelEventHandlerRegion + : cc::MainThreadScrollingReason::kTouchEventHandlerRegion); + } + // UMA_HISTOGRAM_ENUMERATION requires that the enum_max must be strictly // greater than the sample value. kMainThreadScrollingReasonCount doesn't // include the NotScrollingOnMain enum but the histograms do so adding @@ -511,51 +522,6 @@ void InputHandlerProxy::RecordMainThreadScrollingReasons( } } -void InputHandlerProxy::RecordScrollingThreadStatus( - blink::WebGestureDevice device, - uint32_t reasons) { - if (device != blink::kWebGestureDeviceTouchpad && - device != blink::kWebGestureDeviceTouchscreen) { - return; - } - - ScrollingThreadStatus scrolling_thread_status = SCROLLING_ON_MAIN; - if (reasons == cc::MainThreadScrollingReason::kNotScrollingOnMain) { - int32_t event_disposition_result = - (device == blink::kWebGestureDeviceTouchpad ? mouse_wheel_result_ - : touch_result_); - switch (event_disposition_result) { - case kEventDispositionUndefined: - case DID_NOT_HANDLE_NON_BLOCKING_DUE_TO_FLING: - case DID_HANDLE_NON_BLOCKING: - case DROP_EVENT: - scrolling_thread_status = SCROLLING_ON_COMPOSITOR; - break; - case DID_NOT_HANDLE: - scrolling_thread_status = SCROLLING_ON_COMPOSITOR_BLOCKED_ON_MAIN; - break; - default: - NOTREACHED(); - scrolling_thread_status = SCROLLING_ON_COMPOSITOR; - } - } - - // UMA_HISTOGRAM_ENUMERATION requires that the enum_max must be strictly - // greater than the sample value. - const uint32_t kScrolingThreadStatusEnumMax = - ScrollingThreadStatus::LAST_SCROLLING_THREAD_STATUS_VALUE + 1; - - if (device == blink::kWebGestureDeviceTouchscreen) { - UMA_HISTOGRAM_ENUMERATION("Renderer4.GestureScrollingThreadStatus", - scrolling_thread_status, - kScrolingThreadStatusEnumMax); - } else { - UMA_HISTOGRAM_ENUMERATION("Renderer4.WheelScrollingThreadStatus", - scrolling_thread_status, - kScrolingThreadStatusEnumMax); - } -} - bool InputHandlerProxy::ShouldAnimate(bool has_precise_scroll_deltas) const { #if defined(OS_MACOSX) // Mac does not smooth scroll wheel events (crbug.com/574283). @@ -646,9 +612,6 @@ InputHandlerProxy::EventDisposition InputHandlerProxy::HandleGestureScrollBegin( RecordMainThreadScrollingReasons(gesture_event.SourceDevice(), scroll_status.main_thread_scrolling_reasons); - RecordScrollingThreadStatus(gesture_event.SourceDevice(), - scroll_status.main_thread_scrolling_reasons); - InputHandlerProxy::EventDisposition result = DID_NOT_HANDLE; scroll_sequence_ignored_ = false; in_inertial_scrolling_ = false; diff --git a/chromium/ui/events/blink/input_handler_proxy_unittest.cc b/chromium/ui/events/blink/input_handler_proxy_unittest.cc index 9f5bd515b72..c0d6d6fb0b9 100644 --- a/chromium/ui/events/blink/input_handler_proxy_unittest.cc +++ b/chromium/ui/events/blink/input_handler_proxy_unittest.cc @@ -1375,234 +1375,6 @@ TEST(SynchronousInputHandlerProxyTest, SetOffset) { testing::Mock::VerifyAndClearExpectations(&mock_synchronous_input_handler); } -TEST_P(InputHandlerProxyTest, MainThreadScrollingMouseWheelHistograms) { - input_handler_->RecordMainThreadScrollingReasonsForTest( - blink::kWebGestureDeviceTouchpad, - cc::MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects | - cc::MainThreadScrollingReason::kThreadedScrollingDisabled | - cc::MainThreadScrollingReason::kPageOverlay | - cc::MainThreadScrollingReason::kHandlingScrollFromMainThread); - - EXPECT_THAT( - histogram_tester().GetAllSamples("Renderer4.MainThreadWheelScrollReason"), - testing::ElementsAre(base::Bucket(1, 1), base::Bucket(3, 1), - base::Bucket(5, 1))); - - // We only want to record "Handling scroll from main thread" reason if it's - // the only reason. If it's not the only reason, the "real" reason for - // scrolling on main is something else, and we only want to pay attention to - // that reason. So we should only include this reason in the histogram when - // its on its own. - input_handler_->RecordMainThreadScrollingReasonsForTest( - blink::kWebGestureDeviceTouchpad, - cc::MainThreadScrollingReason::kHandlingScrollFromMainThread); - - EXPECT_THAT( - histogram_tester().GetAllSamples("Renderer4.MainThreadWheelScrollReason"), - testing::ElementsAre(base::Bucket(1, 1), base::Bucket(3, 1), - base::Bucket(5, 1), base::Bucket(14, 1))); -} - -TEST_P(InputHandlerProxyTest, GestureScrollingThreadStatusHistogram) { - VERIFY_AND_RESET_MOCKS(); - - WebTouchEvent touch_start(WebInputEvent::kTouchStart, - WebInputEvent::kNoModifiers, - WebInputEvent::GetStaticTimeStampForTests()); - touch_start.touches_length = 1; - touch_start.touch_start_or_first_touch_move = true; - touch_start.touches[0] = - CreateWebTouchPoint(WebTouchPoint::kStatePressed, 10, 10); - - WebGestureEvent gesture_scroll_begin( - WebInputEvent::kGestureScrollBegin, WebInputEvent::kNoModifiers, - WebInputEvent::GetStaticTimeStampForTests(), - blink::kWebGestureDeviceTouchscreen); - - WebGestureEvent gesture_scroll_end( - WebInputEvent::kGestureScrollEnd, WebInputEvent::kNoModifiers, - WebInputEvent::GetStaticTimeStampForTests(), - blink::kWebGestureDeviceTouchscreen); - - // Touch start with passive event listener. - EXPECT_CALL( - mock_input_handler_, - EventListenerTypeForTouchStartOrMoveAt( - testing::Property(&gfx::Point::x, testing::Gt(0)), testing::_)) - .WillOnce(testing::Return( - cc::InputHandler::TouchStartOrMoveEventListenerType::NO_HANDLER)); - EXPECT_CALL( - mock_input_handler_, - GetEventListenerProperties(cc::EventListenerClass::kTouchStartOrMove)) - .WillOnce(testing::Return(cc::EventListenerProperties::kPassive)); - EXPECT_CALL(mock_client_, - SetWhiteListedTouchAction(testing::_, testing::_, testing::_)) - .WillOnce(testing::Return()); - - expected_disposition_ = InputHandlerProxy::DID_HANDLE_NON_BLOCKING; - EXPECT_EQ(expected_disposition_, - input_handler_->HandleInputEvent(touch_start)); - - EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(kImplThreadScrollState)); - expected_disposition_ = InputHandlerProxy::DID_HANDLE; - EXPECT_EQ(expected_disposition_, - input_handler_->HandleInputEvent(gesture_scroll_begin)); - - EXPECT_THAT(histogram_tester().GetAllSamples( - "Renderer4.GestureScrollingThreadStatus"), - testing::ElementsAre(base::Bucket(0, 1))); - - EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, true)); - expected_disposition_ = InputHandlerProxy::DID_HANDLE; - EXPECT_EQ(expected_disposition_, - input_handler_->HandleInputEvent(gesture_scroll_end)); - - VERIFY_AND_RESET_MOCKS(); - - // Touch event with HANDLER_ON_SCROLLING_LAYER event listener. - EXPECT_CALL( - mock_input_handler_, - EventListenerTypeForTouchStartOrMoveAt( - testing::Property(&gfx::Point::x, testing::Gt(0)), testing::_)) - .WillOnce( - testing::Return(cc::InputHandler::TouchStartOrMoveEventListenerType:: - HANDLER_ON_SCROLLING_LAYER)); - EXPECT_CALL(mock_client_, - SetWhiteListedTouchAction(testing::_, testing::_, testing::_)) - .WillOnce(testing::Return()); - - expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; - EXPECT_EQ(expected_disposition_, - input_handler_->HandleInputEvent(touch_start)); - - EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(kImplThreadScrollState)); - expected_disposition_ = InputHandlerProxy::DID_HANDLE; - EXPECT_EQ(expected_disposition_, - input_handler_->HandleInputEvent(gesture_scroll_begin)); - - EXPECT_THAT(histogram_tester().GetAllSamples( - "Renderer4.GestureScrollingThreadStatus"), - testing::ElementsAre(base::Bucket(0, 1), base::Bucket(1, 1))); - - EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, true)); - expected_disposition_ = InputHandlerProxy::DID_HANDLE; - EXPECT_EQ(expected_disposition_, - input_handler_->HandleInputEvent(gesture_scroll_end)); - - VERIFY_AND_RESET_MOCKS(); - - // Gesture scrolling on main thread. - EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(kMainThreadScrollState)); - expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; - EXPECT_EQ(expected_disposition_, - input_handler_->HandleInputEvent(gesture_scroll_begin)); - - EXPECT_THAT(histogram_tester().GetAllSamples( - "Renderer4.GestureScrollingThreadStatus"), - testing::ElementsAre(base::Bucket(0, 1), base::Bucket(1, 1), - base::Bucket(2, 1))); - - EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, true)); - expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; - EXPECT_EQ(expected_disposition_, - input_handler_->HandleInputEvent(gesture_scroll_end)); - - VERIFY_AND_RESET_MOCKS(); -} - -TEST_P(InputHandlerProxyTest, WheelScrollingThreadStatusHistogram) { - VERIFY_AND_RESET_MOCKS(); - - WebMouseWheelEvent wheel(WebInputEvent::kMouseWheel, - WebInputEvent::kControlKey, - WebInputEvent::GetStaticTimeStampForTests()); - - WebGestureEvent gesture_scroll_begin( - WebInputEvent::kGestureScrollBegin, WebInputEvent::kNoModifiers, - WebInputEvent::GetStaticTimeStampForTests(), - blink::kWebGestureDeviceTouchpad); - - WebGestureEvent gesture_scroll_end( - WebInputEvent::kGestureScrollEnd, WebInputEvent::kNoModifiers, - WebInputEvent::GetStaticTimeStampForTests(), - blink::kWebGestureDeviceTouchpad); - - // Wheel event with passive event listener. - EXPECT_CALL(mock_input_handler_, HasBlockingWheelEventHandlerAt(testing::_)) - .WillRepeatedly(testing::Return(false)); - EXPECT_CALL(mock_input_handler_, - GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) - .WillOnce(testing::Return(cc::EventListenerProperties::kPassive)); - expected_disposition_ = InputHandlerProxy::DID_HANDLE_NON_BLOCKING; - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(wheel)); - - EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(kImplThreadScrollState)); - expected_disposition_ = InputHandlerProxy::DID_HANDLE; - EXPECT_EQ(expected_disposition_, - input_handler_->HandleInputEvent(gesture_scroll_begin)); - - EXPECT_THAT( - histogram_tester().GetAllSamples("Renderer4.WheelScrollingThreadStatus"), - testing::ElementsAre(base::Bucket(0, 1))); - - EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, true)); - expected_disposition_ = InputHandlerProxy::DID_HANDLE; - EXPECT_EQ(expected_disposition_, - input_handler_->HandleInputEvent(gesture_scroll_end)); - - VERIFY_AND_RESET_MOCKS(); - - // Wheel event with blocking event listener. If there is a wheel event handler - // at the point, we do not need to call GetEventListenerProperties since it - // indicates kBlocking. - EXPECT_CALL(mock_input_handler_, HasBlockingWheelEventHandlerAt(testing::_)) - .WillRepeatedly(testing::Return(true)); - expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; - EXPECT_EQ(expected_disposition_, input_handler_->HandleInputEvent(wheel)); - - EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(kImplThreadScrollState)); - expected_disposition_ = InputHandlerProxy::DID_HANDLE; - EXPECT_EQ(expected_disposition_, - input_handler_->HandleInputEvent(gesture_scroll_begin)); - - EXPECT_THAT( - histogram_tester().GetAllSamples("Renderer4.WheelScrollingThreadStatus"), - testing::ElementsAre(base::Bucket(0, 1), base::Bucket(1, 1))); - - EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, true)); - expected_disposition_ = InputHandlerProxy::DID_HANDLE; - EXPECT_EQ(expected_disposition_, - input_handler_->HandleInputEvent(gesture_scroll_end)); - - VERIFY_AND_RESET_MOCKS(); - - // Wheel scrolling on main thread. - EXPECT_CALL(mock_input_handler_, HasBlockingWheelEventHandlerAt(testing::_)) - .WillRepeatedly(testing::Return(true)); - EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) - .WillOnce(testing::Return(kMainThreadScrollState)); - expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; - EXPECT_EQ(expected_disposition_, - input_handler_->HandleInputEvent(gesture_scroll_begin)); - - EXPECT_THAT( - histogram_tester().GetAllSamples("Renderer4.WheelScrollingThreadStatus"), - testing::ElementsAre(base::Bucket(0, 1), base::Bucket(1, 1), - base::Bucket(2, 1))); - - EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, true)); - expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; - EXPECT_EQ(expected_disposition_, - input_handler_->HandleInputEvent(gesture_scroll_end)); - - VERIFY_AND_RESET_MOCKS(); -} - TEST_F(InputHandlerProxyEventQueueTest, VSyncAlignedGestureScroll) { base::HistogramTester histogram_tester; @@ -2103,8 +1875,423 @@ TEST_F(InputHandlerProxyEventQueueTest, ScrollPredictorTest) { testing::Mock::VerifyAndClearExpectations(&mock_input_handler_); } +class InputHandlerProxyMainThreadScrollingReasonTest + : public InputHandlerProxyTest { + public: + enum TestEventType { + Touch, + MouseWheel, + }; + + InputHandlerProxyMainThreadScrollingReasonTest() : InputHandlerProxyTest() {} + ~InputHandlerProxyMainThreadScrollingReasonTest() { input_handler_.reset(); } + + void SetupEvents(TestEventType type) { + touch_start_ = + WebTouchEvent(WebInputEvent::kTouchStart, WebInputEvent::kNoModifiers, + WebInputEvent::GetStaticTimeStampForTests()); + touch_end_ = + WebTouchEvent(WebInputEvent::kTouchEnd, WebInputEvent::kNoModifiers, + WebInputEvent::GetStaticTimeStampForTests()); + wheel_event_ = WebMouseWheelEvent( + WebInputEvent::kMouseWheel, WebInputEvent::kControlKey, + WebInputEvent::GetStaticTimeStampForTests()); + gesture_scroll_begin_ = WebGestureEvent( + WebInputEvent::kGestureScrollBegin, WebInputEvent::kNoModifiers, + WebInputEvent::GetStaticTimeStampForTests(), + type == TestEventType::MouseWheel + ? blink::kWebGestureDeviceTouchpad + : blink::kWebGestureDeviceTouchscreen); + gesture_scroll_end_ = WebGestureEvent( + WebInputEvent::kGestureScrollEnd, WebInputEvent::kNoModifiers, + WebInputEvent::GetStaticTimeStampForTests(), + type == TestEventType::MouseWheel + ? blink::kWebGestureDeviceTouchpad + : blink::kWebGestureDeviceTouchscreen); + touch_start_.touches_length = 1; + touch_start_.touch_start_or_first_touch_move = true; + touch_start_.touches[0] = + CreateWebTouchPoint(WebTouchPoint::kStatePressed, 10, 10); + + touch_end_.touches_length = 1; + } + + base::HistogramBase::Sample GetBucketSample(uint32_t reason) { + if (reason == cc::MainThreadScrollingReason::kNotScrollingOnMain) + return 0; + + uint32_t bucket = 1; + while ((reason = reason >> 1)) + bucket++; + return bucket; + } + + protected: + WebTouchEvent touch_start_; + WebTouchEvent touch_end_; + WebMouseWheelEvent wheel_event_; + WebGestureEvent gesture_scroll_begin_; + WebGestureEvent gesture_scroll_end_; +}; + +TEST_P(InputHandlerProxyMainThreadScrollingReasonTest, + GestureScrollNotScrollOnMain) { + // Touch start with passive event listener. + SetupEvents(TestEventType::Touch); + + EXPECT_CALL( + mock_input_handler_, + EventListenerTypeForTouchStartOrMoveAt( + testing::Property(&gfx::Point::x, testing::Gt(0)), testing::_)) + .WillOnce(testing::Return( + cc::InputHandler::TouchStartOrMoveEventListenerType::NO_HANDLER)); + EXPECT_CALL( + mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kTouchStartOrMove)) + .WillOnce(testing::Return(cc::EventListenerProperties::kPassive)); + EXPECT_CALL(mock_client_, + SetWhiteListedTouchAction(testing::_, testing::_, testing::_)) + .WillOnce(testing::Return()); + + expected_disposition_ = InputHandlerProxy::DID_HANDLE_NON_BLOCKING; + EXPECT_EQ(expected_disposition_, + input_handler_->HandleInputEvent(touch_start_)); + + EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(kImplThreadScrollState)); + expected_disposition_ = InputHandlerProxy::DID_HANDLE; + EXPECT_EQ(expected_disposition_, + input_handler_->HandleInputEvent(gesture_scroll_begin_)); + EXPECT_THAT( + histogram_tester().GetAllSamples( + "Renderer4.MainThreadGestureScrollReason"), + testing::ElementsAre(base::Bucket( + GetBucketSample(cc::MainThreadScrollingReason::kNotScrollingOnMain), + 1))); + + EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, true)); + expected_disposition_ = InputHandlerProxy::DID_HANDLE; + EXPECT_EQ(expected_disposition_, + input_handler_->HandleInputEvent(gesture_scroll_end_)); +} + +TEST_P(InputHandlerProxyMainThreadScrollingReasonTest, + GestureScrollTouchEventHandlerRegion) { + // The touch event hits a touch event handler and should block on main thread. + // Since ScrollBegin allows the gesture to scroll on impl. We collect + // TouchEventHandler reason but not HandlingScrollFromMainThread. + SetupEvents(TestEventType::Touch); + + EXPECT_CALL( + mock_input_handler_, + EventListenerTypeForTouchStartOrMoveAt( + testing::Property(&gfx::Point::x, testing::Gt(0)), testing::_)) + .WillOnce( + testing::Return(cc::InputHandler::TouchStartOrMoveEventListenerType:: + HANDLER_ON_SCROLLING_LAYER)); + EXPECT_CALL(mock_client_, + SetWhiteListedTouchAction(testing::_, testing::_, testing::_)) + .WillOnce(testing::Return()); + + expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; + EXPECT_EQ(expected_disposition_, + input_handler_->HandleInputEvent(touch_start_)); + + EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(kImplThreadScrollState)); + expected_disposition_ = InputHandlerProxy::DID_HANDLE; + EXPECT_EQ(expected_disposition_, + input_handler_->HandleInputEvent(gesture_scroll_begin_)); + EXPECT_THAT(histogram_tester().GetAllSamples( + "Renderer4.MainThreadGestureScrollReason"), + testing::ElementsAre(base::Bucket( + GetBucketSample( + cc::MainThreadScrollingReason::kTouchEventHandlerRegion), + 1))); + + EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, true)); + expected_disposition_ = InputHandlerProxy::DID_HANDLE; + EXPECT_EQ(expected_disposition_, + input_handler_->HandleInputEvent(gesture_scroll_end_)); +} + +TEST_P(InputHandlerProxyMainThreadScrollingReasonTest, + GestureScrollTouchEventHandlerRegionAndHandlingScrollFromMainThread) { + // The touch event hits a touch event handler and should block on main thread. + // Since ScrollBegin doesn't allow the gesture to scroll on impl. We report + // TouchEventHandler reason as well as HandlingScrollFromMainThread. Since we + // do not collect HandlingScrollFromMainThread when there are other reasons + // present, TouchEventHandler is the only reason being collected in the + // histogram. + SetupEvents(TestEventType::Touch); + + EXPECT_CALL( + mock_input_handler_, + EventListenerTypeForTouchStartOrMoveAt( + testing::Property(&gfx::Point::x, testing::Gt(0)), testing::_)) + .WillOnce( + testing::Return(cc::InputHandler::TouchStartOrMoveEventListenerType:: + HANDLER_ON_SCROLLING_LAYER)); + EXPECT_CALL(mock_client_, + SetWhiteListedTouchAction(testing::_, testing::_, testing::_)) + .WillOnce(testing::Return()); + + expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; + EXPECT_EQ(expected_disposition_, + input_handler_->HandleInputEvent(touch_start_)); + + EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(kMainThreadScrollState)); + expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; + EXPECT_EQ(expected_disposition_, + input_handler_->HandleInputEvent(gesture_scroll_begin_)); + + EXPECT_THAT(histogram_tester().GetAllSamples( + "Renderer4.MainThreadGestureScrollReason"), + testing::ElementsAre(base::Bucket( + GetBucketSample( + cc::MainThreadScrollingReason::kTouchEventHandlerRegion), + 1))); + + // Handle touch end event so that input handler proxy is out of the state of + // DID_NOT_HANDLE. + EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, true)); + expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; + EXPECT_EQ(expected_disposition_, + input_handler_->HandleInputEvent(gesture_scroll_end_)); + + EXPECT_EQ(expected_disposition_, + input_handler_->HandleInputEvent(touch_end_)); +} + +TEST_P(InputHandlerProxyMainThreadScrollingReasonTest, + GestureScrollHandlingScrollFromMainThread) { + // Gesture scrolling on main thread. We only record + // HandlingScrollFromMainThread when it's the only available reason. + SetupEvents(TestEventType::Touch); + EXPECT_CALL( + mock_input_handler_, + EventListenerTypeForTouchStartOrMoveAt( + testing::Property(&gfx::Point::x, testing::Gt(0)), testing::_)) + .WillOnce(testing::Return( + cc::InputHandler::TouchStartOrMoveEventListenerType::NO_HANDLER)); + EXPECT_CALL(mock_client_, + SetWhiteListedTouchAction(testing::_, testing::_, testing::_)) + .WillOnce(testing::Return()); + EXPECT_CALL(mock_input_handler_, GetEventListenerProperties(testing::_)) + .WillRepeatedly(testing::Return(cc::EventListenerProperties::kPassive)); + + expected_disposition_ = InputHandlerProxy::DID_HANDLE_NON_BLOCKING; + EXPECT_EQ(expected_disposition_, + input_handler_->HandleInputEvent(touch_start_)); + + EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(kMainThreadScrollState)); + expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; + EXPECT_EQ(expected_disposition_, + input_handler_->HandleInputEvent(gesture_scroll_begin_)); + + EXPECT_THAT( + histogram_tester().GetAllSamples( + "Renderer4.MainThreadGestureScrollReason"), + testing::ElementsAre(base::Bucket( + GetBucketSample( + cc::MainThreadScrollingReason::kHandlingScrollFromMainThread), + 1))); + + EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, true)); + expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; + EXPECT_EQ(expected_disposition_, + input_handler_->HandleInputEvent(gesture_scroll_end_)); +} + +TEST_P(InputHandlerProxyMainThreadScrollingReasonTest, WheelScrollHistogram) { + // Firstly check if input handler can correctly record main thread scrolling + // reasons. + input_handler_->RecordMainThreadScrollingReasonsForTest( + blink::kWebGestureDeviceTouchpad, + cc::MainThreadScrollingReason::kHasBackgroundAttachmentFixedObjects | + cc::MainThreadScrollingReason::kThreadedScrollingDisabled | + cc::MainThreadScrollingReason::kPageOverlay | + cc::MainThreadScrollingReason::kHandlingScrollFromMainThread); + + EXPECT_THAT( + histogram_tester().GetAllSamples("Renderer4.MainThreadWheelScrollReason"), + testing::ElementsAre( + base::Bucket( + GetBucketSample(cc::MainThreadScrollingReason:: + kHasBackgroundAttachmentFixedObjects), + 1), + base::Bucket( + GetBucketSample( + cc::MainThreadScrollingReason::kThreadedScrollingDisabled), + 1), + base::Bucket( + GetBucketSample(cc::MainThreadScrollingReason::kPageOverlay), + 1))); + + // We only want to record "Handling scroll from main thread" reason if it's + // the only reason. If it's not the only reason, the "real" reason for + // scrolling on main is something else, and we only want to pay attention to + // that reason. So we should only include this reason in the histogram when + // its on its own. + input_handler_->RecordMainThreadScrollingReasonsForTest( + blink::kWebGestureDeviceTouchpad, + cc::MainThreadScrollingReason::kHandlingScrollFromMainThread); + + EXPECT_THAT( + histogram_tester().GetAllSamples("Renderer4.MainThreadWheelScrollReason"), + testing::ElementsAre( + base::Bucket( + GetBucketSample(cc::MainThreadScrollingReason:: + kHasBackgroundAttachmentFixedObjects), + 1), + base::Bucket( + GetBucketSample( + cc::MainThreadScrollingReason::kThreadedScrollingDisabled), + 1), + base::Bucket( + GetBucketSample(cc::MainThreadScrollingReason::kPageOverlay), 1), + base::Bucket( + GetBucketSample( + cc::MainThreadScrollingReason::kHandlingScrollFromMainThread), + 1))); +} + +TEST_P(InputHandlerProxyMainThreadScrollingReasonTest, + WheelScrollNotScrollingOnMain) { + // Even if a scroller is composited, we still need to record its main thread + // scrolling reason if it is blocked on a main thread event handler. + SetupEvents(TestEventType::MouseWheel); + + // We can scroll on impl for an wheel event with passive event listener. + EXPECT_CALL(mock_input_handler_, HasBlockingWheelEventHandlerAt(testing::_)) + .WillRepeatedly(testing::Return(false)); + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kPassive)); + expected_disposition_ = InputHandlerProxy::DID_HANDLE_NON_BLOCKING; + EXPECT_EQ(expected_disposition_, + input_handler_->HandleInputEvent(wheel_event_)); + + EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(kImplThreadScrollState)); + expected_disposition_ = InputHandlerProxy::DID_HANDLE; + EXPECT_EQ(expected_disposition_, + input_handler_->HandleInputEvent(gesture_scroll_begin_)); + + EXPECT_THAT( + histogram_tester().GetAllSamples("Renderer4.MainThreadWheelScrollReason"), + testing::ElementsAre(base::Bucket( + GetBucketSample(cc::MainThreadScrollingReason::kNotScrollingOnMain), + 1))); + + EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, true)); + expected_disposition_ = InputHandlerProxy::DID_HANDLE; + EXPECT_EQ(expected_disposition_, + input_handler_->HandleInputEvent(gesture_scroll_end_)); +} + +TEST_P(InputHandlerProxyMainThreadScrollingReasonTest, + WheelScrollWheelEventHandlerRegion) { + // Wheel event with blocking event listener. If there is a wheel event handler + // at the point, we do not need to call GetEventListenerProperties since it + // indicates kBlocking. + SetupEvents(TestEventType::MouseWheel); + EXPECT_CALL(mock_input_handler_, HasBlockingWheelEventHandlerAt(testing::_)) + .WillRepeatedly(testing::Return(true)); + expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; + EXPECT_EQ(expected_disposition_, + input_handler_->HandleInputEvent(wheel_event_)); + + EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(kImplThreadScrollState)); + expected_disposition_ = InputHandlerProxy::DID_HANDLE; + EXPECT_EQ(expected_disposition_, + input_handler_->HandleInputEvent(gesture_scroll_begin_)); + + EXPECT_THAT( + histogram_tester().GetAllSamples("Renderer4.MainThreadWheelScrollReason"), + testing::ElementsAre(base::Bucket( + GetBucketSample( + cc::MainThreadScrollingReason::kWheelEventHandlerRegion), + 1))); + + EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, true)); + expected_disposition_ = InputHandlerProxy::DID_HANDLE; + EXPECT_EQ(expected_disposition_, + input_handler_->HandleInputEvent(gesture_scroll_end_)); +} + +TEST_P(InputHandlerProxyMainThreadScrollingReasonTest, + WheelScrollWheelEventHandlerRegionAndHandlingScrollFromMainThread) { + // Wheel scrolling on main thread. Because we also block scrolling with wheel + // event handler, we should record that reason as well. + SetupEvents(TestEventType::MouseWheel); + EXPECT_CALL(mock_input_handler_, HasBlockingWheelEventHandlerAt(testing::_)) + .WillRepeatedly(testing::Return(true)); + expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; + EXPECT_EQ(expected_disposition_, + input_handler_->HandleInputEvent(wheel_event_)); + + EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(kMainThreadScrollState)); + expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; + EXPECT_EQ(expected_disposition_, + input_handler_->HandleInputEvent(gesture_scroll_begin_)); + + EXPECT_THAT( + histogram_tester().GetAllSamples("Renderer4.MainThreadWheelScrollReason"), + testing::ElementsAre(base::Bucket( + GetBucketSample( + cc::MainThreadScrollingReason::kWheelEventHandlerRegion), + 1))); + + EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, true)); + expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; + EXPECT_EQ(expected_disposition_, + input_handler_->HandleInputEvent(gesture_scroll_end_)); +} + +TEST_P(InputHandlerProxyMainThreadScrollingReasonTest, + WheelScrollHandlingScrollFromMainThread) { + // Gesture scrolling on main thread. We only record + // HandlingScrollFromMainThread when it's the only available reason. + SetupEvents(TestEventType::MouseWheel); + EXPECT_CALL(mock_input_handler_, HasBlockingWheelEventHandlerAt(testing::_)) + .WillRepeatedly(testing::Return(false)); + EXPECT_CALL(mock_input_handler_, + GetEventListenerProperties(cc::EventListenerClass::kMouseWheel)) + .WillOnce(testing::Return(cc::EventListenerProperties::kNone)); + expected_disposition_ = InputHandlerProxy::DROP_EVENT; + EXPECT_EQ(expected_disposition_, + input_handler_->HandleInputEvent(wheel_event_)); + + EXPECT_CALL(mock_input_handler_, ScrollBegin(testing::_, testing::_)) + .WillOnce(testing::Return(kMainThreadScrollState)); + expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; + EXPECT_EQ(expected_disposition_, + input_handler_->HandleInputEvent(gesture_scroll_begin_)); + + EXPECT_THAT( + histogram_tester().GetAllSamples("Renderer4.MainThreadWheelScrollReason"), + testing::ElementsAre(base::Bucket( + GetBucketSample( + cc::MainThreadScrollingReason::kHandlingScrollFromMainThread), + 1))); + + EXPECT_CALL(mock_input_handler_, ScrollEnd(testing::_, true)); + expected_disposition_ = InputHandlerProxy::DID_NOT_HANDLE; + EXPECT_EQ(expected_disposition_, + input_handler_->HandleInputEvent(gesture_scroll_end_)); +} + INSTANTIATE_TEST_CASE_P(AnimateInput, InputHandlerProxyTest, testing::ValuesIn(test_types)); + +INSTANTIATE_TEST_CASE_P(AnimateInput, + InputHandlerProxyMainThreadScrollingReasonTest, + testing::ValuesIn(test_types)); } // namespace test } // namespace ui diff --git a/chromium/ui/events/blink/prediction/input_predictor_unittest_helpers.cc b/chromium/ui/events/blink/prediction/input_predictor_unittest_helpers.cc new file mode 100644 index 00000000000..9912ea66222 --- /dev/null +++ b/chromium/ui/events/blink/prediction/input_predictor_unittest_helpers.cc @@ -0,0 +1,31 @@ +// 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/events/blink/prediction/input_predictor_unittest_helpers.h" + +namespace ui { + +InputPredictorTest::InputPredictorTest() = default; +InputPredictorTest::~InputPredictorTest() = default; + +void InputPredictorTest::ValidatePredictor( + const std::vector<double>& x, + const std::vector<double>& y, + const std::vector<double>& timestamp_ms) { + predictor_->Reset(); + for (size_t i = 0; i < timestamp_ms.size(); i++) { + if (predictor_->HasPrediction()) { + ui::InputPredictor::InputData result; + EXPECT_TRUE(predictor_->GeneratePrediction( + FromMilliseconds(timestamp_ms[i]), &result)); + EXPECT_NEAR(result.pos.x(), x[i], kEpsilon); + EXPECT_NEAR(result.pos.y(), y[i], kEpsilon); + } + InputPredictor::InputData data = {gfx::PointF(x[i], y[i]), + FromMilliseconds(timestamp_ms[i])}; + predictor_->Update(data); + } +} + +} // namespace ui diff --git a/chromium/ui/events/blink/prediction/input_predictor_unittest.cc b/chromium/ui/events/blink/prediction/input_predictor_unittest_helpers.h index ec8a051a363..569d7a2e74b 100644 --- a/chromium/ui/events/blink/prediction/input_predictor_unittest.cc +++ b/chromium/ui/events/blink/prediction/input_predictor_unittest_helpers.h @@ -2,25 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_EVENTS_BLINK_PREDICTION_INPUT_PREDICTOR_UNITTEST_H_ -#define UI_EVENTS_BLINK_PREDICTION_INPUT_PREDICTOR_UNITTEST_H_ +#ifndef UI_EVENTS_BLINK_PREDICTION_INPUT_PREDICTOR_UNITTEST_HELPERS_H_ +#define UI_EVENTS_BLINK_PREDICTION_INPUT_PREDICTOR_UNITTEST_HELPERS_H_ #include "ui/events/blink/prediction/input_predictor.h" + #include "testing/gtest/include/gtest/gtest.h" #include "ui/events/blink/blink_event_util.h" -namespace { - -constexpr double kEpsilon = 0.1; - -} // namespace - namespace ui { // Base class for predictor unit tests class InputPredictorTest : public testing::Test { public: - InputPredictorTest() {} + InputPredictorTest(); + ~InputPredictorTest() override; static base::TimeTicks FromMilliseconds(int64_t ms) { return blink::WebInputEvent::GetStaticTimeStampForTests() + @@ -29,23 +25,11 @@ class InputPredictorTest : public testing::Test { void ValidatePredictor(const std::vector<double>& x, const std::vector<double>& y, - const std::vector<double>& timestamp_ms) { - predictor_->Reset(); - for (size_t i = 0; i < timestamp_ms.size(); i++) { - if (predictor_->HasPrediction()) { - ui::InputPredictor::InputData result; - EXPECT_TRUE(predictor_->GeneratePrediction( - FromMilliseconds(timestamp_ms[i]), &result)); - EXPECT_NEAR(result.pos.x(), x[i], kEpsilon); - EXPECT_NEAR(result.pos.y(), y[i], kEpsilon); - } - InputPredictor::InputData data = {gfx::PointF(x[i], y[i]), - FromMilliseconds(timestamp_ms[i])}; - predictor_->Update(data); - } - } + const std::vector<double>& timestamp_ms); protected: + static constexpr double kEpsilon = 0.1; + std::unique_ptr<InputPredictor> predictor_; DISALLOW_COPY_AND_ASSIGN(InputPredictorTest); @@ -53,4 +37,4 @@ class InputPredictorTest : public testing::Test { } // namespace ui -#endif // UI_EVENTS_BLINK_PREDICTION_INPUT_PREDICTOR_UNITTEST_H_ +#endif // UI_EVENTS_BLINK_PREDICTION_INPUT_PREDICTOR_UNITTEST_HELPERS_H_ diff --git a/chromium/ui/events/blink/prediction/kalman_predictor_unittest.cc b/chromium/ui/events/blink/prediction/kalman_predictor_unittest.cc index 6a4074cf02c..4d8a1d80deb 100644 --- a/chromium/ui/events/blink/prediction/kalman_predictor_unittest.cc +++ b/chromium/ui/events/blink/prediction/kalman_predictor_unittest.cc @@ -5,7 +5,7 @@ #include <vector> #include "testing/gtest/include/gtest/gtest.h" -#include "ui/events/blink/prediction/input_predictor_unittest.cc" +#include "ui/events/blink/prediction/input_predictor_unittest_helpers.h" #include "ui/events/blink/prediction/kalman_predictor.h" namespace ui { @@ -13,7 +13,6 @@ namespace test { namespace { constexpr uint32_t kExpectedStableIterNum = 4; -constexpr double kEpsilon = 0.001; struct DataSet { double initial_observation; @@ -26,6 +25,7 @@ struct DataSet { void ValidateSingleKalmanFilter(const DataSet& data) { std::unique_ptr<KalmanFilter> kalman_filter = std::make_unique<KalmanFilter>(); + constexpr double kEpsilon = 0.001; constexpr double kDtMillisecond = 8; kalman_filter->Update(data.initial_observation, kDtMillisecond); for (size_t i = 0; i < data.observation.size(); i++) { diff --git a/chromium/ui/events/blink/prediction/least_squares_predictor.cc b/chromium/ui/events/blink/prediction/least_squares_predictor.cc index 94f1be7dabd..534d3de407d 100644 --- a/chromium/ui/events/blink/prediction/least_squares_predictor.cc +++ b/chromium/ui/events/blink/prediction/least_squares_predictor.cc @@ -10,12 +10,12 @@ namespace ui { namespace { -constexpr double kEpsilon = std::numeric_limits<double>::epsilon(); - // Solve XB = y. static bool SolveLeastSquares(const gfx::Matrix3F& x, const std::deque<double>& y, gfx::Vector3dF& result) { + constexpr double kEpsilon = std::numeric_limits<double>::epsilon(); + // return last point if y didn't change. if (std::abs(y[0] - y[1]) < kEpsilon && std::abs(y[1] - y[2]) < kEpsilon) { result = gfx::Vector3dF(y[2], 0, 0); diff --git a/chromium/ui/events/blink/prediction/least_squares_predictor_unittest.cc b/chromium/ui/events/blink/prediction/least_squares_predictor_unittest.cc index c4836f01b47..d1d2c032ef2 100644 --- a/chromium/ui/events/blink/prediction/least_squares_predictor_unittest.cc +++ b/chromium/ui/events/blink/prediction/least_squares_predictor_unittest.cc @@ -4,7 +4,7 @@ #include "ui/events/blink/prediction/least_squares_predictor.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/events/blink/prediction/input_predictor_unittest.cc" +#include "ui/events/blink/prediction/input_predictor_unittest_helpers.h" namespace ui { namespace test { diff --git a/chromium/ui/events/blink/web_input_event.cc b/chromium/ui/events/blink/web_input_event.cc index e693fc2e1ca..5f318baf91e 100644 --- a/chromium/ui/events/blink/web_input_event.cc +++ b/chromium/ui/events/blink/web_input_event.cc @@ -8,6 +8,7 @@ #include "ui/events/blink/blink_event_util.h" #include "ui/events/blink/blink_features.h" #include "ui/events/event.h" +#include "ui/events/event_target.h" #include "ui/events/event_utils.h" #include "ui/events/keycodes/dom/keycode_converter.h" #include "ui/events/keycodes/keyboard_code_conversion.h" @@ -24,12 +25,8 @@ namespace ui { namespace { -gfx::PointF GetScreenLocationFromEvent( - const LocatedEvent& event, - const base::Callback<gfx::PointF(const LocatedEvent& event)>& - screen_location_callback) { - DCHECK(!screen_location_callback.is_null()); - return event.target() ? screen_location_callback.Run(event) +gfx::PointF GetScreenLocationFromEvent(const LocatedEvent& event) { + return event.target() ? event.target()->GetScreenLocationF(event) : event.root_location_f(); } @@ -232,12 +229,11 @@ blink::WebMouseWheelEvent MakeWebMouseWheelEventFromUiEvent( // The only place where an Event's data differs from what the underlying // PlatformEvent would provide is position data. We would like to provide // coordinates relative to its hosting window, rather than the top level -// platform window. To do this a callback is accepted to allow for clients to -// map the coordinates. +// platform window. The event target is used to get the screen coordinates. // // The approach is to fully construct a blink::WebInputEvent from the // Event's PlatformEvent, and then replace the coordinate fields with -// the translated values from the Event. +// the translated values from the Event (and EventTarget). // // The exception is mouse events on linux. The MouseEvent contains enough // necessary information to construct a WebMouseEvent. So instead of extracting @@ -246,10 +242,7 @@ blink::WebMouseWheelEvent MakeWebMouseWheelEventFromUiEvent( // MouseEvent. This will not be necessary once only XInput2 is supported. // -blink::WebMouseEvent MakeWebMouseEvent( - const MouseEvent& event, - const base::Callback<gfx::PointF(const LocatedEvent& event)>& - screen_location_callback) { +blink::WebMouseEvent MakeWebMouseEvent(const MouseEvent& event) { // Construct an untranslated event from the platform event data. blink::WebMouseEvent webkit_event = #if defined(OS_WIN) @@ -273,17 +266,13 @@ blink::WebMouseEvent MakeWebMouseEvent( return webkit_event; #endif - const gfx::PointF screen_point = - GetScreenLocationFromEvent(event, screen_location_callback); + const gfx::PointF screen_point = GetScreenLocationFromEvent(event); webkit_event.SetPositionInScreen(screen_point.x(), screen_point.y()); return webkit_event; } -blink::WebMouseWheelEvent MakeWebMouseWheelEvent( - const MouseWheelEvent& event, - const base::Callback<gfx::PointF(const LocatedEvent& event)>& - screen_location_callback) { +blink::WebMouseWheelEvent MakeWebMouseWheelEvent(const MouseWheelEvent& event) { #if defined(OS_WIN) // Construct an untranslated event from the platform event data. blink::WebMouseWheelEvent webkit_event = @@ -302,17 +291,13 @@ blink::WebMouseWheelEvent MakeWebMouseWheelEvent( // |event|. webkit_event.SetPositionInWidget(event.x(), event.y()); - const gfx::PointF screen_point = - GetScreenLocationFromEvent(event, screen_location_callback); + const gfx::PointF screen_point = GetScreenLocationFromEvent(event); webkit_event.SetPositionInScreen(screen_point.x(), screen_point.y()); return webkit_event; } -blink::WebMouseWheelEvent MakeWebMouseWheelEvent( - const ScrollEvent& event, - const base::Callback<gfx::PointF(const LocatedEvent& event)>& - screen_location_callback) { +blink::WebMouseWheelEvent MakeWebMouseWheelEvent(const ScrollEvent& event) { #if defined(OS_WIN) // Construct an untranslated event from the platform event data. blink::WebMouseWheelEvent webkit_event = @@ -331,8 +316,7 @@ blink::WebMouseWheelEvent MakeWebMouseWheelEvent( // |event|. webkit_event.SetPositionInWidget(event.x(), event.y()); - const gfx::PointF screen_point = - GetScreenLocationFromEvent(event, screen_location_callback); + const gfx::PointF screen_point = GetScreenLocationFromEvent(event); webkit_event.SetPositionInScreen(screen_point.x(), screen_point.y()); return webkit_event; @@ -363,30 +347,22 @@ blink::WebKeyboardEvent MakeWebKeyboardEvent(const KeyEvent& event) { return webkit_event; } -blink::WebGestureEvent MakeWebGestureEvent( - const GestureEvent& event, - const base::Callback<gfx::PointF(const LocatedEvent& event)>& - screen_location_callback) { +blink::WebGestureEvent MakeWebGestureEvent(const GestureEvent& event) { blink::WebGestureEvent gesture_event = MakeWebGestureEventFromUIEvent(event); gesture_event.SetPositionInWidget(event.location_f()); - const gfx::PointF screen_point = - GetScreenLocationFromEvent(event, screen_location_callback); + const gfx::PointF screen_point = GetScreenLocationFromEvent(event); gesture_event.SetPositionInScreen(screen_point); return gesture_event; } -blink::WebGestureEvent MakeWebGestureEvent( - const ScrollEvent& event, - const base::Callback<gfx::PointF(const LocatedEvent& event)>& - screen_location_callback) { +blink::WebGestureEvent MakeWebGestureEvent(const ScrollEvent& event) { blink::WebGestureEvent gesture_event = MakeWebGestureEventFromUiEvent(event); gesture_event.SetPositionInWidget(event.location_f()); - const gfx::PointF screen_point = - GetScreenLocationFromEvent(event, screen_location_callback); + const gfx::PointF screen_point = GetScreenLocationFromEvent(event); gesture_event.SetPositionInScreen(screen_point); return gesture_event; diff --git a/chromium/ui/events/blink/web_input_event.h b/chromium/ui/events/blink/web_input_event.h index 17d90025a14..277851aea73 100644 --- a/chromium/ui/events/blink/web_input_event.h +++ b/chromium/ui/events/blink/web_input_event.h @@ -5,7 +5,6 @@ #ifndef UI_EVENTS_BLINK_WEB_INPUT_EVENT_H_ #define UI_EVENTS_BLINK_WEB_INPUT_EVENT_H_ -#include "base/callback.h" #include "third_party/blink/public/platform/web_gesture_event.h" #include "third_party/blink/public/platform/web_input_event.h" #include "third_party/blink/public/platform/web_keyboard_event.h" @@ -15,42 +14,17 @@ namespace ui { class GestureEvent; class KeyEvent; -class LocatedEvent; class MouseEvent; class MouseWheelEvent; class ScrollEvent; -// Several methods take a |screen_location_callback| which should translate the -// provided coordinates relative to the hosting window, rather than the top -// level platform window. -// -// If a valid event cannot be created, then the returned events will have the -// type UNKNOWN. -// -// TODO(jonross): Ideally this callback would not be needed. The callback should -// be removed once ui::Event::root_location has been deprecated and replaced -// with ui::Event::screen_location (crbug.com/608547) -blink::WebMouseEvent MakeWebMouseEvent( - const MouseEvent& event, - const base::Callback<gfx::PointF(const ui::LocatedEvent& event)>& - screen_location_callback); -blink::WebMouseWheelEvent MakeWebMouseWheelEvent( - const MouseWheelEvent& event, - const base::Callback<gfx::PointF(const ui::LocatedEvent& event)>& - screen_location_callback); -blink::WebMouseWheelEvent MakeWebMouseWheelEvent( - const ScrollEvent& event, - const base::Callback<gfx::PointF(const ui::LocatedEvent& event)>& - screen_location_callback); +// If a valid event cannot be created, the returned event type will be UNKNOWN. +blink::WebMouseEvent MakeWebMouseEvent(const MouseEvent& event); +blink::WebMouseWheelEvent MakeWebMouseWheelEvent(const MouseWheelEvent& event); +blink::WebMouseWheelEvent MakeWebMouseWheelEvent(const ScrollEvent& event); blink::WebKeyboardEvent MakeWebKeyboardEvent(const KeyEvent& event); -blink::WebGestureEvent MakeWebGestureEvent( - const GestureEvent& event, - const base::Callback<gfx::PointF(const ui::LocatedEvent& event)>& - screen_location_callback); -blink::WebGestureEvent MakeWebGestureEvent( - const ScrollEvent& event, - const base::Callback<gfx::PointF(const ui::LocatedEvent& event)>& - screen_location_callback); +blink::WebGestureEvent MakeWebGestureEvent(const GestureEvent& event); +blink::WebGestureEvent MakeWebGestureEvent(const ScrollEvent& event); blink::WebGestureEvent MakeWebGestureEventFlingCancel( const blink::WebMouseWheelEvent& wheel_event); diff --git a/chromium/ui/events/blink/web_input_event_traits.cc b/chromium/ui/events/blink/web_input_event_traits.cc index 4d1cccadb84..ba06edfb546 100644 --- a/chromium/ui/events/blink/web_input_event_traits.cc +++ b/chromium/ui/events/blink/web_input_event_traits.cc @@ -243,18 +243,6 @@ bool WebInputEventTraits::ShouldBlockEventStream(const WebInputEvent& event) { } } -bool WebInputEventTraits::CanCauseScroll( - const blink::WebMouseWheelEvent& event) { -#if defined(USE_AURA) - // Scroll events generated from the mouse wheel when the control key is held - // don't trigger scrolling. Instead, they may cause zooming. - return event.has_precise_scrolling_deltas || - (event.GetModifiers() & blink::WebInputEvent::kControlKey) == 0; -#else - return true; -#endif -} - uint32_t WebInputEventTraits::GetUniqueTouchEventId( const WebInputEvent& event) { if (WebInputEvent::IsTouchEventType(event.GetType())) { diff --git a/chromium/ui/events/blink/web_input_event_traits.h b/chromium/ui/events/blink/web_input_event_traits.h index 15708011280..ecaf878c2b2 100644 --- a/chromium/ui/events/blink/web_input_event_traits.h +++ b/chromium/ui/events/blink/web_input_event_traits.h @@ -10,7 +10,6 @@ namespace blink { class WebGestureEvent; -class WebMouseWheelEvent; } namespace ui { @@ -30,8 +29,6 @@ class WebInputEventTraits { static WebScopedInputEvent Clone(const blink::WebInputEvent& event); static bool ShouldBlockEventStream(const blink::WebInputEvent& event); - static bool CanCauseScroll(const blink::WebMouseWheelEvent& event); - // Return uniqueTouchEventId for WebTouchEvent, otherwise return 0. static uint32_t GetUniqueTouchEventId(const blink::WebInputEvent& event); static LatencyInfo CreateLatencyInfoForWebGestureEvent( diff --git a/chromium/ui/events/blink/web_input_event_unittest.cc b/chromium/ui/events/blink/web_input_event_unittest.cc index ae13e468d74..c304c997ff4 100644 --- a/chromium/ui/events/blink/web_input_event_unittest.cc +++ b/chromium/ui/events/blink/web_input_event_unittest.cc @@ -7,7 +7,6 @@ #include <stddef.h> #include <stdint.h> -#include "base/bind.h" #include "base/macros.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" @@ -27,14 +26,6 @@ namespace ui { -namespace { - -gfx::PointF GetScreenLocationFromEvent(const LocatedEvent& event) { - return event.root_location_f(); -} - -} // namespace - // Checks that MakeWebKeyboardEvent makes a DOM3 spec compliant key event. // crbug.com/127142 TEST(WebInputEventTest, TestMakeWebKeyboardEvent) { @@ -251,8 +242,7 @@ TEST(WebInputEventTest, TestMakeWebMouseEvent) { MouseEvent ui_event(ET_MOUSE_PRESSED, gfx::Point(123, 321), gfx::Point(123, 321), timestamp, EF_LEFT_MOUSE_BUTTON, EF_LEFT_MOUSE_BUTTON); - blink::WebMouseEvent webkit_event = - MakeWebMouseEvent(ui_event, base::Bind(&GetScreenLocationFromEvent)); + blink::WebMouseEvent webkit_event = MakeWebMouseEvent(ui_event); EXPECT_EQ(EventFlagsToWebEventModifiers(ui_event.flags()), webkit_event.GetModifiers()); EXPECT_EQ(timestamp, webkit_event.TimeStamp()); @@ -268,8 +258,7 @@ TEST(WebInputEventTest, TestMakeWebMouseEvent) { MouseEvent ui_event(ET_MOUSE_RELEASED, gfx::Point(123, 321), gfx::Point(123, 321), timestamp, 0, EF_LEFT_MOUSE_BUTTON); - blink::WebMouseEvent webkit_event = - MakeWebMouseEvent(ui_event, base::Bind(&GetScreenLocationFromEvent)); + blink::WebMouseEvent webkit_event = MakeWebMouseEvent(ui_event); EXPECT_EQ(EventFlagsToWebEventModifiers(ui_event.flags()), webkit_event.GetModifiers()); EXPECT_EQ(timestamp, webkit_event.TimeStamp()); @@ -285,8 +274,7 @@ TEST(WebInputEventTest, TestMakeWebMouseEvent) { MouseEvent ui_event(ET_MOUSE_PRESSED, gfx::Point(123, 321), gfx::Point(123, 321), timestamp, EF_MIDDLE_MOUSE_BUTTON, EF_MIDDLE_MOUSE_BUTTON); - blink::WebMouseEvent webkit_event = - MakeWebMouseEvent(ui_event, base::Bind(&GetScreenLocationFromEvent)); + blink::WebMouseEvent webkit_event = MakeWebMouseEvent(ui_event); EXPECT_EQ(EventFlagsToWebEventModifiers(ui_event.flags()), webkit_event.GetModifiers()); EXPECT_EQ(timestamp, webkit_event.TimeStamp()); @@ -302,8 +290,7 @@ TEST(WebInputEventTest, TestMakeWebMouseEvent) { MouseEvent ui_event(ET_MOUSE_RELEASED, gfx::Point(123, 321), gfx::Point(123, 321), timestamp, 0, EF_MIDDLE_MOUSE_BUTTON); - blink::WebMouseEvent webkit_event = - MakeWebMouseEvent(ui_event, base::Bind(&GetScreenLocationFromEvent)); + blink::WebMouseEvent webkit_event = MakeWebMouseEvent(ui_event); EXPECT_EQ(EventFlagsToWebEventModifiers(ui_event.flags()), webkit_event.GetModifiers()); EXPECT_EQ(timestamp, webkit_event.TimeStamp()); @@ -319,8 +306,7 @@ TEST(WebInputEventTest, TestMakeWebMouseEvent) { MouseEvent ui_event(ET_MOUSE_PRESSED, gfx::Point(123, 321), gfx::Point(123, 321), timestamp, EF_RIGHT_MOUSE_BUTTON, EF_RIGHT_MOUSE_BUTTON); - blink::WebMouseEvent webkit_event = - MakeWebMouseEvent(ui_event, base::Bind(&GetScreenLocationFromEvent)); + blink::WebMouseEvent webkit_event = MakeWebMouseEvent(ui_event); EXPECT_EQ(EventFlagsToWebEventModifiers(ui_event.flags()), webkit_event.GetModifiers()); EXPECT_EQ(timestamp, webkit_event.TimeStamp()); @@ -336,8 +322,7 @@ TEST(WebInputEventTest, TestMakeWebMouseEvent) { MouseEvent ui_event(ET_MOUSE_RELEASED, gfx::Point(123, 321), gfx::Point(123, 321), timestamp, 0, EF_RIGHT_MOUSE_BUTTON); - blink::WebMouseEvent webkit_event = - MakeWebMouseEvent(ui_event, base::Bind(&GetScreenLocationFromEvent)); + blink::WebMouseEvent webkit_event = MakeWebMouseEvent(ui_event); EXPECT_EQ(EventFlagsToWebEventModifiers(ui_event.flags()), webkit_event.GetModifiers()); EXPECT_EQ(timestamp, webkit_event.TimeStamp()); @@ -352,8 +337,7 @@ TEST(WebInputEventTest, TestMakeWebMouseEvent) { base::TimeTicks timestamp = EventTimeForNow(); MouseEvent ui_event(ET_MOUSE_MOVED, gfx::Point(123, 321), gfx::Point(123, 321), timestamp, 0, 0); - blink::WebMouseEvent webkit_event = - MakeWebMouseEvent(ui_event, base::Bind(&GetScreenLocationFromEvent)); + blink::WebMouseEvent webkit_event = MakeWebMouseEvent(ui_event); EXPECT_EQ(EventFlagsToWebEventModifiers(ui_event.flags()), webkit_event.GetModifiers()); EXPECT_EQ(timestamp, webkit_event.TimeStamp()); @@ -369,8 +353,7 @@ TEST(WebInputEventTest, TestMakeWebMouseEvent) { MouseEvent ui_event(ET_MOUSE_MOVED, gfx::Point(123, 321), gfx::Point(123, 321), timestamp, EF_LEFT_MOUSE_BUTTON, 0); - blink::WebMouseEvent webkit_event = - MakeWebMouseEvent(ui_event, base::Bind(&GetScreenLocationFromEvent)); + blink::WebMouseEvent webkit_event = MakeWebMouseEvent(ui_event); EXPECT_EQ(EventFlagsToWebEventModifiers(ui_event.flags()), webkit_event.GetModifiers()); EXPECT_EQ(timestamp, webkit_event.TimeStamp()); @@ -386,8 +369,7 @@ TEST(WebInputEventTest, TestMakeWebMouseEvent) { MouseEvent ui_event( ET_MOUSE_PRESSED, gfx::Point(123, 321), gfx::Point(123, 321), timestamp, EF_LEFT_MOUSE_BUTTON | EF_SHIFT_DOWN, EF_LEFT_MOUSE_BUTTON); - blink::WebMouseEvent webkit_event = - MakeWebMouseEvent(ui_event, base::Bind(&GetScreenLocationFromEvent)); + blink::WebMouseEvent webkit_event = MakeWebMouseEvent(ui_event); EXPECT_EQ(EventFlagsToWebEventModifiers(ui_event.flags()), webkit_event.GetModifiers()); EXPECT_EQ(timestamp, webkit_event.TimeStamp()); @@ -403,8 +385,7 @@ TEST(WebInputEventTest, TestMakeWebMouseEvent) { MouseEvent ui_event(ET_MOUSE_PRESSED, gfx::Point(123, 321), gfx::Point(123, 321), timestamp, EF_LEFT_MOUSE_BUTTON, EF_LEFT_MOUSE_BUTTON); - blink::WebMouseEvent webkit_event = - MakeWebMouseEvent(ui_event, base::Bind(&GetScreenLocationFromEvent)); + blink::WebMouseEvent webkit_event = MakeWebMouseEvent(ui_event); EXPECT_EQ(blink::WebPointerProperties::PointerType::kMouse, webkit_event.pointer_type); @@ -431,8 +412,7 @@ TEST(WebInputEventTest, TestMakeWebMouseEvent) { MouseEvent ui_event(ET_MOUSE_PRESSED, gfx::Point(123, 321), gfx::Point(123, 321), timestamp, EF_LEFT_MOUSE_BUTTON, EF_LEFT_MOUSE_BUTTON, pointer_details); - blink::WebMouseEvent webkit_event = - MakeWebMouseEvent(ui_event, base::Bind(&GetScreenLocationFromEvent)); + blink::WebMouseEvent webkit_event = MakeWebMouseEvent(ui_event); EXPECT_EQ(blink::WebPointerProperties::PointerType::kPen, webkit_event.pointer_type); @@ -455,8 +435,7 @@ TEST(WebInputEventTest, TestMakeWebMouseWheelEvent) { -MouseWheelEvent::kWheelDelta * 2), gfx::Point(123, 321), gfx::Point(123, 321), timestamp, 0, 0); - blink::WebMouseWheelEvent webkit_event = MakeWebMouseWheelEvent( - ui_event, base::Bind(&GetScreenLocationFromEvent)); + blink::WebMouseWheelEvent webkit_event = MakeWebMouseWheelEvent(ui_event); EXPECT_EQ(EventFlagsToWebEventModifiers(ui_event.flags()), webkit_event.GetModifiers()); EXPECT_EQ(timestamp, webkit_event.TimeStamp()); @@ -510,8 +489,7 @@ TEST(WebInputEventTest, WheelEvent) { ui::MouseEvent(ui::ET_UNKNOWN, gfx::Point(), gfx::Point(), base::TimeTicks(), 0, 0), kDeltaX, kDeltaY); - blink::WebMouseWheelEvent web_event = - MakeWebMouseWheelEvent(ui_event, base::Bind(&GetScreenLocationFromEvent)); + blink::WebMouseWheelEvent web_event = MakeWebMouseWheelEvent(ui_event); ASSERT_EQ(blink::WebInputEvent::kMouseWheel, web_event.GetType()); ASSERT_EQ(0, web_event.GetModifiers()); ASSERT_EQ(kDeltaX, web_event.delta_x); @@ -543,8 +521,7 @@ TEST(WebInputEventTest, MousePointerEvent) { ui::MouseEvent ui_event(tests[i].ui_type, tests[i].location, tests[i].screen_location, base::TimeTicks(), tests[i].ui_modifiers, 0); - blink::WebMouseEvent web_event = - MakeWebMouseEvent(ui_event, base::Bind(&GetScreenLocationFromEvent)); + blink::WebMouseEvent web_event = MakeWebMouseEvent(ui_event); ASSERT_TRUE(blink::WebInputEvent::IsMouseEventType(web_event.GetType())); ASSERT_EQ(tests[i].web_type, web_event.GetType()); ASSERT_EQ(tests[i].web_modifiers, web_event.GetModifiers()); diff --git a/chromium/ui/events/cocoa/events_mac.mm b/chromium/ui/events/cocoa/events_mac.mm index c60c04c7661..17d539bb102 100644 --- a/chromium/ui/events/cocoa/events_mac.mm +++ b/chromium/ui/events/cocoa/events_mac.mm @@ -63,6 +63,7 @@ EventType EventTypeFromNative(const PlatformEvent& native_event) { case NSEventTypeRotate: case NSEventTypeBeginGesture: case NSEventTypeEndGesture: + case NSEventTypePressure: break; default: NOTIMPLEMENTED() << type; diff --git a/chromium/ui/events/devices/device_util_linux.cc b/chromium/ui/events/devices/device_util_linux.cc index 4fb5a278789..d81d3a813d9 100644 --- a/chromium/ui/events/devices/device_util_linux.cc +++ b/chromium/ui/events/devices/device_util_linux.cc @@ -6,7 +6,6 @@ #include <string> -#include "base/files/file_enumerator.h" #include "base/files/file_path.h" #include "base/files/file_util.h" #include "base/strings/string_util.h" @@ -38,8 +37,9 @@ InputDeviceType GetInputDeviceTypeFromPath(const base::FilePath& path) { path = path.DirName()) { // Bluetooth LE devices are virtual "uhid" devices. if (path == - base::FilePath(FILE_PATH_LITERAL("/sys/devices/virtual/misc/uhid"))) - return InputDeviceType::INPUT_DEVICE_EXTERNAL; + base::FilePath(FILE_PATH_LITERAL("/sys/devices/virtual/misc/uhid"))) { + return InputDeviceType::INPUT_DEVICE_BLUETOOTH; + } std::string subsystem_path = base::MakeAbsoluteFilePath(path.Append(FILE_PATH_LITERAL("subsystem"))) @@ -61,12 +61,12 @@ InputDeviceType GetInputDeviceTypeFromPath(const base::FilePath& path) { // External bus attachments. if (subsystem_path == "/sys/bus/usb") - return InputDeviceType::INPUT_DEVICE_EXTERNAL; + return InputDeviceType::INPUT_DEVICE_USB; if (subsystem_path == "/sys/class/bluetooth") - return InputDeviceType::INPUT_DEVICE_EXTERNAL; + return InputDeviceType::INPUT_DEVICE_BLUETOOTH; } return InputDeviceType::INPUT_DEVICE_UNKNOWN; } -} // namespace +} // namespace ui diff --git a/chromium/ui/events/devices/input_device.h b/chromium/ui/events/devices/input_device.h index f97154d9f49..2e0da79b1ab 100644 --- a/chromium/ui/events/devices/input_device.h +++ b/chromium/ui/events/devices/input_device.h @@ -14,9 +14,10 @@ namespace ui { enum InputDeviceType { - INPUT_DEVICE_INTERNAL, // Internally connected input device. - INPUT_DEVICE_EXTERNAL, // Known externally connected input device. - INPUT_DEVICE_UNKNOWN, // Device that may or may not be an external device. + INPUT_DEVICE_INTERNAL, // Internally connected input device. + INPUT_DEVICE_USB, // Known externally connected usb input device. + INPUT_DEVICE_BLUETOOTH, // Known externally connected bluetooth input device. + INPUT_DEVICE_UNKNOWN, // Device that may or may not be an external device. }; // Represents an input device state. diff --git a/chromium/ui/events/devices/mojo/input_device_struct_traits.cc b/chromium/ui/events/devices/mojo/input_device_struct_traits.cc index b7e005c1305..edae74ac0df 100644 --- a/chromium/ui/events/devices/mojo/input_device_struct_traits.cc +++ b/chromium/ui/events/devices/mojo/input_device_struct_traits.cc @@ -15,8 +15,10 @@ EnumTraits<ui::mojom::InputDeviceType, ui::InputDeviceType>::ToMojom( switch (type) { case ui::INPUT_DEVICE_INTERNAL: return ui::mojom::InputDeviceType::INPUT_DEVICE_INTERNAL; - case ui::INPUT_DEVICE_EXTERNAL: - return ui::mojom::InputDeviceType::INPUT_DEVICE_EXTERNAL; + case ui::INPUT_DEVICE_USB: + return ui::mojom::InputDeviceType::INPUT_DEVICE_USB; + case ui::INPUT_DEVICE_BLUETOOTH: + return ui::mojom::InputDeviceType::INPUT_DEVICE_BLUETOOTH; case ui::INPUT_DEVICE_UNKNOWN: return ui::mojom::InputDeviceType::INPUT_DEVICE_UNKNOWN; } @@ -31,8 +33,11 @@ bool EnumTraits<ui::mojom::InputDeviceType, ui::InputDeviceType>::FromMojom( case ui::mojom::InputDeviceType::INPUT_DEVICE_INTERNAL: *output = ui::INPUT_DEVICE_INTERNAL; break; - case ui::mojom::InputDeviceType::INPUT_DEVICE_EXTERNAL: - *output = ui::INPUT_DEVICE_EXTERNAL; + case ui::mojom::InputDeviceType::INPUT_DEVICE_USB: + *output = ui::INPUT_DEVICE_USB; + break; + case ui::mojom::InputDeviceType::INPUT_DEVICE_BLUETOOTH: + *output = ui::INPUT_DEVICE_BLUETOOTH; break; case ui::mojom::InputDeviceType::INPUT_DEVICE_UNKNOWN: *output = ui::INPUT_DEVICE_UNKNOWN; diff --git a/chromium/ui/events/devices/mojo/input_devices.mojom b/chromium/ui/events/devices/mojo/input_devices.mojom index c9f60f62b22..2671cec05a1 100644 --- a/chromium/ui/events/devices/mojo/input_devices.mojom +++ b/chromium/ui/events/devices/mojo/input_devices.mojom @@ -9,7 +9,8 @@ import "ui/gfx/geometry/mojo/geometry.mojom"; // Corresponds to ui::InputDeviceType enum InputDeviceType { INPUT_DEVICE_INTERNAL, - INPUT_DEVICE_EXTERNAL, + INPUT_DEVICE_USB, + INPUT_DEVICE_BLUETOOTH, INPUT_DEVICE_UNKNOWN, }; diff --git a/chromium/ui/events/devices/x11/device_data_manager_x11.cc b/chromium/ui/events/devices/x11/device_data_manager_x11.cc index ec5a0acedc1..19648ce1bac 100644 --- a/chromium/ui/events/devices/x11/device_data_manager_x11.cc +++ b/chromium/ui/events/devices/x11/device_data_manager_x11.cc @@ -832,8 +832,7 @@ void DeviceDataManagerX11::DisableDevice(int deviceid) { blocked_devices_.set(deviceid, true); // TODO(rsadam@): Support blocking touchscreen devices. std::vector<InputDevice> keyboards = GetKeyboardDevices(); - std::vector<InputDevice>::iterator it = - FindDeviceWithId(keyboards.begin(), keyboards.end(), deviceid); + auto it = FindDeviceWithId(keyboards.begin(), keyboards.end(), deviceid); if (it != std::end(keyboards)) { blocked_keyboard_devices_.insert( std::pair<int, InputDevice>(deviceid, *it)); @@ -844,8 +843,7 @@ void DeviceDataManagerX11::DisableDevice(int deviceid) { void DeviceDataManagerX11::EnableDevice(int deviceid) { blocked_devices_.set(deviceid, false); - std::map<int, InputDevice>::iterator it = - blocked_keyboard_devices_.find(deviceid); + auto it = blocked_keyboard_devices_.find(deviceid); if (it != blocked_keyboard_devices_.end()) { std::vector<InputDevice> devices = GetKeyboardDevices(); // Add device to current list of active devices. @@ -879,13 +877,11 @@ bool DeviceDataManagerX11::IsEventBlocked(const XEvent& xev) { void DeviceDataManagerX11::OnKeyboardDevicesUpdated( const std::vector<InputDevice>& devices) { std::vector<InputDevice> keyboards(devices); - for (std::map<int, InputDevice>::iterator blocked_iter = - blocked_keyboard_devices_.begin(); + for (auto blocked_iter = blocked_keyboard_devices_.begin(); blocked_iter != blocked_keyboard_devices_.end();) { // Check if the blocked device still exists in list of devices. int device_id = blocked_iter->first; - std::vector<InputDevice>::iterator it = - FindDeviceWithId(keyboards.begin(), keyboards.end(), device_id); + auto it = FindDeviceWithId(keyboards.begin(), keyboards.end(), device_id); // If the device no longer exists, unblock it, else filter it out from our // active list. if (it == keyboards.end()) { diff --git a/chromium/ui/events/devices/x11/touch_factory_x11.cc b/chromium/ui/events/devices/x11/touch_factory_x11.cc index d7c032720df..40c9e65b7d2 100644 --- a/chromium/ui/events/devices/x11/touch_factory_x11.cc +++ b/chromium/ui/events/devices/x11/touch_factory_x11.cc @@ -312,8 +312,7 @@ void TouchFactory::SetTouchDeviceForTest( const std::vector<int>& devices) { touch_device_lookup_.reset(); touch_device_list_.clear(); - for (std::vector<int>::const_iterator iter = devices.begin(); - iter != devices.end(); ++iter) { + for (auto iter = devices.begin(); iter != devices.end(); ++iter) { DCHECK(IsValidDevice(*iter)); touch_device_lookup_[*iter] = true; touch_device_list_[*iter] = {true, EventPointerType::POINTER_TYPE_TOUCH}; @@ -324,8 +323,7 @@ void TouchFactory::SetTouchDeviceForTest( void TouchFactory::SetPointerDeviceForTest( const std::vector<int>& devices) { pointer_device_lookup_.reset(); - for (std::vector<int>::const_iterator iter = devices.begin(); - iter != devices.end(); ++iter) { + for (auto iter = devices.begin(); iter != devices.end(); ++iter) { pointer_device_lookup_[*iter] = true; } } diff --git a/chromium/ui/events/event.cc b/chromium/ui/events/event.cc index d77961db09a..d2eca3cd64b 100644 --- a/chromium/ui/events/event.cc +++ b/chromium/ui/events/event.cc @@ -180,6 +180,10 @@ const char* Event::GetName() const { return EventTypeName(type_); } +void Event::SetProperties(const Properties& properties) { + properties_ = std::make_unique<Properties>(properties); +} + bool Event::IsMousePointerEvent() const { return IsPointerEvent() && AsPointerEvent()->pointer_details().pointer_type == @@ -363,7 +367,34 @@ Event::Event(const Event& copy) target_(NULL), phase_(EP_PREDISPATCH), result_(ER_UNHANDLED), - source_device_id_(copy.source_device_id_) { + source_device_id_(copy.source_device_id_), + properties_(copy.properties_ + ? std::make_unique<Properties>(*copy.properties_) + : nullptr) {} + +Event& Event::operator=(const Event& rhs) { + if (this != &rhs) { + if (delete_native_event_) + ReleaseCopiedNativeEvent(native_event_); + + type_ = rhs.type_; + time_stamp_ = rhs.time_stamp_; + latency_ = rhs.latency_; + flags_ = rhs.flags_; + native_event_ = CopyNativeEvent(rhs.native_event_); + delete_native_event_ = true; + cancelable_ = rhs.cancelable_; + target_ = rhs.target_; + phase_ = rhs.phase_; + result_ = rhs.result_; + source_device_id_ = rhs.source_device_id_; + if (rhs.properties_) + properties_ = std::make_unique<Properties>(*rhs.properties_); + else + properties_.reset(); + } + latency_.set_source_event_type(ui::SourceEventType::OTHER); + return *this; } void Event::SetType(EventType type) { @@ -795,8 +826,7 @@ TouchEvent::TouchEvent(EventType type, const gfx::Point& location, base::TimeTicks time_stamp, const PointerDetails& pointer_details, - int flags, - float angle) + int flags) : LocatedEvent(type, gfx::PointF(location), gfx::PointF(location), @@ -808,7 +838,6 @@ TouchEvent::TouchEvent(EventType type, hovering_(false), pointer_details_(pointer_details) { latency()->AddLatencyNumber(INPUT_EVENT_LATENCY_UI_COMPONENT); - pointer_details_.twist = angle; } TouchEvent::TouchEvent(const TouchEvent& copy) @@ -1103,7 +1132,7 @@ KeyEvent::KeyEvent(const PlatformEvent& native_event, int event_flags) #if defined(OS_WIN) // Only Windows has native character events. if (is_char_) { - key_ = DomKey::FromCharacter(native_event.wParam); + key_ = DomKey::FromCharacter(static_cast<int32_t>(native_event.wParam)); set_flags(PlatformKeyMap::ReplaceControlAndAltWithAltGraph(flags())); } else { int adjusted_flags = flags(); @@ -1161,10 +1190,7 @@ KeyEvent::KeyEvent(const KeyEvent& rhs) key_code_(rhs.key_code_), code_(rhs.code_), is_char_(rhs.is_char_), - key_(rhs.key_), - properties_(rhs.properties_ - ? std::make_unique<Properties>(*rhs.properties_) - : nullptr) {} + key_(rhs.key_) {} KeyEvent& KeyEvent::operator=(const KeyEvent& rhs) { if (this != &rhs) { @@ -1173,12 +1199,7 @@ KeyEvent& KeyEvent::operator=(const KeyEvent& rhs) { code_ = rhs.code_; key_ = rhs.key_; is_char_ = rhs.is_char_; - if (rhs.properties_) - properties_ = std::make_unique<Properties>(*rhs.properties_); - else - properties_.reset(); } - latency()->set_source_event_type(ui::SourceEventType::OTHER); return *this; } @@ -1312,10 +1333,6 @@ void KeyEvent::NormalizeFlags() { set_flags(flags() & ~mask); } -void KeyEvent::SetProperties(const Properties& properties) { - properties_ = std::make_unique<Properties>(properties); -} - KeyboardCode KeyEvent::GetLocatedWindowsKeyboardCode() const { return NonLocatedToLocatedKeyboardCode(key_code_, code_); } diff --git a/chromium/ui/events/event.h b/chromium/ui/events/event.h index 69411630ff0..b9b4c72be8b 100644 --- a/chromium/ui/events/event.h +++ b/chromium/ui/events/event.h @@ -34,6 +34,7 @@ class Transform; namespace ui { class CancelModeEvent; +class Event; class EventTarget; class KeyEvent; class LocatedEvent; @@ -42,15 +43,15 @@ class MouseWheelEvent; class PointerEvent; class ScrollEvent; class TouchEvent; + enum class DomCode; -class Event; -class MouseWheelEvent; -using ScopedEvent = std::unique_ptr<Event>; using PointerId = int32_t; class EVENTS_EXPORT Event { public: + using Properties = base::flat_map<std::string, std::vector<uint8_t>>; + // Copies an arbitrary event. If you have a typed event (e.g. a MouseEvent) // just use its copy constructor. static std::unique_ptr<Event> Clone(const Event& event); @@ -103,6 +104,14 @@ class EVENTS_EXPORT Event { int source_device_id() const { return source_device_id_; } void set_source_device_id(int id) { source_device_id_ = id; } + // Sets the properties associated with this Event. + void SetProperties(const Properties& properties); + + // Returns the properties associated with this event, which may be null. + // The properties are meant to provide a way to associate arbitrary key/value + // pairs with Events and not used by Event. + const Properties* properties() const { return properties_.get(); } + // By default, events are "cancelable", this means any default processing that // the containing abstraction layer may perform can be prevented by calling // SetHandled(). SetHandled() or StopPropagation() must not be called for @@ -314,6 +323,8 @@ class EVENTS_EXPORT Event { Event(EventType type, base::TimeTicks time_stamp, int flags); Event(const PlatformEvent& native_event, EventType type, int flags); Event(const Event& copy); + Event& operator=(const Event& rhs); + void SetType(EventType type); void set_cancelable(bool cancelable) { cancelable_ = cancelable; } @@ -338,6 +349,8 @@ class EVENTS_EXPORT Event { // The device id the event came from, or ED_UNKNOWN_DEVICE if the information // is not available. int source_device_id_; + + std::unique_ptr<Properties> properties_; }; class EVENTS_EXPORT CancelModeEvent : public Event { @@ -542,7 +555,6 @@ class EVENTS_EXPORT MouseEvent : public LocatedEvent { set_flags(flags); } - // Used for synthetic events in testing, gesture recognizer and Ozone // Note: Use the ctor for MouseWheelEvent if type is ET_MOUSEWHEEL. MouseEvent(EventType type, const gfx::Point& location, @@ -704,8 +716,7 @@ class EVENTS_EXPORT TouchEvent : public LocatedEvent { const gfx::Point& location, base::TimeTicks time_stamp, const PointerDetails& pointer_details, - int flags = 0, - float angle = 0.0f); + int flags = 0); TouchEvent(const TouchEvent& copy); @@ -725,6 +736,9 @@ class EVENTS_EXPORT TouchEvent : public LocatedEvent { should_remove_native_touch_id_mapping_ = should_remove_native_touch_id_mapping; } + bool should_remove_native_touch_id_mapping() const { + return should_remove_native_touch_id_mapping_; + } // Overridden from LocatedEvent. void UpdateForRootTransform( @@ -744,6 +758,9 @@ class EVENTS_EXPORT TouchEvent : public LocatedEvent { private: // A unique identifier for the touch event. + // NOTE: this is *not* serialized over mojom, as the number is unique to + // a particular process, and as mojom may go cross process, to serialize could + // lead to conflicts. uint32_t unique_event_id_; // Whether the (unhandled) touch event will produce a scroll event (e.g., a @@ -755,6 +772,7 @@ class EVENTS_EXPORT TouchEvent : public LocatedEvent { // event id and the touch_id_. This should only be the case for // release and cancel events where the associated touch press event // created a mapping between the native id and the touch_id_. + // NOTE: this is not serialized, as it's generally unique to the source. bool should_remove_native_touch_id_mapping_; // True for devices like some pens when they support hovering over @@ -832,8 +850,6 @@ class EVENTS_EXPORT PointerEvent : public LocatedEvent { // class EVENTS_EXPORT KeyEvent : public Event { public: - using Properties = base::flat_map<std::string, std::vector<uint8_t>>; - // Create a KeyEvent from a NativeEvent. For Windows this native event can // be either a keystroke message (WM_KEYUP/WM_KEYDOWN) or a character message // (WM_CHAR). Other systems have only keystroke events. @@ -941,14 +957,6 @@ class EVENTS_EXPORT KeyEvent : public Event { // (Native X11 event flags describe the state before the event.) void NormalizeFlags(); - // Sets the properties associated with this KeyEvent. - void SetProperties(const Properties& properties); - - // Returns the properties associated with this event, which may be null. - // The properties are meant to provide a way to associate arbitrary key/value - // pairs with KeyEvents and not used by KeyEvent. - const Properties* properties() const { return properties_.get(); } - protected: friend class KeyEventTestApi; @@ -987,8 +995,6 @@ class EVENTS_EXPORT KeyEvent : public Event { // it may be set only if and when GetCharacter() or GetDomKey() is called. mutable DomKey key_ = DomKey::NONE; - std::unique_ptr<Properties> properties_; - static KeyEvent* last_key_event_; #if defined(USE_X11) static KeyEvent* last_ibus_key_event_; diff --git a/chromium/ui/events/event_handler.cc b/chromium/ui/events/event_handler.cc index 66279d43cda..179420f376b 100644 --- a/chromium/ui/events/event_handler.cc +++ b/chromium/ui/events/event_handler.cc @@ -4,7 +4,6 @@ #include "ui/events/event_handler.h" -#include "base/debug/alias.h" #include "ui/events/event.h" #include "ui/events/event_dispatcher.h" @@ -22,10 +21,6 @@ EventHandler::~EventHandler() { } void EventHandler::OnEvent(Event* event) { - // TODO(sky): remove |event_type|. Temporary while tracking down crash. - // https://crbug.com/867035 - const EventType event_type = event->type(); - base::debug::Alias(&event_type); if (event->IsKeyEvent()) OnKeyEvent(event->AsKeyEvent()); else if (event->IsMouseEvent()) diff --git a/chromium/ui/events/event_rewriter_unittest.cc b/chromium/ui/events/event_rewriter_unittest.cc index ae9abcea417..90c5d261c54 100644 --- a/chromium/ui/events/event_rewriter_unittest.cc +++ b/chromium/ui/events/event_rewriter_unittest.cc @@ -125,8 +125,7 @@ class TestStateMachineEventRewriter : public EventRewriter { EventRewriteStatus RewriteEvent( const Event& event, std::unique_ptr<Event>* rewritten_event) override { - RewriteRules::iterator find = - rules_.find(RewriteCase(state_, event.type())); + auto find = rules_.find(RewriteCase(state_, event.type())); if (find == rules_.end()) return EVENT_REWRITE_CONTINUE; if ((find->second.status == EVENT_REWRITE_REWRITTEN) || diff --git a/chromium/ui/events/event_source.cc b/chromium/ui/events/event_source.cc index 21e160b3130..d2c955ce6fe 100644 --- a/chromium/ui/events/event_source.cc +++ b/chromium/ui/events/event_source.cc @@ -34,8 +34,7 @@ void EventSource::AddEventRewriter(EventRewriter* rewriter) { } void EventSource::RemoveEventRewriter(EventRewriter* rewriter) { - EventRewriterList::iterator find = - std::find(rewriter_list_.begin(), rewriter_list_.end(), rewriter); + auto find = std::find(rewriter_list_.begin(), rewriter_list_.end(), rewriter); if (find != rewriter_list_.end()) rewriter_list_.erase(find); } diff --git a/chromium/ui/events/event_target.cc b/chromium/ui/events/event_target.cc index 431a66a60f9..e72b72cc924 100644 --- a/chromium/ui/events/event_target.cc +++ b/chromium/ui/events/event_target.cc @@ -8,18 +8,25 @@ #include "base/logging.h" #include "ui/events/event.h" +#include "ui/gfx/geometry/point_conversions.h" namespace ui { -EventTarget::EventTarget() - : target_handler_(NULL) { -} +EventTarget::EventTarget() = default; -EventTarget::~EventTarget() { -} +EventTarget::~EventTarget() = default; void EventTarget::ConvertEventToTarget(EventTarget* target, - LocatedEvent* event) { + LocatedEvent* event) {} + +gfx::PointF EventTarget::GetScreenLocationF( + const ui::LocatedEvent& event) const { + NOTREACHED(); + return event.root_location_f(); +} + +gfx::Point EventTarget::GetScreenLocation(const ui::LocatedEvent& event) const { + return gfx::ToFlooredPoint(GetScreenLocationF(event)); } void EventTarget::AddPreTargetHandler(EventHandler* handler, @@ -51,10 +58,8 @@ void EventTarget::AddPostTargetHandler(EventHandler* handler) { } void EventTarget::RemovePostTargetHandler(EventHandler* handler) { - EventHandlerList::iterator find = - std::find(post_target_list_.begin(), - post_target_list_.end(), - handler); + auto find = + std::find(post_target_list_.begin(), post_target_list_.end(), handler); if (find != post_target_list_.end()) post_target_list_.erase(find); } @@ -92,8 +97,9 @@ void EventTarget::GetPreTargetHandlers(EventHandlerList* list) { void EventTarget::GetPostTargetHandlers(EventHandlerList* list) { EventTarget* target = this; while (target) { - for (EventHandlerList::iterator it = target->post_target_list_.begin(), - end = target->post_target_list_.end(); it != end; ++it) { + for (auto it = target->post_target_list_.begin(), + end = target->post_target_list_.end(); + it != end; ++it) { list->push_back(*it); } target = target->GetParentTarget(); diff --git a/chromium/ui/events/event_target.h b/chromium/ui/events/event_target.h index 4314aca5f6c..7c47527c214 100644 --- a/chromium/ui/events/event_target.h +++ b/chromium/ui/events/event_target.h @@ -12,6 +12,8 @@ #include "base/macros.h" #include "ui/events/event_handler.h" #include "ui/events/events_export.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/point_f.h" namespace ui { @@ -54,6 +56,10 @@ class EVENTS_EXPORT EventTarget { virtual void ConvertEventToTarget(EventTarget* target, LocatedEvent* event); + // Get |event|'s screen location, using the EventTarget's screen location. + virtual gfx::PointF GetScreenLocationF(const LocatedEvent& event) const; + gfx::Point GetScreenLocation(const LocatedEvent& event) const; + // Priority levels for PreTargetHandlers. enum class Priority { // The Accessibility level is the highest, and gets events before @@ -120,7 +126,7 @@ class EVENTS_EXPORT EventTarget { EventHandlerPriorityList pre_target_list_; EventHandlerList post_target_list_; - EventHandler* target_handler_; + EventHandler* target_handler_ = nullptr; DISALLOW_COPY_AND_ASSIGN(EventTarget); }; diff --git a/chromium/ui/events/event_unittest.cc b/chromium/ui/events/event_unittest.cc index 8a63b46a092..4742a797f60 100644 --- a/chromium/ui/events/event_unittest.cc +++ b/chromium/ui/events/event_unittest.cc @@ -25,6 +25,7 @@ #if defined(USE_X11) #include "ui/events/test/events_test_utils_x11.h" +#include "ui/events/x/events_x_utils.h" // nogncheck #include "ui/gfx/x/x11.h" // nogncheck #include "ui/gfx/x/x11_types.h" // nogncheck #endif @@ -430,6 +431,15 @@ TEST(EventTest, KeyEventCode) { #if defined(USE_X11) namespace { +class MockTimestampServer : public ui::TimestampServer { + public: + Time GetCurrentServerTime() override { return base_time_; } + void SetBaseTime(Time time) { base_time_ = time; } + + private: + Time base_time_ = 0; +}; + void SetKeyEventTimestamp(XEvent* event, int64_t time) { event->xkey.time = time & UINT32_MAX; } @@ -440,7 +450,29 @@ void AdvanceKeyEventTimestamp(XEvent* event) { } // namespace -TEST(EventTest, AutoRepeat) { +class X11EventTest : public testing::Test { + public: + X11EventTest() {} + ~X11EventTest() override {} + + void SetUp() override { + SetTimestampServer(&server_); + SetUseFixedTimeForXEventTesting(true); + } + + void TearDown() override { + SetTimestampServer(nullptr); + SetUseFixedTimeForXEventTesting(false); + } + + protected: + MockTimestampServer server_; + + private: + DISALLOW_COPY_AND_ASSIGN(X11EventTest); +}; + +TEST_F(X11EventTest, AutoRepeat) { const uint16_t kNativeCodeA = ui::KeycodeConverter::DomCodeToNativeKeycode(DomCode::US_A); const uint16_t kNativeCodeB = @@ -468,6 +500,7 @@ TEST(EventTest, AutoRepeat) { int64_t ticks_base = (base::TimeTicks::Now() - base::TimeTicks()).InMilliseconds() - 5000; + server_.SetBaseTime(static_cast<Time>(ticks_base)); SetKeyEventTimestamp(native_event_a_pressed, ticks_base); SetKeyEventTimestamp(native_event_a_pressed_1500, ticks_base + 1500); SetKeyEventTimestamp(native_event_a_pressed_3000, ticks_base + 3000); @@ -582,8 +615,8 @@ TEST(EventTest, TouchEventRotationAngleFixing) { TouchEvent event(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), time, PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, /* pointer_id*/ 0, radius_x, radius_y, - /* force */ 0), - 0, angle_in_range); + /* force */ 0, angle_in_range), + 0); EXPECT_FLOAT_EQ(angle_in_range, event.ComputeRotationAngle()); } @@ -592,8 +625,8 @@ TEST(EventTest, TouchEventRotationAngleFixing) { TouchEvent event(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), time, PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, /* pointer_id*/ 0, radius_x, radius_y, - /* force */ 0), - 0, angle_in_range); + /* force */ 0, angle_in_range), + 0); EXPECT_FLOAT_EQ(angle_in_range, event.ComputeRotationAngle()); } @@ -602,8 +635,8 @@ TEST(EventTest, TouchEventRotationAngleFixing) { TouchEvent event(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), time, PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, /* pointer_id*/ 0, radius_x, radius_y, - /* force */ 0), - 0, angle_negative); + /* force */ 0, angle_negative), + 0); EXPECT_FLOAT_EQ(180 - 0.1f, event.ComputeRotationAngle()); } @@ -612,8 +645,8 @@ TEST(EventTest, TouchEventRotationAngleFixing) { TouchEvent event(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), time, PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, /* pointer_id*/ 0, radius_x, radius_y, - /* force */ 0), - 0, angle_negative); + /* force */ 0, angle_negative), + 0); EXPECT_FLOAT_EQ(360 - 200, event.ComputeRotationAngle()); } @@ -622,8 +655,8 @@ TEST(EventTest, TouchEventRotationAngleFixing) { TouchEvent event(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), time, PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, /* pointer_id*/ 0, radius_x, radius_y, - /* force */ 0), - 0, angle_too_big); + /* force */ 0, angle_too_big), + 0); EXPECT_FLOAT_EQ(0, event.ComputeRotationAngle()); } @@ -632,8 +665,8 @@ TEST(EventTest, TouchEventRotationAngleFixing) { TouchEvent event(ui::ET_TOUCH_PRESSED, gfx::Point(0, 0), time, PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, /* pointer_id*/ 0, radius_x, radius_y, - /* force */ 0), - 0, angle_too_big); + /* force */ 0, angle_too_big), + 0); EXPECT_FLOAT_EQ(400 - 360, event.ComputeRotationAngle()); } } @@ -999,8 +1032,9 @@ TEST(EventTest, PointerEventToTouchEventDetails) { /* pointer_id*/ 15, /* radius_x */ 11.5, /* radius_y */ 13.5, - /* force */ 0.5), - 0, 13.0)); + /* force */ 0.0, + /* twist */ 13.0), + 0)); ui::TouchEvent touch_event(pointer_event); EXPECT_EQ(pointer_event.location(), touch_event.location()); @@ -1122,6 +1156,20 @@ TEST(EventTest, UpdateForRootTransformation) { } } +TEST(EventTest, OperatorEqual) { + MouseEvent m1(ET_MOUSE_PRESSED, gfx::Point(1, 2), gfx::Point(2, 3), + EventTimeForNow(), EF_LEFT_MOUSE_BUTTON, EF_RIGHT_MOUSE_BUTTON); + base::flat_map<std::string, std::vector<uint8_t>> properties; + properties["a"] = {1u}; + m1.SetProperties(properties); + EXPECT_EQ(properties, *(m1.properties())); + MouseEvent m2(ET_MOUSE_RELEASED, gfx::Point(11, 21), gfx::Point(2, 2), + EventTimeForNow(), EF_RIGHT_MOUSE_BUTTON, EF_LEFT_MOUSE_BUTTON); + m2 = m1; + ASSERT_TRUE(m2.properties()); + EXPECT_EQ(properties, *(m2.properties())); +} + #if defined(OS_WIN) namespace { diff --git a/chromium/ui/events/event_utils.cc b/chromium/ui/events/event_utils.cc index 404b4525b7c..164fd2285ce 100644 --- a/chromium/ui/events/event_utils.cc +++ b/chromium/ui/events/event_utils.cc @@ -7,6 +7,7 @@ #include <vector> #include "base/metrics/histogram_macros.h" +#include "base/numerics/safe_conversions.h" #include "ui/display/display.h" #include "ui/display/screen.h" @@ -88,8 +89,7 @@ display::Display::TouchSupport GetInternalDisplayTouchSupport() { if (!screen) return display::Display::TouchSupport::UNKNOWN; const std::vector<display::Display>& displays = screen->GetAllDisplays(); - for (std::vector<display::Display>::const_iterator it = displays.begin(); - it != displays.end(); ++it) { + for (auto it = displays.begin(); it != displays.end(); ++it) { if (it->IsInternal()) return it->touch_support(); } @@ -108,20 +108,24 @@ void ComputeEventLatencyOS(const PlatformEvent& native_event) { case ET_SCROLL: #endif case ET_MOUSEWHEEL: - UMA_HISTOGRAM_CUSTOM_COUNTS("Event.Latency.OS.MOUSE_WHEEL", - delta.InMicroseconds(), 1, 1000000, 50); + UMA_HISTOGRAM_CUSTOM_COUNTS( + "Event.Latency.OS.MOUSE_WHEEL", + base::saturated_cast<int>(delta.InMicroseconds()), 1, 1000000, 50); return; case ET_TOUCH_MOVED: - UMA_HISTOGRAM_CUSTOM_COUNTS("Event.Latency.OS.TOUCH_MOVED", - delta.InMicroseconds(), 1, 1000000, 50); + UMA_HISTOGRAM_CUSTOM_COUNTS( + "Event.Latency.OS.TOUCH_MOVED", + base::saturated_cast<int>(delta.InMicroseconds()), 1, 1000000, 50); return; case ET_TOUCH_PRESSED: - UMA_HISTOGRAM_CUSTOM_COUNTS("Event.Latency.OS.TOUCH_PRESSED", - delta.InMicroseconds(), 1, 1000000, 50); + UMA_HISTOGRAM_CUSTOM_COUNTS( + "Event.Latency.OS.TOUCH_PRESSED", + base::saturated_cast<int>(delta.InMicroseconds()), 1, 1000000, 50); return; case ET_TOUCH_RELEASED: - UMA_HISTOGRAM_CUSTOM_COUNTS("Event.Latency.OS.TOUCH_RELEASED", - delta.InMicroseconds(), 1, 1000000, 50); + UMA_HISTOGRAM_CUSTOM_COUNTS( + "Event.Latency.OS.TOUCH_RELEASED", + base::saturated_cast<int>(delta.InMicroseconds()), 1, 1000000, 50); return; default: return; diff --git a/chromium/ui/events/fuchsia/input_event_dispatcher.cc b/chromium/ui/events/fuchsia/input_event_dispatcher.cc new file mode 100644 index 00000000000..b240779e533 --- /dev/null +++ b/chromium/ui/events/fuchsia/input_event_dispatcher.cc @@ -0,0 +1,194 @@ +// 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/events/fuchsia/input_event_dispatcher.h" + +#include <memory> + +#include "base/memory/ptr_util.h" +#include "ui/events/event.h" +#include "ui/events/fuchsia/input_event_dispatcher_delegate.h" +#include "ui/events/keycodes/dom/keycode_converter.h" +#include "ui/events/keycodes/keyboard_code_conversion.h" + +namespace ui { +namespace { + +const uint32_t kUsbHidKeyboardPage = 0x07; + +int KeyModifiersToFlags(int modifiers) { + int flags = 0; + if (modifiers & fuchsia::ui::input::kModifierShift) + flags |= EF_SHIFT_DOWN; + if (modifiers & fuchsia::ui::input::kModifierControl) + flags |= EF_CONTROL_DOWN; + if (modifiers & fuchsia::ui::input::kModifierAlt) + flags |= EF_ALT_DOWN; + // TODO(crbug.com/850697): Add AltGraph support. + return flags; +} + +} // namespace + +InputEventDispatcher::InputEventDispatcher( + InputEventDispatcherDelegate* delegate) + : delegate_(delegate) { + DCHECK(delegate_); +} + +InputEventDispatcher::~InputEventDispatcher() = default; + +bool InputEventDispatcher::ProcessEvent( + const fuchsia::ui::input::InputEvent& event) const { + switch (event.Which()) { + case fuchsia::ui::input::InputEvent::Tag::kPointer: + switch (event.pointer().type) { + case fuchsia::ui::input::PointerEventType::MOUSE: + return ProcessMouseEvent(event.pointer()); + case fuchsia::ui::input::PointerEventType::TOUCH: + return ProcessTouchEvent(event.pointer()); + case fuchsia::ui::input::PointerEventType::STYLUS: + case fuchsia::ui::input::PointerEventType::INVERTED_STYLUS: + NOTIMPLEMENTED() << "Stylus input is not yet supported."; + return false; + } + + case fuchsia::ui::input::InputEvent::Tag::kKeyboard: + return ProcessKeyboardEvent(event.keyboard()); + + case fuchsia::ui::input::InputEvent::Tag::kFocus: + case fuchsia::ui::input::InputEvent::Tag::Invalid: + return false; + } +} + +bool InputEventDispatcher::ProcessMouseEvent( + const fuchsia::ui::input::PointerEvent& event) const { + int flags = 0; + EventType event_type; + int buttons_flags = 0; + if (event.buttons & fuchsia::ui::input::kMouseButtonPrimary) { + buttons_flags |= EF_LEFT_MOUSE_BUTTON; + } + if (event.buttons & fuchsia::ui::input::kMouseButtonSecondary) { + buttons_flags |= EF_RIGHT_MOUSE_BUTTON; + } + if (event.buttons & fuchsia::ui::input::kMouseButtonTertiary) { + buttons_flags |= EF_MIDDLE_MOUSE_BUTTON; + } + + switch (event.phase) { + case fuchsia::ui::input::PointerEventPhase::DOWN: + event_type = ET_MOUSE_PRESSED; + break; + case fuchsia::ui::input::PointerEventPhase::MOVE: + event_type = flags ? ET_MOUSE_DRAGGED : ET_MOUSE_MOVED; + break; + case fuchsia::ui::input::PointerEventPhase::UP: + event_type = ET_MOUSE_RELEASED; + break; + + // Following phases are not expected for mouse events. + case fuchsia::ui::input::PointerEventPhase::HOVER: + case fuchsia::ui::input::PointerEventPhase::CANCEL: + case fuchsia::ui::input::PointerEventPhase::ADD: + case fuchsia::ui::input::PointerEventPhase::REMOVE: + NOTREACHED() << "Unexpected mouse phase " + << fidl::ToUnderlying(event.phase); + return false; + } + + ui::MouseEvent mouse_event(event_type, gfx::Point(), gfx::Point(), + base::TimeTicks::FromZxTime(event.event_time), + buttons_flags, buttons_flags); + mouse_event.set_location_f(gfx::PointF(event.x, event.y)); + delegate_->DispatchEvent(&mouse_event); + return mouse_event.handled(); +} + +bool InputEventDispatcher::ProcessTouchEvent( + const fuchsia::ui::input::PointerEvent& event) const { + EventType event_type; + bool hovering = false; + switch (event.phase) { + case fuchsia::ui::input::PointerEventPhase::HOVER: + hovering = true; + event_type = ET_TOUCH_PRESSED; + break; + case fuchsia::ui::input::PointerEventPhase::DOWN: + event_type = ET_TOUCH_PRESSED; + break; + case fuchsia::ui::input::PointerEventPhase::MOVE: + event_type = ET_TOUCH_MOVED; + break; + case fuchsia::ui::input::PointerEventPhase::CANCEL: + event_type = ET_TOUCH_CANCELLED; + break; + case fuchsia::ui::input::PointerEventPhase::UP: + event_type = ET_TOUCH_RELEASED; + break; + case fuchsia::ui::input::PointerEventPhase::ADD: + case fuchsia::ui::input::PointerEventPhase::REMOVE: + return false; + } + + // TODO(crbug.com/876933): Add more detailed fields such as + // force/orientation/tilt once they are added to PointerEvent. + ui::PointerDetails pointer_details(ui::EventPointerType::POINTER_TYPE_TOUCH, + event.pointer_id); + + ui::TouchEvent touch_event(event_type, gfx::Point(), + base::TimeTicks::FromZxTime(event.event_time), + pointer_details); + touch_event.set_hovering(hovering); + touch_event.set_location_f(gfx::PointF(event.x, event.y)); + delegate_->DispatchEvent(&touch_event); + return touch_event.handled(); +} + +bool InputEventDispatcher::ProcessKeyboardEvent( + const fuchsia::ui::input::KeyboardEvent& event) const { + EventType event_type; + + switch (event.phase) { + case fuchsia::ui::input::KeyboardEventPhase::PRESSED: + case fuchsia::ui::input::KeyboardEventPhase::REPEAT: + event_type = ET_KEY_PRESSED; + break; + + case fuchsia::ui::input::KeyboardEventPhase::RELEASED: + event_type = ET_KEY_RELEASED; + break; + + case fuchsia::ui::input::KeyboardEventPhase::CANCELLED: + NOTIMPLEMENTED() << "Key event cancellation is not supported."; + event_type = ET_KEY_RELEASED; + break; + } + + // Currently KeyboardEvent doesn't specify HID Usage page. |hid_usage| + // field always contains values from the Keyboard page. See + // https://fuchsia.atlassian.net/browse/SCN-762 . + DomCode dom_code = KeycodeConverter::UsbKeycodeToDomCode( + (kUsbHidKeyboardPage << 16) | event.hid_usage); + DomKey dom_key; + KeyboardCode key_code; + if (!DomCodeToUsLayoutDomKey(dom_code, KeyModifiersToFlags(event.modifiers), + &dom_key, &key_code)) { + LOG(ERROR) << "DomCodeToUsLayoutDomKey() failed for usb_key: " + << event.hid_usage; + key_code = VKEY_UNKNOWN; + } + + if (event.code_point) + dom_key = DomKey::FromCharacter(event.code_point); + + ui::KeyEvent key_event(event_type, key_code, dom_code, + KeyModifiersToFlags(event.modifiers), dom_key, + base::TimeTicks::FromZxTime(event.event_time)); + delegate_->DispatchEvent(&key_event); + return key_event.handled(); +} + +} // namespace ui diff --git a/chromium/ui/events/fuchsia/input_event_dispatcher.h b/chromium/ui/events/fuchsia/input_event_dispatcher.h new file mode 100644 index 00000000000..6307773edd8 --- /dev/null +++ b/chromium/ui/events/fuchsia/input_event_dispatcher.h @@ -0,0 +1,44 @@ +// 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_EVENTS_FUCHSIA_INPUT_EVENT_DISPATCHER_H_ +#define UI_EVENTS_FUCHSIA_INPUT_EVENT_DISPATCHER_H_ + +#include <fuchsia/ui/input/cpp/fidl.h> + +#include "base/macros.h" +#include "ui/events/events_export.h" + +namespace ui { + +class InputEventDispatcherDelegate; + +// Translates Fuchsia input events to Chrome ui::Events. +class EVENTS_EXPORT InputEventDispatcher { + public: + // |delegate|: The recipient of any Chrome events that are processed from + // Fuchsia events. + explicit InputEventDispatcher(InputEventDispatcherDelegate* delegate); + ~InputEventDispatcher(); + + // Processes a Fuchsia |event| and dispatches Chrome ui::Events from it. + // |event|'s coordinates must be specified in device-independent pixels. + // + // Returns true if the event was processed. + bool ProcessEvent(const fuchsia::ui::input::InputEvent& event) const; + + private: + bool ProcessMouseEvent(const fuchsia::ui::input::PointerEvent& event) const; + bool ProcessTouchEvent(const fuchsia::ui::input::PointerEvent& event) const; + bool ProcessKeyboardEvent( + const fuchsia::ui::input::KeyboardEvent& event) const; + + InputEventDispatcherDelegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(InputEventDispatcher); +}; + +} // namespace ui + +#endif // UI_EVENTS_FUCHSIA_INPUT_EVENT_DISPATCHER_H_ diff --git a/chromium/ui/events/fuchsia/input_event_dispatcher_delegate.h b/chromium/ui/events/fuchsia/input_event_dispatcher_delegate.h new file mode 100644 index 00000000000..78a70a8b327 --- /dev/null +++ b/chromium/ui/events/fuchsia/input_event_dispatcher_delegate.h @@ -0,0 +1,22 @@ +// 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_EVENTS_FUCHSIA_INPUT_EVENT_DISPATCHER_DELEGATE_H_ +#define UI_EVENTS_FUCHSIA_INPUT_EVENT_DISPATCHER_DELEGATE_H_ + +#include "ui/events/events_export.h" + +namespace ui { + +class Event; + +// Translates Fuchsia input events to Chrome ui::Events. +class EVENTS_EXPORT InputEventDispatcherDelegate { + public: + virtual void DispatchEvent(ui::Event* event) = 0; +}; + +} // namespace ui + +#endif // UI_EVENTS_FUCHSIA_INPUT_EVENT_DISPATCHER_DELEGATE_H_ diff --git a/chromium/ui/events/fuchsia/input_event_dispatcher_unittest.cc b/chromium/ui/events/fuchsia/input_event_dispatcher_unittest.cc new file mode 100644 index 00000000000..54a14b9909b --- /dev/null +++ b/chromium/ui/events/fuchsia/input_event_dispatcher_unittest.cc @@ -0,0 +1,263 @@ +// 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/events/fuchsia/input_event_dispatcher.h" + +#include <fuchsia/ui/input/cpp/fidl.h> +#include <memory> + +#include "base/macros.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/event.h" +#include "ui/events/fuchsia/input_event_dispatcher_delegate.h" +#include "ui/events/keycodes/dom/dom_key.h" + +using fuchsia::ui::input::InputEvent; +using FuchsiaKeyboardEvent = fuchsia::ui::input::KeyboardEvent; +using FuchsiaPointerEvent = fuchsia::ui::input::PointerEvent; + +namespace ui { +namespace { + +class InputEventDispatcherTest : public testing::Test, + public InputEventDispatcherDelegate { + public: + InputEventDispatcherTest() : dispatcher_(this) {} + ~InputEventDispatcherTest() override = default; + + void DispatchEvent(Event* event) override { + DCHECK(!captured_event_); + captured_event_ = Event::Clone(*event); + } + + protected: + InputEventDispatcher dispatcher_; + std::unique_ptr<ui::Event> captured_event_; + + void ResetCapturedEvent() { + DCHECK(captured_event_); + captured_event_.reset(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(InputEventDispatcherTest); +}; + +TEST_F(InputEventDispatcherTest, MouseEventButtons) { + // Left mouse button. + FuchsiaPointerEvent pointer_event; + pointer_event.x = 1; + pointer_event.y = 1; + pointer_event.phase = fuchsia::ui::input::PointerEventPhase::DOWN; + pointer_event.buttons = fuchsia::ui::input::kMouseButtonPrimary; + pointer_event.type = fuchsia::ui::input::PointerEventType::MOUSE; + InputEvent event; + event.set_pointer(pointer_event); + dispatcher_.ProcessEvent(event); + EXPECT_TRUE(captured_event_->AsMouseEvent()->IsLeftMouseButton()); + ResetCapturedEvent(); + + // Right mouse button. + pointer_event.buttons = fuchsia::ui::input::kMouseButtonSecondary; + event.set_pointer(pointer_event); + dispatcher_.ProcessEvent(event); + EXPECT_TRUE(captured_event_->AsMouseEvent()->IsRightMouseButton()); + ResetCapturedEvent(); + + // Middle mouse button. + pointer_event.buttons = fuchsia::ui::input::kMouseButtonTertiary; + event.set_pointer(pointer_event); + dispatcher_.ProcessEvent(event); + EXPECT_TRUE(captured_event_->AsMouseEvent()->IsMiddleMouseButton()); + ResetCapturedEvent(); + + // Left mouse drag. + pointer_event.buttons = fuchsia::ui::input::kMouseButtonPrimary; + pointer_event.phase = fuchsia::ui::input::PointerEventPhase::MOVE; + event.set_pointer(pointer_event); + dispatcher_.ProcessEvent(event); + EXPECT_EQ(ET_MOUSE_DRAGGED, captured_event_->type()); + ResetCapturedEvent(); + + // Left mouse up. + pointer_event.buttons = fuchsia::ui::input::kMouseButtonPrimary; + pointer_event.phase = fuchsia::ui::input::PointerEventPhase::UP; + event.set_pointer(pointer_event); + dispatcher_.ProcessEvent(event); + EXPECT_EQ(ET_MOUSE_RELEASED, captured_event_->type()); +} + +TEST_F(InputEventDispatcherTest, MouseMove) { + FuchsiaPointerEvent pointer_event; + pointer_event.x = 1.5; + pointer_event.y = 2.5; + pointer_event.phase = fuchsia::ui::input::PointerEventPhase::MOVE; + pointer_event.type = fuchsia::ui::input::PointerEventType::MOUSE; + InputEvent event; + event.set_pointer(pointer_event); + dispatcher_.ProcessEvent(event); + EXPECT_EQ(1.5, captured_event_->AsMouseEvent()->x()); + EXPECT_EQ(2.5, captured_event_->AsMouseEvent()->y()); +} + +TEST_F(InputEventDispatcherTest, TouchLocation) { + FuchsiaPointerEvent pointer_event; + pointer_event.phase = fuchsia::ui::input::PointerEventPhase::DOWN; + pointer_event.type = fuchsia::ui::input::PointerEventType::TOUCH; + pointer_event.x = 1.5; + pointer_event.y = 2.5; + InputEvent event; + event.set_pointer(pointer_event); + dispatcher_.ProcessEvent(event); + EXPECT_EQ(1.5, captured_event_->AsTouchEvent()->x()); + EXPECT_EQ(2.5, captured_event_->AsTouchEvent()->y()); +} + +TEST_F(InputEventDispatcherTest, TouchPointerIds) { + FuchsiaPointerEvent pointer_event; + pointer_event.phase = fuchsia::ui::input::PointerEventPhase::DOWN; + pointer_event.type = fuchsia::ui::input::PointerEventType::TOUCH; + pointer_event.pointer_id = 1; + InputEvent event; + event.set_pointer(pointer_event); + dispatcher_.ProcessEvent(event); + EXPECT_EQ(ET_TOUCH_PRESSED, captured_event_->type()); + EXPECT_EQ(1, captured_event_->AsTouchEvent()->pointer_details().id); + ResetCapturedEvent(); + + pointer_event.pointer_id = 2; + event.set_pointer(pointer_event); + dispatcher_.ProcessEvent(event); + EXPECT_EQ(ET_TOUCH_PRESSED, captured_event_->type()); + EXPECT_EQ(2, captured_event_->AsTouchEvent()->pointer_details().id); +} + +TEST_F(InputEventDispatcherTest, TouchPhases) { + FuchsiaPointerEvent pointer_event; + pointer_event.x = 1; + pointer_event.y = 1; + pointer_event.phase = fuchsia::ui::input::PointerEventPhase::DOWN; + pointer_event.type = fuchsia::ui::input::PointerEventType::TOUCH; + InputEvent event; + event.set_pointer(pointer_event); + dispatcher_.ProcessEvent(event); + EXPECT_EQ(ET_TOUCH_PRESSED, captured_event_->type()); + ResetCapturedEvent(); + + pointer_event.phase = fuchsia::ui::input::PointerEventPhase::HOVER; + event.set_pointer(pointer_event); + dispatcher_.ProcessEvent(event); + EXPECT_EQ(ET_TOUCH_PRESSED, captured_event_->type()); + EXPECT_TRUE(captured_event_->AsTouchEvent()->hovering()); + ResetCapturedEvent(); + + pointer_event.phase = fuchsia::ui::input::PointerEventPhase::MOVE; + event.set_pointer(pointer_event); + dispatcher_.ProcessEvent(event); + EXPECT_EQ(ET_TOUCH_MOVED, captured_event_->type()); + ResetCapturedEvent(); + + pointer_event.phase = fuchsia::ui::input::PointerEventPhase::CANCEL; + event.set_pointer(pointer_event); + dispatcher_.ProcessEvent(event); + EXPECT_EQ(ET_TOUCH_CANCELLED, captured_event_->type()); + ResetCapturedEvent(); + + pointer_event.phase = fuchsia::ui::input::PointerEventPhase::UP; + event.set_pointer(pointer_event); + dispatcher_.ProcessEvent(event); + EXPECT_EQ(ET_TOUCH_RELEASED, captured_event_->type()); + ResetCapturedEvent(); + + pointer_event.phase = fuchsia::ui::input::PointerEventPhase::ADD; + event.set_pointer(pointer_event); + dispatcher_.ProcessEvent(event); + EXPECT_EQ(nullptr, captured_event_.get()); + + pointer_event.phase = fuchsia::ui::input::PointerEventPhase::REMOVE; + event.set_pointer(pointer_event); + dispatcher_.ProcessEvent(event); + EXPECT_EQ(nullptr, captured_event_.get()); +} + +TEST_F(InputEventDispatcherTest, KeyPhases) { + FuchsiaKeyboardEvent key_event; + key_event.hid_usage = 4; // Corresponds to pressing "a" on a US keyboard. + key_event.phase = fuchsia::ui::input::KeyboardEventPhase::PRESSED; + + InputEvent event; + event.set_keyboard(key_event); + dispatcher_.ProcessEvent(event); + EXPECT_EQ(ET_KEY_PRESSED, captured_event_->type()); + ResetCapturedEvent(); + + key_event.phase = fuchsia::ui::input::KeyboardEventPhase::REPEAT; + event.set_keyboard(key_event); + dispatcher_.ProcessEvent(event); + EXPECT_EQ(ET_KEY_PRESSED, captured_event_->type()); + ResetCapturedEvent(); + + key_event.phase = fuchsia::ui::input::KeyboardEventPhase::RELEASED; + event.set_keyboard(key_event); + dispatcher_.ProcessEvent(event); + EXPECT_EQ(ET_KEY_RELEASED, captured_event_->type()); + ResetCapturedEvent(); + + key_event.phase = fuchsia::ui::input::KeyboardEventPhase::CANCELLED; + event.set_keyboard(key_event); + dispatcher_.ProcessEvent(event); + EXPECT_EQ(ET_KEY_RELEASED, captured_event_->type()); +} + +TEST_F(InputEventDispatcherTest, KeyModifiers) { + FuchsiaKeyboardEvent key_event; + key_event.hid_usage = 4; // Corresponds to pressing "a" on a US keyboard. + key_event.phase = fuchsia::ui::input::KeyboardEventPhase::PRESSED; + + InputEvent event; + event.set_keyboard(key_event); + dispatcher_.ProcessEvent(event); + EXPECT_EQ(ET_KEY_PRESSED, captured_event_->type()); + EXPECT_EQ('a', + DomKey(captured_event_->AsKeyEvent()->GetDomKey()).ToCharacter()); + EXPECT_EQ('a', captured_event_->AsKeyEvent()->GetCharacter()); + ResetCapturedEvent(); + + key_event.modifiers = fuchsia::ui::input::kModifierShift; + event.set_keyboard(key_event); + dispatcher_.ProcessEvent(event); + EXPECT_EQ(ET_KEY_PRESSED, captured_event_->type()); + EXPECT_EQ('A', + DomKey(captured_event_->AsKeyEvent()->GetDomKey()).ToCharacter()); + EXPECT_EQ('A', captured_event_->AsKeyEvent()->GetCharacter()); + EXPECT_TRUE(captured_event_->IsShiftDown()); + EXPECT_FALSE(captured_event_->IsControlDown()); + EXPECT_FALSE(captured_event_->IsAltDown()); + ResetCapturedEvent(); + + key_event.modifiers = + fuchsia::ui::input::kModifierShift | fuchsia::ui::input::kModifierControl; + event.set_keyboard(key_event); + dispatcher_.ProcessEvent(event); + EXPECT_EQ(ET_KEY_PRESSED, captured_event_->type()); + EXPECT_TRUE(captured_event_->IsShiftDown()); + EXPECT_TRUE(captured_event_->IsControlDown()); + EXPECT_FALSE(captured_event_->IsAltDown()); + ResetCapturedEvent(); + + key_event.modifiers = fuchsia::ui::input::kModifierShift | + fuchsia::ui::input::kModifierControl | + fuchsia::ui::input::kModifierAlt; + event.set_keyboard(key_event); + dispatcher_.ProcessEvent(event); + EXPECT_EQ(ET_KEY_PRESSED, captured_event_->type()); + EXPECT_EQ('A', + DomKey(captured_event_->AsKeyEvent()->GetDomKey()).ToCharacter()); + EXPECT_TRUE(captured_event_->IsShiftDown()); + EXPECT_TRUE(captured_event_->IsControlDown()); + EXPECT_TRUE(captured_event_->IsAltDown()); +} + +} // namespace +} // namespace ui diff --git a/chromium/ui/events/gesture_detection/filtered_gesture_provider.cc b/chromium/ui/events/gesture_detection/filtered_gesture_provider.cc index 9ef35b20278..b44f953463d 100644 --- a/chromium/ui/events/gesture_detection/filtered_gesture_provider.cc +++ b/chromium/ui/events/gesture_detection/filtered_gesture_provider.cc @@ -64,6 +64,10 @@ void FilteredGestureProvider::OnTouchEventAck( is_source_touch_event_set_non_blocking); } +void FilteredGestureProvider::ResetGestureHandlingState() { + gesture_filter_.ResetGestureHandlingState(); +} + void FilteredGestureProvider::ResetDetection() { gesture_provider_->ResetDetection(); } diff --git a/chromium/ui/events/gesture_detection/filtered_gesture_provider.h b/chromium/ui/events/gesture_detection/filtered_gesture_provider.h index 1f6761203d7..14718904d29 100644 --- a/chromium/ui/events/gesture_detection/filtered_gesture_provider.h +++ b/chromium/ui/events/gesture_detection/filtered_gesture_provider.h @@ -49,6 +49,8 @@ class GESTURE_DETECTION_EXPORT FilteredGestureProvider bool event_consumed, bool is_source_touch_event_set_non_blocking); + void ResetGestureHandlingState(); + // Methods delegated to |gesture_provider_|. void ResetDetection(); void SetMultiTouchZoomSupportEnabled(bool enabled); diff --git a/chromium/ui/events/gesture_detection/gesture_provider.cc b/chromium/ui/events/gesture_detection/gesture_provider.cc index ad21fe07cc5..d724b5dc819 100644 --- a/chromium/ui/events/gesture_detection/gesture_provider.cc +++ b/chromium/ui/events/gesture_detection/gesture_provider.cc @@ -196,8 +196,10 @@ class GestureProvider::GestureListenerImpl : public ScaleGestureListener, break; case ET_GESTURE_PINCH_BEGIN: DCHECK(!pinch_event_sent_); - if (!scroll_event_sent_) + if (!scroll_event_sent_ && + !scale_gesture_detector_.InAnchoredScaleMode()) { Send(GestureEventData(ET_GESTURE_SCROLL_BEGIN, gesture)); + } pinch_event_sent_ = true; break; case ET_GESTURE_PINCH_END: diff --git a/chromium/ui/events/gesture_detection/gesture_provider_unittest.cc b/chromium/ui/events/gesture_detection/gesture_provider_unittest.cc index 3207893aed8..4cf65bc0863 100644 --- a/chromium/ui/events/gesture_detection/gesture_provider_unittest.cc +++ b/chromium/ui/events/gesture_detection/gesture_provider_unittest.cc @@ -773,7 +773,6 @@ TEST_F(GestureProviderTest, DoubleTapDragZoomBasic) { MotionEvent::Action::MOVE, kFakeCoordX, kFakeCoordY + 100); EXPECT_TRUE(gesture_provider_->OnTouchEvent(event)); - EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN)); ASSERT_EQ(ET_GESTURE_PINCH_BEGIN, GetNthMostRecentGestureEventType(1)); ASSERT_EQ(ET_GESTURE_PINCH_UPDATE, GetMostRecentGestureEventType()); EXPECT_LT(1.f, GetMostRecentGestureEvent().details.scale()); @@ -803,7 +802,7 @@ TEST_F(GestureProviderTest, DoubleTapDragZoomBasic) { kFakeCoordY - 200); EXPECT_TRUE(gesture_provider_->OnTouchEvent(event)); EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_PINCH_END)); - EXPECT_EQ(ET_GESTURE_SCROLL_END, GetMostRecentGestureEventType()); + EXPECT_EQ(ET_GESTURE_PINCH_END, GetMostRecentGestureEventType()); EXPECT_EQ(BoundsForSingleMockTouchAtLocation(kFakeCoordX, kFakeCoordY - 200), GetMostRecentGestureEvent().details.bounding_box_f()); } @@ -1273,7 +1272,7 @@ TEST_F(GestureProviderTest, NoGestureLongPressDuringDoubleTap) { MotionEvent::Action::UP, kFakeCoordX, kFakeCoordY + 1); event.SetPrimaryPointerId(motion_event_id); EXPECT_TRUE(gesture_provider_->OnTouchEvent(event)); - EXPECT_EQ(ET_GESTURE_SCROLL_END, GetMostRecentGestureEventType()); + EXPECT_EQ(ET_GESTURE_PINCH_END, GetMostRecentGestureEventType()); EXPECT_EQ(motion_event_id, GetMostRecentGestureEvent().motion_event_id); EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points()); EXPECT_FALSE(gesture_provider_->IsDoubleTapInProgress()); @@ -1641,7 +1640,6 @@ TEST_F(GestureProviderTest, FixedPageScaleDuringDoubleTapDragZoom) { MotionEvent::Action::MOVE, kFakeCoordX, kFakeCoordY + 100); EXPECT_TRUE(gesture_provider_->OnTouchEvent(event)); - EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN)); EXPECT_EQ(ET_GESTURE_PINCH_BEGIN, GetNthMostRecentGestureEventType(1)); EXPECT_EQ(ET_GESTURE_PINCH_UPDATE, GetMostRecentGestureEventType()); EXPECT_LT(1.f, GetMostRecentGestureEvent().details.scale()); @@ -1664,7 +1662,7 @@ TEST_F(GestureProviderTest, FixedPageScaleDuringDoubleTapDragZoom) { kFakeCoordY + 200); EXPECT_TRUE(gesture_provider_->OnTouchEvent(event)); EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_PINCH_END)); - EXPECT_EQ(ET_GESTURE_SCROLL_END, GetMostRecentGestureEventType()); + EXPECT_EQ(ET_GESTURE_PINCH_END, GetMostRecentGestureEventType()); EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points()); // The double-tap gesture has finished, but the page scale is fixed. @@ -2094,7 +2092,6 @@ TEST_F(GestureProviderTest, DoubleTapDragZoomCancelledOnSecondaryPointerDown) { MotionEvent::Action::MOVE, kFakeCoordX, kFakeCoordY - 30); EXPECT_TRUE(gesture_provider_->OnTouchEvent(event)); - EXPECT_TRUE(HasReceivedGesture(ET_GESTURE_SCROLL_BEGIN)); EXPECT_EQ(ET_GESTURE_PINCH_BEGIN, GetNthMostRecentGestureEventType(1)); EXPECT_EQ(ET_GESTURE_PINCH_UPDATE, GetMostRecentGestureEventType()); EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points()); @@ -2115,9 +2112,7 @@ TEST_F(GestureProviderTest, DoubleTapDragZoomCancelledOnSecondaryPointerDown) { event = ObtainMotionEvent(down_time_2 + kOneSecond, MotionEvent::Action::UP); EXPECT_TRUE(gesture_provider_->OnTouchEvent(event)); - EXPECT_EQ(gesture_count + 1, GetReceivedGestureCount()); - EXPECT_EQ(ET_GESTURE_SCROLL_END, GetMostRecentGestureEventType()); - EXPECT_EQ(1, GetMostRecentGestureEvent().details.touch_points()); + EXPECT_EQ(ET_GESTURE_PINCH_END, GetMostRecentGestureEventType()); } // Verify that gesture begin and gesture end events are dispatched correctly. diff --git a/chromium/ui/events/gesture_detection/touch_disposition_gesture_filter.cc b/chromium/ui/events/gesture_detection/touch_disposition_gesture_filter.cc index 37bbda37ecb..6f6af6f15ae 100644 --- a/chromium/ui/events/gesture_detection/touch_disposition_gesture_filter.cc +++ b/chromium/ui/events/gesture_detection/touch_disposition_gesture_filter.cc @@ -181,9 +181,9 @@ TouchDispositionGestureFilter::OnGesturePacket( Tail().back().unique_touch_event_id())); } if (!Head().empty()) { - DCHECK_NE(packet.unique_touch_event_id(), - Head().front().unique_touch_event_id()); - + DCHECK((packet.gesture_source() == GestureEventDataPacket::TOUCH_TIMEOUT) || + packet.unique_touch_event_id() != + Head().front().unique_touch_event_id()); } Tail().push(packet); @@ -259,6 +259,10 @@ bool TouchDispositionGestureFilter::IsEmpty() const { return sequences_.empty(); } +void TouchDispositionGestureFilter::ResetGestureHandlingState() { + state_ = GestureHandlingState(); +} + void TouchDispositionGestureFilter::FilterAndSendPacket( const GestureEventDataPacket& packet) { if (packet.gesture_source() == GestureEventDataPacket::TOUCH_SEQUENCE_START) { diff --git a/chromium/ui/events/gesture_detection/touch_disposition_gesture_filter.h b/chromium/ui/events/gesture_detection/touch_disposition_gesture_filter.h index 6f4683fcf71..8c63fdeb77f 100644 --- a/chromium/ui/events/gesture_detection/touch_disposition_gesture_filter.h +++ b/chromium/ui/events/gesture_detection/touch_disposition_gesture_filter.h @@ -52,6 +52,8 @@ class GESTURE_DETECTION_EXPORT TouchDispositionGestureFilter { // Whether there are any active gesture sequences still queued in the filter. bool IsEmpty() const; + void ResetGestureHandlingState(); + private: // A single GestureSequence corresponds to all gestures created // between the first finger down and the last finger up, including gestures diff --git a/chromium/ui/events/gestures/gesture_provider_aura.cc b/chromium/ui/events/gestures/gesture_provider_aura.cc index b2a4d792ff2..83f085a1cae 100644 --- a/chromium/ui/events/gestures/gesture_provider_aura.cc +++ b/chromium/ui/events/gestures/gesture_provider_aura.cc @@ -66,6 +66,10 @@ void GestureProviderAura::OnTouchEventAck( is_source_touch_event_set_non_blocking); } +void GestureProviderAura::ResetGestureHandlingState() { + filtered_gesture_provider_.ResetGestureHandlingState(); +} + void GestureProviderAura::OnGestureEvent(const GestureEventData& gesture) { std::unique_ptr<ui::GestureEvent> event( new ui::GestureEvent(gesture.x, gesture.y, gesture.flags, @@ -92,10 +96,10 @@ GestureProviderAura::GetAndResetPendingGestures() { } void GestureProviderAura::OnTouchEnter(int pointer_id, float x, float y) { - std::unique_ptr<TouchEvent> touch_event(new TouchEvent( + auto touch_event = std::make_unique<TouchEvent>( ET_TOUCH_PRESSED, gfx::Point(), ui::EventTimeForNow(), PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, pointer_id), - EF_IS_SYNTHESIZED, 0.0f)); + EF_IS_SYNTHESIZED); gfx::PointF point(x, y); touch_event->set_location_f(point); touch_event->set_root_location_f(point); diff --git a/chromium/ui/events/gestures/gesture_provider_aura.h b/chromium/ui/events/gestures/gesture_provider_aura.h index 987fbbe254b..7072bf8e9ff 100644 --- a/chromium/ui/events/gestures/gesture_provider_aura.h +++ b/chromium/ui/events/gestures/gesture_provider_aura.h @@ -49,6 +49,8 @@ class EVENTS_EXPORT GestureProviderAura : public GestureProviderClient { std::vector<std::unique_ptr<GestureEvent>> GetAndResetPendingGestures(); void OnTouchEnter(int pointer_id, float x, float y); + void ResetGestureHandlingState(); + // GestureProviderClient implementation void OnGestureEvent(const GestureEventData& gesture) override; bool RequiresDoubleTapGestureEvents() const override; diff --git a/chromium/ui/events/gestures/gesture_provider_aura_unittest.cc b/chromium/ui/events/gestures/gesture_provider_aura_unittest.cc index 1eaa215287d..b425a987fae 100644 --- a/chromium/ui/events/gestures/gesture_provider_aura_unittest.cc +++ b/chromium/ui/events/gestures/gesture_provider_aura_unittest.cc @@ -125,55 +125,55 @@ TEST_F(GestureProviderAuraTest, IgnoresIdenticalMoveEvents) { const int kTouchId0 = 5; const int kTouchId1 = 3; - PointerDetails pointerDetails1(EventPointerType::POINTER_TYPE_TOUCH, - kTouchId0); + PointerDetails pointer_details1(EventPointerType::POINTER_TYPE_TOUCH, + kTouchId0); base::TimeTicks time = ui::EventTimeForNow(); TouchEvent press0_1(ET_TOUCH_PRESSED, gfx::Point(9, 10), time, - pointerDetails1); + pointer_details1); EXPECT_TRUE(provider()->OnTouchEvent(&press0_1)); - PointerDetails pointerDetails2(EventPointerType::POINTER_TYPE_TOUCH, - kTouchId1); + PointerDetails pointer_details2(EventPointerType::POINTER_TYPE_TOUCH, + kTouchId1); TouchEvent press1_1(ET_TOUCH_PRESSED, gfx::Point(40, 40), time, - pointerDetails2); + pointer_details2); EXPECT_TRUE(provider()->OnTouchEvent(&press1_1)); time += base::TimeDelta::FromMilliseconds(10); - pointerDetails1 = PointerDetails(EventPointerType::POINTER_TYPE_TOUCH, - kTouchId0, kRadiusX, kRadiusY, kForce); - TouchEvent move0_1(ET_TOUCH_MOVED, gfx::Point(10, 10), time, pointerDetails1, - 0, kAngle); + pointer_details1 = + PointerDetails(EventPointerType::POINTER_TYPE_TOUCH, kTouchId0, kRadiusX, + kRadiusY, kForce, kAngle); + TouchEvent move0_1(ET_TOUCH_MOVED, gfx::Point(10, 10), time, pointer_details1, + 0); EXPECT_TRUE(provider()->OnTouchEvent(&move0_1)); - pointerDetails2 = PointerDetails(EventPointerType::POINTER_TYPE_TOUCH, - kTouchId1, kRadiusX, kRadiusY, kForce); + pointer_details2 = + PointerDetails(EventPointerType::POINTER_TYPE_TOUCH, kTouchId1, kRadiusX, + kRadiusY, kForce, kAngle); TouchEvent move1_1(ET_TOUCH_MOVED, gfx::Point(100, 200), time, - pointerDetails2, 0, kAngle); + pointer_details2, 0); EXPECT_TRUE(provider()->OnTouchEvent(&move1_1)); time += base::TimeDelta::FromMilliseconds(10); - TouchEvent move0_2(ET_TOUCH_MOVED, gfx::Point(10, 10), time, pointerDetails1, - 0, kAngle); + TouchEvent move0_2(ET_TOUCH_MOVED, gfx::Point(10, 10), time, pointer_details1, + 0); // Nothing has changed, so ignore the move. EXPECT_FALSE(provider()->OnTouchEvent(&move0_2)); TouchEvent move1_2(ET_TOUCH_MOVED, gfx::Point(100, 200), time, - pointerDetails2, 0, kAngle); + pointer_details2, 0); // Nothing has changed, so ignore the move. EXPECT_FALSE(provider()->OnTouchEvent(&move1_2)); time += base::TimeDelta::FromMilliseconds(10); - TouchEvent move0_3(ET_TOUCH_MOVED, gfx::Point(), time, pointerDetails1, 0, - kAngle); + TouchEvent move0_3(ET_TOUCH_MOVED, gfx::Point(), time, pointer_details1, 0); move0_3.set_location_f(gfx::PointF(70, 75.1f)); move0_3.set_root_location_f(gfx::PointF(70, 75.1f)); // Position has changed, so don't ignore the move. EXPECT_TRUE(provider()->OnTouchEvent(&move0_3)); time += base::TimeDelta::FromMilliseconds(10); - pointerDetails2.radius_y += 1; - TouchEvent move0_4(ET_TOUCH_MOVED, gfx::Point(), time, pointerDetails2, 0, - kAngle); + pointer_details2.radius_y += 1; + TouchEvent move0_4(ET_TOUCH_MOVED, gfx::Point(), time, pointer_details2, 0); move0_4.set_location_f(gfx::PointF(70, 75.1f)); move0_4.set_root_location_f(gfx::PointF(70, 75.1f)); } diff --git a/chromium/ui/events/gestures/gesture_recognizer.cc b/chromium/ui/events/gestures/gesture_recognizer.cc new file mode 100644 index 00000000000..533ab77e924 --- /dev/null +++ b/chromium/ui/events/gestures/gesture_recognizer.cc @@ -0,0 +1,22 @@ +// 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/events/gestures/gesture_recognizer.h" + +#include "ui/events/gestures/gesture_recognizer_observer.h" + +namespace ui { + +GestureRecognizer::GestureRecognizer() = default; +GestureRecognizer::~GestureRecognizer() = default; + +void GestureRecognizer::AddObserver(GestureRecognizerObserver* observer) { + observers_.AddObserver(observer); +} + +void GestureRecognizer::RemoveObserver(GestureRecognizerObserver* observer) { + observers_.RemoveObserver(observer); +} + +} // namespace ui diff --git a/chromium/ui/events/gestures/gesture_recognizer.h b/chromium/ui/events/gestures/gesture_recognizer.h index f4da6a06dd7..de966ee36aa 100644 --- a/chromium/ui/events/gestures/gesture_recognizer.h +++ b/chromium/ui/events/gestures/gesture_recognizer.h @@ -10,19 +10,23 @@ #include <memory> #include <vector> +#include "base/observer_list.h" #include "ui/events/event_constants.h" #include "ui/events/events_export.h" #include "ui/events/gestures/gesture_types.h" #include "ui/gfx/geometry/point_f.h" namespace ui { +class GestureRecognizerObserver; + // A GestureRecognizer is an abstract base class for conversion of touch events // into gestures. class EVENTS_EXPORT GestureRecognizer { public: using Gestures = std::vector<std::unique_ptr<GestureEvent>>; - virtual ~GestureRecognizer() {} + GestureRecognizer(); + virtual ~GestureRecognizer(); // Invoked before event dispatch. If the event is invalid given the current // touch sequence, returns false. @@ -57,15 +61,14 @@ class EVENTS_EXPORT GestureRecognizer { // |not_cancelled| == nullptr, cancels all touches. virtual void CancelActiveTouchesExcept(GestureConsumer* not_cancelled) = 0; - enum class ShouldCancelTouches { Cancel, DontCancel }; - // Transfer the gesture stream from the drag source (current_consumer) to the - // consumer used for dragging (new_consumer). If |should_cancel_touches| is - // Cancel, dispatches cancel events to |current_consumer| to ensure that its - // touch stream remains valid. - virtual void TransferEventsTo(GestureConsumer* current_consumer, - GestureConsumer* new_consumer, - ShouldCancelTouches should_cancel_touches) = 0; + // consumer used for dragging (new_consumer). If |transfer_touches_behavior| + // is kCancel, dispatches cancel events to |current_consumer| to ensure that + // its touch stream remains valid. + virtual void TransferEventsTo( + GestureConsumer* current_consumer, + GestureConsumer* new_consumer, + TransferTouchesBehavior transfer_touches_behavior) = 0; // If a gesture is underway for |consumer| |point| is set to the last touch // point and true is returned. If no touch events have been processed for @@ -87,6 +90,19 @@ class EVENTS_EXPORT GestureRecognizer { // Since the GestureRecognizer does not own the |helper|, it is not deleted // and must be cleaned up appropriately by the caller. virtual void RemoveGestureEventHelper(GestureEventHelper* helper) = 0; + + void AddObserver(GestureRecognizerObserver* observer); + void RemoveObserver(GestureRecognizerObserver* observer); + + protected: + const base::ObserverList<GestureRecognizerObserver>& observers() { + return observers_; + } + + private: + base::ObserverList<GestureRecognizerObserver> observers_; + + DISALLOW_COPY_AND_ASSIGN(GestureRecognizer); }; } // namespace ui diff --git a/chromium/ui/events/gestures/gesture_recognizer_impl.cc b/chromium/ui/events/gestures/gesture_recognizer_impl.cc index 1702be9de15..6e811311856 100644 --- a/chromium/ui/events/gestures/gesture_recognizer_impl.cc +++ b/chromium/ui/events/gestures/gesture_recognizer_impl.cc @@ -19,6 +19,7 @@ #include "ui/events/event_switches.h" #include "ui/events/event_utils.h" #include "ui/events/gesture_detection/gesture_configuration.h" +#include "ui/events/gestures/gesture_recognizer_observer.h" #include "ui/events/gestures/gesture_types.h" namespace ui { @@ -107,33 +108,21 @@ GestureConsumer* GestureRecognizerImpl::GetTargetForLocation( void GestureRecognizerImpl::CancelActiveTouchesExcept( GestureConsumer* not_cancelled) { - // Do not iterate directly over |consumer_gesture_provider_| because canceling - // active touches may cause the consumer to be removed from - // |consumer_gesture_provider_|. See crbug.com/651258 for more info. - std::vector<GestureConsumer*> consumers(consumer_gesture_provider_.size()); - for (const auto& entry : consumer_gesture_provider_) { - if (entry.first == not_cancelled) - continue; - - consumers.push_back(entry.first); - } - - for (auto* consumer : consumers) - CancelActiveTouches(consumer); + CancelActiveTouchesExceptImpl(not_cancelled, kNotifyObservers); } void GestureRecognizerImpl::TransferEventsTo( GestureConsumer* current_consumer, GestureConsumer* new_consumer, - ShouldCancelTouches should_cancel_touches) { + TransferTouchesBehavior transfer_touches_behavior) { // This method transfers the gesture stream from |current_consumer| to - // |new_consumer|. If |should_cancel_touches| is Cancel, it ensures that both - // consumers retain a touch event stream which is reasonably valid. In order - // to do this we + // |new_consumer|. If |transfer_touches_behavior| is kCancel, it ensures that + // both consumers retain a touch event stream which is reasonably valid. In + // order to do this we // - record what pointers are currently down on |current_consumer| // - cancel touches on consumers other than |current_consumer| // - move the gesture provider from |current_consumer| to |new_consumer| - // - if |should_cancel_touches| + // - if |transfer_touches_behavior| is kCancel // - synchronize the state of the new gesture provider associated with // current_consumer with with the touch state of the consumer itself via // OnTouchEnter. @@ -153,7 +142,7 @@ void GestureRecognizerImpl::TransferEventsTo( touchids_targeted_at_current.push_back(touch_id_target.first); } - CancelActiveTouchesExcept(current_consumer); + CancelActiveTouchesExceptImpl(current_consumer, kDontNotifyObservers); std::vector<std::unique_ptr<TouchEvent>> cancelling_touches = GetEventPerPointForConsumer(current_consumer, ET_TOUCH_CANCELLED); @@ -164,9 +153,7 @@ void GestureRecognizerImpl::TransferEventsTo( // but has some pointers down which need cancelling. In order to ensure that // the GR sees a valid event stream, inform it of these pointers via // OnTouchEnter, and then synthesize a touch cancel per pointer. - if (should_cancel_touches == - GestureRecognizer::ShouldCancelTouches::Cancel && - helper) { + if (transfer_touches_behavior == TransferTouchesBehavior::kCancel && helper) { GestureProviderAura* gesture_provider = GetGestureProviderForConsumer(current_consumer); @@ -177,8 +164,18 @@ void GestureRecognizerImpl::TransferEventsTo( } } + // The underlying gesture provider for current_consumer might have filtered + // gesture detection for some reasons but that might not be applied to the new + // consumer. See also: + // https://docs.google.com/document/d/1AKeK8IuF-j2TJ-2sPsewORXdjnr6oAzy5nnR1zwrsfc/edit# + if (base::ContainsKey(consumer_gesture_provider_, new_consumer)) + GetGestureProviderForConsumer(new_consumer)->ResetGestureHandlingState(); + for (int touch_id : touchids_targeted_at_current) touch_id_target_[touch_id] = new_consumer; + for (GestureRecognizerObserver& observer : observers()) + observer.OnEventsTransferred(current_consumer, new_consumer, + transfer_touches_behavior); } bool GestureRecognizerImpl::GetLastTouchPointForTarget( @@ -210,11 +207,11 @@ GestureRecognizerImpl::GetEventPerPointForConsumer(GestureConsumer* consumer, if (pointer_state.GetPointerCount() == 0) return cancelling_touches; for (size_t i = 0; i < pointer_state.GetPointerCount(); ++i) { - std::unique_ptr<TouchEvent> touch_event( - new TouchEvent(type, gfx::Point(), EventTimeForNow(), - PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, - pointer_state.GetPointerId(i)), - EF_IS_SYNTHESIZED, 0.0f)); + auto touch_event = std::make_unique<TouchEvent>( + type, gfx::Point(), EventTimeForNow(), + PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, + pointer_state.GetPointerId(i)), + EF_IS_SYNTHESIZED); gfx::PointF point(pointer_state.GetX(i), pointer_state.GetY(i)); touch_event->set_location_f(point); touch_event->set_root_location_f(point); @@ -224,21 +221,11 @@ GestureRecognizerImpl::GetEventPerPointForConsumer(GestureConsumer* consumer, } bool GestureRecognizerImpl::CancelActiveTouches(GestureConsumer* consumer) { - GestureEventHelper* helper = - FindDispatchHelperForConsumer(consumer); - - if (!helper) - return false; - - std::vector<std::unique_ptr<TouchEvent>> cancelling_touches = - GetEventPerPointForConsumer(consumer, ET_TOUCH_CANCELLED); - for (const std::unique_ptr<TouchEvent>& cancelling_touch : cancelling_touches) - helper->DispatchSyntheticTouchEvent(cancelling_touch.get()); - return cancelling_touches.size() > 0U; + return CancelActiveTouchesImpl(consumer, kNotifyObservers); } //////////////////////////////////////////////////////////////////////////////// -// GestureRecognizerImpl, private: +// GestureRecognizerImpl, protected: GestureProviderAura* GestureRecognizerImpl::GetGestureProviderForConsumer( GestureConsumer* consumer) { @@ -256,6 +243,22 @@ GestureProviderAura* GestureRecognizerImpl::GetGestureProviderForConsumer( return gesture_provider; } +bool GestureRecognizerImpl::ProcessTouchEventPreDispatch( + TouchEvent* event, + GestureConsumer* consumer) { + SetupTargets(*event, consumer); + + if (event->result() & ER_CONSUMED) + return false; + + GestureProviderAura* gesture_provider = + GetGestureProviderForConsumer(consumer); + return gesture_provider->OnTouchEvent(event); +} + +//////////////////////////////////////////////////////////////////////////////// +// GestureRecognizerImpl, private: + void GestureRecognizerImpl::SetupTargets(const TouchEvent& event, GestureConsumer* target) { event_to_gesture_provider_[event.unique_event_id()] = @@ -279,19 +282,6 @@ void GestureRecognizerImpl::DispatchGestureEvent( } } -bool GestureRecognizerImpl::ProcessTouchEventPreDispatch( - TouchEvent* event, - GestureConsumer* consumer) { - SetupTargets(*event, consumer); - - if (event->result() & ER_CONSUMED) - return false; - - GestureProviderAura* gesture_provider = - GetGestureProviderForConsumer(consumer); - return gesture_provider->OnTouchEvent(event); -} - GestureRecognizer::Gestures GestureRecognizerImpl::AckTouchEvent( uint32_t unique_event_id, ui::EventResult result, @@ -314,6 +304,48 @@ GestureRecognizer::Gestures GestureRecognizerImpl::AckTouchEvent( return gesture_provider->GetAndResetPendingGestures(); } +void GestureRecognizerImpl::CancelActiveTouchesExceptImpl( + GestureConsumer* not_cancelled, + ShouldNotifyObservers should_notify) { + // Do not iterate directly over |consumer_gesture_provider_| because canceling + // active touches may cause the consumer to be removed from + // |consumer_gesture_provider_|. See https://crbug.com/651258 for more info. + std::vector<GestureConsumer*> consumers(consumer_gesture_provider_.size()); + for (const auto& entry : consumer_gesture_provider_) { + if (entry.first == not_cancelled) + continue; + + consumers.push_back(entry.first); + } + + for (auto* consumer : consumers) + CancelActiveTouchesImpl(consumer, kDontNotifyObservers); + + if (should_notify == kDontNotifyObservers) + return; + for (GestureRecognizerObserver& observer : observers()) + observer.OnActiveTouchesCanceledExcept(not_cancelled); +} + +bool GestureRecognizerImpl::CancelActiveTouchesImpl( + GestureConsumer* consumer, + ShouldNotifyObservers should_notify) { + GestureEventHelper* helper = FindDispatchHelperForConsumer(consumer); + + if (!helper) + return false; + + std::vector<std::unique_ptr<TouchEvent>> cancelling_touches = + GetEventPerPointForConsumer(consumer, ET_TOUCH_CANCELLED); + for (const std::unique_ptr<TouchEvent>& cancelling_touch : cancelling_touches) + helper->DispatchSyntheticTouchEvent(cancelling_touch.get()); + if (should_notify == kNotifyObservers) { + for (GestureRecognizerObserver& observer : observers()) + observer.OnActiveTouchesCanceled(consumer); + } + return !cancelling_touches.empty(); +} + bool GestureRecognizerImpl::CleanupStateForConsumer( GestureConsumer* consumer) { bool state_cleaned_up = false; @@ -341,8 +373,7 @@ void GestureRecognizerImpl::AddGestureEventHelper(GestureEventHelper* helper) { void GestureRecognizerImpl::RemoveGestureEventHelper( GestureEventHelper* helper) { - std::vector<GestureEventHelper*>::iterator it = std::find(helpers_.begin(), - helpers_.end(), helper); + auto it = std::find(helpers_.begin(), helpers_.end(), helper); if (it != helpers_.end()) helpers_.erase(it); } diff --git a/chromium/ui/events/gestures/gesture_recognizer_impl.h b/chromium/ui/events/gestures/gesture_recognizer_impl.h index ba6c416b7fc..36e8fc9f2cc 100644 --- a/chromium/ui/events/gestures/gesture_recognizer_impl.h +++ b/chromium/ui/events/gestures/gesture_recognizer_impl.h @@ -48,9 +48,10 @@ class EVENTS_EXPORT GestureRecognizerImpl : public GestureRecognizer, GestureConsumer* GetTargetForLocation(const gfx::PointF& location, int source_device_id) override; void CancelActiveTouchesExcept(GestureConsumer* not_cancelled) override; - void TransferEventsTo(GestureConsumer* current_consumer, - GestureConsumer* new_consumer, - ShouldCancelTouches should_cancel_touches) override; + void TransferEventsTo( + GestureConsumer* current_consumer, + GestureConsumer* new_consumer, + TransferTouchesBehavior transfer_touches_behavior) override; bool GetLastTouchPointForTarget(GestureConsumer* consumer, gfx::PointF* point) override; bool CancelActiveTouches(GestureConsumer* consumer) override; @@ -64,6 +65,8 @@ class EVENTS_EXPORT GestureRecognizerImpl : public GestureRecognizer, GestureConsumer* consumer) override; private: + enum ShouldNotifyObservers { kNotifyObservers, kDontNotifyObservers }; + // Sets up the target consumer for gestures based on the touch-event. void SetupTargets(const TouchEvent& event, GestureConsumer* consumer); @@ -75,6 +78,11 @@ class EVENTS_EXPORT GestureRecognizerImpl : public GestureRecognizer, bool is_source_touch_event_set_non_blocking, GestureConsumer* consumer) override; + void CancelActiveTouchesExceptImpl(GestureConsumer* not_cancelled, + ShouldNotifyObservers should_notify); + bool CancelActiveTouchesImpl(GestureConsumer* consumer, + ShouldNotifyObservers should_notify); + bool CleanupStateForConsumer(GestureConsumer* consumer) override; void AddGestureEventHelper(GestureEventHelper* helper) override; void RemoveGestureEventHelper(GestureEventHelper* helper) override; diff --git a/chromium/ui/events/gestures/gesture_recognizer_impl_mac.cc b/chromium/ui/events/gestures/gesture_recognizer_impl_mac.cc index b52a8db1d32..d66805dfef0 100644 --- a/chromium/ui/events/gestures/gesture_recognizer_impl_mac.cc +++ b/chromium/ui/events/gestures/gesture_recognizer_impl_mac.cc @@ -45,7 +45,7 @@ void GestureRecognizerImplMac::CancelActiveTouchesExcept( void GestureRecognizerImplMac::TransferEventsTo( GestureConsumer* current_consumer, GestureConsumer* new_consumer, - ShouldCancelTouches should_cancel_touches) {} + TransferTouchesBehavior transfer_touches_behavior) {} bool GestureRecognizerImplMac::GetLastTouchPointForTarget( GestureConsumer* consumer, diff --git a/chromium/ui/events/gestures/gesture_recognizer_impl_mac.h b/chromium/ui/events/gestures/gesture_recognizer_impl_mac.h index 391bd2bbec3..f0c4615f436 100644 --- a/chromium/ui/events/gestures/gesture_recognizer_impl_mac.h +++ b/chromium/ui/events/gestures/gesture_recognizer_impl_mac.h @@ -34,9 +34,10 @@ class EVENTS_EXPORT GestureRecognizerImplMac : public GestureRecognizer { GestureConsumer* GetTargetForLocation(const gfx::PointF& location, int source_device_id) override; void CancelActiveTouchesExcept(GestureConsumer* not_cancelled) override; - void TransferEventsTo(GestureConsumer* current_consumer, - GestureConsumer* new_consumer, - ShouldCancelTouches should_cancel_touches) override; + void TransferEventsTo( + GestureConsumer* current_consumer, + GestureConsumer* new_consumer, + TransferTouchesBehavior transfer_touches_behavior) override; bool GetLastTouchPointForTarget(GestureConsumer* consumer, gfx::PointF* point) override; bool CancelActiveTouches(GestureConsumer* consumer) override; diff --git a/chromium/ui/events/gestures/gesture_recognizer_impl_unittest.cc b/chromium/ui/events/gestures/gesture_recognizer_impl_unittest.cc new file mode 100644 index 00000000000..6446df7a5c5 --- /dev/null +++ b/chromium/ui/events/gestures/gesture_recognizer_impl_unittest.cc @@ -0,0 +1,163 @@ +// 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/events/gestures/gesture_recognizer_impl.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/gestures/gesture_recognizer_observer.h" + +namespace ui { + +class TestGestureRecognizerObserver : public GestureRecognizerObserver { + public: + TestGestureRecognizerObserver(GestureRecognizer* gesture_recognizer) + : gesture_recognizer_(gesture_recognizer) { + gesture_recognizer_->AddObserver(this); + } + + ~TestGestureRecognizerObserver() override { + gesture_recognizer_->RemoveObserver(this); + } + + int active_touches_cancelled_except_call_count() const { + return active_touches_cancelled_except_call_count_; + } + int active_touches_cancelled_call_count() const { + return active_touches_cancelled_call_count_; + } + int events_transfer_call_count() const { return events_transfer_call_count_; } + + const GestureConsumer* last_not_cancelled() const { + return last_not_cancelled_; + } + const GestureConsumer* last_cancelled() const { + return last_cancelled_consumer_; + } + + const GestureConsumer* last_transferred_source() const { + return last_transferred_source_; + } + const GestureConsumer* last_transferred_destination() const { + return last_transferred_destination_; + } + TransferTouchesBehavior last_transfer_touches_behavior() const { + return last_transfer_touches_behavior_; + } + + private: + // GestureRecognizerObserver: + void OnActiveTouchesCanceledExcept(GestureConsumer* not_cancelled) override { + active_touches_cancelled_except_call_count_++; + last_not_cancelled_ = not_cancelled; + } + void OnEventsTransferred( + GestureConsumer* current_consumer, + GestureConsumer* new_consumer, + TransferTouchesBehavior transfer_touches_behavior) override { + events_transfer_call_count_++; + last_transferred_source_ = current_consumer; + last_transferred_destination_ = new_consumer; + last_transfer_touches_behavior_ = transfer_touches_behavior; + } + void OnActiveTouchesCanceled(GestureConsumer* consumer) override { + active_touches_cancelled_call_count_++; + last_cancelled_consumer_ = consumer; + } + + GestureRecognizer* gesture_recognizer_; + + int active_touches_cancelled_except_call_count_ = 0; + int active_touches_cancelled_call_count_ = 0; + int events_transfer_call_count_ = 0; + GestureConsumer* last_not_cancelled_ = nullptr; + GestureConsumer* last_cancelled_consumer_ = nullptr; + GestureConsumer* last_transferred_source_ = nullptr; + GestureConsumer* last_transferred_destination_ = nullptr; + TransferTouchesBehavior last_transfer_touches_behavior_ = + TransferTouchesBehavior::kCancel; + + DISALLOW_COPY_AND_ASSIGN(TestGestureRecognizerObserver); +}; + +class TestGestureEventHelper : public GestureEventHelper { + public: + TestGestureEventHelper() = default; + ~TestGestureEventHelper() override = default; + + private: + // GestureEventHelper: + bool CanDispatchToConsumer(GestureConsumer* consumer) override { + return true; + } + void DispatchGestureEvent(GestureConsumer* raw_input_consumer, + GestureEvent* event) override {} + void DispatchSyntheticTouchEvent(TouchEvent* event) override {} + + DISALLOW_COPY_AND_ASSIGN(TestGestureEventHelper); +}; + +class GestureRecognizerImplTest : public testing::Test { + public: + GestureRecognizerImplTest() = default; + ~GestureRecognizerImplTest() override = default; + + void SetUp() override { + gesture_recognizer_.helpers().push_back(&helper_); + observer_ = + std::make_unique<TestGestureRecognizerObserver>(&gesture_recognizer_); + } + + void TearDown() override { observer_.reset(); } + + protected: + GestureRecognizer* gesture_recognizer() { return &gesture_recognizer_; } + TestGestureRecognizerObserver* observer() { return observer_.get(); } + + private: + GestureRecognizerImpl gesture_recognizer_; + TestGestureEventHelper helper_; + std::unique_ptr<TestGestureRecognizerObserver> observer_; + + DISALLOW_COPY_AND_ASSIGN(GestureRecognizerImplTest); +}; + +TEST_F(GestureRecognizerImplTest, CancelActiveTouchEvents) { + GestureConsumer consumer; + gesture_recognizer()->CancelActiveTouches(&consumer); + + EXPECT_EQ(1, observer()->active_touches_cancelled_call_count()); + EXPECT_EQ(&consumer, observer()->last_cancelled()); +} + +TEST_F(GestureRecognizerImplTest, CancelActiveTouchEventsExcept) { + GestureConsumer consumer; + gesture_recognizer()->CancelActiveTouchesExcept(&consumer); + + // OnActiveTouchesCancelled() shouldn't occur. + EXPECT_EQ(0, observer()->active_touches_cancelled_call_count()); + EXPECT_EQ(1, observer()->active_touches_cancelled_except_call_count()); + EXPECT_EQ(&consumer, observer()->last_not_cancelled()); +} + +TEST_F(GestureRecognizerImplTest, CancelActiveTouchEventsExceptNullPtr) { + gesture_recognizer()->CancelActiveTouchesExcept(nullptr); + + EXPECT_EQ(1, observer()->active_touches_cancelled_except_call_count()); + EXPECT_FALSE(observer()->last_not_cancelled()); +} + +TEST_F(GestureRecognizerImplTest, TransferEventsTo) { + GestureConsumer consumer1; + GestureConsumer consumer2; + + gesture_recognizer()->TransferEventsTo(&consumer1, &consumer2, + TransferTouchesBehavior::kDontCancel); + EXPECT_EQ(1, observer()->events_transfer_call_count()); + EXPECT_EQ(&consumer1, observer()->last_transferred_source()); + EXPECT_EQ(&consumer2, observer()->last_transferred_destination()); + EXPECT_EQ(TransferTouchesBehavior::kDontCancel, + observer()->last_transfer_touches_behavior()); +} + +} // namespace ui diff --git a/chromium/ui/events/gestures/gesture_recognizer_observer.cc b/chromium/ui/events/gestures/gesture_recognizer_observer.cc new file mode 100644 index 00000000000..4430530ca6e --- /dev/null +++ b/chromium/ui/events/gestures/gesture_recognizer_observer.cc @@ -0,0 +1,11 @@ +// 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/events/gestures/gesture_recognizer_observer.h" + +namespace ui { + +GestureRecognizerObserver::~GestureRecognizerObserver() = default; + +} // namespace ui diff --git a/chromium/ui/events/gestures/gesture_recognizer_observer.h b/chromium/ui/events/gestures/gesture_recognizer_observer.h new file mode 100644 index 00000000000..4826cda5bb2 --- /dev/null +++ b/chromium/ui/events/gestures/gesture_recognizer_observer.h @@ -0,0 +1,30 @@ +// 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_EVENTS_GESTURES_GESTURE_RECOGNIZER_OBSERVER_H_ +#define UI_EVENTS_GESTURES_GESTURE_RECOGNIZER_OBSERVER_H_ + +#include "base/observer_list_types.h" +#include "ui/events/events_export.h" +#include "ui/events/gestures/gesture_types.h" + +namespace ui { + +class EVENTS_EXPORT GestureRecognizerObserver : public base::CheckedObserver { + public: + virtual void OnActiveTouchesCanceledExcept( + GestureConsumer* not_cancelled) = 0; + virtual void OnEventsTransferred( + GestureConsumer* current_consumer, + GestureConsumer* new_consumer, + TransferTouchesBehavior transfer_touches_behavior) = 0; + virtual void OnActiveTouchesCanceled(GestureConsumer* consumer) = 0; + + protected: + ~GestureRecognizerObserver() override; +}; + +} // namespace ui + +#endif // UI_EVENTS_GESTURES_GESTURE_RECOGNIZER_OBSERVER_H_ diff --git a/chromium/ui/events/gestures/gesture_types.h b/chromium/ui/events/gestures/gesture_types.h index 3d63c572279..e2334111ea8 100644 --- a/chromium/ui/events/gestures/gesture_types.h +++ b/chromium/ui/events/gestures/gesture_types.h @@ -12,6 +12,17 @@ namespace ui { class GestureEvent; class TouchEvent; +// TransferTouchesBehavior customizes the behavior of +// GestureRecognizer::TransferEventsTo. +enum class TransferTouchesBehavior { + // Dispatches the cancel events to the current consumer on transfer to ensure + // its touch stream remains valid. + kCancel, + + // Do not dispatch cancel events. + kDontCancel +}; + // An abstract type for consumers of gesture-events created by the // gesture-recognizer. class EVENTS_EXPORT GestureConsumer { diff --git a/chromium/ui/events/gestures/motion_event_aura_unittest.cc b/chromium/ui/events/gestures/motion_event_aura_unittest.cc index 276876f2059..5fb665b71cd 100644 --- a/chromium/ui/events/gestures/motion_event_aura_unittest.cc +++ b/chromium/ui/events/gestures/motion_event_aura_unittest.cc @@ -40,8 +40,8 @@ ui::TouchEvent TouchWithTapParams(ui::EventType type, ui::TouchEvent event( type, gfx::Point(1, 1), base::TimeTicks(), ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, id, radius_x, - radius_y, pressure), - 0, rotation_angle); + radius_y, pressure, rotation_angle), + 0); return event; } diff --git a/chromium/ui/events/keyboard_hook.h b/chromium/ui/events/keyboard_hook.h index accb8050a79..36e9b88f23e 100644 --- a/chromium/ui/events/keyboard_hook.h +++ b/chromium/ui/events/keyboard_hook.h @@ -38,7 +38,7 @@ class EVENTS_EXPORT KeyboardHook { KeyEventCallback callback); // True if |dom_code| is reserved for an active KeyboardLock request. - virtual bool IsKeyLocked(DomCode dom_code) = 0; + virtual bool IsKeyLocked(DomCode dom_code) const = 0; }; } // namespace ui diff --git a/chromium/ui/events/keyboard_hook_base.cc b/chromium/ui/events/keyboard_hook_base.cc index 6f971814170..56381738b4a 100644 --- a/chromium/ui/events/keyboard_hook_base.cc +++ b/chromium/ui/events/keyboard_hook_base.cc @@ -23,7 +23,7 @@ KeyboardHookBase::KeyboardHookBase( KeyboardHookBase::~KeyboardHookBase() = default; -bool KeyboardHookBase::IsKeyLocked(DomCode dom_code) { +bool KeyboardHookBase::IsKeyLocked(DomCode dom_code) const { return ShouldCaptureKeyEvent(dom_code); } diff --git a/chromium/ui/events/keyboard_hook_base.h b/chromium/ui/events/keyboard_hook_base.h index 8d18d94246d..81ffae2ff9f 100644 --- a/chromium/ui/events/keyboard_hook_base.h +++ b/chromium/ui/events/keyboard_hook_base.h @@ -22,7 +22,7 @@ class KeyboardHookBase : public KeyboardHook { ~KeyboardHookBase() override; // KeyboardHook implementation. - bool IsKeyLocked(DomCode dom_code) override; + bool IsKeyLocked(DomCode dom_code) const override; protected: // Indicates whether |dom_code| should be intercepted by the keyboard hook. diff --git a/chromium/ui/events/keycodes/keyboard_code_conversion_mac.mm b/chromium/ui/events/keycodes/keyboard_code_conversion_mac.mm index 2b4dd2f096d..873c4ce4457 100644 --- a/chromium/ui/events/keycodes/keyboard_code_conversion_mac.mm +++ b/chromium/ui/events/keycodes/keyboard_code_conversion_mac.mm @@ -892,7 +892,7 @@ UniChar TranslatedUnicodeCharFromKeyCode(TISInputSourceRef input_source, &buffer_length, char_buffer); OSSTATUS_DCHECK(ret == noErr, ret); - // TODO(chongz): Handle multiple character case. Should be rare. + // TODO(input-dev): Handle multiple character case. Should be rare. return char_buffer[0]; } diff --git a/chromium/ui/events/keycodes/keysym_to_unicode.cc b/chromium/ui/events/keycodes/keysym_to_unicode.cc index 5a358804aed..9413e97d59b 100644 --- a/chromium/ui/events/keycodes/keysym_to_unicode.cc +++ b/chromium/ui/events/keycodes/keysym_to_unicode.cc @@ -834,7 +834,7 @@ class KeySymToUnicode { } // Other KeySyms which are not Unicode-style. - KeySymToUnicodeMap::const_iterator i = keysym_to_unicode_map_.find(keysym); + auto i = keysym_to_unicode_map_.find(keysym); return i != keysym_to_unicode_map_.end() ? i->second : 0; } diff --git a/chromium/ui/events/keycodes/platform_key_map_win.cc b/chromium/ui/events/keycodes/platform_key_map_win.cc index c1e3b8368df..c803dc47130 100644 --- a/chromium/ui/events/keycodes/platform_key_map_win.cc +++ b/chromium/ui/events/keycodes/platform_key_map_win.cc @@ -192,7 +192,7 @@ const struct NonPrintableKeyEntry { {VKEY_EREOF, DomKey::ERASE_EOF}, {VKEY_PLAY, DomKey::PLAY}, {VKEY_ZOOM, DomKey::ZOOM_TOGGLE}, - // TODO(chongz): Handle VKEY_NONAME, VKEY_PA1. + // TODO(input-dev): Handle VKEY_NONAME, VKEY_PA1. // https://crbug.com/616910 {VKEY_OEM_CLEAR, DomKey::CLEAR}, }; @@ -268,7 +268,8 @@ struct PlatformKeyMapInstanceTlsTraits base::ThreadLocalStorage::Slot> { static base::ThreadLocalStorage::Slot* New(void* instance) { // Use placement new to initialize our instance in our preallocated space. - // TODO(chongz): Use std::default_delete instead of providing own function. + // TODO(input-dev): Use std::default_delete instead of providing own + // function. return new (instance) base::ThreadLocalStorage::Slot(CleanupKeyMapTls); } }; @@ -382,7 +383,7 @@ void PlatformKeyMap::UpdateLayout(HKL layout) { if (!::GetKeyboardState(keyboard_state_to_restore)) return; - // TODO(chongz): Optimize layout switching (see crbug.com/587147). + // TODO(input-dev): Optimize layout switching (see crbug.com/587147). keyboard_layout_ = layout; printable_keycode_to_key_.clear(); has_alt_graph_ = false; @@ -418,7 +419,7 @@ void PlatformKeyMap::UpdateLayout(HKL layout) { flags)] = DomKey::DeadKeyFromCombiningCharacter(translated_chars[0]); } else { - // TODO(chongz): Check if this will actually happen. + // TODO(input-dev): Check if this will actually happen. } } else if (rv == 1) { if (translated_chars[0] >= 0x20) { @@ -434,7 +435,7 @@ void PlatformKeyMap::UpdateLayout(HKL layout) { // Ignores legacy non-printable control characters. } } else { - // TODO(chongz): Handle rv <= -2 and rv >= 2. + // TODO(input-dev): Handle rv <= -2 and rv >= 2. } } } diff --git a/chromium/ui/events/keycodes/platform_key_map_win.h b/chromium/ui/events/keycodes/platform_key_map_win.h index bf0e19a72a7..2a8dc38b720 100644 --- a/chromium/ui/events/keycodes/platform_key_map_win.h +++ b/chromium/ui/events/keycodes/platform_key_map_win.h @@ -44,12 +44,12 @@ class EVENTS_EXPORT PlatformKeyMap { PlatformKeyMap(); - // TODO(chongz): Expose this function when we need to access separate layout. - // Returns the DomKey 'meaning' of |KeyboardCode| in the context of specified - // |EventFlags| and stored keyboard layout. + // TODO(input-dev): Expose this function when we need to access separate + // layout. Returns the DomKey 'meaning' of |KeyboardCode| in the context of + // specified |EventFlags| and stored keyboard layout. DomKey DomKeyFromKeyboardCodeImpl(KeyboardCode, int* flags) const; - // TODO(chongz): Expose this function in response to WM_INPUTLANGCHANGE. + // TODO(input-dev): Expose this function in response to WM_INPUTLANGCHANGE. void UpdateLayout(HKL layout); HKL keyboard_layout_ = 0; diff --git a/chromium/ui/events/keycodes/platform_key_map_win_unittest.cc b/chromium/ui/events/keycodes/platform_key_map_win_unittest.cc index f992eefc7cf..da2459ca174 100644 --- a/chromium/ui/events/keycodes/platform_key_map_win_unittest.cc +++ b/chromium/ui/events/keycodes/platform_key_map_win_unittest.cc @@ -265,8 +265,8 @@ TEST_F(PlatformKeyMapTest, NonPrintableKey) { int scan_code = ui::KeycodeConverter::DomCodeToNativeKeycode(test_case.dom_code); - // TODO(chongz): Some |scan_code| should map to different |key_code| based - // on modifiers. + // TODO(input-dev): Some |scan_code| should map to different |key_code| + // based on modifiers. KeyboardCode key_code = static_cast<KeyboardCode>( ::MapVirtualKeyEx(scan_code, MAPVK_VSC_TO_VK, layout)); diff --git a/chromium/ui/events/mojo/BUILD.gn b/chromium/ui/events/mojo/BUILD.gn index 9515ba479c4..a4523c50e25 100644 --- a/chromium/ui/events/mojo/BUILD.gn +++ b/chromium/ui/events/mojo/BUILD.gn @@ -13,18 +13,7 @@ mojom("interfaces") { public_deps = [ "//mojo/public/mojom/base", - "//ui/gfx/mojo", + "//ui/gfx/geometry/mojo", "//ui/latency/mojo:interfaces", ] } - -mojom("test_interfaces") { - testonly = true - sources = [ - "traits_test_service.mojom", - ] - - public_deps = [ - ":interfaces", - ] -} diff --git a/chromium/ui/events/mojo/event.mojom b/chromium/ui/events/mojo/event.mojom index b68dff4550b..9dafcc23621 100644 --- a/chromium/ui/events/mojo/event.mojom +++ b/chromium/ui/events/mojo/event.mojom @@ -7,6 +7,7 @@ module ui.mojom; import "mojo/public/mojom/base/time.mojom"; import "ui/events/mojo/event_constants.mojom"; import "ui/events/mojo/keyboard_codes.mojom"; +import "ui/gfx/geometry/mojo/geometry.mojom"; import "ui/latency/mojo/latency_info.mojom"; struct KeyData { @@ -46,23 +47,13 @@ struct KeyData { // Like |text|, but unmodified by concurrently held modifier keys (except // shift). Corresponds to blink::WebKeyboardEvent::unmodifiedText. uint16 unmodified_text; - - // Mirrors KeyEvent::properties_. - map<string, array<uint8>>? properties; }; struct LocationData { - // |x| and |y| are in the coordinate system of the View. - // Typically, this will be an integer-valued translation w.r.t. - // the screen and in this case, |x| and |y| are in units of physical - // pixels. However, some View embedders may apply arbitrary transformations - // of a view w.r.t. the screen. - float x; - float y; - // |screen_x| and |screen_y| are in screen coordinates in units of - // physical pixels. - float screen_x; - float screen_y; + // |relative_location| is in the coordinate system of the target and in DIPs. + gfx.mojom.PointF relative_location; + // |root_location| is relative to the client's root and in dips. + gfx.mojom.PointF root_location; }; // TODO(rjkroege,sadrul): Add gesture representation. @@ -143,6 +134,37 @@ struct ScrollData { ScrollEventPhase scroll_event_phase; }; +// This mirrors the C++ class of the same name, see it for details. +struct PointerDetails { + PointerKind pointer_type; + float radius_x; + float radius_y; + float force; + float tilt_x; + float tilt_y; + float tangential_pressure; + float twist; + int32 id; + int32 offset_x; + int32 offset_y; +}; + +struct MouseData { + int32 changed_button_flags; + LocationData location; + PointerDetails pointer_details; + // Only used for mouse wheel. + gfx.mojom.Vector2d wheel_offset; +}; + +// This is used for TouchEvents. +struct TouchData { + bool may_cause_scrolling; + bool hovering; + LocationData location; + PointerDetails pointer_details; +}; + struct Event { // TODO(sky): rename to type. EventType action; @@ -160,4 +182,7 @@ struct Event { PointerData? pointer_data; GestureData? gesture_data; ScrollData? scroll_data; + TouchData? touch_data; + MouseData? mouse_data; + map<string, array<uint8>>? properties; }; diff --git a/chromium/ui/events/mojo/event.typemap b/chromium/ui/events/mojo/event.typemap index d8fe8661fcb..a652049bb12 100644 --- a/chromium/ui/events/mojo/event.typemap +++ b/chromium/ui/events/mojo/event.typemap @@ -8,6 +8,7 @@ traits_headers = [ "//ui/events/mojo/event_struct_traits.h" ] public_deps = [ "//ui/events", "//ui/events:dom_keycode_converter", + "//ui/gfx/geometry/mojo", "//ui/latency/mojo:interfaces", ] deps = [ @@ -22,5 +23,6 @@ sources = [ type_mappings = [ "ui.mojom.Event=std::unique_ptr<ui::Event>[move_only]", "ui.mojom.EventMomentumPhase=ui::EventMomentumPhase", + "ui.mojom.PointerDetails=ui::PointerDetails", "ui.mojom.ScrollEventPhase=ui::ScrollEventPhase", ] diff --git a/chromium/ui/events/mojo/event_constants.mojom b/chromium/ui/events/mojo/event_constants.mojom index c7bd18fd4a2..e013bf06a5d 100644 --- a/chromium/ui/events/mojo/event_constants.mojom +++ b/chromium/ui/events/mojo/event_constants.mojom @@ -29,6 +29,21 @@ enum EventType { SCROLL_FLING_START, SCROLL_FLING_CANCEL, CANCEL_MODE, + // MOUSE_MOVED has a naming conflict in Windows, where there is a + // #define MOUSE_MOVED. Therefore we had to add the suffix, _EVENT. for + // consistency, all other mouse event types also were given this suffix. + MOUSE_PRESSED_EVENT, + MOUSE_DRAGGED_EVENT, + MOUSE_RELEASED_EVENT, + MOUSE_MOVED_EVENT, + MOUSE_ENTERED_EVENT, + MOUSE_EXITED_EVENT, + MOUSE_WHEEL_EVENT, + MOUSE_CAPTURE_CHANGED_EVENT, + TOUCH_RELEASED, + TOUCH_PRESSED, + TOUCH_MOVED, + TOUCH_CANCELLED, }; // This mirrors ui::EventFlags @@ -56,7 +71,9 @@ const int32 kMouseEventFlagIsNonClient = 0x20000; // TODO(erg): Move accessibility flags and maybe synthetic touch events here. +// TODO(sky): rename EventPointerType. enum PointerKind { + UNKNOWN, MOUSE, PEN, TOUCH, diff --git a/chromium/ui/events/mojo/event_struct_traits.cc b/chromium/ui/events/mojo/event_struct_traits.cc index 054e31bdab3..c5324bbc8f5 100644 --- a/chromium/ui/events/mojo/event_struct_traits.cc +++ b/chromium/ui/events/mojo/event_struct_traits.cc @@ -17,36 +17,31 @@ namespace mojo { namespace { -ui::mojom::LocationDataPtr GetLocationData(const ui::LocatedEvent* event) { +ui::mojom::LocationDataPtr CreateLocationData(const ui::LocatedEvent* event) { ui::mojom::LocationDataPtr location_data(ui::mojom::LocationData::New()); - location_data->x = event->location_f().x(); - location_data->y = event->location_f().y(); - location_data->screen_x = event->root_location_f().x(); - location_data->screen_y = event->root_location_f().y(); + location_data->relative_location = event->location_f(); + location_data->root_location = event->root_location_f(); return location_data; } -ui::EventPointerType PointerTypeFromPointerKind(ui::mojom::PointerKind kind) { - switch (kind) { - case ui::mojom::PointerKind::MOUSE: - return ui::EventPointerType::POINTER_TYPE_MOUSE; - case ui::mojom::PointerKind::TOUCH: - return ui::EventPointerType::POINTER_TYPE_TOUCH; - case ui::mojom::PointerKind::PEN: - return ui::EventPointerType::POINTER_TYPE_PEN; - case ui::mojom::PointerKind::ERASER: - return ui::EventPointerType::POINTER_TYPE_ERASER; - } - NOTREACHED(); - return ui::EventPointerType::POINTER_TYPE_UNKNOWN; +void UpdateEventLocation(const ui::mojom::PointerData& pointer_data, + EventUniquePtr* out) { + // Set the float location, as the constructor only takes a gfx::Point. + // This uses the event root_location field to store screen pixel + // coordinates. See http://crbug.com/608547 + out->get()->AsLocatedEvent()->set_location_f( + pointer_data.location->relative_location); + out->get()->AsLocatedEvent()->set_root_location_f( + pointer_data.location->root_location); } -bool ReadPointerDetails(ui::mojom::EventType event_type, - const ui::mojom::PointerData& pointer_data, - ui::PointerDetails* out) { +bool ReadPointerDetailsDeprecated(ui::mojom::EventType event_type, + const ui::mojom::PointerData& pointer_data, + ui::PointerDetails* out) { switch (pointer_data.kind) { case ui::mojom::PointerKind::MOUSE: { - if (event_type == ui::mojom::EventType::POINTER_WHEEL_CHANGED) { + if (event_type == ui::mojom::EventType::POINTER_WHEEL_CHANGED || + event_type == ui::mojom::EventType::MOUSE_WHEEL_EVENT) { *out = ui::PointerDetails( ui::EventPointerType::POINTER_TYPE_MOUSE, gfx::Vector2d(static_cast<int>(pointer_data.wheel_data->delta_x), @@ -60,18 +55,23 @@ bool ReadPointerDetails(ui::mojom::EventType event_type, } case ui::mojom::PointerKind::TOUCH: case ui::mojom::PointerKind::PEN: { + ui::EventPointerType pointer_type; + if (!EnumTraits<ui::mojom::PointerKind, ui::EventPointerType>::FromMojom( + pointer_data.kind, &pointer_type)) + return false; const ui::mojom::BrushData& brush_data = *pointer_data.brush_data; *out = ui::PointerDetails( - PointerTypeFromPointerKind(pointer_data.kind), - pointer_data.pointer_id, brush_data.width, brush_data.height, - brush_data.pressure, brush_data.twist, brush_data.tilt_x, - brush_data.tilt_y, brush_data.tangential_pressure); + pointer_type, pointer_data.pointer_id, brush_data.width, + brush_data.height, brush_data.pressure, brush_data.twist, + brush_data.tilt_x, brush_data.tilt_y, brush_data.tangential_pressure); return true; } case ui::mojom::PointerKind::ERASER: // TODO(jamescook): Eraser support. NOTIMPLEMENTED(); return false; + case ui::mojom::PointerKind::UNKNOWN: + return false; } NOTREACHED(); return false; @@ -86,7 +86,8 @@ bool ReadScrollData(ui::mojom::EventDataView* event, *out = std::make_unique<ui::ScrollEvent>( mojo::ConvertTo<ui::EventType>(event->action()), - gfx::Point(scroll_data->location->x, scroll_data->location->y), + gfx::Point(scroll_data->location->relative_location.x(), + scroll_data->location->relative_location.y()), time_stamp, event->flags(), scroll_data->x_offset, scroll_data->y_offset, scroll_data->x_offset_ordinal, scroll_data->y_offset_ordinal, scroll_data->finger_count, scroll_data->momentum_phase); @@ -101,8 +102,8 @@ bool ReadGestureData(ui::mojom::EventDataView* event, return false; *out = std::make_unique<ui::GestureEvent>( - gesture_data->location->x, gesture_data->location->y, event->flags(), - time_stamp, + gesture_data->location->relative_location.x(), + gesture_data->location->relative_location.y(), event->flags(), time_stamp, ui::GestureEventDetails(ConvertTo<ui::EventType>(event->action()))); return true; } @@ -193,6 +194,30 @@ ui::mojom::EventType TypeConverter<ui::mojom::EventType, return ui::mojom::EventType::SCROLL_FLING_CANCEL; case ui::ET_CANCEL_MODE: return ui::mojom::EventType::CANCEL_MODE; + case ui::ET_MOUSE_PRESSED: + return ui::mojom::EventType::MOUSE_PRESSED_EVENT; + case ui::ET_MOUSE_DRAGGED: + return ui::mojom::EventType::MOUSE_DRAGGED_EVENT; + case ui::ET_MOUSE_RELEASED: + return ui::mojom::EventType::MOUSE_RELEASED_EVENT; + case ui::ET_MOUSE_MOVED: + return ui::mojom::EventType::MOUSE_MOVED_EVENT; + case ui::ET_MOUSE_ENTERED: + return ui::mojom::EventType::MOUSE_ENTERED_EVENT; + case ui::ET_MOUSE_EXITED: + return ui::mojom::EventType::MOUSE_EXITED_EVENT; + case ui::ET_MOUSEWHEEL: + return ui::mojom::EventType::MOUSE_WHEEL_EVENT; + case ui::ET_MOUSE_CAPTURE_CHANGED: + return ui::mojom::EventType::MOUSE_CAPTURE_CHANGED_EVENT; + case ui::ET_TOUCH_RELEASED: + return ui::mojom::EventType::TOUCH_RELEASED; + case ui::ET_TOUCH_PRESSED: + return ui::mojom::EventType::TOUCH_PRESSED; + case ui::ET_TOUCH_MOVED: + return ui::mojom::EventType::TOUCH_MOVED; + case ui::ET_TOUCH_CANCELLED: + return ui::mojom::EventType::TOUCH_CANCELLED; default: NOTREACHED() << "Using unknown event types closes connections:" << ui::EventTypeName(type); @@ -235,35 +260,64 @@ ui::EventType TypeConverter<ui::EventType, ui::mojom::EventType>::Convert( return ui::ET_SCROLL_FLING_START; case ui::mojom::EventType::SCROLL_FLING_CANCEL: return ui::ET_SCROLL_FLING_CANCEL; + case ui::mojom::EventType::MOUSE_PRESSED_EVENT: + return ui::ET_MOUSE_PRESSED; + case ui::mojom::EventType::MOUSE_DRAGGED_EVENT: + return ui::ET_MOUSE_DRAGGED; + case ui::mojom::EventType::MOUSE_RELEASED_EVENT: + return ui::ET_MOUSE_RELEASED; + case ui::mojom::EventType::MOUSE_MOVED_EVENT: + return ui::ET_MOUSE_MOVED; + case ui::mojom::EventType::MOUSE_ENTERED_EVENT: + return ui::ET_MOUSE_ENTERED; + case ui::mojom::EventType::MOUSE_EXITED_EVENT: + return ui::ET_MOUSE_EXITED; + case ui::mojom::EventType::MOUSE_WHEEL_EVENT: + return ui::ET_MOUSEWHEEL; + case ui::mojom::EventType::MOUSE_CAPTURE_CHANGED_EVENT: + return ui::ET_MOUSE_CAPTURE_CHANGED; + case ui::mojom::EventType::TOUCH_RELEASED: + return ui::ET_TOUCH_RELEASED; + case ui::mojom::EventType::TOUCH_PRESSED: + return ui::ET_TOUCH_PRESSED; + case ui::mojom::EventType::TOUCH_MOVED: + return ui::ET_TOUCH_MOVED; + case ui::mojom::EventType::TOUCH_CANCELLED: + return ui::ET_TOUCH_CANCELLED; default: NOTREACHED(); } return ui::ET_UNKNOWN; } +// static ui::mojom::EventType StructTraits<ui::mojom::EventDataView, EventUniquePtr>::action( const EventUniquePtr& event) { return mojo::ConvertTo<ui::mojom::EventType>(event->type()); } +// static int32_t StructTraits<ui::mojom::EventDataView, EventUniquePtr>::flags( const EventUniquePtr& event) { return event->flags(); } +// static base::TimeTicks StructTraits<ui::mojom::EventDataView, EventUniquePtr>::time_stamp( const EventUniquePtr& event) { return event->time_stamp(); } +// static const ui::LatencyInfo& StructTraits<ui::mojom::EventDataView, EventUniquePtr>::latency( const EventUniquePtr& event) { return *event->latency(); } +// static ui::mojom::KeyDataPtr StructTraits<ui::mojom::EventDataView, EventUniquePtr>::key_data( const EventUniquePtr& event) { @@ -281,23 +335,22 @@ StructTraits<ui::mojom::EventDataView, EventUniquePtr>::key_data( key_event->GetLocatedWindowsKeyboardCode()); key_data->text = key_event->GetText(); key_data->unmodified_text = key_event->GetUnmodifiedText(); - if (key_event->properties()) - key_data->properties = *(key_event->properties()); - return key_data; } +// static ui::mojom::PointerDataPtr StructTraits<ui::mojom::EventDataView, EventUniquePtr>::pointer_data( const EventUniquePtr& event) { if (!event->IsPointerEvent()) return nullptr; - const ui::PointerEvent* pointer_event = event->AsPointerEvent(); ui::mojom::PointerDataPtr pointer_data(ui::mojom::PointerData::New()); - pointer_data->pointer_id = pointer_event->pointer_details().id; - pointer_data->changed_button_flags = pointer_event->changed_button_flags(); + const ui::PointerEvent* pointer_event = event->AsPointerEvent(); const ui::PointerDetails* pointer_details = &pointer_event->pointer_details(); + pointer_data->changed_button_flags = pointer_event->changed_button_flags(); + pointer_data->location = CreateLocationData(event->AsLocatedEvent()); + pointer_data->pointer_id = pointer_details->id; ui::EventPointerType pointer_type = pointer_details->pointer_type; switch (pointer_type) { @@ -335,8 +388,6 @@ StructTraits<ui::mojom::EventDataView, EventUniquePtr>::pointer_data( // TODO(rjkroege): Handle force-touch on MacOS // TODO(rjkroege): Adjust brush data appropriately for Android. - pointer_data->location = GetLocationData(event->AsLocatedEvent()); - if (event->type() == ui::ET_POINTER_WHEEL_CHANGED) { ui::mojom::WheelDataPtr wheel_data(ui::mojom::WheelData::New()); @@ -362,6 +413,24 @@ StructTraits<ui::mojom::EventDataView, EventUniquePtr>::pointer_data( return pointer_data; } +// static +ui::mojom::MouseDataPtr +StructTraits<ui::mojom::EventDataView, EventUniquePtr>::mouse_data( + const EventUniquePtr& event) { + if (!event->IsMouseEvent()) + return nullptr; + + const ui::MouseEvent* mouse_event = event->AsMouseEvent(); + ui::mojom::MouseDataPtr mouse_data(ui::mojom::MouseData::New()); + mouse_data->changed_button_flags = mouse_event->changed_button_flags(); + mouse_data->pointer_details = mouse_event->pointer_details(); + mouse_data->location = CreateLocationData(mouse_event); + if (mouse_event->IsMouseWheelEvent()) + mouse_data->wheel_offset = mouse_event->AsMouseWheelEvent()->offset(); + return mouse_data; +} + +// static ui::mojom::GestureDataPtr StructTraits<ui::mojom::EventDataView, EventUniquePtr>::gesture_data( const EventUniquePtr& event) { @@ -369,10 +438,11 @@ StructTraits<ui::mojom::EventDataView, EventUniquePtr>::gesture_data( return nullptr; ui::mojom::GestureDataPtr gesture_data(ui::mojom::GestureData::New()); - gesture_data->location = GetLocationData(event->AsLocatedEvent()); + gesture_data->location = CreateLocationData(event->AsLocatedEvent()); return gesture_data; } +// static ui::mojom::ScrollDataPtr StructTraits<ui::mojom::EventDataView, EventUniquePtr>::scroll_data( const EventUniquePtr& event) { @@ -380,7 +450,7 @@ StructTraits<ui::mojom::EventDataView, EventUniquePtr>::scroll_data( return nullptr; ui::mojom::ScrollDataPtr scroll_data(ui::mojom::ScrollData::New()); - scroll_data->location = GetLocationData(event->AsLocatedEvent()); + scroll_data->location = CreateLocationData(event->AsLocatedEvent()); const ui::ScrollEvent* scroll_event = event->AsScrollEvent(); scroll_data->x_offset = scroll_event->x_offset(); scroll_data->y_offset = scroll_event->y_offset(); @@ -391,6 +461,30 @@ StructTraits<ui::mojom::EventDataView, EventUniquePtr>::scroll_data( return scroll_data; } +// static +ui::mojom::TouchDataPtr +StructTraits<ui::mojom::EventDataView, EventUniquePtr>::touch_data( + const EventUniquePtr& event) { + if (!event->IsTouchEvent()) + return nullptr; + + const ui::TouchEvent* touch_event = event->AsTouchEvent(); + ui::mojom::TouchDataPtr touch_data(ui::mojom::TouchData::New()); + touch_data->may_cause_scrolling = touch_event->may_cause_scrolling(); + touch_data->hovering = touch_event->hovering(); + touch_data->location = CreateLocationData(touch_event); + touch_data->pointer_details = touch_event->pointer_details(); + return touch_data; +} + +// static +base::flat_map<std::string, std::vector<uint8_t>> +StructTraits<ui::mojom::EventDataView, EventUniquePtr>::properties( + const EventUniquePtr& event) { + return event->properties() ? *(event->properties()) : ui::Event::Properties(); +} + +// static bool StructTraits<ui::mojom::EventDataView, EventUniquePtr>::Read( ui::mojom::EventDataView event, EventUniquePtr* out) { @@ -420,8 +514,6 @@ bool StructTraits<ui::mojom::EventDataView, EventUniquePtr>::Read( static_cast<ui::KeyboardCode>(key_data->key_code), event.flags(), time_stamp); } - if (key_data->properties) - (*out)->AsKeyEvent()->SetProperties(*key_data->properties); break; } case ui::mojom::EventType::POINTER_DOWN: @@ -437,19 +529,16 @@ bool StructTraits<ui::mojom::EventDataView, EventUniquePtr>::Read( return false; ui::PointerDetails pointer_details; - if (!ReadPointerDetails(event.action(), *pointer_data, &pointer_details)) + if (!ReadPointerDetailsDeprecated(event.action(), *pointer_data, + &pointer_details)) return false; - const gfx::Point location(pointer_data->location->x, - pointer_data->location->y); - const gfx::Point screen_location(pointer_data->location->screen_x, - pointer_data->location->screen_y); - // This uses the event root_location field to store screen pixel - // coordinates. See http://crbug.com/608547 *out = std::make_unique<ui::PointerEvent>( - mojo::ConvertTo<ui::EventType>(event.action()), location, - screen_location, event.flags(), pointer_data->changed_button_flags, + mojo::ConvertTo<ui::EventType>(event.action()), gfx::Point(), + gfx::Point(), event.flags(), pointer_data->changed_button_flags, pointer_details, time_stamp); + + UpdateEventLocation(*pointer_data, out); break; } case ui::mojom::EventType::GESTURE_TAP: @@ -474,6 +563,57 @@ bool StructTraits<ui::mojom::EventDataView, EventUniquePtr>::Read( case ui::mojom::EventType::CANCEL_MODE: *out = std::make_unique<ui::CancelModeEvent>(); break; + case ui::mojom::EventType::MOUSE_PRESSED_EVENT: + case ui::mojom::EventType::MOUSE_RELEASED_EVENT: + case ui::mojom::EventType::MOUSE_DRAGGED_EVENT: + case ui::mojom::EventType::MOUSE_MOVED_EVENT: + case ui::mojom::EventType::MOUSE_ENTERED_EVENT: + case ui::mojom::EventType::MOUSE_EXITED_EVENT: + case ui::mojom::EventType::MOUSE_WHEEL_EVENT: + case ui::mojom::EventType::MOUSE_CAPTURE_CHANGED_EVENT: { + ui::mojom::MouseDataPtr mouse_data; + if (!event.ReadMouseData(&mouse_data)) + return false; + + std::unique_ptr<ui::MouseEvent> mouse_event; + if (event.action() == ui::mojom::EventType::MOUSE_WHEEL_EVENT) { + mouse_event = std::make_unique<ui::MouseWheelEvent>( + mouse_data->wheel_offset, + gfx::Point(), // Real location set below. + gfx::Point(), // Real location set below. + time_stamp, event.flags(), mouse_data->changed_button_flags); + } else { + mouse_event = std::make_unique<ui::MouseEvent>( + mojo::ConvertTo<ui::EventType>(event.action()), + gfx::Point(), // Real location set below. + gfx::Point(), // Real location set below. + time_stamp, event.flags(), mouse_data->changed_button_flags, + mouse_data->pointer_details); + } + mouse_event->set_location_f(mouse_data->location->relative_location); + mouse_event->set_root_location_f(mouse_data->location->root_location); + *out = std::move(mouse_event); + break; + } + case ui::mojom::EventType::TOUCH_RELEASED: + case ui::mojom::EventType::TOUCH_PRESSED: + case ui::mojom::EventType::TOUCH_MOVED: + case ui::mojom::EventType::TOUCH_CANCELLED: { + ui::mojom::TouchDataPtr touch_data; + if (!event.ReadTouchData(&touch_data)) + return false; + std::unique_ptr<ui::TouchEvent> touch_event = + std::make_unique<ui::TouchEvent>( + mojo::ConvertTo<ui::EventType>(event.action()), + gfx::Point(), // Real location set below. + time_stamp, touch_data->pointer_details, event.flags()); + touch_event->set_location_f(touch_data->location->relative_location); + touch_event->set_root_location_f(touch_data->location->root_location); + touch_event->set_may_cause_scrolling(touch_data->may_cause_scrolling); + touch_event->set_hovering(touch_data->hovering); + *out = std::move(touch_event); + break; + } case ui::mojom::EventType::UNKNOWN: NOTREACHED() << "Using unknown event types closes connections"; return false; @@ -482,7 +622,35 @@ bool StructTraits<ui::mojom::EventDataView, EventUniquePtr>::Read( if (!out->get()) return false; - return event.ReadLatency((*out)->latency()); + if (!event.ReadLatency((*out)->latency())) + return false; + + ui::Event::Properties properties; + if (!event.ReadProperties(&properties)) + return false; + if (!properties.empty()) + (*out)->SetProperties(properties); + + return true; +} + +// static +bool StructTraits<ui::mojom::PointerDetailsDataView, ui::PointerDetails>::Read( + ui::mojom::PointerDetailsDataView data, + ui::PointerDetails* out) { + if (!data.ReadPointerType(&out->pointer_type)) + return false; + out->radius_x = data.radius_x(); + out->radius_y = data.radius_y(); + out->force = data.force(); + out->tilt_x = data.tilt_x(); + out->tilt_y = data.tilt_y(); + out->tangential_pressure = data.tangential_pressure(); + out->twist = data.twist(); + out->id = data.id(); + out->offset.set_x(data.offset_x()); + out->offset.set_y(data.offset_y()); + return true; } } // namespace mojo diff --git a/chromium/ui/events/mojo/event_struct_traits.h b/chromium/ui/events/mojo/event_struct_traits.h index 68b5680775d..1e5d3fb1bbb 100644 --- a/chromium/ui/events/mojo/event_struct_traits.h +++ b/chromium/ui/events/mojo/event_struct_traits.h @@ -5,6 +5,11 @@ #ifndef UI_EVENTS_MOJO_EVENT_STRUCT_TRAITS_H_ #define UI_EVENTS_MOJO_EVENT_STRUCT_TRAITS_H_ +#include <stdint.h> + +#include <string> + +#include "base/containers/flat_map.h" #include "mojo/public/cpp/bindings/type_converter.h" #include "ui/events/event_constants.h" #include "ui/events/mojo/event.mojom.h" @@ -13,6 +18,7 @@ namespace ui { class Event; class LatencyInfo; +struct PointerDetails; } namespace mojo { @@ -39,6 +45,10 @@ struct StructTraits<ui::mojom::EventDataView, EventUniquePtr> { static ui::mojom::PointerDataPtr pointer_data(const EventUniquePtr& event); static ui::mojom::GestureDataPtr gesture_data(const EventUniquePtr& event); static ui::mojom::ScrollDataPtr scroll_data(const EventUniquePtr& event); + static ui::mojom::TouchDataPtr touch_data(const EventUniquePtr& event); + static ui::mojom::MouseDataPtr mouse_data(const EventUniquePtr& event); + static base::flat_map<std::string, std::vector<uint8_t>> properties( + const EventUniquePtr& event); static bool Read(ui::mojom::EventDataView r, EventUniquePtr* out); }; @@ -86,6 +96,71 @@ struct EnumTraits<ui::mojom::EventMomentumPhase, ui::EventMomentumPhase> { }; template <> +struct EnumTraits<ui::mojom::PointerKind, ui::EventPointerType> { + static ui::mojom::PointerKind ToMojom(ui::EventPointerType input) { + switch (input) { + case ui::EventPointerType::POINTER_TYPE_UNKNOWN: + return ui::mojom::PointerKind::UNKNOWN; + case ui::EventPointerType::POINTER_TYPE_MOUSE: + return ui::mojom::PointerKind::MOUSE; + case ui::EventPointerType::POINTER_TYPE_PEN: + return ui::mojom::PointerKind::PEN; + case ui::EventPointerType::POINTER_TYPE_TOUCH: + return ui::mojom::PointerKind::TOUCH; + case ui::EventPointerType::POINTER_TYPE_ERASER: + return ui::mojom::PointerKind::ERASER; + } + NOTREACHED(); + return ui::mojom::PointerKind::UNKNOWN; + } + + static bool FromMojom(ui::mojom::PointerKind input, + ui::EventPointerType* out) { + switch (input) { + case ui::mojom::PointerKind::UNKNOWN: + *out = ui::EventPointerType::POINTER_TYPE_UNKNOWN; + return true; + case ui::mojom::PointerKind::MOUSE: + *out = ui::EventPointerType::POINTER_TYPE_MOUSE; + return true; + case ui::mojom::PointerKind::PEN: + *out = ui::EventPointerType::POINTER_TYPE_PEN; + return true; + case ui::mojom::PointerKind::TOUCH: + *out = ui::EventPointerType::POINTER_TYPE_TOUCH; + return true; + case ui::mojom::PointerKind::ERASER: + *out = ui::EventPointerType::POINTER_TYPE_ERASER; + return true; + } + NOTREACHED(); + return false; + } +}; + +template <> +struct StructTraits<ui::mojom::PointerDetailsDataView, ui::PointerDetails> { + static ui::EventPointerType pointer_type(const ui::PointerDetails& i) { + return i.pointer_type; + } + static float radius_x(const ui::PointerDetails& i) { return i.radius_x; } + static float radius_y(const ui::PointerDetails& i) { return i.radius_y; } + static float force(const ui::PointerDetails& i) { return i.force; } + static float tilt_x(const ui::PointerDetails& i) { return i.tilt_x; } + static float tilt_y(const ui::PointerDetails& i) { return i.tilt_y; } + static float tangential_pressure(const ui::PointerDetails& i) { + return i.tangential_pressure; + } + static float twist(const ui::PointerDetails& i) { return i.twist; } + static int32_t id(const ui::PointerDetails& i) { return i.id; } + static int32_t offset_x(const ui::PointerDetails& i) { return i.offset.x(); } + static int32_t offset_y(const ui::PointerDetails& i) { return i.offset.y(); } + + static bool Read(ui::mojom::PointerDetailsDataView data, + ui::PointerDetails* out); +}; + +template <> struct EnumTraits<ui::mojom::ScrollEventPhase, ui::ScrollEventPhase> { static ui::mojom::ScrollEventPhase ToMojom(ui::ScrollEventPhase input) { switch (input) { diff --git a/chromium/ui/events/mojo/struct_traits_unittest.cc b/chromium/ui/events/mojo/struct_traits_unittest.cc index 1a7bdbb8a85..2c4d62b7b32 100644 --- a/chromium/ui/events/mojo/struct_traits_unittest.cc +++ b/chromium/ui/events/mojo/struct_traits_unittest.cc @@ -4,74 +4,97 @@ #include <utility> -#include "base/message_loop/message_loop.h" +#include "base/stl_util.h" #include "mojo/public/cpp/base/time_mojom_traits.h" -#include "mojo/public/cpp/bindings/binding_set.h" +#include "mojo/public/cpp/test_support/test_utils.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/events/event.h" #include "ui/events/keycodes/dom/dom_code.h" #include "ui/events/mojo/event.mojom.h" #include "ui/events/mojo/event_struct_traits.h" -#include "ui/events/mojo/traits_test_service.mojom.h" +#include "ui/gfx/geometry/mojo/geometry_struct_traits.h" #include "ui/latency/mojo/latency_info_struct_traits.h" namespace ui { namespace { -class StructTraitsTest : public testing::Test, public mojom::TraitsTestService { - public: - StructTraitsTest() {} - ~StructTraitsTest() override = default; +void ExpectTouchEventsEqual(const TouchEvent& expected, + const TouchEvent& actual) { + EXPECT_EQ(expected.may_cause_scrolling(), actual.may_cause_scrolling()); + EXPECT_EQ(expected.hovering(), actual.hovering()); + EXPECT_EQ(expected.pointer_details(), actual.pointer_details()); +} - protected: - mojom::TraitsTestServicePtr GetTraitsTestProxy() { - mojom::TraitsTestServicePtr proxy; - traits_test_bindings_.AddBinding(this, mojo::MakeRequest(&proxy)); - return proxy; - } +void ExpectLocatedEventsEqual(const LocatedEvent& expected, + const LocatedEvent& actual) { + EXPECT_EQ(expected.location_f(), actual.location_f()); + EXPECT_EQ(expected.root_location_f(), actual.root_location_f()); +} - private: - // TraitsTestService: - void EchoEvent(std::unique_ptr<ui::Event> e, - EchoEventCallback callback) override { - std::move(callback).Run(std::move(e)); - } +void ExpectMouseEventsEqual(const MouseEvent& expected, + const MouseEvent& actual) { + EXPECT_EQ(expected.pointer_details(), actual.pointer_details()); + EXPECT_EQ(expected.changed_button_flags(), actual.changed_button_flags()); +} - base::MessageLoop loop_; - mojo::BindingSet<TraitsTestService> traits_test_bindings_; - DISALLOW_COPY_AND_ASSIGN(StructTraitsTest); -}; +void ExpectMouseWheelEventsEqual(const MouseWheelEvent& expected, + const MouseWheelEvent& actual) { + EXPECT_EQ(expected.offset(), actual.offset()); +} + +void ExpectEventsEqual(const Event& expected, const Event& actual) { + EXPECT_EQ(expected.type(), actual.type()); + EXPECT_EQ(expected.time_stamp(), actual.time_stamp()); + EXPECT_EQ(expected.flags(), actual.flags()); + if (expected.IsLocatedEvent()) { + ASSERT_TRUE(actual.IsLocatedEvent()); + ExpectLocatedEventsEqual(*expected.AsLocatedEvent(), + *actual.AsLocatedEvent()); + } + if (expected.IsMouseEvent()) { + ASSERT_TRUE(actual.IsMouseEvent()); + ExpectMouseEventsEqual(*expected.AsMouseEvent(), *actual.AsMouseEvent()); + } + if (expected.IsMouseWheelEvent()) { + ASSERT_TRUE(actual.IsMouseWheelEvent()); + ExpectMouseWheelEventsEqual(*expected.AsMouseWheelEvent(), + *actual.AsMouseWheelEvent()); + } + if (expected.IsTouchEvent()) { + ASSERT_TRUE(actual.IsTouchEvent()); + ExpectTouchEventsEqual(*expected.AsTouchEvent(), *actual.AsTouchEvent()); + } +} } // namespace -TEST_F(StructTraitsTest, KeyEvent) { - KeyEvent kTestData[] = { +TEST(StructTraitsTest, KeyEvent) { + const KeyEvent kTestData[] = { {ET_KEY_PRESSED, VKEY_RETURN, EF_CONTROL_DOWN}, {ET_KEY_PRESSED, VKEY_MENU, EF_ALT_DOWN}, {ET_KEY_RELEASED, VKEY_SHIFT, EF_SHIFT_DOWN}, {ET_KEY_RELEASED, VKEY_MENU, EF_ALT_DOWN}, - {ET_KEY_PRESSED, VKEY_A, ui::DomCode::US_A, EF_NONE}, - {ET_KEY_PRESSED, VKEY_B, ui::DomCode::US_B, - EF_CONTROL_DOWN | EF_ALT_DOWN}, - {'\x12', VKEY_2, ui::DomCode::NONE, EF_CONTROL_DOWN}, - {'Z', VKEY_Z, ui::DomCode::NONE, EF_CAPS_LOCK_ON}, - {'z', VKEY_Z, ui::DomCode::NONE, EF_NONE}, + {ET_KEY_PRESSED, VKEY_A, DomCode::US_A, EF_NONE}, + {ET_KEY_PRESSED, VKEY_B, DomCode::US_B, EF_CONTROL_DOWN | EF_ALT_DOWN}, + {'\x12', VKEY_2, DomCode::NONE, EF_CONTROL_DOWN}, + {'Z', VKEY_Z, DomCode::NONE, EF_CAPS_LOCK_ON}, + {'z', VKEY_Z, DomCode::NONE, EF_NONE}, {ET_KEY_PRESSED, VKEY_Z, EF_NONE, base::TimeTicks() + base::TimeDelta::FromMicroseconds(101)}, - {'Z', VKEY_Z, ui::DomCode::NONE, EF_NONE, + {'Z', VKEY_Z, DomCode::NONE, EF_NONE, base::TimeTicks() + base::TimeDelta::FromMicroseconds(102)}, }; - mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy(); - for (size_t i = 0; i < arraysize(kTestData); i++) { + for (size_t i = 0; i < base::size(kTestData); i++) { + std::unique_ptr<Event> expected_copy = Event::Clone(kTestData[i]); std::unique_ptr<Event> output; - proxy->EchoEvent(Event::Clone(kTestData[i]), &output); + ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::Event>( + &expected_copy, &output)); EXPECT_TRUE(output->IsKeyEvent()); const KeyEvent* output_key_event = output->AsKeyEvent(); - EXPECT_EQ(kTestData[i].type(), output_key_event->type()); - EXPECT_EQ(kTestData[i].flags(), output_key_event->flags()); + ExpectEventsEqual(kTestData[i], *output_key_event); EXPECT_EQ(kTestData[i].GetCharacter(), output_key_event->GetCharacter()); EXPECT_EQ(kTestData[i].GetUnmodifiedText(), output_key_event->GetUnmodifiedText()); @@ -81,12 +104,11 @@ TEST_F(StructTraitsTest, KeyEvent) { EXPECT_EQ(kTestData[i].GetConflatedWindowsKeyCode(), output_key_event->GetConflatedWindowsKeyCode()); EXPECT_EQ(kTestData[i].code(), output_key_event->code()); - EXPECT_EQ(kTestData[i].time_stamp(), output_key_event->time_stamp()); } } -TEST_F(StructTraitsTest, PointerEvent) { - PointerEvent kTestData[] = { +TEST(StructTraitsTest, PointerEvent) { + const PointerEvent kTestData[] = { // Mouse pointer events: {ET_POINTER_DOWN, gfx::Point(10, 10), gfx::Point(20, 30), EF_NONE, 0, PointerDetails(EventPointerType::POINTER_TYPE_MOUSE, @@ -168,10 +190,11 @@ TEST_F(StructTraitsTest, PointerEvent) { base::TimeTicks() + base::TimeDelta::FromMicroseconds(211)}, }; - mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy(); - for (size_t i = 0; i < arraysize(kTestData); i++) { + for (size_t i = 0; i < base::size(kTestData); i++) { + std::unique_ptr<Event> expected_copy = Event::Clone(kTestData[i]); std::unique_ptr<Event> output; - proxy->EchoEvent(Event::Clone(kTestData[i]), &output); + ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::Event>( + &expected_copy, &output)); EXPECT_TRUE(output->IsPointerEvent()); const PointerEvent* output_ptr_event = output->AsPointerEvent(); @@ -189,8 +212,56 @@ TEST_F(StructTraitsTest, PointerEvent) { } } -TEST_F(StructTraitsTest, PointerWheelEvent) { - MouseWheelEvent kTestData[] = { +TEST(StructTraitsTest, MouseEvent) { + const MouseEvent kTestData[] = { + {ET_MOUSE_PRESSED, gfx::Point(10, 10), gfx::Point(20, 30), + base::TimeTicks() + base::TimeDelta::FromMicroseconds(201), EF_NONE, 0, + PointerDetails(EventPointerType::POINTER_TYPE_MOUSE, + MouseEvent::kMousePointerId)}, + {ET_MOUSE_DRAGGED, gfx::Point(1, 5), gfx::Point(5, 1), + base::TimeTicks() + base::TimeDelta::FromMicroseconds(202), + EF_LEFT_MOUSE_BUTTON, EF_LEFT_MOUSE_BUTTON, + PointerDetails(EventPointerType::POINTER_TYPE_MOUSE, + MouseEvent::kMousePointerId)}, + {ET_MOUSE_RELEASED, gfx::Point(411, 130), gfx::Point(20, 30), + base::TimeTicks() + base::TimeDelta::FromMicroseconds(203), + EF_MIDDLE_MOUSE_BUTTON | EF_RIGHT_MOUSE_BUTTON, EF_RIGHT_MOUSE_BUTTON, + PointerDetails(EventPointerType::POINTER_TYPE_MOUSE, + MouseEvent::kMousePointerId)}, + {ET_MOUSE_MOVED, gfx::Point(0, 1), gfx::Point(2, 3), + base::TimeTicks() + base::TimeDelta::FromMicroseconds(204), EF_ALT_DOWN, + 0, + PointerDetails(EventPointerType::POINTER_TYPE_MOUSE, + MouseEvent::kMousePointerId)}, + {ET_MOUSE_ENTERED, gfx::Point(6, 7), gfx::Point(8, 9), + base::TimeTicks() + base::TimeDelta::FromMicroseconds(205), EF_NONE, 0, + PointerDetails(EventPointerType::POINTER_TYPE_MOUSE, + MouseEvent::kMousePointerId)}, + {ET_MOUSE_EXITED, gfx::Point(10, 10), gfx::Point(20, 30), + base::TimeTicks() + base::TimeDelta::FromMicroseconds(206), + EF_BACK_MOUSE_BUTTON, 0, + PointerDetails(EventPointerType::POINTER_TYPE_MOUSE, + MouseEvent::kMousePointerId)}, + {ET_MOUSE_CAPTURE_CHANGED, gfx::Point(99, 99), gfx::Point(99, 99), + base::TimeTicks() + base::TimeDelta::FromMicroseconds(207), + EF_CONTROL_DOWN, 0, + PointerDetails(EventPointerType::POINTER_TYPE_MOUSE, + MouseEvent::kMousePointerId)}, + }; + + for (size_t i = 0; i < base::size(kTestData); i++) { + std::unique_ptr<Event> expected_copy = Event::Clone(kTestData[i]); + std::unique_ptr<Event> output; + ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::Event>( + &expected_copy, &output)); + ASSERT_TRUE(output->IsMouseEvent()); + + ExpectEventsEqual(kTestData[i], *output); + } +} + +TEST(StructTraitsTest, PointerWheelEvent) { + const MouseWheelEvent kTestData[] = { {gfx::Vector2d(11, 15), gfx::Point(3, 4), gfx::Point(40, 30), base::TimeTicks() + base::TimeDelta::FromMicroseconds(301), EF_LEFT_MOUSE_BUTTON, EF_LEFT_MOUSE_BUTTON}, @@ -203,10 +274,12 @@ TEST_F(StructTraitsTest, PointerWheelEvent) { EF_NONE}, }; - mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy(); - for (size_t i = 0; i < arraysize(kTestData); i++) { + for (size_t i = 0; i < base::size(kTestData); i++) { + std::unique_ptr<Event> expected_copy = + std::make_unique<PointerEvent>(kTestData[i]); std::unique_ptr<Event> output; - proxy->EchoEvent(Event::Clone(ui::PointerEvent(kTestData[i])), &output); + ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::Event>( + &expected_copy, &output)); EXPECT_EQ(ET_POINTER_WHEEL_CHANGED, output->type()); const PointerEvent* output_pointer_event = output->AsPointerEvent(); @@ -221,7 +294,54 @@ TEST_F(StructTraitsTest, PointerWheelEvent) { } } -TEST_F(StructTraitsTest, KeyEventPropertiesSerialized) { +TEST(StructTraitsTest, MouseWheelEvent) { + const MouseWheelEvent kTestData[] = { + {gfx::Vector2d(11, 15), gfx::Point(3, 4), gfx::Point(40, 30), + base::TimeTicks() + base::TimeDelta::FromMicroseconds(301), + EF_LEFT_MOUSE_BUTTON, EF_LEFT_MOUSE_BUTTON}, + {gfx::Vector2d(-5, 3), gfx::Point(40, 3), gfx::Point(4, 0), + base::TimeTicks() + base::TimeDelta::FromMicroseconds(302), + EF_MIDDLE_MOUSE_BUTTON | EF_RIGHT_MOUSE_BUTTON, + EF_MIDDLE_MOUSE_BUTTON | EF_RIGHT_MOUSE_BUTTON}, + {gfx::Vector2d(1, 0), gfx::Point(3, 4), gfx::Point(40, 30), + base::TimeTicks() + base::TimeDelta::FromMicroseconds(303), EF_NONE, + EF_NONE}, + }; + + for (size_t i = 0; i < base::size(kTestData); i++) { + std::unique_ptr<Event> expected_copy = + std::make_unique<MouseWheelEvent>(PointerEvent(kTestData[i])); + std::unique_ptr<Event> output; + ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::Event>( + &expected_copy, &output)); + ASSERT_EQ(ET_MOUSEWHEEL, output->type()); + + const MouseWheelEvent* output_event = output->AsMouseWheelEvent(); + // TODO(sky): make this use ExpectEventsEqual(). + ExpectMouseWheelEventsEqual(kTestData[i], *output_event); + } +} + +TEST(StructTraitsTest, FloatingPointLocations) { + MouseEvent input_event( + ET_MOUSE_PRESSED, gfx::Point(10, 10), gfx::Point(20, 30), + base::TimeTicks() + base::TimeDelta::FromMicroseconds(201), EF_NONE, 0, + PointerDetails(EventPointerType::POINTER_TYPE_MOUSE, + MouseEvent::kMousePointerId)); + + input_event.set_location_f(gfx::PointF(10.1, 10.2)); + input_event.set_root_location_f(gfx::PointF(20.2, 30.3)); + + std::unique_ptr<Event> expected_copy = Event::Clone(input_event); + std::unique_ptr<Event> output; + ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::Event>(&expected_copy, + &output)); + ASSERT_TRUE(output->IsMouseEvent()); + + ExpectEventsEqual(input_event, *output->AsMouseEvent()); +} + +TEST(StructTraitsTest, KeyEventPropertiesSerialized) { KeyEvent key_event(ET_KEY_PRESSED, VKEY_T, EF_NONE); const std::string key = "key"; const std::vector<uint8_t> value(0xCD, 2); @@ -238,8 +358,8 @@ TEST_F(StructTraitsTest, KeyEventPropertiesSerialized) { EXPECT_EQ(properties, *(deserialized->AsKeyEvent()->properties())); } -TEST_F(StructTraitsTest, GestureEvent) { - GestureEvent kTestData[] = { +TEST(StructTraitsTest, GestureEvent) { + const GestureEvent kTestData[] = { {10, 20, EF_NONE, base::TimeTicks() + base::TimeDelta::FromMicroseconds(401), GestureEventDetails(ET_SCROLL_FLING_START)}, @@ -248,26 +368,23 @@ TEST_F(StructTraitsTest, GestureEvent) { GestureEventDetails(ET_GESTURE_TAP)}, }; - mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy(); - for (size_t i = 0; i < arraysize(kTestData); i++) { + for (size_t i = 0; i < base::size(kTestData); i++) { + std::unique_ptr<Event> expected_copy = Event::Clone(kTestData[i]); std::unique_ptr<Event> output; - proxy->EchoEvent(Event::Clone(kTestData[i]), &output); + ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::Event>( + &expected_copy, &output)); EXPECT_TRUE(output->IsGestureEvent()); const GestureEvent* output_ptr_event = output->AsGestureEvent(); - EXPECT_EQ(kTestData[i].type(), output_ptr_event->type()); - EXPECT_EQ(kTestData[i].flags(), output_ptr_event->flags()); - EXPECT_EQ(kTestData[i].location(), output_ptr_event->location()); - EXPECT_EQ(kTestData[i].root_location(), output_ptr_event->root_location()); + ExpectEventsEqual(kTestData[i], *output); EXPECT_EQ(kTestData[i].details(), output_ptr_event->details()); EXPECT_EQ(kTestData[i].unique_touch_event_id(), output_ptr_event->unique_touch_event_id()); - EXPECT_EQ(kTestData[i].time_stamp(), output_ptr_event->time_stamp()); } } -TEST_F(StructTraitsTest, ScrollEvent) { - ScrollEvent kTestData[] = { +TEST(StructTraitsTest, ScrollEvent) { + const ScrollEvent kTestData[] = { {ET_SCROLL, gfx::Point(10, 20), base::TimeTicks() + base::TimeDelta::FromMicroseconds(501), EF_NONE, 1, 2, 3, 4, 5, EventMomentumPhase::NONE, ScrollEventPhase::kNone}, @@ -298,17 +415,15 @@ TEST_F(StructTraitsTest, ScrollEvent) { 2, 3, 4, 5, EventMomentumPhase::END, ScrollEventPhase::kNone}, }; - mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy(); - for (size_t i = 0; i < arraysize(kTestData); i++) { + for (size_t i = 0; i < base::size(kTestData); i++) { + std::unique_ptr<Event> expected_copy = Event::Clone(kTestData[i]); std::unique_ptr<Event> output; - proxy->EchoEvent(Event::Clone(kTestData[i]), &output); + ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::Event>( + &expected_copy, &output)); EXPECT_TRUE(output->IsScrollEvent()); const ScrollEvent* output_ptr_event = output->AsScrollEvent(); - EXPECT_EQ(kTestData[i].type(), output_ptr_event->type()); - EXPECT_EQ(kTestData[i].location(), output_ptr_event->location()); - EXPECT_EQ(kTestData[i].time_stamp(), output_ptr_event->time_stamp()); - EXPECT_EQ(kTestData[i].flags(), output_ptr_event->flags()); + ExpectEventsEqual(kTestData[i], *output_ptr_event); EXPECT_EQ(kTestData[i].x_offset(), output_ptr_event->x_offset()); EXPECT_EQ(kTestData[i].y_offset(), output_ptr_event->y_offset()); EXPECT_EQ(kTestData[i].x_offset_ordinal(), @@ -321,4 +436,79 @@ TEST_F(StructTraitsTest, ScrollEvent) { } } +TEST(StructTraitsTest, PointerDetails) { + const PointerDetails kTestData[] = { + {EventPointerType::POINTER_TYPE_UNKNOWN, 1, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f}, + {EventPointerType::POINTER_TYPE_MOUSE, 1, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f}, + {EventPointerType::POINTER_TYPE_PEN, 11, 12.f, 13.f, 14.f, 15.f, 16.f, + 17.f}, + {EventPointerType::POINTER_TYPE_TOUCH, 1, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f}, + {EventPointerType::POINTER_TYPE_ERASER, 21, 22.f, 23.f, 24.f, 25.f, 26.f, + 27.f}, + }; + for (size_t i = 0; i < base::size(kTestData); i++) { + // Set |offset| as the constructor used above does not modify it. + PointerDetails input(kTestData[i]); + input.offset.set_x(i); + input.offset.set_y(i + 1); + + PointerDetails output; + ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::PointerDetails>( + &input, &output)); + EXPECT_EQ(input, output); + } +} + +TEST(StructTraitsTest, TouchEvent) { + const TouchEvent kTestData[] = { + {ET_TOUCH_RELEASED, + {1, 2}, + base::TimeTicks::Now(), + {EventPointerType::POINTER_TYPE_UNKNOWN, 1, 2.f, 3.f, 4.f, 5.f, 6.f, + 7.f}, + EF_SHIFT_DOWN}, + {ET_TOUCH_PRESSED, {1, 2}, base::TimeTicks::Now(), {}, EF_CONTROL_DOWN}, + {ET_TOUCH_MOVED, {1, 2}, base::TimeTicks::Now(), {}, EF_NONE}, + {ET_TOUCH_CANCELLED, {1, 2}, base::TimeTicks::Now(), {}, EF_NONE}, + }; + for (size_t i = 0; i < base::size(kTestData); i++) { + std::unique_ptr<Event> expected_copy = Event::Clone(kTestData[i]); + std::unique_ptr<Event> output; + ASSERT_TRUE(mojo::test::SerializeAndDeserialize<mojom::Event>( + &expected_copy, &output)); + ExpectEventsEqual(*expected_copy, *output); + } + + // Serialize/Deserialize with fields that can not be set from constructor. + std::unique_ptr<TouchEvent> touch_event = + std::make_unique<TouchEvent>(ET_TOUCH_CANCELLED, gfx::Point(), + base::TimeTicks::Now(), PointerDetails()); + touch_event->set_may_cause_scrolling(true); + touch_event->set_hovering(true); + std::unique_ptr<Event> expected = std::move(touch_event); + std::unique_ptr<Event> output; + ASSERT_TRUE( + mojo::test::SerializeAndDeserialize<mojom::Event>(&expected, &output)); + ExpectEventsEqual(*expected, *output); +} + +TEST(StructTraitsTest, UnserializedTouchEventFields) { + std::unique_ptr<TouchEvent> touch_event = + std::make_unique<TouchEvent>(ET_TOUCH_CANCELLED, gfx::Point(), + base::TimeTicks::Now(), PointerDetails()); + touch_event->set_should_remove_native_touch_id_mapping(true); + std::unique_ptr<Event> expected = std::move(touch_event); + std::unique_ptr<Event> output; + ASSERT_TRUE( + mojo::test::SerializeAndDeserialize<mojom::Event>(&expected, &output)); + ExpectEventsEqual(*expected, *output); + // Have to set this back to false, else the destructor tries to access + // state not setup in tests. + expected->AsTouchEvent()->set_should_remove_native_touch_id_mapping(false); + // See comments in TouchEvent for why these two fields are not persisted. + EXPECT_FALSE(output->AsTouchEvent()->should_remove_native_touch_id_mapping()); + EXPECT_NE(expected->AsTouchEvent()->unique_event_id(), + output->AsTouchEvent()->unique_event_id()); +} + } // namespace ui diff --git a/chromium/ui/events/mojo/traits_test_service.mojom b/chromium/ui/events/mojo/traits_test_service.mojom deleted file mode 100644 index 1df4cbecf80..00000000000 --- a/chromium/ui/events/mojo/traits_test_service.mojom +++ /dev/null @@ -1,12 +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. - -module ui.mojom; - -import "ui/events/mojo/event.mojom"; - -interface TraitsTestService { - [Sync] - EchoEvent(Event e) => (Event pass); -}; diff --git a/chromium/ui/events/ozone/evdev/event_device_info.cc b/chromium/ui/events/ozone/evdev/event_device_info.cc index c04cf9c51b4..c7e5d043d03 100644 --- a/chromium/ui/events/ozone/evdev/event_device_info.cc +++ b/chromium/ui/events/ozone/evdev/event_device_info.cc @@ -197,6 +197,11 @@ bool EventDeviceInfo::Initialize(int fd, const base::FilePath& path) { device_type_ = GetInputDeviceTypeFromPath(path); + // TODO(spang): Implement these quirks in a better way. + constexpr uint16_t kGoogleVendorId = 0x18d1; + if (vendor_id_ == kGoogleVendorId && product_id_ == 0x5030) + device_type_ = InputDeviceType::INPUT_DEVICE_INTERNAL; + return true; } diff --git a/chromium/ui/events/ozone/evdev/event_device_test_util.cc b/chromium/ui/events/ozone/evdev/event_device_test_util.cc index 86feff7d83d..e72c4e7b7e6 100644 --- a/chromium/ui/events/ozone/evdev/event_device_test_util.cc +++ b/chromium/ui/events/ozone/evdev/event_device_test_util.cc @@ -634,7 +634,7 @@ ui::InputDeviceType InputDeviceTypeFromBusType(int bustype) { return ui::InputDeviceType::INPUT_DEVICE_INTERNAL; case BUS_USB: case 0x1D: // Used in kLogitechTouchKeyboardK400 but not listed in input.h. - return ui::InputDeviceType::INPUT_DEVICE_EXTERNAL; + return ui::InputDeviceType::INPUT_DEVICE_USB; default: NOTREACHED() << "Unexpected bus type"; return ui::InputDeviceType::INPUT_DEVICE_UNKNOWN; diff --git a/chromium/ui/events/ozone/evdev/event_factory_evdev.cc b/chromium/ui/events/ozone/evdev/event_factory_evdev.cc index 733f2979d8b..27f934cbe0b 100644 --- a/chromium/ui/events/ozone/evdev/event_factory_evdev.cc +++ b/chromium/ui/events/ozone/evdev/event_factory_evdev.cc @@ -336,13 +336,13 @@ void EventFactoryEvdev::DispatchTouchEvent(const TouchEventParams& params) { gfx::PointF location = GetTransformedEventLocation(params); PointerDetails details = GetTransformedEventPointerDetails(params); + details.twist = 0.f; // params.slot is guaranteed to be < kNumTouchEvdevSlots. int input_id = params.device_id * kNumTouchEvdevSlots + params.slot; details.id = touch_id_generator_.GetGeneratedID(input_id); TouchEvent touch_event(params.type, gfx::Point(), params.timestamp, details, - modifiers_.GetModifierFlags() | params.flags, - /* angle */ 0.f); + modifiers_.GetModifierFlags() | params.flags); touch_event.set_location_f(location); touch_event.set_root_location_f(location); touch_event.set_source_device_id(params.device_id); diff --git a/chromium/ui/events/platform/x11/x11_event_source.cc b/chromium/ui/events/platform/x11/x11_event_source.cc index 7e9f6df4762..a68188d77dc 100644 --- a/chromium/ui/events/platform/x11/x11_event_source.cc +++ b/chromium/ui/events/platform/x11/x11_event_source.cc @@ -100,6 +100,7 @@ X11EventSource::X11EventSource(X11EventSourceDelegate* delegate, distribution_(0, 999) { DCHECK(!instance_); instance_ = this; + SetTimestampServer(this); DCHECK(delegate_); DCHECK(display_); @@ -110,6 +111,7 @@ X11EventSource::X11EventSource(X11EventSourceDelegate* delegate, X11EventSource::~X11EventSource() { DCHECK_EQ(this, instance_); instance_ = nullptr; + SetTimestampServer(nullptr); if (dummy_initialized_) XDestroyWindow(display_, dummy_window_); } diff --git a/chromium/ui/events/platform/x11/x11_event_source.h b/chromium/ui/events/platform/x11/x11_event_source.h index e818c4acfa8..c2ee795b68f 100644 --- a/chromium/ui/events/platform/x11/x11_event_source.h +++ b/chromium/ui/events/platform/x11/x11_event_source.h @@ -14,6 +14,7 @@ #include "base/optional.h" #include "build/build_config.h" #include "ui/events/events_export.h" +#include "ui/events/x/events_x_utils.h" #include "ui/gfx/x/x11_types.h" using Time = unsigned long; @@ -46,7 +47,7 @@ class X11EventSourceDelegate { // Receives X11 events and sends them to X11EventSourceDelegate. Handles // receiving, pre-process and post-processing XEvents. -class EVENTS_EXPORT X11EventSource { +class EVENTS_EXPORT X11EventSource : TimestampServer { public: X11EventSource(X11EventSourceDelegate* delegate, XDisplay* display); ~X11EventSource(); @@ -82,7 +83,7 @@ class EVENTS_EXPORT X11EventSource { // Explicitly asks the X11 server for the current timestamp, and updates // |last_seen_server_time_| with this value. - Time GetCurrentServerTime(); + Time GetCurrentServerTime() override; protected: // Extracts cookie data from |xevent| if it's of GenericType, and dispatches diff --git a/chromium/ui/events/win/events_win_utils.cc b/chromium/ui/events/win/events_win_utils.cc index f8bd8d3c155..4025b905b62 100644 --- a/chromium/ui/events/win/events_win_utils.cc +++ b/chromium/ui/events/win/events_win_utils.cc @@ -254,7 +254,7 @@ gfx::Point EventLocationFromMSG(const MSG& native_event) { // Note: Wheel events are considered client, but their position is in screen // coordinates. // Client message. The position is contained in the LPARAM. - return gfx::Point(native_event.lParam); + return gfx::Point(static_cast<DWORD>(native_event.lParam)); } else { DCHECK(IsNonClientMouseEvent(native_event) || IsMouseWheelEvent(native_event) || IsScrollEvent(native_event)); diff --git a/chromium/ui/events/win/keyboard_hook_win.cc b/chromium/ui/events/win/keyboard_hook_win.cc index 1688827db96..042e295679c 100644 --- a/chromium/ui/events/win/keyboard_hook_win.cc +++ b/chromium/ui/events/win/keyboard_hook_win.cc @@ -2,12 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/events/keyboard_hook_base.h" +#include "ui/events/win/keyboard_hook_win.h" #include <utility> -#include <windows.h> - #include "base/logging.h" #include "base/macros.h" #include "base/optional.h" @@ -16,30 +14,112 @@ #include "ui/events/event_utils.h" #include "ui/events/keycodes/dom/dom_code.h" #include "ui/events/keycodes/dom/keycode_converter.h" +#include "ui/events/keycodes/keyboard_code_conversion.h" +#include "ui/events/win/events_win_utils.h" #include "ui/gfx/native_widget_types.h" namespace ui { namespace { -// These keys interfere with the return value of ::GetKeyState() as observing -// them directly in LowLevelKeyboardProc will cause their key combinations to be -// ignored. As an example, the KeyF event in an Alt + F combination will result -// in a missing alt-down flag. Since a regular application can successfully -// receive these keys without using LowLevelKeyboardProc, they can be ignored. -bool IsOSReservedKey(DWORD vk) { - return vk == VK_SHIFT || vk == VK_LSHIFT || vk == VK_RSHIFT || - vk == VK_CONTROL || vk == VK_LCONTROL || vk == VK_RCONTROL || - vk == VK_MENU || vk == VK_LMENU || vk == VK_RMENU || vk == VK_LWIN || - vk == VK_RWIN || vk == VK_CAPITAL || vk == VK_NUMLOCK || - vk == VK_SCROLL; +// The Windows KeyboardHook implementation uses a low-level hook in order to +// intercept system key combinations such as Alt + Tab. A standard hook or +// other window message filtering mechanisms will allow us to observe key events +// but we would be unable to block them or the corresponding system action. +// There are downsides to this approach as described in the following blurbs. + +// Low-level hooks are not given a repeat state for each key event. This is +// because the events are intercepted prior to the OS handling them and tracking +// their states the usual way. We solve this by tracking the last key seen and +// modifying the event manually to indicate it is a repeat. This works for +// every key except escape which is used to exit fullscreen and tear down the +// hook. The quirk is that after the hook is torn down, the first escape key +// event passed to the window will appear like an initial keydown. This is +// because it *is* an initial keydown from Windows' perspective as it was being +// intercepted before then. + +// Intercepting modifier keys (Ctrl, Shift, Win, Etc.) within a low-level +// keyboard hook will result in an incorrect modifier state reported by the OS +// for that key. This is because the hook intercepts the event before the OS +// has a chance to observe the event and update its internal state. This +// trade-off is necessary as we also want to intercept and prevent specific key +// combos from taking effect. + +// A related side-effect occurs when intercepting printable characters. Since +// the key event is intercepted before the OS handles it, no char events are +// produced. So if we intercept scan codes which should generate a printable +// character, the result is that the key up/down events are sent correctly but +// no WM_CHAR message is generated. This is unacceptable for some usages of the +// hook as the behavior is very different between locked and unlocked states. + +// This is a fair number of downsides however the ability to block system key +// combos is a hard requirement so we need to work around them. + +// The solution we have adopted is: +// - Only intercept modifiers and allow all other keys to pass through +// Note: Shift is not included as otherwise it is not applied by the OS and +// the printable characters generated by the key event will be wrong. +// - Track the repeat state ourselves +// - Update the per-thread key state for the tracked modifiers using +// SetKeyboardState(). + +// In practice this works well as intercepting the modifiers allows us to block +// system keyboard combos and all other keys generate the proper events and +// printable chars. + +// Using SetKeyboardState() allows us to tell Windows the current state for the +// modifiers we intercept. This state only works for the current thread, +// meaning that other applications / threads which check the key state for the +// modifiers will not receive an accurate value. One constraint for the +// KeyboardHook is that it is only engaged when our window is fullscreen and +// focused so we don't need to worry too much about other apps. + +// Represents a VK_LCONTROL scancode with the 0x02 flag in the high word. +// The 0x02 flag does not seem to be documented on MSDN or in the public Windows +// headers. I suspect it is related to the KF_ family of constants which skips +// from 0x0100 to 0x0800 with 0x0200 and 0x0400 undocumented (likely reserved). +constexpr DWORD kSynthesizedControlScanCodeForAltGr = 0x021D; + +// {Get|Set}KeyboardState() receives an array with 256 elements. This is +// described in MSDN but no constant exists for this value. Function reference: +// https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getkeyboardstate +// https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-setkeyboardstate +constexpr int kKeyboardStateArraySize = 256; + +// Used for setting and testing the bits in the KeyboardState array. +constexpr BYTE kKeyDown = 0x80; +constexpr BYTE kKeyUp = 0x00; + +bool IsAltKey(DWORD vk) { + return vk == VK_MENU || vk == VK_LMENU || vk == VK_RMENU; +} + +bool IsControlKey(DWORD vk) { + return vk == VK_CONTROL || vk == VK_LCONTROL || vk == VK_RCONTROL; +} + +bool IsWindowsKey(DWORD vk) { + return vk == VK_LWIN || vk == VK_RWIN; +} + +bool IsModifierKey(DWORD vk) { + // We don't track the state of the shift key as we want to allow the OS to + // know about its state so it is correctly applied to printable characters. + return IsAltKey(vk) || IsControlKey(vk) || IsWindowsKey(vk); } -class KeyboardHookWin : public KeyboardHookBase { +class KeyboardHookWinImpl : public KeyboardHookWin { public: - KeyboardHookWin(base::Optional<base::flat_set<DomCode>> dom_codes, - KeyEventCallback callback); - ~KeyboardHookWin() override; + KeyboardHookWinImpl(base::Optional<base::flat_set<DomCode>> dom_codes, + KeyEventCallback callback, + bool enable_hook_registration); + ~KeyboardHookWinImpl() override; + + // KeyboardHookWin implementation. + bool ProcessKeyEventMessage(WPARAM w_param, + DWORD vk, + DWORD scan_code, + DWORD time_stamp) override; bool Register(); @@ -47,30 +127,50 @@ class KeyboardHookWin : public KeyboardHookBase { static LRESULT CALLBACK ProcessKeyEvent(int code, WPARAM w_param, LPARAM l_param); - static KeyboardHookWin* instance_; + + void UpdateModifierState(DWORD vk, bool key_down); + + void ClearModifierStates(); + + static KeyboardHookWinImpl* instance_; THREAD_CHECKER(thread_checker_); HHOOK hook_ = nullptr; - // Tracks the last key down seen in order to determine if the current key - // event is a repeated key press. + // Tracks the last non-located key down seen in order to determine if the + // current key event should be marked as a repeated key press. DWORD last_key_down_ = 0; - DISALLOW_COPY_AND_ASSIGN(KeyboardHookWin); + // Tracks the number of AltGr key sequences seen since the start of the most + // recent AltGr key down. When the AltGr key is pressed, Windows injects a + // synthesized left control key event followed by the right alt key event. + // This sequence occurs on the initial keypress and every repeat. + int altgr_sequence_count_ = 0; + + const bool enable_hook_registration_ = true; + + DISALLOW_COPY_AND_ASSIGN(KeyboardHookWinImpl); }; // static -KeyboardHookWin* KeyboardHookWin::instance_ = nullptr; +KeyboardHookWinImpl* KeyboardHookWinImpl::instance_ = nullptr; -KeyboardHookWin::KeyboardHookWin( +KeyboardHookWinImpl::KeyboardHookWinImpl( base::Optional<base::flat_set<DomCode>> dom_codes, - KeyEventCallback callback) - : KeyboardHookBase(std::move(dom_codes), std::move(callback)) {} + KeyEventCallback callback, + bool enable_hook_registration) + : KeyboardHookWin(std::move(dom_codes), std::move(callback)), + enable_hook_registration_(enable_hook_registration) {} -KeyboardHookWin::~KeyboardHookWin() { +KeyboardHookWinImpl::~KeyboardHookWinImpl() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + ClearModifierStates(); + + if (!enable_hook_registration_) + return; + DCHECK_EQ(instance_, this); instance_ = nullptr; @@ -78,9 +178,12 @@ KeyboardHookWin::~KeyboardHookWin() { DPLOG(ERROR) << "UnhookWindowsHookEx failed"; } -bool KeyboardHookWin::Register() { +bool KeyboardHookWinImpl::Register() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + // If the hook was created for testing, |Register()| should not be called. + DCHECK(enable_hook_registration_); + // Only one instance of this class can be registered at a time. DCHECK(!instance_); instance_ = this; @@ -89,7 +192,7 @@ bool KeyboardHookWin::Register() { // which installed it. hook_ = SetWindowsHookEx( WH_KEYBOARD_LL, - reinterpret_cast<HOOKPROC>(&KeyboardHookWin::ProcessKeyEvent), + reinterpret_cast<HOOKPROC>(&KeyboardHookWinImpl::ProcessKeyEvent), /*hMod=*/nullptr, /*dwThreadId=*/0); DPLOG_IF(ERROR, !hook_) << "SetWindowsHookEx failed"; @@ -97,10 +200,127 @@ bool KeyboardHookWin::Register() { return hook_ != nullptr; } +void KeyboardHookWinImpl::ClearModifierStates() { + BYTE keyboard_state[kKeyboardStateArraySize] = {0}; + if (!GetKeyboardState(keyboard_state)) { + DPLOG(ERROR) << "GetKeyboardState() failed: "; + return; + } + + // Reset each modifier position. + keyboard_state[VK_CONTROL] = kKeyUp; + keyboard_state[VK_LCONTROL] = kKeyUp; + keyboard_state[VK_RCONTROL] = kKeyUp; + keyboard_state[VK_MENU] = kKeyUp; + keyboard_state[VK_LMENU] = kKeyUp; + keyboard_state[VK_RMENU] = kKeyUp; + keyboard_state[VK_LWIN] = kKeyUp; + keyboard_state[VK_RWIN] = kKeyUp; + + if (!SetKeyboardState(keyboard_state)) + DPLOG(ERROR) << "SetKeyboardState() failed: "; +} + +bool KeyboardHookWinImpl::ProcessKeyEventMessage(WPARAM w_param, + DWORD vk, + DWORD scan_code, + DWORD time_stamp) { + // The |vk| delivered to the low-level hook includes a location which is + // needed to track individual keystates such as when both left and right + // control keys are pressed. Make sure that location information was retained + // when the vkey was passed into this method. + DCHECK_NE(vk, static_cast<DWORD>(VK_CONTROL)); + DCHECK_NE(vk, static_cast<DWORD>(VK_MENU)); + + if (!IsModifierKey(vk)) + return false; + + // Windows synthesizes an additional key event when AltGr is pressed. The + // event has the left control scan code in the low word and a 0x02 flag set in + // the high word. The VK_RMENU event will be sent once this key is processed. + bool is_altgr_sequence = false; + if (scan_code == kSynthesizedControlScanCodeForAltGr) { + is_altgr_sequence = true; + scan_code = KeycodeConverter::DomCodeToNativeKeycode(DomCode::CONTROL_LEFT); + } + + // If the caller has only requested that Alt be captured, then we don't want + // to bail early on the injected control event. + DomCode dom_code = DomCode::NONE; + if (is_altgr_sequence) + dom_code = DomCode::ALT_RIGHT; + else + dom_code = KeycodeConverter::NativeKeycodeToDomCode(scan_code); + + if (!ShouldCaptureKeyEvent(dom_code)) + return false; + + if (is_altgr_sequence) + altgr_sequence_count_++; + else if (vk != VK_RMENU) + altgr_sequence_count_ = 0; + + // The Windows key is always located, the other modifiers are not. + DWORD non_located_vk = + IsWindowsKey(vk) + ? vk + : LocatedToNonLocatedKeyboardCode(static_cast<KeyboardCode>(vk)); + + bool is_repeat = false; + MSG msg = {nullptr, w_param, non_located_vk, GetLParamFromScanCode(scan_code), + time_stamp}; + EventType event_type = EventTypeFromMSG(msg); + if (event_type == ET_KEY_PRESSED) { + UpdateModifierState(vk, /*key_down=*/true); + // We use the non-located vkey to determine whether a key event is a repeat + // or not. The exception is for AltGr which has a two key sequence which + // alternates. + is_repeat = (last_key_down_ == non_located_vk) || altgr_sequence_count_ > 1; + last_key_down_ = non_located_vk; + } else { + DCHECK_EQ(event_type, ET_KEY_RELEASED); + UpdateModifierState(vk, /*key_down=*/false); + altgr_sequence_count_ = 0; + last_key_down_ = 0; + } + + std::unique_ptr<KeyEvent> key_event = + std::make_unique<KeyEvent>(KeyEventFromMSG(msg)); + if (is_repeat) + key_event->set_flags(key_event->flags() | EF_IS_REPEAT); + ForwardCapturedKeyEvent(std::move(key_event)); + + return true; +} + +void KeyboardHookWinImpl::UpdateModifierState(DWORD vk, bool is_key_down) { + BYTE keyboard_state[kKeyboardStateArraySize] = {0}; + if (!GetKeyboardState(keyboard_state)) { + DPLOG(ERROR) << "GetKeyboardState() failed: "; + return; + } + + // Update the located virtual key first. + keyboard_state[vk] = is_key_down ? kKeyDown : kKeyUp; + + // Now update the non-located virtual key. + keyboard_state[VK_CONTROL] = (keyboard_state[VK_LCONTROL] == kKeyDown || + keyboard_state[VK_RCONTROL] == kKeyDown) + ? kKeyDown + : kKeyUp; + keyboard_state[VK_MENU] = (keyboard_state[VK_LMENU] == kKeyDown || + keyboard_state[VK_RMENU] == kKeyDown) + ? kKeyDown + : kKeyUp; + + if (!SetKeyboardState(keyboard_state)) + PLOG(ERROR) << "SetKeyboardState() failed: "; +} + // static -LRESULT CALLBACK KeyboardHookWin::ProcessKeyEvent(int code, - WPARAM w_param, - LPARAM l_param) { +LRESULT CALLBACK KeyboardHookWinImpl::ProcessKeyEvent(int code, + WPARAM w_param, + LPARAM l_param) { // If there is an error unhooking, this method could be called with a null // |instance_|. Ensure we have a valid instance and that |code| is correct // before proceeding. @@ -110,33 +330,20 @@ LRESULT CALLBACK KeyboardHookWin::ProcessKeyEvent(int code, DCHECK_CALLED_ON_VALID_THREAD(instance_->thread_checker_); KBDLLHOOKSTRUCT* ll_hooks = reinterpret_cast<KBDLLHOOKSTRUCT*>(l_param); - DomCode dom_code = - KeycodeConverter::NativeKeycodeToDomCode(ll_hooks->scanCode); - if (!IsOSReservedKey(ll_hooks->vkCode) && - instance_->ShouldCaptureKeyEvent(dom_code)) { - MSG msg = {nullptr, w_param, ll_hooks->vkCode, - (ll_hooks->scanCode << 16) | (ll_hooks->flags & 0xFFFF), - ll_hooks->time}; - KeyEvent key_event = KeyEventFromMSG(msg); - - // Determine if this key event represents a repeated key event. - // A low level KB Hook is not passed a parameter or flag which indicates - // whether the key event is a repeat or not as it is called very early in - // input handling pipeline. That means we need to apply our own heuristic - // to determine if it should be treated as a repeated key press or not. - if (key_event.type() == ET_KEY_PRESSED) { - if (instance_->last_key_down_ != 0 && - instance_->last_key_down_ == ll_hooks->vkCode) { - key_event.set_flags(key_event.flags() | EF_IS_REPEAT); - } - instance_->last_key_down_ = ll_hooks->vkCode; - } else { - DCHECK_EQ(key_event.type(), ET_KEY_RELEASED); - instance_->last_key_down_ = 0; - } - instance_->ForwardCapturedKeyEvent(std::make_unique<KeyEvent>(key_event)); + + // This vkey represents both a vkey and a location on the keyboard such as + // VK_LCONTROL or VK_RCONTROL. + DWORD vk = ll_hooks->vkCode; + + // Apply the extended flag prior to passing |scan_code| since |instance_| does + // not have access to the low-level hook flags. + DWORD scan_code = ll_hooks->scanCode; + if (ll_hooks->flags & LLKHF_EXTENDED) + scan_code |= 0xE000; + + if (instance_->ProcessKeyEventMessage(w_param, vk, scan_code, ll_hooks->time)) return 1; - } + return CallNextHookEx(nullptr, code, w_param, l_param); } @@ -147,9 +354,10 @@ std::unique_ptr<KeyboardHook> KeyboardHook::Create( base::Optional<base::flat_set<DomCode>> dom_codes, gfx::AcceleratedWidget accelerated_widget, KeyEventCallback callback) { - std::unique_ptr<KeyboardHookWin> keyboard_hook = - std::make_unique<KeyboardHookWin>(std::move(dom_codes), - std::move(callback)); + std::unique_ptr<KeyboardHookWinImpl> keyboard_hook = + std::make_unique<KeyboardHookWinImpl>(std::move(dom_codes), + std::move(callback), + /*enable_hook_registration=*/true); if (!keyboard_hook->Register()) return nullptr; @@ -157,4 +365,19 @@ std::unique_ptr<KeyboardHook> KeyboardHook::Create( return keyboard_hook; } +std::unique_ptr<KeyboardHookWin> KeyboardHookWin::CreateForTesting( + base::Optional<base::flat_set<DomCode>> dom_codes, + KeyEventCallback callback) { + return std::make_unique<KeyboardHookWinImpl>( + std::move(dom_codes), std::move(callback), + /*enable_hook_registration=*/false); +} + +KeyboardHookWin::KeyboardHookWin( + base::Optional<base::flat_set<DomCode>> dom_codes, + KeyEventCallback callback) + : KeyboardHookBase(std::move(dom_codes), std::move(callback)) {} + +KeyboardHookWin::~KeyboardHookWin() = default; + } // namespace ui diff --git a/chromium/ui/events/win/keyboard_hook_win.h b/chromium/ui/events/win/keyboard_hook_win.h new file mode 100644 index 00000000000..627ba313b17 --- /dev/null +++ b/chromium/ui/events/win/keyboard_hook_win.h @@ -0,0 +1,52 @@ +// 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_EVENTS_WIN_KEYBOARD_HOOK_WIN_H_ +#define UI_EVENTS_WIN_KEYBOARD_HOOK_WIN_H_ + +#include <memory> + +#include <windows.h> + +#include "base/containers/flat_set.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/optional.h" +#include "ui/events/event.h" +#include "ui/events/events_export.h" +#include "ui/events/keyboard_hook_base.h" +#include "ui/events/keycodes/dom/dom_code.h" + +namespace ui { + +// Exposes a method to drive the Windows KeyboardHook implementation by feeding +// it key event data. This method is used by both the low-level keyboard hook +// and by unit tests which simulate the hooked behavior w/o actually installing +// a hook (doing so would cause problems with test parallelization). +class EVENTS_EXPORT KeyboardHookWin : public KeyboardHookBase { + public: + KeyboardHookWin(base::Optional<base::flat_set<DomCode>> dom_codes, + KeyEventCallback callback); + ~KeyboardHookWin() override; + + // Create a KeyboardHookWin instance which does not register a low-level hook. + static std::unique_ptr<KeyboardHookWin> CreateForTesting( + base::Optional<base::flat_set<DomCode>> dom_codes, + KeyEventCallback callback); + + // Called when a key event message is delivered via the low-level hook. + // Exposed here to allow for testing w/o engaging the low-level hook. + // Returns true if the message was handled. + virtual bool ProcessKeyEventMessage(WPARAM w_param, + DWORD vk, + DWORD scan_code, + DWORD time_stamp) = 0; + + private: + DISALLOW_COPY_AND_ASSIGN(KeyboardHookWin); +}; + +} // namespace ui + +#endif // UI_EVENTS_WIN_KEYBOARD_HOOK_WIN_H_ diff --git a/chromium/ui/events/win/keyboard_hook_win_unittest.cc b/chromium/ui/events/win/keyboard_hook_win_unittest.cc new file mode 100644 index 00000000000..d8f3deaa805 --- /dev/null +++ b/chromium/ui/events/win/keyboard_hook_win_unittest.cc @@ -0,0 +1,1085 @@ +// 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 <memory> +#include <vector> + +#include "base/bind.h" +#include "base/containers/flat_set.h" +#include "base/optional.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/event.h" +#include "ui/events/keyboard_hook.h" +#include "ui/events/keycodes/dom/dom_code.h" +#include "ui/events/keycodes/dom/keycode_converter.h" +#include "ui/events/keycodes/keyboard_codes.h" +#include "ui/events/test/keyboard_layout.h" +#include "ui/events/win/keyboard_hook_win.h" +#include "ui/events/win/system_event_state_lookup.h" + +namespace ui { + +class KeyboardHookWinTest : public testing::Test { + public: + KeyboardHookWinTest(); + ~KeyboardHookWinTest() override; + + // testing::Test overrides. + void SetUp() override; + + void HandleKeyPress(KeyEvent* key_event); + + protected: + KeyboardHookWin* keyboard_hook() { return keyboard_hook_.get(); } + + uint32_t next_time_stamp() { return time_stamp_++; } + + std::vector<KeyEvent>* key_events() { return &key_events_; } + + // Used for sending key events which are handled by the hook. + void SendModifierKeyDownEvent(KeyboardCode key_code, + DomCode dom_code, + int repeat_count = 1); + void SendModifierKeyUpEvent(KeyboardCode key_code, DomCode dom_code); + + void SetKeyboardLayoutForTest(KeyboardLayout new_layout); + + private: + uint32_t time_stamp_ = 0; + std::unique_ptr<KeyboardHookWin> keyboard_hook_; + std::vector<KeyEvent> key_events_; + std::unique_ptr<ScopedKeyboardLayout> keyboard_layout_; + + DISALLOW_COPY_AND_ASSIGN(KeyboardHookWinTest); +}; + +KeyboardHookWinTest::KeyboardHookWinTest() = default; + +KeyboardHookWinTest::~KeyboardHookWinTest() = default; + +void KeyboardHookWinTest::SetUp() { + keyboard_hook_ = KeyboardHookWin::CreateForTesting( + base::Optional<base::flat_set<DomCode>>(), + base::BindRepeating(&KeyboardHookWinTest::HandleKeyPress, + base::Unretained(this))); + + keyboard_layout_ = std::make_unique<ScopedKeyboardLayout>( + KeyboardLayout::KEYBOARD_LAYOUT_ENGLISH_US); +} + +void KeyboardHookWinTest::HandleKeyPress(KeyEvent* key_event) { + key_events_.push_back(*key_event); +} + +void KeyboardHookWinTest::SendModifierKeyDownEvent(KeyboardCode key_code, + DomCode dom_code, + int repeat_count /*=1*/) { + // Ensure we have a valid repeat count and the modifer passed in contains + // location information. + DCHECK_GT(repeat_count, 0); + DCHECK_NE(key_code, KeyboardCode::VKEY_CONTROL); + DCHECK_NE(key_code, KeyboardCode::VKEY_MENU); + + for (int i = 0; i < repeat_count; i++) { + ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage( + WM_KEYDOWN, key_code, + KeycodeConverter::DomCodeToNativeKeycode(dom_code), next_time_stamp())); + } +} + +void KeyboardHookWinTest::SendModifierKeyUpEvent(KeyboardCode key_code, + DomCode dom_code) { + // Ensure we have a valid repeat count and the modifer passed in contains + // location information. + DCHECK_NE(key_code, KeyboardCode::VKEY_CONTROL); + DCHECK_NE(key_code, KeyboardCode::VKEY_MENU); + + ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage( + WM_KEYUP, key_code, KeycodeConverter::DomCodeToNativeKeycode(dom_code), + next_time_stamp())); +} + +void VerifyKeyEvent(KeyEvent* key_event, + KeyboardCode non_located_key_code, + DomCode dom_code, + bool key_down, + bool is_repeat) { + if (key_down) { + ASSERT_EQ(key_event->type(), ET_KEY_PRESSED); + ASSERT_EQ(key_event->is_repeat(), is_repeat); + } else { + ASSERT_EQ(key_event->type(), ET_KEY_RELEASED); + ASSERT_FALSE(key_event->is_repeat()); + } + ASSERT_EQ(key_event->key_code(), non_located_key_code); + ASSERT_EQ(key_event->code(), dom_code); +} + +TEST_F(KeyboardHookWinTest, SimpleLeftControlKeypressTest) { + const KeyboardCode key_code = KeyboardCode::VKEY_LCONTROL; + const DomCode dom_code = DomCode::CONTROL_LEFT; + SendModifierKeyDownEvent(key_code, dom_code); + ASSERT_EQ(key_events()->size(), 1ULL); + SendModifierKeyUpEvent(key_code, dom_code); + ASSERT_EQ(key_events()->size(), 2ULL); + + KeyEvent down_event = key_events()->at(0); + VerifyKeyEvent(&down_event, KeyboardCode::VKEY_CONTROL, dom_code, true, + false); + ASSERT_TRUE(down_event.IsControlDown()); + ASSERT_FALSE(down_event.IsAltDown()); + ASSERT_FALSE(down_event.IsCommandDown()); + + KeyEvent up_event = key_events()->at(1); + VerifyKeyEvent(&up_event, KeyboardCode::VKEY_CONTROL, dom_code, false, false); + ASSERT_FALSE(up_event.IsControlDown()); +} + +TEST_F(KeyboardHookWinTest, RepeatingLeftControlKeypressTest) { + const int repeat_count = 10; + const KeyboardCode key_code = KeyboardCode::VKEY_LCONTROL; + const DomCode dom_code = DomCode::CONTROL_LEFT; + SendModifierKeyDownEvent(key_code, dom_code, repeat_count); + ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count); + SendModifierKeyUpEvent(key_code, dom_code); + ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count + 1); + + bool should_repeat = false; + for (int i = 0; i < repeat_count; i++) { + KeyEvent event = key_events()->at(i); + VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, dom_code, true, + should_repeat); + ASSERT_TRUE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_FALSE(event.IsCommandDown()); + should_repeat = true; + } + + KeyEvent up_event = key_events()->at(repeat_count); + VerifyKeyEvent(&up_event, KeyboardCode::VKEY_CONTROL, dom_code, false, false); + ASSERT_FALSE(up_event.IsControlDown()); +} + +TEST_F(KeyboardHookWinTest, SimpleRightControlKeypressTest) { + const KeyboardCode key_code = KeyboardCode::VKEY_RCONTROL; + const DomCode dom_code = DomCode::CONTROL_RIGHT; + SendModifierKeyDownEvent(key_code, dom_code); + ASSERT_EQ(key_events()->size(), 1ULL); + SendModifierKeyUpEvent(key_code, dom_code); + ASSERT_EQ(key_events()->size(), 2ULL); + + KeyEvent down_event = key_events()->at(0); + VerifyKeyEvent(&down_event, KeyboardCode::VKEY_CONTROL, dom_code, true, + false); + ASSERT_TRUE(down_event.IsControlDown()); + ASSERT_FALSE(down_event.IsAltDown()); + ASSERT_FALSE(down_event.IsCommandDown()); + + KeyEvent up_event = key_events()->at(1); + VerifyKeyEvent(&up_event, KeyboardCode::VKEY_CONTROL, dom_code, false, false); + ASSERT_FALSE(up_event.IsControlDown()); +} + +TEST_F(KeyboardHookWinTest, RepeatingRightControlKeypressTest) { + const int repeat_count = 10; + const KeyboardCode key_code = KeyboardCode::VKEY_RCONTROL; + const DomCode dom_code = DomCode::CONTROL_RIGHT; + SendModifierKeyDownEvent(key_code, dom_code, repeat_count); + ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count); + SendModifierKeyUpEvent(key_code, dom_code); + ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count + 1); + + bool should_repeat = false; + for (int i = 0; i < repeat_count; i++) { + KeyEvent event = key_events()->at(i); + VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, dom_code, true, + should_repeat); + ASSERT_TRUE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_FALSE(event.IsCommandDown()); + should_repeat = true; + } + + KeyEvent up_event = key_events()->at(repeat_count); + VerifyKeyEvent(&up_event, KeyboardCode::VKEY_CONTROL, dom_code, false, false); + ASSERT_FALSE(up_event.IsControlDown()); +} + +TEST_F(KeyboardHookWinTest, SimpleLifoControlSequenceTest) { + const KeyboardCode left_key_code = KeyboardCode::VKEY_LCONTROL; + const DomCode left_dom_code = DomCode::CONTROL_LEFT; + const KeyboardCode right_key_code = KeyboardCode::VKEY_RCONTROL; + const DomCode right_dom_code = DomCode::CONTROL_RIGHT; + SendModifierKeyDownEvent(left_key_code, left_dom_code, 2); + SendModifierKeyDownEvent(right_key_code, right_dom_code, 2); + SendModifierKeyUpEvent(right_key_code, right_dom_code); + SendModifierKeyUpEvent(left_key_code, left_dom_code); + ASSERT_EQ(key_events()->size(), 6ULL); + + // First key down, no repeat. + KeyEvent event = key_events()->at(0); + VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, left_dom_code, true, + false); + ASSERT_TRUE(event.IsControlDown()); + + // First key still down, repeat. + event = key_events()->at(1); + VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, left_dom_code, true, true); + ASSERT_TRUE(event.IsControlDown()); + + // Second key down, repeat. + event = key_events()->at(2); + VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, right_dom_code, true, + true); + ASSERT_TRUE(event.IsControlDown()); + + // Second key still down, repeat. + event = key_events()->at(3); + VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, right_dom_code, true, + true); + ASSERT_TRUE(event.IsControlDown()); + + // Second key up. + event = key_events()->at(4); + VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, right_dom_code, false, + false); + ASSERT_TRUE(event.IsControlDown()); + + // First key up. + event = key_events()->at(5); + VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, left_dom_code, false, + false); + ASSERT_FALSE(event.IsControlDown()); +} + +TEST_F(KeyboardHookWinTest, SimpleFifoControlSequenceTest) { + const KeyboardCode left_key_code = KeyboardCode::VKEY_LCONTROL; + const DomCode left_dom_code = DomCode::CONTROL_LEFT; + const KeyboardCode right_key_code = KeyboardCode::VKEY_RCONTROL; + const DomCode right_dom_code = DomCode::CONTROL_RIGHT; + SendModifierKeyDownEvent(right_key_code, right_dom_code, 2); + SendModifierKeyDownEvent(left_key_code, left_dom_code, 2); + SendModifierKeyUpEvent(right_key_code, right_dom_code); + SendModifierKeyUpEvent(left_key_code, left_dom_code); + ASSERT_EQ(key_events()->size(), 6ULL); + + // First key down, no repeat. + KeyEvent event = key_events()->at(0); + VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, right_dom_code, true, + false); + ASSERT_TRUE(event.IsControlDown()); + + // First key still down, repeat. + event = key_events()->at(1); + VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, right_dom_code, true, + true); + ASSERT_TRUE(event.IsControlDown()); + + // Second key down, repeat. + event = key_events()->at(2); + VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, left_dom_code, true, true); + ASSERT_TRUE(event.IsControlDown()); + + // Second key still down, repeat. + event = key_events()->at(3); + VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, left_dom_code, true, true); + ASSERT_TRUE(event.IsControlDown()); + + // First key up. + event = key_events()->at(4); + VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, right_dom_code, false, + false); + ASSERT_TRUE(event.IsControlDown()); + + // Second key up. + event = key_events()->at(5); + VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, left_dom_code, false, + false); + ASSERT_FALSE(event.IsControlDown()); +} + +TEST_F(KeyboardHookWinTest, SimpleLeftAltKeypressTest) { + const KeyboardCode key_code = KeyboardCode::VKEY_LMENU; + const DomCode dom_code = DomCode::ALT_LEFT; + SendModifierKeyDownEvent(key_code, dom_code); + ASSERT_EQ(key_events()->size(), 1ULL); + SendModifierKeyUpEvent(key_code, dom_code); + ASSERT_EQ(key_events()->size(), 2ULL); + + KeyEvent down_event = key_events()->at(0); + VerifyKeyEvent(&down_event, KeyboardCode::VKEY_MENU, dom_code, true, false); + ASSERT_FALSE(down_event.IsControlDown()); + ASSERT_TRUE(down_event.IsAltDown()); + ASSERT_FALSE(down_event.IsCommandDown()); + + KeyEvent up_event = key_events()->at(1); + VerifyKeyEvent(&up_event, KeyboardCode::VKEY_MENU, dom_code, false, false); + ASSERT_FALSE(up_event.IsAltDown()); +} + +TEST_F(KeyboardHookWinTest, RepeatingLeftAltKeypressTest) { + const int repeat_count = 10; + const KeyboardCode key_code = KeyboardCode::VKEY_LMENU; + const DomCode dom_code = DomCode::ALT_LEFT; + SendModifierKeyDownEvent(key_code, dom_code, repeat_count); + ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count); + SendModifierKeyUpEvent(key_code, dom_code); + ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count + 1); + + bool should_repeat = false; + for (int i = 0; i < repeat_count; i++) { + KeyEvent event = key_events()->at(i); + VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, dom_code, true, + should_repeat); + ASSERT_FALSE(event.IsControlDown()); + ASSERT_TRUE(event.IsAltDown()); + ASSERT_FALSE(event.IsCommandDown()); + should_repeat = true; + } + + KeyEvent up_event = key_events()->at(repeat_count); + VerifyKeyEvent(&up_event, KeyboardCode::VKEY_MENU, dom_code, false, false); + ASSERT_FALSE(up_event.IsAltDown()); +} + +TEST_F(KeyboardHookWinTest, SimpleRightAltKeypressTest) { + const KeyboardCode key_code = KeyboardCode::VKEY_RMENU; + const DomCode dom_code = DomCode::ALT_LEFT; + SendModifierKeyDownEvent(key_code, dom_code); + ASSERT_EQ(key_events()->size(), 1ULL); + SendModifierKeyUpEvent(key_code, dom_code); + ASSERT_EQ(key_events()->size(), 2ULL); + + KeyEvent down_event = key_events()->at(0); + VerifyKeyEvent(&down_event, KeyboardCode::VKEY_MENU, dom_code, true, false); + ASSERT_FALSE(down_event.IsControlDown()); + ASSERT_TRUE(down_event.IsAltDown()); + ASSERT_FALSE(down_event.IsCommandDown()); + + KeyEvent up_event = key_events()->at(1); + VerifyKeyEvent(&up_event, KeyboardCode::VKEY_MENU, dom_code, false, false); + ASSERT_FALSE(up_event.IsAltDown()); +} + +TEST_F(KeyboardHookWinTest, RepeatingRightAltKeypressTest) { + const int repeat_count = 10; + const KeyboardCode key_code = KeyboardCode::VKEY_RMENU; + const DomCode dom_code = DomCode::ALT_RIGHT; + SendModifierKeyDownEvent(key_code, dom_code, repeat_count); + ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count); + SendModifierKeyUpEvent(key_code, dom_code); + ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count + 1); + + bool should_repeat = false; + for (int i = 0; i < repeat_count; i++) { + KeyEvent event = key_events()->at(i); + VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, dom_code, true, + should_repeat); + ASSERT_FALSE(event.IsControlDown()); + ASSERT_TRUE(event.IsAltDown()); + ASSERT_FALSE(event.IsCommandDown()); + should_repeat = true; + } + + KeyEvent up_event = key_events()->at(repeat_count); + VerifyKeyEvent(&up_event, KeyboardCode::VKEY_MENU, dom_code, false, false); + ASSERT_FALSE(up_event.IsAltDown()); +} + +TEST_F(KeyboardHookWinTest, SimpleLifoAltSequenceTest) { + const KeyboardCode left_key_code = KeyboardCode::VKEY_LMENU; + const DomCode left_dom_code = DomCode::ALT_LEFT; + const KeyboardCode right_key_code = KeyboardCode::VKEY_RMENU; + const DomCode right_dom_code = DomCode::ALT_RIGHT; + SendModifierKeyDownEvent(left_key_code, left_dom_code, 2); + SendModifierKeyDownEvent(right_key_code, right_dom_code, 2); + SendModifierKeyUpEvent(right_key_code, right_dom_code); + SendModifierKeyUpEvent(left_key_code, left_dom_code); + ASSERT_EQ(key_events()->size(), 6ULL); + + // First key down, no repeat. + KeyEvent event = key_events()->at(0); + VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, left_dom_code, true, false); + ASSERT_TRUE(event.IsAltDown()); + + // First key still down, repeat. + event = key_events()->at(1); + VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, left_dom_code, true, true); + ASSERT_TRUE(event.IsAltDown()); + + // Second key down, repeat. + event = key_events()->at(2); + VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, right_dom_code, true, true); + ASSERT_TRUE(event.IsAltDown()); + + // Second key still down, repeat. + event = key_events()->at(3); + VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, right_dom_code, true, true); + ASSERT_TRUE(event.IsAltDown()); + + // Second key up. + event = key_events()->at(4); + VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, right_dom_code, false, false); + ASSERT_TRUE(event.IsAltDown()); + + // First key up. + event = key_events()->at(5); + VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, left_dom_code, false, false); + ASSERT_FALSE(event.IsAltDown()); +} + +TEST_F(KeyboardHookWinTest, SimpleFifoAltSequenceTest) { + const KeyboardCode left_key_code = KeyboardCode::VKEY_LMENU; + const DomCode left_dom_code = DomCode::ALT_LEFT; + const KeyboardCode right_key_code = KeyboardCode::VKEY_RMENU; + const DomCode right_dom_code = DomCode::ALT_RIGHT; + SendModifierKeyDownEvent(right_key_code, right_dom_code, 2); + SendModifierKeyDownEvent(left_key_code, left_dom_code, 2); + SendModifierKeyUpEvent(right_key_code, right_dom_code); + SendModifierKeyUpEvent(left_key_code, left_dom_code); + ASSERT_EQ(key_events()->size(), 6ULL); + + // First key down, no repeat. + KeyEvent event = key_events()->at(0); + VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, right_dom_code, true, false); + ASSERT_TRUE(event.IsAltDown()); + + // First key still down, repeat. + event = key_events()->at(1); + VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, right_dom_code, true, true); + ASSERT_TRUE(event.IsAltDown()); + + // Second key down, repeat. + event = key_events()->at(2); + VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, left_dom_code, true, true); + ASSERT_TRUE(event.IsAltDown()); + + // Second key still down, repeat. + event = key_events()->at(3); + VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, left_dom_code, true, true); + ASSERT_TRUE(event.IsAltDown()); + + // First key up. + event = key_events()->at(4); + VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, right_dom_code, false, false); + ASSERT_TRUE(event.IsAltDown()); + + // Second key up. + event = key_events()->at(5); + VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, left_dom_code, false, false); + ASSERT_FALSE(event.IsAltDown()); +} + +TEST_F(KeyboardHookWinTest, SimpleLeftWinKeypressTest) { + const KeyboardCode key_code = KeyboardCode::VKEY_LWIN; + const DomCode dom_code = DomCode::META_LEFT; + SendModifierKeyDownEvent(key_code, dom_code); + ASSERT_EQ(key_events()->size(), 1ULL); + SendModifierKeyUpEvent(key_code, dom_code); + ASSERT_EQ(key_events()->size(), 2ULL); + + KeyEvent down_event = key_events()->at(0); + // VKEY_LWIN is the 'non-located' version of the Windows key. + VerifyKeyEvent(&down_event, KeyboardCode::VKEY_LWIN, dom_code, true, false); + ASSERT_FALSE(down_event.IsControlDown()); + ASSERT_FALSE(down_event.IsAltDown()); + ASSERT_TRUE(down_event.IsCommandDown()); + + KeyEvent up_event = key_events()->at(1); + VerifyKeyEvent(&up_event, KeyboardCode::VKEY_LWIN, dom_code, false, false); + ASSERT_FALSE(up_event.IsCommandDown()); +} + +TEST_F(KeyboardHookWinTest, RepeatingLeftWinKeypressTest) { + const int repeat_count = 10; + const KeyboardCode key_code = KeyboardCode::VKEY_LWIN; + const DomCode dom_code = DomCode::META_LEFT; + SendModifierKeyDownEvent(key_code, dom_code, repeat_count); + ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count); + SendModifierKeyUpEvent(key_code, dom_code); + ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count + 1); + + bool should_repeat = false; + for (int i = 0; i < repeat_count; i++) { + KeyEvent event = key_events()->at(i); + // VKEY_LWIN is the 'non-located' version of the Windows key. + VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, dom_code, true, + should_repeat); + ASSERT_FALSE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_TRUE(event.IsCommandDown()); + should_repeat = true; + } + + KeyEvent up_event = key_events()->at(repeat_count); + VerifyKeyEvent(&up_event, KeyboardCode::VKEY_LWIN, dom_code, false, false); + ASSERT_FALSE(up_event.IsCommandDown()); +} + +TEST_F(KeyboardHookWinTest, SimpleRightWinKeypressTest) { + const KeyboardCode key_code = KeyboardCode::VKEY_RWIN; + const DomCode dom_code = DomCode::META_RIGHT; + SendModifierKeyDownEvent(key_code, dom_code); + ASSERT_EQ(key_events()->size(), 1ULL); + SendModifierKeyUpEvent(key_code, dom_code); + ASSERT_EQ(key_events()->size(), 2ULL); + + KeyEvent down_event = key_events()->at(0); + // VKEY_LWIN is the 'non-located' version of the Windows key. + VerifyKeyEvent(&down_event, KeyboardCode::VKEY_RWIN, dom_code, true, false); + ASSERT_FALSE(down_event.IsControlDown()); + ASSERT_FALSE(down_event.IsAltDown()); + ASSERT_TRUE(down_event.IsCommandDown()); + + KeyEvent up_event = key_events()->at(1); + VerifyKeyEvent(&up_event, KeyboardCode::VKEY_RWIN, dom_code, false, false); + ASSERT_FALSE(up_event.IsCommandDown()); +} + +TEST_F(KeyboardHookWinTest, RepeatingRightWinKeypressTest) { + const int repeat_count = 10; + const KeyboardCode key_code = KeyboardCode::VKEY_RWIN; + const DomCode dom_code = DomCode::META_RIGHT; + SendModifierKeyDownEvent(key_code, dom_code, repeat_count); + ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count); + SendModifierKeyUpEvent(key_code, dom_code); + ASSERT_EQ(static_cast<int>(key_events()->size()), repeat_count + 1); + + bool should_repeat = false; + for (int i = 0; i < repeat_count; i++) { + KeyEvent event = key_events()->at(i); + // VKEY_LWIN is the 'non-located' version of the Windows key. + VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, dom_code, true, + should_repeat); + ASSERT_FALSE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_TRUE(event.IsCommandDown()); + should_repeat = true; + } + + KeyEvent up_event = key_events()->at(repeat_count); + VerifyKeyEvent(&up_event, KeyboardCode::VKEY_RWIN, dom_code, false, false); + ASSERT_FALSE(up_event.IsCommandDown()); +} + +TEST_F(KeyboardHookWinTest, SimpleLifoWinSequenceTest) { + const KeyboardCode left_key_code = KeyboardCode::VKEY_LWIN; + const DomCode left_dom_code = DomCode::META_LEFT; + const KeyboardCode right_key_code = KeyboardCode::VKEY_RWIN; + const DomCode right_dom_code = DomCode::META_RIGHT; + SendModifierKeyDownEvent(left_key_code, left_dom_code, 2); + SendModifierKeyDownEvent(right_key_code, right_dom_code, 2); + SendModifierKeyUpEvent(right_key_code, right_dom_code); + SendModifierKeyUpEvent(left_key_code, left_dom_code); + ASSERT_EQ(key_events()->size(), 6ULL); + + // First key down, no repeat. + KeyEvent event = key_events()->at(0); + VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, left_dom_code, true, false); + ASSERT_TRUE(event.IsCommandDown()); + + // First key still down, repeat. + event = key_events()->at(1); + VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, left_dom_code, true, true); + ASSERT_TRUE(event.IsCommandDown()); + + // Second key down, no repeat. + event = key_events()->at(2); + VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, right_dom_code, true, false); + ASSERT_TRUE(event.IsCommandDown()); + + // Second key still down, repeat. + event = key_events()->at(3); + VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, right_dom_code, true, true); + ASSERT_TRUE(event.IsCommandDown()); + + // Second key up. + event = key_events()->at(4); + VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, right_dom_code, false, false); + ASSERT_TRUE(event.IsCommandDown()); + + // First key up. + event = key_events()->at(5); + VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, left_dom_code, false, false); + ASSERT_FALSE(event.IsCommandDown()); +} + +TEST_F(KeyboardHookWinTest, SimpleFifoWinSequenceTest) { + const KeyboardCode left_key_code = KeyboardCode::VKEY_LWIN; + const DomCode left_dom_code = DomCode::META_LEFT; + const KeyboardCode right_key_code = KeyboardCode::VKEY_RWIN; + const DomCode right_dom_code = DomCode::META_RIGHT; + SendModifierKeyDownEvent(right_key_code, right_dom_code, 2); + SendModifierKeyDownEvent(left_key_code, left_dom_code, 2); + SendModifierKeyUpEvent(right_key_code, right_dom_code); + SendModifierKeyUpEvent(left_key_code, left_dom_code); + ASSERT_EQ(key_events()->size(), 6ULL); + + // First key down, no repeat. + KeyEvent event = key_events()->at(0); + VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, right_dom_code, true, false); + ASSERT_TRUE(event.IsCommandDown()); + + // First key still down, repeat. + event = key_events()->at(1); + VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, right_dom_code, true, true); + ASSERT_TRUE(event.IsCommandDown()); + + // Second key down, no repeat. + event = key_events()->at(2); + VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, left_dom_code, true, false); + ASSERT_TRUE(event.IsCommandDown()); + + // Second key still down, repeat. + event = key_events()->at(3); + VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, left_dom_code, true, true); + ASSERT_TRUE(event.IsCommandDown()); + + // First key up. + event = key_events()->at(4); + VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, right_dom_code, false, false); + ASSERT_TRUE(event.IsCommandDown()); + + // Second key up. + event = key_events()->at(5); + VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, left_dom_code, false, false); + ASSERT_FALSE(event.IsCommandDown()); +} + +TEST_F(KeyboardHookWinTest, CombinedModifierLifoSequenceKeypressTest) { + const KeyboardCode first_key_code = KeyboardCode::VKEY_LCONTROL; + const DomCode first_dom_code = DomCode::CONTROL_LEFT; + const KeyboardCode second_key_code = KeyboardCode::VKEY_RWIN; + const DomCode second_dom_code = DomCode::META_RIGHT; + const KeyboardCode third_key_code = KeyboardCode::VKEY_LMENU; + const DomCode third_dom_code = DomCode::ALT_LEFT; + SendModifierKeyDownEvent(first_key_code, first_dom_code, 2); + SendModifierKeyDownEvent(second_key_code, second_dom_code, 2); + SendModifierKeyDownEvent(third_key_code, third_dom_code, 2); + SendModifierKeyUpEvent(third_key_code, third_dom_code); + SendModifierKeyUpEvent(second_key_code, second_dom_code); + SendModifierKeyUpEvent(first_key_code, first_dom_code); + ASSERT_EQ(key_events()->size(), 9ULL); + + // First key down, no repeat. + KeyEvent event = key_events()->at(0); + VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, first_dom_code, true, + false); + ASSERT_TRUE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_FALSE(event.IsCommandDown()); + + // First key still down, repeat. + event = key_events()->at(1); + VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, first_dom_code, true, + true); + ASSERT_TRUE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_FALSE(event.IsCommandDown()); + + // Second key down, no repeat. + event = key_events()->at(2); + VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, second_dom_code, true, false); + ASSERT_TRUE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_TRUE(event.IsCommandDown()); + + // Second key still down, repeat. + event = key_events()->at(3); + VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, second_dom_code, true, true); + ASSERT_TRUE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_TRUE(event.IsCommandDown()); + + // Third key down, no repeat. + event = key_events()->at(4); + VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, third_dom_code, true, false); + ASSERT_TRUE(event.IsControlDown()); + ASSERT_TRUE(event.IsAltDown()); + ASSERT_TRUE(event.IsCommandDown()); + + // Third key still down, repeat. + event = key_events()->at(5); + VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, third_dom_code, true, true); + ASSERT_TRUE(event.IsControlDown()); + ASSERT_TRUE(event.IsAltDown()); + ASSERT_TRUE(event.IsCommandDown()); + + // Third key up. + event = key_events()->at(6); + VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, third_dom_code, false, false); + ASSERT_TRUE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_TRUE(event.IsCommandDown()); + + // Second key up. + event = key_events()->at(7); + VerifyKeyEvent(&event, KeyboardCode::VKEY_RWIN, second_dom_code, false, + false); + ASSERT_TRUE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_FALSE(event.IsCommandDown()); + + // First key up. + event = key_events()->at(8); + VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, first_dom_code, false, + false); + ASSERT_FALSE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_FALSE(event.IsCommandDown()); +} + +TEST_F(KeyboardHookWinTest, CombinedModifierFifoSequenceKeypressTest) { + const KeyboardCode first_key_code = KeyboardCode::VKEY_RCONTROL; + const DomCode first_dom_code = DomCode::CONTROL_RIGHT; + const KeyboardCode second_key_code = KeyboardCode::VKEY_LWIN; + const DomCode second_dom_code = DomCode::META_LEFT; + const KeyboardCode third_key_code = KeyboardCode::VKEY_RMENU; + const DomCode third_dom_code = DomCode::ALT_RIGHT; + SendModifierKeyDownEvent(first_key_code, first_dom_code, 2); + SendModifierKeyDownEvent(second_key_code, second_dom_code, 2); + SendModifierKeyDownEvent(third_key_code, third_dom_code, 2); + SendModifierKeyUpEvent(first_key_code, first_dom_code); + SendModifierKeyUpEvent(second_key_code, second_dom_code); + SendModifierKeyUpEvent(third_key_code, third_dom_code); + ASSERT_EQ(key_events()->size(), 9ULL); + + // First key down, no repeat. + KeyEvent event = key_events()->at(0); + VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, first_dom_code, true, + false); + ASSERT_TRUE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_FALSE(event.IsCommandDown()); + + // First key still down, repeat. + event = key_events()->at(1); + VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, first_dom_code, true, + true); + ASSERT_TRUE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_FALSE(event.IsCommandDown()); + + // Second key down, no repeat. + event = key_events()->at(2); + VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, second_dom_code, true, false); + ASSERT_TRUE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_TRUE(event.IsCommandDown()); + + // Second key still down, repeat. + event = key_events()->at(3); + VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, second_dom_code, true, true); + ASSERT_TRUE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_TRUE(event.IsCommandDown()); + + // Third key down, no repeat. + event = key_events()->at(4); + VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, third_dom_code, true, false); + ASSERT_TRUE(event.IsControlDown()); + ASSERT_TRUE(event.IsAltDown()); + ASSERT_TRUE(event.IsCommandDown()); + + // Third key still down, repeat. + event = key_events()->at(5); + VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, third_dom_code, true, true); + ASSERT_TRUE(event.IsControlDown()); + ASSERT_TRUE(event.IsAltDown()); + ASSERT_TRUE(event.IsCommandDown()); + + // First key up. + event = key_events()->at(6); + VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, first_dom_code, false, + false); + ASSERT_FALSE(event.IsControlDown()); + ASSERT_TRUE(event.IsAltDown()); + ASSERT_TRUE(event.IsCommandDown()); + + // Second key up. + event = key_events()->at(7); + VerifyKeyEvent(&event, KeyboardCode::VKEY_LWIN, second_dom_code, false, + false); + ASSERT_FALSE(event.IsControlDown()); + ASSERT_TRUE(event.IsAltDown()); + ASSERT_FALSE(event.IsCommandDown()); + + // Third key up. + event = key_events()->at(8); + VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, third_dom_code, false, false); + ASSERT_FALSE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_FALSE(event.IsCommandDown()); +} + +TEST_F(KeyboardHookWinTest, VerifyPlatformModifierStateTest) { + SendModifierKeyDownEvent(KeyboardCode::VKEY_LCONTROL, DomCode::CONTROL_LEFT); + ASSERT_TRUE(win::IsCtrlPressed()); + ASSERT_FALSE(win::IsAltPressed()); + ASSERT_FALSE(win::IsWindowsKeyPressed()); + + SendModifierKeyDownEvent(KeyboardCode::VKEY_RWIN, DomCode::META_RIGHT); + ASSERT_TRUE(win::IsCtrlPressed()); + ASSERT_FALSE(win::IsAltPressed()); + ASSERT_TRUE(win::IsWindowsKeyPressed()); + + SendModifierKeyDownEvent(KeyboardCode::VKEY_LMENU, DomCode::ALT_LEFT); + ASSERT_TRUE(win::IsCtrlPressed()); + ASSERT_TRUE(win::IsAltPressed()); + ASSERT_FALSE(win::IsAltRightPressed()); + ASSERT_TRUE(win::IsWindowsKeyPressed()); + + SendModifierKeyUpEvent(KeyboardCode::VKEY_RWIN, DomCode::META_RIGHT); + ASSERT_TRUE(win::IsCtrlPressed()); + ASSERT_TRUE(win::IsAltPressed()); + ASSERT_FALSE(win::IsWindowsKeyPressed()); + + SendModifierKeyUpEvent(KeyboardCode::VKEY_LCONTROL, DomCode::CONTROL_LEFT); + ASSERT_FALSE(win::IsCtrlPressed()); + ASSERT_TRUE(win::IsAltPressed()); + ASSERT_FALSE(win::IsWindowsKeyPressed()); + + SendModifierKeyUpEvent(KeyboardCode::VKEY_LMENU, DomCode::ALT_LEFT); + ASSERT_FALSE(win::IsCtrlPressed()); + ASSERT_FALSE(win::IsAltPressed()); + ASSERT_FALSE(win::IsWindowsKeyPressed()); + + SendModifierKeyDownEvent(KeyboardCode::VKEY_RMENU, DomCode::ALT_RIGHT); + ASSERT_TRUE(win::IsAltPressed()); + ASSERT_TRUE(win::IsAltRightPressed()); +} + +TEST_F(KeyboardHookWinTest, SimpleAltGrKeyPressTest) { + ScopedKeyboardLayout keyboard_layout(KeyboardLayout::KEYBOARD_LAYOUT_GERMAN); + + // AltGr produces two events, an injected, modified scan code for VK_LCONTROL, + // and an event for VK_RMENU. We simulate that sequence here. + const KeyboardCode altgr_key_code = KeyboardCode::VKEY_RMENU; + const DomCode altgr_dom_code = DomCode::ALT_RIGHT; + const DomCode control_dom_code = DomCode::CONTROL_LEFT; + const DWORD control_scan_code = + KeycodeConverter::DomCodeToNativeKeycode(control_dom_code); + const DWORD injected_control_scan_code = control_scan_code | 0x0200; + + ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage( + WM_KEYDOWN, KeyboardCode::VKEY_LCONTROL, injected_control_scan_code, + next_time_stamp())); + SendModifierKeyDownEvent(altgr_key_code, altgr_dom_code); + + ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage( + WM_KEYUP, KeyboardCode::VKEY_LCONTROL, + KeycodeConverter::DomCodeToNativeKeycode(control_dom_code), + next_time_stamp())); + SendModifierKeyUpEvent(altgr_key_code, altgr_dom_code); + ASSERT_EQ(key_events()->size(), 4ULL); + + // Injected control key down, no repeat. + KeyEvent event = key_events()->at(0); + VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, control_dom_code, true, + false); + ASSERT_TRUE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_FALSE(event.IsAltGrDown()); + ASSERT_FALSE(event.IsCommandDown()); + + // Altgr key down. + event = key_events()->at(1); + VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, altgr_dom_code, true, false); + ASSERT_FALSE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_TRUE(event.IsAltGrDown()); + ASSERT_FALSE(event.IsCommandDown()); + + // Injected control key up. + event = key_events()->at(2); + VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, control_dom_code, false, + false); + ASSERT_FALSE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_TRUE(event.IsAltGrDown()); + ASSERT_FALSE(event.IsCommandDown()); + + // AltGr key up. + event = key_events()->at(3); + VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, altgr_dom_code, false, false); + ASSERT_FALSE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_FALSE(event.IsAltGrDown()); + ASSERT_FALSE(event.IsCommandDown()); +} + +TEST_F(KeyboardHookWinTest, RepeatingAltGrKeyPressTest) { + ScopedKeyboardLayout keyboard_layout(KeyboardLayout::KEYBOARD_LAYOUT_GERMAN); + + // AltGr produces two events, an injected, modified scan code for VK_LCONTROL, + // and an event for VK_RMENU. This sequence repeats for each repeated key + // press. We simulate that sequence here. + const KeyboardCode altgr_key_code = KeyboardCode::VKEY_RMENU; + const DomCode altgr_dom_code = DomCode::ALT_RIGHT; + const DomCode control_dom_code = DomCode::CONTROL_LEFT; + const DWORD control_scan_code = + KeycodeConverter::DomCodeToNativeKeycode(control_dom_code); + const DWORD injected_control_scan_code = control_scan_code | 0x0200; + + ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage( + WM_KEYDOWN, KeyboardCode::VKEY_LCONTROL, injected_control_scan_code, + next_time_stamp())); + SendModifierKeyDownEvent(altgr_key_code, altgr_dom_code); + ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage( + WM_KEYDOWN, KeyboardCode::VKEY_LCONTROL, injected_control_scan_code, + next_time_stamp())); + SendModifierKeyDownEvent(altgr_key_code, altgr_dom_code); + ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage( + WM_KEYDOWN, KeyboardCode::VKEY_LCONTROL, injected_control_scan_code, + next_time_stamp())); + SendModifierKeyDownEvent(altgr_key_code, altgr_dom_code); + ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage( + WM_KEYUP, KeyboardCode::VKEY_LCONTROL, + KeycodeConverter::DomCodeToNativeKeycode(control_dom_code), + next_time_stamp())); + SendModifierKeyUpEvent(altgr_key_code, altgr_dom_code); + ASSERT_EQ(key_events()->size(), 8ULL); + + // Injected control key down, no repeat. + KeyEvent event = key_events()->at(0); + VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, control_dom_code, true, + false); + ASSERT_TRUE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_FALSE(event.IsAltGrDown()); + ASSERT_FALSE(event.IsCommandDown()); + + // Altgr key down. + event = key_events()->at(1); + VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, altgr_dom_code, true, false); + ASSERT_FALSE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_TRUE(event.IsAltGrDown()); + ASSERT_FALSE(event.IsCommandDown()); + + // Injected control key down, repeat. + event = key_events()->at(2); + VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, control_dom_code, true, + true); + ASSERT_FALSE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_TRUE(event.IsAltGrDown()); + ASSERT_FALSE(event.IsCommandDown()); + + // Altgr key down, repeat. + event = key_events()->at(3); + VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, altgr_dom_code, true, true); + ASSERT_FALSE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_TRUE(event.IsAltGrDown()); + ASSERT_FALSE(event.IsCommandDown()); + + // Injected control key down, repeat. + event = key_events()->at(4); + VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, control_dom_code, true, + true); + ASSERT_FALSE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_TRUE(event.IsAltGrDown()); + ASSERT_FALSE(event.IsCommandDown()); + + // Altgr key down, repeat. + event = key_events()->at(5); + VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, altgr_dom_code, true, true); + ASSERT_FALSE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_TRUE(event.IsAltGrDown()); + ASSERT_FALSE(event.IsCommandDown()); + + // Injected control key up. + event = key_events()->at(6); + VerifyKeyEvent(&event, KeyboardCode::VKEY_CONTROL, control_dom_code, false, + false); + ASSERT_FALSE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_TRUE(event.IsAltGrDown()); + ASSERT_FALSE(event.IsCommandDown()); + + // AltGr key up. + event = key_events()->at(7); + VerifyKeyEvent(&event, KeyboardCode::VKEY_MENU, altgr_dom_code, false, false); + ASSERT_FALSE(event.IsControlDown()); + ASSERT_FALSE(event.IsAltDown()); + ASSERT_FALSE(event.IsAltGrDown()); + ASSERT_FALSE(event.IsCommandDown()); +} + +TEST_F(KeyboardHookWinTest, VerifyAltGrPlatformModifierStateTest) { + ScopedKeyboardLayout keyboard_layout(KeyboardLayout::KEYBOARD_LAYOUT_GERMAN); + + // AltGr produces two events, an injected, modified scan code for VK_LCONTROL, + // and an event for VK_RMENU. We simulate that sequence here. + const KeyboardCode altgr_key_code = KeyboardCode::VKEY_RMENU; + const DomCode altgr_dom_code = DomCode::ALT_RIGHT; + const DomCode control_dom_code = DomCode::CONTROL_LEFT; + const DWORD control_scan_code = + KeycodeConverter::DomCodeToNativeKeycode(control_dom_code); + const DWORD injected_control_scan_code = control_scan_code | 0x0200; + + ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage( + WM_KEYDOWN, KeyboardCode::VKEY_LCONTROL, injected_control_scan_code, + next_time_stamp())); + ASSERT_TRUE(win::IsCtrlPressed()); + ASSERT_FALSE(win::IsAltPressed()); + ASSERT_FALSE(win::IsAltRightPressed()); + ASSERT_FALSE(win::IsWindowsKeyPressed()); + + SendModifierKeyDownEvent(altgr_key_code, altgr_dom_code); + ASSERT_TRUE(win::IsCtrlPressed()); + ASSERT_TRUE(win::IsAltPressed()); + ASSERT_TRUE(win::IsAltRightPressed()); + ASSERT_FALSE(win::IsWindowsKeyPressed()); + + ASSERT_TRUE(keyboard_hook()->ProcessKeyEventMessage( + WM_KEYUP, KeyboardCode::VKEY_LCONTROL, + KeycodeConverter::DomCodeToNativeKeycode(control_dom_code), + next_time_stamp())); + ASSERT_FALSE(win::IsCtrlPressed()); + ASSERT_TRUE(win::IsAltPressed()); + ASSERT_TRUE(win::IsAltRightPressed()); + ASSERT_FALSE(win::IsWindowsKeyPressed()); + + SendModifierKeyUpEvent(altgr_key_code, altgr_dom_code); + ASSERT_FALSE(win::IsCtrlPressed()); + ASSERT_FALSE(win::IsAltPressed()); + ASSERT_FALSE(win::IsAltRightPressed()); + ASSERT_FALSE(win::IsWindowsKeyPressed()); +} + +TEST_F(KeyboardHookWinTest, NonInterceptedKeysTest) { + // Here we try a few keys we do not expect to be intercepted / handled. + ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage( + WM_KEYDOWN, KeyboardCode::VKEY_RSHIFT, + KeycodeConverter::DomCodeToNativeKeycode(DomCode::SHIFT_RIGHT), + next_time_stamp())); + ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage( + WM_KEYUP, KeyboardCode::VKEY_RSHIFT, + KeycodeConverter::DomCodeToNativeKeycode(DomCode::SHIFT_RIGHT), + next_time_stamp())); + + ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage( + WM_KEYDOWN, KeyboardCode::VKEY_MEDIA_PLAY_PAUSE, + KeycodeConverter::DomCodeToNativeKeycode(DomCode::MEDIA_PLAY_PAUSE), + next_time_stamp())); + ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage( + WM_KEYUP, KeyboardCode::VKEY_MEDIA_PLAY_PAUSE, + KeycodeConverter::DomCodeToNativeKeycode(DomCode::MEDIA_PLAY_PAUSE), + next_time_stamp())); + + ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage( + WM_KEYDOWN, KeyboardCode::VKEY_A, + KeycodeConverter::DomCodeToNativeKeycode(DomCode::US_A), + next_time_stamp())); + ASSERT_FALSE(keyboard_hook()->ProcessKeyEventMessage( + WM_KEYUP, KeyboardCode::VKEY_A, + KeycodeConverter::DomCodeToNativeKeycode(DomCode::US_A), + next_time_stamp())); +} + +} // namespace ui diff --git a/chromium/ui/events/x/events_x.cc b/chromium/ui/events/x/events_x.cc index b8df7b02091..62530318bc2 100644 --- a/chromium/ui/events/x/events_x.cc +++ b/chromium/ui/events/x/events_x.cc @@ -81,9 +81,7 @@ int EventFlagsFromNative(const PlatformEvent& native_event) { } base::TimeTicks EventTimeFromNative(const PlatformEvent& native_event) { - base::TimeTicks timestamp = EventTimeFromXEvent(*native_event); - ValidateEventTimeClock(×tamp); - return timestamp; + return EventTimeFromXEvent(*native_event); } gfx::PointF EventLocationFromNative(const PlatformEvent& native_event) { diff --git a/chromium/ui/events/x/events_x_unittest.cc b/chromium/ui/events/x/events_x_unittest.cc index 897c5c5cb94..3419309a22a 100644 --- a/chromium/ui/events/x/events_x_unittest.cc +++ b/chromium/ui/events/x/events_x_unittest.cc @@ -76,6 +76,15 @@ float ComputeRotationAngle(float twist) { return rotation_angle; } +class MockTimestampServer : public ui::TimestampServer { + public: + Time GetCurrentServerTime() override { return base_time_; } + void SetBaseTime(Time time) { base_time_ = time; } + + private: + Time base_time_ = 0; +}; + } // namespace class EventsXTest : public testing::Test { @@ -84,13 +93,15 @@ class EventsXTest : public testing::Test { ~EventsXTest() override {} void SetUp() override { + SetTimestampServer(&server_); DeviceDataManagerX11::CreateInstance(); ui::TouchFactory::GetInstance()->ResetForTest(); - ResetTimestampRolloverCountersForTesting(); } - void TearDown() override { ResetTimestampRolloverCountersForTesting(); } + + void TearDown() override { SetTimestampServer(nullptr); } private: + MockTimestampServer server_; DISALLOW_COPY_AND_ASSIGN(EventsXTest); }; @@ -542,52 +553,4 @@ TEST_F(EventsXTest, IgnoresMotionEventForMouseWheelScroll) { EXPECT_EQ(ui::ET_UNKNOWN, ui::EventTypeFromNative(xev)); } -namespace { - -// Returns a fake TimeTicks based on the given millisecond offset. -base::TimeTicks TimeTicksFromMillis(int64_t millis) { - return base::TimeTicks() + base::TimeDelta::FromMilliseconds(millis); -} - -} // namespace - -TEST_F(EventsXTest, TimestampRolloverAndAdjustWhenDecreasing) { - XEvent event; - InitButtonEvent(&event, true, gfx::Point(5, 10), 1, 0); - - test::ScopedEventTestTickClock clock; - clock.SetNowTicks(TimeTicksFromMillis(0x100000001)); - ResetTimestampRolloverCountersForTesting(); - - event.xbutton.time = 0xFFFFFFFF; - EXPECT_EQ(TimeTicksFromMillis(0xFFFFFFFF), ui::EventTimeFromNative(&event)); - - clock.SetNowTicks(TimeTicksFromMillis(0x100000007)); - ResetTimestampRolloverCountersForTesting(); - - event.xbutton.time = 3; - EXPECT_EQ(TimeTicksFromMillis(0x100000000 + 3), - ui::EventTimeFromNative(&event)); -} - -TEST_F(EventsXTest, NoTimestampRolloverWhenMonotonicIncreasing) { - XEvent event; - InitButtonEvent(&event, true, gfx::Point(5, 10), 1, 0); - - test::ScopedEventTestTickClock clock; - clock.SetNowTicks(TimeTicksFromMillis(10)); - ResetTimestampRolloverCountersForTesting(); - - event.xbutton.time = 6; - EXPECT_EQ(TimeTicksFromMillis(6), ui::EventTimeFromNative(&event)); - event.xbutton.time = 7; - EXPECT_EQ(TimeTicksFromMillis(7), ui::EventTimeFromNative(&event)); - - clock.SetNowTicks(TimeTicksFromMillis(0x100000005)); - ResetTimestampRolloverCountersForTesting(); - - event.xbutton.time = 0xFFFFFFFF; - EXPECT_EQ(TimeTicksFromMillis(0xFFFFFFFF), ui::EventTimeFromNative(&event)); -} - } // namespace ui diff --git a/chromium/ui/events/x/events_x_utils.cc b/chromium/ui/events/x/events_x_utils.cc index 86fbd1937c6..8dec888198a 100644 --- a/chromium/ui/events/x/events_x_utils.cc +++ b/chromium/ui/events/x/events_x_utils.cc @@ -27,6 +27,25 @@ namespace { +ui::TimestampServer* g_timestamp_server = nullptr; +bool g_use_fixed_time_for_testing = false; + +// Clamps a TimeDelta to be within [-30 seconds, 30 seconds]. +base::TimeDelta ClampDeltaFromExternalSource(const base::TimeDelta& delta) { + // Ignore pathologically long deltas. External source is probably having + // issues. + constexpr base::TimeDelta pathologically_long_duration = + base::TimeDelta::FromSeconds(30); + if (delta > pathologically_long_duration) + return base::TimeDelta(); + + // Ignore negative deltas. External source is probably having issues. + if (delta < -pathologically_long_duration) + return base::TimeDelta(); + + return delta; +} + // Scroll amount for each wheelscroll event. 53 is also the value used for GTK+. const int kWheelScrollAmount = 53; @@ -308,40 +327,36 @@ bool GetGestureTimes(const XEvent& xev, double* start_time, double* end_time) { return true; } -int64_t g_last_seen_timestamp_ms = 0; -int64_t g_rollover_ms = 0; - -// Takes Xlib Time and returns a time delta that is immune to timer rollover. -// This function is not thread safe as we do not use a lock. base::TimeTicks TimeTicksFromXEventTime(Time timestamp) { - int64_t timestamp64 = timestamp; - - if (!timestamp) - return ui::EventTimeForNow(); - - // If this is the first event that we get, assume the time stamp roll-over - // might have happened before the process was started. - // Register a rollover if the distance between last timestamp and current one - // is larger than half the width. This avoids false rollovers even in a case - // where X server delivers reasonably close events out-of-order. - bool had_recent_rollover = - !g_last_seen_timestamp_ms || - g_last_seen_timestamp_ms - timestamp64 > (UINT32_MAX >> 1); - - g_last_seen_timestamp_ms = timestamp64; - if (!had_recent_rollover) - return base::TimeTicks() + - base::TimeDelta::FromMilliseconds(g_rollover_ms + timestamp); - - DCHECK(timestamp64 <= UINT32_MAX) - << "X11 Time does not roll over 32 bit, the below logic is likely wrong"; - - base::TimeTicks now_ticks = ui::EventTimeForNow(); - int64_t now_ms = (now_ticks - base::TimeTicks()).InMilliseconds(); - - g_rollover_ms = now_ms & ~static_cast<int64_t>(UINT32_MAX); - uint32_t delta = static_cast<uint32_t>(now_ms - timestamp); - return base::TimeTicks() + base::TimeDelta::FromMilliseconds(now_ms - delta); + // There's no way to convert from an X time to a base::TimeTicks without + // knowing the current X server time. + if (!g_timestamp_server) + return base::TimeTicks(); + + // X11 uses a uint32_t on the wire protocol. Xlib casts this to an unsigned + // long by prepending with 0s. We cast back to a uint32_t so that subtraction + // works properly when the timestamp overflows back to 0. + uint32_t event_server_time_ms = static_cast<uint32_t>(timestamp); + uint32_t current_server_time_ms = + static_cast<uint32_t>(g_timestamp_server->GetCurrentServerTime()); + + // On X11, event times are in X11 Server time. To convert to base::TimeTicks, + // we perform a round-trip to the X11 Server, subtract the two times to get a + // TimeDelta, and then subtract that from base::TimeTicks::Now(). Since we're + // working with units of time from an external source, we clamp the TimeDelta + // to reasonable values. + int64_t delta_ms = static_cast<int64_t>(current_server_time_ms) - + static_cast<int64_t>(event_server_time_ms); + base::TimeDelta delta = base::TimeDelta::FromMilliseconds(delta_ms); + base::TimeDelta sanitized = ClampDeltaFromExternalSource(delta); + + base::TimeTicks now; + if (g_use_fixed_time_for_testing) + now = base::TimeTicks() + base::TimeDelta::FromDays(1); + else + now = base::TimeTicks::Now(); + + return now - sanitized; } } // namespace @@ -831,9 +846,16 @@ bool IsAltPressed() { return XModifierStateWatcher::GetInstance()->state() & Mod1Mask; } -void ResetTimestampRolloverCountersForTesting() { - g_last_seen_timestamp_ms = 0; - g_rollover_ms = 0; +void SetTimestampServer(TimestampServer* server) { + // This method must be setting or unsetting a timestamp server. It should + // never replace an existing timestamp server, nor change from + // nullptr->nullptr. + CHECK(!!g_timestamp_server ^ !!server); + g_timestamp_server = server; +} + +void SetUseFixedTimeForXEventTesting(bool use_fixed_time) { + g_use_fixed_time_for_testing = use_fixed_time; } } // namespace ui diff --git a/chromium/ui/events/x/events_x_utils.h b/chromium/ui/events/x/events_x_utils.h index 64814e87715..ad11a08a17a 100644 --- a/chromium/ui/events/x/events_x_utils.h +++ b/chromium/ui/events/x/events_x_utils.h @@ -16,6 +16,8 @@ #include "ui/gfx/geometry/point.h" #include "ui/gfx/x/x11_types.h" +using Time = unsigned long; + namespace ui { // Gets the EventType from a XEvent. @@ -91,6 +93,18 @@ EVENTS_X_EXPORT bool IsAltPressed(); EVENTS_X_EXPORT void ResetTimestampRolloverCountersForTesting(); +// Conversion from X Time to base::TimeTicks requires checking the current X +// Server Time. This functionality is provided by X11EventSource, but due to odd +// layering that cannot be referenced directly. +class TimestampServer { + public: + virtual Time GetCurrentServerTime() = 0; +}; +EVENTS_X_EXPORT void SetTimestampServer(TimestampServer* server); + +// Allows tests to force a fixed time.. +EVENTS_X_EXPORT void SetUseFixedTimeForXEventTesting(bool use_fixed_time); + } // namespace ui #endif // UI_EVENTS_X_EVENTS_X_UTILS_H_ diff --git a/chromium/ui/file_manager/BUILD.gn b/chromium/ui/file_manager/BUILD.gn index 01983b819db..99433199a4b 100644 --- a/chromium/ui/file_manager/BUILD.gn +++ b/chromium/ui/file_manager/BUILD.gn @@ -33,6 +33,7 @@ group("closure_compile") { deps = [ "audio_player/elements:closure_compile", "audio_player/js:closure_compile", + "base/js:closure_compile", "file_manager/background/js:closure_compile", "file_manager/common/js:closure_compile", "file_manager/foreground/elements:closure_compile", @@ -50,7 +51,10 @@ group("closure_compile") { group("unit_test_data") { deps = [ + "file_manager/common/js:unit_tests", + "file_manager/foreground/js:unit_tests", "gallery/js:unit_tests", "gallery/js/image_editor:unit_tests", + "image_loader:unit_tests", ] } diff --git a/chromium/ui/file_manager/audio_player/js/BUILD.gn b/chromium/ui/file_manager/audio_player/js/BUILD.gn index 2225d46efb7..89c18c22f10 100644 --- a/chromium/ui/file_manager/audio_player/js/BUILD.gn +++ b/chromium/ui/file_manager/audio_player/js/BUILD.gn @@ -18,10 +18,8 @@ js_library("closure_compile_externs") { sources = [] externs_list = [ "$externs_path/chrome_extensions.js", - "$externs_path/command_line_private.js", "$externs_path/metrics_private.js", "../../externs/audio_player_foreground.js", - "../../externs/entry_location.js", "../../externs/platform.js", "//third_party/analytics/externs.js", ] @@ -29,12 +27,12 @@ js_library("closure_compile_externs") { js_library("audio_player") { deps = [ - "../../file_manager/common/js:util", - "../../file_manager/foreground/js:volume_manager_wrapper", - "../../file_manager/foreground/js/metadata:content_metadata_provider", - "../../file_manager/foreground/js/metadata:metadata_model", "../elements:audio_player", "../elements:track_list", + "//ui/file_manager/base/js:filtered_volume_manager", + "//ui/file_manager/file_manager/common/js:util", + "//ui/file_manager/file_manager/foreground/js/metadata:content_metadata_provider", + "//ui/file_manager/file_manager/foreground/js/metadata:metadata_model", ] } diff --git a/chromium/ui/file_manager/base/js/BUILD.gn b/chromium/ui/file_manager/base/js/BUILD.gn new file mode 100644 index 00000000000..b71798141bb --- /dev/null +++ b/chromium/ui/file_manager/base/js/BUILD.gn @@ -0,0 +1,27 @@ +# 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. + +import("//third_party/closure_compiler/compile_js.gni") + +visibility = [ "//ui/file_manager/*" ] + +js_type_check("closure_compile") { + deps = [ + ":filtered_volume_manager", + ] +} + +js_library("filtered_volume_manager") { + deps = [ + "//ui/file_manager/externs:file_manager_private", + "//ui/file_manager/externs:volume_manager", + "//ui/file_manager/file_manager/common/js:async_util", + "//ui/file_manager/file_manager/common/js:files_app_entry_types", + "//ui/file_manager/file_manager/common/js:volume_manager_common", + "//ui/webui/resources/js:cr", + "//ui/webui/resources/js/cr/ui:array_data_model", + ] + externs_list = + [ "//ui/file_manager/externs/background/volume_manager_factory.js" ] +} diff --git a/chromium/ui/file_manager/externs/BUILD.gn b/chromium/ui/file_manager/externs/BUILD.gn index 1d7d52ae3dc..dad5e37013e 100644 --- a/chromium/ui/file_manager/externs/BUILD.gn +++ b/chromium/ui/file_manager/externs/BUILD.gn @@ -15,3 +15,29 @@ js_library("webview_tag") { "webview_tag.js", ] } + +js_library("file_manager_private") { + sources = [] + + # The file_manager_private extern depends on file_system_provider and + # extension APIs. Ensure they're pulled in together. + externs_list = [ + "$externs_path/chrome.js", + "$externs_path/chrome_extensions.js", + "$externs_path/file_manager_private.js", + "$externs_path/file_system_provider.js", + ] +} + +js_library("volume_manager") { + sources = [] + + # Encapsulate volume_manager.js and its dependencies. Note this should really + # depend on volume_manager_common.js as well, but that's not an extern. + externs_list = [ + "entry_location.js", + "volume_info.js", + "volume_info_list.js", + "volume_manager.js", + ] +} diff --git a/chromium/ui/file_manager/file_manager/background/js/BUILD.gn b/chromium/ui/file_manager/file_manager/background/js/BUILD.gn index d6a933fbfe3..a645f6897d8 100644 --- a/chromium/ui/file_manager/file_manager/background/js/BUILD.gn +++ b/chromium/ui/file_manager/file_manager/background/js/BUILD.gn @@ -4,6 +4,22 @@ import("//third_party/closure_compiler/compile_js.gni") +# TODO(tapted): This folder should be restricted to file_manager, but related +# apps currently depend on background_base, which depends on +# volume_manager_factory, and that pulls in nearly everything else. For now, +# document externally-exposed targets visible with this helper, and hide +# transitive dependencies (but note those transitive dependencies should move +# elsewhere too). +related_apps = [ + "//ui/file_manager/audio_player/*", + "//ui/file_manager/file_manager/*", + "//ui/file_manager/gallery/*", + "//ui/file_manager/video_player/*", +] + +# Default to private. +visibility = [ ":*" ] + js_type_check("closure_compile") { deps = [ ":app_window_wrapper", @@ -39,9 +55,6 @@ js_type_check("closure_compile") { js_library("closure_compile_externs") { sources = [] externs_list = [ - "$externs_path/command_line_private.js", - "$externs_path/file_manager_private.js", - "$externs_path/file_system_provider.js", "$externs_path/metrics_private.js", "../../../externs/background/file_browser_background.js", "../../../externs/background/file_browser_background_full.js", @@ -62,6 +75,7 @@ js_library("closure_compile_externs") { } js_library("app_window_wrapper") { + visibility += related_apps deps = [ ":app_windows", "../../common/js:async_util", @@ -94,6 +108,7 @@ js_library("background") { } js_library("background_base") { + visibility += related_apps deps = [ ":app_windows", ":volume_manager_factory", @@ -211,10 +226,12 @@ js_library("media_scanner") { } js_library("mock_volume_manager") { + visibility += related_apps deps = [ ":volume_info_impl", ":volume_info_list_impl", ":volume_manager_factory", + ":volume_manager_impl", "../../common/js:mock_entry", ] } @@ -236,21 +253,17 @@ js_library("task_queue") { } js_library("test_util_base") { + # TODO(tapted): Move this target to //ui/file_manager/base. It is used in the + # background page of all |related_apps|, but accessed via extension messaging. + # So it doesn't need to be visible as a closure dependency, except for the + # "unpacked" test framework. + visibility += [ "//ui/file_manager/file_manager/test/js:test_util" ] + deps = [ ":app_windows", + "../../../externs:webview_tag", "../../common/js:error_util", ] - - # The callback test_util_base.js passes to chrome.runtime.onMessageExternal() - # requires chrome_extensions.js, which has a dependency on chrome.js. Ensure - # the externs are introduced at the same time (in this order). Note we are - # lucky the list below also sorts in this order, or we'd need some other fix. - # A compile error results if the dependency walker encounters a target - # that puts chrome_extensions.js into the argument list before chrome.js. - externs_list = [ - "$externs_path/chrome.js", - "$externs_path/chrome_extensions.js", - ] } js_library("volume_info_impl") { diff --git a/chromium/ui/file_manager/file_manager/common/js/BUILD.gn b/chromium/ui/file_manager/file_manager/common/js/BUILD.gn index 506fde5ff24..f4b2fdf8e92 100644 --- a/chromium/ui/file_manager/file_manager/common/js/BUILD.gn +++ b/chromium/ui/file_manager/file_manager/common/js/BUILD.gn @@ -3,11 +3,14 @@ # found in the LICENSE file. import("//third_party/closure_compiler/compile_js.gni") +import("//third_party/closure_compiler/js_unit_tests.gni") -js_type_check("closure_compile") { +# TODO(tapted): This entire folder should move to //ui/file_manager/base. +visibility = [ "//ui/file_manager/*" ] + +js_type_check("closure_compile_module") { deps = [ ":async_util", - ":closure_compile_externs", ":error_util", ":file_type", ":files_app_entry_types", @@ -24,45 +27,95 @@ js_type_check("closure_compile") { ] } -js_library("closure_compile_externs") { - sources = [] - externs_list = [ - "../../../externs/background/file_browser_background.js", - "../../../externs/background_window.js", - ] +js_library("async_util") { } -js_library("async_util") { +js_library("async_util_unittest") { + deps = [ + ":async_util", + ":unittest_util", + ] } js_library("error_util") { } js_library("files_app_entry_types") { + deps = [ + "../../../externs:file_manager_private", + ] externs_list = [ "../../../externs/volume_info.js" ] } +js_library("files_app_entry_types_unittest") { + deps = [ + ":files_app_entry_types", + ":unittest_util", + ":volume_manager_common", + ] +} + js_library("file_type") { + deps = [ + ":files_app_entry_types", + ":volume_manager_common", + ] } +# These importer files actually belong here. Nothing outside the Files app uses +# them, so restrict visibility. TODO(tapted): Simplify visibility when +# everything else moves to //ui/file_manager/base. js_library("importer_common") { + visibility = [] + visibility = [ "//ui/file_manager/file_manager/*" ] deps = [ ":file_type", ":volume_manager_common", + "../../../externs:volume_manager", + ] + externs_list = [ + "//third_party/analytics/externs.js", + "../../../externs/volume_info_list.js", + "../../../externs/background_window.js", + "../../../externs/background/file_browser_background.js", + ] +} + +js_library("test_importer_common") { + deps = [ + ":importer_common", + ":unittest_util", + ] + visibility = [] + visibility = [ "//ui/file_manager/file_manager/*" ] +} + +js_library("importer_common_unittest") { + deps = [ + ":mock_entry", + ":test_importer_common", + ":util", + "//ui/file_manager/file_manager/background/js:mock_volume_manager", ] } js_library("lru_cache") { } +js_library("lru_cache_unittest") { + deps = [ + ":lru_cache", + "//ui/webui/resources/js:webui_resource_test", + ] +} + js_library("metrics") { deps = [ ":metrics_base", + "../../../externs:file_manager_private", "//ui/webui/resources/js:assert", ] externs_list = [ - "$externs_path/file_manager_private.js", - "$externs_path/file_system_provider.js", "$externs_path/metrics_private.js", "//third_party/analytics/externs.js", ] @@ -91,20 +144,20 @@ js_library("unittest_util") { deps = [ "//ui/webui/resources/js:webui_resource_test", ] + externs_list = [ "$externs_path/command_line_private.js" ] } js_library("util") { deps = [ ":files_app_entry_types", ":volume_manager_common", + "../../../externs:file_manager_private", "//ui/webui/resources/js:load_time_data", "//ui/webui/resources/js:util", "//ui/webui/resources/js/cr:event_target", "//ui/webui/resources/js/cr:ui", ] externs_list = [ - "$externs_path/chrome.js", - "$externs_path/chrome_extensions.js", "$externs_path/command_line_private.js", "../../../externs/app_window_common.js", "../../../externs/entry_location.js", @@ -112,8 +165,40 @@ js_library("util") { ] } +js_library("util_unittest") { + deps = [ + ":mock_entry", + ":unittest_util", + ":util", + ] +} + js_library("volume_manager_common") { deps = [ "//ui/webui/resources/js:assert", ] } + +js_unit_tests("unit_tests") { + deps = [ + ":async_util_unittest", + ":files_app_entry_types_unittest", + ":importer_common_unittest", + ":lru_cache_unittest", + ":util_unittest", + ] +} + +js_type_check("test_support_type_check") { + deps = [ + ":test_importer_common", + ] +} + +group("closure_compile") { + deps = [ + ":closure_compile_module", + ":test_support_type_check", + ":unit_tests_type_check", + ] +} diff --git a/chromium/ui/file_manager/file_manager/foreground/elements/BUILD.gn b/chromium/ui/file_manager/file_manager/foreground/elements/BUILD.gn index dcb7cb91d6a..b1ecbbcb771 100644 --- a/chromium/ui/file_manager/file_manager/foreground/elements/BUILD.gn +++ b/chromium/ui/file_manager/file_manager/foreground/elements/BUILD.gn @@ -4,6 +4,8 @@ import("//third_party/closure_compiler/compile_js.gni") +visibility = [ "//ui/file_manager/file_manager/foreground/*" ] + js_type_check("closure_compile") { deps = [ ":files_icon_button", @@ -51,12 +53,18 @@ js_library("files_safe_media") { js_library("files_safe_media_webview_content") { } +# TODO(tapted): Move this to //ui/file_manager/base. js_library("files_toast") { + visibility += [ "//ui/file_manager/gallery/*" ] externs_list = [ "$externs_path/web_animations.js" ] } +# TODO(tapted): Move this to //ui/file_manager/base. js_library("files_toggle_ripple") { + visibility += [ "//ui/file_manager/gallery/*" ] } +# TODO(tapted): Move this to //ui/file_manager/base. js_library("files_tooltip") { + visibility += [ "//ui/file_manager/gallery/*" ] } diff --git a/chromium/ui/file_manager/file_manager/foreground/js/BUILD.gn b/chromium/ui/file_manager/file_manager/foreground/js/BUILD.gn index 4d9de83bf71..2823093fb10 100644 --- a/chromium/ui/file_manager/file_manager/foreground/js/BUILD.gn +++ b/chromium/ui/file_manager/file_manager/foreground/js/BUILD.gn @@ -3,8 +3,14 @@ # found in the LICENSE file. import("//third_party/closure_compiler/compile_js.gni") +import("//third_party/closure_compiler/js_unit_tests.gni") -js_type_check("closure_compile") { +visibility = [ + "//ui/file_manager/file_manager/foreground/*", + "//ui/file_manager/file_manager/test/*", +] + +js_type_check("closure_compile_module") { deps = [ ":actions_controller", ":actions_model", @@ -12,6 +18,7 @@ js_type_check("closure_compile") { ":closure_compile_externs", ":column_visibility_controller", ":constants", + ":crostini", ":dialog_action_controller", ":dialog_type", ":directory_contents", @@ -49,14 +56,12 @@ js_type_check("closure_compile") { ":scan_controller", ":search_controller", ":selection_menu_controller", - ":share_client", ":sort_menu_controller", ":spinner_controller", ":task_controller", ":task_history", ":thumbnail_loader", ":toolbar_controller", - ":volume_manager_wrapper", ":web_store_utils", ":webui_command_extender", ] @@ -110,11 +115,11 @@ js_library("actions_controller") { js_library("actions_model") { deps = [ ":folder_shortcuts_data_model", + "../../common/js:util", "metadata:metadata_model", "ui:error_dialog", "ui:files_alert_dialog", "ui:list_container", - "ui:share_dialog", "//ui/webui/resources/js:cr", ] } @@ -146,7 +151,6 @@ js_library("dialog_action_controller") { ":file_selection", ":launch_param", ":naming_controller", - ":volume_manager_wrapper", "../../common/js:metrics", "metadata:metadata_model", "ui:dialog_footer", @@ -154,6 +158,10 @@ js_library("dialog_action_controller") { ] } +js_library("crostini") { + deps = [] +} + js_library("dialog_type") { } @@ -175,7 +183,6 @@ js_library("directory_model") { deps = [ ":directory_contents", ":file_watcher", - ":volume_manager_wrapper", "../../common/js:importer_common", "../../common/js:metrics_events", "ui:file_list_selection_model", @@ -185,7 +192,6 @@ js_library("directory_model") { js_library("navigation_uma") { deps = [ ":dialog_type", - ":volume_manager_wrapper", "../../common/js:metrics", ] } @@ -253,10 +259,10 @@ js_library("file_manager") { ":spinner_controller", ":task_controller", ":toolbar_controller", - ":volume_manager_wrapper", "ui:commandbutton", "ui:directory_tree", "ui:file_manager_ui", + "//ui/file_manager/base/js:filtered_volume_manager", "//ui/webui/resources/js/cr/ui:list_selection_model", ] } @@ -274,7 +280,6 @@ js_library("file_manager_commands") { ":providers_model", ":spinner_controller", ":task_controller", - ":volume_manager_wrapper", "ui:directory_tree", "ui:file_manager_ui", "//ui/webui/resources/cr_elements/cr_input:cr_input", @@ -285,7 +290,6 @@ js_library("file_selection") { deps = [ ":constants", ":directory_model", - ":volume_manager_wrapper", "../../common/js:file_type", "../../common/js:util", "../../common/js:volume_manager_common", @@ -298,9 +302,9 @@ js_library("file_selection") { js_library("file_tasks") { deps = [ + ":crostini", ":directory_model", ":task_history", - ":volume_manager_wrapper", "metadata:metadata_model", "ui:file_manager_ui", ] @@ -311,7 +315,6 @@ js_library("file_transfer_controller") { ":directory_model", ":drop_effect_and_label", ":file_selection", - ":volume_manager_wrapper", ":webui_command_extender", "../../common/js:progress_center_common", "metadata:metadata_model", @@ -335,11 +338,11 @@ js_library("file_watcher") { js_library("folder_shortcuts_data_model") { deps = [ - ":volume_manager_wrapper", - "../../common/js:async_util", - "../../common/js:metrics", - "../../common/js:util", - "../../common/js:volume_manager_common", + "//ui/file_manager/base/js:filtered_volume_manager", + "//ui/file_manager/file_manager/common/js:async_util", + "//ui/file_manager/file_manager/common/js:metrics", + "//ui/file_manager/file_manager/common/js:util", + "//ui/file_manager/file_manager/common/js:volume_manager_common", ] } @@ -389,7 +392,6 @@ js_library("list_thumbnail_loader") { ":directory_model", ":file_list_model", ":thumbnail_loader", - ":volume_manager_wrapper", "../../common/js:volume_manager_common", "metadata:thumbnail_model", ] @@ -413,7 +415,6 @@ js_library("main_window_component") { ":file_selection", ":naming_controller", ":task_controller", - ":volume_manager_wrapper", "ui:file_manager_ui", ] } @@ -457,7 +458,6 @@ js_library("naming_controller") { js_library("navigation_list_model") { deps = [ ":folder_shortcuts_data_model", - ":volume_manager_wrapper", "//ui/webui/resources/js/cr:event_target", "//ui/webui/resources/js/cr/ui:array_data_model", ] @@ -472,7 +472,6 @@ js_library("progress_center_item_group") { js_library("providers_model") { deps = [ - ":volume_manager_wrapper", "//ui/webui/resources/js:assert", ] } @@ -485,7 +484,6 @@ js_library("quick_view_controller") { ":quick_view_model", ":quick_view_uma", ":task_controller", - ":volume_manager_wrapper", "metadata:metadata_model", "ui:list_container", "//ui/webui/resources/js/cr/ui:list_selection_model", @@ -503,7 +501,6 @@ js_library("quick_view_uma") { deps = [ ":dialog_type", ":file_tasks", - ":volume_manager_wrapper", "../../common/js:file_type", ] } @@ -535,14 +532,6 @@ js_library("selection_menu_controller") { ] } -js_library("share_client") { - deps = [ - "../../../externs:webview_tag", - "../../common/js:volume_manager_common", - "//ui/webui/resources/js/cr:event_target", - ] -} - js_library("sort_menu_controller") { deps = [ ":file_list_model", @@ -565,7 +554,6 @@ js_library("task_controller") { ":file_tasks", ":metadata_update_controller", ":task_history", - ":volume_manager_wrapper", "metadata:metadata_model", "ui:file_manager_ui", ] @@ -577,7 +565,9 @@ js_library("task_history") { ] } +# TODO(tapted): Move this into //ui/file_manager/base. js_library("thumbnail_loader") { + visibility += [ "//ui/file_manager/gallery/*" ] deps = [ "../../../image_loader:image_loader_client", "../../common/js:file_type", @@ -586,6 +576,15 @@ js_library("thumbnail_loader") { ] } +js_library("thumbnail_loader_unittest") { + deps = [ + ":thumbnail_loader", + "../../common/js:mock_entry", + "../../common/js:unittest_util", + "//ui/webui/resources/js:webui_resource_test", + ] +} + js_library("toolbar_controller") { deps = [ ":file_selection", @@ -596,27 +595,6 @@ js_library("toolbar_controller") { ] } -js_library("volume_manager_wrapper") { - deps = [ - "../../common/js:async_util", - "../../common/js:volume_manager_common", - "//ui/webui/resources/js:cr", - "//ui/webui/resources/js/cr:event_target", - "//ui/webui/resources/js/cr/ui:array_data_model", - ] - externs_list = [ - # Note: volume_info has a dependency on chrome.fileManagerPrivate.IconSet. - # Also, fileManagerPrivate depends on chrome.fileSystemProvider, so these - # must be introduced together. - "$externs_path/file_manager_private.js", - "$externs_path/file_system_provider.js", - "../../../externs/background/volume_manager_factory.js", - "../../../externs/volume_info.js", - "../../../externs/volume_info_list.js", - "../../../externs/volume_manager.js", - ] -} - js_library("web_store_utils") { deps = [ ":constants", @@ -629,3 +607,17 @@ js_library("webui_command_extender") { "//ui/webui/resources/js/cr/ui:command", ] } + +js_unit_tests("unit_tests") { + deps = [ + ":thumbnail_loader_unittest", + ] +} + +group("closure_compile") { + visibility += [ "*" ] + deps = [ + ":closure_compile_module", + ":unit_tests_type_check", + ] +} diff --git a/chromium/ui/file_manager/file_manager/foreground/js/metadata/BUILD.gn b/chromium/ui/file_manager/file_manager/foreground/js/metadata/BUILD.gn index 4269da747f9..a698efc533c 100644 --- a/chromium/ui/file_manager/file_manager/foreground/js/metadata/BUILD.gn +++ b/chromium/ui/file_manager/file_manager/foreground/js/metadata/BUILD.gn @@ -4,6 +4,9 @@ import("//third_party/closure_compiler/compile_js.gni") +# TODO(tapted): This entire folder should move to //ui/file_manager/base. +visibility = [ "//ui/file_manager/*" ] + js_type_check("closure_compile") { deps = [ ":byte_reader", @@ -35,9 +38,6 @@ js_type_check("closure_compile") { js_library("closure_compile_externs") { sources = [] externs_list = [ - "$externs_path/command_line_private.js", - "$externs_path/file_manager_private.js", - "$externs_path/file_system_provider.js", "../../../../externs/app_window_common.js", "../../../../externs/entry_location.js", "../../../../externs/platform.js", @@ -148,6 +148,7 @@ js_library("metadata_model") { ":metadata_provider", ":multi_metadata_provider", "../../../common/js:util", + "//ui/file_manager/externs:volume_manager", ] } @@ -186,6 +187,7 @@ js_library("multi_metadata_provider") { ":external_metadata_provider", ":file_system_metadata_provider", ":metadata_provider", + "//ui/file_manager/externs:volume_manager", ] } diff --git a/chromium/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn b/chromium/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn index d3105ea28e8..3462ef36301 100644 --- a/chromium/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn +++ b/chromium/ui/file_manager/file_manager/foreground/js/ui/BUILD.gn @@ -4,6 +4,8 @@ import("//third_party/closure_compiler/compile_js.gni") +visibility = [ "//ui/file_manager/file_manager/foreground/*" ] + js_type_check("closure_compile") { deps = [ ":actions_submenu", @@ -36,7 +38,6 @@ js_type_check("closure_compile") { ":progress_center_panel", ":providers_menu", ":search_box", - ":share_dialog", ":suggest_apps_dialog", ] } @@ -44,7 +45,6 @@ js_type_check("closure_compile") { js_library("closure_compile_externs") { sources = [] externs_list = [ - "$externs_path/command_line_private.js", "$externs_path/metrics_private.js", "$externs_path/web_animations.js", "../../../../externs/app_window_common.js", @@ -59,7 +59,6 @@ js_library("closure_compile_externs") { "../../../../externs/css_rule.js", "../../../../externs/drag_target.js", "../../../../externs/entries_changed_event.js", - "../../../../externs/entry_location.js", "../../../../externs/gallery_foreground.js", "../../../../externs/menu_item_update_event.js", "../../../../externs/paper_elements.js", @@ -81,7 +80,6 @@ js_library("actions_submenu") { js_library("banners") { deps = [ "..:directory_model", - "..:volume_manager_wrapper", "../../../common/js:util", "../../../common/js:volume_manager_common", "//ui/webui/resources/js:assert", @@ -128,7 +126,6 @@ js_library("directory_tree") { deps = [ "..:directory_model", "..:navigation_list_model", - "..:volume_manager_wrapper", "../../../common/js:util", "../../../common/js:volume_manager_common", "../metadata:metadata_model", @@ -209,7 +206,6 @@ js_library("file_manager_ui") { ":progress_center_panel", ":providers_menu", ":search_box", - ":share_dialog", ":suggest_apps_dialog", "..:launch_param", "..:providers_model", @@ -261,13 +257,17 @@ js_library("file_tap_handler") { ] } +# TODO(tapted): Move this into //ui/file_manager/base. js_library("files_alert_dialog") { + visibility += [ "//ui/file_manager/gallery/*" ] deps = [ "//ui/webui/resources/js/cr/ui:dialogs", ] } +# TODO(tapted): Move this into //ui/file_manager/base. js_library("files_confirm_dialog") { + visibility += [ "//ui/file_manager/gallery/*" ] deps = [ "//ui/webui/resources/js/cr/ui:dialogs", ] @@ -303,11 +303,11 @@ js_library("list_container") { js_library("location_line") { deps = [ - "..:volume_manager_wrapper", "../../../common/js:files_app_entry_types", "../../../common/js:metrics", "../../../common/js:util", "../../../common/js:volume_manager_common", + "//ui/file_manager/externs:volume_manager", ] } @@ -319,6 +319,9 @@ js_library("multi_profile_share_dialog") { } js_library("progress_center_panel") { + # The progress_center on the background page maintains a list of panels. + visibility += [ "//ui/file_manager/file_manager/background/*" ] + deps = [ "..:progress_center_item_group", "../../../common/js:progress_center_common", @@ -349,15 +352,6 @@ js_library("search_box") { ] } -js_library("share_dialog") { - deps = [ - ":file_manager_dialog_base", - "..:share_client", - "../../../common/js:async_util", - "../../../common/js:util", - ] -} - js_library("suggest_apps_dialog") { deps = [ ":file_manager_dialog_base", diff --git a/chromium/ui/file_manager/file_manager/test/BUILD.gn b/chromium/ui/file_manager/file_manager/test/BUILD.gn index 6ec1da2d8e3..7c5254a8c5c 100644 --- a/chromium/ui/file_manager/file_manager/test/BUILD.gn +++ b/chromium/ui/file_manager/file_manager/test/BUILD.gn @@ -8,7 +8,6 @@ action("create_test_main") { script = "//ui/file_manager/file_manager/test/scripts/create_test_main.py" output = "$target_gen_dir/../test.html" sources = [ - "../../../webui/resources/css/text_defaults.css", "../background/js/background_common_scripts.js", "../background/js/background_scripts.js", "../foreground/elements/elements_bundle.html", @@ -18,20 +17,18 @@ action("create_test_main") { "../foreground/js/elements_importer.js", "../foreground/js/main_scripts.js", "../main.html", + "//chrome/app/file_manager_strings.grdp", + "//chrome/browser/chromeos/extensions/file_manager/private_api_strings.cc", + "//ui/webui/resources/css/text_defaults.css", "check_select.js", "crostini.js", + "js/strings.js", "quick_view.js", "uma.js", ] args = [ "--output=" + rebase_path(output, root_build_dir) ] outputs = [ output, - "$target_gen_dir/gen/css/text_defaults.css", - "$target_gen_dir/gen/elements/elements_bundle.html", - "$target_gen_dir/gen/elements/files_quick_view.html", - "$target_gen_dir/gen/elements/files_safe_media.html", - "$target_gen_dir/gen/elements/files_safe_media.js", - "$target_gen_dir/gen/js/elements_importer.js", ] } @@ -45,13 +42,14 @@ js_type_check("closure_compile") { ] } +# Remaining targets in this file are private. +visibility = [ ":*" ] + js_library("closure_compile_externs") { sources = [] externs_list = [ "js/externs.js", "$externs_path/command_line_private.js", - "$externs_path/file_manager_private.js", - "$externs_path/file_system_provider.js", "$externs_path/metrics_private.js", "../../externs/app_window_common.js", "../../externs/background/file_browser_background.js", @@ -72,6 +70,7 @@ js_library("check_select") { js_library("crostini") { deps = [ + "../foreground/js:crostini", "js:test_util", "//ui/webui/resources/js:webui_resource_test", ] diff --git a/chromium/ui/file_manager/file_manager/test/js/BUILD.gn b/chromium/ui/file_manager/file_manager/test/js/BUILD.gn index 46577f8a83b..df093277383 100644 --- a/chromium/ui/file_manager/file_manager/test/js/BUILD.gn +++ b/chromium/ui/file_manager/file_manager/test/js/BUILD.gn @@ -4,6 +4,8 @@ import("//third_party/closure_compiler/compile_js.gni") +visibility = [ "//ui/file_manager/file_manager/*" ] + js_library("chrome_api_test_impl") { } diff --git a/chromium/ui/file_manager/gallery/js/BUILD.gn b/chromium/ui/file_manager/gallery/js/BUILD.gn index 02347f8e835..17e71d7f579 100644 --- a/chromium/ui/file_manager/gallery/js/BUILD.gn +++ b/chromium/ui/file_manager/gallery/js/BUILD.gn @@ -3,7 +3,7 @@ # found in the LICENSE file. import("//third_party/closure_compiler/compile_js.gni") -import("//ui/file_manager/js_unit_tests.gni") +import("//third_party/closure_compiler/js_unit_tests.gni") js_type_check("closure_compile_module") { deps = [ @@ -52,13 +52,10 @@ js_library("dimmable_ui_controller_unittest") { js_library("entry_list_watcher") { deps = [ + "../../externs:file_manager_private", "//ui/webui/resources/js:assert", "//ui/webui/resources/js/cr/ui:array_data_model", ] - externs_list = [ - "$externs_path/file_system_provider.js", - "$externs_path/file_manager_private.js", - ] } js_library("entry_list_watcher_unittest") { @@ -81,11 +78,10 @@ js_library("gallery") { ":gallery_constants", ":gallery_item", ":thumbnail_mode", - "../../file_manager/common/js:util", - "../../file_manager/foreground/js:volume_manager_wrapper", - "../../file_manager/foreground/js/ui:files_confirm_dialog", - "../../file_manager/foreground/js/ui:share_dialog", - "../../gallery/js:slide_mode", + "//ui/file_manager/base/js:filtered_volume_manager", + "//ui/file_manager/file_manager/common/js:util", + "//ui/file_manager/file_manager/foreground/js/ui:files_confirm_dialog", + "//ui/file_manager/gallery/js:slide_mode", "//ui/webui/resources/js:i18n_template_no_process", ] } @@ -117,7 +113,6 @@ js_library("gallery_item") { ":gallery_util", "../../file_manager/common/js:metrics", "../../file_manager/common/js:util", - "../../file_manager/foreground/js:volume_manager_wrapper", "../../file_manager/foreground/js/metadata:metadata_model", "../../file_manager/foreground/js/metadata:thumbnail_model", "image_editor:image_encoder", @@ -140,7 +135,7 @@ js_library("gallery_util") { "../../file_manager/common/js:file_type", "../../file_manager/common/js:util", "../../file_manager/common/js:volume_manager_common", - "../../file_manager/foreground/js:volume_manager_wrapper", + "//ui/file_manager/externs:volume_manager", ] } @@ -240,6 +235,6 @@ js_unit_tests("unit_tests") { group("closure_compile") { deps = [ ":closure_compile_module", - ":unit_tests", + ":unit_tests_type_check", ] } diff --git a/chromium/ui/file_manager/gallery/js/image_editor/BUILD.gn b/chromium/ui/file_manager/gallery/js/image_editor/BUILD.gn index 86440b68a0f..e7a6675b35f 100644 --- a/chromium/ui/file_manager/gallery/js/image_editor/BUILD.gn +++ b/chromium/ui/file_manager/gallery/js/image_editor/BUILD.gn @@ -3,7 +3,7 @@ # found in the LICENSE file. import("//third_party/closure_compiler/compile_js.gni") -import("//ui/file_manager/js_unit_tests.gni") +import("//third_party/closure_compiler/js_unit_tests.gni") js_type_check("closure_compile_module") { deps = [ @@ -209,6 +209,6 @@ js_unit_tests("unit_tests") { group("closure_compile") { deps = [ ":closure_compile_module", - ":unit_tests", + ":unit_tests_type_check", ] } diff --git a/chromium/ui/file_manager/image_loader/BUILD.gn b/chromium/ui/file_manager/image_loader/BUILD.gn index 1b4618741b0..f88321191fc 100644 --- a/chromium/ui/file_manager/image_loader/BUILD.gn +++ b/chromium/ui/file_manager/image_loader/BUILD.gn @@ -3,32 +3,23 @@ # found in the LICENSE file. import("//third_party/closure_compiler/compile_js.gni") +import("//third_party/closure_compiler/js_unit_tests.gni") -js_type_check("closure_compile") { +js_type_check("closure_compile_module") { + closure_flags = default_closure_args + [ "jscomp_error=strictCheckTypes" ] deps = [ ":background", ":cache", - ":closure_compile_externs", ":image_loader", ":image_loader_client", ":image_loader_util", + ":load_image_request", ":piex_loader", ":request", ":scheduler", ] } -js_library("closure_compile_externs") { - sources = [] - externs_list = [ - "$externs_path/chrome_extensions.js", - "$externs_path/file_manager_private.js", - "$externs_path/file_system_provider.js", - "$externs_path/metrics_private.js", - "//third_party/analytics/externs.js", - ] -} - js_library("background") { deps = [ ":image_loader", @@ -38,43 +29,93 @@ js_library("background") { js_library("cache") { } +js_library("cache_unittest") { + deps = [ + ":cache", + ":load_image_request", + "//ui/webui/resources/js:webui_resource_test", + ] +} + js_library("image_loader") { deps = [ ":cache", + ":load_image_request", ":piex_loader", ":request", ":scheduler", + "../externs:file_manager_private", + ] +} + +js_library("image_loader_unittest") { + deps = [ + ":image_loader", + "//ui/webui/resources/js:webui_resource_test", ] } js_library("image_loader_util") { deps = [ + ":load_image_request", ":piex_loader", "//ui/webui/resources/js:assert", ] } +js_library("load_image_request") { + deps = [ + "../file_manager/foreground/js/metadata:image_orientation", + "//ui/webui/resources/js:assert", + ] +} + js_library("image_loader_client") { deps = [ + ":load_image_request", "../file_manager/common/js:lru_cache", ] + externs_list = [ + "$externs_path/chrome.js", + "$externs_path/chrome_extensions.js", + "$externs_path/metrics_private.js", + ] +} + +js_library("image_loader_client_unittest") { + deps = [ + ":image_loader_client", + "../file_manager/common/js:unittest_util", + "//ui/webui/resources/js:webui_resource_test", + ] } js_library("piex_loader") { deps = [ + ":load_image_request", "../file_manager/foreground/js/metadata:image_orientation", ] } +js_library("piex_loader_unittest") { + deps = [ + ":piex_loader", + "../file_manager/common/js:unittest_util", + "//ui/webui/resources/js:webui_resource_test", + ] +} + js_library("request") { deps = [ ":cache", ":image_loader_util", + ":load_image_request", ":piex_loader", "../file_manager/common/js:file_type", "../file_manager/common/js:metrics", "../file_manager/common/js:metrics_events", ] + externs_list = [ "../externs/platform.js" ] } js_library("scheduler") { @@ -82,3 +123,20 @@ js_library("scheduler") { ":request", ] } + +js_unit_tests("unit_tests") { + closure_flags = default_closure_args + [ "jscomp_error=strictCheckTypes" ] + deps = [ + ":cache_unittest", + ":image_loader_client_unittest", + ":image_loader_unittest", + ":piex_loader_unittest", + ] +} + +group("closure_compile") { + deps = [ + ":closure_compile_module", + ":unit_tests_type_check", + ] +} diff --git a/chromium/ui/file_manager/js_unit_tests.gni b/chromium/ui/file_manager/js_unit_tests.gni deleted file mode 100644 index edb9a07e950..00000000000 --- a/chromium/ui/file_manager/js_unit_tests.gni +++ /dev/null @@ -1,68 +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. - -import("//third_party/closure_compiler/compile_js.gni") - -# Describes a list of js_library targets that will each have an html file -# written listing all its (flattened) js dependencies, for loading as a test. -# Must be declared after the js_library targets it depends on. -# -# Variables: -# deps: -# List of js_library targets to depend on -# -# mocks: -# An optional list of .js files to load before any other scripts -# -# Example: -# js_unit_tests("folder_tests") { -# deps = [ -# ":foo_unittest", -# ":bar_unittest", -# ":baz_unittest", -# ] -# mocks = [ "my_mocks.js" ] -# } - -template("js_unit_tests") { - html_gen_target_name = target_name + "_html_gen" - action_foreach(html_gen_target_name) { - script_path = "//ui/file_manager" - script = "$script_path/js_unit_test.py" - forward_variables_from(invoker, - [ - "deps", - "mocks", - ]) - sources = [] - foreach(dep, deps) { - sources += get_target_outputs(dep) - } - - outputs = [ - "$target_gen_dir/{{source_name_part}}.html", - ] - args = [ "--output" ] + rebase_path(outputs, root_build_dir) - args += [ "--input" ] + [ "{{source}}" ] - - if (defined(mocks)) { - args += [ "--mocks" ] + rebase_path(mocks, root_build_dir) - data = mocks - } - } - type_check_deps = [] - foreach(dep, invoker.deps) { - type_check_target_name = target_name + "_" + dep + "_type_check" - type_check_deps += [ ":$type_check_target_name" ] - js_type_check(type_check_target_name) { - deps = [ - dep, - ] - } - } - group(target_name) { - data = get_target_outputs(":$html_gen_target_name") - deps = [ ":$html_gen_target_name" ] + type_check_deps - } -} diff --git a/chromium/ui/file_manager/video_player/js/BUILD.gn b/chromium/ui/file_manager/video_player/js/BUILD.gn index 38d3912324b..e00d4b6fb28 100644 --- a/chromium/ui/file_manager/video_player/js/BUILD.gn +++ b/chromium/ui/file_manager/video_player/js/BUILD.gn @@ -20,11 +20,9 @@ js_library("closure_compile_externs") { sources = [] externs_list = [ "$externs_path/chrome_extensions.js", - "$externs_path/command_line_private.js", "$externs_path/media_player_private.js", "$externs_path/metrics_private.js", "../../externs/chrome_cast.js", - "../../externs/entry_location.js", "../../externs/platform.js", "//third_party/analytics/externs.js", ] @@ -58,12 +56,12 @@ js_library("video_player") { ":media_controls", ":mouse_inactivity_watcher", ":video_player_metrics", - "../../file_manager/common/js:metrics", - "../../file_manager/common/js:util", - "../../file_manager/foreground/js:volume_manager_wrapper", - "../../image_loader:image_loader_client", "cast:cast_video_element", "cast:media_manager", + "//ui/file_manager/base/js:filtered_volume_manager", + "//ui/file_manager/file_manager/common/js:metrics", + "//ui/file_manager/file_manager/common/js:util", + "//ui/file_manager/image_loader:image_loader_client", "//ui/webui/resources/js:i18n_template_no_process", "//ui/webui/resources/js/cr/ui:menu", "//ui/webui/resources/js/cr/ui:menu_item", diff --git a/chromium/ui/file_manager/video_player/js/cast/BUILD.gn b/chromium/ui/file_manager/video_player/js/cast/BUILD.gn index 2435e5b479f..6071386427a 100644 --- a/chromium/ui/file_manager/video_player/js/cast/BUILD.gn +++ b/chromium/ui/file_manager/video_player/js/cast/BUILD.gn @@ -18,19 +18,12 @@ js_library("closure_compile_externs") { sources = [] externs_list = [ "$externs_path/chrome_extensions.js", - "$externs_path/command_line_private.js", - "$externs_path/file_manager_private.js", - "$externs_path/file_system_provider.js", "$externs_path/media_player_private.js", "$externs_path/metrics_private.js", "../../../externs/app_window_common.js", "../../../externs/background/volume_manager_factory.js", "../../../externs/chrome_cast.js", - "../../../externs/entry_location.js", "../../../externs/platform.js", - "../../../externs/volume_info.js", - "../../../externs/volume_info_list.js", - "../../../externs/volume_manager.js", "//third_party/analytics/externs.js", ] } diff --git a/chromium/ui/gfx/BUILD.gn b/chromium/ui/gfx/BUILD.gn index 7611eb0879b..b9e0290dba2 100644 --- a/chromium/ui/gfx/BUILD.gn +++ b/chromium/ui/gfx/BUILD.gn @@ -25,6 +25,7 @@ source_set("gfx_export") { jumbo_component("geometry_skia") { sources = [ + "geometry_skia_export.h", "skia_util.cc", "skia_util.h", "transform.cc", @@ -32,16 +33,14 @@ jumbo_component("geometry_skia") { "transform_util.cc", "transform_util.h", ] - public_deps = [ - ":gfx_export", - ] + configs += [ "//build/config/compiler:wexit_time_destructors" ] deps = [ "//base", "//gpu/vulkan:buildflags", "//skia", "//ui/gfx/geometry", ] - defines = [ "GFX_IMPLEMENTATION" ] + defines = [ "GEOMETRY_SKIA_IMPLEMENTATION" ] } jumbo_component("gfx") { @@ -104,7 +103,6 @@ jumbo_component("gfx") { "image/image_png_rep.h", "image/image_skia.cc", "image/image_skia.h", - "image/image_skia_rep.cc", "image/image_skia_rep.h", "image/image_skia_source.cc", "image/image_skia_source.h", @@ -212,6 +210,8 @@ jumbo_component("gfx") { "image/image_generic.cc", "image/image_skia_operations.cc", "image/image_skia_operations.h", + "image/image_skia_rep_default.cc", + "image/image_skia_rep_default.h", "paint_throbber.cc", "paint_throbber.h", "scoped_canvas.cc", @@ -220,6 +220,16 @@ jumbo_component("gfx") { "shadow_util.h", "skia_paint_util.cc", "skia_paint_util.h", + "skia_vector_animation.cc", + "skia_vector_animation.h", + "skia_vector_animation_observer.h", + "skottie_wrapper.cc", + "skottie_wrapper.h", + ] + } else { + sources += [ + "image/image_skia_rep_ios.cc", + "image/image_skia_rep_ios.h", ] } @@ -228,6 +238,7 @@ jumbo_component("gfx") { # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. "//build/config/compiler:no_size_t_to_int_warning", + "//build/config/compiler:wexit_time_destructors", ] # This is part of the gfx component in the component build. @@ -401,20 +412,6 @@ jumbo_component("color_space") { defines = [ "COLOR_SPACE_IMPLEMENTATION" ] } -# Depend on this to use half_float.h without pulling in all of gfx. -source_set("half_float") { - sources = [ - "half_float.h", - ] - - defines = [ "GFX_IMPLEMENTATION" ] - - public_deps = [ - ":gfx_export", - "//base", - ] -} - # Depend on this to use native_widget_types.h without pulling in all of gfx. source_set("native_widget_types") { public = [ @@ -449,6 +446,8 @@ source_set("selection_bound_sources") { "selection_bound.h", ] + configs += [ "//build/config/compiler:wexit_time_destructors" ] + defines = [ "GFX_IMPLEMENTATION" ] public_deps = [ @@ -483,7 +482,7 @@ group("memory_buffer") { } # Cannot be a static_library in component builds due to exported functions -source_set("memory_buffer_sources") { +jumbo_source_set("memory_buffer_sources") { visibility = [ ":*" ] # Depend on through ":memory_buffer". # TODO(brettw) refactor this so these sources are in a coherent directory @@ -516,6 +515,8 @@ source_set("memory_buffer_sources") { ] } + configs += [ "//build/config/compiler:wexit_time_destructors" ] + defines = [ "GFX_IMPLEMENTATION" ] public_deps = [ @@ -574,7 +575,7 @@ jumbo_component("gfx_switches") { ] } -static_library("test_support") { +jumbo_static_library("test_support") { testonly = true sources = [ "animation/animation_test_api.cc", @@ -686,6 +687,7 @@ test("gfx_unittests") { "sequential_id_generator_unittest.cc", "shadow_value_unittest.cc", "skbitmap_operations_unittest.cc", + "skia_vector_animation_unittest.cc", "skrect_conversion_unittest.cc", "transform_util_unittest.cc", "utf16_indexing_unittest.cc", diff --git a/chromium/ui/gfx/OWNERS b/chromium/ui/gfx/OWNERS index 128cf4cb9ca..225898fbe52 100644 --- a/chromium/ui/gfx/OWNERS +++ b/chromium/ui/gfx/OWNERS @@ -27,12 +27,17 @@ per-file skia_paint_util*=danakj@chromium.org # GPU memory buffer and GpuFence interfaces. per-file gpu_fence*=reveman@chromium.org per-file gpu_fence*=dcastagna@chromium.org +per-file gpu_fence*=rjkroege@chromium.org per-file gpu_memory_buffer*=reveman@chromium.org per-file gpu_memory_buffer*=dcastagna@chromium.org +per-file gpu_memory_buffer*=rjkroege@chromium.org per-file buffer_format*=reveman@chromium.org per-file buffer_format*=dcastagna@chromium.org +per-file buffer_format*=rjkroege@chromium.org per-file *buffer_types.*=reveman@chromium.org per-file *buffer_types.*=dcastagna@chromium.org +per-file *buffer_types.*=rjkroege@chromium.org +per-file *pixmap.*=rjkroege@chromium.org # Vector icons. per-file *vector_icon*=estade@chromium.org diff --git a/chromium/ui/gfx/android/java_bitmap.h b/chromium/ui/gfx/android/java_bitmap.h index 919e62a9168..950509164a6 100644 --- a/chromium/ui/gfx/android/java_bitmap.h +++ b/chromium/ui/gfx/android/java_bitmap.h @@ -12,6 +12,7 @@ #include "base/macros.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/gfx/geometry/size.h" +#include "ui/gfx/gfx_export.h" namespace gfx { diff --git a/chromium/ui/gfx/animation/animation_container.cc b/chromium/ui/gfx/animation/animation_container.cc index ef15c8f64c2..884c3c82081 100644 --- a/chromium/ui/gfx/animation/animation_container.cc +++ b/chromium/ui/gfx/animation/animation_container.cc @@ -114,7 +114,7 @@ std::pair<TimeDelta, size_t> AnimationContainer::GetMinIntervalAndCount() // elements in the set share the same interval). TimeDelta min; size_t count = 1; - Elements::const_iterator i = elements_.begin(); + auto i = elements_.begin(); min = (*i)->GetTimerInterval(); for (++i; i != elements_.end(); ++i) { auto interval = (*i)->GetTimerInterval(); diff --git a/chromium/ui/gfx/buffer_format_util.cc b/chromium/ui/gfx/buffer_format_util.cc index f72941f5258..5e3de26ae3f 100644 --- a/chromium/ui/gfx/buffer_format_util.cc +++ b/chromium/ui/gfx/buffer_format_util.cc @@ -11,26 +11,15 @@ namespace gfx { namespace { -const BufferFormat kBufferFormats[] = {BufferFormat::ATC, - BufferFormat::ATCIA, - BufferFormat::DXT1, - BufferFormat::DXT5, - BufferFormat::ETC1, - BufferFormat::R_8, - BufferFormat::R_16, - BufferFormat::RG_88, - BufferFormat::BGR_565, - BufferFormat::RGBA_4444, - BufferFormat::RGBX_8888, - BufferFormat::RGBA_8888, - BufferFormat::BGRX_8888, - BufferFormat::BGRX_1010102, - BufferFormat::RGBX_1010102, - BufferFormat::BGRA_8888, - BufferFormat::RGBA_F16, - BufferFormat::UYVY_422, - BufferFormat::YUV_420_BIPLANAR, - BufferFormat::YVU_420}; +const BufferFormat kBufferFormats[] = { + BufferFormat::R_8, BufferFormat::R_16, + BufferFormat::RG_88, BufferFormat::BGR_565, + BufferFormat::RGBA_4444, BufferFormat::RGBX_8888, + BufferFormat::RGBA_8888, BufferFormat::BGRX_8888, + BufferFormat::BGRX_1010102, BufferFormat::RGBX_1010102, + BufferFormat::BGRA_8888, BufferFormat::RGBA_F16, + BufferFormat::UYVY_422, BufferFormat::YUV_420_BIPLANAR, + BufferFormat::YVU_420}; static_assert(arraysize(kBufferFormats) == (static_cast<int>(BufferFormat::LAST) + 1), @@ -41,18 +30,6 @@ bool RowSizeForBufferFormatChecked( size_t width, BufferFormat format, size_t plane, size_t* size_in_bytes) { base::CheckedNumeric<size_t> checked_size = width; switch (format) { - case BufferFormat::ATCIA: - case BufferFormat::DXT5: - DCHECK_EQ(0u, plane); - *size_in_bytes = width; - return true; - case BufferFormat::ATC: - case BufferFormat::DXT1: - case BufferFormat::ETC1: - DCHECK_EQ(0u, plane); - DCHECK_EQ(0u, width % 2); - *size_in_bytes = width / 2; - return true; case BufferFormat::R_8: checked_size += 3; if (!checked_size.IsValid()) @@ -109,11 +86,6 @@ std::vector<BufferFormat> GetBufferFormatsForTesting() { size_t NumberOfPlanesForBufferFormat(BufferFormat format) { switch (format) { - case BufferFormat::ATC: - case BufferFormat::ATCIA: - case BufferFormat::DXT1: - case BufferFormat::DXT5: - case BufferFormat::ETC1: case BufferFormat::R_8: case BufferFormat::R_16: case BufferFormat::RG_88: @@ -139,11 +111,6 @@ size_t NumberOfPlanesForBufferFormat(BufferFormat format) { size_t SubsamplingFactorForBufferFormat(BufferFormat format, size_t plane) { switch (format) { - case BufferFormat::ATC: - case BufferFormat::ATCIA: - case BufferFormat::DXT1: - case BufferFormat::DXT5: - case BufferFormat::ETC1: case BufferFormat::R_8: case BufferFormat::R_16: case BufferFormat::RG_88: @@ -214,11 +181,6 @@ size_t BufferOffsetForBufferFormat(const Size& size, size_t plane) { DCHECK_LT(plane, gfx::NumberOfPlanesForBufferFormat(format)); switch (format) { - case BufferFormat::ATC: - case BufferFormat::ATCIA: - case BufferFormat::DXT1: - case BufferFormat::DXT5: - case BufferFormat::ETC1: case BufferFormat::R_8: case BufferFormat::R_16: case BufferFormat::RG_88: @@ -252,16 +214,6 @@ size_t BufferOffsetForBufferFormat(const Size& size, const char* BufferFormatToString(BufferFormat format) { switch (format) { - case BufferFormat::ATC: - return "ATC"; - case BufferFormat::ATCIA: - return "ATCIA"; - case BufferFormat::DXT1: - return "DXT1"; - case BufferFormat::DXT5: - return "DXT5"; - case BufferFormat::ETC1: - return "ETC1"; case BufferFormat::R_8: return "R_8"; case BufferFormat::R_16: diff --git a/chromium/ui/gfx/buffer_types.h b/chromium/ui/gfx/buffer_types.h index 47c8b60c10e..be72cf7480b 100644 --- a/chromium/ui/gfx/buffer_types.h +++ b/chromium/ui/gfx/buffer_types.h @@ -12,11 +12,6 @@ namespace gfx { // The format needs to be taken into account when mapping a buffer into the // client's address space. enum class BufferFormat { - ATC, - ATCIA, - DXT1, - DXT5, - ETC1, R_8, R_16, RG_88, diff --git a/chromium/ui/gfx/canvas.cc b/chromium/ui/gfx/canvas.cc index ab5bcab982a..21b6e1358a6 100644 --- a/chromium/ui/gfx/canvas.cc +++ b/chromium/ui/gfx/canvas.cc @@ -26,6 +26,7 @@ #include "ui/gfx/scoped_canvas.h" #include "ui/gfx/skia_paint_util.h" #include "ui/gfx/skia_util.h" +#include "ui/gfx/switches.h" #include "ui/gfx/transform.h" namespace gfx { @@ -357,8 +358,17 @@ void Canvas::DrawImageInt(const ImageSkia& image, ScopedCanvas scoper(this); canvas_->scale(SkFloatToScalar(1.0f / bitmap_scale), SkFloatToScalar(1.0f / bitmap_scale)); - canvas_->drawImage(image_rep.paint_image(), SkFloatToScalar(x * bitmap_scale), - SkFloatToScalar(y * bitmap_scale), &flags); + if (base::FeatureList::IsEnabled(features::kUsePaintRecordForImageSkia)) { + canvas_->translate(std::round(x * bitmap_scale), + std::round(y * bitmap_scale)); + canvas_->saveLayer(nullptr, &flags); + canvas_->drawPicture(image_rep.GetPaintRecord()); + canvas_->restore(); + } else { + canvas_->drawImage(image_rep.paint_image(), + SkFloatToScalar(x * bitmap_scale), + SkFloatToScalar(y * bitmap_scale), &flags); + } } void Canvas::DrawImageInt(const ImageSkia& image, diff --git a/chromium/ui/gfx/codec/chromeos/jpeg_codec_robust_slow.cc b/chromium/ui/gfx/codec/chromeos/jpeg_codec_robust_slow.cc index 1ab3208d090..e029cafe31e 100644 --- a/chromium/ui/gfx/codec/chromeos/jpeg_codec_robust_slow.cc +++ b/chromium/ui/gfx/codec/chromeos/jpeg_codec_robust_slow.cc @@ -139,26 +139,12 @@ void RGBtoBGRA(const unsigned char* bgra, int pixel_width, unsigned char* rgb) { } #endif // !defined(JCS_EXTENSIONS) -// This class destroys the given jpeg_decompress object when it goes out of -// scope. It simplifies the error handling in Decode (and even applies to the -// success case). -class IjgDecompressDestroyer { - public: - IjgDecompressDestroyer() : cinfo_(NULL) {} - ~IjgDecompressDestroyer() { DestroyManagedObject(); } - void SetManagedObject(jpeg_decompress_struct* ci) { - DestroyManagedObject(); - cinfo_ = ci; +// jpeg_decompress_struct Deleter. +struct JpegRobustDecompressStructDeleter { + void operator()(jpeg_decompress_struct* ptr) { + jpeg_destroy_decompress(ptr); + delete ptr; } - void DestroyManagedObject() { - if (cinfo_) { - jpeg_destroy_decompress(cinfo_); - cinfo_ = NULL; - } - } - - private: - jpeg_decompress_struct* cinfo_; }; } // namespace @@ -169,28 +155,26 @@ bool JPEGCodecRobustSlow::Decode(const unsigned char* input, std::vector<unsigned char>* output, int* w, int* h) { - jpeg_decompress_struct cinfo; - IjgDecompressDestroyer destroyer; - destroyer.SetManagedObject(&cinfo); + std::unique_ptr<jpeg_decompress_struct, JpegRobustDecompressStructDeleter> + cinfo(new jpeg_decompress_struct); output->clear(); // We set up the normal JPEG error routines, then override error_exit. // This must be done before the call to create_decompress. IjgCoderErrorMgr errmgr; - cinfo.err = jpeg_std_error(&errmgr.pub); + cinfo->err = jpeg_std_error(&errmgr.pub); errmgr.pub.error_exit = IjgErrorExit; // Establish the setjmp return context for IjgErrorExit to use. if (setjmp(errmgr.setjmp_buffer)) { // If we get here, the JPEG code has signaled an error. - // See note in JPEGCodec::Encode() for why we need to destroy the cinfo - // manually here. - destroyer.DestroyManagedObject(); + // Release |cinfo| by hand to avoid use-after-free of |errmgr|. + cinfo.reset(); return false; } // The destroyer will destroy() cinfo on exit. We don't want to set the // destroyer's object until cinfo is initialized. - jpeg_create_decompress(&cinfo); + jpeg_create_decompress(cinfo.get()); // set up the source manager jpeg_source_mgr srcmgr; @@ -199,17 +183,17 @@ bool JPEGCodecRobustSlow::Decode(const unsigned char* input, srcmgr.skip_input_data = IjgSkipInputData; srcmgr.resync_to_restart = jpeg_resync_to_restart; // use default routine srcmgr.term_source = IjgTermSource; - cinfo.src = &srcmgr; + cinfo->src = &srcmgr; IjgJpegDecoderState state(input, input_size); - cinfo.client_data = &state; + cinfo->client_data = &state; // fill the file metadata into our buffer - if (jpeg_read_header(&cinfo, true) != JPEG_HEADER_OK) + if (jpeg_read_header(cinfo.get(), true) != JPEG_HEADER_OK) return false; // we want to always get RGB data out - switch (cinfo.jpeg_color_space) { + switch (cinfo->jpeg_color_space) { case JCS_GRAYSCALE: case JCS_RGB: case JCS_YCbCr: @@ -219,24 +203,22 @@ bool JPEGCodecRobustSlow::Decode(const unsigned char* input, // used by Chromium (i.e. RGB, RGBA, and BGRA) and we just map the input // parameters to a colorspace. if (format == FORMAT_RGB) { - cinfo.out_color_space = JCS_RGB; - cinfo.output_components = 3; + cinfo->out_color_space = JCS_RGB; + cinfo->output_components = 3; } else if (format == FORMAT_RGBA || (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) { - cinfo.out_color_space = JCS_EXT_RGBX; - cinfo.output_components = 4; + cinfo->out_color_space = JCS_EXT_RGBX; + cinfo->output_components = 4; } else if (format == FORMAT_BGRA || (format == FORMAT_SkBitmap && SK_B32_SHIFT == 0)) { - cinfo.out_color_space = JCS_EXT_BGRX; - cinfo.output_components = 4; + cinfo->out_color_space = JCS_EXT_BGRX; + cinfo->output_components = 4; } else { - // We can exit this function without calling jpeg_destroy_decompress() - // because IjgDecompressDestroyer automaticaly calls it. NOTREACHED() << "Invalid pixel format"; return false; } #else - cinfo.out_color_space = JCS_RGB; + cinfo->out_color_space = JCS_RGB; #endif break; case JCS_CMYK: @@ -248,39 +230,39 @@ bool JPEGCodecRobustSlow::Decode(const unsigned char* input, return false; } #ifndef JCS_EXTENSIONS - cinfo.output_components = 3; + cinfo->output_components = 3; #endif - jpeg_calc_output_dimensions(&cinfo); - *w = cinfo.output_width; - *h = cinfo.output_height; + jpeg_calc_output_dimensions(cinfo.get()); + *w = cinfo->output_width; + *h = cinfo->output_height; - jpeg_start_decompress(&cinfo); + jpeg_start_decompress(cinfo.get()); // FIXME(brettw) we may want to allow the capability for callers to request // how to align row lengths as we do for the compressor. - int row_read_stride = cinfo.output_width * cinfo.output_components; + int row_read_stride = cinfo->output_width * cinfo->output_components; #ifdef JCS_EXTENSIONS // Create memory for a decoded image and write decoded lines to the memory // without conversions same as JPEGCodec::Encode(). int row_write_stride = row_read_stride; - output->resize(row_write_stride * cinfo.output_height); + output->resize(row_write_stride * cinfo->output_height); - for (int row = 0; row < static_cast<int>(cinfo.output_height); row++) { + for (int row = 0; row < static_cast<int>(cinfo->output_height); row++) { unsigned char* rowptr = &(*output)[row * row_write_stride]; - if (!jpeg_read_scanlines(&cinfo, &rowptr, 1)) + if (!jpeg_read_scanlines(cinfo.get(), &rowptr, 1)) return false; } #else if (format == FORMAT_RGB) { // easy case, row needs no conversion int row_write_stride = row_read_stride; - output->resize(row_write_stride * cinfo.output_height); + output->resize(row_write_stride * cinfo->output_height); - for (int row = 0; row < static_cast<int>(cinfo.output_height); row++) { + for (int row = 0; row < static_cast<int>(cinfo->output_height); row++) { unsigned char* rowptr = &(*output)[row * row_write_stride]; - if (!jpeg_read_scanlines(&cinfo, &rowptr, 1)) + if (!jpeg_read_scanlines(cinfo.get(), &rowptr, 1)) return false; } } else { @@ -291,33 +273,32 @@ bool JPEGCodecRobustSlow::Decode(const unsigned char* input, void (*converter)(const unsigned char* rgb, int w, unsigned char* out); if (format == FORMAT_RGBA || (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) { - row_write_stride = cinfo.output_width * 4; + row_write_stride = cinfo->output_width * 4; converter = AddAlpha; } else if (format == FORMAT_BGRA || (format == FORMAT_SkBitmap && SK_B32_SHIFT == 0)) { - row_write_stride = cinfo.output_width * 4; + row_write_stride = cinfo->output_width * 4; converter = RGBtoBGRA; } else { NOTREACHED() << "Invalid pixel format"; - jpeg_destroy_decompress(&cinfo); + jpeg_destroy_decompress(cinfo.get()); return false; } - output->resize(row_write_stride * cinfo.output_height); + output->resize(row_write_stride * cinfo->output_height); std::unique_ptr<unsigned char[]> row_data( new unsigned char[row_read_stride]); unsigned char* rowptr = row_data.get(); - for (int row = 0; row < static_cast<int>(cinfo.output_height); row++) { - if (!jpeg_read_scanlines(&cinfo, &rowptr, 1)) + for (int row = 0; row < static_cast<int>(cinfo->output_height); row++) { + if (!jpeg_read_scanlines(cinfo.get(), &rowptr, 1)) return false; converter(rowptr, *w, &(*output)[row * row_write_stride]); } } #endif - jpeg_finish_decompress(&cinfo); - jpeg_destroy_decompress(&cinfo); + jpeg_finish_decompress(cinfo.get()); return true; } diff --git a/chromium/ui/gfx/codec/jpeg_codec.cc b/chromium/ui/gfx/codec/jpeg_codec.cc index 5ff77d782b8..b03dc8f9368 100644 --- a/chromium/ui/gfx/codec/jpeg_codec.cc +++ b/chromium/ui/gfx/codec/jpeg_codec.cc @@ -11,7 +11,6 @@ #include "base/logging.h" #include "third_party/skia/include/core/SkBitmap.h" #include "third_party/skia/include/core/SkColorPriv.h" -#include "third_party/skia/include/encode/SkJpegEncoder.h" #include "ui/gfx/codec/vector_wstream.h" extern "C" { @@ -47,15 +46,23 @@ void ErrorExit(jpeg_common_struct* cinfo) { // Encoder --------------------------------------------------------------------- -bool JPEGCodec::Encode(const SkPixmap& src, +bool JPEGCodec::Encode(const SkPixmap& input, int quality, + SkJpegEncoder::Downsample downsample, std::vector<unsigned char>* output) { output->clear(); VectorWStream dst(output); SkJpegEncoder::Options options; options.fQuality = quality; - return SkJpegEncoder::Encode(&dst, src, options); + options.fDownsample = downsample; + return SkJpegEncoder::Encode(&dst, input, options); +} + +bool JPEGCodec::Encode(const SkPixmap& input, + int quality, + std::vector<unsigned char>* output) { + return Encode(input, quality, SkJpegEncoder::Downsample::k420, output); } bool JPEGCodec::Encode(const SkBitmap& src, @@ -143,31 +150,14 @@ void SkipInputData(j_decompress_ptr cinfo, long num_bytes) { // "Terminate source --- called by jpeg_finish_decompress() after all data has // been read to clean up JPEG source manager. NOT called by jpeg_abort() or // jpeg_destroy()." -void TermSource(j_decompress_ptr cinfo) { -} +void TermSource(j_decompress_ptr cinfo) {} -// This class destroys the given jpeg_decompress object when it goes out of -// scope. It simplifies the error handling in Decode (and even applies to the -// success case). -class DecompressDestroyer { - public: - DecompressDestroyer() : cinfo_(NULL) { - } - ~DecompressDestroyer() { - DestroyManagedObject(); - } - void SetManagedObject(jpeg_decompress_struct* ci) { - DestroyManagedObject(); - cinfo_ = ci; - } - void DestroyManagedObject() { - if (cinfo_) { - jpeg_destroy_decompress(cinfo_); - cinfo_ = NULL; - } +// jpeg_decompress_struct Deleter. +struct JpegDecompressStructDeleter { + void operator()(jpeg_decompress_struct* ptr) { + jpeg_destroy_decompress(ptr); + delete ptr; } - private: - jpeg_decompress_struct* cinfo_; }; } // namespace @@ -175,28 +165,26 @@ class DecompressDestroyer { bool JPEGCodec::Decode(const unsigned char* input, size_t input_size, ColorFormat format, std::vector<unsigned char>* output, int* w, int* h) { - jpeg_decompress_struct cinfo; - DecompressDestroyer destroyer; - destroyer.SetManagedObject(&cinfo); + std::unique_ptr<jpeg_decompress_struct, JpegDecompressStructDeleter> cinfo( + new jpeg_decompress_struct); output->clear(); // We set up the normal JPEG error routines, then override error_exit. - // This must be done before the call to create_decompress. + // This must be done before the call to jpeg_create_decompress. CoderErrorMgr errmgr; - cinfo.err = jpeg_std_error(&errmgr.pub); + cinfo->err = jpeg_std_error(&errmgr.pub); errmgr.pub.error_exit = ErrorExit; // Establish the setjmp return context for ErrorExit to use. if (setjmp(errmgr.setjmp_buffer)) { // If we get here, the JPEG code has signaled an error. - // See note in JPEGCodec::Encode() for why we need to destroy the cinfo - // manually here. - destroyer.DestroyManagedObject(); + // Release |cinfo| by hand to avoid use-after-free of |errmgr|. + cinfo.reset(); return false; } // The destroyer will destroy() cinfo on exit. We don't want to set the // destroyer's object until cinfo is initialized. - jpeg_create_decompress(&cinfo); + jpeg_create_decompress(cinfo.get()); // set up the source manager jpeg_source_mgr srcmgr; @@ -205,17 +193,17 @@ bool JPEGCodec::Decode(const unsigned char* input, size_t input_size, srcmgr.skip_input_data = SkipInputData; srcmgr.resync_to_restart = jpeg_resync_to_restart; // use default routine srcmgr.term_source = TermSource; - cinfo.src = &srcmgr; + cinfo->src = &srcmgr; JpegDecoderState state(input, input_size); - cinfo.client_data = &state; + cinfo->client_data = &state; // fill the file metadata into our buffer - if (jpeg_read_header(&cinfo, true) != JPEG_HEADER_OK) + if (jpeg_read_header(cinfo.get(), true) != JPEG_HEADER_OK) return false; // we want to always get RGB data out - switch (cinfo.jpeg_color_space) { + switch (cinfo->jpeg_color_space) { case JCS_GRAYSCALE: case JCS_RGB: case JCS_YCbCr: @@ -225,15 +213,13 @@ bool JPEGCodec::Decode(const unsigned char* input, size_t input_size, // parameters to a colorspace. if (format == FORMAT_RGBA || (format == FORMAT_SkBitmap && SK_R32_SHIFT == 0)) { - cinfo.out_color_space = JCS_EXT_RGBX; - cinfo.output_components = 4; + cinfo->out_color_space = JCS_EXT_RGBX; + cinfo->output_components = 4; } else if (format == FORMAT_BGRA || (format == FORMAT_SkBitmap && SK_B32_SHIFT == 0)) { - cinfo.out_color_space = JCS_EXT_BGRX; - cinfo.output_components = 4; + cinfo->out_color_space = JCS_EXT_BGRX; + cinfo->output_components = 4; } else { - // We can exit this function without calling jpeg_destroy_decompress() - // because DecompressDestroyer automaticaly calls it. NOTREACHED() << "Invalid pixel format"; return false; } @@ -247,29 +233,28 @@ bool JPEGCodec::Decode(const unsigned char* input, size_t input_size, return false; } - jpeg_calc_output_dimensions(&cinfo); - *w = cinfo.output_width; - *h = cinfo.output_height; + jpeg_calc_output_dimensions(cinfo.get()); + *w = cinfo->output_width; + *h = cinfo->output_height; - jpeg_start_decompress(&cinfo); + jpeg_start_decompress(cinfo.get()); // FIXME(brettw) we may want to allow the capability for callers to request // how to align row lengths as we do for the compressor. - int row_read_stride = cinfo.output_width * cinfo.output_components; + int row_read_stride = cinfo->output_width * cinfo->output_components; // Create memory for a decoded image and write decoded lines to the memory // without conversions same as JPEGCodec::Encode(). int row_write_stride = row_read_stride; - output->resize(row_write_stride * cinfo.output_height); + output->resize(row_write_stride * cinfo->output_height); - for (int row = 0; row < static_cast<int>(cinfo.output_height); row++) { + for (int row = 0; row < static_cast<int>(cinfo->output_height); row++) { unsigned char* rowptr = &(*output)[row * row_write_stride]; - if (!jpeg_read_scanlines(&cinfo, &rowptr, 1)) + if (!jpeg_read_scanlines(cinfo.get(), &rowptr, 1)) return false; } - jpeg_finish_decompress(&cinfo); - jpeg_destroy_decompress(&cinfo); + jpeg_finish_decompress(cinfo.get()); return true; } diff --git a/chromium/ui/gfx/codec/jpeg_codec.h b/chromium/ui/gfx/codec/jpeg_codec.h index 1fdd143e8d7..14f88793d2d 100644 --- a/chromium/ui/gfx/codec/jpeg_codec.h +++ b/chromium/ui/gfx/codec/jpeg_codec.h @@ -12,6 +12,7 @@ #include "third_party/skia/include/core/SkImageInfo.h" #include "third_party/skia/include/core/SkPixmap.h" +#include "third_party/skia/include/encode/SkJpegEncoder.h" #include "ui/gfx/codec/codec_export.h" class SkBitmap; @@ -43,6 +44,20 @@ class CODEC_EXPORT JPEGCodec { // success. On failure (false), the contents of the output buffer are // undefined. // + // downsample: specifies how pixels will be sampled in the encoded JPEG image, + // can be either k420, k422 or k444. + // quality: an integer in the range 0-100, where 100 is the highest quality. + static bool Encode(const SkPixmap& input, + int quality, + SkJpegEncoder::Downsample downsample, + std::vector<unsigned char>* output); + + // Encodes the given raw 'input' pixmap, which includes a pointer to pixels + // as well as information describing the pixel format. The encoded JPEG data + // will be written into the supplied vector and true will be returned on + // success. On failure (false), the contents of the output buffer are + // undefined. + // // quality: an integer in the range 0-100, where 100 is the highest quality. static bool Encode(const SkPixmap& input, int quality, diff --git a/chromium/ui/gfx/color_analysis.cc b/chromium/ui/gfx/color_analysis.cc index 07c3cfbd79c..a23a9ad95c1 100644 --- a/chromium/ui/gfx/color_analysis.cc +++ b/chromium/ui/gfx/color_analysis.cc @@ -137,16 +137,6 @@ class KMeanCluster { uint32_t weight_; }; -// Un-premultiplies each pixel in |bitmap| into an output |buffer|. Requires -// approximately 10 microseconds for a 16x16 icon on an Intel Core i5. -void UnPreMultiply(const SkBitmap& bitmap, uint32_t* buffer, int buffer_size) { - uint32_t* in = static_cast<uint32_t*>(bitmap.getPixels()); - uint32_t* out = buffer; - int pixel_count = std::min(bitmap.width() * bitmap.height(), buffer_size); - for (int i = 0; i < pixel_count; ++i) - *out++ = SkUnPreMultiply::PMColorToColor(*in++); -} - // Prominent color utilities --------------------------------------------------- // A color value with an associated weight. @@ -551,7 +541,7 @@ SkColor CalculateKMeanColorOfBuffer(uint8_t* decoded_data, clusters.resize(kNumberOfClusters, KMeanCluster()); // Pick a starting point for each cluster - std::vector<KMeanCluster>::iterator cluster = clusters.begin(); + auto cluster = clusters.begin(); while (cluster != clusters.end()) { // Try up to 10 times to find a unique color. If no unique color can be // found, destroy this cluster. @@ -572,9 +562,8 @@ SkColor CalculateKMeanColorOfBuffer(uint8_t* decoded_data, // Loop through the previous clusters and check to see if we have seen // this color before. color_unique = true; - for (std::vector<KMeanCluster>::iterator - cluster_check = clusters.begin(); - cluster_check != cluster; ++cluster_check) { + for (auto cluster_check = clusters.begin(); cluster_check != cluster; + ++cluster_check) { if (cluster_check->IsAtCentroid(r, g, b)) { color_unique = false; break; @@ -622,11 +611,11 @@ SkColor CalculateKMeanColorOfBuffer(uint8_t* decoded_data, continue; uint32_t distance_sqr_to_closest_cluster = UINT_MAX; - std::vector<KMeanCluster>::iterator closest_cluster = clusters.begin(); + auto closest_cluster = clusters.begin(); // Figure out which cluster this color is closest to in RGB space. - for (std::vector<KMeanCluster>::iterator cluster = clusters.begin(); - cluster != clusters.end(); ++cluster) { + for (auto cluster = clusters.begin(); cluster != clusters.end(); + ++cluster) { uint32_t distance_sqr = cluster->GetDistanceSqr(r, g, b); if (distance_sqr < distance_sqr_to_closest_cluster) { @@ -640,8 +629,8 @@ SkColor CalculateKMeanColorOfBuffer(uint8_t* decoded_data, // Calculate the new cluster centers and see if we've converged or not. convergence = true; - for (std::vector<KMeanCluster>::iterator cluster = clusters.begin(); - cluster != clusters.end(); ++cluster) { + for (auto cluster = clusters.begin(); cluster != clusters.end(); + ++cluster) { convergence &= cluster->CompareCentroidWithAggregate(); cluster->RecomputeCentroid(); @@ -655,8 +644,8 @@ SkColor CalculateKMeanColorOfBuffer(uint8_t* decoded_data, // Loop through the clusters to figure out which cluster has an appropriate // color. Skip any that are too bright/dark and go in order of weight. - for (std::vector<KMeanCluster>::iterator cluster = clusters.begin(); - cluster != clusters.end(); ++cluster) { + for (auto cluster = clusters.begin(); cluster != clusters.end(); + ++cluster) { uint8_t r, g, b; cluster->GetCentroid(&r, &g, &b); @@ -713,12 +702,24 @@ SkColor CalculateKMeanColorOfBitmap(const SkBitmap& bitmap, const HSL& lower_bound, const HSL& upper_bound, bool find_closest) { + // Clamp the height being used to the height of the provided image (otherwise, + // we can end up creating a larger buffer than we have data for, and the end + // of the buffer will remain uninitialized after we copy/UnPreMultiply the + // image data into it). + height = std::min(height, bitmap.height()); + // SkBitmap uses pre-multiplied alpha but the KMean clustering function // above uses non-pre-multiplied alpha. Transform the bitmap before we // analyze it because the function reads each pixel multiple times. int pixel_count = bitmap.width() * height; std::unique_ptr<uint32_t[]> image(new uint32_t[pixel_count]); - UnPreMultiply(bitmap, image.get(), pixel_count); + + // Un-premultiplies each pixel in bitmap into the buffer. Requires + // approximately 10 microseconds for a 16x16 icon on an Intel Core i5. + uint32_t* in = static_cast<uint32_t*>(bitmap.getPixels()); + uint32_t* out = image.get(); + for (int i = 0; i < pixel_count; ++i) + *out++ = SkUnPreMultiply::PMColorToColor(*in++); GridSampler sampler; return CalculateKMeanColorOfBuffer(reinterpret_cast<uint8_t*>(image.get()), diff --git a/chromium/ui/gfx/color_palette.h b/chromium/ui/gfx/color_palette.h index 0d55ae0ab9a..7b2d2358891 100644 --- a/chromium/ui/gfx/color_palette.h +++ b/chromium/ui/gfx/color_palette.h @@ -20,16 +20,20 @@ constexpr SkColor kGoogleBlue500 = SkColorSetRGB(0x42, 0x85, 0xF4); constexpr SkColor kGoogleBlue600 = SkColorSetRGB(0x1A, 0x73, 0xE8); constexpr SkColor kGoogleBlue700 = SkColorSetRGB(0x19, 0x67, 0xD2); constexpr SkColor kGoogleBlue900 = SkColorSetRGB(0x17, 0x4E, 0xA6); +constexpr SkColor kGoogleBlueDark400 = SkColorSetRGB(0x6B, 0xA5, 0xED); constexpr SkColor kGoogleBlueDark600 = SkColorSetRGB(0x25, 0x81, 0xDF); constexpr SkColor kGoogleRed300 = SkColorSetRGB(0xF2, 0x8B, 0xB2); +constexpr SkColor kGoogleRed500 = SkColorSetRGB(0xEA, 0x43, 0x35); constexpr SkColor kGoogleRed600 = SkColorSetRGB(0xD9, 0x30, 0x25); constexpr SkColor kGoogleRed700 = SkColorSetRGB(0xC5, 0x22, 0x1F); constexpr SkColor kGoogleRed800 = SkColorSetRGB(0xB3, 0x14, 0x12); +constexpr SkColor kGoogleRedDark500 = SkColorSetRGB(0xE6, 0x6A, 0x5E); constexpr SkColor kGoogleRedDark600 = SkColorSetRGB(0xD3, 0x3B, 0x30); constexpr SkColor kGoogleRedDark800 = SkColorSetRGB(0xB4, 0x1B, 0x1A); constexpr SkColor kGoogleGreen300 = SkColorSetRGB(0x81, 0xC9, 0x95); constexpr SkColor kGoogleGreen600 = SkColorSetRGB(0x1E, 0x8E, 0x3E); constexpr SkColor kGoogleGreen700 = SkColorSetRGB(0x18, 0x80, 0x38); +constexpr SkColor kGoogleGreenDark500 = SkColorSetRGB(0x41, 0xAF, 0x6A); constexpr SkColor kGoogleGreenDark600 = SkColorSetRGB(0x28, 0x99, 0x4F); constexpr SkColor kGoogleYellow300 = SkColorSetRGB(0xFD, 0xD6, 0x63); constexpr SkColor kGoogleYellow700 = SkColorSetRGB(0xF2, 0x99, 0x00); diff --git a/chromium/ui/gfx/color_space.cc b/chromium/ui/gfx/color_space.cc index d341167573e..95f8665d61f 100644 --- a/chromium/ui/gfx/color_space.cc +++ b/chromium/ui/gfx/color_space.cc @@ -48,8 +48,6 @@ static bool IsAlmostZero(float value) { // static int ColorSpace::kInvalidId = -1; -ColorSpace::ColorSpace() {} - ColorSpace::ColorSpace(PrimaryID primaries, TransferID transfer) : primaries_(primaries), @@ -58,15 +56,6 @@ ColorSpace::ColorSpace(PrimaryID primaries, range_(RangeID::FULL) {} ColorSpace::ColorSpace(PrimaryID primaries, - TransferID transfer, - MatrixID matrix, - RangeID range) - : primaries_(primaries), - transfer_(transfer), - matrix_(matrix), - range_(range) {} - -ColorSpace::ColorSpace(PrimaryID primaries, const SkColorSpaceTransferFn& fn, MatrixID matrix, RangeID range) @@ -120,9 +109,10 @@ ColorSpace::ColorSpace(const SkColorSpace& sk_color_space) } // Use custom primaries, if they are representable as a "to XYZD50" matrix. - if (const auto* to_XYZD50 = sk_color_space.toXYZD50()) { + SkMatrix44 to_XYZD50{SkMatrix44::kUninitialized_Constructor}; + if (sk_color_space.toXYZD50(&to_XYZD50)) { primaries_ = PrimaryID::CUSTOM; - SetCustomPrimaries(*to_XYZD50); + SetCustomPrimaries(to_XYZD50); return; } @@ -130,52 +120,12 @@ ColorSpace::ColorSpace(const SkColorSpace& sk_color_space) primaries_ = PrimaryID::INVALID; } -ColorSpace::ColorSpace(const ColorSpace& other) - : primaries_(other.primaries_), - transfer_(other.transfer_), - matrix_(other.matrix_), - range_(other.range_), - icc_profile_id_(other.icc_profile_id_) { - if (transfer_ == TransferID::CUSTOM) { - memcpy(custom_transfer_params_, other.custom_transfer_params_, - sizeof(custom_transfer_params_)); - } - if (primaries_ == PrimaryID::CUSTOM) { - memcpy(custom_primary_matrix_, other.custom_primary_matrix_, - sizeof(custom_primary_matrix_)); - } -} - -ColorSpace::ColorSpace(ColorSpace&& other) = default; - -ColorSpace& ColorSpace::operator=(const ColorSpace& other) = default; - -ColorSpace::~ColorSpace() = default; - bool ColorSpace::IsValid() const { return primaries_ != PrimaryID::INVALID && transfer_ != TransferID::INVALID && matrix_ != MatrixID::INVALID && range_ != RangeID::INVALID; } // static -ColorSpace ColorSpace::CreateSRGB() { - return ColorSpace(PrimaryID::BT709, TransferID::IEC61966_2_1, MatrixID::RGB, - RangeID::FULL); -} - -// static -ColorSpace ColorSpace::CreateDisplayP3D65() { - return ColorSpace(PrimaryID::SMPTEST432_1, TransferID::IEC61966_2_1, - MatrixID::RGB, RangeID::FULL); -} - -// static -ColorSpace ColorSpace::CreateExtendedSRGB() { - return ColorSpace(PrimaryID::BT709, TransferID::IEC61966_2_1_HDR, - MatrixID::RGB, RangeID::FULL); -} - -// static ColorSpace ColorSpace::CreateCustom(const SkMatrix44& to_XYZD50, const SkColorSpaceTransferFn& fn) { ColorSpace result(ColorSpace::PrimaryID::CUSTOM, @@ -219,38 +169,6 @@ ColorSpace ColorSpace::CreateCustom(const SkMatrix44& to_XYZD50, } // static -ColorSpace ColorSpace::CreateSCRGBLinear() { - return ColorSpace(PrimaryID::BT709, TransferID::LINEAR_HDR, MatrixID::RGB, - RangeID::FULL); -} - -// Static -ColorSpace ColorSpace::CreateXYZD50() { - return ColorSpace(PrimaryID::XYZ_D50, TransferID::LINEAR, MatrixID::RGB, - RangeID::FULL); -} - -// static -ColorSpace ColorSpace::CreateJpeg() { - // TODO(ccameron): Determine which primaries and transfer function were - // intended here. - return ColorSpace(PrimaryID::BT709, TransferID::IEC61966_2_1, - MatrixID::SMPTE170M, RangeID::FULL); -} - -// static -ColorSpace ColorSpace::CreateREC601() { - return ColorSpace(PrimaryID::SMPTE170M, TransferID::SMPTE170M, - MatrixID::SMPTE170M, RangeID::LIMITED); -} - -// static -ColorSpace ColorSpace::CreateREC709() { - return ColorSpace(PrimaryID::BT709, TransferID::BT709, MatrixID::BT709, - RangeID::LIMITED); -} - -// static int ColorSpace::GetNextId() { return g_color_space_id.GetNext(); } diff --git a/chromium/ui/gfx/color_space.h b/chromium/ui/gfx/color_space.h index a625b30ecc1..862edfa8c15 100644 --- a/chromium/ui/gfx/color_space.h +++ b/chromium/ui/gfx/color_space.h @@ -119,45 +119,73 @@ class COLOR_SPACE_EXPORT ColorSpace { LAST = DERIVED, }; - ColorSpace(); + constexpr ColorSpace() {} ColorSpace(PrimaryID primaries, TransferID transfer); - ColorSpace(PrimaryID primaries, - TransferID transfer, - MatrixID matrix, - RangeID full_range); + constexpr ColorSpace(PrimaryID primaries, + TransferID transfer, + MatrixID matrix, + RangeID full_range) + : primaries_(primaries), + transfer_(transfer), + matrix_(matrix), + range_(full_range) {} + ColorSpace(PrimaryID primaries, const SkColorSpaceTransferFn& fn, MatrixID matrix, RangeID full_range); explicit ColorSpace(const SkColorSpace& sk_color_space); - ColorSpace(const ColorSpace& other); - ColorSpace(ColorSpace&& other); - ColorSpace& operator=(const ColorSpace& other); - ~ColorSpace(); // Returns true if this is not the default-constructor object. bool IsValid() const; - static ColorSpace CreateSRGB(); - static ColorSpace CreateDisplayP3D65(); + static constexpr ColorSpace CreateSRGB() { + return ColorSpace(PrimaryID::BT709, TransferID::IEC61966_2_1, MatrixID::RGB, + RangeID::FULL); + } + + static constexpr ColorSpace CreateDisplayP3D65() { + return ColorSpace(PrimaryID::SMPTEST432_1, TransferID::IEC61966_2_1, + MatrixID::RGB, RangeID::FULL); + } static ColorSpace CreateCustom(const SkMatrix44& to_XYZD50, TransferID transfer_id); static ColorSpace CreateCustom(const SkMatrix44& to_XYZD50, const SkColorSpaceTransferFn& fn); - static ColorSpace CreateXYZD50(); + static constexpr ColorSpace CreateXYZD50() { + return ColorSpace(PrimaryID::XYZ_D50, TransferID::LINEAR, MatrixID::RGB, + RangeID::FULL); + } // Extended sRGB matches sRGB for values in [0, 1], and extends the transfer // function to all real values. - static ColorSpace CreateExtendedSRGB(); + static constexpr ColorSpace CreateExtendedSRGB() { + return ColorSpace(PrimaryID::BT709, TransferID::IEC61966_2_1_HDR, + MatrixID::RGB, RangeID::FULL); + } // scRGB uses the same primaries as sRGB but has a linear transfer function // for all real values. - static ColorSpace CreateSCRGBLinear(); + static constexpr ColorSpace CreateSCRGBLinear() { + return ColorSpace(PrimaryID::BT709, TransferID::LINEAR_HDR, MatrixID::RGB, + RangeID::FULL); + } // TODO(ccameron): Remove these, and replace with more generic constructors. - static ColorSpace CreateJpeg(); - static ColorSpace CreateREC601(); - static ColorSpace CreateREC709(); + static constexpr ColorSpace CreateJpeg() { + // TODO(ccameron): Determine which primaries and transfer function were + // intended here. + return ColorSpace(PrimaryID::BT709, TransferID::IEC61966_2_1, + MatrixID::SMPTE170M, RangeID::FULL); + } + static constexpr ColorSpace CreateREC601() { + return ColorSpace(PrimaryID::SMPTE170M, TransferID::SMPTE170M, + MatrixID::SMPTE170M, RangeID::LIMITED); + } + static constexpr ColorSpace CreateREC709() { + return ColorSpace(PrimaryID::BT709, TransferID::BT709, MatrixID::BT709, + RangeID::LIMITED); + } // Generates a process global unique ID that can be used to key a color space. static int GetNextId(); diff --git a/chromium/ui/gfx/color_space_win.cc b/chromium/ui/gfx/color_space_win.cc index 84507a6bc6c..a4541e08b68 100644 --- a/chromium/ui/gfx/color_space_win.cc +++ b/chromium/ui/gfx/color_space_win.cc @@ -132,8 +132,9 @@ DXVA2_ExtendedFormat ColorSpaceWin::GetExtendedFormat( } DXGI_COLOR_SPACE_TYPE ColorSpaceWin::GetDXGIColorSpace( - const ColorSpace& color_space) { - if (color_space.matrix_ == gfx::ColorSpace::MatrixID::RGB) { + const ColorSpace& color_space, + bool force_yuv) { + if (color_space.matrix_ == gfx::ColorSpace::MatrixID::RGB && !force_yuv) { // For RGB, we default to FULL if (color_space.range_ == gfx::ColorSpace::RangeID::LIMITED) { if (color_space.primaries_ == gfx::ColorSpace::PrimaryID::BT2020) { diff --git a/chromium/ui/gfx/color_space_win.h b/chromium/ui/gfx/color_space_win.h index d02bca7898f..9a00bf573b3 100644 --- a/chromium/ui/gfx/color_space_win.h +++ b/chromium/ui/gfx/color_space_win.h @@ -27,7 +27,14 @@ namespace gfx { class COLOR_SPACE_EXPORT ColorSpaceWin { public: static DXVA2_ExtendedFormat GetExtendedFormat(const ColorSpace& color_space); - static DXGI_COLOR_SPACE_TYPE GetDXGIColorSpace(const ColorSpace& color_space); + + // Returns a DXGI_COLOR_SPACE value based on the primaries and transfer + // function of |color_space|. If the color space's MatrixID is RGB, then the + // returned color space is also RGB unless |force_yuv| is true in which case + // it is a YUV color space. + static DXGI_COLOR_SPACE_TYPE GetDXGIColorSpace(const ColorSpace& color_space, + bool force_yuv = false); + static D3D11_VIDEO_PROCESSOR_COLOR_SPACE GetD3D11ColorSpace( const ColorSpace& color_space); }; diff --git a/chromium/ui/gfx/color_transform_unittest.cc b/chromium/ui/gfx/color_transform_unittest.cc index 59407f4ef34..77c85b84902 100644 --- a/chromium/ui/gfx/color_transform_unittest.cc +++ b/chromium/ui/gfx/color_transform_unittest.cc @@ -110,7 +110,6 @@ TEST(SimpleColorSpace, BT2020CLtoBT2020RGB) { ColorSpace bt2020rgb(ColorSpace::PrimaryID::BT2020, ColorSpace::TransferID::BT2020_10, ColorSpace::MatrixID::RGB, ColorSpace::RangeID::FULL); - ColorSpace sRGB = ColorSpace::CreateSRGB(); std::unique_ptr<ColorTransform> t(ColorTransform::NewColorTransform( bt2020cl, bt2020rgb, ColorTransform::Intent::INTENT_ABSOLUTE)); @@ -492,9 +491,9 @@ TEST(SimpleColorSpace, MAYBE_SampleShaderSource) { " color.r = TransferFn1(color.r);\n" " color.g = TransferFn1(color.g);\n" " color.b = TransferFn1(color.b);\n" - " color = mat3(6.27403915e-01, 6.90973178e-02, 1.63914412e-02,\n" - " 3.29283148e-01, 9.19540286e-01, 8.80132914e-02,\n" - " 4.33131084e-02, 1.13623003e-02, 8.95595253e-01) " + " color = mat3(6.27404153e-01, 6.90974146e-02, 1.63914431e-02,\n" + " 3.29283088e-01, 9.19540644e-01, 8.80132765e-02,\n" + " 4.33131084e-02, 1.13623096e-02, 8.95595253e-01) " "* color;\n" " color.r = TransferFn3(color.r);\n" " color.g = TransferFn3(color.g);\n" diff --git a/chromium/ui/gfx/color_utils.cc b/chromium/ui/gfx/color_utils.cc index 62a98b0e4a7..6bb5a66e51e 100644 --- a/chromium/ui/gfx/color_utils.cc +++ b/chromium/ui/gfx/color_utils.cc @@ -189,9 +189,10 @@ void MakeHSLShiftValid(HSL* hsl) { } bool IsHSLShiftMeaningful(const HSL& hsl) { - // -1 in any channel is no-op, but additionally 0.5 is no-op for S/L. - return hsl.h != -1 && hsl.s != -1 && hsl.s != 0.5 && hsl.l != -1 && - hsl.l != 0.5; + // -1 in any channel has no effect, and 0.5 has no effect for S/L. A shift + // with an effective value in ANY channel is meaningful. + return hsl.h != -1 || (hsl.s != -1 && hsl.s != 0.5) || + (hsl.l != -1 && hsl.l != 0.5); } SkColor HSLShift(SkColor color, const HSL& shift) { diff --git a/chromium/ui/gfx/color_utils_unittest.cc b/chromium/ui/gfx/color_utils_unittest.cc index 966205af9ec..bb0a5426aa7 100644 --- a/chromium/ui/gfx/color_utils_unittest.cc +++ b/chromium/ui/gfx/color_utils_unittest.cc @@ -103,6 +103,32 @@ TEST(ColorUtils, IsWithinHSLRangeHueWrapAround) { EXPECT_FALSE(IsWithinHSLRange(hsl, lower, upper)); } +TEST(ColorUtils, IsHSLShiftMeaningful) { + HSL noop_all_neg_one{-1.0, -1.0, -1.0}; + HSL noop_s_point_five{-1.0, 0.5, -1.0}; + HSL noop_l_point_five{-1.0, -1.0, 0.5}; + + HSL only_h{0.1, -1.0, -1.0}; + HSL only_s{-1.0, 0.1, -1.0}; + HSL only_l{-1.0, -1.0, 0.1}; + HSL only_hs{0.1, 0.1, -1.0}; + HSL only_hl{0.1, -1.0, 0.1}; + HSL only_sl{-1.0, 0.1, 0.1}; + HSL all_set{0.1, 0.2, 0.3}; + + EXPECT_FALSE(IsHSLShiftMeaningful(noop_all_neg_one)); + EXPECT_FALSE(IsHSLShiftMeaningful(noop_s_point_five)); + EXPECT_FALSE(IsHSLShiftMeaningful(noop_l_point_five)); + + EXPECT_TRUE(IsHSLShiftMeaningful(only_h)); + EXPECT_TRUE(IsHSLShiftMeaningful(only_s)); + EXPECT_TRUE(IsHSLShiftMeaningful(only_l)); + EXPECT_TRUE(IsHSLShiftMeaningful(only_hs)); + EXPECT_TRUE(IsHSLShiftMeaningful(only_hl)); + EXPECT_TRUE(IsHSLShiftMeaningful(only_sl)); + EXPECT_TRUE(IsHSLShiftMeaningful(all_set)); +} + TEST(ColorUtils, ColorToHSLRegisterSpill) { // In a opt build on Linux, this was causing a register spill on my laptop // (Pentium M) when converting from SkColor to HSL. diff --git a/chromium/ui/gfx/font_list_impl.cc b/chromium/ui/gfx/font_list_impl.cc index 6decee94999..da82a0fb90b 100644 --- a/chromium/ui/gfx/font_list_impl.cc +++ b/chromium/ui/gfx/font_list_impl.cc @@ -219,8 +219,7 @@ void FontListImpl::CacheCommonFontHeightAndBaseline() const { int ascent = 0; int descent = 0; const std::vector<Font>& fonts = GetFonts(); - for (std::vector<Font>::const_iterator i = fonts.begin(); - i != fonts.end(); ++i) { + for (auto i = fonts.begin(); i != fonts.end(); ++i) { ascent = std::max(ascent, i->GetBaseline()); descent = std::max(descent, i->GetHeight() - i->GetBaseline()); } diff --git a/chromium/ui/gfx/font_render_params_android.cc b/chromium/ui/gfx/font_render_params_android.cc index 846190937ce..4657d6a5e42 100644 --- a/chromium/ui/gfx/font_render_params_android.cc +++ b/chromium/ui/gfx/font_render_params_android.cc @@ -6,6 +6,7 @@ #include "base/logging.h" #include "base/macros.h" +#include "base/no_destructor.h" namespace gfx { @@ -39,8 +40,8 @@ FontRenderParams GetFontRenderParams(const FontRenderParamsQuery& query, if (family_out) NOTIMPLEMENTED(); // Customized font rendering settings are not supported, only defaults. - CR_DEFINE_STATIC_LOCAL(const gfx::FontRenderParams, params, (LoadDefaults())); - return params; + static const base::NoDestructor<gfx::FontRenderParams> params(LoadDefaults()); + return *params; } float GetFontRenderParamsDeviceScaleFactor() { diff --git a/chromium/ui/gfx/font_render_params_fuchsia.cc b/chromium/ui/gfx/font_render_params_fuchsia.cc index f693f926c22..6229bcc9db6 100644 --- a/chromium/ui/gfx/font_render_params_fuchsia.cc +++ b/chromium/ui/gfx/font_render_params_fuchsia.cc @@ -6,6 +6,7 @@ #include "base/logging.h" #include "base/macros.h" +#include "base/no_destructor.h" namespace gfx { @@ -31,8 +32,8 @@ FontRenderParams GetFontRenderParams(const FontRenderParamsQuery& query, if (family_out) NOTIMPLEMENTED(); // Customized font rendering settings are not supported, only defaults. - CR_DEFINE_STATIC_LOCAL(const gfx::FontRenderParams, params, (LoadDefaults())); - return params; + static const base::NoDestructor<gfx::FontRenderParams> params(LoadDefaults()); + return *params; } } // namespace gfx diff --git a/chromium/ui/gfx/font_render_params_linux.cc b/chromium/ui/gfx/font_render_params_linux.cc index f12db00e1b9..1277d923a7f 100644 --- a/chromium/ui/gfx/font_render_params_linux.cc +++ b/chromium/ui/gfx/font_render_params_linux.cc @@ -138,8 +138,7 @@ bool QueryFontconfig(const FontRenderParamsQuery& query, FcPatternAddBool(query_pattern.get(), FC_SCALABLE, FcTrue); - for (std::vector<std::string>::const_iterator it = query.families.begin(); - it != query.families.end(); ++it) { + for (auto it = query.families.begin(); it != query.families.end(); ++it) { FcPatternAddString(query_pattern.get(), FC_FAMILY, reinterpret_cast<const FcChar8*>(it->c_str())); } diff --git a/chromium/ui/gfx/font_render_params_mac.cc b/chromium/ui/gfx/font_render_params_mac.cc index 83fd341e507..80b46daadfc 100644 --- a/chromium/ui/gfx/font_render_params_mac.cc +++ b/chromium/ui/gfx/font_render_params_mac.cc @@ -6,6 +6,7 @@ #include "base/logging.h" #include "base/macros.h" +#include "base/no_destructor.h" namespace gfx { @@ -31,8 +32,8 @@ FontRenderParams GetFontRenderParams(const FontRenderParamsQuery& query, if (family_out) NOTIMPLEMENTED(); // TODO: Query the OS for font render settings instead of returning defaults. - CR_DEFINE_STATIC_LOCAL(const gfx::FontRenderParams, params, (LoadDefaults())); - return params; + static const base::NoDestructor<gfx::FontRenderParams> params(LoadDefaults()); + return *params; } } // namespace gfx diff --git a/chromium/ui/gfx/font_unittest.cc b/chromium/ui/gfx/font_unittest.cc index 00945ae910d..e9e810640ec 100644 --- a/chromium/ui/gfx/font_unittest.cc +++ b/chromium/ui/gfx/font_unittest.cc @@ -21,33 +21,6 @@ namespace { using FontTest = testing::Test; -#if defined(OS_WIN) -class ScopedMinimumFontSizeCallback { - public: - explicit ScopedMinimumFontSizeCallback(int minimum_size) { - minimum_size_ = minimum_size; - old_callback_ = PlatformFontWin::get_minimum_font_size_callback; - PlatformFontWin::get_minimum_font_size_callback = &GetMinimumFontSize; - } - - ~ScopedMinimumFontSizeCallback() { - PlatformFontWin::get_minimum_font_size_callback = old_callback_; - } - - private: - static int GetMinimumFontSize() { - return minimum_size_; - } - - PlatformFontWin::GetMinimumFontSizeCallback old_callback_; - static int minimum_size_; - - DISALLOW_COPY_AND_ASSIGN(ScopedMinimumFontSizeCallback); -}; - -int ScopedMinimumFontSizeCallback::minimum_size_ = 0; -#endif // defined(OS_WIN) - TEST_F(FontTest, LoadArial) { Font cf(kTestFontName, 16); #if defined(OS_WIN) || defined(OS_MACOSX) || defined(OS_IOS) @@ -151,8 +124,7 @@ TEST_F(FontTest, DeriveFont) { #if defined(OS_WIN) TEST_F(FontTest, DeriveResizesIfSizeTooSmall) { Font cf(kTestFontName, 8); - // The minimum font size is set to 5 in browser_main.cc. - ScopedMinimumFontSizeCallback minimum_size(5); + PlatformFontWin::SetGetMinimumFontSizeCallback([] { return 5; }); Font derived_font = cf.Derive(-4, cf.GetStyle(), cf.GetWeight()); EXPECT_EQ(5, derived_font.GetFontSize()); @@ -160,8 +132,7 @@ TEST_F(FontTest, DeriveResizesIfSizeTooSmall) { TEST_F(FontTest, DeriveKeepsOriginalSizeIfHeightOk) { Font cf(kTestFontName, 8); - // The minimum font size is set to 5 in browser_main.cc. - ScopedMinimumFontSizeCallback minimum_size(5); + PlatformFontWin::SetGetMinimumFontSizeCallback([] { return 5; }); Font derived_font = cf.Derive(-2, cf.GetStyle(), cf.GetWeight()); EXPECT_EQ(6, derived_font.GetFontSize()); diff --git a/chromium/ui/gfx/geometry/BUILD.gn b/chromium/ui/gfx/geometry/BUILD.gn index 74043fa5e0f..6450fdd952c 100644 --- a/chromium/ui/gfx/geometry/BUILD.gn +++ b/chromium/ui/gfx/geometry/BUILD.gn @@ -16,6 +16,7 @@ jumbo_component("geometry") { "cubic_bezier.h", "dip_util.cc", "dip_util.h", + "geometry_export.h", "insets.cc", "insets.h", "insets_f.cc", @@ -59,11 +60,10 @@ jumbo_component("geometry") { "vector3d_f.h", ] - defines = [ "GFX_IMPLEMENTATION" ] + defines = [ "GEOMETRY_IMPLEMENTATION" ] deps = [ "//base", - "//ui/gfx:gfx_export", ] if (is_android && !is_debug) { diff --git a/chromium/ui/gfx/geometry/angle_conversions.h b/chromium/ui/gfx/geometry/angle_conversions.h index 68e9209f726..876ad5c5ac4 100644 --- a/chromium/ui/gfx/geometry/angle_conversions.h +++ b/chromium/ui/gfx/geometry/angle_conversions.h @@ -6,21 +6,21 @@ #define UI_GFX_GEOMETRY_ANGLE_CONVERSIONS_H_ #include "base/numerics/math_constants.h" -#include "ui/gfx/gfx_export.h" +#include "ui/gfx/geometry/geometry_export.h" namespace gfx { -GFX_EXPORT constexpr double DegToRad(double deg) { +GEOMETRY_EXPORT constexpr double DegToRad(double deg) { return deg * base::kPiDouble / 180.0; } -GFX_EXPORT constexpr float DegToRad(float deg) { +GEOMETRY_EXPORT constexpr float DegToRad(float deg) { return deg * base::kPiFloat / 180.0f; } -GFX_EXPORT constexpr double RadToDeg(double rad) { +GEOMETRY_EXPORT constexpr double RadToDeg(double rad) { return rad * 180.0 / base::kPiDouble; } -GFX_EXPORT constexpr float RadToDeg(float rad) { +GEOMETRY_EXPORT constexpr float RadToDeg(float rad) { return rad * 180.0f / base::kPiFloat; } diff --git a/chromium/ui/gfx/geometry/axis_transform2d.h b/chromium/ui/gfx/geometry/axis_transform2d.h index 1829bf60fcf..6aec0e5acbf 100644 --- a/chromium/ui/gfx/geometry/axis_transform2d.h +++ b/chromium/ui/gfx/geometry/axis_transform2d.h @@ -5,9 +5,9 @@ #ifndef UI_GFX_GEOMETRY_AXIS_TRANSFORM2D_H_ #define UI_GFX_GEOMETRY_AXIS_TRANSFORM2D_H_ +#include "ui/gfx/geometry/geometry_export.h" #include "ui/gfx/geometry/rect_f.h" #include "ui/gfx/geometry/vector2d_f.h" -#include "ui/gfx/gfx_export.h" namespace gfx { @@ -16,7 +16,7 @@ namespace gfx { // Internally this is stored as a scalar pre-scale factor, and a vector // for post-translation. The class constructor and member accessor follows // the same convention. -class GFX_EXPORT AxisTransform2d { +class GEOMETRY_EXPORT AxisTransform2d { public: constexpr AxisTransform2d() = default; constexpr AxisTransform2d(float scale, const Vector2dF& translation) diff --git a/chromium/ui/gfx/geometry/box_f.h b/chromium/ui/gfx/geometry/box_f.h index b4d9eff1c02..443174d3eb6 100644 --- a/chromium/ui/gfx/geometry/box_f.h +++ b/chromium/ui/gfx/geometry/box_f.h @@ -15,7 +15,7 @@ namespace gfx { // A 3d version of gfx::RectF, with the positive z-axis pointed towards // the camera. -class GFX_EXPORT BoxF { +class GEOMETRY_EXPORT BoxF { public: constexpr BoxF() : BoxF(0, 0, 0) {} constexpr BoxF(float width, float height, float depth) @@ -114,7 +114,7 @@ class GFX_EXPORT BoxF { float depth_; }; -GFX_EXPORT BoxF UnionBoxes(const BoxF& a, const BoxF& b); +GEOMETRY_EXPORT BoxF UnionBoxes(const BoxF& a, const BoxF& b); inline BoxF ScaleBox(const BoxF& b, float x_scale, diff --git a/chromium/ui/gfx/geometry/cubic_bezier.h b/chromium/ui/gfx/geometry/cubic_bezier.h index 013123999c5..dcda5798695 100644 --- a/chromium/ui/gfx/geometry/cubic_bezier.h +++ b/chromium/ui/gfx/geometry/cubic_bezier.h @@ -6,11 +6,11 @@ #define UI_GFX_GEOMETRY_CUBIC_BEZIER_H_ #include "base/macros.h" -#include "ui/gfx/gfx_export.h" +#include "ui/gfx/geometry/geometry_export.h" namespace gfx { -class GFX_EXPORT CubicBezier { +class GEOMETRY_EXPORT CubicBezier { public: CubicBezier(double p1x, double p1y, double p2x, double p2y); CubicBezier(const CubicBezier& other); diff --git a/chromium/ui/gfx/geometry/dip_util.h b/chromium/ui/gfx/geometry/dip_util.h index e88d49b0079..cb344c93fed 100644 --- a/chromium/ui/gfx/geometry/dip_util.h +++ b/chromium/ui/gfx/geometry/dip_util.h @@ -5,7 +5,7 @@ #ifndef UI_GFX_GEOMETRY_DIP_UTIL_H_ #define UI_GFX_GEOMETRY_DIP_UTIL_H_ -#include "ui/gfx/gfx_export.h" +#include "ui/gfx/geometry/geometry_export.h" namespace gfx { @@ -15,27 +15,31 @@ class PointF; class Rect; class Size; -GFX_EXPORT gfx::Insets ConvertInsetsToDIP(float scale_factor, - const gfx::Insets& insets_in_pixel); -GFX_EXPORT gfx::Point ConvertPointToDIP(float scale_factor, - const gfx::Point& point_in_pixel); -GFX_EXPORT gfx::PointF ConvertPointToDIP(float scale_factor, - const gfx::PointF& point_in_pixel); -GFX_EXPORT gfx::Size ConvertSizeToDIP(float scale_factor, - const gfx::Size& size_in_pixel); -GFX_EXPORT gfx::Rect ConvertRectToDIP(float scale_factor, - const gfx::Rect& rect_in_pixel); +GEOMETRY_EXPORT gfx::Insets ConvertInsetsToDIP( + float scale_factor, + const gfx::Insets& insets_in_pixel); +GEOMETRY_EXPORT gfx::Point ConvertPointToDIP(float scale_factor, + const gfx::Point& point_in_pixel); +GEOMETRY_EXPORT gfx::PointF ConvertPointToDIP( + float scale_factor, + const gfx::PointF& point_in_pixel); +GEOMETRY_EXPORT gfx::Size ConvertSizeToDIP(float scale_factor, + const gfx::Size& size_in_pixel); +GEOMETRY_EXPORT gfx::Rect ConvertRectToDIP(float scale_factor, + const gfx::Rect& rect_in_pixel); -GFX_EXPORT gfx::Insets ConvertInsetsToPixel(float scale_factor, - const gfx::Insets& insets_in_dip); -GFX_EXPORT gfx::Point ConvertPointToPixel(float scale_factor, - const gfx::Point& point_in_dip); -GFX_EXPORT gfx::PointF ConvertPointToPixel(float scale_factor, - const gfx::PointF& point_in_dip); -GFX_EXPORT gfx::Size ConvertSizeToPixel(float scale_factor, - const gfx::Size& size_in_dip); -GFX_EXPORT gfx::Rect ConvertRectToPixel(float scale_factor, - const gfx::Rect& rect_in_dip); +GEOMETRY_EXPORT gfx::Insets ConvertInsetsToPixel( + float scale_factor, + const gfx::Insets& insets_in_dip); +GEOMETRY_EXPORT gfx::Point ConvertPointToPixel(float scale_factor, + const gfx::Point& point_in_dip); +GEOMETRY_EXPORT gfx::PointF ConvertPointToPixel( + float scale_factor, + const gfx::PointF& point_in_dip); +GEOMETRY_EXPORT gfx::Size ConvertSizeToPixel(float scale_factor, + const gfx::Size& size_in_dip); +GEOMETRY_EXPORT gfx::Rect ConvertRectToPixel(float scale_factor, + const gfx::Rect& rect_in_dip); } // gfx #endif // UI_GFX_GEOMETRY_DIP_UTIL_H_ diff --git a/chromium/ui/gfx/geometry/geometry_export.h b/chromium/ui/gfx/geometry/geometry_export.h new file mode 100644 index 00000000000..5e687875eb9 --- /dev/null +++ b/chromium/ui/gfx/geometry/geometry_export.h @@ -0,0 +1,29 @@ +// 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_GFX_GEOMETRY_GEOMETRY_EXPORT_H_ +#define UI_GFX_GEOMETRY_GEOMETRY_EXPORT_H_ + +#if defined(COMPONENT_BUILD) +#if defined(WIN32) + +#if defined(GEOMETRY_IMPLEMENTATION) +#define GEOMETRY_EXPORT __declspec(dllexport) +#else +#define GEOMETRY_EXPORT __declspec(dllimport) +#endif // defined(GEOMETRY_IMPLEMENTATION) + +#else // defined(WIN32) +#if defined(GEOMETRY_IMPLEMENTATION) +#define GEOMETRY_EXPORT __attribute__((visibility("default"))) +#else +#define GEOMETRY_EXPORT +#endif +#endif + +#else // defined(COMPONENT_BUILD) +#define GEOMETRY_EXPORT +#endif + +#endif // UI_GFX_GEOMETRY_GEOMETRY_EXPORT_H_ diff --git a/chromium/ui/gfx/geometry/insets.h b/chromium/ui/gfx/geometry/insets.h index a88bae3e42b..10516583b82 100644 --- a/chromium/ui/gfx/geometry/insets.h +++ b/chromium/ui/gfx/geometry/insets.h @@ -7,8 +7,8 @@ #include <string> +#include "ui/gfx/geometry/geometry_export.h" #include "ui/gfx/geometry/insets_f.h" -#include "ui/gfx/gfx_export.h" namespace gfx { @@ -21,7 +21,7 @@ class Vector2d; // This can be used to represent a space within a rectangle, by "shrinking" the // rectangle by the inset amount on all four sides. Alternatively, it can // represent a border that has a different thickness on each side. -class GFX_EXPORT Insets { +class GEOMETRY_EXPORT Insets { public: constexpr Insets() : top_(0), left_(0), bottom_(0), right_(0) {} constexpr explicit Insets(int all) diff --git a/chromium/ui/gfx/geometry/insets_f.h b/chromium/ui/gfx/geometry/insets_f.h index 32302879578..281a599b97d 100644 --- a/chromium/ui/gfx/geometry/insets_f.h +++ b/chromium/ui/gfx/geometry/insets_f.h @@ -7,12 +7,12 @@ #include <string> -#include "ui/gfx/gfx_export.h" +#include "ui/gfx/geometry/geometry_export.h" namespace gfx { // A floating point version of gfx::Insets. -class GFX_EXPORT InsetsF { +class GEOMETRY_EXPORT InsetsF { public: constexpr InsetsF() : top_(0.f), left_(0.f), bottom_(0.f), right_(0.f) {} constexpr explicit InsetsF(float all) diff --git a/chromium/ui/gfx/geometry/matrix3_f.h b/chromium/ui/gfx/geometry/matrix3_f.h index 7fab2e3e7c7..065a7fb3f92 100644 --- a/chromium/ui/gfx/geometry/matrix3_f.h +++ b/chromium/ui/gfx/geometry/matrix3_f.h @@ -10,7 +10,7 @@ namespace gfx { -class GFX_EXPORT Matrix3F { +class GEOMETRY_EXPORT Matrix3F { public: ~Matrix3F(); @@ -131,8 +131,10 @@ inline Matrix3F operator-(const Matrix3F& lhs, const Matrix3F& rhs) { return lhs.Subtract(rhs); } -GFX_EXPORT Matrix3F MatrixProduct(const Matrix3F& lhs, const Matrix3F& rhs); -GFX_EXPORT Vector3dF MatrixProduct(const Matrix3F& lhs, const Vector3dF& rhs); +GEOMETRY_EXPORT Matrix3F MatrixProduct(const Matrix3F& lhs, + const Matrix3F& rhs); +GEOMETRY_EXPORT Vector3dF MatrixProduct(const Matrix3F& lhs, + const Vector3dF& rhs); } // namespace gfx diff --git a/chromium/ui/gfx/geometry/point.h b/chromium/ui/gfx/geometry/point.h index b1ba5065de4..f68433b9065 100644 --- a/chromium/ui/gfx/geometry/point.h +++ b/chromium/ui/gfx/geometry/point.h @@ -11,8 +11,8 @@ #include "base/numerics/clamped_math.h" #include "build/build_config.h" +#include "ui/gfx/geometry/geometry_export.h" #include "ui/gfx/geometry/vector2d.h" -#include "ui/gfx/gfx_export.h" #if defined(OS_WIN) typedef unsigned long DWORD; @@ -24,7 +24,7 @@ typedef struct CGPoint CGPoint; namespace gfx { // A point has an x and y coordinate. -class GFX_EXPORT Point { +class GEOMETRY_EXPORT Point { public: constexpr Point() : x_(0), y_(0) {} constexpr Point(int x, int y) : x_(x), y_(y) {} @@ -130,18 +130,18 @@ inline Point PointAtOffsetFromOrigin(const Vector2d& offset_from_origin) { void PrintTo(const Point& point, ::std::ostream* os); // Helper methods to scale a gfx::Point to a new gfx::Point. -GFX_EXPORT Point ScaleToCeiledPoint(const Point& point, - float x_scale, - float y_scale); -GFX_EXPORT Point ScaleToCeiledPoint(const Point& point, float x_scale); -GFX_EXPORT Point ScaleToFlooredPoint(const Point& point, - float x_scale, - float y_scale); -GFX_EXPORT Point ScaleToFlooredPoint(const Point& point, float x_scale); -GFX_EXPORT Point ScaleToRoundedPoint(const Point& point, - float x_scale, - float y_scale); -GFX_EXPORT Point ScaleToRoundedPoint(const Point& point, float x_scale); +GEOMETRY_EXPORT Point ScaleToCeiledPoint(const Point& point, + float x_scale, + float y_scale); +GEOMETRY_EXPORT Point ScaleToCeiledPoint(const Point& point, float x_scale); +GEOMETRY_EXPORT Point ScaleToFlooredPoint(const Point& point, + float x_scale, + float y_scale); +GEOMETRY_EXPORT Point ScaleToFlooredPoint(const Point& point, float x_scale); +GEOMETRY_EXPORT Point ScaleToRoundedPoint(const Point& point, + float x_scale, + float y_scale); +GEOMETRY_EXPORT Point ScaleToRoundedPoint(const Point& point, float x_scale); } // namespace gfx diff --git a/chromium/ui/gfx/geometry/point3_f.h b/chromium/ui/gfx/geometry/point3_f.h index 15a560c4972..e295e988ffe 100644 --- a/chromium/ui/gfx/geometry/point3_f.h +++ b/chromium/ui/gfx/geometry/point3_f.h @@ -8,14 +8,14 @@ #include <iosfwd> #include <string> +#include "ui/gfx/geometry/geometry_export.h" #include "ui/gfx/geometry/point_f.h" #include "ui/gfx/geometry/vector3d_f.h" -#include "ui/gfx/gfx_export.h" namespace gfx { // A point has an x, y and z coordinate. -class GFX_EXPORT Point3F { +class GEOMETRY_EXPORT Point3F { public: constexpr Point3F() : x_(0), y_(0), z_(0) {} constexpr Point3F(float x, float y, float z) : x_(x), y_(y), z_(z) {} @@ -89,15 +89,15 @@ inline bool operator!=(const Point3F& lhs, const Point3F& rhs) { } // Add a vector to a point, producing a new point offset by the vector. -GFX_EXPORT Point3F operator+(const Point3F& lhs, const Vector3dF& rhs); +GEOMETRY_EXPORT Point3F operator+(const Point3F& lhs, const Vector3dF& rhs); // Subtract a vector from a point, producing a new point offset by the vector's // inverse. -GFX_EXPORT Point3F operator-(const Point3F& lhs, const Vector3dF& rhs); +GEOMETRY_EXPORT Point3F operator-(const Point3F& lhs, const Vector3dF& rhs); // Subtract one point from another, producing a vector that represents the // distances between the two points along each axis. -GFX_EXPORT Vector3dF operator-(const Point3F& lhs, const Point3F& rhs); +GEOMETRY_EXPORT Vector3dF operator-(const Point3F& lhs, const Point3F& rhs); inline Point3F PointAtOffsetFromOrigin(const Vector3dF& offset) { return Point3F(offset.x(), offset.y(), offset.z()); diff --git a/chromium/ui/gfx/geometry/point_conversions.h b/chromium/ui/gfx/geometry/point_conversions.h index bfab9e4e463..894272c6bd3 100644 --- a/chromium/ui/gfx/geometry/point_conversions.h +++ b/chromium/ui/gfx/geometry/point_conversions.h @@ -11,13 +11,13 @@ namespace gfx { // Returns a Point with each component from the input PointF floored. -GFX_EXPORT Point ToFlooredPoint(const PointF& point); +GEOMETRY_EXPORT Point ToFlooredPoint(const PointF& point); // Returns a Point with each component from the input PointF ceiled. -GFX_EXPORT Point ToCeiledPoint(const PointF& point); +GEOMETRY_EXPORT Point ToCeiledPoint(const PointF& point); // Returns a Point with each component from the input PointF rounded. -GFX_EXPORT Point ToRoundedPoint(const PointF& point); +GEOMETRY_EXPORT Point ToRoundedPoint(const PointF& point); } // namespace gfx diff --git a/chromium/ui/gfx/geometry/point_f.h b/chromium/ui/gfx/geometry/point_f.h index 5d92b1100c4..a3823bcb0f5 100644 --- a/chromium/ui/gfx/geometry/point_f.h +++ b/chromium/ui/gfx/geometry/point_f.h @@ -9,14 +9,14 @@ #include <string> #include <tuple> +#include "ui/gfx/geometry/geometry_export.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/vector2d_f.h" -#include "ui/gfx/gfx_export.h" namespace gfx { // A floating version of gfx::Point. -class GFX_EXPORT PointF { +class GEOMETRY_EXPORT PointF { public: constexpr PointF() : x_(0.f), y_(0.f) {} constexpr PointF(float x, float y) : x_(x), y_(y) {} @@ -110,7 +110,9 @@ inline PointF PointAtOffsetFromOrigin(const Vector2dF& offset_from_origin) { return PointF(offset_from_origin.x(), offset_from_origin.y()); } -GFX_EXPORT PointF ScalePoint(const PointF& p, float x_scale, float y_scale); +GEOMETRY_EXPORT PointF ScalePoint(const PointF& p, + float x_scale, + float y_scale); inline PointF ScalePoint(const PointF& p, float scale) { return ScalePoint(p, scale, scale); diff --git a/chromium/ui/gfx/geometry/quad_f.h b/chromium/ui/gfx/geometry/quad_f.h index 8e45b3e2dea..3f9e08ac3b9 100644 --- a/chromium/ui/gfx/geometry/quad_f.h +++ b/chromium/ui/gfx/geometry/quad_f.h @@ -13,15 +13,15 @@ #include <string> #include "base/logging.h" +#include "ui/gfx/geometry/geometry_export.h" #include "ui/gfx/geometry/point_f.h" #include "ui/gfx/geometry/rect_f.h" -#include "ui/gfx/gfx_export.h" namespace gfx { // A Quad is defined by four corners, allowing it to have edges that are not // axis-aligned, unlike a Rect. -class GFX_EXPORT QuadF { +class GEOMETRY_EXPORT QuadF { public: constexpr QuadF() = default; constexpr QuadF(const PointF& p1, @@ -115,10 +115,10 @@ inline bool operator!=(const QuadF& lhs, const QuadF& rhs) { } // Add a vector to a quad, offseting each point in the quad by the vector. -GFX_EXPORT QuadF operator+(const QuadF& lhs, const Vector2dF& rhs); +GEOMETRY_EXPORT QuadF operator+(const QuadF& lhs, const Vector2dF& rhs); // Subtract a vector from a quad, offseting each point in the quad by the // inverse of the vector. -GFX_EXPORT QuadF operator-(const QuadF& lhs, const Vector2dF& rhs); +GEOMETRY_EXPORT QuadF operator-(const QuadF& lhs, const Vector2dF& rhs); // This is declared here for use in gtest-based unit tests but is defined in // the //ui/gfx:test_support target. Depend on that to use this in your unit diff --git a/chromium/ui/gfx/geometry/quaternion.h b/chromium/ui/gfx/geometry/quaternion.h index 7f65b796e6f..2558f271063 100644 --- a/chromium/ui/gfx/geometry/quaternion.h +++ b/chromium/ui/gfx/geometry/quaternion.h @@ -7,13 +7,13 @@ #include <string> -#include "ui/gfx/gfx_export.h" +#include "ui/gfx/geometry/geometry_export.h" namespace gfx { class Vector3dF; -class GFX_EXPORT Quaternion { +class GEOMETRY_EXPORT Quaternion { public: constexpr Quaternion() = default; constexpr Quaternion(double x, double y, double z, double w) diff --git a/chromium/ui/gfx/geometry/rect.h b/chromium/ui/gfx/geometry/rect.h index 8a4a3f878f6..84dc72c069c 100644 --- a/chromium/ui/gfx/geometry/rect.h +++ b/chromium/ui/gfx/geometry/rect.h @@ -33,7 +33,7 @@ namespace gfx { class Insets; -class GFX_EXPORT Rect { +class GEOMETRY_EXPORT Rect { public: constexpr Rect() = default; constexpr Rect(int width, int height) : size_(width, height) {} @@ -258,16 +258,16 @@ inline bool operator!=(const Rect& lhs, const Rect& rhs) { return !(lhs == rhs); } -GFX_EXPORT Rect operator+(const Rect& lhs, const Vector2d& rhs); -GFX_EXPORT Rect operator-(const Rect& lhs, const Vector2d& rhs); +GEOMETRY_EXPORT Rect operator+(const Rect& lhs, const Vector2d& rhs); +GEOMETRY_EXPORT Rect operator-(const Rect& lhs, const Vector2d& rhs); inline Rect operator+(const Vector2d& lhs, const Rect& rhs) { return rhs + lhs; } -GFX_EXPORT Rect IntersectRects(const Rect& a, const Rect& b); -GFX_EXPORT Rect UnionRects(const Rect& a, const Rect& b); -GFX_EXPORT Rect SubtractRects(const Rect& a, const Rect& b); +GEOMETRY_EXPORT Rect IntersectRects(const Rect& a, const Rect& b); +GEOMETRY_EXPORT Rect UnionRects(const Rect& a, const Rect& b); +GEOMETRY_EXPORT Rect SubtractRects(const Rect& a, const Rect& b); // Constructs a rectangle with |p1| and |p2| as opposite corners. // @@ -275,7 +275,7 @@ GFX_EXPORT Rect SubtractRects(const Rect& a, const Rect& b); // points", except that we consider points on the right/bottom edges of the // rect to be outside the rect. So technically one or both points will not be // contained within the rect, because they will appear on one of these edges. -GFX_EXPORT Rect BoundingRect(const Point& p1, const Point& p2); +GEOMETRY_EXPORT Rect BoundingRect(const Point& p1, const Point& p2); // Scales the rect and returns the enclosing rect. Use this only the inputs are // known to not overflow. Use ScaleToEnclosingRectSafe if the inputs are diff --git a/chromium/ui/gfx/geometry/rect_conversions.cc b/chromium/ui/gfx/geometry/rect_conversions.cc index 3a5b2dceade..43d866f8dba 100644 --- a/chromium/ui/gfx/geometry/rect_conversions.cc +++ b/chromium/ui/gfx/geometry/rect_conversions.cc @@ -12,6 +12,20 @@ namespace gfx { +namespace { + +int ToFlooredIntIgnoringError(float f, float error) { + int rounded = ToRoundedInt(f); + return std::abs(rounded - f) < error ? rounded : ToFlooredInt(f); +} + +int ToCeiledIntIgnoringError(float f, float error) { + int rounded = ToRoundedInt(f); + return std::abs(rounded - f) < error ? rounded : ToCeiledInt(f); +} + +} // anonymous namespace + Rect ToEnclosingRect(const RectF& r) { int left = ToFlooredInt(r.x()); int right = r.width() ? ToCeiledInt(r.right()) : left; @@ -23,6 +37,17 @@ Rect ToEnclosingRect(const RectF& r) { return result; } +Rect ToEnclosingRectIgnoringError(const RectF& r, float error) { + int left = ToFlooredIntIgnoringError(r.x(), error); + int right = r.width() ? ToCeiledIntIgnoringError(r.right(), error) : left; + int top = ToFlooredIntIgnoringError(r.y(), error); + int bottom = r.height() ? ToCeiledIntIgnoringError(r.bottom(), error) : top; + + Rect result; + result.SetByBounds(left, top, right, bottom); + return result; +} + Rect ToEnclosedRect(const RectF& rect) { Rect result; result.SetByBounds(ToCeiledInt(rect.x()), ToCeiledInt(rect.y()), diff --git a/chromium/ui/gfx/geometry/rect_conversions.h b/chromium/ui/gfx/geometry/rect_conversions.h index 617074abeee..44b94d9104a 100644 --- a/chromium/ui/gfx/geometry/rect_conversions.h +++ b/chromium/ui/gfx/geometry/rect_conversions.h @@ -11,25 +11,33 @@ namespace gfx { // Returns the smallest Rect that encloses the given RectF. -GFX_EXPORT Rect ToEnclosingRect(const RectF& rect); +GEOMETRY_EXPORT Rect ToEnclosingRect(const RectF& rect); + +// Similar to ToEnclosingRect(), but for each edge, if the distance between the +// edge and the nearest integer grid is smaller than |error|, the edge is +// snapped to the integer grid. Unlike ToNearestRect() which only accepts +// integer rect with or without floating point error, this function also accepts +// non-integer rect. +GEOMETRY_EXPORT Rect ToEnclosingRectIgnoringError(const RectF& rect, + float error); // Returns the largest Rect that is enclosed by the given RectF. -GFX_EXPORT Rect ToEnclosedRect(const RectF& rect); +GEOMETRY_EXPORT Rect ToEnclosedRect(const RectF& rect); // Returns the Rect after snapping the corners of the RectF to an integer grid. // This should only be used when the RectF you provide is expected to be an // integer rect with floating point error. If it is an arbitrary RectF, then // you should use a different method. -GFX_EXPORT Rect ToNearestRect(const RectF& rect); +GEOMETRY_EXPORT Rect ToNearestRect(const RectF& rect); // Returns true if the Rect produced after snapping the corners of the RectF // to an integer grid is withing |distance|. -GFX_EXPORT bool IsNearestRectWithinDistance( - const gfx::RectF& rect, float distance); +GEOMETRY_EXPORT bool IsNearestRectWithinDistance(const gfx::RectF& rect, + float distance); // Returns a Rect obtained by flooring the values of the given RectF. // Please prefer the previous two functions in new code. -GFX_EXPORT Rect ToFlooredRectDeprecated(const RectF& rect); +GEOMETRY_EXPORT Rect ToFlooredRectDeprecated(const RectF& rect); } // namespace gfx diff --git a/chromium/ui/gfx/geometry/rect_f.h b/chromium/ui/gfx/geometry/rect_f.h index 7ec5c97549b..e07f86d50db 100644 --- a/chromium/ui/gfx/geometry/rect_f.h +++ b/chromium/ui/gfx/geometry/rect_f.h @@ -23,7 +23,7 @@ namespace gfx { class InsetsF; // A floating version of gfx::Rect. -class GFX_EXPORT RectF { +class GEOMETRY_EXPORT RectF { public: constexpr RectF() = default; constexpr RectF(float width, float height) : size_(width, height) {} @@ -225,9 +225,9 @@ inline RectF operator+(const Vector2dF& lhs, const RectF& rhs) { return rhs + lhs; } -GFX_EXPORT RectF IntersectRects(const RectF& a, const RectF& b); -GFX_EXPORT RectF UnionRects(const RectF& a, const RectF& b); -GFX_EXPORT RectF SubtractRects(const RectF& a, const RectF& b); +GEOMETRY_EXPORT RectF IntersectRects(const RectF& a, const RectF& b); +GEOMETRY_EXPORT RectF UnionRects(const RectF& a, const RectF& b); +GEOMETRY_EXPORT RectF SubtractRects(const RectF& a, const RectF& b); inline RectF ScaleRect(const RectF& r, float x_scale, float y_scale) { return RectF(r.x() * x_scale, r.y() * y_scale, @@ -244,7 +244,7 @@ inline RectF ScaleRect(const RectF& r, float scale) { // points", except that we consider points on the right/bottom edges of the // rect to be outside the rect. So technically one or both points will not be // contained within the rect, because they will appear on one of these edges. -GFX_EXPORT RectF BoundingRect(const PointF& p1, const PointF& p2); +GEOMETRY_EXPORT RectF BoundingRect(const PointF& p1, const PointF& p2); // This is declared here for use in gtest-based unit tests but is defined in // the //ui/gfx:test_support target. Depend on that to use this in your unit diff --git a/chromium/ui/gfx/geometry/rect_unittest.cc b/chromium/ui/gfx/geometry/rect_unittest.cc index 7acfb806501..1f6d576b9e4 100644 --- a/chromium/ui/gfx/geometry/rect_unittest.cc +++ b/chromium/ui/gfx/geometry/rect_unittest.cc @@ -6,7 +6,7 @@ #include <stddef.h> -#include "base/macros.h" +#include "base/stl_util.h" #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/gfx/geometry/rect.h" @@ -40,7 +40,7 @@ TEST(RectTest, Contains) { {0, 0, -10, -10, 0, 0, false}, #endif }; - for (size_t i = 0; i < arraysize(contains_cases); ++i) { + for (size_t i = 0; i < base::size(contains_cases); ++i) { const ContainsCase& value = contains_cases[i]; Rect rect(value.rect_x, value.rect_y, value.rect_width, value.rect_height); EXPECT_EQ(value.contained, rect.Contains(value.point_x, value.point_y)); @@ -70,7 +70,7 @@ TEST(RectTest, Intersects) { { 10, 10, 10, 10, 20, 15, 10, 10, false }, { 10, 10, 10, 10, 21, 15, 10, 10, false } }; - for (size_t i = 0; i < arraysize(tests); ++i) { + for (size_t i = 0; i < base::size(tests); ++i) { Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1); Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2); EXPECT_EQ(tests[i].intersects, r1.Intersects(r2)); @@ -112,7 +112,7 @@ TEST(RectTest, Intersect) { 0, 0, 2, 2, 0, 0, 0, 0 } }; - for (size_t i = 0; i < arraysize(tests); ++i) { + for (size_t i = 0; i < base::size(tests); ++i) { Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1); Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2); Rect r3(tests[i].x3, tests[i].y3, tests[i].w3, tests[i].h3); @@ -161,7 +161,7 @@ TEST(RectTest, Union) { 2, 2, 2, 2, 2, 2, 2, 2 } }; - for (size_t i = 0; i < arraysize(tests); ++i) { + for (size_t i = 0; i < base::size(tests); ++i) { Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1); Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2); Rect r3(tests[i].x3, tests[i].y3, tests[i].w3, tests[i].h3); @@ -213,7 +213,7 @@ TEST(RectTest, AdjustToFit) { 0, 0, 3, 3, 2, 2, 1, 1 } }; - for (size_t i = 0; i < arraysize(tests); ++i) { + for (size_t i = 0; i < base::size(tests); ++i) { Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1); Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2); Rect r3(tests[i].x3, tests[i].y3, tests[i].w3, tests[i].h3); @@ -457,7 +457,7 @@ TEST(RectTest, ScaleRect) { std::numeric_limits<float>::max() } }; - for (size_t i = 0; i < arraysize(tests); ++i) { + for (size_t i = 0; i < base::size(tests); ++i) { RectF r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1); RectF r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2); @@ -496,9 +496,12 @@ TEST(RectTest, ToEnclosedRect) { {{max_float, max_float, 2.0f, 2.0f}, {max_int, max_int, 0, 0}}, {{0.0f, 0.0f, max_float, max_float}, {0, 0, max_int, max_int}}, {{20000.5f, 20000.5f, 0.5f, 0.5f}, {20001, 20001, 0, 0}}, - {{max_int_f, max_int_f, max_int_f, max_int_f}, {max_int, max_int, 0, 0}}}; + {{max_int_f, max_int_f, max_int_f, max_int_f}, {max_int, max_int, 0, 0}}, + {{1.9999f, 2.0002f, 5.9998f, 6.0001f}, {2, 3, 5, 5}}, + {{1.9999f, 2.0001f, 6.0002f, 5.9998f}, {2, 3, 6, 4}}, + {{1.9998f, 2.0002f, 6.0001f, 5.9999f}, {2, 3, 5, 5}}}; - for (size_t i = 0; i < arraysize(tests); ++i) { + for (size_t i = 0; i < base::size(tests); ++i) { RectF source(tests[i].in.x, tests[i].in.y, tests[i].in.width, tests[i].in.height); Rect enclosed = ToEnclosedRect(source); @@ -556,9 +559,12 @@ TEST(RectTest, ToEnclosingRect) { {{0.0f, 0.0f, max_float, max_float}, {0, 0, max_int, max_int}}, {{20000.5f, 20000.5f, 0.5f, 0.5f}, {20000, 20000, 1, 1}}, {{max_int_f, max_int_f, max_int_f, max_int_f}, {max_int, max_int, 0, 0}}, - {{-0.5f, -0.5f, 22777712.f, 1.f}, {-1, -1, 22777713, 2}}}; + {{-0.5f, -0.5f, 22777712.f, 1.f}, {-1, -1, 22777713, 2}}, + {{1.9999f, 2.0002f, 5.9998f, 6.0001f}, {1, 2, 7, 7}}, + {{1.9999f, 2.0001f, 6.0002f, 5.9998f}, {1, 2, 8, 6}}, + {{1.9998f, 2.0002f, 6.0001f, 5.9999f}, {1, 2, 7, 7}}}; - for (size_t i = 0; i < arraysize(tests); ++i) { + for (size_t i = 0; i < base::size(tests); ++i) { RectF source(tests[i].in.x, tests[i].in.y, tests[i].in.width, tests[i].in.height); @@ -585,6 +591,53 @@ TEST(RectTest, ToEnclosingRect) { } } +TEST(RectTest, ToEnclosingRectIgnoringError) { + static const int max_int = std::numeric_limits<int>::max(); + static const float max_float = std::numeric_limits<float>::max(); + static const float epsilon_float = std::numeric_limits<float>::epsilon(); + static const float max_int_f = static_cast<float>(max_int); + static const float error = 0.001f; + static const struct Test { + struct { + float x; + float y; + float width; + float height; + } in; + struct { + int x; + int y; + int width; + int height; + } expected; + } tests[] = { + {{0.0f, 0.0f, 0.0f, 0.0f}, {0, 0, 0, 0}}, + {{5.5f, 5.5f, 0.0f, 0.0f}, {5, 5, 0, 0}}, + {{3.5f, 2.5f, epsilon_float, -0.0f}, {3, 2, 0, 0}}, + {{3.5f, 2.5f, 0.f, 0.001f}, {3, 2, 0, 1}}, + {{-1.5f, -1.5f, 3.0f, 3.0f}, {-2, -2, 4, 4}}, + {{-1.5f, -1.5f, 3.5f, 3.5f}, {-2, -2, 4, 4}}, + {{max_float, max_float, 2.0f, 2.0f}, {max_int, max_int, 0, 0}}, + {{0.0f, 0.0f, max_float, max_float}, {0, 0, max_int, max_int}}, + {{20000.5f, 20000.5f, 0.5f, 0.5f}, {20000, 20000, 1, 1}}, + {{max_int_f, max_int_f, max_int_f, max_int_f}, {max_int, max_int, 0, 0}}, + {{-0.5f, -0.5f, 22777712.f, 1.f}, {-1, -1, 22777713, 2}}, + {{1.9999f, 2.0002f, 5.9998f, 6.0001f}, {2, 2, 6, 6}}, + {{1.9999f, 2.0001f, 6.0002f, 5.9998f}, {2, 2, 6, 6}}, + {{1.9998f, 2.0002f, 6.0001f, 5.9999f}, {2, 2, 6, 6}}}; + + for (size_t i = 0; i < base::size(tests); ++i) { + RectF source(tests[i].in.x, tests[i].in.y, tests[i].in.width, + tests[i].in.height); + + Rect enclosing = ToEnclosingRectIgnoringError(source, error); + EXPECT_EQ(tests[i].expected.x, enclosing.x()); + EXPECT_EQ(tests[i].expected.y, enclosing.y()); + EXPECT_EQ(tests[i].expected.width, enclosing.width()); + EXPECT_EQ(tests[i].expected.height, enclosing.height()); + } +} + TEST(RectTest, ToNearestRect) { Rect rect; EXPECT_EQ(rect, ToNearestRect(RectF(rect))); @@ -617,7 +670,7 @@ TEST(RectTest, ToFlooredRect) { 20000, 20000, 0, 0 }, }; - for (size_t i = 0; i < arraysize(tests); ++i) { + for (size_t i = 0; i < base::size(tests); ++i) { RectF r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1); Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2); @@ -666,7 +719,7 @@ TEST(RectTest, ScaleToEnclosedRect) { } }; - for (size_t i = 0; i < arraysize(tests); ++i) { + for (size_t i = 0; i < base::size(tests); ++i) { Rect result = ScaleToEnclosedRect(tests[i].input_rect, tests[i].input_scale); EXPECT_EQ(tests[i].expected_rect, result); @@ -710,7 +763,7 @@ TEST(RectTest, ScaleToEnclosingRect) { } }; - for (size_t i = 0; i < arraysize(tests); ++i) { + for (size_t i = 0; i < base::size(tests); ++i) { Rect result = ScaleToEnclosingRect(tests[i].input_rect, tests[i].input_scale); EXPECT_EQ(tests[i].expected_rect, result); @@ -760,7 +813,7 @@ TEST(RectTest, BoundingRect) { { Point(-4, 6), Point(6, -4), Rect(-4, -4, 10, 10) }, }; - for (size_t i = 0; i < arraysize(int_tests); ++i) { + for (size_t i = 0; i < base::size(int_tests); ++i) { Rect actual = BoundingRect(int_tests[i].a, int_tests[i].b); EXPECT_EQ(int_tests[i].expected, actual); } @@ -797,7 +850,7 @@ TEST(RectTest, BoundingRect) { RectF(-4.2f, -4.2f, 11.0f, 11.0f) } }; - for (size_t i = 0; i < arraysize(float_tests); ++i) { + for (size_t i = 0; i < base::size(float_tests); ++i) { RectF actual = BoundingRect(float_tests[i].a, float_tests[i].b); EXPECT_RECTF_EQ(float_tests[i].expected, actual); } diff --git a/chromium/ui/gfx/geometry/safe_integer_conversions.h b/chromium/ui/gfx/geometry/safe_integer_conversions.h index 5efe134f079..c89cec2d422 100644 --- a/chromium/ui/gfx/geometry/safe_integer_conversions.h +++ b/chromium/ui/gfx/geometry/safe_integer_conversions.h @@ -9,7 +9,6 @@ #include <limits> #include "base/numerics/safe_conversions.h" -#include "ui/gfx/gfx_export.h" namespace gfx { diff --git a/chromium/ui/gfx/geometry/scroll_offset.h b/chromium/ui/gfx/geometry/scroll_offset.h index d7aa2f97d88..1f604fa6a7f 100644 --- a/chromium/ui/gfx/geometry/scroll_offset.h +++ b/chromium/ui/gfx/geometry/scroll_offset.h @@ -8,9 +8,9 @@ #include <iosfwd> #include <string> +#include "ui/gfx/geometry/geometry_export.h" #include "ui/gfx/geometry/safe_integer_conversions.h" #include "ui/gfx/geometry/vector2d.h" -#include "ui/gfx/gfx_export.h" namespace gfx { @@ -18,7 +18,7 @@ namespace gfx { // to a 'position' in blink. In blink, 'offset' means something else. See // third_party/WebKit/Source/core/layout/README.md for more information. -class GFX_EXPORT ScrollOffset { +class GEOMETRY_EXPORT ScrollOffset { public: ScrollOffset() : x_(0), y_(0) {} ScrollOffset(float x, float y) : x_(x), y_(y) {} diff --git a/chromium/ui/gfx/geometry/size.h b/chromium/ui/gfx/geometry/size.h index 8ce4178b3cf..03490b5ab81 100644 --- a/chromium/ui/gfx/geometry/size.h +++ b/chromium/ui/gfx/geometry/size.h @@ -11,7 +11,7 @@ #include "base/compiler_specific.h" #include "base/numerics/safe_math.h" #include "build/build_config.h" -#include "ui/gfx/gfx_export.h" +#include "ui/gfx/geometry/geometry_export.h" #if defined(OS_WIN) typedef struct tagSIZE SIZE; @@ -22,7 +22,7 @@ typedef struct CGSize CGSize; namespace gfx { // A size has width and height values. -class GFX_EXPORT Size { +class GEOMETRY_EXPORT Size { public: constexpr Size() : width_(0), height_(0) {} constexpr Size(int width, int height) @@ -85,18 +85,18 @@ inline bool operator!=(const Size& lhs, const Size& rhs) { void PrintTo(const Size& size, ::std::ostream* os); // Helper methods to scale a gfx::Size to a new gfx::Size. -GFX_EXPORT Size ScaleToCeiledSize(const Size& size, - float x_scale, - float y_scale); -GFX_EXPORT Size ScaleToCeiledSize(const Size& size, float scale); -GFX_EXPORT Size ScaleToFlooredSize(const Size& size, - float x_scale, - float y_scale); -GFX_EXPORT Size ScaleToFlooredSize(const Size& size, float scale); -GFX_EXPORT Size ScaleToRoundedSize(const Size& size, - float x_scale, - float y_scale); -GFX_EXPORT Size ScaleToRoundedSize(const Size& size, float scale); +GEOMETRY_EXPORT Size ScaleToCeiledSize(const Size& size, + float x_scale, + float y_scale); +GEOMETRY_EXPORT Size ScaleToCeiledSize(const Size& size, float scale); +GEOMETRY_EXPORT Size ScaleToFlooredSize(const Size& size, + float x_scale, + float y_scale); +GEOMETRY_EXPORT Size ScaleToFlooredSize(const Size& size, float scale); +GEOMETRY_EXPORT Size ScaleToRoundedSize(const Size& size, + float x_scale, + float y_scale); +GEOMETRY_EXPORT Size ScaleToRoundedSize(const Size& size, float scale); } // namespace gfx diff --git a/chromium/ui/gfx/geometry/size_conversions.h b/chromium/ui/gfx/geometry/size_conversions.h index 96fb79f93c2..6bc74c0a612 100644 --- a/chromium/ui/gfx/geometry/size_conversions.h +++ b/chromium/ui/gfx/geometry/size_conversions.h @@ -11,13 +11,13 @@ namespace gfx { // Returns a Size with each component from the input SizeF floored. -GFX_EXPORT Size ToFlooredSize(const SizeF& size); +GEOMETRY_EXPORT Size ToFlooredSize(const SizeF& size); // Returns a Size with each component from the input SizeF ceiled. -GFX_EXPORT Size ToCeiledSize(const SizeF& size); +GEOMETRY_EXPORT Size ToCeiledSize(const SizeF& size); // Returns a Size with each component from the input SizeF rounded. -GFX_EXPORT Size ToRoundedSize(const SizeF& size); +GEOMETRY_EXPORT Size ToRoundedSize(const SizeF& size); } // namespace gfx diff --git a/chromium/ui/gfx/geometry/size_f.h b/chromium/ui/gfx/geometry/size_f.h index 0757ef6481a..ceda9edfc44 100644 --- a/chromium/ui/gfx/geometry/size_f.h +++ b/chromium/ui/gfx/geometry/size_f.h @@ -10,8 +10,8 @@ #include "base/compiler_specific.h" #include "base/gtest_prod_util.h" +#include "ui/gfx/geometry/geometry_export.h" #include "ui/gfx/geometry/size.h" -#include "ui/gfx/gfx_export.h" namespace gfx { @@ -20,7 +20,7 @@ FORWARD_DECLARE_TEST(SizeTest, ClampsToZero); FORWARD_DECLARE_TEST(SizeTest, ConsistentClamping); // A floating version of gfx::Size. -class GFX_EXPORT SizeF { +class GEOMETRY_EXPORT SizeF { public: constexpr SizeF() : width_(0.f), height_(0.f) {} constexpr SizeF(float width, float height) @@ -81,7 +81,7 @@ inline bool operator!=(const SizeF& lhs, const SizeF& rhs) { return !(lhs == rhs); } -GFX_EXPORT SizeF ScaleSize(const SizeF& p, float x_scale, float y_scale); +GEOMETRY_EXPORT SizeF ScaleSize(const SizeF& p, float x_scale, float y_scale); inline SizeF ScaleSize(const SizeF& p, float scale) { return ScaleSize(p, scale, scale); diff --git a/chromium/ui/gfx/geometry/vector2d.h b/chromium/ui/gfx/geometry/vector2d.h index d9964532f51..11d55bf0e82 100644 --- a/chromium/ui/gfx/geometry/vector2d.h +++ b/chromium/ui/gfx/geometry/vector2d.h @@ -15,12 +15,12 @@ #include <iosfwd> #include <string> +#include "ui/gfx/geometry/geometry_export.h" #include "ui/gfx/geometry/vector2d_f.h" -#include "ui/gfx/gfx_export.h" namespace gfx { -class GFX_EXPORT Vector2d { +class GEOMETRY_EXPORT Vector2d { public: constexpr Vector2d() : x_(0), y_(0) {} constexpr Vector2d(int x, int y) : x_(x), y_(y) {} diff --git a/chromium/ui/gfx/geometry/vector2d_conversions.h b/chromium/ui/gfx/geometry/vector2d_conversions.h index f4e16ae4be7..055ebd05f18 100644 --- a/chromium/ui/gfx/geometry/vector2d_conversions.h +++ b/chromium/ui/gfx/geometry/vector2d_conversions.h @@ -11,13 +11,13 @@ namespace gfx { // Returns a Vector2d with each component from the input Vector2dF floored. -GFX_EXPORT Vector2d ToFlooredVector2d(const Vector2dF& vector2d); +GEOMETRY_EXPORT Vector2d ToFlooredVector2d(const Vector2dF& vector2d); // Returns a Vector2d with each component from the input Vector2dF ceiled. -GFX_EXPORT Vector2d ToCeiledVector2d(const Vector2dF& vector2d); +GEOMETRY_EXPORT Vector2d ToCeiledVector2d(const Vector2dF& vector2d); // Returns a Vector2d with each component from the input Vector2dF rounded. -GFX_EXPORT Vector2d ToRoundedVector2d(const Vector2dF& vector2d); +GEOMETRY_EXPORT Vector2d ToRoundedVector2d(const Vector2dF& vector2d); } // namespace gfx diff --git a/chromium/ui/gfx/geometry/vector2d_f.h b/chromium/ui/gfx/geometry/vector2d_f.h index 7e40ea881fc..938e45aeacc 100644 --- a/chromium/ui/gfx/geometry/vector2d_f.h +++ b/chromium/ui/gfx/geometry/vector2d_f.h @@ -13,11 +13,11 @@ #include <iosfwd> #include <string> -#include "ui/gfx/gfx_export.h" +#include "ui/gfx/geometry/geometry_export.h" namespace gfx { -class GFX_EXPORT Vector2dF { +class GEOMETRY_EXPORT Vector2dF { public: constexpr Vector2dF() : x_(0), y_(0) {} constexpr Vector2dF(float x, float y) : x_(x), y_(y) {} @@ -92,16 +92,16 @@ inline Vector2dF operator-(const Vector2dF& lhs, const Vector2dF& rhs) { } // Return the cross product of two vectors. -GFX_EXPORT double CrossProduct(const Vector2dF& lhs, const Vector2dF& rhs); +GEOMETRY_EXPORT double CrossProduct(const Vector2dF& lhs, const Vector2dF& rhs); // Return the dot product of two vectors. -GFX_EXPORT double DotProduct(const Vector2dF& lhs, const Vector2dF& rhs); +GEOMETRY_EXPORT double DotProduct(const Vector2dF& lhs, const Vector2dF& rhs); // Return a vector that is |v| scaled by the given scale factors along each // axis. -GFX_EXPORT Vector2dF ScaleVector2d(const Vector2dF& v, - float x_scale, - float y_scale); +GEOMETRY_EXPORT Vector2dF ScaleVector2d(const Vector2dF& v, + float x_scale, + float y_scale); // Return a vector that is |v| scaled by the given scale factor. inline Vector2dF ScaleVector2d(const Vector2dF& v, float scale) { diff --git a/chromium/ui/gfx/geometry/vector3d_f.h b/chromium/ui/gfx/geometry/vector3d_f.h index 0e5e43713a8..32b72655b1e 100644 --- a/chromium/ui/gfx/geometry/vector3d_f.h +++ b/chromium/ui/gfx/geometry/vector3d_f.h @@ -13,12 +13,12 @@ #include <iosfwd> #include <string> +#include "ui/gfx/geometry/geometry_export.h" #include "ui/gfx/geometry/vector2d_f.h" -#include "ui/gfx/gfx_export.h" namespace gfx { -class GFX_EXPORT Vector3dF { +class GEOMETRY_EXPORT Vector3dF { public: constexpr Vector3dF() : x_(0), y_(0), z_(0) {} constexpr Vector3dF(float x, float y, float z) : x_(x), y_(y), z_(z) {} @@ -111,14 +111,14 @@ inline Vector3dF CrossProduct(const Vector3dF& lhs, const Vector3dF& rhs) { } // Return the dot product of two vectors. -GFX_EXPORT float DotProduct(const Vector3dF& lhs, const Vector3dF& rhs); +GEOMETRY_EXPORT float DotProduct(const Vector3dF& lhs, const Vector3dF& rhs); // Return a vector that is |v| scaled by the given scale factors along each // axis. -GFX_EXPORT Vector3dF ScaleVector3d(const Vector3dF& v, - float x_scale, - float y_scale, - float z_scale); +GEOMETRY_EXPORT Vector3dF ScaleVector3d(const Vector3dF& v, + float x_scale, + float y_scale, + float z_scale); // Return a vector that is |v| scaled by the components of |s| inline Vector3dF ScaleVector3d(const Vector3dF& v, const Vector3dF& s) { @@ -131,12 +131,12 @@ inline Vector3dF ScaleVector3d(const Vector3dF& v, float scale) { } // Returns the angle between |base| and |other| in degrees. -GFX_EXPORT float AngleBetweenVectorsInDegrees(const gfx::Vector3dF& base, - const gfx::Vector3dF& other); +GEOMETRY_EXPORT float AngleBetweenVectorsInDegrees(const gfx::Vector3dF& base, + const gfx::Vector3dF& other); // Returns the clockwise angle between |base| and |other| where |normal| is the // normal of the virtual surface to measure clockwise according to. -GFX_EXPORT float ClockwiseAngleBetweenVectorsInDegrees( +GEOMETRY_EXPORT float ClockwiseAngleBetweenVectorsInDegrees( const gfx::Vector3dF& base, const gfx::Vector3dF& other, const gfx::Vector3dF& normal); diff --git a/chromium/ui/gfx/geometry_skia_export.h b/chromium/ui/gfx/geometry_skia_export.h new file mode 100644 index 00000000000..c6819823476 --- /dev/null +++ b/chromium/ui/gfx/geometry_skia_export.h @@ -0,0 +1,29 @@ +// 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_GFX_GEOMETRY_SKIA_EXPORT_H_ +#define UI_GFX_GEOMETRY_SKIA_EXPORT_H_ + +#if defined(COMPONENT_BUILD) +#if defined(WIN32) + +#if defined(GEOMETRY_SKIA_IMPLEMENTATION) +#define GEOMETRY_SKIA_EXPORT __declspec(dllexport) +#else +#define GEOMETRY_SKIA_EXPORT __declspec(dllimport) +#endif // defined(GEOMETRY_SKIA_IMPLEMENTATION) + +#else // defined(WIN32) +#if defined(GEOMETRY_SKIA_IMPLEMENTATION) +#define GEOMETRY_SKIA_EXPORT __attribute__((visibility("default"))) +#else +#define GEOMETRY_SKIA_EXPORT +#endif +#endif + +#else // defined(COMPONENT_BUILD) +#define GEOMETRY_SKIA_EXPORT +#endif + +#endif // UI_GFX_GEOMETRY_SKIA_EXPORT_H_ diff --git a/chromium/ui/gfx/gpu_memory_buffer.cc b/chromium/ui/gfx/gpu_memory_buffer.cc index e6751d82f6d..5da026af502 100644 --- a/chromium/ui/gfx/gpu_memory_buffer.cc +++ b/chromium/ui/gfx/gpu_memory_buffer.cc @@ -8,7 +8,8 @@ namespace gfx { -GpuMemoryBufferHandle::GpuMemoryBufferHandle() : type(EMPTY_BUFFER), id(0) {} +GpuMemoryBufferHandle::GpuMemoryBufferHandle() + : type(EMPTY_BUFFER), id(0), offset(0), stride(0) {} // TODO(crbug.com/863011): Reset |type| and possibly the handles on the // moved-from object. diff --git a/chromium/ui/gfx/harfbuzz_font_skia.cc b/chromium/ui/gfx/harfbuzz_font_skia.cc index cad4334a045..fe88148b97c 100644 --- a/chromium/ui/gfx/harfbuzz_font_skia.cc +++ b/chromium/ui/gfx/harfbuzz_font_skia.cc @@ -13,6 +13,7 @@ #include "base/lazy_instance.h" #include "base/logging.h" #include "base/macros.h" +#include "base/no_destructor.h" #include "build/build_config.h" #include "third_party/skia/include/core/SkTypeface.h" #include "ui/gfx/render_text.h" @@ -285,10 +286,11 @@ hb_font_t* CreateHarfBuzzFont(sk_sp<SkTypeface> skia_face, SkScalar text_size, const FontRenderParams& params, bool subpixel_rendering_suppressed) { - // TODO(ckocagil): This shouldn't grow indefinitely. Maybe use base::MRUCache? - static std::map<SkFontID, FaceCache> face_caches; + // TODO(https://crbug.com/890298): This shouldn't grow indefinitely. + // Maybe use base::MRUCache? + static base::NoDestructor<std::map<SkFontID, FaceCache>> face_caches; - FaceCache* face_cache = &face_caches[skia_face->uniqueID()]; + FaceCache* face_cache = &(*face_caches)[skia_face->uniqueID()]; if (face_cache->first.get() == NULL) face_cache->first.Init(skia_face.get()); diff --git a/chromium/ui/gfx/icc_profile.cc b/chromium/ui/gfx/icc_profile.cc index a126363087e..3fcef25f18c 100644 --- a/chromium/ui/gfx/icc_profile.cc +++ b/chromium/ui/gfx/icc_profile.cc @@ -12,7 +12,7 @@ #include "base/lazy_instance.h" #include "base/metrics/histogram_macros.h" #include "base/synchronization/lock.h" -#include "third_party/skia/include/core/SkColorSpaceXform.h" +#include "third_party/skia/include/core/SkColorSpace.h" #include "third_party/skia/third_party/skcms/skcms.h" #include "ui/gfx/skia_color_space_util.h" @@ -163,7 +163,6 @@ ICCProfile ICCProfile::FromDataWithId(const void* data_as_void, } // Insert the profile into all caches. - ColorSpace color_space = icc_profile.GetColorSpace(); if (icc_profile.internals_->id_) g_id_to_profile_cache.Get().Put(icc_profile.internals_->id_, icc_profile); g_data_to_profile_cache.Get().Put(icc_profile.internals_->data_, icc_profile); diff --git a/chromium/ui/gfx/image/canvas_image_source.cc b/chromium/ui/gfx/image/canvas_image_source.cc index 4d68d315bde..c6ddb53c21d 100644 --- a/chromium/ui/gfx/image/canvas_image_source.cc +++ b/chromium/ui/gfx/image/canvas_image_source.cc @@ -5,9 +5,13 @@ #include "ui/gfx/image/canvas_image_source.h" #include "base/logging.h" +#include "cc/paint/display_item_list.h" +#include "cc/paint/record_paint_canvas.h" #include "ui/gfx/canvas.h" #include "ui/gfx/geometry/insets.h" +#include "ui/gfx/geometry/size_conversions.h" #include "ui/gfx/image/image_skia.h" +#include "ui/gfx/switches.h" namespace gfx { @@ -49,7 +53,33 @@ CanvasImageSource::CanvasImageSource(const Size& size, bool is_opaque) : size_(size), is_opaque_(is_opaque) {} ImageSkiaRep CanvasImageSource::GetImageForScale(float scale) { - Canvas canvas(size_, scale, is_opaque_); + if (base::FeatureList::IsEnabled(features::kUsePaintRecordForImageSkia)) { + scoped_refptr<cc::DisplayItemList> display_item_list = + base::MakeRefCounted<cc::DisplayItemList>( + cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer); + display_item_list->StartPaint(); + + SizeF size_in_pixel = ScaleSize(SizeF(size_), scale); + cc::RecordPaintCanvas record_canvas( + display_item_list.get(), + SkRect::MakeWH(SkFloatToScalar(size_in_pixel.width()), + SkFloatToScalar(size_in_pixel.height()))); + gfx::Canvas canvas(&record_canvas, scale); +#if DCHECK_IS_ON() + Rect clip_rect; + DCHECK(canvas.GetClipBounds(&clip_rect)); + DCHECK(clip_rect.Contains(gfx::Rect(ToCeiledSize(size_in_pixel)))); +#endif + canvas.Scale(scale, scale); + Draw(&canvas); + + display_item_list->EndPaintOfPairedEnd(); + display_item_list->Finalize(); + return ImageSkiaRep(display_item_list->ReleaseAsRecord(), + gfx::ScaleToCeiledSize(size_, scale), scale); + } + + gfx::Canvas canvas(size_, scale, is_opaque_); Draw(&canvas); return ImageSkiaRep(canvas.GetBitmap(), scale); } diff --git a/chromium/ui/gfx/image/image.cc b/chromium/ui/gfx/image/image.cc index 855387a4238..a81091e8975 100644 --- a/chromium/ui/gfx/image/image.cc +++ b/chromium/ui/gfx/image/image.cc @@ -123,8 +123,7 @@ class ImageRepPNG : public ImageRep { gfx::Size Size() const override { // Read the PNG data to get the image size, caching it. if (!size_cache_) { - for (std::vector<ImagePNGRep>::const_iterator it = image_reps().begin(); - it != image_reps().end(); ++it) { + for (auto it = image_reps().begin(); it != image_reps().end(); ++it) { if (it->scale == 1.0f) { size_cache_.reset(new gfx::Size(it->Size())); return *size_cache_; diff --git a/chromium/ui/gfx/image/image_family.cc b/chromium/ui/gfx/image/image_family.cc index 0adc6692336..adc1482c398 100644 --- a/chromium/ui/gfx/image/image_family.cc +++ b/chromium/ui/gfx/image/image_family.cc @@ -79,8 +79,7 @@ const gfx::Image* ImageFamily::GetBest(int width, int height) const { float ImageFamily::GetClosestAspect(float desired_aspect) const { // Find the two aspect ratios on either side of |desired_aspect|. - std::map<MapKey, gfx::Image>::const_iterator greater_or_equal = - map_.lower_bound(MapKey(desired_aspect, 0)); + auto greater_or_equal = map_.lower_bound(MapKey(desired_aspect, 0)); // Early exit optimization if there is an exact match. if (greater_or_equal != map_.end() && greater_or_equal->first.aspect() == desired_aspect) { @@ -91,8 +90,7 @@ float ImageFamily::GetClosestAspect(float desired_aspect) const { // aspect ratio >= |desired_aspect|, and |less_than| will point to the last // image with aspect ratio < |desired_aspect|. if (greater_or_equal != map_.begin()) { - std::map<MapKey, gfx::Image>::const_iterator less_than = - greater_or_equal; + auto less_than = greater_or_equal; --less_than; float thinner_aspect = less_than->first.aspect(); DCHECK_GT(thinner_aspect, 0.0f); @@ -148,8 +146,7 @@ gfx::Image ImageFamily::CreateExact(const gfx::Size& size) const { const gfx::Image* ImageFamily::GetWithExactAspect(float aspect, int width) const { // Find the two images of given aspect ratio on either side of |width|. - std::map<MapKey, gfx::Image>::const_iterator greater_or_equal = - map_.lower_bound(MapKey(aspect, width)); + auto greater_or_equal = map_.lower_bound(MapKey(aspect, width)); if (greater_or_equal != map_.end() && greater_or_equal->first.aspect() == aspect) { // We have found the smallest image of the same size or greater. @@ -157,7 +154,7 @@ const gfx::Image* ImageFamily::GetWithExactAspect(float aspect, } DCHECK(greater_or_equal != map_.begin()); - std::map<MapKey, gfx::Image>::const_iterator less_than = greater_or_equal; + auto less_than = greater_or_equal; --less_than; // This must be true because there must be at least one image with |aspect|. DCHECK_EQ(less_than->first.aspect(), aspect); diff --git a/chromium/ui/gfx/image/image_generic.cc b/chromium/ui/gfx/image/image_generic.cc index 3f424915dc9..acca9d0bd36 100644 --- a/chromium/ui/gfx/image/image_generic.cc +++ b/chromium/ui/gfx/image/image_generic.cc @@ -39,8 +39,8 @@ class PNGImageSource : public ImageSkiaSource { // 1) The ImageSkiaRep with the highest scale if all available // scales are smaller than |scale|. // 2) The ImageSkiaRep with the smallest one that is larger than |scale|. - for (ImageSkiaRepSet::const_iterator iter = image_skia_reps_.begin(); - iter != image_skia_reps_.end(); ++iter) { + for (auto iter = image_skia_reps_.begin(); iter != image_skia_reps_.end(); + ++iter) { if ((*iter).scale() == scale) return (*iter); if (!rep || rep->scale() < (*iter).scale()) @@ -112,7 +112,7 @@ scoped_refptr<base::RefCountedMemory> Get1xPNGBytesFromImageSkia( scoped_refptr<base::RefCountedBytes> png_bytes(new base::RefCountedBytes()); if (image_skia_rep.scale() != 1.0f || - !PNGCodec::EncodeBGRASkBitmap(image_skia_rep.sk_bitmap(), false, + !PNGCodec::EncodeBGRASkBitmap(image_skia_rep.GetBitmap(), false, &png_bytes->data())) { return NULL; } diff --git a/chromium/ui/gfx/image/image_mac_unittest.mm b/chromium/ui/gfx/image/image_mac_unittest.mm index f54b9bc4867..dc26e924e91 100644 --- a/chromium/ui/gfx/image/image_mac_unittest.mm +++ b/chromium/ui/gfx/image/image_mac_unittest.mm @@ -200,10 +200,10 @@ TEST_F(ImageMacTest, MultiResolutionPNGToNSImage) { // Convert to ImageSkia to check pixel contents of NSImageReps. gfx::ImageSkia image_skia = gfx::ImageSkiaFromNSImage(ns_image); EXPECT_TRUE(gt::ArePNGBytesCloseToBitmap( - bytes1x, image_skia.GetRepresentation(1.0f).sk_bitmap(), + bytes1x, image_skia.GetRepresentation(1.0f).GetBitmap(), gt::MaxColorSpaceConversionColorShift())); - EXPECT_TRUE(gt::ArePNGBytesCloseToBitmap(bytes2x, - image_skia.GetRepresentation(2.0f).sk_bitmap(), + EXPECT_TRUE(gt::ArePNGBytesCloseToBitmap( + bytes2x, image_skia.GetRepresentation(2.0f).GetBitmap(), gt::MaxColorSpaceConversionColorShift())); } diff --git a/chromium/ui/gfx/image/image_skia.cc b/chromium/ui/gfx/image/image_skia.cc index 772344f967d..245a74081d9 100644 --- a/chromium/ui/gfx/image/image_skia.cc +++ b/chromium/ui/gfx/image/image_skia.cc @@ -14,6 +14,7 @@ #include "base/command_line.h" #include "base/logging.h" #include "base/memory/ptr_util.h" +#include "base/no_destructor.h" #include "base/sequence_checker.h" #include "build/build_config.h" #include "ui/gfx/geometry/rect.h" @@ -29,8 +30,8 @@ namespace { // static gfx::ImageSkiaRep& NullImageRep() { - CR_DEFINE_STATIC_LOCAL(ImageSkiaRep, null_image_rep, ()); - return null_image_rep; + static base::NoDestructor<ImageSkiaRep> null_image_rep; + return *null_image_rep; } std::vector<float>* g_supported_scales = NULL; @@ -63,11 +64,11 @@ ImageSkiaRep ScaleImageSkiaRep(const ImageSkiaRep& rep, float target_scale) { gfx::Size scaled_size = gfx::ScaleToCeiledSize(rep.pixel_size(), target_scale / rep.scale()); - return ImageSkiaRep(skia::ImageOperations::Resize( - rep.sk_bitmap(), - skia::ImageOperations::RESIZE_LANCZOS3, - scaled_size.width(), - scaled_size.height()), target_scale); + return ImageSkiaRep( + skia::ImageOperations::Resize(rep.GetBitmap(), + skia::ImageOperations::RESIZE_LANCZOS3, + scaled_size.width(), scaled_size.height()), + target_scale); } } // namespace @@ -153,7 +154,7 @@ ImageSkiaStorage::ImageSkiaStorage(std::unique_ptr<ImageSkiaSource> source, float scale) : source_(std::move(source)), read_only_(false) { DCHECK(source_); - ImageSkia::ImageSkiaReps::iterator it = FindRepresentation(scale, true); + auto it = FindRepresentation(scale, true); if (it == image_reps_.end() || it->is_null()) source_.reset(); else @@ -186,11 +187,11 @@ void ImageSkiaStorage::AddRepresentation(const ImageSkiaRep& image) { DCHECK(!HasRepresentationAtAllScales()); if (image.scale() != 1.0f) { - for (ImageSkia::ImageSkiaReps::iterator it = image_reps_.begin(); - it < image_reps_.end(); ++it) { + ImageSkia::ImageSkiaReps::iterator it; + for (it = image_reps_.begin(); it < image_reps_.end(); ++it) { if (it->unscaled()) { DCHECK_EQ(1.0f, it->scale()); - it->SetScaled(); + *it = ImageSkiaRep(it->GetBitmap(), it->scale()); break; } } @@ -207,12 +208,11 @@ std::vector<ImageSkiaRep>::iterator ImageSkiaStorage::FindRepresentation( bool fetch_new_image) const { ImageSkiaStorage* non_const = const_cast<ImageSkiaStorage*>(this); - ImageSkia::ImageSkiaReps::iterator closest_iter = - non_const->image_reps().end(); - ImageSkia::ImageSkiaReps::iterator exact_iter = non_const->image_reps().end(); + auto closest_iter = non_const->image_reps().end(); + auto exact_iter = non_const->image_reps().end(); float smallest_diff = std::numeric_limits<float>::max(); - for (ImageSkia::ImageSkiaReps::iterator it = non_const->image_reps().begin(); - it < image_reps_.end(); ++it) { + for (auto it = non_const->image_reps().begin(); it < image_reps_.end(); + ++it) { if (it->scale() == scale) { // found exact match fetch_new_image = false; @@ -238,8 +238,7 @@ std::vector<ImageSkiaRep>::iterator ImageSkiaStorage::FindRepresentation( if (!HasRepresentationAtAllScales() && g_supported_scales) resource_scale = MapToSupportedScale(scale); if (scale != resource_scale) { - std::vector<ImageSkiaRep>::iterator iter = - FindRepresentation(resource_scale, fetch_new_image); + auto iter = FindRepresentation(resource_scale, fetch_new_image); CHECK(iter != image_reps_.end()); image = iter->unscaled() ? (*iter) : ScaleImageSkiaRep(*iter, scale); } else { @@ -348,8 +347,7 @@ std::unique_ptr<ImageSkia> ImageSkia::DeepCopy() const { CHECK(CanRead()); std::vector<gfx::ImageSkiaRep>& reps = storage_->image_reps(); - for (std::vector<gfx::ImageSkiaRep>::iterator iter = reps.begin(); - iter != reps.end(); ++iter) { + for (auto iter = reps.begin(); iter != reps.end(); ++iter) { copy->AddRepresentation(*iter); } // The copy has its own storage. Detach the copy from the current @@ -391,8 +389,7 @@ void ImageSkia::RemoveRepresentation(float scale) { CHECK(CanModify()); ImageSkiaReps& image_reps = storage_->image_reps(); - ImageSkiaReps::iterator it = - storage_->FindRepresentation(scale, false); + auto it = storage_->FindRepresentation(scale, false); if (it != image_reps.end() && it->scale() == scale) image_reps.erase(it); } @@ -410,7 +407,7 @@ bool ImageSkia::HasRepresentation(float scale) const { if (storage_->HasRepresentationAtAllScales()) return true; - ImageSkiaReps::iterator it = storage_->FindRepresentation(scale, false); + auto it = storage_->FindRepresentation(scale, false); return (it != storage_->image_reps().end() && it->scale() == scale); } @@ -420,7 +417,7 @@ const ImageSkiaRep& ImageSkia::GetRepresentation(float scale) const { CHECK(CanRead()); - ImageSkiaReps::iterator it = storage_->FindRepresentation(scale, true); + auto it = storage_->FindRepresentation(scale, true); if (it == storage_->image_reps().end()) return NullImageRep(); @@ -469,8 +466,8 @@ std::vector<ImageSkiaRep> ImageSkia::image_reps() const { // Create list of image reps to return, skipping null image reps which were // added for caching purposes only. ImageSkiaReps image_reps; - for (ImageSkiaReps::iterator it = internal_image_reps.begin(); - it != internal_image_reps.end(); ++it) { + for (auto it = internal_image_reps.begin(); it != internal_image_reps.end(); + ++it) { if (!it->is_null()) image_reps.push_back(*it); } @@ -499,7 +496,7 @@ void ImageSkia::RemoveUnsupportedRepresentationsForScale(float scale) { } void ImageSkia::Init(const ImageSkiaRep& image_rep) { - if (image_rep.sk_bitmap().drawsNothing()) { + if (image_rep.GetBitmap().drawsNothing()) { storage_ = NULL; return; } @@ -512,7 +509,7 @@ const SkBitmap& ImageSkia::GetBitmap() const { if (isNull()) { // Callers expect a ImageSkiaRep even if it is |isNull()|. // TODO(pkotwicz): Fix this. - return NullImageRep().sk_bitmap(); + return NullImageRep().GetBitmap(); } // TODO(oshima): This made a few tests flaky on Windows. @@ -521,10 +518,10 @@ const SkBitmap& ImageSkia::GetBitmap() const { CHECK(CanRead()); #endif - ImageSkiaReps::iterator it = storage_->FindRepresentation(1.0f, true); + auto it = storage_->FindRepresentation(1.0f, true); if (it != storage_->image_reps().end()) - return it->sk_bitmap(); - return NullImageRep().sk_bitmap(); + return it->GetBitmap(); + return NullImageRep().GetBitmap(); } bool ImageSkia::CanRead() const { diff --git a/chromium/ui/gfx/image/image_skia_operations.cc b/chromium/ui/gfx/image/image_skia_operations.cc index c6928208db4..51743e51fc5 100644 --- a/chromium/ui/gfx/image/image_skia_operations.cc +++ b/chromium/ui/gfx/image/image_skia_operations.cc @@ -119,7 +119,7 @@ class BlendingImageSource : public BinaryImageSource { const ImageSkiaRep& first_rep, const ImageSkiaRep& second_rep) const override { SkBitmap blended = SkBitmapOperations::CreateBlendedBitmap( - first_rep.sk_bitmap(), second_rep.sk_bitmap(), alpha_); + first_rep.GetBitmap(), second_rep.GetBitmap(), alpha_); return ImageSkiaRep(blended, first_rep.scale()); } @@ -173,7 +173,7 @@ class TransparentImageSource : public gfx::ImageSkiaSource { image_rep.pixel_height()); alpha.eraseColor(SkColorSetARGB(alpha_ * 255, 0, 0, 0)); return ImageSkiaRep( - SkBitmapOperations::CreateMaskedBitmap(image_rep.sk_bitmap(), alpha), + SkBitmapOperations::CreateMaskedBitmap(image_rep.GetBitmap(), alpha), image_rep.scale()); } @@ -196,7 +196,7 @@ class MaskedImageSource : public BinaryImageSource { const ImageSkiaRep& first_rep, const ImageSkiaRep& second_rep) const override { return ImageSkiaRep(SkBitmapOperations::CreateMaskedBitmap( - first_rep.sk_bitmap(), second_rep.sk_bitmap()), + first_rep.GetBitmap(), second_rep.GetBitmap()), first_rep.scale()); } @@ -223,11 +223,10 @@ class TiledImageSource : public gfx::ImageSkiaSource { ImageSkiaRep source_rep = source_.GetRepresentation(scale); gfx::Rect bounds = DIPToPixelBounds(gfx::Rect(src_x_, src_y_, dst_w_, dst_h_), source_rep.scale()); - return ImageSkiaRep( - SkBitmapOperations::CreateTiledBitmap( - source_rep.sk_bitmap(), - bounds.x(), bounds.y(), bounds.width(), bounds.height()), - source_rep.scale()); + return ImageSkiaRep(SkBitmapOperations::CreateTiledBitmap( + source_rep.GetBitmap(), bounds.x(), bounds.y(), + bounds.width(), bounds.height()), + source_rep.scale()); } private: @@ -253,9 +252,9 @@ class HSLImageSource : public gfx::ImageSkiaSource { // gfx::ImageSkiaSource overrides: ImageSkiaRep GetImageForScale(float scale) override { ImageSkiaRep image_rep = image_.GetRepresentation(scale); - return gfx::ImageSkiaRep( - SkBitmapOperations::CreateHSLShiftedBitmap(image_rep.sk_bitmap(), - hsl_shift_), image_rep.scale()); + return gfx::ImageSkiaRep(SkBitmapOperations::CreateHSLShiftedBitmap( + image_rep.GetBitmap(), hsl_shift_), + image_rep.scale()); } private: @@ -288,9 +287,9 @@ class ButtonImageSource: public gfx::ImageSkiaSource { mask_rep = mask_.GetRepresentation(1.0f); } return gfx::ImageSkiaRep( - SkBitmapOperations::CreateButtonBackground(color_, - image_rep.sk_bitmap(), mask_rep.sk_bitmap()), - image_rep.scale()); + SkBitmapOperations::CreateButtonBackground( + color_, image_rep.GetBitmap(), mask_rep.GetBitmap()), + image_rep.scale()); } private: @@ -319,8 +318,8 @@ class ExtractSubsetImageSource: public gfx::ImageSkiaSource { SkIRect subset_bounds_in_pixel = RectToSkIRect( DIPToPixelBounds(subset_bounds_, image_rep.scale())); SkBitmap dst; - bool success = image_rep.sk_bitmap().extractSubset(&dst, - subset_bounds_in_pixel); + bool success = + image_rep.GetBitmap().extractSubset(&dst, subset_bounds_in_pixel); DCHECK(success); return gfx::ImageSkiaRep(dst, image_rep.scale()); } @@ -354,9 +353,7 @@ class ResizeSource : public ImageSkiaSource { const Size target_pixel_size = DIPToPixelSize(target_dip_size_, scale); const SkBitmap resized = skia::ImageOperations::Resize( - image_rep.sk_bitmap(), - resize_method_, - target_pixel_size.width(), + image_rep.GetBitmap(), resize_method_, target_pixel_size.width(), target_pixel_size.height()); return ImageSkiaRep(resized, scale); } @@ -386,8 +383,7 @@ class DropShadowSource : public ImageSkiaSource { shadows_in_pixel.push_back(shadows_in_dip_[i].Scale(scale)); const SkBitmap shadow_bitmap = SkBitmapOperations::CreateDropShadow( - image_rep.sk_bitmap(), - shadows_in_pixel); + image_rep.GetBitmap(), shadows_in_pixel); return ImageSkiaRep(shadow_bitmap, image_rep.scale()); } @@ -447,7 +443,7 @@ class RotatedSource : public ImageSkiaSource { ImageSkiaRep GetImageForScale(float scale) override { const ImageSkiaRep& image_rep = source_.GetRepresentation(scale); const SkBitmap rotated_bitmap = - SkBitmapOperations::Rotate(image_rep.sk_bitmap(), rotation_); + SkBitmapOperations::Rotate(image_rep.GetBitmap(), rotation_); return ImageSkiaRep(rotated_bitmap, image_rep.scale()); } diff --git a/chromium/ui/gfx/image/image_skia_rep.h b/chromium/ui/gfx/image/image_skia_rep.h index 3942ad7e851..4f9d6b1bbc6 100644 --- a/chromium/ui/gfx/image/image_skia_rep.h +++ b/chromium/ui/gfx/image/image_skia_rep.h @@ -6,75 +6,11 @@ #define UI_GFX_IMAGE_IMAGE_SKIA_REP_H_ #include "build/build_config.h" -#include "third_party/skia/include/core/SkBitmap.h" -#include "ui/gfx/geometry/size.h" -#include "ui/gfx/gfx_export.h" -#if !defined(OS_IOS) -#include "cc/paint/paint_image.h" -#endif - -namespace gfx { - -// An ImageSkiaRep represents a bitmap and the scale factor it is intended for. -// 0.0f scale is used to indicate that this ImageSkiaRep is used for unscaled -// image (the image that only returns 1.0f scale image). -class GFX_EXPORT ImageSkiaRep { - public: - // Create null bitmap. - ImageSkiaRep(); - ~ImageSkiaRep(); - - // Note: This is for testing purpose only. - // Creates a bitmap with kARGB_8888_Config config with given |size| in DIP. - // This allocates pixels in the bitmap. It is guaranteed that the data in the - // bitmap are initialized but the actual values are undefined. - // Specifying 0 scale means the image is for unscaled image. (unscaled() - // returns truen, and scale() returns 1.0f;) - ImageSkiaRep(const gfx::Size& size, float scale); - - // Creates a bitmap with given scale. - // Adds ref to |src|. - ImageSkiaRep(const SkBitmap& src, float scale); - - // Returns true if the backing bitmap is null. - bool is_null() const { return bitmap_.isNull(); } - - // Get width and height of bitmap in DIP. - int GetWidth() const; - int GetHeight() const; - - // Get width and height of bitmap in pixels. - int pixel_width() const { return bitmap_.width(); } - int pixel_height() const { return bitmap_.height(); } - Size pixel_size() const { - return Size(pixel_width(), pixel_height()); - } - - // Retrieves the scale that the bitmap will be painted at. - float scale() const { return unscaled() ? 1.0f : scale_; } - - bool unscaled() const { return scale_ == 0.0f; } - - // Mark the image to be used as scaled image. - void SetScaled(); - - // Returns backing bitmap. - const SkBitmap& sk_bitmap() const { return bitmap_; } - -#if !defined(OS_IOS) - const cc::PaintImage& paint_image() const { return paint_image_; } -#endif - - private: -#if !defined(OS_IOS) - cc::PaintImage paint_image_; -#endif - SkBitmap bitmap_; - - float scale_; -}; - -} // namespace gfx +#if defined(OS_IOS) +#include "ui/gfx/image/image_skia_rep_ios.h" +#else +#include "ui/gfx/image/image_skia_rep_default.h" +#endif // defined(OS_IOS) #endif // UI_GFX_IMAGE_IMAGE_SKIA_REP_H_ diff --git a/chromium/ui/gfx/image/image_skia_rep_default.cc b/chromium/ui/gfx/image/image_skia_rep_default.cc new file mode 100644 index 00000000000..bee88848ac5 --- /dev/null +++ b/chromium/ui/gfx/image/image_skia_rep_default.cc @@ -0,0 +1,106 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/gfx/image/image_skia_rep_default.h" + +#include "base/logging.h" +#include "cc/paint/display_item_list.h" +#include "cc/paint/record_paint_canvas.h" +#include "cc/paint/skia_paint_canvas.h" +#include "third_party/skia/include/core/SkCanvas.h" + +namespace gfx { + +ImageSkiaRep::ImageSkiaRep() + : type_(ImageRepType::kImageTypeDrawable), scale_(0.0f) {} + +ImageSkiaRep::ImageSkiaRep(const gfx::Size& size, float scale) + : type_(ImageRepType::kImageTypeBitmap), scale_(scale) { + bitmap_.allocN32Pixels(static_cast<int>(size.width() * this->scale()), + static_cast<int>(size.height() * this->scale())); + bitmap_.eraseColor(SK_ColorRED); + bitmap_.setImmutable(); + pixel_size_.SetSize(bitmap_.width(), bitmap_.height()); + paint_image_ = cc::PaintImage::CreateFromBitmap(bitmap_); +} + +ImageSkiaRep::ImageSkiaRep(const SkBitmap& src, float scale) + : type_(ImageRepType::kImageTypeBitmap), + pixel_size_(gfx::Size(src.width(), src.height())), + bitmap_(src), + scale_(scale) { + bitmap_.setImmutable(); + paint_image_ = cc::PaintImage::CreateFromBitmap(src); +} + +ImageSkiaRep::ImageSkiaRep(sk_sp<cc::PaintRecord> paint_record, + const gfx::Size& pixel_size, + float scale) + : paint_record_(std::move(paint_record)), + type_(ImageRepType::kImageTypeDrawable), + pixel_size_(pixel_size), + scale_(scale) {} + +ImageSkiaRep::ImageSkiaRep(const ImageSkiaRep& other) + : paint_image_(other.paint_image_), + paint_record_(other.paint_record_), + type_(other.type_), + pixel_size_(other.pixel_size_), + bitmap_(other.bitmap_), + scale_(other.scale_) {} + +ImageSkiaRep::~ImageSkiaRep() {} + +int ImageSkiaRep::GetWidth() const { + return static_cast<int>(pixel_width() / scale()); +} + +int ImageSkiaRep::GetHeight() const { + return static_cast<int>(pixel_height() / scale()); +} + +sk_sp<cc::PaintRecord> ImageSkiaRep::GetPaintRecord() const { + DCHECK(type_ == ImageRepType::kImageTypeBitmap || !is_null()); + // If this image rep is of |kImageTypeDrawable| then it must have a paint + // record. + if (type_ == ImageRepType::kImageTypeDrawable || paint_record_) + return paint_record_; + + // If this ImageRep was generated using a bitmap then it may not have a + // paint record generated for it yet. We would have to generate it now. + scoped_refptr<cc::DisplayItemList> display_item_list = + base::MakeRefCounted<cc::DisplayItemList>( + cc::DisplayItemList::kToBeReleasedAsPaintOpBuffer); + + cc::RecordPaintCanvas record_canvas( + display_item_list.get(), SkRect::MakeIWH(pixel_width(), pixel_height())); + + display_item_list->StartPaint(); + record_canvas.drawImage(paint_image(), 0, 0, nullptr); + display_item_list->EndPaintOfPairedEnd(); + display_item_list->Finalize(); + + paint_record_ = display_item_list->ReleaseAsRecord(); + return paint_record_; +} + +const SkBitmap& ImageSkiaRep::GetBitmap() const { + if (type_ == ImageRepType::kImageTypeDrawable && bitmap_.isNull() && + paint_record_) { + // TODO(malaykeshav): Add a NOTREACHED() once all instances of this call + // path is removed from the code base. + + // A request for bitmap was made even though this ImageSkiaRep is sourced + // form a drawable(e.g. CanvasImageSource). This should not be happenning + // as it forces a rasterization on the UI thread. + bitmap_.allocN32Pixels(pixel_width(), pixel_height()); + bitmap_.eraseColor(SK_ColorTRANSPARENT); + SkCanvas canvas(bitmap_); + paint_record_->Playback(&canvas); + bitmap_.setImmutable(); + } + return bitmap_; +} + +} // namespace gfx diff --git a/chromium/ui/gfx/image/image_skia_rep_default.h b/chromium/ui/gfx/image/image_skia_rep_default.h new file mode 100644 index 00000000000..79705fb0435 --- /dev/null +++ b/chromium/ui/gfx/image/image_skia_rep_default.h @@ -0,0 +1,98 @@ +// 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_GFX_IMAGE_IMAGE_SKIA_REP_DEFAULT_H_ +#define UI_GFX_IMAGE_IMAGE_SKIA_REP_DEFAULT_H_ + +#include "build/build_config.h" +#include "cc/paint/paint_image.h" +#include "cc/paint/paint_op_buffer.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/gfx_export.h" + +namespace gfx { + +// An ImageSkiaRep represents an image and the scale factor it is intended for. +// 0.0f scale is used to indicate that this ImageSkiaRep is used for unscaled +// (ImageSkia does not automatically scale the image). +// TODO(malaykeshav): Support transport of PaintRecord across mojo. This would +// require adding inline serialization support for PaintRecords. +class GFX_EXPORT ImageSkiaRep { + public: + // Create null bitmap. + ImageSkiaRep(); + + // Note: This is for testing purpose only. + // Creates a bitmap with kARGB_8888_Config config with given |size| in DIP. + // This allocates pixels in the bitmap. It is guaranteed that the data in the + // bitmap are initialized but the actual values are undefined. + // Specifying 0 scale means the image is for unscaled image. (unscaled() + // returns truen, and scale() returns 1.0f;) + ImageSkiaRep(const gfx::Size& size, float scale); + + // Creates a bitmap with given scale. + // Adds ref to |src|. + ImageSkiaRep(const SkBitmap& src, float scale); + + // Creates an image rep backed by a paint record of given size and scale. This + // is used when the image representation is sourced from a drawable sunch as + // CanvasImageSource. + ImageSkiaRep(sk_sp<cc::PaintRecord> paint_record, + const gfx::Size& size, + float scale); + + ImageSkiaRep(const ImageSkiaRep& other); + ~ImageSkiaRep(); + + // Get width and height of the image in pixels. + int pixel_width() const { return pixel_size_.width(); } + int pixel_height() const { return pixel_size_.height(); } + const Size& pixel_size() const { return pixel_size_; } + + // Get width and height of the image in DIP. + int GetWidth() const; + int GetHeight() const; + + // Retrieves the scale for which this image is a representation of. + float scale() const { return unscaled() ? 1.0f : scale_; } + bool unscaled() const { return scale_ == 0.0f; } + + bool is_null() const { + return type_ == ImageRepType::kImageTypeBitmap ? bitmap_.isNull() + : !paint_record_; + } + + // Returns the backing bitmap when the image representation is sourced from a + // bitmap. If this is a |kImageTypeDrawable| then it will generate(and cache) + // a bitmap. + const SkBitmap& GetBitmap() const; + + // Returns the backing drawable as a PaintRecord. Use this when the type of + // ImageRep is |kImageTypeDrawable|. + sk_sp<cc::PaintRecord> GetPaintRecord() const; + + const cc::PaintImage& paint_image() const { return paint_image_; }; + bool has_paint_image() const { return !!paint_image_; } + + private: + enum class ImageRepType { + kImageTypeBitmap, // When the source image is rasterized. (Bitmaps, PNGs) + kImageTypeDrawable // When the source image is a drawable generated by a + // CanvasImageSource. + }; + + // TODO(malaykeshav): Remove when migration is complete and it is safe. + cc::PaintImage paint_image_; + mutable sk_sp<cc::PaintRecord> paint_record_; + ImageRepType type_; + + Size pixel_size_; + mutable SkBitmap bitmap_; + float scale_; +}; + +} // namespace gfx + +#endif // UI_GFX_IMAGE_IMAGE_SKIA_REP_DEFAULT_H_ diff --git a/chromium/ui/gfx/image/image_skia_rep.cc b/chromium/ui/gfx/image/image_skia_rep_ios.cc index 241569ee684..5e389f5c75d 100644 --- a/chromium/ui/gfx/image/image_skia_rep.cc +++ b/chromium/ui/gfx/image/image_skia_rep_ios.cc @@ -1,50 +1,47 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// 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/gfx/image/image_skia_rep.h" +#include "ui/gfx/image/image_skia_rep_ios.h" #include "base/logging.h" namespace gfx { -ImageSkiaRep::ImageSkiaRep() : scale_(0.0f) { -} - -ImageSkiaRep::~ImageSkiaRep() { -} +ImageSkiaRep::ImageSkiaRep() : scale_(0.0f) {} ImageSkiaRep::ImageSkiaRep(const gfx::Size& size, float scale) : scale_(scale) { bitmap_.allocN32Pixels(static_cast<int>(size.width() * this->scale()), static_cast<int>(size.height() * this->scale())); bitmap_.eraseColor(SK_ColorRED); bitmap_.setImmutable(); -#if !defined(OS_IOS) - paint_image_ = cc::PaintImage::CreateFromBitmap(bitmap_); -#endif + pixel_size_.SetSize(bitmap_.width(), bitmap_.height()); } ImageSkiaRep::ImageSkiaRep(const SkBitmap& src, float scale) - : bitmap_(src), + : pixel_size_(gfx::Size(src.width(), src.height())), + bitmap_(src), scale_(scale) { bitmap_.setImmutable(); -#if !defined(OS_IOS) - paint_image_ = cc::PaintImage::CreateFromBitmap(bitmap_); -#endif } +ImageSkiaRep::ImageSkiaRep(const ImageSkiaRep& other) + : pixel_size_(other.pixel_size_), + bitmap_(other.bitmap_), + scale_(other.scale_) {} + +ImageSkiaRep::~ImageSkiaRep() {} + int ImageSkiaRep::GetWidth() const { - return static_cast<int>(bitmap_.width() / scale()); + return static_cast<int>(pixel_width() / scale()); } int ImageSkiaRep::GetHeight() const { - return static_cast<int>(bitmap_.height() / scale()); + return static_cast<int>(pixel_height() / scale()); } -void ImageSkiaRep::SetScaled() { - DCHECK_EQ(0.0f, scale_); - if (scale_ == 0.0f) - scale_ = 1.0f; +const SkBitmap& ImageSkiaRep::GetBitmap() const { + return bitmap_; } } // namespace gfx diff --git a/chromium/ui/gfx/image/image_skia_rep_ios.h b/chromium/ui/gfx/image/image_skia_rep_ios.h new file mode 100644 index 00000000000..51e8f8473c0 --- /dev/null +++ b/chromium/ui/gfx/image/image_skia_rep_ios.h @@ -0,0 +1,67 @@ +// 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_GFX_IMAGE_IMAGE_SKIA_REP_IOS_H_ +#define UI_GFX_IMAGE_IMAGE_SKIA_REP_IOS_H_ + +#include "build/build_config.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/gfx_export.h" + +namespace gfx { + +// An ImageSkiaRep represents an image and the scale factor it is intended for. +// 0.0f scale is used to indicate that this ImageSkiaRep is used for unscaled +// image (ImageSkia does not automatically scale the image). +// iOS does not support cc's PaintOpBuffer and instead uses cocoa frameworks +// image formats. +class GFX_EXPORT ImageSkiaRep { + public: + // Create null bitmap. + ImageSkiaRep(); + + // Note: This is for testing purpose only. + // Creates a bitmap with kARGB_8888_Config config with given |size| in DIP. + // This allocates pixels in the bitmap. It is guaranteed that the data in the + // bitmap are initialized but the actual values are undefined. + // Specifying 0 scale means the image is for unscaled image. (unscaled() + // returns truen, and scale() returns 1.0f;) + ImageSkiaRep(const gfx::Size& size, float scale); + + // Creates a bitmap with given scale. + // Adds ref to |src|. + ImageSkiaRep(const SkBitmap& src, float scale); + ImageSkiaRep(const ImageSkiaRep& other); + + ~ImageSkiaRep(); + + // Get width and height of the image in pixels. + int pixel_width() const { return bitmap_.width(); } + int pixel_height() const { return bitmap_.height(); } + Size pixel_size() const { return gfx::Size(pixel_width(), pixel_height()); } + + // Get width and height of the image in DIP. + int GetWidth() const; + int GetHeight() const; + + // Retrieves the scale for which this image is a representation of. + float scale() const { return unscaled() ? 1.0f : scale_; } + bool unscaled() const { return scale_ == 0.0f; } + + bool is_null() const { return bitmap_.isNull(); } + + // Returns the backing bitmap when the image representation is sourced from a + // bitmap. + const SkBitmap& GetBitmap() const; + + private: + Size pixel_size_; + SkBitmap bitmap_; + float scale_; +}; + +} // namespace gfx + +#endif // UI_GFX_IMAGE_IMAGE_SKIA_REP_IOS_H_ diff --git a/chromium/ui/gfx/image/image_skia_util_ios.mm b/chromium/ui/gfx/image/image_skia_util_ios.mm index 89b25eb27ad..e7ac3b490e5 100644 --- a/chromium/ui/gfx/image/image_skia_util_ios.mm +++ b/chromium/ui/gfx/image/image_skia_util_ios.mm @@ -49,7 +49,7 @@ UIImage* UIImageFromImageSkiaRep(const gfx::ImageSkiaRep& image_skia_rep) { float scale = image_skia_rep.scale(); base::ScopedCFTypeRef<CGColorSpaceRef> color_space( CGColorSpaceCreateDeviceRGB()); - return skia::SkBitmapToUIImageWithColorSpace(image_skia_rep.sk_bitmap(), + return skia::SkBitmapToUIImageWithColorSpace(image_skia_rep.GetBitmap(), scale, color_space); } diff --git a/chromium/ui/gfx/image/image_skia_util_mac.mm b/chromium/ui/gfx/image/image_skia_util_mac.mm index b06eddf4518..35b3845e977 100644 --- a/chromium/ui/gfx/image/image_skia_util_mac.mm +++ b/chromium/ui/gfx/image/image_skia_util_mac.mm @@ -88,8 +88,7 @@ NSImage* NSImageFromImageSkia(const gfx::ImageSkia& image_skia) { std::vector<gfx::ImageSkiaRep> image_reps = image_skia.image_reps(); for (std::vector<gfx::ImageSkiaRep>::const_iterator it = image_reps.begin(); it != image_reps.end(); ++it) { - [image addRepresentation: - skia::SkBitmapToNSBitmapImageRep(it->sk_bitmap())]; + [image addRepresentation:skia::SkBitmapToNSBitmapImageRep(it->GetBitmap())]; } [image setSize:NSMakeSize(image_skia.width(), image_skia.height())]; @@ -106,9 +105,8 @@ NSImage* NSImageFromImageSkiaWithColorSpace(const gfx::ImageSkia& image_skia, std::vector<gfx::ImageSkiaRep> image_reps = image_skia.image_reps(); for (std::vector<gfx::ImageSkiaRep>::const_iterator it = image_reps.begin(); it != image_reps.end(); ++it) { - [image addRepresentation: - skia::SkBitmapToNSBitmapImageRepWithColorSpace(it->sk_bitmap(), - color_space)]; + [image addRepresentation:skia::SkBitmapToNSBitmapImageRepWithColorSpace( + it->GetBitmap(), color_space)]; } [image setSize:NSMakeSize(image_skia.width(), image_skia.height())]; diff --git a/chromium/ui/gfx/image/image_unittest.cc b/chromium/ui/gfx/image/image_unittest.cc index aaad851ed24..faa55c299a0 100644 --- a/chromium/ui/gfx/image/image_unittest.cc +++ b/chromium/ui/gfx/image/image_unittest.cc @@ -227,11 +227,11 @@ TEST_F(ImageTest, MultiResolutionPNGToImageSkia) { scales.push_back(1.0f); scales.push_back(2.0f); gfx::ImageSkia image_skia = image.AsImageSkia(); - EXPECT_TRUE(gt::ArePNGBytesCloseToBitmap(bytes1x, - image_skia.GetRepresentation(1.0f).sk_bitmap(), + EXPECT_TRUE(gt::ArePNGBytesCloseToBitmap( + bytes1x, image_skia.GetRepresentation(1.0f).GetBitmap(), gt::MaxColorSpaceConversionColorShift())); - EXPECT_TRUE(gt::ArePNGBytesCloseToBitmap(bytes2x, - image_skia.GetRepresentation(2.0f).sk_bitmap(), + EXPECT_TRUE(gt::ArePNGBytesCloseToBitmap( + bytes2x, image_skia.GetRepresentation(2.0f).GetBitmap(), gt::MaxColorSpaceConversionColorShift())); EXPECT_TRUE(gt::ImageSkiaStructureMatches(image_skia, kSize1x, kSize1x, scales)); diff --git a/chromium/ui/gfx/image/image_unittest_util.cc b/chromium/ui/gfx/image/image_unittest_util.cc index f618cee205e..95cfd57f866 100644 --- a/chromium/ui/gfx/image/image_unittest_util.cc +++ b/chromium/ui/gfx/image/image_unittest_util.cc @@ -112,7 +112,7 @@ bool AreImagesClose(const gfx::Image& img1, float scale = img1_reps[i].scale(); const gfx::ImageSkiaRep& image_rep2 = image_skia2.GetRepresentation(scale); if (image_rep2.scale() != scale || - !AreBitmapsClose(img1_reps[i].sk_bitmap(), image_rep2.sk_bitmap(), + !AreBitmapsClose(img1_reps[i].GetBitmap(), image_rep2.GetBitmap(), max_deviation)) { return false; } diff --git a/chromium/ui/gfx/image/image_util.cc b/chromium/ui/gfx/image/image_util.cc index 624620c90bc..dc0061dbde2 100644 --- a/chromium/ui/gfx/image/image_util.cc +++ b/chromium/ui/gfx/image/image_util.cc @@ -61,7 +61,7 @@ bool JPEG1xEncodedDataFromSkiaRepresentation(const Image& image, if (image_skia_rep.scale() != 1.0f) return false; - const SkBitmap& bitmap = image_skia_rep.sk_bitmap(); + const SkBitmap& bitmap = image_skia_rep.GetBitmap(); if (!bitmap.readyToDraw()) return false; @@ -74,7 +74,7 @@ void GetVisibleMargins(const ImageSkia& image, int* left, int* right) { *right = 0; if (!image.HasRepresentation(1.f)) return; - const SkBitmap& bitmap = image.GetRepresentation(1.f).sk_bitmap(); + const SkBitmap& bitmap = image.GetRepresentation(1.f).GetBitmap(); if (bitmap.drawsNothing() || bitmap.isOpaque()) return; diff --git a/chromium/ui/gfx/image/mojo/image_skia_struct_traits.h b/chromium/ui/gfx/image/mojo/image_skia_struct_traits.h index 8eabab01fd2..43529dbce87 100644 --- a/chromium/ui/gfx/image/mojo/image_skia_struct_traits.h +++ b/chromium/ui/gfx/image/mojo/image_skia_struct_traits.h @@ -20,7 +20,7 @@ namespace mojo { template <> struct StructTraits<gfx::mojom::ImageSkiaRepDataView, gfx::ImageSkiaRep> { static SkBitmap bitmap(const gfx::ImageSkiaRep& input) { - return input.sk_bitmap(); + return input.GetBitmap(); } static float scale(const gfx::ImageSkiaRep& input); diff --git a/chromium/ui/gfx/image/mojo/image_traits_unittest.cc b/chromium/ui/gfx/image/mojo/image_traits_unittest.cc index de87d867765..1977989f001 100644 --- a/chromium/ui/gfx/image/mojo/image_traits_unittest.cc +++ b/chromium/ui/gfx/image/mojo/image_traits_unittest.cc @@ -96,8 +96,8 @@ TEST_F(ImageTraitsTest, EmptyImageSkiaRep) { ImageSkiaRep output(gfx::Size(1, 1), 1.0f); ASSERT_FALSE(output.is_null()); service()->EchoImageSkiaRep(empty_rep, &output); - EXPECT_TRUE(empty_rep.sk_bitmap().drawsNothing()); - EXPECT_TRUE(test::AreBitmapsEqual(empty_rep.sk_bitmap(), output.sk_bitmap())); + EXPECT_TRUE(empty_rep.GetBitmap().drawsNothing()); + EXPECT_TRUE(test::AreBitmapsEqual(empty_rep.GetBitmap(), output.GetBitmap())); } TEST_F(ImageTraitsTest, ImageSkiaRep) { @@ -108,7 +108,7 @@ TEST_F(ImageTraitsTest, ImageSkiaRep) { EXPECT_FALSE(output.is_null()); EXPECT_EQ(image_rep.scale(), output.scale()); - EXPECT_TRUE(test::AreBitmapsEqual(image_rep.sk_bitmap(), output.sk_bitmap())); + EXPECT_TRUE(test::AreBitmapsEqual(image_rep.GetBitmap(), output.GetBitmap())); } TEST_F(ImageTraitsTest, UnscaledImageSkiaRep) { @@ -119,7 +119,7 @@ TEST_F(ImageTraitsTest, UnscaledImageSkiaRep) { EXPECT_FALSE(output.unscaled()); service()->EchoImageSkiaRep(image_rep, &output); EXPECT_TRUE(output.unscaled()); - EXPECT_TRUE(test::AreBitmapsEqual(image_rep.sk_bitmap(), output.sk_bitmap())); + EXPECT_TRUE(test::AreBitmapsEqual(image_rep.GetBitmap(), output.GetBitmap())); } TEST_F(ImageTraitsTest, NullImageSkia) { diff --git a/chromium/ui/gfx/interpolated_transform.h b/chromium/ui/gfx/interpolated_transform.h index 3e0c6456641..33c50d91560 100644 --- a/chromium/ui/gfx/interpolated_transform.h +++ b/chromium/ui/gfx/interpolated_transform.h @@ -11,6 +11,7 @@ #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/point3_f.h" #include "ui/gfx/geometry/vector3d_f.h" +#include "ui/gfx/gfx_export.h" #include "ui/gfx/transform.h" #include "ui/gfx/transform_util.h" diff --git a/chromium/ui/gfx/linux/OWNERS b/chromium/ui/gfx/linux/OWNERS index 4bd7b3f514a..2d13a903030 100644 --- a/chromium/ui/gfx/linux/OWNERS +++ b/chromium/ui/gfx/linux/OWNERS @@ -2,3 +2,4 @@ dcastagna@chromium.org dongseong.hwang@chromium.org dnicoara@chromium.org reveman@chromium.org +rjkroege@chromium.org diff --git a/chromium/ui/gfx/mac/io_surface.cc b/chromium/ui/gfx/mac/io_surface.cc index 00ad38db090..8d9f38b1f3d 100644 --- a/chromium/ui/gfx/mac/io_surface.cc +++ b/chromium/ui/gfx/mac/io_surface.cc @@ -52,11 +52,6 @@ int32_t BytesPerElement(gfx::BufferFormat format, int plane) { case gfx::BufferFormat::UYVY_422: DCHECK_EQ(plane, 0); return 2; - case gfx::BufferFormat::ATC: - case gfx::BufferFormat::ATCIA: - case gfx::BufferFormat::DXT1: - case gfx::BufferFormat::DXT5: - case gfx::BufferFormat::ETC1: case gfx::BufferFormat::BGR_565: case gfx::BufferFormat::RGBA_4444: case gfx::BufferFormat::RGBX_8888: @@ -88,11 +83,6 @@ int32_t PixelFormat(gfx::BufferFormat format) { return '2vuy'; case gfx::BufferFormat::R_16: case gfx::BufferFormat::RG_88: - case gfx::BufferFormat::ATC: - case gfx::BufferFormat::ATCIA: - case gfx::BufferFormat::DXT1: - case gfx::BufferFormat::DXT5: - case gfx::BufferFormat::ETC1: case gfx::BufferFormat::BGR_565: case gfx::BufferFormat::RGBA_4444: case gfx::BufferFormat::RGBX_8888: diff --git a/chromium/ui/gfx/mojo/BUILD.gn b/chromium/ui/gfx/mojo/BUILD.gn index 046afff7f55..fb02266e148 100644 --- a/chromium/ui/gfx/mojo/BUILD.gn +++ b/chromium/ui/gfx/mojo/BUILD.gn @@ -15,6 +15,7 @@ mojom("mojo") { "overlay_transform.mojom", "presentation_feedback.mojom", "selection_bound.mojom", + "swap_result.mojom", "transform.mojom", ] diff --git a/chromium/ui/gfx/mojo/buffer_types.mojom b/chromium/ui/gfx/mojo/buffer_types.mojom index c14e5ae8698..170f1eb60e3 100644 --- a/chromium/ui/gfx/mojo/buffer_types.mojom +++ b/chromium/ui/gfx/mojo/buffer_types.mojom @@ -6,11 +6,6 @@ module gfx.mojom; // gfx::BufferFormat enum BufferFormat { - ATC, - ATCIA, - DXT1, - DXT5, - ETC1, R_8, R_16, RG_88, diff --git a/chromium/ui/gfx/mojo/buffer_types_struct_traits.cc b/chromium/ui/gfx/mojo/buffer_types_struct_traits.cc index 534c7f9a509..bfe71c55134 100644 --- a/chromium/ui/gfx/mojo/buffer_types_struct_traits.cc +++ b/chromium/ui/gfx/mojo/buffer_types_struct_traits.cc @@ -13,6 +13,10 @@ #include "mojo/public/cpp/system/scope_to_message_pipe.h" #endif +#if !defined(OS_LINUX) +#include "base/no_destructor.h" +#endif + namespace mojo { // static @@ -74,8 +78,8 @@ StructTraits<gfx::mojom::GpuMemoryBufferHandleDataView, #if defined(OS_LINUX) return handle.native_pixmap_handle; #else - static gfx::NativePixmapHandle pixmap_handle; - return pixmap_handle; + static base::NoDestructor<gfx::NativePixmapHandle> pixmap_handle; + return *pixmap_handle; #endif } diff --git a/chromium/ui/gfx/mojo/buffer_types_struct_traits.h b/chromium/ui/gfx/mojo/buffer_types_struct_traits.h index 33069bc27af..8c8c1fc9d5e 100644 --- a/chromium/ui/gfx/mojo/buffer_types_struct_traits.h +++ b/chromium/ui/gfx/mojo/buffer_types_struct_traits.h @@ -15,16 +15,6 @@ template <> struct EnumTraits<gfx::mojom::BufferFormat, gfx::BufferFormat> { static gfx::mojom::BufferFormat ToMojom(gfx::BufferFormat format) { switch (format) { - case gfx::BufferFormat::ATC: - return gfx::mojom::BufferFormat::ATC; - case gfx::BufferFormat::ATCIA: - return gfx::mojom::BufferFormat::ATCIA; - case gfx::BufferFormat::DXT1: - return gfx::mojom::BufferFormat::DXT1; - case gfx::BufferFormat::DXT5: - return gfx::mojom::BufferFormat::DXT5; - case gfx::BufferFormat::ETC1: - return gfx::mojom::BufferFormat::ETC1; case gfx::BufferFormat::R_8: return gfx::mojom::BufferFormat::R_8; case gfx::BufferFormat::R_16: @@ -63,21 +53,6 @@ struct EnumTraits<gfx::mojom::BufferFormat, gfx::BufferFormat> { static bool FromMojom(gfx::mojom::BufferFormat input, gfx::BufferFormat* out) { switch (input) { - case gfx::mojom::BufferFormat::ATC: - *out = gfx::BufferFormat::ATC; - return true; - case gfx::mojom::BufferFormat::ATCIA: - *out = gfx::BufferFormat::ATCIA; - return true; - case gfx::mojom::BufferFormat::DXT1: - *out = gfx::BufferFormat::DXT1; - return true; - case gfx::mojom::BufferFormat::DXT5: - *out = gfx::BufferFormat::DXT5; - return true; - case gfx::mojom::BufferFormat::ETC1: - *out = gfx::BufferFormat::ETC1; - return true; case gfx::mojom::BufferFormat::R_8: *out = gfx::BufferFormat::R_8; return true; diff --git a/chromium/ui/gfx/mojo/selection_bound.typemap b/chromium/ui/gfx/mojo/selection_bound.typemap index e2b16f7a264..d6bb0940b34 100644 --- a/chromium/ui/gfx/mojo/selection_bound.typemap +++ b/chromium/ui/gfx/mojo/selection_bound.typemap @@ -6,3 +6,6 @@ mojom = "//ui/gfx/mojo/selection_bound.mojom" public_headers = [ "//ui/gfx/selection_bound.h" ] traits_headers = [ "//ui/gfx/mojo/selection_bound_struct_traits.h" ] type_mappings = [ "gfx.mojom.SelectionBound=gfx::SelectionBound" ] +deps = [ + "//ui/gfx/geometry/mojo:struct_traits", +] diff --git a/chromium/ui/gfx/mojo/selection_bound_struct_traits.h b/chromium/ui/gfx/mojo/selection_bound_struct_traits.h index f4ba831b09b..681de71703b 100644 --- a/chromium/ui/gfx/mojo/selection_bound_struct_traits.h +++ b/chromium/ui/gfx/mojo/selection_bound_struct_traits.h @@ -5,6 +5,7 @@ #ifndef UI_GFX_MOJO_SELECTION_BOUND_STRUCT_TRAITS_H_ #define UI_GFX_MOJO_SELECTION_BOUND_STRUCT_TRAITS_H_ +#include "ui/gfx/geometry/mojo/geometry_struct_traits.h" #include "ui/gfx/mojo/selection_bound.mojom-shared.h" #include "ui/gfx/selection_bound.h" diff --git a/chromium/ui/gfx/mojo/swap_result.mojom b/chromium/ui/gfx/mojo/swap_result.mojom new file mode 100644 index 00000000000..b73d4e43f59 --- /dev/null +++ b/chromium/ui/gfx/mojo/swap_result.mojom @@ -0,0 +1,16 @@ +// 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. + +module gfx.mojom; + +// SwapResult information which is used to indicate whether buffer swap +// succeeded or not. These values correspond to gfx::SwapResult values in +// ui/gfx/swap_result.h. Currently, it is used by the Ozone/Wayland to identify +// whether a buffer swap requested by the GPU process has been successful on +// the browser process side or not. +enum SwapResult { + ACK, + FAILED, + NAK_RECREATE_BUFFERS, +}; diff --git a/chromium/ui/gfx/mojo/swap_result.typemap b/chromium/ui/gfx/mojo/swap_result.typemap new file mode 100644 index 00000000000..76d24a5085c --- /dev/null +++ b/chromium/ui/gfx/mojo/swap_result.typemap @@ -0,0 +1,8 @@ +# 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. + +mojom = "//ui/gfx/mojo/swap_result.mojom" +public_headers = [ "//ui/gfx/swap_result.h" ] +traits_headers = [ "//ui/gfx/mojo/swap_result_enum_traits.h" ] +type_mappings = [ "gfx.mojom.SwapResult=gfx::SwapResult" ] diff --git a/chromium/ui/gfx/mojo/swap_result_enum_traits.h b/chromium/ui/gfx/mojo/swap_result_enum_traits.h new file mode 100644 index 00000000000..c32ca59b055 --- /dev/null +++ b/chromium/ui/gfx/mojo/swap_result_enum_traits.h @@ -0,0 +1,48 @@ +// 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_GFX_MOJO_SWAP_RESULT_ENUM_TRAITS_H_ +#define UI_GFX_MOJO_SWAP_RESULT_ENUM_TRAITS_H_ + +#include "mojo/public/cpp/bindings/enum_traits.h" +#include "ui/gfx/mojo/swap_result.mojom-shared.h" +#include "ui/gfx/swap_result.h" + +namespace mojo { + +template <> +struct EnumTraits<gfx::mojom::SwapResult, gfx::SwapResult> { + static gfx::mojom::SwapResult ToMojom(gfx::SwapResult input) { + switch (input) { + case gfx::SwapResult::SWAP_ACK: + return gfx::mojom::SwapResult::ACK; + case gfx::SwapResult::SWAP_FAILED: + return gfx::mojom::SwapResult::FAILED; + case gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS: + return gfx::mojom::SwapResult::NAK_RECREATE_BUFFERS; + } + NOTREACHED(); + return gfx::mojom::SwapResult::FAILED; + } + + static bool FromMojom(gfx::mojom::SwapResult input, gfx::SwapResult* out) { + switch (input) { + case gfx::mojom::SwapResult::ACK: + *out = gfx::SwapResult::SWAP_ACK; + return true; + case gfx::mojom::SwapResult::FAILED: + *out = gfx::SwapResult::SWAP_FAILED; + return true; + case gfx::mojom::SwapResult::NAK_RECREATE_BUFFERS: + *out = gfx::SwapResult::SWAP_NAK_RECREATE_BUFFERS; + return true; + } + NOTREACHED(); + return false; + } +}; + +} // namespace mojo + +#endif // UI_GFX_MOJO_SWAP_RESULT_ENUM_TRAITS_H_ diff --git a/chromium/ui/gfx/platform_font_win.cc b/chromium/ui/gfx/platform_font_win.cc index 187ffbdaa03..7a43e1309b2 100644 --- a/chromium/ui/gfx/platform_font_win.cc +++ b/chromium/ui/gfx/platform_font_win.cc @@ -4,19 +4,22 @@ #include "ui/gfx/platform_font_win.h" -#include <windows.h> #include <dwrite.h> #include <limits.h> #include <math.h> #include <stdint.h> #include <wchar.h> +#include <windows.h> #include <wrl/client.h> #include <algorithm> +#include <utility> +#include "base/containers/flat_map.h" #include "base/debug/alias.h" #include "base/logging.h" #include "base/macros.h" +#include "base/no_destructor.h" #include "base/strings/string16.h" #include "base/strings/string_util.h" #include "base/strings/sys_string_conversions.h" @@ -35,32 +38,6 @@ namespace { -// Returns the minimum font size, using the minimum size callback, if set. -int GetMinimumFontSize() { - int min_font_size = 0; - if (gfx::PlatformFontWin::get_minimum_font_size_callback) - min_font_size = gfx::PlatformFontWin::get_minimum_font_size_callback(); - return min_font_size; -} - -// Returns either minimum font allowed for a current locale or -// lf_height + size_delta value. -int AdjustFontSize(int lf_height, int size_delta) { - if (lf_height < 0) { - lf_height -= size_delta; - } else { - lf_height += size_delta; - } - const int min_font_size = GetMinimumFontSize(); - // Make sure lf_height is not smaller than allowed min font size for current - // locale. - if (abs(lf_height) < min_font_size) { - return lf_height < 0 ? -min_font_size : min_font_size; - } else { - return lf_height; - } -} - // Sets style properties on |font_info| based on |font_style|. void SetLogFontStyle(int font_style, LOGFONT* font_info) { font_info->lfUnderline = (font_style & gfx::Font::UNDERLINE) != 0; @@ -251,14 +228,134 @@ HRESULT GetMatchingDirectWriteFont(LOGFONT* font_info, namespace gfx { +namespace internal { + +class SystemFonts { + public: + SystemFonts() { + NONCLIENTMETRICS_XP metrics; + base::win::GetNonClientMetrics(&metrics); + + // NOTE(dfried): When rendering Chrome, we do all of our own font scaling + // based on a number of factors, but what Windows reports to us has some + // (but not all) of these factors baked in, and not in a way that is + // display-consistent. + // + // For example, if your system DPI is 192 (200%) but you connect a monitor + // with a standard DPI (100%) then even if Chrome starts on the second + // monitor, we will be told the system font is 24pt instead of 12pt. + // Conversely, if the system DPI is set to 96 (100%) but all of our monitors + // are currently at 150%, Windows will still report 12pt fonts. + // + // The same is true with Text Zoom (a new accessibility feature). If zoom is + // set to 150%, then Windows will report a font size of 18pt. But again, we + // already take Text Zoom into account when rendering, so we want to account + // for that. + // + // Our system fonts are in DIPs, so we must always take what Windows gives + // us, figure out which adjustments it's making (and undo them), make our + // own adjustments for localization (for example, we always render Hindi 25% + // larger for readability), and only then can we store (and report) the + // system fonts. + + // Factor in/out scale adjustment that fall outside what we can access here. + // This includes l10n adjustments and those we have to ask UWP or other COM + // interfaces for (since we don't have dependencies on that code from this + // module, and don't want to implicitly invoke COM for testing purposes if + // we don't have to). + gfx::PlatformFontWin::FontAdjustment font_adjustment; + if (PlatformFontWin::adjust_font_callback_) { + PlatformFontWin::adjust_font_callback_(&font_adjustment); + } + + // Factor out system DPI scale that Windows will include in reported font + // sizes. Note that these are (sadly) system-wide and do not reflect + // specific displays' DPI. + double system_scale = GetSystemScale(); + font_adjustment.font_scale /= system_scale; + + // Grab each of the fonts from the NONCLIENTMETRICS block, adjust it + // appropriately, and store it in the font table. + AddFont(gfx::PlatformFontWin::SystemFont::kCaption, font_adjustment, + &metrics.lfCaptionFont); + AddFont(gfx::PlatformFontWin::SystemFont::kSmallCaption, font_adjustment, + &metrics.lfSmCaptionFont); + AddFont(gfx::PlatformFontWin::SystemFont::kMenu, font_adjustment, + &metrics.lfMenuFont); + AddFont(gfx::PlatformFontWin::SystemFont::kMessage, font_adjustment, + &metrics.lfMessageFont); + AddFont(gfx::PlatformFontWin::SystemFont::kStatus, font_adjustment, + &metrics.lfStatusFont); + + is_initialized_ = true; + } + + const gfx::Font& GetFont(gfx::PlatformFontWin::SystemFont system_font) const { + auto it = system_fonts_.find(system_font); + DCHECK(it != system_fonts_.end()) + << "System font #" << static_cast<int>(system_font) << " not found!"; + DCHECK(it->second.GetNativeFont()) + << "Font for system font #" << static_cast<int>(system_font) + << " has invalid handle."; + return it->second; + } + + static SystemFonts* Instance() { + static base::NoDestructor<SystemFonts> instance; + return instance.get(); + } + + static bool IsInitialized() { return is_initialized_; } + + private: + void AddFont(gfx::PlatformFontWin::SystemFont system_font, + const gfx::PlatformFontWin::FontAdjustment& font_adjustment, + LOGFONT* logfont) { + // Make adjustments to the font as necessary. + PlatformFontWin::AdjustLOGFONT(font_adjustment, logfont); + + // Cap at minimum font size. + logfont->lfHeight = PlatformFontWin::AdjustFontSize(logfont->lfHeight, 0); + + // Create the Font object. + HFONT font = CreateFontIndirect(logfont); + DLOG_ASSERT(font); + system_fonts_.emplace(system_font, gfx::PlatformFontWin::HFontToFont(font)); + } + + // Returns the system DPI scale (standard DPI being 1.0). + // TODO(dfried): move dpi.[h|cc] somewhere in base/win so we can share this + // logic. However, note that the similar function in dpi.h is used many places + // it ought not to be. + static double GetSystemScale() { + constexpr double kDefaultDPI = 96.0; + base::win::ScopedGetDC screen_dc(nullptr); + return GetDeviceCaps(screen_dc, LOGPIXELSY) / kDefaultDPI; + } + + // Use a flat map for faster lookups. + base::flat_map<gfx::PlatformFontWin::SystemFont, gfx::Font> system_fonts_; + + static bool is_initialized_; + + DISALLOW_COPY_AND_ASSIGN(SystemFonts); +}; + +// static +bool SystemFonts::is_initialized_ = false; + +} // namespace internal + // static PlatformFontWin::HFontRef* PlatformFontWin::base_font_ref_; // static -PlatformFontWin::AdjustFontCallback - PlatformFontWin::adjust_font_callback = nullptr; -PlatformFontWin::GetMinimumFontSizeCallback - PlatformFontWin::get_minimum_font_size_callback = NULL; +gfx::PlatformFontWin::AdjustFontCallback + PlatformFontWin::adjust_font_callback_ = nullptr; + +// static +gfx::PlatformFontWin::GetMinimumFontSizeCallback + PlatformFontWin::get_minimum_font_size_callback_ = nullptr; IDWriteFactory* PlatformFontWin::direct_write_factory_ = nullptr; @@ -303,6 +400,57 @@ PlatformFontWin::PlatformFontWin(const std::string& font_name, InitWithFontNameAndSize(font_name, font_size); } +// static +void PlatformFontWin::SetGetMinimumFontSizeCallback( + GetMinimumFontSizeCallback callback) { + DCHECK(!internal::SystemFonts::IsInitialized()); + get_minimum_font_size_callback_ = callback; +} + +// static +void PlatformFontWin::SetAdjustFontCallback(AdjustFontCallback callback) { + DCHECK(!internal::SystemFonts::IsInitialized()); + adjust_font_callback_ = callback; +} + +// static +void PlatformFontWin::SetDirectWriteFactory(IDWriteFactory* factory) { + // We grab a reference on the DirectWrite factory. This reference is + // leaked, which is ok because skia leaks it as well. + factory->AddRef(); + direct_write_factory_ = factory; +} + +// static +bool PlatformFontWin::IsDirectWriteEnabled() { + return direct_write_factory_ != nullptr; +} + +// static +const Font& PlatformFontWin::GetSystemFont(SystemFont system_font) { + return internal::SystemFonts::Instance()->GetFont(system_font); +} + +// static +Font PlatformFontWin::AdjustExistingFont( + NativeFont existing_font, + const FontAdjustment& font_adjustment) { + LOGFONT logfont; + auto result = GetObject(existing_font, sizeof(logfont), &logfont); + DCHECK(result); + + // Make the necessary adjustments. + AdjustLOGFONT(font_adjustment, &logfont); + + // Cap at minimum font size. + logfont.lfHeight = AdjustFontSize(logfont.lfHeight, 0); + + // Create the Font object. + HFONT hfont = CreateFontIndirect(&logfont); + DCHECK(hfont); + return HFontToFont(hfont); +} + //////////////////////////////////////////////////////////////////////////////// // PlatformFontWin, PlatformFont implementation: @@ -377,49 +525,15 @@ int PlatformFontWin::GetFontSize() const { } const FontRenderParams& PlatformFontWin::GetFontRenderParams() { - CR_DEFINE_STATIC_LOCAL(const FontRenderParams, params, - (gfx::GetFontRenderParams(FontRenderParamsQuery(), NULL))); - return params; + static const base::NoDestructor<FontRenderParams> params( + gfx::GetFontRenderParams(FontRenderParamsQuery(), nullptr)); + return *params; } NativeFont PlatformFontWin::GetNativeFont() const { return font_ref_->hfont(); } -// static -void PlatformFontWin::SetDirectWriteFactory(IDWriteFactory* factory) { - // We grab a reference on the DirectWrite factory. This reference is - // leaked, which is ok because skia leaks it as well. - factory->AddRef(); - direct_write_factory_ = factory; -} - -// static -bool PlatformFontWin::IsDirectWriteEnabled() { - return direct_write_factory_ != nullptr; -} - -// static -void PlatformFontWin::GetTextMetricsForFont(HDC hdc, - HFONT font, - TEXTMETRIC* text_metrics) { - base::win::ScopedSelectObject scoped_font(hdc, font); - GetTextMetrics(hdc, text_metrics); -} - -// static -int PlatformFontWin::GetFontSize(const LOGFONT& font_info) { - if (font_info.lfHeight < 0) - return -font_info.lfHeight; - - base::win::ScopedGetDC screen_dc(NULL); - base::win::ScopedGDIObject<HFONT> font(CreateFontIndirect(&font_info)); - - TEXTMETRIC font_metrics = {0}; - PlatformFontWin::GetTextMetricsForFont(screen_dc, font.get(), &font_metrics); - return font_metrics.tmAscent; -} - //////////////////////////////////////////////////////////////////////////////// // Font, private: @@ -443,20 +557,23 @@ void PlatformFontWin::InitWithFontNameAndSize(const std::string& font_name, } // static -PlatformFontWin::HFontRef* PlatformFontWin::GetBaseFontRef() { - if (base_font_ref_ == NULL) { - NONCLIENTMETRICS_XP metrics; - base::win::GetNonClientMetrics(&metrics); +void PlatformFontWin::GetTextMetricsForFont(HDC hdc, + HFONT font, + TEXTMETRIC* text_metrics) { + base::win::ScopedSelectObject scoped_font(hdc, font); + GetTextMetrics(hdc, text_metrics); +} - if (adjust_font_callback) - adjust_font_callback(&metrics.lfMessageFont); - metrics.lfMessageFont.lfHeight = - AdjustFontSize(metrics.lfMessageFont.lfHeight, 0); - HFONT font = CreateFontIndirect(&metrics.lfMessageFont); - DLOG_ASSERT(font); - base_font_ref_ = PlatformFontWin::CreateHFontRef(font); - // base_font_ref_ is global, up the ref count so it's never deleted. - base_font_ref_->AddRef(); +// static +PlatformFontWin::HFontRef* PlatformFontWin::GetBaseFontRef() { + if (base_font_ref_ == nullptr) { + // We'll delegate to our SystemFonts instance to give us the default + // message font. + PlatformFontWin* message_font = + static_cast<PlatformFontWin*>(internal::SystemFonts::Instance() + ->GetFont(SystemFont::kMessage) + .platform_font()); + base_font_ref_ = message_font->font_ref_.get(); } return base_font_ref_; } @@ -596,6 +713,50 @@ PlatformFontWin::HFontRef* PlatformFontWin::CreateHFontRefFromSkia( ToGfxFontWeight(font_info.lfWeight), style); } +// static +int PlatformFontWin::AdjustFontSize(int lf_height, int size_delta) { + // Extract out the sign of |lf_height| - we'll add it back later. + const int lf_sign = lf_height < 0 ? -1 : 1; + lf_height = std::abs(lf_height); + + // Apply the size adjustment. + lf_height += size_delta; + + // Make sure |lf_height| is not smaller than allowed min allowed font size. + int min_font_size = 0; + if (get_minimum_font_size_callback_) { + min_font_size = get_minimum_font_size_callback_(); + DCHECK_GE(min_font_size, 0); + } + lf_height = std::max(min_font_size, lf_height); + + // Add back the sign. + return lf_sign * lf_height; +} + +// static +void PlatformFontWin::AdjustLOGFONT( + const gfx::PlatformFontWin::FontAdjustment& font_adjustment, + LOGFONT* logfont) { + DCHECK_GT(font_adjustment.font_scale, 0.0); + LONG new_height = + LONG{std::round(logfont->lfHeight * font_adjustment.font_scale)}; + if (logfont->lfHeight && !new_height) + new_height = logfont->lfHeight > 0 ? 1 : -1; + logfont->lfHeight = new_height; + if (!font_adjustment.font_family_override.empty()) { + auto result = wcscpy_s(logfont->lfFaceName, + font_adjustment.font_family_override.c_str()); + DCHECK_EQ(0, result) << "Font name " << font_adjustment.font_family_override + << " cannot be copied into LOGFONT structure."; + } +} + +// static +Font PlatformFontWin::HFontToFont(HFONT hfont) { + return Font(new PlatformFontWin(CreateHFontRef(hfont))); +} + PlatformFontWin::PlatformFontWin(HFontRef* hfont_ref) : font_ref_(hfont_ref) { } diff --git a/chromium/ui/gfx/platform_font_win.h b/chromium/ui/gfx/platform_font_win.h index 487d4677b43..96003b89fa4 100644 --- a/chromium/ui/gfx/platform_font_win.h +++ b/chromium/ui/gfx/platform_font_win.h @@ -5,6 +5,8 @@ #ifndef UI_GFX_PLATFORM_FONT_WIN_H_ #define UI_GFX_PLATFORM_FONT_WIN_H_ +#include <windows.h> + #include <string> #include "base/compiler_specific.h" @@ -14,15 +16,31 @@ #include "ui/gfx/gfx_export.h" #include "ui/gfx/platform_font.h" -#include <windows.h> - struct IDWriteFactory; struct IDWriteFont; namespace gfx { +namespace internal { +class SystemFonts; +} + class GFX_EXPORT PlatformFontWin : public PlatformFont { public: + enum class SystemFont : int { + kCaption = 0, + kSmallCaption, + kMenu, + kStatus, + kMessage + }; + + // Represents an optional override of system font and scale. + struct FontAdjustment { + base::string16 font_family_override; + double font_scale = 1.0; + }; + PlatformFontWin(); explicit PlatformFontWin(NativeFont native_font); PlatformFontWin(const std::string& font_name, int font_size); @@ -39,14 +57,15 @@ class GFX_EXPORT PlatformFontWin : public PlatformFont { // Callback that returns the minimum height that should be used for // gfx::Fonts. Optional. If not specified, the minimum font size is 0. typedef int (*GetMinimumFontSizeCallback)(); - static GetMinimumFontSizeCallback get_minimum_font_size_callback; + static void SetGetMinimumFontSizeCallback( + GetMinimumFontSizeCallback callback); - // Callback that adjusts a LOGFONT to meet suitability requirements of the - // embedding application. Optional. If not specified, no adjustments are - // performed other than clamping to a minimum font height if + // Callback that adjusts a SystemFontInfo to meet suitability requirements + // of the embedding application. Optional. If not specified, no adjustments + // are performed other than clamping to a minimum font size if // |get_minimum_font_size_callback| is specified. - typedef void (*AdjustFontCallback)(LOGFONT* lf); - static AdjustFontCallback adjust_font_callback; + typedef void (*AdjustFontCallback)(FontAdjustment* font_adjustment); + static void SetAdjustFontCallback(AdjustFontCallback callback); // Returns the font name for the system locale. Some fonts, particularly // East Asian fonts, have different names per locale. If the localized font @@ -75,18 +94,31 @@ class GFX_EXPORT PlatformFontWin : public PlatformFont { static bool IsDirectWriteEnabled(); - // Returns the GDI metrics for the font passed in. - static void GetTextMetricsForFont(HDC hdc, - HFONT font, - TEXTMETRIC* text_metrics); + // Returns the specified Windows system font, suitable for drawing on screen + // elements. + static const Font& GetSystemFont(SystemFont system_font); - // Returns the size of the font based on the font information passed in. - static int GetFontSize(const LOGFONT& font_info); + // Apply a font adjustment to an existing font. + static Font AdjustExistingFont(NativeFont existing_font, + const FontAdjustment& font_adjustment); private: + friend class internal::SystemFonts; FRIEND_TEST_ALL_PREFIXES(RenderTextHarfBuzzTest, HarfBuzz_UniscribeFallback); FRIEND_TEST_ALL_PREFIXES(PlatformFontWinTest, Metrics_SkiaVersusGDI); FRIEND_TEST_ALL_PREFIXES(PlatformFontWinTest, DirectWriteFontSubstitution); + FRIEND_TEST_ALL_PREFIXES(PlatformFontWinTest, AdjustFontSize); + FRIEND_TEST_ALL_PREFIXES(PlatformFontWinTest, + AdjustFontSize_MinimumSizeSpecified); + FRIEND_TEST_ALL_PREFIXES(PlatformFontWinTest, AdjustLOGFONT_NoAdjustment); + FRIEND_TEST_ALL_PREFIXES(PlatformFontWinTest, AdjustLOGFONT_ChangeFace); + FRIEND_TEST_ALL_PREFIXES(PlatformFontWinTest, AdjustLOGFONT_ScaleDown); + FRIEND_TEST_ALL_PREFIXES(PlatformFontWinTest, + AdjustLOGFONT_ScaleDownWithRounding); + FRIEND_TEST_ALL_PREFIXES(PlatformFontWinTest, + AdjustLOGFONT_ScaleUpWithFaceChange); + FRIEND_TEST_ALL_PREFIXES(PlatformFontWinTest, + AdjustLOGFONT_ScaleUpWithRounding); ~PlatformFontWin() override; @@ -168,6 +200,11 @@ class GFX_EXPORT PlatformFontWin : public PlatformFont { void InitWithFontNameAndSize(const std::string& font_name, int font_size); + // Returns the GDI metrics for the font passed in. + static void GetTextMetricsForFont(HDC hdc, + HFONT font, + TEXTMETRIC* text_metrics); + // Returns the base font ref. This should ONLY be invoked on the // UI thread. static HFontRef* GetBaseFontRef(); @@ -194,6 +231,22 @@ class GFX_EXPORT PlatformFontWin : public PlatformFont { HFONT gdi_font, const TEXTMETRIC& font_metrics); + // Adjust a font smaller or larger, subject to the global minimum size. + // |lf_height| is the height as reported by the LOGFONT structure, and may + // be positive or negative (but is typically negative, indicating character + // size rather than cell size). The absolute value of |lf_size| will be + // adjusted by |size_delta| and then returned with the original sign. + static int AdjustFontSize(int lf_height, int size_delta); + + // Adjust a LOGFONT structure for optional size scale and face override. + static void AdjustLOGFONT(const FontAdjustment& font_adjustment, + LOGFONT* logfont); + + // Takes control of a native font (e.g. from CreateFontIndirect()) and wraps + // it in a Font object to manage its lifespan. Note that |hfont| may not be + // valid after the call; use the returned Font object instead. + static Font HFontToFont(HFONT hfont); + // Creates a new PlatformFontWin with the specified HFontRef. Used when // constructing a Font from a HFONT we don't want to copy. explicit PlatformFontWin(HFontRef* hfont_ref); @@ -207,6 +260,12 @@ class GFX_EXPORT PlatformFontWin : public PlatformFont { // Pointer to the global IDWriteFactory interface. static IDWriteFactory* direct_write_factory_; + // Font adjustment callback. + static AdjustFontCallback adjust_font_callback_; + + // Minimum size callback. + static GetMinimumFontSizeCallback get_minimum_font_size_callback_; + DISALLOW_COPY_AND_ASSIGN(PlatformFontWin); }; diff --git a/chromium/ui/gfx/platform_font_win_unittest.cc b/chromium/ui/gfx/platform_font_win_unittest.cc index 854377974d5..53684536529 100644 --- a/chromium/ui/gfx/platform_font_win_unittest.cc +++ b/chromium/ui/gfx/platform_font_win_unittest.cc @@ -4,6 +4,10 @@ #include "ui/gfx/platform_font_win.h" +#include <memory.h> +#include <string.h> +#include <windows.h> + #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/strings/string16.h" @@ -17,6 +21,121 @@ namespace gfx { +TEST(PlatformFontWinTest, AdjustFontSize) { + PlatformFontWin::SetGetMinimumFontSizeCallback(nullptr); + EXPECT_EQ(10, PlatformFontWin::AdjustFontSize(10, 0)); + EXPECT_EQ(-10, PlatformFontWin::AdjustFontSize(-10, 0)); + EXPECT_EQ(8, PlatformFontWin::AdjustFontSize(10, -2)); + EXPECT_EQ(-8, PlatformFontWin::AdjustFontSize(-10, -2)); + EXPECT_EQ(13, PlatformFontWin::AdjustFontSize(10, 3)); + EXPECT_EQ(-13, PlatformFontWin::AdjustFontSize(-10, 3)); + EXPECT_EQ(1, PlatformFontWin::AdjustFontSize(10, -9)); + EXPECT_EQ(-1, PlatformFontWin::AdjustFontSize(-10, -9)); + EXPECT_EQ(0, PlatformFontWin::AdjustFontSize(10, -12)); + EXPECT_EQ(0, PlatformFontWin::AdjustFontSize(-10, -12)); +} + +TEST(PlatformFontWinTest, AdjustFontSize_MinimumSizeSpecified) { + PlatformFontWin::SetGetMinimumFontSizeCallback([] { return 1; }); + EXPECT_EQ(10, PlatformFontWin::AdjustFontSize(10, 0)); + EXPECT_EQ(-10, PlatformFontWin::AdjustFontSize(-10, 0)); + EXPECT_EQ(8, PlatformFontWin::AdjustFontSize(10, -2)); + EXPECT_EQ(-8, PlatformFontWin::AdjustFontSize(-10, -2)); + EXPECT_EQ(13, PlatformFontWin::AdjustFontSize(10, 3)); + EXPECT_EQ(-13, PlatformFontWin::AdjustFontSize(-10, 3)); + EXPECT_EQ(1, PlatformFontWin::AdjustFontSize(10, -9)); + EXPECT_EQ(-1, PlatformFontWin::AdjustFontSize(-10, -9)); + EXPECT_EQ(1, PlatformFontWin::AdjustFontSize(10, -12)); + EXPECT_EQ(-1, PlatformFontWin::AdjustFontSize(-10, -12)); +} + +namespace { + +LOGFONT CreateLOGFONT(const base::string16& name, LONG height) { + LOGFONT logfont{}; + logfont.lfHeight = height; + auto result = wcscpy_s(logfont.lfFaceName, name.c_str()); + DCHECK_EQ(0, result); + return logfont; +} + +const base::string16 kSegoeUI(L"Segoe UI"); +const base::string16 kArial(L"Arial"); + +} // namespace + +TEST(PlatformFontWinTest, AdjustLOGFONT_NoAdjustment) { + LOGFONT logfont = CreateLOGFONT(kSegoeUI, -12); + PlatformFontWin::FontAdjustment adjustment; + PlatformFontWin::AdjustLOGFONT(adjustment, &logfont); + EXPECT_EQ(-12, logfont.lfHeight); + EXPECT_EQ(kSegoeUI, logfont.lfFaceName); +} + +TEST(PlatformFontWinTest, AdjustLOGFONT_ChangeFace) { + LOGFONT logfont = CreateLOGFONT(kSegoeUI, -12); + PlatformFontWin::FontAdjustment adjustment{kArial, 1.0}; + PlatformFontWin::AdjustLOGFONT(adjustment, &logfont); + EXPECT_EQ(-12, logfont.lfHeight); + EXPECT_EQ(kArial, logfont.lfFaceName); +} + +TEST(PlatformFontWinTest, AdjustLOGFONT_ScaleDown) { + LOGFONT logfont = CreateLOGFONT(kSegoeUI, -12); + PlatformFontWin::FontAdjustment adjustment{L"", 0.5}; + PlatformFontWin::AdjustLOGFONT(adjustment, &logfont); + EXPECT_EQ(-6, logfont.lfHeight); + EXPECT_EQ(kSegoeUI, logfont.lfFaceName); + + logfont = CreateLOGFONT(kSegoeUI, 12); + adjustment = {L"", 0.5}; + PlatformFontWin::AdjustLOGFONT(adjustment, &logfont); + EXPECT_EQ(6, logfont.lfHeight); + EXPECT_EQ(kSegoeUI, logfont.lfFaceName); +} + +TEST(PlatformFontWinTest, AdjustLOGFONT_ScaleDownWithRounding) { + LOGFONT logfont = CreateLOGFONT(kSegoeUI, -10); + PlatformFontWin::FontAdjustment adjustment{L"", 0.85}; + PlatformFontWin::AdjustLOGFONT(adjustment, &logfont); + EXPECT_EQ(-9, logfont.lfHeight); + EXPECT_EQ(kSegoeUI, logfont.lfFaceName); + + logfont = CreateLOGFONT(kSegoeUI, 10); + adjustment = {L"", 0.85}; + PlatformFontWin::AdjustLOGFONT(adjustment, &logfont); + EXPECT_EQ(9, logfont.lfHeight); + EXPECT_EQ(kSegoeUI, logfont.lfFaceName); +} + +TEST(PlatformFontWinTest, AdjustLOGFONT_ScaleUpWithFaceChange) { + LOGFONT logfont = CreateLOGFONT(kSegoeUI, -12); + PlatformFontWin::FontAdjustment adjustment{kArial, 1.5}; + PlatformFontWin::AdjustLOGFONT(adjustment, &logfont); + EXPECT_EQ(-18, logfont.lfHeight); + EXPECT_EQ(kArial, logfont.lfFaceName); + + logfont = CreateLOGFONT(kSegoeUI, 12); + adjustment = {kArial, 1.5}; + PlatformFontWin::AdjustLOGFONT(adjustment, &logfont); + EXPECT_EQ(18, logfont.lfHeight); + EXPECT_EQ(kArial, logfont.lfFaceName); +} + +TEST(PlatformFontWinTest, AdjustLOGFONT_ScaleUpWithRounding) { + LOGFONT logfont = CreateLOGFONT(kSegoeUI, -10); + PlatformFontWin::FontAdjustment adjustment{L"", 1.111}; + PlatformFontWin::AdjustLOGFONT(adjustment, &logfont); + EXPECT_EQ(-11, logfont.lfHeight); + EXPECT_EQ(kSegoeUI, logfont.lfFaceName); + + logfont = CreateLOGFONT(kSegoeUI, 10); + adjustment = {L"", 1.11}; + PlatformFontWin::AdjustLOGFONT(adjustment, &logfont); + EXPECT_EQ(11, logfont.lfHeight); + EXPECT_EQ(kSegoeUI, logfont.lfFaceName); +} + // Test whether font metrics retrieved by DirectWrite (skia) and GDI match as // per assumptions mentioned below:- // 1. Font size is the same diff --git a/chromium/ui/gfx/render_text.cc b/chromium/ui/gfx/render_text.cc index c29ed3e92bc..deada9af341 100644 --- a/chromium/ui/gfx/render_text.cc +++ b/chromium/ui/gfx/render_text.cc @@ -36,7 +36,6 @@ #include "ui/gfx/scoped_canvas.h" #include "ui/gfx/skia_paint_util.h" #include "ui/gfx/skia_util.h" -#include "ui/gfx/switches.h" #include "ui/gfx/text_elider.h" #include "ui/gfx/text_utils.h" #include "ui/gfx/utf16_indexing.h" @@ -241,11 +240,9 @@ void SkiaTextRenderer::DrawPosText(const SkPoint* pos, static_assert(sizeof(*pos) == 2 * sizeof(*run_buffer.pos), ""); memcpy(run_buffer.pos, pos, glyph_count * sizeof(*pos)); - // TODO(vmpstr): In order to OOP raster this, we would have to plumb PaintFont - // here instead of |flags_|. canvas_skia_->drawTextBlob( base::MakeRefCounted<cc::PaintTextBlob>(builder.make(), - std::vector<cc::PaintTypeface>{}), + std::vector<sk_sp<SkTypeface>>{}), 0, 0, flags_); } @@ -356,14 +353,6 @@ std::unique_ptr<RenderText> RenderText::CreateFor(Typesetter typesetter) { if (typesetter == Typesetter::NATIVE) return std::make_unique<RenderTextMac>(); - if (typesetter == Typesetter::HARFBUZZ) - return CreateHarfBuzzInstance(); - - static const bool use_native = - !base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableHarfBuzzRenderText); - if (use_native) - return std::make_unique<RenderTextMac>(); #endif // defined(OS_MACOSX) return CreateHarfBuzzInstance(); } diff --git a/chromium/ui/gfx/render_text_harfbuzz.cc b/chromium/ui/gfx/render_text_harfbuzz.cc index d08db4ca9da..c8a9af201d3 100644 --- a/chromium/ui/gfx/render_text_harfbuzz.cc +++ b/chromium/ui/gfx/render_text_harfbuzz.cc @@ -1595,10 +1595,9 @@ void RenderTextHarfBuzz::DrawVisualText(internal::SkiaTextRenderer* renderer) { SkIntToScalar(origin.x()) + offset_x, SkIntToScalar(origin.y() + run.font_params.baseline_offset)); } - for (BreakList<SkColor>::const_iterator it = - colors().GetBreak(segment.char_range.start()); + for (auto it = colors().GetBreak(segment.char_range.start()); it != colors().breaks().end() && - it->first < segment.char_range.end(); + it->first < segment.char_range.end(); ++it) { const Range intersection = colors().GetRange(it).Intersect(segment.char_range); diff --git a/chromium/ui/gfx/sequential_id_generator.cc b/chromium/ui/gfx/sequential_id_generator.cc index 3d8b246e233..ad5ab8c3dd4 100644 --- a/chromium/ui/gfx/sequential_id_generator.cc +++ b/chromium/ui/gfx/sequential_id_generator.cc @@ -11,7 +11,7 @@ namespace { // Removes |key| from |first|, and |first[key]| from |second|. template <typename T> void Remove(uint32_t key, T* first, T* second) { - typename T::iterator iter = first->find(key); + auto iter = first->find(key); if (iter == first->end()) return; @@ -34,7 +34,7 @@ SequentialIDGenerator::~SequentialIDGenerator() { } uint32_t SequentialIDGenerator::GetGeneratedID(uint32_t number) { - IDMap::iterator find = number_to_id_.find(number); + auto find = number_to_id_.find(number); if (find != number_to_id_.end()) return find->second; diff --git a/chromium/ui/gfx/skia_paint_util.cc b/chromium/ui/gfx/skia_paint_util.cc index 3ca09ee21c8..40c36a49e2d 100644 --- a/chromium/ui/gfx/skia_paint_util.cc +++ b/chromium/ui/gfx/skia_paint_util.cc @@ -10,6 +10,7 @@ #include "third_party/skia/include/effects/SkGradientShader.h" #include "third_party/skia/include/effects/SkLayerDrawLooper.h" #include "ui/gfx/image/image_skia_rep.h" +#include "ui/gfx/switches.h" namespace gfx { @@ -39,8 +40,22 @@ sk_sp<cc::PaintShader> CreateImageRepShaderForScale( shader_scale.setScaleX(local_matrix.getScaleX() / scale); shader_scale.setScaleY(local_matrix.getScaleY() / scale); - return cc::PaintShader::MakeImage(image_rep.paint_image(), tile_mode_x, - tile_mode_y, &shader_scale); + // TODO(malaykeshav): The check for has_paint_image was only added here to + // prevent generating a paint record in tests. Tests need an instance of + // base::DiscardableMemoryAllocator to generate the PaintRecord. However most + // test suites dont have this set. Ensure that the check is removed before + // enabling the |kUsePaintRecordForImageSkia| feature by default. + // https://crbug.com/891469 + if (base::FeatureList::IsEnabled(features::kUsePaintRecordForImageSkia) && + !image_rep.has_paint_image()) { + return cc::PaintShader::MakePaintRecord( + image_rep.GetPaintRecord(), + SkRect::MakeIWH(image_rep.pixel_width(), image_rep.pixel_height()), + tile_mode_x, tile_mode_y, &shader_scale); + } else { + return cc::PaintShader::MakeImage(image_rep.paint_image(), tile_mode_x, + tile_mode_y, &shader_scale); + } } sk_sp<cc::PaintShader> CreateGradientShader(int start_point, diff --git a/chromium/ui/gfx/skia_util.h b/chromium/ui/gfx/skia_util.h index de6ed6d2085..beea3bd99b1 100644 --- a/chromium/ui/gfx/skia_util.h +++ b/chromium/ui/gfx/skia_util.h @@ -13,7 +13,7 @@ #include "third_party/skia/include/core/SkRect.h" #include "ui/gfx/geometry/quad_f.h" #include "ui/gfx/geometry/size.h" -#include "ui/gfx/gfx_export.h" +#include "ui/gfx/geometry_skia_export.h" #if BUILDFLAG(ENABLE_VULKAN) #include "third_party/skia/include/core/SkImageInfo.h" @@ -32,45 +32,47 @@ class RectF; class Transform; // Convert between Skia and gfx types. -GFX_EXPORT SkPoint PointToSkPoint(const Point& point); -GFX_EXPORT SkIPoint PointToSkIPoint(const Point& point); -GFX_EXPORT SkPoint PointFToSkPoint(const PointF& point); -GFX_EXPORT SkRect RectToSkRect(const Rect& rect); -GFX_EXPORT SkIRect RectToSkIRect(const Rect& rect); -GFX_EXPORT Rect SkIRectToRect(const SkIRect& rect); -GFX_EXPORT SkRect RectFToSkRect(const RectF& rect); -GFX_EXPORT RectF SkRectToRectF(const SkRect& rect); -GFX_EXPORT SkSize SizeFToSkSize(const SizeF& size); -GFX_EXPORT SkISize SizeToSkISize(const Size& size); -GFX_EXPORT SizeF SkSizeToSizeF(const SkSize& size); -GFX_EXPORT Size SkISizeToSize(const SkISize& size); - -GFX_EXPORT void QuadFToSkPoints(const gfx::QuadF& quad, SkPoint points[4]); - -GFX_EXPORT void TransformToFlattenedSkMatrix(const gfx::Transform& transform, - SkMatrix* flattened); +GEOMETRY_SKIA_EXPORT SkPoint PointToSkPoint(const Point& point); +GEOMETRY_SKIA_EXPORT SkIPoint PointToSkIPoint(const Point& point); +GEOMETRY_SKIA_EXPORT SkPoint PointFToSkPoint(const PointF& point); +GEOMETRY_SKIA_EXPORT SkRect RectToSkRect(const Rect& rect); +GEOMETRY_SKIA_EXPORT SkIRect RectToSkIRect(const Rect& rect); +GEOMETRY_SKIA_EXPORT Rect SkIRectToRect(const SkIRect& rect); +GEOMETRY_SKIA_EXPORT SkRect RectFToSkRect(const RectF& rect); +GEOMETRY_SKIA_EXPORT RectF SkRectToRectF(const SkRect& rect); +GEOMETRY_SKIA_EXPORT SkSize SizeFToSkSize(const SizeF& size); +GEOMETRY_SKIA_EXPORT SkISize SizeToSkISize(const Size& size); +GEOMETRY_SKIA_EXPORT SizeF SkSizeToSizeF(const SkSize& size); +GEOMETRY_SKIA_EXPORT Size SkISizeToSize(const SkISize& size); + +GEOMETRY_SKIA_EXPORT void QuadFToSkPoints(const gfx::QuadF& quad, + SkPoint points[4]); + +GEOMETRY_SKIA_EXPORT void TransformToFlattenedSkMatrix( + const gfx::Transform& transform, + SkMatrix* flattened); // Returns true if the two bitmaps contain the same pixels. -GFX_EXPORT bool BitmapsAreEqual(const SkBitmap& bitmap1, - const SkBitmap& bitmap2); +GEOMETRY_SKIA_EXPORT bool BitmapsAreEqual(const SkBitmap& bitmap1, + const SkBitmap& bitmap2); // Converts Skia ARGB format pixels in |skia| to RGBA. -GFX_EXPORT void ConvertSkiaToRGBA(const unsigned char* skia, - int pixel_width, - unsigned char* rgba); +GEOMETRY_SKIA_EXPORT void ConvertSkiaToRGBA(const unsigned char* skia, + int pixel_width, + unsigned char* rgba); // Converts a Skia floating-point value to an int appropriate for hb_position_t. -GFX_EXPORT int SkiaScalarToHarfBuzzUnits(SkScalar value); +GEOMETRY_SKIA_EXPORT int SkiaScalarToHarfBuzzUnits(SkScalar value); // Converts an hb_position_t to a Skia floating-point value. -GFX_EXPORT SkScalar HarfBuzzUnitsToSkiaScalar(int value); +GEOMETRY_SKIA_EXPORT SkScalar HarfBuzzUnitsToSkiaScalar(int value); // Converts an hb_position_t to a float. -GFX_EXPORT float HarfBuzzUnitsToFloat(int value); +GEOMETRY_SKIA_EXPORT float HarfBuzzUnitsToFloat(int value); #if BUILDFLAG(ENABLE_VULKAN) // Converts a Skia color type to a compitable VkFormat -GFX_EXPORT VkFormat SkColorTypeToVkFormat(SkColorType color_type); +GEOMETRY_SKIA_EXPORT VkFormat SkColorTypeToVkFormat(SkColorType color_type); #endif } // namespace gfx diff --git a/chromium/ui/gfx/skia_vector_animation.cc b/chromium/ui/gfx/skia_vector_animation.cc new file mode 100644 index 00000000000..8e16dc9dcc6 --- /dev/null +++ b/chromium/ui/gfx/skia_vector_animation.cc @@ -0,0 +1,261 @@ +// 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/gfx/skia_vector_animation.h" + +#include "base/trace_event/trace_event.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkImage.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/geometry/size_conversions.h" +#include "ui/gfx/image/image_skia.h" +#include "ui/gfx/skia_util.h" +#include "ui/gfx/skia_vector_animation_observer.h" +#include "ui/gfx/skottie_wrapper.h" + +namespace gfx { + +SkiaVectorAnimation::TimerControl::TimerControl( + const base::TimeDelta& offset, + const base::TimeDelta& cycle_duration, + const base::TimeDelta& total_duration, + const base::TimeTicks& start_timestamp, + bool should_reverse) + : start_offset_(offset), + end_offset_((offset + cycle_duration)), + cycle_duration_(end_offset_ - start_offset_), + progress_per_millisecond_(1.0 / total_duration.InMillisecondsF()), + previous_tick_(start_timestamp), + progress_(base::TimeDelta::FromMilliseconds(0)), + current_cycle_progress_(start_offset_), + should_reverse_(should_reverse) {} + +void SkiaVectorAnimation::TimerControl::Step(const base::TimeTicks& timestamp) { + progress_ += timestamp - previous_tick_; + previous_tick_ = timestamp; + + base::TimeDelta completed_cycles_duration = + completed_cycles_ * cycle_duration_; + if (progress_ >= completed_cycles_duration + cycle_duration_) { + completed_cycles_++; + completed_cycles_duration += cycle_duration_; + } + + current_cycle_progress_ = + start_offset_ + progress_ - completed_cycles_duration; + if (should_reverse_ && completed_cycles_ % 2) { + current_cycle_progress_ = + end_offset_ - (current_cycle_progress_ - start_offset_); + } +} + +void SkiaVectorAnimation::TimerControl::Resume( + const base::TimeTicks& timestamp) { + previous_tick_ = timestamp; +} + +double SkiaVectorAnimation::TimerControl::GetNormalizedCurrentCycleProgress() + const { + return current_cycle_progress_.InMillisecondsF() * progress_per_millisecond_; +} + +double SkiaVectorAnimation::TimerControl::GetNormalizedStartOffset() const { + return start_offset_.InMillisecondsF() * progress_per_millisecond_; +} + +double SkiaVectorAnimation::TimerControl::GetNormalizedEndOffset() const { + return end_offset_.InMillisecondsF() * progress_per_millisecond_; +} + +SkiaVectorAnimation::SkiaVectorAnimation(scoped_refptr<SkottieWrapper> skottie) + : skottie_(skottie) {} + +SkiaVectorAnimation::~SkiaVectorAnimation() {} + +void SkiaVectorAnimation::SetAnimationObserver( + SkiaVectorAnimationObserver* observer) { + DCHECK(!observer_ || !observer); + observer_ = observer; +} + +base::TimeDelta SkiaVectorAnimation::GetAnimationDuration() const { + return base::TimeDelta::FromMilliseconds( + std::floor(SkScalarToFloat(skottie_->duration()) * 1000.f)); +} + +gfx::Size SkiaVectorAnimation::GetOriginalSize() const { +#if DCHECK_IS_ON() + // The size should have no fractional component. + gfx::SizeF float_size = gfx::SkSizeToSizeF(skottie_->size()); + gfx::Size rounded_size = gfx::ToRoundedSize(float_size); + + float height_diff = std::abs(float_size.height() - rounded_size.height()); + float width_diff = std::abs(float_size.width() - rounded_size.width()); + + DCHECK_LE(height_diff, std::numeric_limits<float>::epsilon()); + DCHECK_LE(width_diff, std::numeric_limits<float>::epsilon()); +#endif + return gfx::ToRoundedSize(gfx::SkSizeToSizeF(skottie_->size())); +} + +void SkiaVectorAnimation::Start(Style style) { + DCHECK_NE(state_, PlayState::kPaused); + DCHECK_NE(state_, PlayState::kPlaying); + StartSubsection(base::TimeDelta(), GetAnimationDuration(), style); +} + +void SkiaVectorAnimation::StartSubsection(base::TimeDelta start_offset, + base::TimeDelta duration, + Style style) { + DCHECK(state_ == PlayState::kStopped || state_ == PlayState::kEnded); + DCHECK_LE(start_offset + duration, GetAnimationDuration()); + + style_ = style; + + // Reset the |timer_control_| object for a new animation play. + timer_control_.reset(nullptr); + + // Schedule a play for the animation and store the necessary information + // needed to start playing. + state_ = PlayState::kSchedulePlay; + scheduled_start_offset_ = start_offset; + scheduled_duration_ = duration; +} + +void SkiaVectorAnimation::Pause() { + DCHECK(state_ == PlayState::kPlaying || state_ == PlayState::kSchedulePlay); + state_ = PlayState::kPaused; +} + +void SkiaVectorAnimation::ResumePlaying() { + DCHECK(state_ == PlayState::kPaused); + state_ = PlayState::kScheduleResume; +} + +void SkiaVectorAnimation::Stop() { + state_ = PlayState::kStopped; + timer_control_.reset(nullptr); +} + +float SkiaVectorAnimation::GetCurrentProgress() const { + switch (state_) { + case PlayState::kStopped: + return 0; + case PlayState::kEnded: + DCHECK(timer_control_); + return timer_control_->GetNormalizedEndOffset(); + case PlayState::kPaused: + if (timer_control_) { + return timer_control_->GetNormalizedCurrentCycleProgress(); + } else { + // It may be that the timer hasn't been initialized which may happen if + // the animation was paused while it was in |kScheculePlay| state. + return scheduled_start_offset_.InMillisecondsF() / skottie_->duration(); + } + case PlayState::kSchedulePlay: + case PlayState::kPlaying: + case PlayState::kScheduleResume: + // The timer control needs to be initialized before making this call. It + // may not have been initialized if OnAnimationStep has not been called + // yet + DCHECK(timer_control_); + return timer_control_->GetNormalizedCurrentCycleProgress(); + } +} + +void SkiaVectorAnimation::Paint(gfx::Canvas* canvas, + const base::TimeTicks& timestamp, + const gfx::Size& size) { + switch (state_) { + case PlayState::kStopped: + return; + case PlayState::kSchedulePlay: + InitTimer(timestamp); + state_ = PlayState::kPlaying; + if (observer_) + observer_->AnimationWillStartPlaying(this); + break; + case PlayState::kPlaying: + UpdateState(timestamp); + break; + case PlayState::kPaused: + break; + case PlayState::kScheduleResume: + state_ = PlayState::kPlaying; + if (timer_control_) { + timer_control_->Resume(timestamp); + } else { + // The animation may have been paused after a play was scheduled but + // before it started playing. + InitTimer(timestamp); + } + if (observer_) + observer_->AnimationResuming(this); + break; + case PlayState::kEnded: + break; + } + PaintFrame(canvas, GetCurrentProgress(), size); +} + +void SkiaVectorAnimation::PaintFrame(gfx::Canvas* canvas, + float t, + const gfx::Size& size) { + TRACE_EVENT0("ui", "SkiaVectorAnimation Paint"); + DCHECK_GE(t, 0.f); + DCHECK_LE(t, 1.f); + + float scale = canvas->UndoDeviceScaleFactor(); + gfx::Size pixel_size = gfx::ScaleToRoundedSize(size, scale); + + SkBitmap bitmap; + bitmap.allocN32Pixels(std::round(pixel_size.width()), + std::round(pixel_size.height()), false); + SkCanvas skcanvas(bitmap); + skcanvas.clear(SK_ColorTRANSPARENT); + + skottie_->Draw(&skcanvas, t, pixel_size); + + canvas->DrawImageInt(gfx::ImageSkia::CreateFrom1xBitmap(bitmap), 0, 0); +} + +void SkiaVectorAnimation::InitTimer(const base::TimeTicks& timestamp) { + DCHECK(!timer_control_); + timer_control_ = std::make_unique<TimerControl>( + scheduled_start_offset_, scheduled_duration_, GetAnimationDuration(), + timestamp, style_ == Style::kThrobbing); +} + +void SkiaVectorAnimation::UpdateState(const base::TimeTicks& timestamp) { + DCHECK(timer_control_); + int cycles = timer_control_->completed_cycles(); + timer_control_->Step(timestamp); + + if (cycles == timer_control_->completed_cycles()) + return; + + bool inform_observer = true; + switch (style_) { + case Style::kLoop: + break; + case Style::kThrobbing: + // For a throbbing animation, the animation cycle ends when the timer + // goes from 0 to 1 and then back to 0. So the number of timer cycles + // must be even at the end of one throbbing animation cycle. + if (timer_control_->completed_cycles() % 2 != 0) + inform_observer = false; + break; + case Style::kLinear: + state_ = PlayState::kEnded; + break; + } + + // Inform observer if the cycle has ended. + if (observer_ && inform_observer) { + observer_->AnimationCycleEnded(this); + } +} + +} // namespace gfx diff --git a/chromium/ui/gfx/skia_vector_animation.h b/chromium/ui/gfx/skia_vector_animation.h new file mode 100644 index 00000000000..14081bfd3e0 --- /dev/null +++ b/chromium/ui/gfx/skia_vector_animation.h @@ -0,0 +1,241 @@ +// 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_GFX_SKIA_VECTOR_ANIMATION_H_ +#define UI_GFX_SKIA_VECTOR_ANIMATION_H_ + +#include <memory> + +#include "base/macros.h" +#include "base/memory/ref_counted_memory.h" +#include "base/memory/scoped_refptr.h" +#include "base/time/time.h" +#include "third_party/skia/include/core/SkStream.h" +#include "third_party/skia/modules/skottie/include/Skottie.h" +#include "ui/gfx/geometry/size.h" +#include "ui/gfx/gfx_export.h" + +namespace gfx { +class Canvas; +class SkiaVectorAnimationTest; +class SkiaVectorAnimationObserver; +class SkottieWrapper; + +// This class is a wrapper over the Skia object for lottie vector graphic +// animations. It has its own timeline manager for the animation controls. The +// framerate of the animation and the animation ticks are controlled externally +// and hence the consumer must manage the timer and call paint at the desired +// frame per second. +// This helps keep multiple animations be synchronized by having a common +// external tick clock. +// In general you want to use the view framework integrated class that we have +// for SkiaVectorAnimation instead of this class. +// +// Usage example: +// 1. Rendering a single frame on the canvas: +// SkiaVectorAnimation animation_ = SkiaVectorAnimation(data); +// animation_.Paint(canvas, t); +// +// 2. Playing the animation and rendering each frame: +// void SampleClient::Init() { +// SkiaVectorAnimation animation_ = SkiaVectorAnimation(data); +// animation_.Start(SkiaVectorAnimation::Style::LINEAR); +// } +// +// // overrides cc::CompositorAnimationObserver +// void SampleClient::OnAnimationStep(TimeTicks* timestamp) { +// timestamp_ = timestamp; +// SchedulePaint(); +// } +// +// void SampleClient::OnPaint(Canvas* canvas) { +// animation_.Paint(canvas, timestamp_); +// } +// +// 2. If you only want to play a subsection of the animation: +// void SampleClient::Init() { +// // This will seek to the 1st second of the animation and from there +// // play it for 5 seconds. +// SkiaVectorAnimation animation_ = SkiaVectorAnimation(data); +// animation_.Start(TimeDelta::FromSeconds(1), +// TimeDelta::FromSeconds(5)); +// } +// +// // overrides cc::CompositorAnimationObserver +// void SampleClient::OnAnimationStep(TimeTicks*) { +// timestamp_ = timestamp; +// SchedulePaint(); +// } +// +// void SampleClient::OnPaint(Canvas* canvas) { +// animation_.Paint(canvas, timestamp_, gfx::Size(10, 10)); +// } +// +class GFX_EXPORT SkiaVectorAnimation { + public: + enum class Style { + kLinear = 0, // The animation plays from one time instant to another. + kThrobbing, // The animation plays from one time instant to another and + // then back. The animation plays in loop until stopped. + kLoop // Same as LINEAR, except the animation repeats after it ends. + }; + + explicit SkiaVectorAnimation(scoped_refptr<SkottieWrapper> skottie); + ~SkiaVectorAnimation(); + + void SetAnimationObserver(SkiaVectorAnimationObserver* Observer); + + // Animation properties ------------------------------------------------------ + // Returns the total duration of the animation as reported by |animation_|. + base::TimeDelta GetAnimationDuration() const; + + // Returns the size of the vector graphic as reported by |animation_|. This is + // constant for a given |animation_|. + gfx::Size GetOriginalSize() const; + + // Animation controls -------------------------------------------------------- + // This is an asynchronous call that would start playing the animation on the + // next animation step. On a successful start the |observer_| would be + // notified. Use this if you want to play the entire animation. + void Start(Style style = Style::kLoop); + + // This is an asynchronous call that would start playing the animation on the + // next animation step. On a successful start the |observer_| would be + // notified. + // The animation will be scheduled to play from the |start_offset| to + // |start_offset| + |duration|. The values will be clamped so as to not go out + // of bounds. + void StartSubsection(base::TimeDelta start_offset, + base::TimeDelta duration, + Style style = Style::kLoop); + + // Pauses the animation. + void Pause(); + + // This is an asynchronous call that would resume playing a paused animation + // on the next animation step. + void ResumePlaying(); + + // Resets the animation to the first frame and stops. + void Stop(); + + // Returns the current normalized [0..1] value at which the animation frame + // is. + // 0 -> first frame and 1 -> last frame. + float GetCurrentProgress() const; + + // Paint operations ---------------------------------------------------------- + // Paints the frame of the animation for the given |timestamp| at the given + // |size|. + void Paint(gfx::Canvas* canvas, + const base::TimeTicks& timestamp, + const gfx::Size& size); + + // Paints the frame of the animation for the normalized time instance |t|. Use + // this for special cases when you want to manually manage which frame to + // paint. + void PaintFrame(gfx::Canvas* canvas, float t, const gfx::Size& size); + + // Returns the skottie object that contins the animation data. + scoped_refptr<SkottieWrapper> skottie() const { return skottie_; } + + private: + friend class SkiaVectorAnimationTest; + + enum class PlayState { + kStopped = 0, // Animation is stopped. + kSchedulePlay, // Animation will start playing on the next animatin step. + kPlaying, // Animation is playing. + kPaused, // Animation is paused. + kScheduleResume, // Animation will resume playing on the next animation + // step + kEnded // Animation has ended. + }; + + // Class to manage the timeline when playing the animation. Manages the + // normalized progress [0..1] between the given start and end offset. If the + // reverse flag is set, the progress runs in reverse. + class GFX_EXPORT TimerControl { + public: + TimerControl(const base::TimeDelta& offset, + const base::TimeDelta& cycle_duration, + const base::TimeDelta& total_duration, + const base::TimeTicks& start_timestamp, + bool should_reverse); + ~TimerControl() = default; + + // Update timeline progress based on the new timetick |timestamp|. + void Step(const base::TimeTicks& timestamp); + + // Resumes the timer. + void Resume(const base::TimeTicks& timestamp); + + double GetNormalizedCurrentCycleProgress() const; + double GetNormalizedStartOffset() const; + double GetNormalizedEndOffset() const; + int completed_cycles() const { return completed_cycles_; } + + private: + friend class SkiaVectorAnimationTest; + + // Time duration from 0 which marks the beginning of a cycle. + const base::TimeDelta start_offset_; + + // Time duration from 0 which marks the end of a cycle. + const base::TimeDelta end_offset_; + + // Time duration for one cycle. This is essentially a cache of the + // difference between |end_offset_| - |start_offset_|. + const base::TimeDelta cycle_duration_; + + // Normalized animation progress delta per millisecond, that is, the + // normalized progress in per millisecond of time duration. + const double progress_per_millisecond_; + + // The timetick at which |progress_| was updated last. + base::TimeTicks previous_tick_; + + // The progress of the timer. This is a monotonically increasing value. + base::TimeDelta progress_; + + // This is the progress of the timer in the current cycle. + base::TimeDelta current_cycle_progress_; + + // If true, the progress will go into reverse after each cycle. This is used + // for throbbing animations. + bool should_reverse_ = false; + + // The number of times each |cycle_duration_| is covered by the timer. + int completed_cycles_ = 0; + + DISALLOW_COPY_AND_ASSIGN(TimerControl); + }; + + void InitTimer(const base::TimeTicks& timestamp); + void UpdateState(const base::TimeTicks& timestamp); + + // Manages the timeline for the current playing animation. + std::unique_ptr<TimerControl> timer_control_; + + // The style of animation to play. + Style style_ = Style::kLoop; + + // The current state of animation. + PlayState state_ = PlayState::kStopped; + + // The below values of scheduled_* are set when we have scheduled a play. + // These will be used to initialize |timer_control_|. + base::TimeDelta scheduled_start_offset_; + base::TimeDelta scheduled_duration_; + + SkiaVectorAnimationObserver* observer_ = nullptr; + + scoped_refptr<SkottieWrapper> skottie_; + + DISALLOW_COPY_AND_ASSIGN(SkiaVectorAnimation); +}; + +} // namespace gfx + +#endif // UI_GFX_SKIA_VECTOR_ANIMATION_H_ diff --git a/chromium/ui/gfx/skia_vector_animation_observer.h b/chromium/ui/gfx/skia_vector_animation_observer.h new file mode 100644 index 00000000000..1e6e479a29d --- /dev/null +++ b/chromium/ui/gfx/skia_vector_animation_observer.h @@ -0,0 +1,33 @@ +// 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_GFX_SKIA_VECTOR_ANIMATION_OBSERVER_H_ +#define UI_GFX_SKIA_VECTOR_ANIMATION_OBSERVER_H_ + +#include "ui/gfx/gfx_export.h" + +namespace gfx { +class SkiaVectorAnimation; + +class GFX_EXPORT SkiaVectorAnimationObserver { + public: + // Called when the animation started playing. + virtual void AnimationWillStartPlaying(const SkiaVectorAnimation* animation) { + } + + // Called when one animation cycle has completed. This happens when a linear + // animation has reached its end, or a loop/throbbing animation has finished + // a cycle. + virtual void AnimationCycleEnded(const SkiaVectorAnimation* animation) {} + + // Called when the animation has successfully resumed. + virtual void AnimationResuming(const SkiaVectorAnimation* animation) {} + + protected: + virtual ~SkiaVectorAnimationObserver() = default; +}; + +} // namespace gfx + +#endif // UI_GFX_SKIA_VECTOR_ANIMATION_OBSERVER_H_ diff --git a/chromium/ui/gfx/skia_vector_animation_unittest.cc b/chromium/ui/gfx/skia_vector_animation_unittest.cc new file mode 100644 index 00000000000..e7394e2ae7d --- /dev/null +++ b/chromium/ui/gfx/skia_vector_animation_unittest.cc @@ -0,0 +1,835 @@ +// 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/gfx/skia_vector_animation.h" + +#include <string> + +#include "base/macros.h" +#include "base/memory/ref_counted_memory.h" +#include "base/memory/scoped_refptr.h" +#include "base/test/simple_test_tick_clock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkStream.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/skia_vector_animation_observer.h" +#include "ui/gfx/skottie_wrapper.h" + +namespace gfx { +namespace { + +// A skottie animation with solid green color for the first 2.5 seconds and then +// a solid blue color for the next 2.5 seconds. +constexpr char kData[] = + "{" + " \"v\" : \"4.12.0\"," + " \"fr\": 30," + " \"w\" : 400," + " \"h\" : 200," + " \"ip\": 0," + " \"op\": 150," + " \"assets\": []," + + " \"layers\": [" + " {" + " \"ty\": 1," + " \"sw\": 400," + " \"sh\": 200," + " \"sc\": \"#00ff00\"," + " \"ip\": 0," + " \"op\": 75" + " }," + " {" + " \"ty\": 1," + " \"sw\": 400," + " \"sh\": 200," + " \"sc\": \"#0000ff\"," + " \"ip\": 76," + " \"op\": 150" + " }" + " ]" + "}"; +constexpr float kAnimationWidth = 400.f; +constexpr float kAnimationHeight = 200.f; +constexpr float kAnimationDuration = 5.f; + +class TestAnimationObserver : public SkiaVectorAnimationObserver { + public: + TestAnimationObserver() = default; + + void AnimationWillStartPlaying( + const SkiaVectorAnimation* animation) override { + animation_will_start_playing_ = true; + } + + void AnimationCycleEnded(const SkiaVectorAnimation* animation) override { + animation_cycle_ended_ = true; + } + + void AnimationResuming(const SkiaVectorAnimation* animation) override { + animation_resuming_ = true; + } + + void Reset() { + animation_cycle_ended_ = false; + animation_will_start_playing_ = false; + animation_resuming_ = false; + } + + bool animation_cycle_ended() const { return animation_cycle_ended_; } + bool animation_will_start_playing() const { + return animation_will_start_playing_; + } + bool animation_resuming() const { return animation_resuming_; } + + private: + bool animation_cycle_ended_ = false; + bool animation_will_start_playing_ = false; + bool animation_resuming_ = false; + + DISALLOW_COPY_AND_ASSIGN(TestAnimationObserver); +}; + +} // namespace + +class SkiaVectorAnimationTest : public testing::Test { + public: + SkiaVectorAnimationTest() = default; + ~SkiaVectorAnimationTest() override {} + + void SetUp() override { + canvas_.reset(new gfx::Canvas(gfx::Size(kAnimationWidth, kAnimationHeight), + 1.f, false)); + skottie_ = base::MakeRefCounted<SkottieWrapper>( + std::make_unique<SkMemoryStream>(kData, std::strlen(kData))); + animation_ = std::make_unique<SkiaVectorAnimation>(skottie_); + } + + void TearDown() override { animation_.reset(nullptr); } + + Canvas* canvas() { return canvas_.get(); } + + SkiaVectorAnimation::Style GetStyle() const { return animation_->style_; } + + SkiaVectorAnimation::PlayState GetState() const { return animation_->state_; } + + bool IsStopped() const { + return GetState() == SkiaVectorAnimation::PlayState::kStopped; + } + + bool IsScheduledToPlay() const { + return GetState() == SkiaVectorAnimation::PlayState::kSchedulePlay; + } + + bool IsPlaying() const { + return GetState() == SkiaVectorAnimation::PlayState::kPlaying; + } + + bool IsScheduledToResume() const { + return GetState() == SkiaVectorAnimation::PlayState::kScheduleResume; + } + + bool HasAnimationEnded() const { + return GetState() == SkiaVectorAnimation::PlayState::kEnded; + } + + bool IsPaused() const { + return GetState() == SkiaVectorAnimation::PlayState::kPaused; + } + + const SkiaVectorAnimation::TimerControl* GetTimerControl() const { + return animation_->timer_control_.get(); + } + + const base::TickClock* test_clock() const { return &test_clock_; } + + void AdvanceClock(int64_t ms) { + test_clock_.Advance(base::TimeDelta::FromMilliseconds(ms)); + } + + base::TimeDelta TimeDeltaSince(const base::TimeTicks& ticks) const { + return test_clock_.NowTicks() - ticks; + } + + const base::TimeTicks NowTicks() const { return test_clock_.NowTicks(); } + + double GetTimerStartOffset() const { + return animation_->timer_control_->GetNormalizedStartOffset(); + } + + double GetTimerEndOffset() const { + return animation_->timer_control_->GetNormalizedEndOffset(); + } + + const base::TimeTicks& GetTimerPreviousTick() const { + return animation_->timer_control_->previous_tick_; + } + + double GetTimerProgressPerMs() const { + return animation_->timer_control_->progress_per_millisecond_; + } + + int GetTimerCycles() const { + return animation_->timer_control_->completed_cycles(); + } + + void IsAllSameColor(SkColor color, const SkBitmap& bitmap) const { + if (bitmap.colorType() == kBGRA_8888_SkColorType) { + const SkColor* pixels = reinterpret_cast<SkColor*>(bitmap.getPixels()); + const int num_pixels = bitmap.width() * bitmap.height(); + for (int i = 0; i < num_pixels; i++) + EXPECT_EQ(pixels[i], color); + } else { + for (int x = 0; x < bitmap.width(); x++) + for (int y = 0; y < bitmap.height(); y++) + EXPECT_EQ(bitmap.getColor(x, y), color); + } + } + + protected: + std::unique_ptr<SkiaVectorAnimation> animation_; + scoped_refptr<SkottieWrapper> skottie_; + + private: + std::unique_ptr<gfx::Canvas> canvas_; + base::SimpleTestTickClock test_clock_; + + DISALLOW_COPY_AND_ASSIGN(SkiaVectorAnimationTest); +}; + +TEST_F(SkiaVectorAnimationTest, InitializationAndLoadingData) { + auto bytes = base::MakeRefCounted<base::RefCountedBytes>( + std::vector<unsigned char>(kData, kData + std::strlen(kData))); + skottie_ = base::MakeRefCounted<SkottieWrapper>(bytes.get()); + animation_ = std::make_unique<SkiaVectorAnimation>(skottie_); + EXPECT_FLOAT_EQ(animation_->GetOriginalSize().width(), kAnimationWidth); + EXPECT_FLOAT_EQ(animation_->GetOriginalSize().height(), kAnimationHeight); + EXPECT_FLOAT_EQ(animation_->GetAnimationDuration().InSecondsF(), + kAnimationDuration); + EXPECT_TRUE(IsStopped()); + + skottie_ = base::MakeRefCounted<SkottieWrapper>( + std::make_unique<SkMemoryStream>(kData, std::strlen(kData))); + animation_ = std::make_unique<SkiaVectorAnimation>(skottie_); + EXPECT_FLOAT_EQ(animation_->GetOriginalSize().width(), kAnimationWidth); + EXPECT_FLOAT_EQ(animation_->GetOriginalSize().height(), kAnimationHeight); + EXPECT_FLOAT_EQ(animation_->GetAnimationDuration().InSecondsF(), + kAnimationDuration); + EXPECT_TRUE(IsStopped()); +} + +TEST_F(SkiaVectorAnimationTest, PlayLinearAnimation) { + TestAnimationObserver observer; + animation_->SetAnimationObserver(&observer); + + // Advance clock by 300 milliseconds. + AdvanceClock(300); + + EXPECT_TRUE(IsStopped()); + animation_->Start(SkiaVectorAnimation::Style::kLinear); + EXPECT_TRUE(IsScheduledToPlay()); + EXPECT_FALSE(observer.animation_will_start_playing()); + + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FALSE(IsScheduledToPlay()); + EXPECT_TRUE(IsPlaying()); + EXPECT_TRUE(observer.animation_will_start_playing()); + + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 0); + EXPECT_FLOAT_EQ(GetTimerStartOffset(), 0); + EXPECT_FLOAT_EQ(GetTimerEndOffset(), 1.f); + + EXPECT_FLOAT_EQ(GetTimerProgressPerMs(), 1.f / 5000.f); + + AdvanceClock(50); + EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 50); + + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 50.f / 5000.f); + EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0); + + // Advance the clock to the end of the animation. + AdvanceClock(4951); + EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 4951); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 1.f); + EXPECT_TRUE(HasAnimationEnded()); + EXPECT_TRUE(observer.animation_cycle_ended()); +} + +TEST_F(SkiaVectorAnimationTest, StopLinearAnimation) { + TestAnimationObserver observer; + animation_->SetAnimationObserver(&observer); + + // Advance clock by 300 milliseconds. + AdvanceClock(300); + + animation_->Start(SkiaVectorAnimation::Style::kLinear); + + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_TRUE(IsPlaying()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 0); + + AdvanceClock(50); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 50.f / 5000.f); + + animation_->Stop(); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 0.f); + EXPECT_TRUE(IsStopped()); +} + +TEST_F(SkiaVectorAnimationTest, PlaySubsectionOfLinearAnimation) { + const int start_time_ms = 400; + const int duration_ms = 1000; + const float total_duration_ms = kAnimationDuration * 1000.f; + + TestAnimationObserver observer; + + animation_->SetAnimationObserver(&observer); + + // Advance clock by 300 milliseconds. + AdvanceClock(300); + + EXPECT_FALSE(observer.animation_cycle_ended()); + animation_->StartSubsection(base::TimeDelta::FromMilliseconds(start_time_ms), + base::TimeDelta::FromMilliseconds(duration_ms), + SkiaVectorAnimation::Style::kLinear); + + EXPECT_TRUE(IsScheduledToPlay()); + EXPECT_FALSE(observer.animation_will_start_playing()); + + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + + EXPECT_FALSE(IsScheduledToPlay()); + EXPECT_TRUE(IsPlaying()); + EXPECT_TRUE(observer.animation_will_start_playing()); + + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerStartOffset()); + EXPECT_FLOAT_EQ(GetTimerEndOffset(), + (start_time_ms + duration_ms) / total_duration_ms); + + EXPECT_FLOAT_EQ(GetTimerProgressPerMs(), 1.f / total_duration_ms); + + AdvanceClock(100); + EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 100); + + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), + (100.f + start_time_ms) / total_duration_ms); + EXPECT_FALSE(observer.animation_cycle_ended()); + + // Advance clock another 300 ms. + AdvanceClock(300); + EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 300); + + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), + (100.f + 300.f + start_time_ms) / total_duration_ms); + EXPECT_FALSE(observer.animation_cycle_ended()); + + // Reach the end of animation. + AdvanceClock(601); + EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 601); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerEndOffset()); + EXPECT_TRUE(observer.animation_cycle_ended()); + EXPECT_TRUE(HasAnimationEnded()); +} + +TEST_F(SkiaVectorAnimationTest, PausingLinearAnimation) { + const int start_time_ms = 400; + const int duration_ms = 1000; + const float total_duration_ms = kAnimationDuration * 1000.f; + TestAnimationObserver observer; + animation_->SetAnimationObserver(&observer); + + AdvanceClock(200); + + animation_->StartSubsection(base::TimeDelta::FromMilliseconds(start_time_ms), + base::TimeDelta::FromMilliseconds(duration_ms), + SkiaVectorAnimation::Style::kLinear); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + + AdvanceClock(100); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 500.f / total_duration_ms); + + AdvanceClock(100); + animation_->Pause(); + EXPECT_TRUE(IsPaused()); + + // Advancing clock and stepping animation should have no effect when animation + // is paused. + AdvanceClock(5000); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 500.f / total_duration_ms); + + // Resume playing the animation. + animation_->ResumePlaying(); + EXPECT_TRUE(IsScheduledToResume()); + + // There should be no progress, since we haven't advanced the clock yet. + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_TRUE(IsPlaying()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 500.f / total_duration_ms); + + AdvanceClock(100); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 600.f / total_duration_ms); + + AdvanceClock(801); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); +} + +TEST_F(SkiaVectorAnimationTest, PlayLoopAnimation) { + TestAnimationObserver observer; + animation_->SetAnimationObserver(&observer); + + // Advance clock by 300 milliseconds. + AdvanceClock(300); + + EXPECT_TRUE(IsStopped()); + animation_->Start(SkiaVectorAnimation::Style::kLoop); + EXPECT_TRUE(IsScheduledToPlay()); + EXPECT_FALSE(observer.animation_will_start_playing()); + + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + + EXPECT_FALSE(IsScheduledToPlay()); + EXPECT_TRUE(IsPlaying()); + EXPECT_TRUE(observer.animation_will_start_playing()); + + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 0); + EXPECT_FLOAT_EQ(GetTimerStartOffset(), 0); + EXPECT_FLOAT_EQ(GetTimerEndOffset(), 1.f); + + EXPECT_FLOAT_EQ(GetTimerProgressPerMs(), 1.f / 5000.f); + + AdvanceClock(50); + EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 50); + + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 50.f / 5000.f); + EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0); + + // Advance the clock to the end of the animation. + AdvanceClock(4950); + EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 4950); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_EQ(GetTimerCycles(), 1); + EXPECT_TRUE(std::abs(animation_->GetCurrentProgress() - 0.f) < 0.0001f); + EXPECT_TRUE(observer.animation_cycle_ended()); + EXPECT_TRUE(IsPlaying()); +} + +TEST_F(SkiaVectorAnimationTest, PlaySubsectionOfLoopAnimation) { + const int start_time_ms = 400; + const int duration_ms = 1000; + const float total_duration_ms = kAnimationDuration * 1000.f; + + + TestAnimationObserver observer; + animation_->SetAnimationObserver(&observer); + + // Advance clock by 300 milliseconds. + AdvanceClock(300); + EXPECT_TRUE(IsStopped()); + animation_->StartSubsection(base::TimeDelta::FromMilliseconds(start_time_ms), + base::TimeDelta::FromMilliseconds(duration_ms), + SkiaVectorAnimation::Style::kLoop); + EXPECT_TRUE(IsScheduledToPlay()); + EXPECT_FALSE(observer.animation_will_start_playing()); + + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + + EXPECT_FALSE(IsScheduledToPlay()); + EXPECT_TRUE(IsPlaying()); + EXPECT_TRUE(observer.animation_will_start_playing()); + + EXPECT_FALSE(observer.animation_cycle_ended()); + EXPECT_FLOAT_EQ(GetTimerStartOffset(), 400.f / total_duration_ms); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerStartOffset()); + EXPECT_FLOAT_EQ(GetTimerEndOffset(), + (start_time_ms + duration_ms) / total_duration_ms); + + EXPECT_FLOAT_EQ(GetTimerProgressPerMs(), 1.f / total_duration_ms); + + AdvanceClock(100); + EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 100); + EXPECT_FALSE(observer.animation_cycle_ended()); + + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), + (100.f + start_time_ms) / total_duration_ms); + + // Advance clock another 300 ms. + AdvanceClock(300); + EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 300); + + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), + (100.f + 300.f + start_time_ms) / total_duration_ms); + EXPECT_FALSE(observer.animation_cycle_ended()); + + // Reach the end of animation. + AdvanceClock(600); + EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 600); + + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_TRUE(observer.animation_cycle_ended()); + EXPECT_TRUE(IsPlaying()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerStartOffset()); +} + +TEST_F(SkiaVectorAnimationTest, PausingLoopAnimation) { + const int start_time_ms = 400; + const int duration_ms = 1000; + const float total_duration_ms = kAnimationDuration * 1000.f; + + TestAnimationObserver observer; + animation_->SetAnimationObserver(&observer); + + AdvanceClock(200); + + animation_->StartSubsection(base::TimeDelta::FromMilliseconds(start_time_ms), + base::TimeDelta::FromMilliseconds(duration_ms), + SkiaVectorAnimation::Style::kLoop); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 400.f / total_duration_ms); + + AdvanceClock(100); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 500.f / total_duration_ms); + + AdvanceClock(100); + animation_->Pause(); + EXPECT_TRUE(IsPaused()); + + // Advancing clock and stepping animation should have no effect when animation + // is paused. + AdvanceClock(5000); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 500.f / total_duration_ms); + + // Resume playing the animation. + animation_->ResumePlaying(); + EXPECT_TRUE(IsScheduledToResume()); + + // There should be no progress, since we haven't advanced the clock yet. + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_TRUE(IsPlaying()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 500.f / total_duration_ms); + + AdvanceClock(100); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 600.f / total_duration_ms); + EXPECT_FALSE(observer.animation_cycle_ended()); + + AdvanceClock(800); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerStartOffset()); + EXPECT_TRUE(IsPlaying()); + EXPECT_TRUE(observer.animation_cycle_ended()); +} + +TEST_F(SkiaVectorAnimationTest, PlayThrobbingAnimation) { + + TestAnimationObserver observer; + animation_->SetAnimationObserver(&observer); + // Advance clock by 300 milliseconds. + AdvanceClock(300); + + animation_->Start(SkiaVectorAnimation::Style::kThrobbing); + EXPECT_TRUE(IsScheduledToPlay()); + EXPECT_FALSE(observer.animation_will_start_playing()); + + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + + EXPECT_FALSE(IsScheduledToPlay()); + EXPECT_TRUE(IsPlaying()); + EXPECT_TRUE(observer.animation_will_start_playing()); + + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 0); + EXPECT_FLOAT_EQ(GetTimerStartOffset(), 0); + EXPECT_FLOAT_EQ(GetTimerEndOffset(), 1.f); + + EXPECT_FLOAT_EQ(GetTimerProgressPerMs(), 1.f / 5000.f); + + AdvanceClock(50); + EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 50); + + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 50.f / 5000.f); + EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0); + + // Advance the clock to the end of the animation. + AdvanceClock(4950); + EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 4950); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 1.f); + EXPECT_TRUE(IsPlaying()); + EXPECT_FALSE(observer.animation_cycle_ended()); + + AdvanceClock(2500); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 0.5f); + EXPECT_TRUE(IsPlaying()); + EXPECT_FALSE(observer.animation_cycle_ended()); + + AdvanceClock(2500); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 0); + EXPECT_TRUE(IsPlaying()); + EXPECT_TRUE(observer.animation_cycle_ended()); +} + +TEST_F(SkiaVectorAnimationTest, PlaySubsectionOfThrobbingAnimation) { + const int start_time_ms = 400; + const int duration_ms = 1000; + const float total_duration_ms = kAnimationDuration * 1000.f; + + + TestAnimationObserver observer; + animation_->SetAnimationObserver(&observer); + + // Advance clock by 300 milliseconds. + AdvanceClock(300); + + animation_->StartSubsection(base::TimeDelta::FromMilliseconds(start_time_ms), + base::TimeDelta::FromMilliseconds(duration_ms), + SkiaVectorAnimation::Style::kThrobbing); + EXPECT_TRUE(IsScheduledToPlay()); + EXPECT_FALSE(observer.animation_will_start_playing()); + + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + + EXPECT_FALSE(IsScheduledToPlay()); + EXPECT_TRUE(IsPlaying()); + EXPECT_TRUE(observer.animation_will_start_playing()); + + EXPECT_FLOAT_EQ(GetTimerStartOffset(), 400.f / total_duration_ms); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerStartOffset()); + EXPECT_FLOAT_EQ(GetTimerEndOffset(), + (start_time_ms + duration_ms) / total_duration_ms); + + EXPECT_FLOAT_EQ(GetTimerProgressPerMs(), 1.f / total_duration_ms); + + AdvanceClock(100); + EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 100); + + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FALSE(observer.animation_cycle_ended()); + EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), + (100.f + start_time_ms) / total_duration_ms); + + // Advance clock another 300 ms. + AdvanceClock(300); + EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 300); + + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FALSE(observer.animation_cycle_ended()); + EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 0); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), + (100.f + 300.f + start_time_ms) / total_duration_ms); + + // Reach the end of animation. + AdvanceClock(600); + EXPECT_EQ(TimeDeltaSince(GetTimerPreviousTick()).InMilliseconds(), 600); + + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_TRUE(IsPlaying()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerEndOffset()); + EXPECT_FALSE(observer.animation_cycle_ended()); + + AdvanceClock(500); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 900.f / total_duration_ms); + EXPECT_TRUE(IsPlaying()); + EXPECT_FALSE(observer.animation_cycle_ended()); + + AdvanceClock(500); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerStartOffset()); + EXPECT_TRUE(IsPlaying()); + EXPECT_TRUE(observer.animation_cycle_ended()); + + observer.Reset(); + + AdvanceClock(100); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), + (start_time_ms + 100.f) / total_duration_ms); + EXPECT_TRUE(IsPlaying()); +} + +TEST_F(SkiaVectorAnimationTest, PausingThrobbingAnimation) { + const int start_time_ms = 400; + const int duration_ms = 1000; + const float total_duration_ms = kAnimationDuration * 1000.f; + + AdvanceClock(200); + + animation_->StartSubsection(base::TimeDelta::FromMilliseconds(start_time_ms), + base::TimeDelta::FromMilliseconds(duration_ms), + SkiaVectorAnimation::Style::kThrobbing); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + + EXPECT_TRUE(IsPlaying()); + + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), + start_time_ms / total_duration_ms); + + AdvanceClock(100); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), + (start_time_ms + 100.f) / total_duration_ms); + + AdvanceClock(100); + animation_->Pause(); + EXPECT_TRUE(IsPaused()); + + // Advancing clock and stepping animation should have no effect when animation + // is paused. + AdvanceClock(5000); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), + (start_time_ms + 100.f) / total_duration_ms); + + // Resume playing the animation. + animation_->ResumePlaying(); + EXPECT_TRUE(IsScheduledToResume()); + + // There should be no progress, since we haven't advanced the clock yet. + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_TRUE(IsPlaying()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), + (start_time_ms + 100.f) / total_duration_ms); + + AdvanceClock(100); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), + (start_time_ms + 200.f) / total_duration_ms); + + AdvanceClock(800); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerEndOffset()); + EXPECT_TRUE(IsPlaying()); + + AdvanceClock(100); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), + (start_time_ms + 900.f) / total_duration_ms); + EXPECT_TRUE(IsPlaying()); + + animation_->Pause(); + EXPECT_TRUE(IsPaused()); + + AdvanceClock(10000); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), + (start_time_ms + 900.f) / total_duration_ms); + + // Resume playing the animation. + animation_->ResumePlaying(); + EXPECT_TRUE(IsScheduledToResume()); + + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_TRUE(IsPlaying()); + + AdvanceClock(500); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), + (start_time_ms + 400.f) / total_duration_ms); + + AdvanceClock(400); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), GetTimerStartOffset()); + + AdvanceClock(100); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), + (start_time_ms + 100.f) / total_duration_ms); + EXPECT_TRUE(IsPlaying()); +} + +TEST_F(SkiaVectorAnimationTest, PauseBeforePlay) { + const float total_duration_ms = kAnimationDuration * 1000.f; + + // Test to see if the race condition is handled correctly. It may happen that + // we pause the video before it even starts playing. + + TestAnimationObserver observer; + animation_->SetAnimationObserver(&observer); + + AdvanceClock(300); + + animation_->Start(); + EXPECT_TRUE(IsScheduledToPlay()); + + animation_->Pause(); + EXPECT_TRUE(IsPaused()); + + AdvanceClock(100); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + + animation_->ResumePlaying(); + EXPECT_TRUE(IsScheduledToResume()); + + AdvanceClock(100); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_TRUE(IsPlaying()); + + AdvanceClock(100); + animation_->Paint(canvas(), NowTicks(), animation_->GetOriginalSize()); + EXPECT_FLOAT_EQ(animation_->GetCurrentProgress(), 100.f / total_duration_ms); +} + +TEST_F(SkiaVectorAnimationTest, PaintTest) { + std::unique_ptr<gfx::Canvas> canvas(new gfx::Canvas( + gfx::Size(kAnimationWidth, kAnimationHeight), 1.f, false)); + + // Advance clock by 300 milliseconds. + AdvanceClock(300); + + animation_->Start(SkiaVectorAnimation::Style::kLinear); + animation_->Paint(canvas.get(), NowTicks(), animation_->GetOriginalSize()); + + AdvanceClock(50); + animation_->Paint(canvas.get(), NowTicks(), animation_->GetOriginalSize()); + SkBitmap bitmap = canvas->GetBitmap(); + IsAllSameColor(SK_ColorGREEN, bitmap); + + AdvanceClock(2450); + animation_->Paint(canvas.get(), NowTicks(), animation_->GetOriginalSize()); + bitmap = canvas->GetBitmap(); + IsAllSameColor(SK_ColorGREEN, bitmap); + + AdvanceClock(50); + animation_->Paint(canvas.get(), NowTicks(), animation_->GetOriginalSize()); + bitmap = canvas->GetBitmap(); + IsAllSameColor(SK_ColorBLUE, bitmap); + + AdvanceClock(1000); + animation_->Paint(canvas.get(), NowTicks(), animation_->GetOriginalSize()); + bitmap = canvas->GetBitmap(); + IsAllSameColor(SK_ColorBLUE, bitmap); + + AdvanceClock(1400); + animation_->Paint(canvas.get(), NowTicks(), animation_->GetOriginalSize()); + bitmap = canvas->GetBitmap(); + IsAllSameColor(SK_ColorBLUE, bitmap); +} + +} // namespace gfx diff --git a/chromium/ui/gfx/skottie_wrapper.cc b/chromium/ui/gfx/skottie_wrapper.cc new file mode 100644 index 00000000000..1cba085fb46 --- /dev/null +++ b/chromium/ui/gfx/skottie_wrapper.cc @@ -0,0 +1,36 @@ +// 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/gfx/skottie_wrapper.h" + +#include "base/memory/ref_counted_memory.h" +#include "base/trace_event/trace_event.h" +#include "third_party/skia/include/core/SkRect.h" +#include "third_party/skia/include/core/SkStream.h" +#include "ui/gfx/geometry/size.h" + +namespace gfx { + +SkottieWrapper::SkottieWrapper( + const scoped_refptr<base::RefCountedMemory>& data_stream) { + TRACE_EVENT0("ui", "SkottieWrapper Parse"); + SkMemoryStream sk_stream(data_stream->front(), data_stream->size()); + animation_ = skottie::Animation::Make(&sk_stream); +} + +SkottieWrapper::SkottieWrapper(std::unique_ptr<SkMemoryStream> stream) + : animation_(skottie::Animation::Make(stream.get())) {} + +SkottieWrapper::~SkottieWrapper() {} + +void SkottieWrapper::Draw(SkCanvas* canvas, float t, const gfx::Size& size) { + SkRect dst = SkRect::MakeXYWH(0, 0, size.width(), size.height()); + { + base::AutoLock lock(lock_); + animation_->seek(t); + animation_->render(canvas, &dst); + } +} + +} // namespace gfx diff --git a/chromium/ui/gfx/skottie_wrapper.h b/chromium/ui/gfx/skottie_wrapper.h new file mode 100644 index 00000000000..006d0cd26bf --- /dev/null +++ b/chromium/ui/gfx/skottie_wrapper.h @@ -0,0 +1,55 @@ +// 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_GFX_SKOTTIE_WRAPPER_H_ +#define UI_GFX_SKOTTIE_WRAPPER_H_ + +#include <memory> + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/synchronization/lock.h" +#include "third_party/skia/modules/skottie/include/Skottie.h" +#include "ui/gfx/gfx_export.h" + +class SkCanvas; +class SkMemoryStream; + +namespace base { +class RefCountedMemory; +} // namespace base + +namespace gfx { +class Size; + +// A wrapper over Skia's Skottie object that can be shared by multiple +// SkiaVectorAnimation objects. This class is thread safe when performing a draw +// on an SkCanvas. +class GFX_EXPORT SkottieWrapper + : public base::RefCountedThreadSafe<SkottieWrapper> { + public: + explicit SkottieWrapper( + const scoped_refptr<base::RefCountedMemory>& data_stream); + explicit SkottieWrapper(std::unique_ptr<SkMemoryStream> stream); + + // A thread safe call that will draw an image of size |size| for the frame at + // normalized time instant |t| onto the |canvas|. + void Draw(SkCanvas* canvas, float t, const Size& size); + + float duration() const { return animation_->duration(); } + SkSize size() const { return animation_->size(); } + + private: + friend class base::RefCountedThreadSafe<SkottieWrapper>; + ~SkottieWrapper(); + + base::Lock lock_; + sk_sp<skottie::Animation> animation_; + + DISALLOW_COPY_AND_ASSIGN(SkottieWrapper); +}; + +} // namespace gfx + +#endif // UI_GFX_SKOTTIE_WRAPPER_H_ diff --git a/chromium/ui/gfx/switches.cc b/chromium/ui/gfx/switches.cc index e1943d5970a..b0c3d1daf7a 100644 --- a/chromium/ui/gfx/switches.cc +++ b/chromium/ui/gfx/switches.cc @@ -12,12 +12,6 @@ namespace switches { const char kDisableDirectWriteForUI[] = "disable-directwrite-for-ui"; #endif -#if defined(OS_MACOSX) -// Enables the HarfBuzz port of RenderText on Mac (it's already used only for -// text editing; this enables it for everything else). -const char kEnableHarfBuzzRenderText[] = "enable-harfbuzz-rendertext"; -#endif - // Force disables font subpixel positioning. This affects the character glyph // sharpness, kerning, hinting and layout. const char kDisableFontSubpixelPositioning[] = @@ -37,4 +31,16 @@ namespace features { const base::Feature kLeftToRightUrls{"LeftToRightUrls", base::FEATURE_DISABLED_BY_DEFAULT}; +// Enables or disables the use of cc::PaintRecords as a backing store for +// ImageSkiaReps. This may reduce load on the UI thread by moving rasterization +// of drawables away from this thread. +const base::Feature kUsePaintRecordForImageSkia{ + "UsePaintRecordForImageSkia", +#if defined(OS_CHROMEOS) + base::FEATURE_ENABLED_BY_DEFAULT +#else + base::FEATURE_DISABLED_BY_DEFAULT +#endif +}; + } // namespace features diff --git a/chromium/ui/gfx/switches.h b/chromium/ui/gfx/switches.h index dc4921dbf0c..b206f18ad77 100644 --- a/chromium/ui/gfx/switches.h +++ b/chromium/ui/gfx/switches.h @@ -15,10 +15,6 @@ namespace switches { GFX_SWITCHES_EXPORT extern const char kDisableDirectWriteForUI[]; #endif -#if defined(OS_MACOSX) -GFX_SWITCHES_EXPORT extern const char kEnableHarfBuzzRenderText[]; -#endif - GFX_SWITCHES_EXPORT extern const char kDisableFontSubpixelPositioning[]; GFX_SWITCHES_EXPORT extern const char kHeadless[]; @@ -29,6 +25,8 @@ namespace features { GFX_SWITCHES_EXPORT extern const base::Feature kLeftToRightUrls; +GFX_SWITCHES_EXPORT extern const base::Feature kUsePaintRecordForImageSkia; + } // namespace features #endif // UI_GFX_SWITCHES_H_ diff --git a/chromium/ui/gfx/transform.h b/chromium/ui/gfx/transform.h index f5e600f9f81..433a545dcc1 100644 --- a/chromium/ui/gfx/transform.h +++ b/chromium/ui/gfx/transform.h @@ -11,7 +11,7 @@ #include "base/compiler_specific.h" #include "third_party/skia/include/core/SkMatrix44.h" #include "ui/gfx/geometry/vector2d_f.h" -#include "ui/gfx/gfx_export.h" +#include "ui/gfx/geometry_skia_export.h" namespace gfx { @@ -25,7 +25,7 @@ class Vector3dF; // 4x4 transformation matrix. Transform is cheap and explicitly allows // copy/assign. -class GFX_EXPORT Transform { +class GEOMETRY_SKIA_EXPORT Transform { public: enum SkipInitialization { diff --git a/chromium/ui/gfx/transform_util.h b/chromium/ui/gfx/transform_util.h index 78b7739e140..2f72cbf4a4a 100644 --- a/chromium/ui/gfx/transform_util.h +++ b/chromium/ui/gfx/transform_util.h @@ -7,7 +7,7 @@ #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/quaternion.h" -#include "ui/gfx/gfx_export.h" +#include "ui/gfx/geometry_skia_export.h" #include "ui/gfx/transform.h" namespace gfx { @@ -16,11 +16,12 @@ class Point; class Rect; // Returns a scale transform at |anchor| point. -GFX_EXPORT Transform GetScaleTransform(const Point& anchor, float scale); +GEOMETRY_SKIA_EXPORT Transform GetScaleTransform(const Point& anchor, + float scale); // Contains the components of a factored transform. These components may be // blended and recomposed. -struct GFX_EXPORT DecomposedTransform { +struct GEOMETRY_SKIA_EXPORT DecomposedTransform { // The default constructor initializes the components in such a way that // if used with Compose below, will produce the identity transform. DecomposedTransform(); @@ -40,7 +41,7 @@ struct GFX_EXPORT DecomposedTransform { // routines described in http://www.w3.org/TR/css3-3d-transform/. // |progress| is in the range [0, 1]. If 0 we will return |from|, if 1, we will // return |to|. -GFX_EXPORT DecomposedTransform +GEOMETRY_SKIA_EXPORT DecomposedTransform BlendDecomposedTransforms(const DecomposedTransform& to, const DecomposedTransform& from, double progress); @@ -48,23 +49,24 @@ BlendDecomposedTransforms(const DecomposedTransform& to, // Decomposes this transform into its translation, scale, skew, perspective, // and rotation components following the routines detailed in this spec: // http://www.w3.org/TR/css3-3d-transforms/. -GFX_EXPORT bool DecomposeTransform(DecomposedTransform* out, - const Transform& transform); +GEOMETRY_SKIA_EXPORT bool DecomposeTransform(DecomposedTransform* out, + const Transform& transform); // Composes a transform from the given translation, scale, skew, prespective, // and rotation components following the routines detailed in this spec: // http://www.w3.org/TR/css3-3d-transforms/. -GFX_EXPORT Transform ComposeTransform(const DecomposedTransform& decomp); +GEOMETRY_SKIA_EXPORT Transform +ComposeTransform(const DecomposedTransform& decomp); -GFX_EXPORT bool SnapTransform(Transform* out, - const Transform& transform, - const Rect& viewport); +GEOMETRY_SKIA_EXPORT bool SnapTransform(Transform* out, + const Transform& transform, + const Rect& viewport); // Calculates a transform with a transformed origin. The resulting tranform is // created by composing P * T * P^-1 where P is a constant transform to the new // origin. -GFX_EXPORT Transform TransformAboutPivot(const gfx::Point& pivot, - const gfx::Transform& transform); +GEOMETRY_SKIA_EXPORT Transform +TransformAboutPivot(const gfx::Point& pivot, const gfx::Transform& transform); } // namespace gfx diff --git a/chromium/ui/gfx/typemaps.gni b/chromium/ui/gfx/typemaps.gni index ba383a829db..c4ea97d798c 100644 --- a/chromium/ui/gfx/typemaps.gni +++ b/chromium/ui/gfx/typemaps.gni @@ -13,6 +13,7 @@ typemaps = [ "//ui/gfx/mojo/gpu_fence_handle.typemap", "//ui/gfx/mojo/overlay_transform.typemap", "//ui/gfx/mojo/presentation_feedback.typemap", + "//ui/gfx/mojo/swap_result.typemap", "//ui/gfx/mojo/selection_bound.typemap", "//ui/gfx/mojo/transform.typemap", "//ui/gfx/range/mojo/range.typemap", diff --git a/chromium/ui/gl/BUILD.gn b/chromium/ui/gl/BUILD.gn index 2699acc9460..67cedb57328 100644 --- a/chromium/ui/gl/BUILD.gn +++ b/chromium/ui/gl/BUILD.gn @@ -3,6 +3,7 @@ # found in the LICENSE file. import("//build/buildflag_header.gni") +import("//build/config/jumbo.gni") import("//build/config/chrome_build.gni") import("//build/config/linux/pkg_config.gni") import("//build/config/ui.gni") @@ -46,10 +47,12 @@ config("gl_config") { } } -component("gl") { +jumbo_component("gl") { output_name = "gl_wrapper" # Avoid colliding with OS X"s libGL.dylib. sources = [ + "android/android_surface_composer_compat.cc", + "android/android_surface_composer_compat.h", "android/scoped_java_surface.cc", "android/scoped_java_surface.h", "android/surface_texture.cc", @@ -125,6 +128,7 @@ component("gl") { "gpu_switching_manager.h", "gpu_timing.cc", "gpu_timing.h", + "progress_reporter.h", "scoped_binders.cc", "scoped_binders.h", "scoped_make_current.cc", @@ -194,11 +198,9 @@ component("gl") { sources += [ "gl_image_ahardwarebuffer.cc", "gl_image_ahardwarebuffer.h", + "gl_surface_egl_surface_control.cc", + "gl_surface_egl_surface_control.h", ] - - if (use_static_angle) { - deps += [ "//third_party/angle:libEGL_static" ] - } } if (is_posix && !is_fuchsia && !is_mac) { @@ -375,7 +377,7 @@ if (is_mac && use_egl) { } } -static_library("gl_unittest_utils") { +jumbo_static_library("gl_unittest_utils") { testonly = true sources = [ "egl_bindings_autogen_mock.cc", @@ -401,7 +403,7 @@ static_library("gl_unittest_utils") { ] } -static_library("test_support") { +jumbo_static_library("test_support") { testonly = true sources = [ "test/gl_image_test_support.cc", diff --git a/chromium/ui/gl/android/android_surface_composer_compat.cc b/chromium/ui/gl/android/android_surface_composer_compat.cc new file mode 100644 index 00000000000..f34d25cae13 --- /dev/null +++ b/chromium/ui/gl/android/android_surface_composer_compat.cc @@ -0,0 +1,253 @@ +// 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/gl/android/android_surface_composer_compat.h" + +#include "base/android/build_info.h" +#include "base/memory/ptr_util.h" +#include "base/no_destructor.h" + +#include <dlfcn.h> + +extern "C" { +// ASurfaceComposer +using pASurfaceComposer_Create = ASurfaceComposer* (*)(ANativeWindow*); +using pASurfaceComposer_Delete = void (*)(ASurfaceComposer*); + +// ASurface +using pASurfaceComposer_CreateSurface = ASurface* (*)(ASurfaceComposer*, + int32_t content_type, + ASurface* parent, + const char* name); +using pASurfaceComposer_DeleteSurface = void (*)(ASurface*); + +// ASurfaceTransaction +using pASurfaceComposer_CreateTransaction = ASurfaceTransaction* (*)(void); +using pASurfaceComposer_DeleteTransaction = void (*)(ASurfaceTransaction*); +using pASurfaceComposer_TransactionApply = int64_t (*)(ASurfaceTransaction*); +using pASurfaceComposer_TransactionSetVisibility = + void (*)(ASurfaceTransaction*, ASurface*, bool show); +using pASurfaceComposer_TransactionSetPosition = void (*)(ASurfaceTransaction*, + ASurface*, + float x, + float y); +using pASurfaceComposer_TransactionSetZOrder = + void (*)(ASurfaceTransaction* transaction, ASurface*, int32_t z); +using pASurfaceComposer_TransactionSetBuffer = + void (*)(ASurfaceTransaction* transaction, + ASurface*, + AHardwareBuffer*, + int32_t fence_fd); +using pASurfaceComposer_TransactionSetSize = + void (*)(ASurfaceTransaction* transaction, + ASurface* surface, + uint32_t width, + uint32_t height); +using pASurfaceComposer_TransactionSetCropRect = + void (*)(ASurfaceTransaction* transaction, + ASurface* surface, + int32_t left, + int32_t top, + int32_t right, + int32_t bottom); +using pASurfaceComposer_TransactionSetOpaque = + void (*)(ASurfaceTransaction* transaction, + ASurface* surface, + uint32_t transform); +} + +namespace gl { +namespace { + +#define LOAD_FUNCTION(lib, func) \ + do { \ + func##Fn = reinterpret_cast<p##func>(dlsym(lib, #func)); \ + if (!func##Fn) { \ + supported = false; \ + LOG(ERROR) << "Unable to load function " << #func; \ + } \ + } while (0) + +struct SurfaceComposerMethods { + public: + static const SurfaceComposerMethods& Get() { + static const base::NoDestructor<SurfaceComposerMethods> instance; + return *instance; + } + + SurfaceComposerMethods() { + void* main_dl_handle = dlopen(nullptr, RTLD_NOW); + if (!main_dl_handle) { + LOG(ERROR) << "Couldnt load android so"; + supported = false; + return; + } + + LOAD_FUNCTION(main_dl_handle, ASurfaceComposer_Create); + LOAD_FUNCTION(main_dl_handle, ASurfaceComposer_Delete); + LOAD_FUNCTION(main_dl_handle, ASurfaceComposer_CreateSurface); + LOAD_FUNCTION(main_dl_handle, ASurfaceComposer_DeleteSurface); + LOAD_FUNCTION(main_dl_handle, ASurfaceComposer_CreateTransaction); + LOAD_FUNCTION(main_dl_handle, ASurfaceComposer_DeleteTransaction); + LOAD_FUNCTION(main_dl_handle, ASurfaceComposer_TransactionApply); + LOAD_FUNCTION(main_dl_handle, ASurfaceComposer_TransactionSetVisibility); + LOAD_FUNCTION(main_dl_handle, ASurfaceComposer_TransactionSetPosition); + LOAD_FUNCTION(main_dl_handle, ASurfaceComposer_TransactionSetZOrder); + LOAD_FUNCTION(main_dl_handle, ASurfaceComposer_TransactionSetBuffer); + LOAD_FUNCTION(main_dl_handle, ASurfaceComposer_TransactionSetSize); + LOAD_FUNCTION(main_dl_handle, ASurfaceComposer_TransactionSetCropRect); + LOAD_FUNCTION(main_dl_handle, ASurfaceComposer_TransactionSetOpaque); + } + + ~SurfaceComposerMethods() = default; + + bool supported = true; + pASurfaceComposer_Create ASurfaceComposer_CreateFn; + pASurfaceComposer_Delete ASurfaceComposer_DeleteFn; + pASurfaceComposer_CreateSurface ASurfaceComposer_CreateSurfaceFn; + pASurfaceComposer_DeleteSurface ASurfaceComposer_DeleteSurfaceFn; + pASurfaceComposer_CreateTransaction ASurfaceComposer_CreateTransactionFn; + pASurfaceComposer_DeleteTransaction ASurfaceComposer_DeleteTransactionFn; + pASurfaceComposer_TransactionApply ASurfaceComposer_TransactionApplyFn; + pASurfaceComposer_TransactionSetVisibility + ASurfaceComposer_TransactionSetVisibilityFn; + pASurfaceComposer_TransactionSetPosition + ASurfaceComposer_TransactionSetPositionFn; + pASurfaceComposer_TransactionSetZOrder + ASurfaceComposer_TransactionSetZOrderFn; + pASurfaceComposer_TransactionSetBuffer + ASurfaceComposer_TransactionSetBufferFn; + pASurfaceComposer_TransactionSetSize ASurfaceComposer_TransactionSetSizeFn; + pASurfaceComposer_TransactionSetCropRect + ASurfaceComposer_TransactionSetCropRectFn; + pASurfaceComposer_TransactionSetOpaque + ASurfaceComposer_TransactionSetOpaqueFn; +}; +}; + +// static +bool SurfaceComposer::IsSupported() { + const int sdk_int = base::android::BuildInfo::GetInstance()->sdk_int(); + if (sdk_int < 29) { + LOG(ERROR) << "SurfaceControl not supported on sdk: " << sdk_int; + return false; + } + return SurfaceComposerMethods::Get().supported; +} + +// static +std::unique_ptr<SurfaceComposer> SurfaceComposer::Create( + ANativeWindow* window) { + DCHECK(SurfaceComposerMethods::Get().supported); + auto* a_composer = + SurfaceComposerMethods::Get().ASurfaceComposer_CreateFn(window); + if (!a_composer) + return nullptr; + return base::WrapUnique(new SurfaceComposer(a_composer)); +} + +SurfaceComposer::SurfaceComposer(ASurfaceComposer* composer) + : composer_(composer) {} + +SurfaceComposer::~SurfaceComposer() { + SurfaceComposerMethods::Get().ASurfaceComposer_DeleteFn(composer_); +} + +SurfaceComposer::Surface::Surface() = default; + +SurfaceComposer::Surface::Surface(SurfaceComposer* composer, + SurfaceContentType content_type, + const char* name, + Surface* parent) { + surface_ = SurfaceComposerMethods::Get().ASurfaceComposer_CreateSurfaceFn( + composer->composer_, static_cast<int32_t>(content_type), + parent ? parent->surface() : nullptr, name); + DCHECK(surface_); +} + +SurfaceComposer::Surface::~Surface() { + if (surface_) + SurfaceComposerMethods::Get().ASurfaceComposer_DeleteSurfaceFn(surface_); +} + +SurfaceComposer::Surface::Surface(Surface&& other) { + surface_ = other.surface_; + other.surface_ = nullptr; +} + +SurfaceComposer::Surface& SurfaceComposer::Surface::operator=(Surface&& other) { + if (surface_) + SurfaceComposerMethods::Get().ASurfaceComposer_DeleteSurfaceFn(surface_); + + surface_ = other.surface_; + other.surface_ = nullptr; + return *this; +} + +SurfaceComposer::Transaction::Transaction() { + transaction_ = + SurfaceComposerMethods::Get().ASurfaceComposer_CreateTransactionFn(); + DCHECK(transaction_); +} + +SurfaceComposer::Transaction::~Transaction() { + SurfaceComposerMethods::Get().ASurfaceComposer_DeleteTransactionFn( + transaction_); +} + +void SurfaceComposer::Transaction::SetVisibility(const Surface& surface, + bool show) { + SurfaceComposerMethods::Get().ASurfaceComposer_TransactionSetVisibilityFn( + transaction_, surface.surface(), show); +} + +void SurfaceComposer::Transaction::SetPosition(const Surface& surface, + float x, + float y) { + SurfaceComposerMethods::Get().ASurfaceComposer_TransactionSetPositionFn( + transaction_, surface.surface(), x, y); +} + +void SurfaceComposer::Transaction::SetZOrder(const Surface& surface, + int32_t z) { + SurfaceComposerMethods::Get().ASurfaceComposer_TransactionSetZOrderFn( + transaction_, surface.surface(), z); +} + +void SurfaceComposer::Transaction::SetBuffer(const Surface& surface, + AHardwareBuffer* buffer, + base::ScopedFD fence_fd) { + SurfaceComposerMethods::Get().ASurfaceComposer_TransactionSetBufferFn( + transaction_, surface.surface(), buffer, + fence_fd.is_valid() ? fence_fd.release() : -1); +} + +void SurfaceComposer::Transaction::SetSize(const Surface& surface, + uint32_t width, + uint32_t height) { + SurfaceComposerMethods::Get().ASurfaceComposer_TransactionSetSizeFn( + transaction_, surface.surface(), width, height); +} + +void SurfaceComposer::Transaction::SetCropRect(const Surface& surface, + int32_t left, + int32_t top, + int32_t right, + int32_t bottom) { + SurfaceComposerMethods::Get().ASurfaceComposer_TransactionSetCropRectFn( + transaction_, surface.surface(), left, top, right, bottom); +} + +void SurfaceComposer::Transaction::SetOpaque(const Surface& surface, + bool opaque) { + SurfaceComposerMethods::Get().ASurfaceComposer_TransactionSetOpaqueFn( + transaction_, surface.surface(), opaque); +} + +void SurfaceComposer::Transaction::Apply() { + SurfaceComposerMethods::Get().ASurfaceComposer_TransactionApplyFn( + transaction_); +} + +} // namespace gl diff --git a/chromium/ui/gl/android/android_surface_composer_compat.h b/chromium/ui/gl/android/android_surface_composer_compat.h new file mode 100644 index 00000000000..05b6c874794 --- /dev/null +++ b/chromium/ui/gl/android/android_surface_composer_compat.h @@ -0,0 +1,86 @@ +// 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_GL_ANDROID_ANDROID_SURFACE_COMPOSER_COMPAT_H_ +#define UI_GL_ANDROID_ANDROID_SURFACE_COMPOSER_COMPAT_H_ + +#include <memory> + +#include <android/hardware_buffer.h> +#include <android/native_window.h> + +#include "base/files/scoped_file.h" +#include "ui/gl/gl_export.h" + +extern "C" { +typedef struct ASurface ASurface; +typedef struct ASurfaceComposer ASurfaceComposer; +typedef struct ASurfaceTransaction ASurfaceTransaction; +} + +namespace gl { + +class GL_EXPORT SurfaceComposer { + public: + enum class SurfaceContentType : int32_t { + kNone = 0, + kAHardwareBuffer = 1, + }; + + class GL_EXPORT Surface { + public: + Surface(); + Surface(SurfaceComposer* composer, + SurfaceContentType content_type, + const char* name, + Surface* parent = nullptr); + ~Surface(); + + Surface(Surface&& other); + Surface& operator=(Surface&& other); + + ASurface* surface() const { return surface_; } + + private: + ASurface* surface_ = nullptr; + }; + + class GL_EXPORT Transaction { + public: + Transaction(); + ~Transaction(); + + void SetVisibility(const Surface& surface, bool show); + void SetPosition(const Surface& surface, float x, float y); + void SetZOrder(const Surface& surface, int32_t z); + void SetBuffer(const Surface& surface, + AHardwareBuffer* buffer, + base::ScopedFD fence_fd); + void SetSize(const Surface& surface, uint32_t width, uint32_t height); + void SetCropRect(const Surface& surface, + int32_t left, + int32_t top, + int32_t right, + int32_t bottom); + void SetOpaque(const Surface& surface, bool opaque); + void Apply(); + + private: + ASurfaceTransaction* transaction_; + }; + + static bool IsSupported(); + + static std::unique_ptr<SurfaceComposer> Create(ANativeWindow* window); + ~SurfaceComposer(); + + private: + explicit SurfaceComposer(ASurfaceComposer* composer); + + ASurfaceComposer* composer_; +}; + +}; // namespace gl + +#endif // UI_GL_ANDROID_ANDROID_SURFACE_COMPOSER_COMPAT_H_ diff --git a/chromium/ui/gl/angle_platform_impl.cc b/chromium/ui/gl/angle_platform_impl.cc index 93c3bba1a06..4a0bc9aef25 100644 --- a/chromium/ui/gl/angle_platform_impl.cc +++ b/chromium/ui/gl/angle_platform_impl.cc @@ -19,13 +19,6 @@ namespace angle { namespace { -// This platform context stores user data accessible inside the impl methods. -struct PlatformContext { - CacheProgramCallback cache_program_callback; -}; - -base::LazyInstance<PlatformContext>::DestructorAtExit g_platform_context = - LAZY_INSTANCE_INITIALIZER; // Place the function pointers for ANGLEGetDisplayPlatform and // ANGLEResetDisplayPlatform in read-only memory after being resolved to prevent // them from being tampered with. See crbug.com/771365 for details. @@ -133,26 +126,6 @@ void ANGLEPlatformImpl_histogramBoolean(PlatformMethods* platform, ANGLEPlatformImpl_histogramEnumeration(platform, name, sample ? 1 : 0, 2); } -void ANGLEPlatformImpl_cacheProgram(PlatformMethods* platform, - const ProgramKeyType& key, - size_t program_size, - const uint8_t* program_bytes) { - PlatformContext* context = - reinterpret_cast<PlatformContext*>(platform->context); - if (context && context->cache_program_callback) { - // Convert the key and binary to string form. - std::string key_string(reinterpret_cast<const char*>(&key[0]), - sizeof(ProgramKeyType)); - std::string value_string(reinterpret_cast<const char*>(program_bytes), - program_size); - std::string key_string_64; - std::string value_string_64; - base::Base64Encode(key_string, &key_string_64); - base::Base64Encode(value_string, &value_string_64); - context->cache_program_callback.Run(key_string_64, value_string_64); - } -} - } // anonymous namespace bool InitializePlatform(EGLDisplay display) { @@ -175,7 +148,7 @@ bool InitializePlatform(EGLDisplay display) { PlatformMethods* platformMethods = nullptr; if (!base::UnsanitizedCfiCall(g_angle_get_platform)( static_cast<EGLDisplayType>(display), g_PlatformMethodNames, - g_NumPlatformMethods, &g_platform_context.Get(), &platformMethods)) + g_NumPlatformMethods, nullptr, &platformMethods)) return false; platformMethods->currentTime = ANGLEPlatformImpl_currentTime; platformMethods->addTraceEvent = ANGLEPlatformImpl_addTraceEvent; @@ -194,7 +167,6 @@ bool InitializePlatform(EGLDisplay display) { ANGLEPlatformImpl_monotonicallyIncreasingTime; platformMethods->updateTraceEventDuration = ANGLEPlatformImpl_updateTraceEventDuration; - platformMethods->cacheProgram = ANGLEPlatformImpl_cacheProgram; return true; } @@ -203,19 +175,10 @@ void ResetPlatform(EGLDisplay display) { return; base::UnsanitizedCfiCall(g_angle_reset_platform)( static_cast<EGLDisplayType>(display)); - ResetCacheProgramCallback(); { auto writer = base::AutoWritableMemory::Create(g_angle_reset_platform); *g_angle_reset_platform = nullptr; } } -void SetCacheProgramCallback(CacheProgramCallback callback) { - g_platform_context.Get().cache_program_callback = callback; -} - -void ResetCacheProgramCallback() { - g_platform_context.Get().cache_program_callback.Reset(); -} - } // namespace angle diff --git a/chromium/ui/gl/angle_platform_impl.h b/chromium/ui/gl/angle_platform_impl.h index 21f101a7b3e..777b1d20828 100644 --- a/chromium/ui/gl/angle_platform_impl.h +++ b/chromium/ui/gl/angle_platform_impl.h @@ -17,11 +17,6 @@ namespace angle { GL_EXPORT bool InitializePlatform(EGLDisplay display); GL_EXPORT void ResetPlatform(EGLDisplay display); -using CacheProgramCallback = - ::base::RepeatingCallback<void(const std::string&, const std::string&)>; -GL_EXPORT void SetCacheProgramCallback(CacheProgramCallback callback); -GL_EXPORT void ResetCacheProgramCallback(); - } // namespace angle #endif // UI_GL_ANGLE_PLATFORM_IMPL_H_ diff --git a/chromium/ui/gl/egl_bindings_autogen_mock.cc b/chromium/ui/gl/egl_bindings_autogen_mock.cc index c1e891ce172..5cfcad5cc73 100644 --- a/chromium/ui/gl/egl_bindings_autogen_mock.cc +++ b/chromium/ui/gl/egl_bindings_autogen_mock.cc @@ -391,43 +391,6 @@ MockEGLInterface::Mock_eglPostSubBufferNV(EGLDisplay dpy, return interface_->PostSubBufferNV(dpy, surface, x, y, width, height); } -EGLint GL_BINDING_CALL -MockEGLInterface::Mock_eglProgramCacheGetAttribANGLE(EGLDisplay dpy, - EGLenum attrib) { - MakeEglMockFunctionUnique("eglProgramCacheGetAttribANGLE"); - return interface_->ProgramCacheGetAttribANGLE(dpy, attrib); -} - -void GL_BINDING_CALL -MockEGLInterface::Mock_eglProgramCachePopulateANGLE(EGLDisplay dpy, - const void* key, - EGLint keysize, - const void* binary, - EGLint binarysize) { - MakeEglMockFunctionUnique("eglProgramCachePopulateANGLE"); - interface_->ProgramCachePopulateANGLE(dpy, key, keysize, binary, binarysize); -} - -void GL_BINDING_CALL -MockEGLInterface::Mock_eglProgramCacheQueryANGLE(EGLDisplay dpy, - EGLint index, - void* key, - EGLint* keysize, - void* binary, - EGLint* binarysize) { - MakeEglMockFunctionUnique("eglProgramCacheQueryANGLE"); - interface_->ProgramCacheQueryANGLE(dpy, index, key, keysize, binary, - binarysize); -} - -EGLint GL_BINDING_CALL -MockEGLInterface::Mock_eglProgramCacheResizeANGLE(EGLDisplay dpy, - EGLint limit, - EGLenum mode) { - MakeEglMockFunctionUnique("eglProgramCacheResizeANGLE"); - return interface_->ProgramCacheResizeANGLE(dpy, limit, mode); -} - EGLenum GL_BINDING_CALL MockEGLInterface::Mock_eglQueryAPI(void) { MakeEglMockFunctionUnique("eglQueryAPI"); return interface_->QueryAPI(); @@ -503,6 +466,14 @@ EGLBoolean GL_BINDING_CALL MockEGLInterface::Mock_eglReleaseThread(void) { return interface_->ReleaseThread(); } +void GL_BINDING_CALL +MockEGLInterface::Mock_eglSetBlobCacheFuncsANDROID(EGLDisplay dpy, + EGLSetBlobFuncANDROID set, + EGLGetBlobFuncANDROID get) { + MakeEglMockFunctionUnique("eglSetBlobCacheFuncsANDROID"); + interface_->SetBlobCacheFuncsANDROID(dpy, set, get); +} + EGLBoolean GL_BINDING_CALL MockEGLInterface::Mock_eglStreamAttribKHR(EGLDisplay dpy, EGLStreamKHR stream, @@ -723,18 +694,6 @@ MockEGLInterface::GetGLProcAddress(const char* name) { return reinterpret_cast<GLFunctionPointerType>(Mock_eglMakeCurrent); if (strcmp(name, "eglPostSubBufferNV") == 0) return reinterpret_cast<GLFunctionPointerType>(Mock_eglPostSubBufferNV); - if (strcmp(name, "eglProgramCacheGetAttribANGLE") == 0) - return reinterpret_cast<GLFunctionPointerType>( - Mock_eglProgramCacheGetAttribANGLE); - if (strcmp(name, "eglProgramCachePopulateANGLE") == 0) - return reinterpret_cast<GLFunctionPointerType>( - Mock_eglProgramCachePopulateANGLE); - if (strcmp(name, "eglProgramCacheQueryANGLE") == 0) - return reinterpret_cast<GLFunctionPointerType>( - Mock_eglProgramCacheQueryANGLE); - if (strcmp(name, "eglProgramCacheResizeANGLE") == 0) - return reinterpret_cast<GLFunctionPointerType>( - Mock_eglProgramCacheResizeANGLE); if (strcmp(name, "eglQueryAPI") == 0) return reinterpret_cast<GLFunctionPointerType>(Mock_eglQueryAPI); if (strcmp(name, "eglQueryContext") == 0) @@ -756,6 +715,9 @@ MockEGLInterface::GetGLProcAddress(const char* name) { return reinterpret_cast<GLFunctionPointerType>(Mock_eglReleaseTexImage); if (strcmp(name, "eglReleaseThread") == 0) return reinterpret_cast<GLFunctionPointerType>(Mock_eglReleaseThread); + if (strcmp(name, "eglSetBlobCacheFuncsANDROID") == 0) + return reinterpret_cast<GLFunctionPointerType>( + Mock_eglSetBlobCacheFuncsANDROID); if (strcmp(name, "eglStreamAttribKHR") == 0) return reinterpret_cast<GLFunctionPointerType>(Mock_eglStreamAttribKHR); if (strcmp(name, "eglStreamConsumerAcquireKHR") == 0) diff --git a/chromium/ui/gl/egl_bindings_autogen_mock.h b/chromium/ui/gl/egl_bindings_autogen_mock.h index e3e97c8e048..1d5010ba008 100644 --- a/chromium/ui/gl/egl_bindings_autogen_mock.h +++ b/chromium/ui/gl/egl_bindings_autogen_mock.h @@ -174,23 +174,6 @@ static EGLBoolean GL_BINDING_CALL Mock_eglPostSubBufferNV(EGLDisplay dpy, EGLint y, EGLint width, EGLint height); -static EGLint GL_BINDING_CALL -Mock_eglProgramCacheGetAttribANGLE(EGLDisplay dpy, EGLenum attrib); -static void GL_BINDING_CALL -Mock_eglProgramCachePopulateANGLE(EGLDisplay dpy, - const void* key, - EGLint keysize, - const void* binary, - EGLint binarysize); -static void GL_BINDING_CALL Mock_eglProgramCacheQueryANGLE(EGLDisplay dpy, - EGLint index, - void* key, - EGLint* keysize, - void* binary, - EGLint* binarysize); -static EGLint GL_BINDING_CALL Mock_eglProgramCacheResizeANGLE(EGLDisplay dpy, - EGLint limit, - EGLenum mode); static EGLenum GL_BINDING_CALL Mock_eglQueryAPI(void); static EGLBoolean GL_BINDING_CALL Mock_eglQueryContext(EGLDisplay dpy, EGLContext ctx, @@ -222,6 +205,10 @@ static EGLBoolean GL_BINDING_CALL Mock_eglReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer); static EGLBoolean GL_BINDING_CALL Mock_eglReleaseThread(void); +static void GL_BINDING_CALL +Mock_eglSetBlobCacheFuncsANDROID(EGLDisplay dpy, + EGLSetBlobFuncANDROID set, + EGLGetBlobFuncANDROID get); static EGLBoolean GL_BINDING_CALL Mock_eglStreamAttribKHR(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, diff --git a/chromium/ui/gl/generate_bindings.py b/chromium/ui/gl/generate_bindings.py index c6b5d458d80..864919616df 100755 --- a/chromium/ui/gl/generate_bindings.py +++ b/chromium/ui/gl/generate_bindings.py @@ -238,10 +238,6 @@ GL_FUNCTIONS = [ 'names': ['glCompileShader'], 'arguments': 'GLuint shader', }, { 'return_type': 'void', - 'versions': [{ 'name': 'glCompressedCopyTextureCHROMIUM', - 'extensions': ['GL_CHROMIUM_copy_compressed_texture'], }], - 'arguments': 'GLuint sourceId, GLuint destId', }, -{ 'return_type': 'void', 'names': ['glCompressedTexImage2D'], 'arguments': 'GLenum target, GLint level, GLenum internalformat, GLsizei width, ' @@ -1329,10 +1325,15 @@ GL_FUNCTIONS = [ { 'name': 'glMatrixLoadIdentityCHROMIUM', 'extensions': ['GL_CHROMIUM_path_rendering'] }], 'arguments': 'GLenum matrixMode' }, +{'return_type': 'void', + 'known_as': 'glMaxShaderCompilerThreadsKHR', + 'versions': [{ 'name': 'glMaxShaderCompilerThreadsKHR', + 'extensions': ['GL_KHR_parallel_shader_compile'] }], + 'arguments': 'GLuint count', }, { 'return_type': 'void', 'names': ['glMemoryBarrierByRegion'], 'arguments': 'GLbitfield barriers', }, -{ 'return_type': 'void', +{'return_type': 'void', 'known_as': 'glMemoryBarrierEXT', 'versions': [{ 'name': 'glMemoryBarrier', 'extensions': ['GL_ARB_shader_image_load_store'] }, @@ -2333,24 +2334,6 @@ EGL_FUNCTIONS = [ 'names': ['eglPostSubBufferNV'], 'arguments': 'EGLDisplay dpy, EGLSurface surface, ' 'EGLint x, EGLint y, EGLint width, EGLint height', }, -{ 'return_type': 'EGLint', - 'versions': [{ 'name': 'eglProgramCacheGetAttribANGLE', - 'extensions': ['EGL_ANGLE_program_cache_control'] }], - 'arguments': 'EGLDisplay dpy, EGLenum attrib', }, -{ 'return_type': 'void', - 'versions': [{ 'name': 'eglProgramCachePopulateANGLE', - 'extensions': ['EGL_ANGLE_program_cache_control'] }], - 'arguments': 'EGLDisplay dpy, const void* key, ' - 'EGLint keysize, const void* binary, EGLint binarysize', }, -{ 'return_type': 'void', - 'versions': [{ 'name': 'eglProgramCacheQueryANGLE', - 'extensions': ['EGL_ANGLE_program_cache_control'] }], - 'arguments': 'EGLDisplay dpy, EGLint index, ' - 'void* key, EGLint* keysize, void* binary, EGLint* binarysize', }, -{ 'return_type': 'EGLint', - 'versions': [{ 'name': 'eglProgramCacheResizeANGLE', - 'extensions': ['EGL_ANGLE_program_cache_control'] }], - 'arguments': 'EGLDisplay dpy, EGLint limit, EGLenum mode', }, { 'return_type': 'EGLenum', 'names': ['eglQueryAPI'], 'arguments': 'void', }, @@ -2392,6 +2375,11 @@ EGL_FUNCTIONS = [ { 'return_type': 'EGLBoolean', 'names': ['eglReleaseThread'], 'arguments': 'void', }, +{ 'return_type': 'void', + 'versions': [{ 'name': 'eglSetBlobCacheFuncsANDROID', + 'extensions': ['EGL_ANDROID_blob_cache'] }], + 'arguments': + 'EGLDisplay dpy, EGLSetBlobFuncANDROID set, EGLGetBlobFuncANDROID get' }, { 'return_type': 'EGLBoolean', 'versions': [{ 'name': 'eglStreamAttribKHR', 'extensions': ['EGL_KHR_stream'] }], @@ -3217,6 +3205,9 @@ void DriverEGL::InitializeExtensionBindings() { r'(const )?[a-zA-Z0-9_]+\* ([a-zA-Z0-9_]+)', r'CONSTVOID_\2', log_argument_names) log_argument_names = re.sub( + r'(const )?EGL[GS]etBlobFuncANDROID ([a-zA-Z0-9_]+)', + r'FUNCPTR_\2', log_argument_names) + log_argument_names = re.sub( r'(?<!E)GLboolean ([a-zA-Z0-9_]+)', r'GLboolean_\1', log_argument_names) log_argument_names = re.sub( r'GLDEBUGPROC ([a-zA-Z0-9_]+)', @@ -3239,6 +3230,9 @@ void DriverEGL::InitializeExtensionBindings() { r'CONSTVOID_([a-zA-Z0-9_]+)', r'static_cast<const void*>(\1)', log_argument_names) log_argument_names = re.sub( + r'FUNCPTR_([a-zA-Z0-9_]+)', + r'reinterpret_cast<const void*>(\1)', log_argument_names) + log_argument_names = re.sub( r'CONSTCHAR_([a-zA-Z0-9_]+)', r'\1', log_argument_names) log_argument_names = re.sub( r'GLboolean_([a-zA-Z0-9_]+)', r'GLEnums::GetStringBool(\1)', @@ -3454,7 +3448,7 @@ def GenerateEnumUtils(out_file, input_filenames): if m: name = m.group(1) value = m.group(2) - if len(value) <= 10: + if len(value) <= 10 and value.startswith('0x'): if not value in dict: dict[value] = name # check our own _CHROMIUM macro conflicts with khronos GL headers. diff --git a/chromium/ui/gl/gl_bindings.h b/chromium/ui/gl/gl_bindings.h index 429d277d2f5..ae88b309acf 100644 --- a/chromium/ui/gl/gl_bindings.h +++ b/chromium/ui/gl/gl_bindings.h @@ -413,6 +413,12 @@ #define GL_FRAMEBUFFER_FLIP_Y_MESA 0x8BBB #endif /* GL_MESA_framebuffer_flip_y */ +#ifndef GL_KHR_parallel_shader_compile +#define GL_KHR_parallel_shader_compile 1 +#define GL_MAX_SHADER_COMPILER_THREADS_KHR 0x91B0 +#define GL_COMPLETION_STATUS_KHR 0x91B1 +#endif /* GL_KHR_parallel_shader_compile */ + #define GL_GLEXT_PROTOTYPES 1 #if defined(OS_WIN) @@ -434,6 +440,8 @@ #define GL_MAX_VIEWS_OVR 0x9631 #define GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR 0x9633 +#define GL_QUERY_RESULT_AVAILABLE_NO_FLUSH_CHROMIUM_EXT 0x8868 + // Forward declare EGL types. typedef uint64_t EGLuint64CHROMIUM; diff --git a/chromium/ui/gl/gl_bindings_api_autogen_egl.h b/chromium/ui/gl/gl_bindings_api_autogen_egl.h index 8b1fa45a636..7e3c0efbc6f 100644 --- a/chromium/ui/gl/gl_bindings_api_autogen_egl.h +++ b/chromium/ui/gl/gl_bindings_api_autogen_egl.h @@ -150,21 +150,6 @@ EGLBoolean eglPostSubBufferNVFn(EGLDisplay dpy, EGLint y, EGLint width, EGLint height) override; -EGLint eglProgramCacheGetAttribANGLEFn(EGLDisplay dpy, EGLenum attrib) override; -void eglProgramCachePopulateANGLEFn(EGLDisplay dpy, - const void* key, - EGLint keysize, - const void* binary, - EGLint binarysize) override; -void eglProgramCacheQueryANGLEFn(EGLDisplay dpy, - EGLint index, - void* key, - EGLint* keysize, - void* binary, - EGLint* binarysize) override; -EGLint eglProgramCacheResizeANGLEFn(EGLDisplay dpy, - EGLint limit, - EGLenum mode) override; EGLenum eglQueryAPIFn(void) override; EGLBoolean eglQueryContextFn(EGLDisplay dpy, EGLContext ctx, @@ -192,6 +177,9 @@ EGLBoolean eglReleaseTexImageFn(EGLDisplay dpy, EGLSurface surface, EGLint buffer) override; EGLBoolean eglReleaseThreadFn(void) override; +void eglSetBlobCacheFuncsANDROIDFn(EGLDisplay dpy, + EGLSetBlobFuncANDROID set, + EGLGetBlobFuncANDROID get) override; EGLBoolean eglStreamAttribKHRFn(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, diff --git a/chromium/ui/gl/gl_bindings_api_autogen_gl.h b/chromium/ui/gl/gl_bindings_api_autogen_gl.h index c6c69b1428d..2247ae06d71 100644 --- a/chromium/ui/gl/gl_bindings_api_autogen_gl.h +++ b/chromium/ui/gl/gl_bindings_api_autogen_gl.h @@ -119,7 +119,6 @@ void glColorMaskFn(GLboolean red, GLboolean blue, GLboolean alpha) override; void glCompileShaderFn(GLuint shader) override; -void glCompressedCopyTextureCHROMIUMFn(GLuint sourceId, GLuint destId) override; void glCompressedTexImage2DFn(GLenum target, GLint level, GLenum internalformat, @@ -868,6 +867,7 @@ void* glMapBufferRangeFn(GLenum target, GLbitfield access) override; void glMatrixLoadfEXTFn(GLenum matrixMode, const GLfloat* m) override; void glMatrixLoadIdentityEXTFn(GLenum matrixMode) override; +void glMaxShaderCompilerThreadsKHRFn(GLuint count) override; void glMemoryBarrierByRegionFn(GLbitfield barriers) override; void glMemoryBarrierEXTFn(GLbitfield barriers) override; void glMinSampleShadingFn(GLfloat value) override; diff --git a/chromium/ui/gl/gl_bindings_autogen_egl.cc b/chromium/ui/gl/gl_bindings_autogen_egl.cc index 0c64bd7ea94..42f12de7757 100644 --- a/chromium/ui/gl/gl_bindings_autogen_egl.cc +++ b/chromium/ui/gl/gl_bindings_autogen_egl.cc @@ -147,6 +147,8 @@ void DriverEGL::InitializeExtensionBindings() { gfx::ExtensionSet extensions(gfx::MakeExtensionSet(platform_extensions)); ALLOW_UNUSED_LOCAL(extensions); + ext.b_EGL_ANDROID_blob_cache = + gfx::HasExtension(extensions, "EGL_ANDROID_blob_cache"); ext.b_EGL_ANDROID_get_frame_timestamps = gfx::HasExtension(extensions, "EGL_ANDROID_get_frame_timestamps"); ext.b_EGL_ANDROID_get_native_client_buffer = @@ -155,8 +157,6 @@ void DriverEGL::InitializeExtensionBindings() { gfx::HasExtension(extensions, "EGL_ANDROID_native_fence_sync"); ext.b_EGL_ANGLE_d3d_share_handle_client_buffer = gfx::HasExtension(extensions, "EGL_ANGLE_d3d_share_handle_client_buffer"); - ext.b_EGL_ANGLE_program_cache_control = - gfx::HasExtension(extensions, "EGL_ANGLE_program_cache_control"); ext.b_EGL_ANGLE_query_surface_pointer = gfx::HasExtension(extensions, "EGL_ANGLE_query_surface_pointer"); ext.b_EGL_ANGLE_stream_producer_d3d_texture = @@ -283,30 +283,6 @@ void DriverEGL::InitializeExtensionBindings() { GetGLProcAddress("eglPostSubBufferNV")); } - if (ext.b_EGL_ANGLE_program_cache_control) { - fn.eglProgramCacheGetAttribANGLEFn = - reinterpret_cast<eglProgramCacheGetAttribANGLEProc>( - GetGLProcAddress("eglProgramCacheGetAttribANGLE")); - } - - if (ext.b_EGL_ANGLE_program_cache_control) { - fn.eglProgramCachePopulateANGLEFn = - reinterpret_cast<eglProgramCachePopulateANGLEProc>( - GetGLProcAddress("eglProgramCachePopulateANGLE")); - } - - if (ext.b_EGL_ANGLE_program_cache_control) { - fn.eglProgramCacheQueryANGLEFn = - reinterpret_cast<eglProgramCacheQueryANGLEProc>( - GetGLProcAddress("eglProgramCacheQueryANGLE")); - } - - if (ext.b_EGL_ANGLE_program_cache_control) { - fn.eglProgramCacheResizeANGLEFn = - reinterpret_cast<eglProgramCacheResizeANGLEProc>( - GetGLProcAddress("eglProgramCacheResizeANGLE")); - } - if (ext.b_EGL_KHR_stream) { fn.eglQueryStreamKHRFn = reinterpret_cast<eglQueryStreamKHRProc>( GetGLProcAddress("eglQueryStreamKHR")); @@ -323,6 +299,12 @@ void DriverEGL::InitializeExtensionBindings() { GetGLProcAddress("eglQuerySurfacePointerANGLE")); } + if (ext.b_EGL_ANDROID_blob_cache) { + fn.eglSetBlobCacheFuncsANDROIDFn = + reinterpret_cast<eglSetBlobCacheFuncsANDROIDProc>( + GetGLProcAddress("eglSetBlobCacheFuncsANDROID")); + } + if (ext.b_EGL_KHR_stream) { fn.eglStreamAttribKHRFn = reinterpret_cast<eglStreamAttribKHRProc>( GetGLProcAddress("eglStreamAttribKHR")); @@ -665,36 +647,6 @@ EGLBoolean EGLApiBase::eglPostSubBufferNVFn(EGLDisplay dpy, return driver_->fn.eglPostSubBufferNVFn(dpy, surface, x, y, width, height); } -EGLint EGLApiBase::eglProgramCacheGetAttribANGLEFn(EGLDisplay dpy, - EGLenum attrib) { - return driver_->fn.eglProgramCacheGetAttribANGLEFn(dpy, attrib); -} - -void EGLApiBase::eglProgramCachePopulateANGLEFn(EGLDisplay dpy, - const void* key, - EGLint keysize, - const void* binary, - EGLint binarysize) { - driver_->fn.eglProgramCachePopulateANGLEFn(dpy, key, keysize, binary, - binarysize); -} - -void EGLApiBase::eglProgramCacheQueryANGLEFn(EGLDisplay dpy, - EGLint index, - void* key, - EGLint* keysize, - void* binary, - EGLint* binarysize) { - driver_->fn.eglProgramCacheQueryANGLEFn(dpy, index, key, keysize, binary, - binarysize); -} - -EGLint EGLApiBase::eglProgramCacheResizeANGLEFn(EGLDisplay dpy, - EGLint limit, - EGLenum mode) { - return driver_->fn.eglProgramCacheResizeANGLEFn(dpy, limit, mode); -} - EGLenum EGLApiBase::eglQueryAPIFn(void) { return driver_->fn.eglQueryAPIFn(); } @@ -753,6 +705,12 @@ EGLBoolean EGLApiBase::eglReleaseThreadFn(void) { return driver_->fn.eglReleaseThreadFn(); } +void EGLApiBase::eglSetBlobCacheFuncsANDROIDFn(EGLDisplay dpy, + EGLSetBlobFuncANDROID set, + EGLGetBlobFuncANDROID get) { + driver_->fn.eglSetBlobCacheFuncsANDROIDFn(dpy, set, get); +} + EGLBoolean EGLApiBase::eglStreamAttribKHRFn(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, @@ -1184,42 +1142,6 @@ EGLBoolean TraceEGLApi::eglPostSubBufferNVFn(EGLDisplay dpy, return egl_api_->eglPostSubBufferNVFn(dpy, surface, x, y, width, height); } -EGLint TraceEGLApi::eglProgramCacheGetAttribANGLEFn(EGLDisplay dpy, - EGLenum attrib) { - TRACE_EVENT_BINARY_EFFICIENT0("gpu", - "TraceGLAPI::eglProgramCacheGetAttribANGLE") - return egl_api_->eglProgramCacheGetAttribANGLEFn(dpy, attrib); -} - -void TraceEGLApi::eglProgramCachePopulateANGLEFn(EGLDisplay dpy, - const void* key, - EGLint keysize, - const void* binary, - EGLint binarysize) { - TRACE_EVENT_BINARY_EFFICIENT0("gpu", - "TraceGLAPI::eglProgramCachePopulateANGLE") - egl_api_->eglProgramCachePopulateANGLEFn(dpy, key, keysize, binary, - binarysize); -} - -void TraceEGLApi::eglProgramCacheQueryANGLEFn(EGLDisplay dpy, - EGLint index, - void* key, - EGLint* keysize, - void* binary, - EGLint* binarysize) { - TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::eglProgramCacheQueryANGLE") - egl_api_->eglProgramCacheQueryANGLEFn(dpy, index, key, keysize, binary, - binarysize); -} - -EGLint TraceEGLApi::eglProgramCacheResizeANGLEFn(EGLDisplay dpy, - EGLint limit, - EGLenum mode) { - TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::eglProgramCacheResizeANGLE") - return egl_api_->eglProgramCacheResizeANGLEFn(dpy, limit, mode); -} - EGLenum TraceEGLApi::eglQueryAPIFn(void) { TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::eglQueryAPI") return egl_api_->eglQueryAPIFn(); @@ -1289,6 +1211,14 @@ EGLBoolean TraceEGLApi::eglReleaseThreadFn(void) { return egl_api_->eglReleaseThreadFn(); } +void TraceEGLApi::eglSetBlobCacheFuncsANDROIDFn(EGLDisplay dpy, + EGLSetBlobFuncANDROID set, + EGLGetBlobFuncANDROID get) { + TRACE_EVENT_BINARY_EFFICIENT0("gpu", + "TraceGLAPI::eglSetBlobCacheFuncsANDROID") + egl_api_->eglSetBlobCacheFuncsANDROIDFn(dpy, set, get); +} + EGLBoolean TraceEGLApi::eglStreamAttribKHRFn(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, @@ -1929,54 +1859,6 @@ EGLBoolean DebugEGLApi::eglPostSubBufferNVFn(EGLDisplay dpy, return result; } -EGLint DebugEGLApi::eglProgramCacheGetAttribANGLEFn(EGLDisplay dpy, - EGLenum attrib) { - GL_SERVICE_LOG("eglProgramCacheGetAttribANGLE" - << "(" << dpy << ", " << attrib << ")"); - EGLint result = egl_api_->eglProgramCacheGetAttribANGLEFn(dpy, attrib); - GL_SERVICE_LOG("GL_RESULT: " << result); - return result; -} - -void DebugEGLApi::eglProgramCachePopulateANGLEFn(EGLDisplay dpy, - const void* key, - EGLint keysize, - const void* binary, - EGLint binarysize) { - GL_SERVICE_LOG("eglProgramCachePopulateANGLE" - << "(" << dpy << ", " << static_cast<const void*>(key) << ", " - << keysize << ", " << static_cast<const void*>(binary) << ", " - << binarysize << ")"); - egl_api_->eglProgramCachePopulateANGLEFn(dpy, key, keysize, binary, - binarysize); -} - -void DebugEGLApi::eglProgramCacheQueryANGLEFn(EGLDisplay dpy, - EGLint index, - void* key, - EGLint* keysize, - void* binary, - EGLint* binarysize) { - GL_SERVICE_LOG("eglProgramCacheQueryANGLE" - << "(" << dpy << ", " << index << ", " - << static_cast<const void*>(key) << ", " - << static_cast<const void*>(keysize) << ", " - << static_cast<const void*>(binary) << ", " - << static_cast<const void*>(binarysize) << ")"); - egl_api_->eglProgramCacheQueryANGLEFn(dpy, index, key, keysize, binary, - binarysize); -} - -EGLint DebugEGLApi::eglProgramCacheResizeANGLEFn(EGLDisplay dpy, - EGLint limit, - EGLenum mode) { - GL_SERVICE_LOG("eglProgramCacheResizeANGLE" - << "(" << dpy << ", " << limit << ", " << mode << ")"); - EGLint result = egl_api_->eglProgramCacheResizeANGLEFn(dpy, limit, mode); - GL_SERVICE_LOG("GL_RESULT: " << result); - return result; -} - EGLenum DebugEGLApi::eglQueryAPIFn(void) { GL_SERVICE_LOG("eglQueryAPI" << "(" @@ -2086,6 +1968,15 @@ EGLBoolean DebugEGLApi::eglReleaseThreadFn(void) { return result; } +void DebugEGLApi::eglSetBlobCacheFuncsANDROIDFn(EGLDisplay dpy, + EGLSetBlobFuncANDROID set, + EGLGetBlobFuncANDROID get) { + GL_SERVICE_LOG("eglSetBlobCacheFuncsANDROID" + << "(" << dpy << ", " << reinterpret_cast<const void*>(set) + << ", " << reinterpret_cast<const void*>(get) << ")"); + egl_api_->eglSetBlobCacheFuncsANDROIDFn(dpy, set, get); +} + EGLBoolean DebugEGLApi::eglStreamAttribKHRFn(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, diff --git a/chromium/ui/gl/gl_bindings_autogen_egl.h b/chromium/ui/gl/gl_bindings_autogen_egl.h index 4ee47ddec6c..4ff1248bec5 100644 --- a/chromium/ui/gl/gl_bindings_autogen_egl.h +++ b/chromium/ui/gl/gl_bindings_autogen_egl.h @@ -184,25 +184,6 @@ typedef EGLBoolean(GL_BINDING_CALL* eglPostSubBufferNVProc)(EGLDisplay dpy, EGLint y, EGLint width, EGLint height); -typedef EGLint(GL_BINDING_CALL* eglProgramCacheGetAttribANGLEProc)( - EGLDisplay dpy, - EGLenum attrib); -typedef void(GL_BINDING_CALL* eglProgramCachePopulateANGLEProc)( - EGLDisplay dpy, - const void* key, - EGLint keysize, - const void* binary, - EGLint binarysize); -typedef void(GL_BINDING_CALL* eglProgramCacheQueryANGLEProc)( - EGLDisplay dpy, - EGLint index, - void* key, - EGLint* keysize, - void* binary, - EGLint* binarysize); -typedef EGLint(GL_BINDING_CALL* eglProgramCacheResizeANGLEProc)(EGLDisplay dpy, - EGLint limit, - EGLenum mode); typedef EGLenum(GL_BINDING_CALL* eglQueryAPIProc)(void); typedef EGLBoolean(GL_BINDING_CALL* eglQueryContextProc)(EGLDisplay dpy, EGLContext ctx, @@ -234,6 +215,10 @@ typedef EGLBoolean(GL_BINDING_CALL* eglReleaseTexImageProc)(EGLDisplay dpy, EGLSurface surface, EGLint buffer); typedef EGLBoolean(GL_BINDING_CALL* eglReleaseThreadProc)(void); +typedef void(GL_BINDING_CALL* eglSetBlobCacheFuncsANDROIDProc)( + EGLDisplay dpy, + EGLSetBlobFuncANDROID set, + EGLGetBlobFuncANDROID get); typedef EGLBoolean(GL_BINDING_CALL* eglStreamAttribKHRProc)(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, @@ -281,11 +266,11 @@ typedef EGLint(GL_BINDING_CALL* eglWaitSyncKHRProc)(EGLDisplay dpy, struct ExtensionsEGL { bool b_EGL_EXT_platform_base; bool b_EGL_KHR_debug; + bool b_EGL_ANDROID_blob_cache; bool b_EGL_ANDROID_get_frame_timestamps; bool b_EGL_ANDROID_get_native_client_buffer; bool b_EGL_ANDROID_native_fence_sync; bool b_EGL_ANGLE_d3d_share_handle_client_buffer; - bool b_EGL_ANGLE_program_cache_control; bool b_EGL_ANGLE_query_surface_pointer; bool b_EGL_ANGLE_stream_producer_d3d_texture; bool b_EGL_ANGLE_surface_d3d_texture_2d_share_handle; @@ -355,10 +340,6 @@ struct ProcsEGL { eglLabelObjectKHRProc eglLabelObjectKHRFn; eglMakeCurrentProc eglMakeCurrentFn; eglPostSubBufferNVProc eglPostSubBufferNVFn; - eglProgramCacheGetAttribANGLEProc eglProgramCacheGetAttribANGLEFn; - eglProgramCachePopulateANGLEProc eglProgramCachePopulateANGLEFn; - eglProgramCacheQueryANGLEProc eglProgramCacheQueryANGLEFn; - eglProgramCacheResizeANGLEProc eglProgramCacheResizeANGLEFn; eglQueryAPIProc eglQueryAPIFn; eglQueryContextProc eglQueryContextFn; eglQueryDebugKHRProc eglQueryDebugKHRFn; @@ -369,6 +350,7 @@ struct ProcsEGL { eglQuerySurfacePointerANGLEProc eglQuerySurfacePointerANGLEFn; eglReleaseTexImageProc eglReleaseTexImageFn; eglReleaseThreadProc eglReleaseThreadFn; + eglSetBlobCacheFuncsANDROIDProc eglSetBlobCacheFuncsANDROIDFn; eglStreamAttribKHRProc eglStreamAttribKHRFn; eglStreamConsumerAcquireKHRProc eglStreamConsumerAcquireKHRFn; eglStreamConsumerGLTextureExternalAttribsNVProc @@ -543,22 +525,6 @@ class GL_EXPORT EGLApi { EGLint y, EGLint width, EGLint height) = 0; - virtual EGLint eglProgramCacheGetAttribANGLEFn(EGLDisplay dpy, - EGLenum attrib) = 0; - virtual void eglProgramCachePopulateANGLEFn(EGLDisplay dpy, - const void* key, - EGLint keysize, - const void* binary, - EGLint binarysize) = 0; - virtual void eglProgramCacheQueryANGLEFn(EGLDisplay dpy, - EGLint index, - void* key, - EGLint* keysize, - void* binary, - EGLint* binarysize) = 0; - virtual EGLint eglProgramCacheResizeANGLEFn(EGLDisplay dpy, - EGLint limit, - EGLenum mode) = 0; virtual EGLenum eglQueryAPIFn(void) = 0; virtual EGLBoolean eglQueryContextFn(EGLDisplay dpy, EGLContext ctx, @@ -586,6 +552,9 @@ class GL_EXPORT EGLApi { EGLSurface surface, EGLint buffer) = 0; virtual EGLBoolean eglReleaseThreadFn(void) = 0; + virtual void eglSetBlobCacheFuncsANDROIDFn(EGLDisplay dpy, + EGLSetBlobFuncANDROID set, + EGLGetBlobFuncANDROID get) = 0; virtual EGLBoolean eglStreamAttribKHRFn(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, @@ -690,14 +659,6 @@ class GL_EXPORT EGLApi { #define eglLabelObjectKHR ::gl::g_current_egl_context->eglLabelObjectKHRFn #define eglMakeCurrent ::gl::g_current_egl_context->eglMakeCurrentFn #define eglPostSubBufferNV ::gl::g_current_egl_context->eglPostSubBufferNVFn -#define eglProgramCacheGetAttribANGLE \ - ::gl::g_current_egl_context->eglProgramCacheGetAttribANGLEFn -#define eglProgramCachePopulateANGLE \ - ::gl::g_current_egl_context->eglProgramCachePopulateANGLEFn -#define eglProgramCacheQueryANGLE \ - ::gl::g_current_egl_context->eglProgramCacheQueryANGLEFn -#define eglProgramCacheResizeANGLE \ - ::gl::g_current_egl_context->eglProgramCacheResizeANGLEFn #define eglQueryAPI ::gl::g_current_egl_context->eglQueryAPIFn #define eglQueryContext ::gl::g_current_egl_context->eglQueryContextFn #define eglQueryDebugKHR ::gl::g_current_egl_context->eglQueryDebugKHRFn @@ -709,6 +670,8 @@ class GL_EXPORT EGLApi { ::gl::g_current_egl_context->eglQuerySurfacePointerANGLEFn #define eglReleaseTexImage ::gl::g_current_egl_context->eglReleaseTexImageFn #define eglReleaseThread ::gl::g_current_egl_context->eglReleaseThreadFn +#define eglSetBlobCacheFuncsANDROID \ + ::gl::g_current_egl_context->eglSetBlobCacheFuncsANDROIDFn #define eglStreamAttribKHR ::gl::g_current_egl_context->eglStreamAttribKHRFn #define eglStreamConsumerAcquireKHR \ ::gl::g_current_egl_context->eglStreamConsumerAcquireKHRFn diff --git a/chromium/ui/gl/gl_bindings_autogen_gl.cc b/chromium/ui/gl/gl_bindings_autogen_gl.cc index 6fca919e161..9666e7b3320 100644 --- a/chromium/ui/gl/gl_bindings_autogen_gl.cc +++ b/chromium/ui/gl/gl_bindings_autogen_gl.cc @@ -334,10 +334,6 @@ void DriverGL::InitializeDynamicBindings(const GLVersionInfo* ver, gfx::HasExtension(extensions, "GL_ARB_vertex_array_object"); ext.b_GL_CHROMIUM_bind_uniform_location = gfx::HasExtension(extensions, "GL_CHROMIUM_bind_uniform_location"); - ext.b_GL_CHROMIUM_compressed_copy_texture = - gfx::HasExtension(extensions, "GL_CHROMIUM_compressed_copy_texture"); - ext.b_GL_CHROMIUM_copy_compressed_texture = - gfx::HasExtension(extensions, "GL_CHROMIUM_copy_compressed_texture"); ext.b_GL_CHROMIUM_copy_texture = gfx::HasExtension(extensions, "GL_CHROMIUM_copy_texture"); ext.b_GL_CHROMIUM_framebuffer_mixed_samples = @@ -400,6 +396,8 @@ void DriverGL::InitializeDynamicBindings(const GLVersionInfo* ver, ext.b_GL_KHR_blend_equation_advanced = gfx::HasExtension(extensions, "GL_KHR_blend_equation_advanced"); ext.b_GL_KHR_debug = gfx::HasExtension(extensions, "GL_KHR_debug"); + ext.b_GL_KHR_parallel_shader_compile = + gfx::HasExtension(extensions, "GL_KHR_parallel_shader_compile"); ext.b_GL_KHR_robustness = gfx::HasExtension(extensions, "GL_KHR_robustness"); ext.b_GL_NV_blend_equation_advanced = gfx::HasExtension(extensions, "GL_NV_blend_equation_advanced"); @@ -617,13 +615,6 @@ void DriverGL::InitializeDynamicBindings(const GLVersionInfo* ver, GetGLProcAddress("glClientWaitSync")); } - if (ext.b_GL_CHROMIUM_copy_compressed_texture || - ext.b_GL_CHROMIUM_compressed_copy_texture) { - fn.glCompressedCopyTextureCHROMIUMFn = - reinterpret_cast<glCompressedCopyTextureCHROMIUMProc>( - GetGLProcAddress("glCompressedCopyTextureCHROMIUM")); - } - if (ext.b_GL_ANGLE_robust_client_memory) { fn.glCompressedTexImage2DRobustANGLEFn = reinterpret_cast<glCompressedTexImage2DRobustANGLEProc>( @@ -1839,6 +1830,12 @@ void DriverGL::InitializeDynamicBindings(const GLVersionInfo* ver, GetGLProcAddress("glMatrixLoadIdentityCHROMIUM")); } + if (ext.b_GL_KHR_parallel_shader_compile) { + fn.glMaxShaderCompilerThreadsKHRFn = + reinterpret_cast<glMaxShaderCompilerThreadsKHRProc>( + GetGLProcAddress("glMaxShaderCompilerThreadsKHR")); + } + if (ver->IsAtLeastGLES(3u, 1u) || ver->IsAtLeastGL(4u, 5u)) { fn.glMemoryBarrierByRegionFn = reinterpret_cast<glMemoryBarrierByRegionProc>( @@ -2918,11 +2915,6 @@ void GLApiBase::glCompileShaderFn(GLuint shader) { driver_->fn.glCompileShaderFn(shader); } -void GLApiBase::glCompressedCopyTextureCHROMIUMFn(GLuint sourceId, - GLuint destId) { - driver_->fn.glCompressedCopyTextureCHROMIUMFn(sourceId, destId); -} - void GLApiBase::glCompressedTexImage2DFn(GLenum target, GLint level, GLenum internalformat, @@ -4478,6 +4470,10 @@ void GLApiBase::glMatrixLoadIdentityEXTFn(GLenum matrixMode) { driver_->fn.glMatrixLoadIdentityEXTFn(matrixMode); } +void GLApiBase::glMaxShaderCompilerThreadsKHRFn(GLuint count) { + driver_->fn.glMaxShaderCompilerThreadsKHRFn(count); +} + void GLApiBase::glMemoryBarrierByRegionFn(GLbitfield barriers) { driver_->fn.glMemoryBarrierByRegionFn(barriers); } @@ -5995,13 +5991,6 @@ void TraceGLApi::glCompileShaderFn(GLuint shader) { gl_api_->glCompileShaderFn(shader); } -void TraceGLApi::glCompressedCopyTextureCHROMIUMFn(GLuint sourceId, - GLuint destId) { - TRACE_EVENT_BINARY_EFFICIENT0("gpu", - "TraceGLAPI::glCompressedCopyTextureCHROMIUM") - gl_api_->glCompressedCopyTextureCHROMIUMFn(sourceId, destId); -} - void TraceGLApi::glCompressedTexImage2DFn(GLenum target, GLint level, GLenum internalformat, @@ -7836,6 +7825,12 @@ void TraceGLApi::glMatrixLoadIdentityEXTFn(GLenum matrixMode) { gl_api_->glMatrixLoadIdentityEXTFn(matrixMode); } +void TraceGLApi::glMaxShaderCompilerThreadsKHRFn(GLuint count) { + TRACE_EVENT_BINARY_EFFICIENT0("gpu", + "TraceGLAPI::glMaxShaderCompilerThreadsKHR") + gl_api_->glMaxShaderCompilerThreadsKHRFn(count); +} + void TraceGLApi::glMemoryBarrierByRegionFn(GLbitfield barriers) { TRACE_EVENT_BINARY_EFFICIENT0("gpu", "TraceGLAPI::glMemoryBarrierByRegion") gl_api_->glMemoryBarrierByRegionFn(barriers); @@ -9638,13 +9633,6 @@ void DebugGLApi::glCompileShaderFn(GLuint shader) { gl_api_->glCompileShaderFn(shader); } -void DebugGLApi::glCompressedCopyTextureCHROMIUMFn(GLuint sourceId, - GLuint destId) { - GL_SERVICE_LOG("glCompressedCopyTextureCHROMIUM" - << "(" << sourceId << ", " << destId << ")"); - gl_api_->glCompressedCopyTextureCHROMIUMFn(sourceId, destId); -} - void DebugGLApi::glCompressedTexImage2DFn(GLenum target, GLint level, GLenum internalformat, @@ -12094,6 +12082,12 @@ void DebugGLApi::glMatrixLoadIdentityEXTFn(GLenum matrixMode) { gl_api_->glMatrixLoadIdentityEXTFn(matrixMode); } +void DebugGLApi::glMaxShaderCompilerThreadsKHRFn(GLuint count) { + GL_SERVICE_LOG("glMaxShaderCompilerThreadsKHR" + << "(" << count << ")"); + gl_api_->glMaxShaderCompilerThreadsKHRFn(count); +} + void DebugGLApi::glMemoryBarrierByRegionFn(GLbitfield barriers) { GL_SERVICE_LOG("glMemoryBarrierByRegion" << "(" << barriers << ")"); @@ -14209,11 +14203,6 @@ void NoContextGLApi::glCompileShaderFn(GLuint shader) { NoContextHelper("glCompileShader"); } -void NoContextGLApi::glCompressedCopyTextureCHROMIUMFn(GLuint sourceId, - GLuint destId) { - NoContextHelper("glCompressedCopyTextureCHROMIUM"); -} - void NoContextGLApi::glCompressedTexImage2DFn(GLenum target, GLint level, GLenum internalformat, @@ -15740,6 +15729,10 @@ void NoContextGLApi::glMatrixLoadIdentityEXTFn(GLenum matrixMode) { NoContextHelper("glMatrixLoadIdentityEXT"); } +void NoContextGLApi::glMaxShaderCompilerThreadsKHRFn(GLuint count) { + NoContextHelper("glMaxShaderCompilerThreadsKHR"); +} + void NoContextGLApi::glMemoryBarrierByRegionFn(GLbitfield barriers) { NoContextHelper("glMemoryBarrierByRegion"); } diff --git a/chromium/ui/gl/gl_bindings_autogen_gl.h b/chromium/ui/gl/gl_bindings_autogen_gl.h index fe9742ab623..7de89ead0e7 100644 --- a/chromium/ui/gl/gl_bindings_autogen_gl.h +++ b/chromium/ui/gl/gl_bindings_autogen_gl.h @@ -136,9 +136,6 @@ typedef void(GL_BINDING_CALL* glColorMaskProc)(GLboolean red, GLboolean blue, GLboolean alpha); typedef void(GL_BINDING_CALL* glCompileShaderProc)(GLuint shader); -typedef void(GL_BINDING_CALL* glCompressedCopyTextureCHROMIUMProc)( - GLuint sourceId, - GLuint destId); typedef void(GL_BINDING_CALL* glCompressedTexImage2DProc)(GLenum target, GLint level, GLenum internalformat, @@ -1023,6 +1020,7 @@ typedef void*(GL_BINDING_CALL* glMapBufferRangeProc)(GLenum target, typedef void(GL_BINDING_CALL* glMatrixLoadfEXTProc)(GLenum matrixMode, const GLfloat* m); typedef void(GL_BINDING_CALL* glMatrixLoadIdentityEXTProc)(GLenum matrixMode); +typedef void(GL_BINDING_CALL* glMaxShaderCompilerThreadsKHRProc)(GLuint count); typedef void(GL_BINDING_CALL* glMemoryBarrierByRegionProc)(GLbitfield barriers); typedef void(GL_BINDING_CALL* glMemoryBarrierEXTProc)(GLbitfield barriers); typedef void(GL_BINDING_CALL* glMinSampleShadingProc)(GLfloat value); @@ -1778,8 +1776,6 @@ struct ExtensionsGL { bool b_GL_ARB_transform_feedback2; bool b_GL_ARB_vertex_array_object; bool b_GL_CHROMIUM_bind_uniform_location; - bool b_GL_CHROMIUM_compressed_copy_texture; - bool b_GL_CHROMIUM_copy_compressed_texture; bool b_GL_CHROMIUM_copy_texture; bool b_GL_CHROMIUM_framebuffer_mixed_samples; bool b_GL_CHROMIUM_gles_depth_binding_hack; @@ -1812,6 +1808,7 @@ struct ExtensionsGL { bool b_GL_INTEL_framebuffer_CMAA; bool b_GL_KHR_blend_equation_advanced; bool b_GL_KHR_debug; + bool b_GL_KHR_parallel_shader_compile; bool b_GL_KHR_robustness; bool b_GL_NV_blend_equation_advanced; bool b_GL_NV_fence; @@ -1871,7 +1868,6 @@ struct ProcsGL { glClientWaitSyncProc glClientWaitSyncFn; glColorMaskProc glColorMaskFn; glCompileShaderProc glCompileShaderFn; - glCompressedCopyTextureCHROMIUMProc glCompressedCopyTextureCHROMIUMFn; glCompressedTexImage2DProc glCompressedTexImage2DFn; glCompressedTexImage2DRobustANGLEProc glCompressedTexImage2DRobustANGLEFn; glCompressedTexImage3DProc glCompressedTexImage3DFn; @@ -2116,6 +2112,7 @@ struct ProcsGL { glMapBufferRangeProc glMapBufferRangeFn; glMatrixLoadfEXTProc glMatrixLoadfEXTFn; glMatrixLoadIdentityEXTProc glMatrixLoadIdentityEXTFn; + glMaxShaderCompilerThreadsKHRProc glMaxShaderCompilerThreadsKHRFn; glMemoryBarrierByRegionProc glMemoryBarrierByRegionFn; glMemoryBarrierEXTProc glMemoryBarrierEXTFn; glMinSampleShadingProc glMinSampleShadingFn; @@ -2416,8 +2413,6 @@ class GL_EXPORT GLApi { GLboolean blue, GLboolean alpha) = 0; virtual void glCompileShaderFn(GLuint shader) = 0; - virtual void glCompressedCopyTextureCHROMIUMFn(GLuint sourceId, - GLuint destId) = 0; virtual void glCompressedTexImage2DFn(GLenum target, GLint level, GLenum internalformat, @@ -3194,6 +3189,7 @@ class GL_EXPORT GLApi { GLbitfield access) = 0; virtual void glMatrixLoadfEXTFn(GLenum matrixMode, const GLfloat* m) = 0; virtual void glMatrixLoadIdentityEXTFn(GLenum matrixMode) = 0; + virtual void glMaxShaderCompilerThreadsKHRFn(GLuint count) = 0; virtual void glMemoryBarrierByRegionFn(GLbitfield barriers) = 0; virtual void glMemoryBarrierEXTFn(GLbitfield barriers) = 0; virtual void glMinSampleShadingFn(GLfloat value) = 0; @@ -3916,8 +3912,6 @@ class GL_EXPORT GLApi { #define glClientWaitSync ::gl::g_current_gl_context->glClientWaitSyncFn #define glColorMask ::gl::g_current_gl_context->glColorMaskFn #define glCompileShader ::gl::g_current_gl_context->glCompileShaderFn -#define glCompressedCopyTextureCHROMIUM \ - ::gl::g_current_gl_context->glCompressedCopyTextureCHROMIUMFn #define glCompressedTexImage2D \ ::gl::g_current_gl_context->glCompressedTexImage2DFn #define glCompressedTexImage2DRobustANGLE \ @@ -4269,6 +4263,8 @@ class GL_EXPORT GLApi { #define glMatrixLoadfEXT ::gl::g_current_gl_context->glMatrixLoadfEXTFn #define glMatrixLoadIdentityEXT \ ::gl::g_current_gl_context->glMatrixLoadIdentityEXTFn +#define glMaxShaderCompilerThreadsKHR \ + ::gl::g_current_gl_context->glMaxShaderCompilerThreadsKHRFn #define glMemoryBarrierByRegion \ ::gl::g_current_gl_context->glMemoryBarrierByRegionFn #define glMemoryBarrierEXT ::gl::g_current_gl_context->glMemoryBarrierEXTFn diff --git a/chromium/ui/gl/gl_bindings_autogen_mock.cc b/chromium/ui/gl/gl_bindings_autogen_mock.cc index 0be76ca4c53..206df2d9ecb 100644 --- a/chromium/ui/gl/gl_bindings_autogen_mock.cc +++ b/chromium/ui/gl/gl_bindings_autogen_mock.cc @@ -472,13 +472,6 @@ void GL_BINDING_CALL MockGLInterface::Mock_glCompileShader(GLuint shader) { } void GL_BINDING_CALL -MockGLInterface::Mock_glCompressedCopyTextureCHROMIUM(GLuint sourceId, - GLuint destId) { - MakeGlMockFunctionUnique("glCompressedCopyTextureCHROMIUM"); - interface_->CompressedCopyTextureCHROMIUM(sourceId, destId); -} - -void GL_BINDING_CALL MockGLInterface::Mock_glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, @@ -2942,6 +2935,12 @@ void GL_BINDING_CALL MockGLInterface::Mock_glMatrixLoadfEXT(GLenum matrixMode, } void GL_BINDING_CALL +MockGLInterface::Mock_glMaxShaderCompilerThreadsKHR(GLuint count) { + MakeGlMockFunctionUnique("glMaxShaderCompilerThreadsKHR"); + interface_->MaxShaderCompilerThreadsKHR(count); +} + +void GL_BINDING_CALL MockGLInterface::Mock_glMemoryBarrier(GLbitfield barriers) { MakeGlMockFunctionUnique("glMemoryBarrier"); interface_->MemoryBarrierEXT(barriers); @@ -4926,9 +4925,6 @@ MockGLInterface::GetGLProcAddress(const char* name) { return reinterpret_cast<GLFunctionPointerType>(Mock_glColorMask); if (strcmp(name, "glCompileShader") == 0) return reinterpret_cast<GLFunctionPointerType>(Mock_glCompileShader); - if (strcmp(name, "glCompressedCopyTextureCHROMIUM") == 0) - return reinterpret_cast<GLFunctionPointerType>( - Mock_glCompressedCopyTextureCHROMIUM); if (strcmp(name, "glCompressedTexImage2D") == 0) return reinterpret_cast<GLFunctionPointerType>(Mock_glCompressedTexImage2D); if (strcmp(name, "glCompressedTexImage2DRobustANGLE") == 0) @@ -5650,6 +5646,9 @@ MockGLInterface::GetGLProcAddress(const char* name) { return reinterpret_cast<GLFunctionPointerType>(Mock_glMatrixLoadfCHROMIUM); if (strcmp(name, "glMatrixLoadfEXT") == 0) return reinterpret_cast<GLFunctionPointerType>(Mock_glMatrixLoadfEXT); + if (strcmp(name, "glMaxShaderCompilerThreadsKHR") == 0) + return reinterpret_cast<GLFunctionPointerType>( + Mock_glMaxShaderCompilerThreadsKHR); if (strcmp(name, "glMemoryBarrier") == 0) return reinterpret_cast<GLFunctionPointerType>(Mock_glMemoryBarrier); if (strcmp(name, "glMemoryBarrierByRegion") == 0) diff --git a/chromium/ui/gl/gl_bindings_autogen_mock.h b/chromium/ui/gl/gl_bindings_autogen_mock.h index f6e48b21d9c..42545f14529 100644 --- a/chromium/ui/gl/gl_bindings_autogen_mock.h +++ b/chromium/ui/gl/gl_bindings_autogen_mock.h @@ -184,8 +184,6 @@ static void GL_BINDING_CALL Mock_glColorMask(GLboolean red, GLboolean blue, GLboolean alpha); static void GL_BINDING_CALL Mock_glCompileShader(GLuint shader); -static void GL_BINDING_CALL -Mock_glCompressedCopyTextureCHROMIUM(GLuint sourceId, GLuint destId); static void GL_BINDING_CALL Mock_glCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat, @@ -1234,6 +1232,7 @@ static void GL_BINDING_CALL Mock_glMatrixLoadfCHROMIUM(GLenum matrixMode, const GLfloat* m); static void GL_BINDING_CALL Mock_glMatrixLoadfEXT(GLenum matrixMode, const GLfloat* m); +static void GL_BINDING_CALL Mock_glMaxShaderCompilerThreadsKHR(GLuint count); static void GL_BINDING_CALL Mock_glMemoryBarrier(GLbitfield barriers); static void GL_BINDING_CALL Mock_glMemoryBarrierByRegion(GLbitfield barriers); static void GL_BINDING_CALL Mock_glMemoryBarrierEXT(GLbitfield barriers); diff --git a/chromium/ui/gl/gl_context_egl.cc b/chromium/ui/gl/gl_context_egl.cc index 66d5f54d829..a8f1c62ba78 100644 --- a/chromium/ui/gl/gl_context_egl.cc +++ b/chromium/ui/gl/gl_context_egl.cc @@ -27,17 +27,17 @@ #ifndef EGL_CHROMIUM_create_context_bind_generates_resource #define EGL_CHROMIUM_create_context_bind_generates_resource 1 -#define EGL_CONTEXT_BIND_GENERATES_RESOURCE_CHROMIUM 0x3AAD +#define EGL_CONTEXT_BIND_GENERATES_RESOURCE_CHROMIUM 0x33AD #endif /* EGL_CHROMIUM_create_context_bind_generates_resource */ #ifndef EGL_ANGLE_create_context_webgl_compatibility #define EGL_ANGLE_create_context_webgl_compatibility 1 -#define EGL_CONTEXT_WEBGL_COMPATIBILITY_ANGLE 0x3AAC +#define EGL_CONTEXT_WEBGL_COMPATIBILITY_ANGLE 0x33AC #endif /* EGL_ANGLE_create_context_webgl_compatibility */ #ifndef EGL_ANGLE_display_texture_share_group #define EGL_ANGLE_display_texture_share_group 1 -#define EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE 0x3AAF +#define EGL_DISPLAY_TEXTURE_SHARE_GROUP_ANGLE 0x33AF #endif /* EGL_ANGLE_display_texture_share_group */ #ifndef EGL_ANGLE_create_context_client_arrays diff --git a/chromium/ui/gl/gl_context_glx_unittest.cc b/chromium/ui/gl/gl_context_glx_unittest.cc index c393a792c5f..ce3dedb0f15 100644 --- a/chromium/ui/gl/gl_context_glx_unittest.cc +++ b/chromium/ui/gl/gl_context_glx_unittest.cc @@ -9,6 +9,7 @@ #include "ui/gfx/x/x11.h" #include "ui/gfx/x/x11_error_tracker.h" #include "ui/gfx/x/x11_types.h" +#include "ui/gl/gl_bindings.h" #include "ui/gl/gl_surface_glx_x11.h" #include "ui/gl/init/gl_factory.h" #include "ui/gl/test/gl_image_test_support.h" diff --git a/chromium/ui/gl/gl_enums_implementation_autogen.h b/chromium/ui/gl/gl_enums_implementation_autogen.h index 27a69b10cd9..8a576262b60 100644 --- a/chromium/ui/gl/gl_enums_implementation_autogen.h +++ b/chromium/ui/gl/gl_enums_implementation_autogen.h @@ -13,6273 +13,4905 @@ static const GLEnums::EnumToString enum_to_string_table[] = { { - 0, - "GL_FALSE", + 0x00, "GL_CLOSE_PATH_NV", }, { - 0x00, - "GL_CLOSE_PATH_NV", + 0x0000, "GL_POINTS", }, { - 0x0000, - "GL_POINTS", + 0x00000000, "GL_PERFQUERY_SINGLE_CONTEXT_INTEL", }, { - 0x00000000, - "GL_PERFQUERY_SINGLE_CONTEXT_INTEL", + 0x00000001, "GL_SYNC_FLUSH_COMMANDS_BIT_APPLE", }, { - 0x00000001, - "GL_SYNC_FLUSH_COMMANDS_BIT_APPLE", + 0x00000002, "GL_CONTEXT_FLAG_DEBUG_BIT_KHR", }, { - 0x00000002, - "GL_CONTEXT_FLAG_DEBUG_BIT_KHR", + 0x00000004, "GL_GEOMETRY_SHADER_BIT_OES", }, { - 0x00000004, - "GL_GEOMETRY_SHADER_BIT_OES", + 0x00000008, "GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR", }, { - 0x00000008, - "GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR", + 0x00000010, "GL_TESS_EVALUATION_SHADER_BIT_OES", }, { - 0x00000010, - "GL_TESS_EVALUATION_SHADER_BIT_OES", + 0x00000020, "GL_COLOR_BUFFER_BIT5_QCOM", }, { - 0x00000020, - "GL_COLOR_BUFFER_BIT5_QCOM", + 0x00000040, "GL_COLOR_BUFFER_BIT6_QCOM", }, { - 0x00000040, - "GL_COLOR_BUFFER_BIT6_QCOM", + 0x00000080, "GL_COLOR_BUFFER_BIT7_QCOM", }, { - 0x00000080, - "GL_COLOR_BUFFER_BIT7_QCOM", + 0x00000100, "GL_DEPTH_BUFFER_BIT", }, { - 0x00000100, - "GL_DEPTH_BUFFER_BIT", + 0x00000200, "GL_DEPTH_BUFFER_BIT1_QCOM", }, { - 0x00000200, - "GL_DEPTH_BUFFER_BIT1_QCOM", + 0x00000400, "GL_STENCIL_BUFFER_BIT", }, { - 0x00000400, - "GL_STENCIL_BUFFER_BIT", + 0x00000800, "GL_DEPTH_BUFFER_BIT3_QCOM", }, { - 0x00000800, - "GL_DEPTH_BUFFER_BIT3_QCOM", + 0x00001000, "GL_DEPTH_BUFFER_BIT4_QCOM", }, { - 0x00001000, - "GL_DEPTH_BUFFER_BIT4_QCOM", + 0x00002000, "GL_DEPTH_BUFFER_BIT5_QCOM", }, { - 0x00002000, - "GL_DEPTH_BUFFER_BIT5_QCOM", + 0x00004000, "GL_COLOR_BUFFER_BIT", }, { - 0x00004000, - "GL_COLOR_BUFFER_BIT", + 0x00008000, "GL_COVERAGE_BUFFER_BIT_NV", }, { - 0x00008000, - "GL_COVERAGE_BUFFER_BIT_NV", + 0x0001, "GL_LINES", }, { - 0x0001, - "GL_LINES", + 0x00010000, "GL_FONT_X_MIN_BOUNDS_BIT_NV", }, { - 0x00010000, - "GL_FONT_X_MIN_BOUNDS_BIT_NV", + 0x0002, "GL_LINE_LOOP", }, { - 0x0002, - "GL_LINE_LOOP", + 0x00020000, "GL_FONT_Y_MIN_BOUNDS_BIT_NV", }, { - 0x00020000, - "GL_FONT_Y_MIN_BOUNDS_BIT_NV", + 0x0003, "GL_LINE_STRIP", }, { - 0x0003, - "GL_LINE_STRIP", + 0x0004, "GL_TRIANGLES", }, { - 0x0004, - "GL_TRIANGLES", + 0x00040000, "GL_FONT_X_MAX_BOUNDS_BIT_NV", }, { - 0x00040000, - "GL_FONT_X_MAX_BOUNDS_BIT_NV", + 0x0005, "GL_TRIANGLE_STRIP", }, { - 0x0005, - "GL_TRIANGLE_STRIP", + 0x0006, "GL_TRIANGLE_FAN", }, { - 0x0006, - "GL_TRIANGLE_FAN", + 0x0007, "GL_QUADS_OES", }, { - 0x0007, - "GL_QUADS_OES", + 0x0008, "GL_MAP_INVALIDATE_BUFFER_BIT_EXT", }, { - 0x0008, - "GL_MAP_INVALIDATE_BUFFER_BIT_EXT", + 0x00080000, "GL_FONT_Y_MAX_BOUNDS_BIT_NV", }, { - 0x00080000, - "GL_FONT_Y_MAX_BOUNDS_BIT_NV", + 0x000A, "GL_LINES_ADJACENCY_OES", }, { - 0x000A, - "GL_LINES_ADJACENCY_OES", + 0x000B, "GL_LINE_STRIP_ADJACENCY_OES", }, { - 0x000B, - "GL_LINE_STRIP_ADJACENCY_OES", + 0x000C, "GL_TRIANGLES_ADJACENCY_OES", }, { - 0x000C, - "GL_TRIANGLES_ADJACENCY_OES", + 0x000D, "GL_TRIANGLE_STRIP_ADJACENCY_OES", }, { - 0x000D, - "GL_TRIANGLE_STRIP_ADJACENCY_OES", + 0x000E, "GL_PATCHES_OES", }, { - 0x000E, - "GL_PATCHES_OES", + 0x0010, "GL_MAP_FLUSH_EXPLICIT_BIT_EXT", }, { - 0x0010, - "GL_MAP_FLUSH_EXPLICIT_BIT_EXT", + 0x00100000, "GL_FONT_UNITS_PER_EM_BIT_NV", }, { - 0x00100000, - "GL_FONT_UNITS_PER_EM_BIT_NV", + 0x0020, "GL_MAP_UNSYNCHRONIZED_BIT_EXT", }, { - 0x0020, - "GL_MAP_UNSYNCHRONIZED_BIT_EXT", + 0x00200000, "GL_FONT_ASCENDER_BIT_NV", }, { - 0x00200000, - "GL_FONT_ASCENDER_BIT_NV", + 0x0040, "GL_MAP_PERSISTENT_BIT_EXT", }, { - 0x0040, - "GL_MAP_PERSISTENT_BIT_EXT", + 0x00400000, "GL_FONT_DESCENDER_BIT_NV", }, { - 0x00400000, - "GL_FONT_DESCENDER_BIT_NV", + 0x0080, "GL_MAP_COHERENT_BIT_EXT", }, { - 0x0080, - "GL_MAP_COHERENT_BIT_EXT", + 0x00800000, "GL_FONT_HEIGHT_BIT_NV", }, { - 0x00800000, - "GL_FONT_HEIGHT_BIT_NV", + 0x01, "GL_BOLD_BIT_NV", }, { - 0x01, - "GL_BOLD_BIT_NV", + 0x0100, "GL_DYNAMIC_STORAGE_BIT_EXT", }, { - 0x0100, - "GL_DYNAMIC_STORAGE_BIT_EXT", + 0x01000000, "GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV", }, { - 0x01000000, - "GL_FONT_MAX_ADVANCE_WIDTH_BIT_NV", + 0x02, "GL_MOVE_TO_NV", }, { - 0x02, - "GL_MOVE_TO_NV", + 0x0200, "GL_NEVER", }, { - 0x0200, - "GL_NEVER", + 0x02000000, "GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV", }, { - 0x02000000, - "GL_FONT_MAX_ADVANCE_HEIGHT_BIT_NV", + 0x0201, "GL_LESS", }, { - 0x0201, - "GL_LESS", + 0x0202, "GL_EQUAL", }, { - 0x0202, - "GL_EQUAL", + 0x0203, "GL_LEQUAL", }, { - 0x0203, - "GL_LEQUAL", + 0x0204, "GL_GREATER", }, { - 0x0204, - "GL_GREATER", + 0x0205, "GL_NOTEQUAL", }, { - 0x0205, - "GL_NOTEQUAL", + 0x0206, "GL_GEQUAL", }, { - 0x0206, - "GL_GEQUAL", + 0x0207, "GL_ALWAYS", }, { - 0x0207, - "GL_ALWAYS", + 0x03, "GL_RELATIVE_MOVE_TO_NV", }, { - 0x03, - "GL_RELATIVE_MOVE_TO_NV", + 0x0300, "GL_SRC_COLOR", }, { - 0x0300, - "GL_SRC_COLOR", + 0x0301, "GL_ONE_MINUS_SRC_COLOR", }, { - 0x0301, - "GL_ONE_MINUS_SRC_COLOR", + 0x0302, "GL_SRC_ALPHA", }, { - 0x0302, - "GL_SRC_ALPHA", + 0x0303, "GL_ONE_MINUS_SRC_ALPHA", }, { - 0x0303, - "GL_ONE_MINUS_SRC_ALPHA", + 0x0304, "GL_DST_ALPHA", }, { - 0x0304, - "GL_DST_ALPHA", + 0x0305, "GL_ONE_MINUS_DST_ALPHA", }, { - 0x0305, - "GL_ONE_MINUS_DST_ALPHA", + 0x0306, "GL_DST_COLOR", }, { - 0x0306, - "GL_DST_COLOR", + 0x0307, "GL_ONE_MINUS_DST_COLOR", }, { - 0x0307, - "GL_ONE_MINUS_DST_COLOR", + 0x0308, "GL_SRC_ALPHA_SATURATE", }, { - 0x0308, - "GL_SRC_ALPHA_SATURATE", + 0x04, "GL_LINE_TO_NV", }, { - 0x04, - "GL_LINE_TO_NV", + 0x04000000, "GL_FONT_UNDERLINE_POSITION_BIT_NV", }, { - 0x04000000, - "GL_FONT_UNDERLINE_POSITION_BIT_NV", + 0x0404, "GL_FRONT", }, { - 0x0404, - "GL_FRONT", + 0x0405, "GL_BACK", }, { - 0x0405, - "GL_BACK", + 0x0408, "GL_FRONT_AND_BACK", }, { - 0x0408, - "GL_FRONT_AND_BACK", + 0x05, "GL_RELATIVE_LINE_TO_NV", }, { - 0x05, - "GL_RELATIVE_LINE_TO_NV", + 0x0500, "GL_INVALID_ENUM", }, { - 0x0500, - "GL_INVALID_ENUM", + 0x0501, "GL_INVALID_VALUE", }, { - 0x0501, - "GL_INVALID_VALUE", + 0x0502, "GL_INVALID_OPERATION", }, { - 0x0502, - "GL_INVALID_OPERATION", + 0x0503, "GL_STACK_OVERFLOW_KHR", }, { - 0x0503, - "GL_STACK_OVERFLOW_KHR", + 0x0504, "GL_STACK_UNDERFLOW_KHR", }, { - 0x0504, - "GL_STACK_UNDERFLOW_KHR", + 0x0505, "GL_OUT_OF_MEMORY", }, { - 0x0505, - "GL_OUT_OF_MEMORY", + 0x0506, "GL_INVALID_FRAMEBUFFER_OPERATION", }, { - 0x0506, - "GL_INVALID_FRAMEBUFFER_OPERATION", + 0x0507, "GL_CONTEXT_LOST_KHR", }, { - 0x0507, - "GL_CONTEXT_LOST_KHR", + 0x06, "GL_HORIZONTAL_LINE_TO_NV", }, { - 0x06, - "GL_HORIZONTAL_LINE_TO_NV", + 0x07, "GL_RELATIVE_HORIZONTAL_LINE_TO_NV", }, { - 0x07, - "GL_RELATIVE_HORIZONTAL_LINE_TO_NV", + 0x08, "GL_VERTICAL_LINE_TO_NV", }, { - 0x08, - "GL_VERTICAL_LINE_TO_NV", + 0x08000000, "GL_FONT_UNDERLINE_THICKNESS_BIT_NV", }, { - 0x08000000, - "GL_FONT_UNDERLINE_THICKNESS_BIT_NV", + 0x09, "GL_RELATIVE_VERTICAL_LINE_TO_NV", }, { - 0x09, - "GL_RELATIVE_VERTICAL_LINE_TO_NV", + 0x0900, "GL_CW", }, { - 0x0900, - "GL_CW", + 0x0901, "GL_CCW", }, { - 0x0901, - "GL_CCW", + 0x0A, "GL_QUADRATIC_CURVE_TO_NV", }, { - 0x0A, - "GL_QUADRATIC_CURVE_TO_NV", + 0x0B, "GL_RELATIVE_QUADRATIC_CURVE_TO_NV", }, { - 0x0B, - "GL_RELATIVE_QUADRATIC_CURVE_TO_NV", + 0x0B21, "GL_LINE_WIDTH", }, { - 0x0B21, - "GL_LINE_WIDTH", + 0x0B40, "GL_POLYGON_MODE_NV", }, { - 0x0B40, - "GL_POLYGON_MODE_NV", + 0x0B44, "GL_CULL_FACE", }, { - 0x0B44, - "GL_CULL_FACE", + 0x0B45, "GL_CULL_FACE_MODE", }, { - 0x0B45, - "GL_CULL_FACE_MODE", + 0x0B46, "GL_FRONT_FACE", }, { - 0x0B46, - "GL_FRONT_FACE", + 0x0B70, "GL_DEPTH_RANGE", }, { - 0x0B70, - "GL_DEPTH_RANGE", + 0x0B71, "GL_DEPTH_TEST", }, { - 0x0B71, - "GL_DEPTH_TEST", + 0x0B72, "GL_DEPTH_WRITEMASK", }, { - 0x0B72, - "GL_DEPTH_WRITEMASK", + 0x0B73, "GL_DEPTH_CLEAR_VALUE", }, { - 0x0B73, - "GL_DEPTH_CLEAR_VALUE", + 0x0B74, "GL_DEPTH_FUNC", }, { - 0x0B74, - "GL_DEPTH_FUNC", + 0x0B90, "GL_STENCIL_TEST", }, { - 0x0B90, - "GL_STENCIL_TEST", + 0x0B91, "GL_STENCIL_CLEAR_VALUE", }, { - 0x0B91, - "GL_STENCIL_CLEAR_VALUE", + 0x0B92, "GL_STENCIL_FUNC", }, { - 0x0B92, - "GL_STENCIL_FUNC", + 0x0B93, "GL_STENCIL_VALUE_MASK", }, { - 0x0B93, - "GL_STENCIL_VALUE_MASK", + 0x0B94, "GL_STENCIL_FAIL", }, { - 0x0B94, - "GL_STENCIL_FAIL", + 0x0B95, "GL_STENCIL_PASS_DEPTH_FAIL", }, { - 0x0B95, - "GL_STENCIL_PASS_DEPTH_FAIL", + 0x0B96, "GL_STENCIL_PASS_DEPTH_PASS", }, { - 0x0B96, - "GL_STENCIL_PASS_DEPTH_PASS", + 0x0B97, "GL_STENCIL_REF", }, { - 0x0B97, - "GL_STENCIL_REF", + 0x0B98, "GL_STENCIL_WRITEMASK", }, { - 0x0B98, - "GL_STENCIL_WRITEMASK", + 0x0BA2, "GL_VIEWPORT", }, { - 0x0BA2, - "GL_VIEWPORT", + 0x0BA3, "GL_PATH_MODELVIEW_STACK_DEPTH_NV", }, { - 0x0BA3, - "GL_PATH_MODELVIEW_STACK_DEPTH_NV", + 0x0BA4, "GL_PATH_PROJECTION_STACK_DEPTH_NV", }, { - 0x0BA4, - "GL_PATH_PROJECTION_STACK_DEPTH_NV", + 0x0BA6, "GL_PATH_MODELVIEW_MATRIX_NV", }, { - 0x0BA6, - "GL_PATH_MODELVIEW_MATRIX_NV", + 0x0BA7, "GL_PATH_PROJECTION_MATRIX_NV", }, { - 0x0BA7, - "GL_PATH_PROJECTION_MATRIX_NV", + 0x0BC0, "GL_ALPHA_TEST_QCOM", }, { - 0x0BC0, - "GL_ALPHA_TEST_QCOM", + 0x0BC1, "GL_ALPHA_TEST_FUNC_QCOM", }, { - 0x0BC1, - "GL_ALPHA_TEST_FUNC_QCOM", + 0x0BC2, "GL_ALPHA_TEST_REF_QCOM", }, { - 0x0BC2, - "GL_ALPHA_TEST_REF_QCOM", + 0x0BD0, "GL_DITHER", }, { - 0x0BD0, - "GL_DITHER", + 0x0BE2, "GL_BLEND", }, { - 0x0BE2, - "GL_BLEND", + 0x0C, "GL_CUBIC_CURVE_TO_NV", }, { - 0x0C, - "GL_CUBIC_CURVE_TO_NV", + 0x0C01, "GL_DRAW_BUFFER_EXT", }, { - 0x0C01, - "GL_DRAW_BUFFER_EXT", + 0x0C02, "GL_READ_BUFFER_EXT", }, { - 0x0C02, - "GL_READ_BUFFER_EXT", + 0x0C10, "GL_SCISSOR_BOX", }, { - 0x0C10, - "GL_SCISSOR_BOX", + 0x0C11, "GL_SCISSOR_TEST", }, { - 0x0C11, - "GL_SCISSOR_TEST", + 0x0C22, "GL_COLOR_CLEAR_VALUE", }, { - 0x0C22, - "GL_COLOR_CLEAR_VALUE", + 0x0C23, "GL_COLOR_WRITEMASK", }, { - 0x0C23, - "GL_COLOR_WRITEMASK", + 0x0CF2, "GL_UNPACK_ROW_LENGTH_EXT", }, { - 0x0CF2, - "GL_UNPACK_ROW_LENGTH_EXT", + 0x0CF3, "GL_UNPACK_SKIP_ROWS_EXT", }, { - 0x0CF3, - "GL_UNPACK_SKIP_ROWS_EXT", + 0x0CF4, "GL_UNPACK_SKIP_PIXELS_EXT", }, { - 0x0CF4, - "GL_UNPACK_SKIP_PIXELS_EXT", + 0x0CF5, "GL_UNPACK_ALIGNMENT", }, { - 0x0CF5, - "GL_UNPACK_ALIGNMENT", + 0x0D, "GL_RELATIVE_CUBIC_CURVE_TO_NV", }, { - 0x0D, - "GL_RELATIVE_CUBIC_CURVE_TO_NV", + 0x0D02, "GL_PACK_ROW_LENGTH", }, { - 0x0D02, - "GL_PACK_ROW_LENGTH", + 0x0D03, "GL_PACK_SKIP_ROWS", }, { - 0x0D03, - "GL_PACK_SKIP_ROWS", + 0x0D04, "GL_PACK_SKIP_PIXELS", }, { - 0x0D04, - "GL_PACK_SKIP_PIXELS", + 0x0D05, "GL_PACK_ALIGNMENT", }, { - 0x0D05, - "GL_PACK_ALIGNMENT", + 0x0D32, "GL_MAX_CLIP_DISTANCES_APPLE", }, { - 0x0D32, - "GL_MAX_CLIP_DISTANCES_APPLE", + 0x0D33, "GL_MAX_TEXTURE_SIZE", }, { - 0x0D33, - "GL_MAX_TEXTURE_SIZE", + 0x0D36, "GL_PATH_MAX_MODELVIEW_STACK_DEPTH_NV", }, { - 0x0D36, - "GL_PATH_MAX_MODELVIEW_STACK_DEPTH_NV", + 0x0D38, "GL_PATH_MAX_PROJECTION_STACK_DEPTH_NV", }, { - 0x0D38, - "GL_PATH_MAX_PROJECTION_STACK_DEPTH_NV", + 0x0D3A, "GL_MAX_VIEWPORT_DIMS", }, { - 0x0D3A, - "GL_MAX_VIEWPORT_DIMS", + 0x0D50, "GL_SUBPIXEL_BITS", }, { - 0x0D50, - "GL_SUBPIXEL_BITS", + 0x0D52, "GL_RED_BITS", }, { - 0x0D52, - "GL_RED_BITS", + 0x0D53, "GL_GREEN_BITS", }, { - 0x0D53, - "GL_GREEN_BITS", + 0x0D54, "GL_BLUE_BITS", }, { - 0x0D54, - "GL_BLUE_BITS", + 0x0D55, "GL_ALPHA_BITS", }, { - 0x0D55, - "GL_ALPHA_BITS", + 0x0D56, "GL_DEPTH_BITS", }, { - 0x0D56, - "GL_DEPTH_BITS", + 0x0D57, "GL_STENCIL_BITS", }, { - 0x0D57, - "GL_STENCIL_BITS", + 0x0DE1, "GL_TEXTURE_2D", }, { - 0x0DE1, - "GL_TEXTURE_2D", + 0x0E, "GL_SMOOTH_QUADRATIC_CURVE_TO_NV", }, { - 0x0E, - "GL_SMOOTH_QUADRATIC_CURVE_TO_NV", + 0x0F, "GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV", }, { - 0x0F, - "GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO_NV", + 0x1, "GL_CA_LAYER_EDGE_LEFT_CHROMIUM", }, { - 0x1, - "GL_CA_LAYER_EDGE_LEFT_CHROMIUM", + 0x10, "GL_SMOOTH_CUBIC_CURVE_TO_NV", }, { - 0x10, - "GL_SMOOTH_CUBIC_CURVE_TO_NV", + 0x100, "GL_GLYPH_HAS_KERNING_BIT_NV", }, { - 0x100, - "GL_GLYPH_HAS_KERNING_BIT_NV", + 0x1000, "GL_TEXTURE_WIDTH", }, { - 0x1000, - "GL_TEXTURE_WIDTH", + 0x10000000, "GL_FONT_HAS_KERNING_BIT_NV", }, { - 0x10000000, - "GL_FONT_HAS_KERNING_BIT_NV", + 0x1001, "GL_TEXTURE_HEIGHT", }, { - 0x1001, - "GL_TEXTURE_HEIGHT", + 0x1003, "GL_TEXTURE_INTERNAL_FORMAT", }, { - 0x1003, - "GL_TEXTURE_INTERNAL_FORMAT", + 0x1004, "GL_TEXTURE_BORDER_COLOR_OES", }, { - 0x1004, - "GL_TEXTURE_BORDER_COLOR_OES", + 0x11, "GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV", }, { - 0x11, - "GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO_NV", + 0x1100, "GL_DONT_CARE", }, { - 0x1100, - "GL_DONT_CARE", + 0x1101, "GL_FASTEST", }, { - 0x1101, - "GL_FASTEST", + 0x1102, "GL_NICEST", }, { - 0x1102, - "GL_NICEST", + 0x12, "GL_SMALL_CCW_ARC_TO_NV", }, { - 0x12, - "GL_SMALL_CCW_ARC_TO_NV", + 0x13, "GL_RELATIVE_SMALL_CCW_ARC_TO_NV", }, { - 0x13, - "GL_RELATIVE_SMALL_CCW_ARC_TO_NV", + 0x14, "GL_SMALL_CW_ARC_TO_NV", }, { - 0x14, - "GL_SMALL_CW_ARC_TO_NV", + 0x1400, "GL_BYTE", }, { - 0x1400, - "GL_BYTE", + 0x1401, "GL_UNSIGNED_BYTE", }, { - 0x1401, - "GL_UNSIGNED_BYTE", + 0x1402, "GL_SHORT", }, { - 0x1402, - "GL_SHORT", + 0x1403, "GL_UNSIGNED_SHORT", }, { - 0x1403, - "GL_UNSIGNED_SHORT", + 0x1404, "GL_INT", }, { - 0x1404, - "GL_INT", + 0x1405, "GL_UNSIGNED_INT", }, { - 0x1405, - "GL_UNSIGNED_INT", + 0x1406, "GL_FLOAT", }, { - 0x1406, - "GL_FLOAT", + 0x140B, "GL_HALF_FLOAT", }, { - 0x140B, - "GL_HALF_FLOAT", + 0x140C, "GL_FIXED", }, { - 0x140C, - "GL_FIXED", + 0x140E, "GL_INT64_NV", }, { - 0x140E, - "GL_INT64_NV", + 0x140F, "GL_UNSIGNED_INT64_NV", }, { - 0x140F, - "GL_UNSIGNED_INT64_NV", + 0x15, "GL_RELATIVE_SMALL_CW_ARC_TO_NV", }, { - 0x15, - "GL_RELATIVE_SMALL_CW_ARC_TO_NV", + 0x1506, "GL_XOR_NV", }, { - 0x1506, - "GL_XOR_NV", + 0x150A, "GL_INVERT", }, { - 0x150A, - "GL_INVERT", + 0x16, "GL_LARGE_CCW_ARC_TO_NV", }, { - 0x16, - "GL_LARGE_CCW_ARC_TO_NV", + 0x17, "GL_RELATIVE_LARGE_CCW_ARC_TO_NV", }, { - 0x17, - "GL_RELATIVE_LARGE_CCW_ARC_TO_NV", + 0x1700, "GL_PATH_MODELVIEW_NV", }, { - 0x1700, - "GL_PATH_MODELVIEW_NV", + 0x1701, "GL_PATH_PROJECTION_NV", }, { - 0x1701, - "GL_PATH_PROJECTION_NV", + 0x1702, "GL_TEXTURE", }, { - 0x1702, - "GL_TEXTURE", + 0x18, "GL_LARGE_CW_ARC_TO_NV", }, { - 0x18, - "GL_LARGE_CW_ARC_TO_NV", + 0x1800, "GL_COLOR_EXT", }, { - 0x1800, - "GL_COLOR_EXT", + 0x1801, "GL_DEPTH_EXT", }, { - 0x1801, - "GL_DEPTH_EXT", + 0x1802, "GL_STENCIL_EXT", }, { - 0x1802, - "GL_STENCIL_EXT", + 0x19, "GL_RELATIVE_LARGE_CW_ARC_TO_NV", }, { - 0x19, - "GL_RELATIVE_LARGE_CW_ARC_TO_NV", + 0x1901, "GL_STENCIL_INDEX_OES", }, { - 0x1901, - "GL_STENCIL_INDEX_OES", + 0x1902, "GL_DEPTH_COMPONENT", }, { - 0x1902, - "GL_DEPTH_COMPONENT", + 0x1903, "GL_RED_EXT", }, { - 0x1903, - "GL_RED_EXT", + 0x1904, "GL_GREEN_NV", }, { - 0x1904, - "GL_GREEN_NV", + 0x1905, "GL_BLUE_NV", }, { - 0x1905, - "GL_BLUE_NV", + 0x1906, "GL_ALPHA", }, { - 0x1906, - "GL_ALPHA", + 0x1907, "GL_RGB", }, { - 0x1907, - "GL_RGB", + 0x1908, "GL_RGBA", }, { - 0x1908, - "GL_RGBA", + 0x1909, "GL_LUMINANCE", }, { - 0x1909, - "GL_LUMINANCE", + 0x190A, "GL_LUMINANCE_ALPHA", }, { - 0x190A, - "GL_LUMINANCE_ALPHA", + 0x1A, "GL_CONIC_CURVE_TO_NV", }, { - 0x1A, - "GL_CONIC_CURVE_TO_NV", + 0x1B, "GL_RELATIVE_CONIC_CURVE_TO_NV", }, { - 0x1B, - "GL_RELATIVE_CONIC_CURVE_TO_NV", + 0x1B00, "GL_POINT_NV", }, { - 0x1B00, - "GL_POINT_NV", + 0x1B01, "GL_LINE_NV", }, { - 0x1B01, - "GL_LINE_NV", + 0x1B02, "GL_FILL_NV", }, { - 0x1B02, - "GL_FILL_NV", + 0x1D00, "GL_FLAT_CHROMIUM", }, { - 0x1D00, - "GL_FLAT_CHROMIUM", + 0x1E00, "GL_KEEP", }, { - 0x1E00, - "GL_KEEP", + 0x1E01, "GL_REPLACE", }, { - 0x1E01, - "GL_REPLACE", + 0x1E02, "GL_INCR", }, { - 0x1E02, - "GL_INCR", + 0x1E03, "GL_DECR", }, { - 0x1E03, - "GL_DECR", + 0x1F00, "GL_VENDOR", }, { - 0x1F00, - "GL_VENDOR", + 0x1F01, "GL_RENDERER", }, { - 0x1F01, - "GL_RENDERER", + 0x1F02, "GL_VERSION", }, { - 0x1F02, - "GL_VERSION", + 0x1F03, "GL_EXTENSIONS", }, { - 0x1F03, - "GL_EXTENSIONS", + 0x2, "GL_CA_LAYER_EDGE_RIGHT_CHROMIUM", }, { - 0x2, - "GL_CA_LAYER_EDGE_RIGHT_CHROMIUM", + 0x20, "GL_GLYPH_VERTICAL_BEARING_X_BIT_NV", }, { - 0x20, - "GL_GLYPH_VERTICAL_BEARING_X_BIT_NV", + 0x20000000, "GL_FONT_NUM_GLYPH_INDICES_BIT_NV", }, { - 0x20000000, - "GL_FONT_NUM_GLYPH_INDICES_BIT_NV", + 0x2400, "GL_EYE_LINEAR_CHROMIUM", }, { - 0x2400, - "GL_EYE_LINEAR_CHROMIUM", + 0x2401, "GL_OBJECT_LINEAR_CHROMIUM", }, { - 0x2401, - "GL_OBJECT_LINEAR_CHROMIUM", + 0x2600, "GL_NEAREST", }, { - 0x2600, - "GL_NEAREST", + 0x2601, "GL_LINEAR", }, { - 0x2601, - "GL_LINEAR", + 0x2700, "GL_NEAREST_MIPMAP_NEAREST", }, { - 0x2700, - "GL_NEAREST_MIPMAP_NEAREST", + 0x2701, "GL_LINEAR_MIPMAP_NEAREST", }, { - 0x2701, - "GL_LINEAR_MIPMAP_NEAREST", + 0x2702, "GL_NEAREST_MIPMAP_LINEAR", }, { - 0x2702, - "GL_NEAREST_MIPMAP_LINEAR", + 0x2703, "GL_LINEAR_MIPMAP_LINEAR", }, { - 0x2703, - "GL_LINEAR_MIPMAP_LINEAR", + 0x2800, "GL_TEXTURE_MAG_FILTER", }, { - 0x2800, - "GL_TEXTURE_MAG_FILTER", + 0x2801, "GL_TEXTURE_MIN_FILTER", }, { - 0x2801, - "GL_TEXTURE_MIN_FILTER", + 0x2802, "GL_TEXTURE_WRAP_S", }, { - 0x2802, - "GL_TEXTURE_WRAP_S", + 0x2803, "GL_TEXTURE_WRAP_T", }, { - 0x2803, - "GL_TEXTURE_WRAP_T", + 0x2901, "GL_REPEAT", }, { - 0x2901, - "GL_REPEAT", + 0x2A00, "GL_POLYGON_OFFSET_UNITS", }, { - 0x2A00, - "GL_POLYGON_OFFSET_UNITS", + 0x2A01, "GL_POLYGON_OFFSET_POINT_NV", }, { - 0x2A01, - "GL_POLYGON_OFFSET_POINT_NV", + 0x2A02, "GL_POLYGON_OFFSET_LINE_NV", }, { - 0x2A02, - "GL_POLYGON_OFFSET_LINE_NV", + 0x3000, "GL_CLIP_DISTANCE0_APPLE", }, { - 0x3000, - "GL_CLIP_DISTANCE0_APPLE", + 0x3001, "GL_CLIP_DISTANCE1_APPLE", }, { - 0x3001, - "GL_CLIP_DISTANCE1_APPLE", + 0x3002, "GL_CLIP_DISTANCE2_APPLE", }, { - 0x3002, - "GL_CLIP_DISTANCE2_APPLE", + 0x3003, "GL_CLIP_DISTANCE3_APPLE", }, { - 0x3003, - "GL_CLIP_DISTANCE3_APPLE", + 0x3004, "GL_CLIP_DISTANCE4_APPLE", }, { - 0x3004, - "GL_CLIP_DISTANCE4_APPLE", + 0x3005, "GL_CLIP_DISTANCE5_APPLE", }, { - 0x3005, - "GL_CLIP_DISTANCE5_APPLE", + 0x3006, "GL_CLIP_DISTANCE6_APPLE", }, { - 0x3006, - "GL_CLIP_DISTANCE6_APPLE", + 0x3007, "GL_CLIP_DISTANCE7_APPLE", }, { - 0x3007, - "GL_CLIP_DISTANCE7_APPLE", + 0x300E, "GL_CONTEXT_LOST", }, { - 0x300E, - "GL_CONTEXT_LOST", + 0x4, "GL_CA_LAYER_EDGE_BOTTOM_CHROMIUM", }, { - 0x4, - "GL_CA_LAYER_EDGE_BOTTOM_CHROMIUM", + 0x40, "GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV", }, { - 0x40, - "GL_GLYPH_VERTICAL_BEARING_Y_BIT_NV", + 0x40000000, "GL_MULTISAMPLE_BUFFER_BIT6_QCOM", }, { - 0x40000000, - "GL_MULTISAMPLE_BUFFER_BIT6_QCOM", + 0x6000, "GL_SCANOUT_CHROMIUM", }, { - 0x6000, - "GL_SCANOUT_CHROMIUM", + 0x6003, "GL_GET_ERROR_QUERY_CHROMIUM", }, { - 0x6003, - "GL_GET_ERROR_QUERY_CHROMIUM", + 0x6004, "GL_COMMANDS_ISSUED_CHROMIUM", }, { - 0x6004, - "GL_COMMANDS_ISSUED_CHROMIUM", + 0x6006, "GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM", }, { - 0x6006, - "GL_ASYNC_PIXEL_PACK_COMPLETED_CHROMIUM", + 0x6007, "GL_LATENCY_QUERY_CHROMIUM", }, { - 0x6007, - "GL_LATENCY_QUERY_CHROMIUM", + 0x78EC, "GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM", }, { - 0x78EC, - "GL_PIXEL_UNPACK_TRANSFER_BUFFER_CHROMIUM", + 0x78ED, "GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM", }, { - 0x78ED, - "GL_PIXEL_PACK_TRANSFER_BUFFER_CHROMIUM", + 0x78EE, "GL_PIXEL_PACK_TRANSFER_BUFFER_BINDING_CHROMIUM", }, { - 0x78EE, - "GL_PIXEL_PACK_TRANSFER_BUFFER_BINDING_CHROMIUM", + 0x78EF, "GL_PIXEL_UNPACK_TRANSFER_BUFFER_BINDING_CHROMIUM", }, { - 0x78EF, - "GL_PIXEL_UNPACK_TRANSFER_BUFFER_BINDING_CHROMIUM", + 0x78FA, "GL_RGB_YCRCB_420_CHROMIUM", }, { - 0x78FA, - "GL_RGB_YCRCB_420_CHROMIUM", + 0x78FB, "GL_RGB_YCBCR_422_CHROMIUM", }, { - 0x78FB, - "GL_RGB_YCBCR_422_CHROMIUM", + 0x78FC, "GL_RGB_YCBCR_420V_CHROMIUM", }, { - 0x78FC, - "GL_RGB_YCBCR_420V_CHROMIUM", + 0x8, "GL_CA_LAYER_EDGE_TOP_CHROMIUM", }, { - 0x8, - "GL_CA_LAYER_EDGE_TOP_CHROMIUM", + 0x80, "GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV", }, { - 0x80, - "GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT_NV", + 0x80000000, "GL_MULTISAMPLE_BUFFER_BIT7_QCOM", }, { - 0x80000000, - "GL_MULTISAMPLE_BUFFER_BIT7_QCOM", + 0x8001, "GL_CONSTANT_COLOR", }, { - 0x8001, - "GL_CONSTANT_COLOR", + 0x8002, "GL_ONE_MINUS_CONSTANT_COLOR", }, { - 0x8002, - "GL_ONE_MINUS_CONSTANT_COLOR", + 0x8003, "GL_CONSTANT_ALPHA", }, { - 0x8003, - "GL_CONSTANT_ALPHA", + 0x8004, "GL_ONE_MINUS_CONSTANT_ALPHA", }, { - 0x8004, - "GL_ONE_MINUS_CONSTANT_ALPHA", + 0x8005, "GL_BLEND_COLOR", }, { - 0x8005, - "GL_BLEND_COLOR", + 0x8006, "GL_FUNC_ADD", }, { - 0x8006, - "GL_FUNC_ADD", + 0x8007, "GL_MIN", }, { - 0x8007, - "GL_MIN", + 0x8008, "GL_MAX", }, { - 0x8008, - "GL_MAX", + 0x8009, "GL_BLEND_EQUATION", }, { - 0x8009, - "GL_BLEND_EQUATION", + 0x800A, "GL_FUNC_SUBTRACT", }, { - 0x800A, - "GL_FUNC_SUBTRACT", + 0x800B, "GL_FUNC_REVERSE_SUBTRACT", }, { - 0x800B, - "GL_FUNC_REVERSE_SUBTRACT", + 0x8033, "GL_UNSIGNED_SHORT_4_4_4_4", }, { - 0x8033, - "GL_UNSIGNED_SHORT_4_4_4_4", + 0x8034, "GL_UNSIGNED_SHORT_5_5_5_1", }, { - 0x8034, - "GL_UNSIGNED_SHORT_5_5_5_1", + 0x8037, "GL_POLYGON_OFFSET_FILL", }, { - 0x8037, - "GL_POLYGON_OFFSET_FILL", + 0x8038, "GL_POLYGON_OFFSET_FACTOR", }, { - 0x8038, - "GL_POLYGON_OFFSET_FACTOR", + 0x803C, "GL_ALPHA8_OES", }, { - 0x803C, - "GL_ALPHA8_OES", + 0x8040, "GL_LUMINANCE8_OES", }, { - 0x8040, - "GL_LUMINANCE8_OES", + 0x8043, "GL_LUMINANCE4_ALPHA4_OES", }, { - 0x8043, - "GL_LUMINANCE4_ALPHA4_OES", + 0x8045, "GL_LUMINANCE8_ALPHA8_OES", }, { - 0x8045, - "GL_LUMINANCE8_ALPHA8_OES", + 0x8051, "GL_RGB8_OES", }, { - 0x8051, - "GL_RGB8_OES", + 0x8052, "GL_RGB10_EXT", }, { - 0x8052, - "GL_RGB10_EXT", + 0x8054, "GL_RGB16_EXT", }, { - 0x8054, - "GL_RGB16_EXT", + 0x8056, "GL_RGBA4", }, { - 0x8056, - "GL_RGBA4", + 0x8057, "GL_RGB5_A1", }, { - 0x8057, - "GL_RGB5_A1", + 0x8058, "GL_RGBA8_OES", }, { - 0x8058, - "GL_RGBA8_OES", + 0x8059, "GL_RGB10_A2_EXT", }, { - 0x8059, - "GL_RGB10_A2_EXT", + 0x805B, "GL_RGBA16_EXT", }, { - 0x805B, - "GL_RGBA16_EXT", + 0x805C, "GL_TEXTURE_RED_SIZE", }, { - 0x805C, - "GL_TEXTURE_RED_SIZE", + 0x805D, "GL_TEXTURE_GREEN_SIZE", }, { - 0x805D, - "GL_TEXTURE_GREEN_SIZE", + 0x805E, "GL_TEXTURE_BLUE_SIZE", }, { - 0x805E, - "GL_TEXTURE_BLUE_SIZE", + 0x805F, "GL_TEXTURE_ALPHA_SIZE", }, { - 0x805F, - "GL_TEXTURE_ALPHA_SIZE", + 0x8069, "GL_TEXTURE_BINDING_2D", }, { - 0x8069, - "GL_TEXTURE_BINDING_2D", + 0x806A, "GL_TEXTURE_BINDING_3D_OES", }, { - 0x806A, - "GL_TEXTURE_BINDING_3D_OES", + 0x806D, "GL_UNPACK_SKIP_IMAGES", }, { - 0x806D, - "GL_UNPACK_SKIP_IMAGES", + 0x806E, "GL_UNPACK_IMAGE_HEIGHT", }, { - 0x806E, - "GL_UNPACK_IMAGE_HEIGHT", + 0x806F, "GL_TEXTURE_3D_OES", }, { - 0x806F, - "GL_TEXTURE_3D_OES", + 0x8071, "GL_TEXTURE_DEPTH", }, { - 0x8071, - "GL_TEXTURE_DEPTH", + 0x8072, "GL_TEXTURE_WRAP_R_OES", }, { - 0x8072, - "GL_TEXTURE_WRAP_R_OES", + 0x8073, "GL_MAX_3D_TEXTURE_SIZE_OES", }, { - 0x8073, - "GL_MAX_3D_TEXTURE_SIZE_OES", + 0x8074, "GL_VERTEX_ARRAY_KHR", }, { - 0x8074, - "GL_VERTEX_ARRAY_KHR", + 0x809D, "GL_MULTISAMPLE_EXT", }, { - 0x809D, - "GL_MULTISAMPLE_EXT", + 0x809E, "GL_SAMPLE_ALPHA_TO_COVERAGE", }, { - 0x809E, - "GL_SAMPLE_ALPHA_TO_COVERAGE", + 0x809F, "GL_SAMPLE_ALPHA_TO_ONE_EXT", }, { - 0x809F, - "GL_SAMPLE_ALPHA_TO_ONE_EXT", + 0x80A0, "GL_SAMPLE_COVERAGE", }, { - 0x80A0, - "GL_SAMPLE_COVERAGE", + 0x80A8, "GL_SAMPLE_BUFFERS", }, { - 0x80A8, - "GL_SAMPLE_BUFFERS", + 0x80A9, "GL_SAMPLES", }, { - 0x80A9, - "GL_SAMPLES", + 0x80AA, "GL_SAMPLE_COVERAGE_VALUE", }, { - 0x80AA, - "GL_SAMPLE_COVERAGE_VALUE", + 0x80AB, "GL_SAMPLE_COVERAGE_INVERT", }, { - 0x80AB, - "GL_SAMPLE_COVERAGE_INVERT", + 0x80C8, "GL_BLEND_DST_RGB", }, { - 0x80C8, - "GL_BLEND_DST_RGB", + 0x80C9, "GL_BLEND_SRC_RGB", }, { - 0x80C9, - "GL_BLEND_SRC_RGB", + 0x80CA, "GL_BLEND_DST_ALPHA", }, { - 0x80CA, - "GL_BLEND_DST_ALPHA", + 0x80CB, "GL_BLEND_SRC_ALPHA", }, { - 0x80CB, - "GL_BLEND_SRC_ALPHA", + 0x80E1, "GL_BGRA_EXT", }, { - 0x80E1, - "GL_BGRA_EXT", + 0x80E8, "GL_MAX_ELEMENTS_VERTICES", }, { - 0x80E8, - "GL_MAX_ELEMENTS_VERTICES", + 0x80E9, "GL_MAX_ELEMENTS_INDICES", }, { - 0x80E9, - "GL_MAX_ELEMENTS_INDICES", + 0x812D, "GL_CLAMP_TO_BORDER_OES", }, { - 0x812D, - "GL_CLAMP_TO_BORDER_OES", + 0x812F, "GL_CLAMP_TO_EDGE", }, { - 0x812F, - "GL_CLAMP_TO_EDGE", + 0x813A, "GL_TEXTURE_MIN_LOD", }, { - 0x813A, - "GL_TEXTURE_MIN_LOD", + 0x813B, "GL_TEXTURE_MAX_LOD", }, { - 0x813B, - "GL_TEXTURE_MAX_LOD", + 0x813C, "GL_TEXTURE_BASE_LEVEL", }, { - 0x813C, - "GL_TEXTURE_BASE_LEVEL", + 0x813D, "GL_TEXTURE_MAX_LEVEL_APPLE", }, { - 0x813D, - "GL_TEXTURE_MAX_LEVEL_APPLE", + 0x8192, "GL_GENERATE_MIPMAP_HINT", }, { - 0x8192, - "GL_GENERATE_MIPMAP_HINT", + 0x81A5, "GL_DEPTH_COMPONENT16", }, { - 0x81A5, - "GL_DEPTH_COMPONENT16", + 0x81A6, "GL_DEPTH_COMPONENT24_OES", }, { - 0x81A6, - "GL_DEPTH_COMPONENT24_OES", + 0x81A7, "GL_DEPTH_COMPONENT32_OES", }, { - 0x81A7, - "GL_DEPTH_COMPONENT32_OES", + 0x8210, "GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT", }, { - 0x8210, - "GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING_EXT", + 0x8211, "GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT", }, { - 0x8211, - "GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE_EXT", + 0x8212, "GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE", }, { - 0x8212, - "GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE", + 0x8213, "GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE", }, { - 0x8213, - "GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE", + 0x8214, "GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE", }, { - 0x8214, - "GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE", + 0x8215, "GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE", }, { - 0x8215, - "GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE", + 0x8216, "GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE", }, { - 0x8216, - "GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE", + 0x8217, "GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE", }, { - 0x8217, - "GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE", + 0x8218, "GL_FRAMEBUFFER_DEFAULT", }, { - 0x8218, - "GL_FRAMEBUFFER_DEFAULT", + 0x8219, "GL_FRAMEBUFFER_UNDEFINED_OES", }, { - 0x8219, - "GL_FRAMEBUFFER_UNDEFINED_OES", + 0x821A, "GL_DEPTH_STENCIL_ATTACHMENT", }, { - 0x821A, - "GL_DEPTH_STENCIL_ATTACHMENT", + 0x821B, "GL_MAJOR_VERSION", }, { - 0x821B, - "GL_MAJOR_VERSION", + 0x821C, "GL_MINOR_VERSION", }, { - 0x821C, - "GL_MINOR_VERSION", + 0x821D, "GL_NUM_EXTENSIONS", }, { - 0x821D, - "GL_NUM_EXTENSIONS", + 0x821F, "GL_BUFFER_IMMUTABLE_STORAGE_EXT", }, { - 0x821F, - "GL_BUFFER_IMMUTABLE_STORAGE_EXT", + 0x8220, "GL_BUFFER_STORAGE_FLAGS_EXT", }, { - 0x8220, - "GL_BUFFER_STORAGE_FLAGS_EXT", + 0x8221, "GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED_OES", }, { - 0x8221, - "GL_PRIMITIVE_RESTART_FOR_PATCHES_SUPPORTED_OES", + 0x8227, "GL_RG_EXT", }, { - 0x8227, - "GL_RG_EXT", + 0x8228, "GL_RG_INTEGER", }, { - 0x8228, - "GL_RG_INTEGER", + 0x8229, "GL_R8_EXT", }, { - 0x8229, - "GL_R8_EXT", + 0x822A, "GL_R16_EXT", }, { - 0x822A, - "GL_R16_EXT", + 0x822B, "GL_RG8_EXT", }, { - 0x822B, - "GL_RG8_EXT", + 0x822C, "GL_RG16_EXT", }, { - 0x822C, - "GL_RG16_EXT", + 0x822D, "GL_R16F_EXT", }, { - 0x822D, - "GL_R16F_EXT", + 0x822E, "GL_R32F_EXT", }, { - 0x822E, - "GL_R32F_EXT", + 0x822F, "GL_RG16F_EXT", }, { - 0x822F, - "GL_RG16F_EXT", + 0x8230, "GL_RG32F_EXT", }, { - 0x8230, - "GL_RG32F_EXT", + 0x8231, "GL_R8I", }, { - 0x8231, - "GL_R8I", + 0x8232, "GL_R8UI", }, { - 0x8232, - "GL_R8UI", + 0x8233, "GL_R16I", }, { - 0x8233, - "GL_R16I", + 0x8234, "GL_R16UI", }, { - 0x8234, - "GL_R16UI", + 0x8235, "GL_R32I", }, { - 0x8235, - "GL_R32I", + 0x8236, "GL_R32UI", }, { - 0x8236, - "GL_R32UI", + 0x8237, "GL_RG8I", }, { - 0x8237, - "GL_RG8I", + 0x8238, "GL_RG8UI", }, { - 0x8238, - "GL_RG8UI", + 0x8239, "GL_RG16I", }, { - 0x8239, - "GL_RG16I", + 0x823A, "GL_RG16UI", }, { - 0x823A, - "GL_RG16UI", + 0x823B, "GL_RG32I", }, { - 0x823B, - "GL_RG32I", + 0x823C, "GL_RG32UI", }, { - 0x823C, - "GL_RG32UI", + 0x8242, "GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR", }, { - 0x8242, - "GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR", + 0x8243, "GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_KHR", }, { - 0x8243, - "GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_KHR", + 0x8244, "GL_DEBUG_CALLBACK_FUNCTION_KHR", }, { - 0x8244, - "GL_DEBUG_CALLBACK_FUNCTION_KHR", + 0x8245, "GL_DEBUG_CALLBACK_USER_PARAM_KHR", }, { - 0x8245, - "GL_DEBUG_CALLBACK_USER_PARAM_KHR", + 0x8246, "GL_DEBUG_SOURCE_API_KHR", }, { - 0x8246, - "GL_DEBUG_SOURCE_API_KHR", + 0x8247, "GL_DEBUG_SOURCE_WINDOW_SYSTEM_KHR", }, { - 0x8247, - "GL_DEBUG_SOURCE_WINDOW_SYSTEM_KHR", + 0x8248, "GL_DEBUG_SOURCE_SHADER_COMPILER_KHR", }, { - 0x8248, - "GL_DEBUG_SOURCE_SHADER_COMPILER_KHR", + 0x8249, "GL_DEBUG_SOURCE_THIRD_PARTY_KHR", }, { - 0x8249, - "GL_DEBUG_SOURCE_THIRD_PARTY_KHR", + 0x824A, "GL_DEBUG_SOURCE_APPLICATION_KHR", }, { - 0x824A, - "GL_DEBUG_SOURCE_APPLICATION_KHR", + 0x824B, "GL_DEBUG_SOURCE_OTHER_KHR", }, { - 0x824B, - "GL_DEBUG_SOURCE_OTHER_KHR", + 0x824C, "GL_DEBUG_TYPE_ERROR_KHR", }, { - 0x824C, - "GL_DEBUG_TYPE_ERROR_KHR", + 0x824D, "GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR", }, { - 0x824D, - "GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR", + 0x824E, "GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR", }, { - 0x824E, - "GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR", + 0x824F, "GL_DEBUG_TYPE_PORTABILITY_KHR", }, { - 0x824F, - "GL_DEBUG_TYPE_PORTABILITY_KHR", + 0x8250, "GL_DEBUG_TYPE_PERFORMANCE_KHR", }, { - 0x8250, - "GL_DEBUG_TYPE_PERFORMANCE_KHR", + 0x8251, "GL_DEBUG_TYPE_OTHER_KHR", }, { - 0x8251, - "GL_DEBUG_TYPE_OTHER_KHR", + 0x8252, "GL_LOSE_CONTEXT_ON_RESET_KHR", }, { - 0x8252, - "GL_LOSE_CONTEXT_ON_RESET_KHR", + 0x8253, "GL_GUILTY_CONTEXT_RESET_KHR", }, { - 0x8253, - "GL_GUILTY_CONTEXT_RESET_KHR", + 0x8254, "GL_INNOCENT_CONTEXT_RESET_KHR", }, { - 0x8254, - "GL_INNOCENT_CONTEXT_RESET_KHR", + 0x8255, "GL_UNKNOWN_CONTEXT_RESET_KHR", }, { - 0x8255, - "GL_UNKNOWN_CONTEXT_RESET_KHR", + 0x8256, "GL_RESET_NOTIFICATION_STRATEGY_KHR", }, { - 0x8256, - "GL_RESET_NOTIFICATION_STRATEGY_KHR", + 0x8257, "GL_PROGRAM_BINARY_RETRIEVABLE_HINT", }, { - 0x8257, - "GL_PROGRAM_BINARY_RETRIEVABLE_HINT", + 0x8258, "GL_PROGRAM_SEPARABLE_EXT", }, { - 0x8258, - "GL_PROGRAM_SEPARABLE_EXT", + 0x8259, "GL_ACTIVE_PROGRAM_EXT", }, { - 0x8259, - "GL_ACTIVE_PROGRAM_EXT", + 0x825A, "GL_PROGRAM_PIPELINE_BINDING_EXT", }, { - 0x825A, - "GL_PROGRAM_PIPELINE_BINDING_EXT", + 0x825B, "GL_MAX_VIEWPORTS_OES", }, { - 0x825B, - "GL_MAX_VIEWPORTS_OES", + 0x825C, "GL_VIEWPORT_SUBPIXEL_BITS_OES", }, { - 0x825C, - "GL_VIEWPORT_SUBPIXEL_BITS_OES", + 0x825D, "GL_VIEWPORT_BOUNDS_RANGE_OES", }, { - 0x825D, - "GL_VIEWPORT_BOUNDS_RANGE_OES", + 0x825E, "GL_LAYER_PROVOKING_VERTEX_OES", }, { - 0x825E, - "GL_LAYER_PROVOKING_VERTEX_OES", + 0x825F, "GL_VIEWPORT_INDEX_PROVOKING_VERTEX_OES", }, { - 0x825F, - "GL_VIEWPORT_INDEX_PROVOKING_VERTEX_OES", + 0x8260, "GL_UNDEFINED_VERTEX_OES", }, { - 0x8260, - "GL_UNDEFINED_VERTEX_OES", + 0x8261, "GL_NO_RESET_NOTIFICATION_KHR", }, { - 0x8261, - "GL_NO_RESET_NOTIFICATION_KHR", + 0x8262, "GL_MAX_COMPUTE_SHARED_MEMORY_SIZE", }, { - 0x8262, - "GL_MAX_COMPUTE_SHARED_MEMORY_SIZE", + 0x8263, "GL_MAX_COMPUTE_UNIFORM_COMPONENTS", }, { - 0x8263, - "GL_MAX_COMPUTE_UNIFORM_COMPONENTS", + 0x8264, "GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS", }, { - 0x8264, - "GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS", + 0x8265, "GL_MAX_COMPUTE_ATOMIC_COUNTERS", }, { - 0x8265, - "GL_MAX_COMPUTE_ATOMIC_COUNTERS", + 0x8266, "GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS", }, { - 0x8266, - "GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS", + 0x8267, "GL_COMPUTE_WORK_GROUP_SIZE", }, { - 0x8267, - "GL_COMPUTE_WORK_GROUP_SIZE", + 0x8268, "GL_DEBUG_TYPE_MARKER_KHR", }, { - 0x8268, - "GL_DEBUG_TYPE_MARKER_KHR", + 0x8269, "GL_DEBUG_TYPE_PUSH_GROUP_KHR", }, { - 0x8269, - "GL_DEBUG_TYPE_PUSH_GROUP_KHR", + 0x826A, "GL_DEBUG_TYPE_POP_GROUP_KHR", }, { - 0x826A, - "GL_DEBUG_TYPE_POP_GROUP_KHR", + 0x826B, "GL_DEBUG_SEVERITY_NOTIFICATION_KHR", }, { - 0x826B, - "GL_DEBUG_SEVERITY_NOTIFICATION_KHR", + 0x826C, "GL_MAX_DEBUG_GROUP_STACK_DEPTH_KHR", }, { - 0x826C, - "GL_MAX_DEBUG_GROUP_STACK_DEPTH_KHR", + 0x826D, "GL_DEBUG_GROUP_STACK_DEPTH_KHR", }, { - 0x826D, - "GL_DEBUG_GROUP_STACK_DEPTH_KHR", + 0x826E, "GL_MAX_UNIFORM_LOCATIONS", }, { - 0x826E, - "GL_MAX_UNIFORM_LOCATIONS", + 0x82D4, "GL_VERTEX_ATTRIB_BINDING", }, { - 0x82D4, - "GL_VERTEX_ATTRIB_BINDING", + 0x82D5, "GL_VERTEX_ATTRIB_RELATIVE_OFFSET", }, { - 0x82D5, - "GL_VERTEX_ATTRIB_RELATIVE_OFFSET", + 0x82D6, "GL_VERTEX_BINDING_DIVISOR", }, { - 0x82D6, - "GL_VERTEX_BINDING_DIVISOR", + 0x82D7, "GL_VERTEX_BINDING_OFFSET", }, { - 0x82D7, - "GL_VERTEX_BINDING_OFFSET", + 0x82D8, "GL_VERTEX_BINDING_STRIDE", }, { - 0x82D8, - "GL_VERTEX_BINDING_STRIDE", + 0x82D9, "GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET", }, { - 0x82D9, - "GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET", + 0x82DA, "GL_MAX_VERTEX_ATTRIB_BINDINGS", }, { - 0x82DA, - "GL_MAX_VERTEX_ATTRIB_BINDINGS", + 0x82DB, "GL_TEXTURE_VIEW_MIN_LEVEL_OES", }, { - 0x82DB, - "GL_TEXTURE_VIEW_MIN_LEVEL_OES", + 0x82DC, "GL_TEXTURE_VIEW_NUM_LEVELS_OES", }, { - 0x82DC, - "GL_TEXTURE_VIEW_NUM_LEVELS_OES", + 0x82DD, "GL_TEXTURE_VIEW_MIN_LAYER_OES", }, { - 0x82DD, - "GL_TEXTURE_VIEW_MIN_LAYER_OES", + 0x82DE, "GL_TEXTURE_VIEW_NUM_LAYERS_OES", }, { - 0x82DE, - "GL_TEXTURE_VIEW_NUM_LAYERS_OES", + 0x82DF, "GL_TEXTURE_IMMUTABLE_LEVELS", }, { - 0x82DF, - "GL_TEXTURE_IMMUTABLE_LEVELS", + 0x82E0, "GL_BUFFER_KHR", }, { - 0x82E0, - "GL_BUFFER_KHR", + 0x82E1, "GL_SHADER_KHR", }, { - 0x82E1, - "GL_SHADER_KHR", + 0x82E2, "GL_PROGRAM_KHR", }, { - 0x82E2, - "GL_PROGRAM_KHR", + 0x82E3, "GL_QUERY_KHR", }, { - 0x82E3, - "GL_QUERY_KHR", + 0x82E4, "GL_PROGRAM_PIPELINE_KHR", }, { - 0x82E4, - "GL_PROGRAM_PIPELINE_KHR", + 0x82E5, "GL_MAX_VERTEX_ATTRIB_STRIDE", }, { - 0x82E5, - "GL_MAX_VERTEX_ATTRIB_STRIDE", + 0x82E6, "GL_SAMPLER_KHR", }, { - 0x82E6, - "GL_SAMPLER_KHR", + 0x82E8, "GL_MAX_LABEL_LENGTH_KHR", }, { - 0x82E8, - "GL_MAX_LABEL_LENGTH_KHR", + 0x82F9, "GL_MAX_CULL_DISTANCES_EXT", }, { - 0x82F9, - "GL_MAX_CULL_DISTANCES_EXT", + 0x82FA, "GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES_EXT", }, { - 0x82FA, - "GL_MAX_COMBINED_CLIP_AND_CULL_DISTANCES_EXT", + 0x82FB, "GL_CONTEXT_RELEASE_BEHAVIOR_KHR", }, { - 0x82FB, - "GL_CONTEXT_RELEASE_BEHAVIOR_KHR", + 0x82FC, "GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR", }, { - 0x82FC, - "GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_KHR", + 0x8363, "GL_UNSIGNED_SHORT_5_6_5", }, { - 0x8363, - "GL_UNSIGNED_SHORT_5_6_5", + 0x8365, "GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT", }, { - 0x8365, - "GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT", + 0x8366, "GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT", }, { - 0x8366, - "GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT", + 0x8368, "GL_UNSIGNED_INT_2_10_10_10_REV_EXT", }, { - 0x8368, - "GL_UNSIGNED_INT_2_10_10_10_REV_EXT", + 0x8370, "GL_MIRRORED_REPEAT", }, { - 0x8370, - "GL_MIRRORED_REPEAT", + 0x83F0, "GL_COMPRESSED_RGB_S3TC_DXT1_EXT", }, { - 0x83F0, - "GL_COMPRESSED_RGB_S3TC_DXT1_EXT", + 0x83F1, "GL_COMPRESSED_RGBA_S3TC_DXT1_EXT", }, { - 0x83F1, - "GL_COMPRESSED_RGBA_S3TC_DXT1_EXT", + 0x83F2, "GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE", }, { - 0x83F2, - "GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE", + 0x83F3, "GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE", }, { - 0x83F3, - "GL_COMPRESSED_RGBA_S3TC_DXT5_ANGLE", + 0x83F9, "GL_PERFQUERY_DONOT_FLUSH_INTEL", }, { - 0x83F9, - "GL_PERFQUERY_DONOT_FLUSH_INTEL", + 0x83FA, "GL_PERFQUERY_FLUSH_INTEL", }, { - 0x83FA, - "GL_PERFQUERY_FLUSH_INTEL", + 0x83FB, "GL_PERFQUERY_WAIT_INTEL", }, { - 0x83FB, - "GL_PERFQUERY_WAIT_INTEL", + 0x83FC, "GL_BLACKHOLE_RENDER_INTEL", }, { - 0x83FE, - "GL_CONSERVATIVE_RASTERIZATION_INTEL", + 0x83FE, "GL_CONSERVATIVE_RASTERIZATION_INTEL", }, { - 0x846D, - "GL_ALIASED_POINT_SIZE_RANGE", + 0x846D, "GL_ALIASED_POINT_SIZE_RANGE", }, { - 0x846E, - "GL_ALIASED_LINE_WIDTH_RANGE", + 0x846E, "GL_ALIASED_LINE_WIDTH_RANGE", }, { - 0x84C0, - "GL_TEXTURE0", + 0x84C0, "GL_TEXTURE0", }, { - 0x84C1, - "GL_TEXTURE1", + 0x84C1, "GL_TEXTURE1", }, { - 0x84C2, - "GL_TEXTURE2", + 0x84C2, "GL_TEXTURE2", }, { - 0x84C3, - "GL_TEXTURE3", + 0x84C3, "GL_TEXTURE3", }, { - 0x84C4, - "GL_TEXTURE4", + 0x84C4, "GL_TEXTURE4", }, { - 0x84C5, - "GL_TEXTURE5", + 0x84C5, "GL_TEXTURE5", }, { - 0x84C6, - "GL_TEXTURE6", + 0x84C6, "GL_TEXTURE6", }, { - 0x84C7, - "GL_TEXTURE7", + 0x84C7, "GL_TEXTURE7", }, { - 0x84C8, - "GL_TEXTURE8", + 0x84C8, "GL_TEXTURE8", }, { - 0x84C9, - "GL_TEXTURE9", + 0x84C9, "GL_TEXTURE9", }, { - 0x84CA, - "GL_TEXTURE10", + 0x84CA, "GL_TEXTURE10", }, { - 0x84CB, - "GL_TEXTURE11", + 0x84CB, "GL_TEXTURE11", }, { - 0x84CC, - "GL_TEXTURE12", + 0x84CC, "GL_TEXTURE12", }, { - 0x84CD, - "GL_TEXTURE13", + 0x84CD, "GL_TEXTURE13", }, { - 0x84CE, - "GL_TEXTURE14", + 0x84CE, "GL_TEXTURE14", }, { - 0x84CF, - "GL_TEXTURE15", + 0x84CF, "GL_TEXTURE15", }, { - 0x84D0, - "GL_TEXTURE16", + 0x84D0, "GL_TEXTURE16", }, { - 0x84D1, - "GL_TEXTURE17", + 0x84D1, "GL_TEXTURE17", }, { - 0x84D2, - "GL_TEXTURE18", + 0x84D2, "GL_TEXTURE18", }, { - 0x84D3, - "GL_TEXTURE19", + 0x84D3, "GL_TEXTURE19", }, { - 0x84D4, - "GL_TEXTURE20", + 0x84D4, "GL_TEXTURE20", }, { - 0x84D5, - "GL_TEXTURE21", + 0x84D5, "GL_TEXTURE21", }, { - 0x84D6, - "GL_TEXTURE22", + 0x84D6, "GL_TEXTURE22", }, { - 0x84D7, - "GL_TEXTURE23", + 0x84D7, "GL_TEXTURE23", }, { - 0x84D8, - "GL_TEXTURE24", + 0x84D8, "GL_TEXTURE24", }, { - 0x84D9, - "GL_TEXTURE25", + 0x84D9, "GL_TEXTURE25", }, { - 0x84DA, - "GL_TEXTURE26", + 0x84DA, "GL_TEXTURE26", }, { - 0x84DB, - "GL_TEXTURE27", + 0x84DB, "GL_TEXTURE27", }, { - 0x84DC, - "GL_TEXTURE28", + 0x84DC, "GL_TEXTURE28", }, { - 0x84DD, - "GL_TEXTURE29", + 0x84DD, "GL_TEXTURE29", }, { - 0x84DE, - "GL_TEXTURE30", + 0x84DE, "GL_TEXTURE30", }, { - 0x84DF, - "GL_TEXTURE31", + 0x84DF, "GL_TEXTURE31", }, { - 0x84E0, - "GL_ACTIVE_TEXTURE", + 0x84E0, "GL_ACTIVE_TEXTURE", }, { - 0x84E3, - "GL_PATH_TRANSPOSE_MODELVIEW_MATRIX_NV", + 0x84E3, "GL_PATH_TRANSPOSE_MODELVIEW_MATRIX_NV", }, { - 0x84E4, - "GL_PATH_TRANSPOSE_PROJECTION_MATRIX_NV", + 0x84E4, "GL_PATH_TRANSPOSE_PROJECTION_MATRIX_NV", }, { - 0x84E8, - "GL_MAX_RENDERBUFFER_SIZE", + 0x84E8, "GL_MAX_RENDERBUFFER_SIZE", }, { - 0x84F2, - "GL_ALL_COMPLETED_NV", + 0x84F2, "GL_ALL_COMPLETED_NV", }, { - 0x84F3, - "GL_FENCE_STATUS_NV", + 0x84F3, "GL_FENCE_STATUS_NV", }, { - 0x84F4, - "GL_FENCE_CONDITION_NV", + 0x84F4, "GL_FENCE_CONDITION_NV", }, { - 0x84F5, - "GL_TEXTURE_RECTANGLE_ARB", + 0x84F5, "GL_TEXTURE_RECTANGLE_ARB", }, { - 0x84F6, - "GL_TEXTURE_BINDING_RECTANGLE_ARB", + 0x84F6, "GL_TEXTURE_BINDING_RECTANGLE_ARB", }, { - 0x84F7, - "GL_COMMANDS_COMPLETED_CHROMIUM", + 0x84F7, "GL_COMMANDS_COMPLETED_CHROMIUM", }, { - 0x84F8, - "GL_READBACK_SHADOW_COPIES_UPDATED_CHROMIUM", + 0x84F8, "GL_READBACK_SHADOW_COPIES_UPDATED_CHROMIUM", }, { - 0x84F9, - "GL_DEPTH_STENCIL_OES", + 0x84F9, "GL_DEPTH_STENCIL_OES", }, { - 0x84FA, - "GL_UNSIGNED_INT_24_8_OES", + 0x84FA, "GL_UNSIGNED_INT_24_8_OES", }, { - 0x84FD, - "GL_MAX_TEXTURE_LOD_BIAS", + 0x84FD, "GL_MAX_TEXTURE_LOD_BIAS", }, { - 0x84FE, - "GL_TEXTURE_MAX_ANISOTROPY_EXT", + 0x84FE, "GL_TEXTURE_MAX_ANISOTROPY_EXT", }, { - 0x84FF, - "GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT", + 0x84FF, "GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT", }, { - 0x8507, - "GL_INCR_WRAP", + 0x8507, "GL_INCR_WRAP", }, { - 0x8508, - "GL_DECR_WRAP", + 0x8508, "GL_DECR_WRAP", }, { - 0x8513, - "GL_TEXTURE_CUBE_MAP", + 0x8513, "GL_TEXTURE_CUBE_MAP", }, { - 0x8514, - "GL_TEXTURE_BINDING_CUBE_MAP", + 0x8514, "GL_TEXTURE_BINDING_CUBE_MAP", }, { - 0x8515, - "GL_TEXTURE_CUBE_MAP_POSITIVE_X", + 0x8515, "GL_TEXTURE_CUBE_MAP_POSITIVE_X", }, { - 0x8516, - "GL_TEXTURE_CUBE_MAP_NEGATIVE_X", + 0x8516, "GL_TEXTURE_CUBE_MAP_NEGATIVE_X", }, { - 0x8517, - "GL_TEXTURE_CUBE_MAP_POSITIVE_Y", + 0x8517, "GL_TEXTURE_CUBE_MAP_POSITIVE_Y", }, { - 0x8518, - "GL_TEXTURE_CUBE_MAP_NEGATIVE_Y", + 0x8518, "GL_TEXTURE_CUBE_MAP_NEGATIVE_Y", }, { - 0x8519, - "GL_TEXTURE_CUBE_MAP_POSITIVE_Z", + 0x8519, "GL_TEXTURE_CUBE_MAP_POSITIVE_Z", }, { - 0x851A, - "GL_TEXTURE_CUBE_MAP_NEGATIVE_Z", + 0x851A, "GL_TEXTURE_CUBE_MAP_NEGATIVE_Z", }, { - 0x851C, - "GL_MAX_CUBE_MAP_TEXTURE_SIZE", + 0x851C, "GL_MAX_CUBE_MAP_TEXTURE_SIZE", }, { - 0x8576, - "GL_CONSTANT_CHROMIUM", + 0x8576, "GL_CONSTANT_CHROMIUM", }, { - 0x8589, - "GL_SRC1_ALPHA_EXT", + 0x8589, "GL_SRC1_ALPHA_EXT", }, { - 0x85B5, - "GL_VERTEX_ARRAY_BINDING_OES", + 0x85B5, "GL_VERTEX_ARRAY_BINDING_OES", }, { - 0x85BA, - "GL_UNSIGNED_SHORT_8_8_APPLE", + 0x85BA, "GL_UNSIGNED_SHORT_8_8_APPLE", }, { - 0x85BB, - "GL_UNSIGNED_SHORT_8_8_REV_APPLE", + 0x85BB, "GL_UNSIGNED_SHORT_8_8_REV_APPLE", }, { - 0x8622, - "GL_VERTEX_ATTRIB_ARRAY_ENABLED", + 0x8622, "GL_VERTEX_ATTRIB_ARRAY_ENABLED", }, { - 0x8623, - "GL_VERTEX_ATTRIB_ARRAY_SIZE", + 0x8623, "GL_VERTEX_ATTRIB_ARRAY_SIZE", }, { - 0x8624, - "GL_VERTEX_ATTRIB_ARRAY_STRIDE", + 0x8624, "GL_VERTEX_ATTRIB_ARRAY_STRIDE", }, { - 0x8625, - "GL_VERTEX_ATTRIB_ARRAY_TYPE", + 0x8625, "GL_VERTEX_ATTRIB_ARRAY_TYPE", }, { - 0x8626, - "GL_CURRENT_VERTEX_ATTRIB", + 0x8626, "GL_CURRENT_VERTEX_ATTRIB", }, { - 0x8645, - "GL_VERTEX_ATTRIB_ARRAY_POINTER", + 0x8645, "GL_VERTEX_ATTRIB_ARRAY_POINTER", }, { - 0x86A1, - "GL_TEXTURE_COMPRESSED", + 0x86A1, "GL_TEXTURE_COMPRESSED", }, { - 0x86A2, - "GL_NUM_COMPRESSED_TEXTURE_FORMATS", + 0x86A2, "GL_NUM_COMPRESSED_TEXTURE_FORMATS", }, { - 0x86A3, - "GL_COMPRESSED_TEXTURE_FORMATS", + 0x86A3, "GL_COMPRESSED_TEXTURE_FORMATS", }, { - 0x8740, - "GL_Z400_BINARY_AMD", + 0x8740, "GL_Z400_BINARY_AMD", }, { - 0x8741, - "GL_PROGRAM_BINARY_LENGTH_OES", + 0x8741, "GL_PROGRAM_BINARY_LENGTH_OES", }, { - 0x8764, - "GL_BUFFER_SIZE", + 0x8743, "GL_MIRROR_CLAMP_TO_EDGE_EXT", }, { - 0x8765, - "GL_BUFFER_USAGE", + 0x875F, "GL_PROGRAM_BINARY_FORMAT_MESA", }, { - 0x87EE, - "GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD", + 0x8764, "GL_BUFFER_SIZE", }, { - 0x87F9, - "GL_3DC_X_AMD", + 0x8765, "GL_BUFFER_USAGE", }, { - 0x87FA, - "GL_3DC_XY_AMD", + 0x87EE, "GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD", }, { - 0x87FE, - "GL_NUM_PROGRAM_BINARY_FORMATS_OES", + 0x87F9, "GL_3DC_X_AMD", }, { - 0x87FF, - "GL_PROGRAM_BINARY_FORMATS_OES", + 0x87FA, "GL_3DC_XY_AMD", }, { - 0x8800, - "GL_STENCIL_BACK_FUNC", + 0x87FE, "GL_NUM_PROGRAM_BINARY_FORMATS_OES", }, { - 0x8801, - "GL_STENCIL_BACK_FAIL", + 0x87FF, "GL_PROGRAM_BINARY_FORMATS_OES", }, { - 0x8802, - "GL_STENCIL_BACK_PASS_DEPTH_FAIL", + 0x8800, "GL_STENCIL_BACK_FUNC", }, { - 0x8803, - "GL_STENCIL_BACK_PASS_DEPTH_PASS", + 0x8801, "GL_STENCIL_BACK_FAIL", }, { - 0x8814, - "GL_RGBA32F_EXT", + 0x8802, "GL_STENCIL_BACK_PASS_DEPTH_FAIL", }, { - 0x8815, - "GL_RGB32F_EXT", + 0x8803, "GL_STENCIL_BACK_PASS_DEPTH_PASS", }, { - 0x8816, - "GL_ALPHA32F_EXT", + 0x8814, "GL_RGBA32F_EXT", }, { - 0x8818, - "GL_LUMINANCE32F_EXT", + 0x8815, "GL_RGB32F_EXT", }, { - 0x8819, - "GL_LUMINANCE_ALPHA32F_EXT", + 0x8816, "GL_ALPHA32F_EXT", }, { - 0x881A, - "GL_RGBA16F_EXT", + 0x8818, "GL_LUMINANCE32F_EXT", }, { - 0x881B, - "GL_RGB16F_EXT", + 0x8819, "GL_LUMINANCE_ALPHA32F_EXT", }, { - 0x881C, - "GL_ALPHA16F_EXT", + 0x881A, "GL_RGBA16F_EXT", }, { - 0x881E, - "GL_LUMINANCE16F_EXT", + 0x881B, "GL_RGB16F_EXT", }, { - 0x881F, - "GL_LUMINANCE_ALPHA16F_EXT", + 0x881C, "GL_ALPHA16F_EXT", }, { - 0x8823, - "GL_WRITEONLY_RENDERING_QCOM", + 0x881E, "GL_LUMINANCE16F_EXT", }, { - 0x8824, - "GL_MAX_DRAW_BUFFERS_EXT", + 0x881F, "GL_LUMINANCE_ALPHA16F_EXT", }, { - 0x8825, - "GL_DRAW_BUFFER0_EXT", + 0x8823, "GL_WRITEONLY_RENDERING_QCOM", }, { - 0x8826, - "GL_DRAW_BUFFER1_EXT", + 0x8824, "GL_MAX_DRAW_BUFFERS_EXT", }, { - 0x8827, - "GL_DRAW_BUFFER2_EXT", + 0x8825, "GL_DRAW_BUFFER0_EXT", }, { - 0x8828, - "GL_DRAW_BUFFER3_EXT", + 0x8826, "GL_DRAW_BUFFER1_EXT", }, { - 0x8829, - "GL_DRAW_BUFFER4_EXT", + 0x8827, "GL_DRAW_BUFFER2_EXT", }, { - 0x882A, - "GL_DRAW_BUFFER5_EXT", + 0x8828, "GL_DRAW_BUFFER3_EXT", }, { - 0x882B, - "GL_DRAW_BUFFER6_EXT", + 0x8829, "GL_DRAW_BUFFER4_EXT", }, { - 0x882C, - "GL_DRAW_BUFFER7_EXT", + 0x882A, "GL_DRAW_BUFFER5_EXT", }, { - 0x882D, - "GL_DRAW_BUFFER8_EXT", + 0x882B, "GL_DRAW_BUFFER6_EXT", }, { - 0x882E, - "GL_DRAW_BUFFER9_EXT", + 0x882C, "GL_DRAW_BUFFER7_EXT", }, { - 0x882F, - "GL_DRAW_BUFFER10_EXT", + 0x882D, "GL_DRAW_BUFFER8_EXT", }, { - 0x8830, - "GL_DRAW_BUFFER11_EXT", + 0x882E, "GL_DRAW_BUFFER9_EXT", }, { - 0x8831, - "GL_DRAW_BUFFER12_EXT", + 0x882F, "GL_DRAW_BUFFER10_EXT", }, { - 0x8832, - "GL_DRAW_BUFFER13_EXT", + 0x8830, "GL_DRAW_BUFFER11_EXT", }, { - 0x8833, - "GL_DRAW_BUFFER14_EXT", + 0x8831, "GL_DRAW_BUFFER12_EXT", }, { - 0x8834, - "GL_DRAW_BUFFER15_EXT", + 0x8832, "GL_DRAW_BUFFER13_EXT", }, { - 0x883D, - "GL_BLEND_EQUATION_ALPHA", + 0x8833, "GL_DRAW_BUFFER14_EXT", }, { - 0x884A, - "GL_TEXTURE_DEPTH_SIZE", + 0x8834, "GL_DRAW_BUFFER15_EXT", }, { - 0x884C, - "GL_TEXTURE_COMPARE_MODE_EXT", + 0x883D, "GL_BLEND_EQUATION_ALPHA", }, { - 0x884D, - "GL_TEXTURE_COMPARE_FUNC_EXT", + 0x884A, "GL_TEXTURE_DEPTH_SIZE", }, { - 0x884E, - "GL_COMPARE_REF_TO_TEXTURE_EXT", + 0x884C, "GL_TEXTURE_COMPARE_MODE_EXT", }, { - 0x8864, - "GL_QUERY_COUNTER_BITS_EXT", + 0x884D, "GL_TEXTURE_COMPARE_FUNC_EXT", }, { - 0x8865, - "GL_CURRENT_QUERY_EXT", + 0x884E, "GL_COMPARE_REF_TO_TEXTURE_EXT", }, { - 0x8866, - "GL_QUERY_RESULT_EXT", + 0x8864, "GL_QUERY_COUNTER_BITS_EXT", }, { - 0x8867, - "GL_QUERY_RESULT_AVAILABLE_EXT", + 0x8865, "GL_CURRENT_QUERY_EXT", }, { - 0x8869, - "GL_MAX_VERTEX_ATTRIBS", + 0x8866, "GL_QUERY_RESULT_EXT", }, { - 0x886A, - "GL_VERTEX_ATTRIB_ARRAY_NORMALIZED", + 0x8867, "GL_QUERY_RESULT_AVAILABLE_EXT", }, { - 0x886C, - "GL_MAX_TESS_CONTROL_INPUT_COMPONENTS_OES", + 0x8869, "GL_MAX_VERTEX_ATTRIBS", }, { - 0x886D, - "GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS_OES", + 0x886A, "GL_VERTEX_ATTRIB_ARRAY_NORMALIZED", }, { - 0x8872, - "GL_MAX_TEXTURE_IMAGE_UNITS", + 0x886C, "GL_MAX_TESS_CONTROL_INPUT_COMPONENTS_OES", }, { - 0x887F, - "GL_GEOMETRY_SHADER_INVOCATIONS_OES", + 0x886D, "GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS_OES", }, { - 0x8892, - "GL_ARRAY_BUFFER", + 0x8872, "GL_MAX_TEXTURE_IMAGE_UNITS", }, { - 0x8893, - "GL_ELEMENT_ARRAY_BUFFER", + 0x887F, "GL_GEOMETRY_SHADER_INVOCATIONS_OES", }, { - 0x8894, - "GL_ARRAY_BUFFER_BINDING", + 0x8892, "GL_ARRAY_BUFFER", }, { - 0x8895, - "GL_ELEMENT_ARRAY_BUFFER_BINDING", + 0x8893, "GL_ELEMENT_ARRAY_BUFFER", }, { - 0x889F, - "GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING", + 0x8894, "GL_ARRAY_BUFFER_BINDING", }, { - 0x88B8, - "GL_READ_ONLY", + 0x8895, "GL_ELEMENT_ARRAY_BUFFER_BINDING", }, { - 0x88B9, - "GL_WRITE_ONLY_OES", + 0x889F, "GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING", }, { - 0x88BA, - "GL_READ_WRITE", + 0x88B8, "GL_READ_ONLY", }, { - 0x88BB, - "GL_BUFFER_ACCESS_OES", + 0x88B9, "GL_WRITE_ONLY_OES", }, { - 0x88BC, - "GL_BUFFER_MAPPED_OES", + 0x88BA, "GL_READ_WRITE", }, { - 0x88BD, - "GL_BUFFER_MAP_POINTER_OES", + 0x88BB, "GL_BUFFER_ACCESS_OES", }, { - 0x88BF, - "GL_TIME_ELAPSED_EXT", + 0x88BC, "GL_BUFFER_MAPPED_OES", }, { - 0x88E0, - "GL_STREAM_DRAW", + 0x88BD, "GL_BUFFER_MAP_POINTER_OES", }, { - 0x88E1, - "GL_STREAM_READ", + 0x88BF, "GL_TIME_ELAPSED_EXT", }, { - 0x88E2, - "GL_STREAM_COPY", + 0x88E0, "GL_STREAM_DRAW", }, { - 0x88E4, - "GL_STATIC_DRAW", + 0x88E1, "GL_STREAM_READ", }, { - 0x88E5, - "GL_STATIC_READ", + 0x88E2, "GL_STREAM_COPY", }, { - 0x88E6, - "GL_STATIC_COPY", + 0x88E4, "GL_STATIC_DRAW", }, { - 0x88E8, - "GL_DYNAMIC_DRAW", + 0x88E5, "GL_STATIC_READ", }, { - 0x88E9, - "GL_DYNAMIC_READ", + 0x88E6, "GL_STATIC_COPY", }, { - 0x88EA, - "GL_DYNAMIC_COPY", + 0x88E8, "GL_DYNAMIC_DRAW", }, { - 0x88EB, - "GL_PIXEL_PACK_BUFFER", + 0x88E9, "GL_DYNAMIC_READ", }, { - 0x88EC, - "GL_PIXEL_UNPACK_BUFFER", + 0x88EA, "GL_DYNAMIC_COPY", }, { - 0x88ED, - "GL_PIXEL_PACK_BUFFER_BINDING", + 0x88EB, "GL_PIXEL_PACK_BUFFER_NV", }, { - 0x88EE, - "GL_ETC1_SRGB8_NV", + 0x88EC, "GL_PIXEL_UNPACK_BUFFER_NV", }, { - 0x88EF, - "GL_PIXEL_UNPACK_BUFFER_BINDING", + 0x88ED, "GL_PIXEL_PACK_BUFFER_BINDING_NV", }, { - 0x88F0, - "GL_DEPTH24_STENCIL8_OES", + 0x88EE, "GL_ETC1_SRGB8_NV", }, { - 0x88F1, - "GL_TEXTURE_STENCIL_SIZE", + 0x88EF, "GL_PIXEL_UNPACK_BUFFER_BINDING_NV", }, { - 0x88F9, - "GL_SRC1_COLOR_EXT", + 0x88F0, "GL_DEPTH24_STENCIL8_OES", }, { - 0x88FA, - "GL_ONE_MINUS_SRC1_COLOR_EXT", + 0x88F1, "GL_TEXTURE_STENCIL_SIZE", }, { - 0x88FB, - "GL_ONE_MINUS_SRC1_ALPHA_EXT", + 0x88F9, "GL_SRC1_COLOR_EXT", }, { - 0x88FC, - "GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT", + 0x88FA, "GL_ONE_MINUS_SRC1_COLOR_EXT", }, { - 0x88FD, - "GL_VERTEX_ATTRIB_ARRAY_INTEGER", + 0x88FB, "GL_ONE_MINUS_SRC1_ALPHA_EXT", }, { - 0x88FE, - "GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE", + 0x88FC, "GL_MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT", }, { - 0x88FF, - "GL_MAX_ARRAY_TEXTURE_LAYERS", + 0x88FD, "GL_VERTEX_ATTRIB_ARRAY_INTEGER", }, { - 0x8904, - "GL_MIN_PROGRAM_TEXEL_OFFSET", + 0x88FE, "GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ANGLE", }, { - 0x8905, - "GL_MAX_PROGRAM_TEXEL_OFFSET", + 0x88FF, "GL_MAX_ARRAY_TEXTURE_LAYERS", }, { - 0x8914, - "GL_SAMPLES_PASSED_ARB", + 0x8904, "GL_MIN_PROGRAM_TEXEL_OFFSET", }, { - 0x8916, - "GL_GEOMETRY_LINKED_VERTICES_OUT_OES", + 0x8905, "GL_MAX_PROGRAM_TEXEL_OFFSET", }, { - 0x8917, - "GL_GEOMETRY_LINKED_INPUT_TYPE_OES", + 0x8914, "GL_SAMPLES_PASSED_ARB", }, { - 0x8918, - "GL_GEOMETRY_LINKED_OUTPUT_TYPE_OES", + 0x8916, "GL_GEOMETRY_LINKED_VERTICES_OUT_OES", }, { - 0x8919, - "GL_SAMPLER_BINDING", + 0x8917, "GL_GEOMETRY_LINKED_INPUT_TYPE_OES", }, { - 0x8A11, - "GL_UNIFORM_BUFFER", + 0x8918, "GL_GEOMETRY_LINKED_OUTPUT_TYPE_OES", }, { - 0x8A1F, - "GL_RGB_422_APPLE", + 0x8919, "GL_SAMPLER_BINDING", }, { - 0x8A28, - "GL_UNIFORM_BUFFER_BINDING", + 0x8A11, "GL_UNIFORM_BUFFER", }, { - 0x8A29, - "GL_UNIFORM_BUFFER_START", + 0x8A1F, "GL_RGB_422_APPLE", }, { - 0x8A2A, - "GL_UNIFORM_BUFFER_SIZE", + 0x8A28, "GL_UNIFORM_BUFFER_BINDING", }, { - 0x8A2B, - "GL_MAX_VERTEX_UNIFORM_BLOCKS", + 0x8A29, "GL_UNIFORM_BUFFER_START", }, { - 0x8A2C, - "GL_MAX_GEOMETRY_UNIFORM_BLOCKS_OES", + 0x8A2A, "GL_UNIFORM_BUFFER_SIZE", }, { - 0x8A2D, - "GL_MAX_FRAGMENT_UNIFORM_BLOCKS", + 0x8A2B, "GL_MAX_VERTEX_UNIFORM_BLOCKS", }, { - 0x8A2E, - "GL_MAX_COMBINED_UNIFORM_BLOCKS", + 0x8A2C, "GL_MAX_GEOMETRY_UNIFORM_BLOCKS_OES", }, { - 0x8A2F, - "GL_MAX_UNIFORM_BUFFER_BINDINGS", + 0x8A2D, "GL_MAX_FRAGMENT_UNIFORM_BLOCKS", }, { - 0x8A30, - "GL_MAX_UNIFORM_BLOCK_SIZE", + 0x8A2E, "GL_MAX_COMBINED_UNIFORM_BLOCKS", }, { - 0x8A31, - "GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS", + 0x8A2F, "GL_MAX_UNIFORM_BUFFER_BINDINGS", }, { - 0x8A32, - "GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS_OES", + 0x8A30, "GL_MAX_UNIFORM_BLOCK_SIZE", }, { - 0x8A33, - "GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS", + 0x8A31, "GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS", }, { - 0x8A34, - "GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT", + 0x8A32, "GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS_OES", }, { - 0x8A35, - "GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH", + 0x8A33, "GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS", }, { - 0x8A36, - "GL_ACTIVE_UNIFORM_BLOCKS", + 0x8A34, "GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT", }, { - 0x8A37, - "GL_UNIFORM_TYPE", + 0x8A35, "GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH", }, { - 0x8A38, - "GL_UNIFORM_SIZE", + 0x8A36, "GL_ACTIVE_UNIFORM_BLOCKS", }, { - 0x8A39, - "GL_UNIFORM_NAME_LENGTH", + 0x8A37, "GL_UNIFORM_TYPE", }, { - 0x8A3A, - "GL_UNIFORM_BLOCK_INDEX", + 0x8A38, "GL_UNIFORM_SIZE", }, { - 0x8A3B, - "GL_UNIFORM_OFFSET", + 0x8A39, "GL_UNIFORM_NAME_LENGTH", }, { - 0x8A3C, - "GL_UNIFORM_ARRAY_STRIDE", + 0x8A3A, "GL_UNIFORM_BLOCK_INDEX", }, { - 0x8A3D, - "GL_UNIFORM_MATRIX_STRIDE", + 0x8A3B, "GL_UNIFORM_OFFSET", }, { - 0x8A3E, - "GL_UNIFORM_IS_ROW_MAJOR", + 0x8A3C, "GL_UNIFORM_ARRAY_STRIDE", }, { - 0x8A3F, - "GL_UNIFORM_BLOCK_BINDING", + 0x8A3D, "GL_UNIFORM_MATRIX_STRIDE", }, { - 0x8A40, - "GL_UNIFORM_BLOCK_DATA_SIZE", + 0x8A3E, "GL_UNIFORM_IS_ROW_MAJOR", }, { - 0x8A41, - "GL_UNIFORM_BLOCK_NAME_LENGTH", + 0x8A3F, "GL_UNIFORM_BLOCK_BINDING", }, { - 0x8A42, - "GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS", + 0x8A40, "GL_UNIFORM_BLOCK_DATA_SIZE", }, { - 0x8A43, - "GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES", + 0x8A41, "GL_UNIFORM_BLOCK_NAME_LENGTH", }, { - 0x8A44, - "GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER", + 0x8A42, "GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS", }, { - 0x8A46, - "GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER", + 0x8A43, "GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES", }, { - 0x8A48, - "GL_TEXTURE_SRGB_DECODE_EXT", + 0x8A44, "GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER", }, { - 0x8A49, - "GL_DECODE_EXT", + 0x8A46, "GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER", }, { - 0x8A4A, - "GL_SKIP_DECODE_EXT", + 0x8A48, "GL_TEXTURE_SRGB_DECODE_EXT", }, { - 0x8A4F, - "GL_PROGRAM_PIPELINE_OBJECT_EXT", + 0x8A49, "GL_DECODE_EXT", }, { - 0x8A51, - "GL_RGB_RAW_422_APPLE", + 0x8A4A, "GL_SKIP_DECODE_EXT", }, { - 0x8A52, - "GL_FRAGMENT_SHADER_DISCARDS_SAMPLES_EXT", + 0x8A4F, "GL_PROGRAM_PIPELINE_OBJECT_EXT", }, { - 0x8A53, - "GL_SYNC_OBJECT_APPLE", + 0x8A51, "GL_RGB_RAW_422_APPLE", }, { - 0x8A54, - "GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT", + 0x8A52, "GL_FRAGMENT_SHADER_DISCARDS_SAMPLES_EXT", }, { - 0x8A55, - "GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT", + 0x8A53, "GL_SYNC_OBJECT_APPLE", }, { - 0x8A56, - "GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT", + 0x8A54, "GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT", }, { - 0x8A57, - "GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT", + 0x8A55, "GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT", }, { - 0x8AF0, - "GL_TEXTURE_FILTERING_HINT_CHROMIUM", + 0x8A56, "GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT", }, { - 0x8AF1, - "GL_COLOR_SPACE_UNSPECIFIED_CHROMIUM", + 0x8A57, "GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT", }, { - 0x8AF2, - "GL_COLOR_SPACE_SCRGB_LINEAR_CHROMIUM", + 0x8AF0, "GL_TEXTURE_FILTERING_HINT_CHROMIUM", }, { - 0x8AF3, - "GL_COLOR_SPACE_SRGB_CHROMIUM", + 0x8AF1, "GL_COLOR_SPACE_UNSPECIFIED_CHROMIUM", }, { - 0x8AF4, - "GL_COLOR_SPACE_DISPLAY_P3_CHROMIUM", + 0x8AF2, "GL_COLOR_SPACE_SCRGB_LINEAR_CHROMIUM", }, { - 0x8B30, - "GL_FRAGMENT_SHADER", + 0x8AF3, "GL_COLOR_SPACE_SRGB_CHROMIUM", }, { - 0x8B31, - "GL_VERTEX_SHADER", + 0x8AF4, "GL_COLOR_SPACE_DISPLAY_P3_CHROMIUM", }, { - 0x8B40, - "GL_PROGRAM_OBJECT_EXT", + 0x8B30, "GL_FRAGMENT_SHADER", }, { - 0x8B48, - "GL_SHADER_OBJECT_EXT", + 0x8B31, "GL_VERTEX_SHADER", }, { - 0x8B49, - "GL_MAX_FRAGMENT_UNIFORM_COMPONENTS", + 0x8B40, "GL_PROGRAM_OBJECT_EXT", }, { - 0x8B4A, - "GL_MAX_VERTEX_UNIFORM_COMPONENTS", + 0x8B48, "GL_SHADER_OBJECT_EXT", }, { - 0x8B4B, - "GL_MAX_VARYING_COMPONENTS", + 0x8B49, "GL_MAX_FRAGMENT_UNIFORM_COMPONENTS", }, { - 0x8B4C, - "GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS", + 0x8B4A, "GL_MAX_VERTEX_UNIFORM_COMPONENTS", }, { - 0x8B4D, - "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS", + 0x8B4B, "GL_MAX_VARYING_COMPONENTS", }, { - 0x8B4F, - "GL_SHADER_TYPE", + 0x8B4C, "GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS", }, { - 0x8B50, - "GL_FLOAT_VEC2", + 0x8B4D, "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS", }, { - 0x8B51, - "GL_FLOAT_VEC3", + 0x8B4F, "GL_SHADER_TYPE", }, { - 0x8B52, - "GL_FLOAT_VEC4", + 0x8B50, "GL_FLOAT_VEC2", }, { - 0x8B53, - "GL_INT_VEC2", + 0x8B51, "GL_FLOAT_VEC3", }, { - 0x8B54, - "GL_INT_VEC3", + 0x8B52, "GL_FLOAT_VEC4", }, { - 0x8B55, - "GL_INT_VEC4", + 0x8B53, "GL_INT_VEC2", }, { - 0x8B56, - "GL_BOOL", + 0x8B54, "GL_INT_VEC3", }, { - 0x8B57, - "GL_BOOL_VEC2", + 0x8B55, "GL_INT_VEC4", }, { - 0x8B58, - "GL_BOOL_VEC3", + 0x8B56, "GL_BOOL", }, { - 0x8B59, - "GL_BOOL_VEC4", + 0x8B57, "GL_BOOL_VEC2", }, { - 0x8B5A, - "GL_FLOAT_MAT2", + 0x8B58, "GL_BOOL_VEC3", }, { - 0x8B5B, - "GL_FLOAT_MAT3", + 0x8B59, "GL_BOOL_VEC4", }, { - 0x8B5C, - "GL_FLOAT_MAT4", + 0x8B5A, "GL_FLOAT_MAT2", }, { - 0x8B5E, - "GL_SAMPLER_2D", + 0x8B5B, "GL_FLOAT_MAT3", }, { - 0x8B5F, - "GL_SAMPLER_3D_OES", + 0x8B5C, "GL_FLOAT_MAT4", }, { - 0x8B60, - "GL_SAMPLER_CUBE", + 0x8B5E, "GL_SAMPLER_2D", }, { - 0x8B62, - "GL_SAMPLER_2D_SHADOW_EXT", + 0x8B5F, "GL_SAMPLER_3D_OES", }, { - 0x8B63, - "GL_SAMPLER_2D_RECT_ARB", + 0x8B60, "GL_SAMPLER_CUBE", }, { - 0x8B65, - "GL_FLOAT_MAT2x3_NV", + 0x8B62, "GL_SAMPLER_2D_SHADOW_EXT", }, { - 0x8B66, - "GL_FLOAT_MAT2x4_NV", + 0x8B63, "GL_SAMPLER_2D_RECT_ARB", }, { - 0x8B67, - "GL_FLOAT_MAT3x2_NV", + 0x8B65, "GL_FLOAT_MAT2x3_NV", }, { - 0x8B68, - "GL_FLOAT_MAT3x4_NV", + 0x8B66, "GL_FLOAT_MAT2x4_NV", }, { - 0x8B69, - "GL_FLOAT_MAT4x2_NV", + 0x8B67, "GL_FLOAT_MAT3x2_NV", }, { - 0x8B6A, - "GL_FLOAT_MAT4x3_NV", + 0x8B68, "GL_FLOAT_MAT3x4_NV", }, { - 0x8B80, - "GL_DELETE_STATUS", + 0x8B69, "GL_FLOAT_MAT4x2_NV", }, { - 0x8B81, - "GL_COMPILE_STATUS", + 0x8B6A, "GL_FLOAT_MAT4x3_NV", }, { - 0x8B82, - "GL_LINK_STATUS", + 0x8B80, "GL_DELETE_STATUS", }, { - 0x8B83, - "GL_VALIDATE_STATUS", + 0x8B81, "GL_COMPILE_STATUS", }, { - 0x8B84, - "GL_INFO_LOG_LENGTH", + 0x8B82, "GL_LINK_STATUS", }, { - 0x8B85, - "GL_ATTACHED_SHADERS", + 0x8B83, "GL_VALIDATE_STATUS", }, { - 0x8B86, - "GL_ACTIVE_UNIFORMS", + 0x8B84, "GL_INFO_LOG_LENGTH", }, { - 0x8B87, - "GL_ACTIVE_UNIFORM_MAX_LENGTH", + 0x8B85, "GL_ATTACHED_SHADERS", }, { - 0x8B88, - "GL_SHADER_SOURCE_LENGTH", + 0x8B86, "GL_ACTIVE_UNIFORMS", }, { - 0x8B89, - "GL_ACTIVE_ATTRIBUTES", + 0x8B87, "GL_ACTIVE_UNIFORM_MAX_LENGTH", }, { - 0x8B8A, - "GL_ACTIVE_ATTRIBUTE_MAX_LENGTH", + 0x8B88, "GL_SHADER_SOURCE_LENGTH", }, { - 0x8B8B, - "GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES", + 0x8B89, "GL_ACTIVE_ATTRIBUTES", }, { - 0x8B8C, - "GL_SHADING_LANGUAGE_VERSION", + 0x8B8A, "GL_ACTIVE_ATTRIBUTE_MAX_LENGTH", }, { - 0x8B8D, - "GL_CURRENT_PROGRAM", + 0x8B8B, "GL_FRAGMENT_SHADER_DERIVATIVE_HINT_OES", }, { - 0x8B90, - "GL_PALETTE4_RGB8_OES", + 0x8B8C, "GL_SHADING_LANGUAGE_VERSION", }, { - 0x8B91, - "GL_PALETTE4_RGBA8_OES", + 0x8B8D, "GL_CURRENT_PROGRAM", }, { - 0x8B92, - "GL_PALETTE4_R5_G6_B5_OES", + 0x8B90, "GL_PALETTE4_RGB8_OES", }, { - 0x8B93, - "GL_PALETTE4_RGBA4_OES", + 0x8B91, "GL_PALETTE4_RGBA8_OES", }, { - 0x8B94, - "GL_PALETTE4_RGB5_A1_OES", + 0x8B92, "GL_PALETTE4_R5_G6_B5_OES", }, { - 0x8B95, - "GL_PALETTE8_RGB8_OES", + 0x8B93, "GL_PALETTE4_RGBA4_OES", }, { - 0x8B96, - "GL_PALETTE8_RGBA8_OES", + 0x8B94, "GL_PALETTE4_RGB5_A1_OES", }, { - 0x8B97, - "GL_PALETTE8_R5_G6_B5_OES", + 0x8B95, "GL_PALETTE8_RGB8_OES", }, { - 0x8B98, - "GL_PALETTE8_RGBA4_OES", + 0x8B96, "GL_PALETTE8_RGBA8_OES", }, { - 0x8B99, - "GL_PALETTE8_RGB5_A1_OES", + 0x8B97, "GL_PALETTE8_R5_G6_B5_OES", }, { - 0x8B9A, - "GL_IMPLEMENTATION_COLOR_READ_TYPE", + 0x8B98, "GL_PALETTE8_RGBA4_OES", }, { - 0x8B9B, - "GL_IMPLEMENTATION_COLOR_READ_FORMAT", + 0x8B99, "GL_PALETTE8_RGB5_A1_OES", }, { - 0x8BBB, - "GL_FRAMEBUFFER_FLIP_Y_MESA", + 0x8B9A, "GL_IMPLEMENTATION_COLOR_READ_TYPE", }, { - 0x8BC0, - "GL_COUNTER_TYPE_AMD", + 0x8B9B, "GL_IMPLEMENTATION_COLOR_READ_FORMAT", }, { - 0x8BC1, - "GL_COUNTER_RANGE_AMD", + 0x8BBB, "GL_FRAMEBUFFER_FLIP_Y_MESA", }, { - 0x8BC2, - "GL_UNSIGNED_INT64_AMD", + 0x8BC0, "GL_COUNTER_TYPE_AMD", }, { - 0x8BC3, - "GL_PERCENTAGE_AMD", + 0x8BC1, "GL_COUNTER_RANGE_AMD", }, { - 0x8BC4, - "GL_PERFMON_RESULT_AVAILABLE_AMD", + 0x8BC2, "GL_UNSIGNED_INT64_AMD", }, { - 0x8BC5, - "GL_PERFMON_RESULT_SIZE_AMD", + 0x8BC3, "GL_PERCENTAGE_AMD", }, { - 0x8BC6, - "GL_PERFMON_RESULT_AMD", + 0x8BC4, "GL_PERFMON_RESULT_AVAILABLE_AMD", }, { - 0x8BD2, - "GL_TEXTURE_WIDTH_QCOM", + 0x8BC5, "GL_PERFMON_RESULT_SIZE_AMD", }, { - 0x8BD3, - "GL_TEXTURE_HEIGHT_QCOM", + 0x8BC6, "GL_PERFMON_RESULT_AMD", }, { - 0x8BD4, - "GL_TEXTURE_DEPTH_QCOM", + 0x8BD2, "GL_TEXTURE_WIDTH_QCOM", }, { - 0x8BD5, - "GL_TEXTURE_INTERNAL_FORMAT_QCOM", + 0x8BD3, "GL_TEXTURE_HEIGHT_QCOM", }, { - 0x8BD6, - "GL_TEXTURE_FORMAT_QCOM", + 0x8BD4, "GL_TEXTURE_DEPTH_QCOM", }, { - 0x8BD7, - "GL_TEXTURE_TYPE_QCOM", + 0x8BD5, "GL_TEXTURE_INTERNAL_FORMAT_QCOM", }, { - 0x8BD8, - "GL_TEXTURE_IMAGE_VALID_QCOM", + 0x8BD6, "GL_TEXTURE_FORMAT_QCOM", }, { - 0x8BD9, - "GL_TEXTURE_NUM_LEVELS_QCOM", + 0x8BD7, "GL_TEXTURE_TYPE_QCOM", }, { - 0x8BDA, - "GL_TEXTURE_TARGET_QCOM", + 0x8BD8, "GL_TEXTURE_IMAGE_VALID_QCOM", }, { - 0x8BDB, - "GL_TEXTURE_OBJECT_VALID_QCOM", + 0x8BD9, "GL_TEXTURE_NUM_LEVELS_QCOM", }, { - 0x8BDC, - "GL_STATE_RESTORE", + 0x8BDA, "GL_TEXTURE_TARGET_QCOM", }, { - 0x8BE7, - "GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT", + 0x8BDB, "GL_TEXTURE_OBJECT_VALID_QCOM", }, { - 0x8BFA, - "GL_TEXTURE_PROTECTED_EXT", + 0x8BDC, "GL_STATE_RESTORE", }, { - 0x8C00, - "GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG", + 0x8BE7, "GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT", }, { - 0x8C01, - "GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG", + 0x8BFA, "GL_TEXTURE_PROTECTED_EXT", }, { - 0x8C02, - "GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG", + 0x8BFB, "GL_TEXTURE_FOVEATED_FEATURE_BITS_QCOM", }, { - 0x8C03, - "GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG", + 0x8BFC, "GL_TEXTURE_FOVEATED_MIN_PIXEL_DENSITY_QCOM", }, { - 0x8C0A, - "GL_SGX_BINARY_IMG", + 0x8BFD, "GL_TEXTURE_FOVEATED_FEATURE_QUERY_QCOM", }, { - 0x8C10, - "GL_TEXTURE_RED_TYPE", + 0x8BFE, "GL_TEXTURE_FOVEATED_NUM_FOCAL_POINTS_QUERY_QCOM", }, { - 0x8C11, - "GL_TEXTURE_GREEN_TYPE", + 0x8BFF, "GL_FRAMEBUFFER_INCOMPLETE_FOVEATION_QCOM", }, { - 0x8C12, - "GL_TEXTURE_BLUE_TYPE", + 0x8C00, "GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG", }, { - 0x8C13, - "GL_TEXTURE_ALPHA_TYPE", + 0x8C01, "GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG", }, { - 0x8C16, - "GL_TEXTURE_DEPTH_TYPE", + 0x8C02, "GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG", }, { - 0x8C17, - "GL_UNSIGNED_NORMALIZED_EXT", + 0x8C03, "GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG", }, { - 0x8C1A, - "GL_TEXTURE_2D_ARRAY", + 0x8C0A, "GL_SGX_BINARY_IMG", }, { - 0x8C1D, - "GL_TEXTURE_BINDING_2D_ARRAY", + 0x8C10, "GL_TEXTURE_RED_TYPE", }, { - 0x8C29, - "GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_OES", + 0x8C11, "GL_TEXTURE_GREEN_TYPE", }, { - 0x8C2A, - "GL_TEXTURE_BUFFER_OES", + 0x8C12, "GL_TEXTURE_BLUE_TYPE", }, { - 0x8C2B, - "GL_MAX_TEXTURE_BUFFER_SIZE_OES", + 0x8C13, "GL_TEXTURE_ALPHA_TYPE", }, { - 0x8C2C, - "GL_TEXTURE_BINDING_BUFFER_OES", + 0x8C16, "GL_TEXTURE_DEPTH_TYPE", }, { - 0x8C2D, - "GL_TEXTURE_BUFFER_DATA_STORE_BINDING_OES", + 0x8C17, "GL_UNSIGNED_NORMALIZED_EXT", }, { - 0x8C2F, - "GL_ANY_SAMPLES_PASSED_EXT", + 0x8C1A, "GL_TEXTURE_2D_ARRAY", }, { - 0x8C36, - "GL_SAMPLE_SHADING_OES", + 0x8C1D, "GL_TEXTURE_BINDING_2D_ARRAY", }, { - 0x8C37, - "GL_MIN_SAMPLE_SHADING_VALUE_OES", + 0x8C29, "GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_OES", }, { - 0x8C3A, - "GL_R11F_G11F_B10F_APPLE", + 0x8C2A, "GL_TEXTURE_BUFFER_OES", }, { - 0x8C3B, - "GL_UNSIGNED_INT_10F_11F_11F_REV_APPLE", + 0x8C2B, "GL_MAX_TEXTURE_BUFFER_SIZE_OES", }, { - 0x8C3D, - "GL_RGB9_E5_APPLE", + 0x8C2C, "GL_TEXTURE_BINDING_BUFFER_OES", }, { - 0x8C3E, - "GL_UNSIGNED_INT_5_9_9_9_REV_APPLE", + 0x8C2D, "GL_TEXTURE_BUFFER_DATA_STORE_BINDING_OES", }, { - 0x8C3F, - "GL_TEXTURE_SHARED_SIZE", + 0x8C2F, "GL_ANY_SAMPLES_PASSED_EXT", }, { - 0x8C40, - "GL_SRGB_EXT", + 0x8C36, "GL_SAMPLE_SHADING_OES", }, { - 0x8C41, - "GL_SRGB8_NV", + 0x8C37, "GL_MIN_SAMPLE_SHADING_VALUE_OES", }, { - 0x8C42, - "GL_SRGB_ALPHA_EXT", + 0x8C3A, "GL_R11F_G11F_B10F_APPLE", }, { - 0x8C43, - "GL_SRGB8_ALPHA8_EXT", + 0x8C3B, "GL_UNSIGNED_INT_10F_11F_11F_REV_APPLE", }, { - 0x8C44, - "GL_SLUMINANCE_ALPHA_NV", + 0x8C3D, "GL_RGB9_E5_APPLE", }, { - 0x8C45, - "GL_SLUMINANCE8_ALPHA8_NV", + 0x8C3E, "GL_UNSIGNED_INT_5_9_9_9_REV_APPLE", }, { - 0x8C46, - "GL_SLUMINANCE_NV", + 0x8C3F, "GL_TEXTURE_SHARED_SIZE", }, { - 0x8C47, - "GL_SLUMINANCE8_NV", + 0x8C40, "GL_SRGB_EXT", }, { - 0x8C4C, - "GL_COMPRESSED_SRGB_S3TC_DXT1_NV", + 0x8C41, "GL_SRGB8_NV", }, { - 0x8C4D, - "GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_NV", + 0x8C42, "GL_SRGB_ALPHA_EXT", }, { - 0x8C4E, - "GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_NV", + 0x8C43, "GL_SRGB8_ALPHA8_EXT", }, { - 0x8C4F, - "GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_NV", + 0x8C44, "GL_SLUMINANCE_ALPHA_NV", }, { - 0x8C76, - "GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH", + 0x8C45, "GL_SLUMINANCE8_ALPHA8_NV", }, { - 0x8C7F, - "GL_TRANSFORM_FEEDBACK_BUFFER_MODE", + 0x8C46, "GL_SLUMINANCE_NV", }, { - 0x8C80, - "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS", + 0x8C47, "GL_SLUMINANCE8_NV", }, { - 0x8C83, - "GL_TRANSFORM_FEEDBACK_VARYINGS", + 0x8C4C, "GL_COMPRESSED_SRGB_S3TC_DXT1_EXT", }, { - 0x8C84, - "GL_TRANSFORM_FEEDBACK_BUFFER_START", + 0x8C4D, "GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT", }, { - 0x8C85, - "GL_TRANSFORM_FEEDBACK_BUFFER_SIZE", + 0x8C4E, "GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT", }, { - 0x8C87, - "GL_PRIMITIVES_GENERATED_OES", + 0x8C4F, "GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT", }, { - 0x8C88, - "GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN", + 0x8C76, "GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH", }, { - 0x8C89, - "GL_RASTERIZER_DISCARD", + 0x8C7F, "GL_TRANSFORM_FEEDBACK_BUFFER_MODE", }, { - 0x8C8A, - "GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS", + 0x8C80, "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS", }, { - 0x8C8B, - "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS", + 0x8C83, "GL_TRANSFORM_FEEDBACK_VARYINGS", }, { - 0x8C8C, - "GL_INTERLEAVED_ATTRIBS", + 0x8C84, "GL_TRANSFORM_FEEDBACK_BUFFER_START", }, { - 0x8C8D, - "GL_SEPARATE_ATTRIBS", + 0x8C85, "GL_TRANSFORM_FEEDBACK_BUFFER_SIZE", }, { - 0x8C8E, - "GL_TRANSFORM_FEEDBACK_BUFFER", + 0x8C87, "GL_PRIMITIVES_GENERATED_OES", }, { - 0x8C8F, - "GL_TRANSFORM_FEEDBACK_BUFFER_BINDING", + 0x8C88, "GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN", }, { - 0x8C92, - "GL_ATC_RGB_AMD", + 0x8C89, "GL_RASTERIZER_DISCARD", }, { - 0x8C93, - "GL_ATC_RGBA_EXPLICIT_ALPHA_AMD", + 0x8C8A, "GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS", }, { - 0x8CA3, - "GL_STENCIL_BACK_REF", + 0x8C8B, "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS", }, { - 0x8CA4, - "GL_STENCIL_BACK_VALUE_MASK", + 0x8C8C, "GL_INTERLEAVED_ATTRIBS", }, { - 0x8CA5, - "GL_STENCIL_BACK_WRITEMASK", + 0x8C8D, "GL_SEPARATE_ATTRIBS", }, { - 0x8CA6, - "GL_FRAMEBUFFER_BINDING", + 0x8C8E, "GL_TRANSFORM_FEEDBACK_BUFFER", }, { - 0x8CA7, - "GL_RENDERBUFFER_BINDING", + 0x8C8F, "GL_TRANSFORM_FEEDBACK_BUFFER_BINDING", }, { - 0x8CA8, - "GL_READ_FRAMEBUFFER_ANGLE", + 0x8C92, "GL_ATC_RGB_AMD", }, { - 0x8CA9, - "GL_DRAW_FRAMEBUFFER_ANGLE", + 0x8C93, "GL_ATC_RGBA_EXPLICIT_ALPHA_AMD", }, { - 0x8CAA, - "GL_READ_FRAMEBUFFER_BINDING_ANGLE", + 0x8CA1, "GL_LOWER_LEFT_EXT", }, { - 0x8CAB, - "GL_RENDERBUFFER_SAMPLES_ANGLE", + 0x8CA2, "GL_UPPER_LEFT_EXT", }, { - 0x8CAC, - "GL_DEPTH_COMPONENT32F", + 0x8CA3, "GL_STENCIL_BACK_REF", }, { - 0x8CAD, - "GL_DEPTH32F_STENCIL8", + 0x8CA4, "GL_STENCIL_BACK_VALUE_MASK", }, { - 0x8CD0, - "GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE", + 0x8CA5, "GL_STENCIL_BACK_WRITEMASK", }, { - 0x8CD1, - "GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME", + 0x8CA6, "GL_FRAMEBUFFER_BINDING", }, { - 0x8CD2, - "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL", + 0x8CA7, "GL_RENDERBUFFER_BINDING", }, { - 0x8CD3, - "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE", + 0x8CA8, "GL_READ_FRAMEBUFFER_ANGLE", }, { - 0x8CD4, - "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_OES", + 0x8CA9, "GL_DRAW_FRAMEBUFFER_ANGLE", }, { - 0x8CD5, - "GL_FRAMEBUFFER_COMPLETE", + 0x8CAA, "GL_READ_FRAMEBUFFER_BINDING_ANGLE", }, { - 0x8CD6, - "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT", + 0x8CAB, "GL_RENDERBUFFER_SAMPLES_ANGLE", }, { - 0x8CD7, - "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT", + 0x8CAC, "GL_DEPTH_COMPONENT32F", }, { - 0x8CD9, - "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS", + 0x8CAD, "GL_DEPTH32F_STENCIL8", }, { - 0x8CDD, - "GL_FRAMEBUFFER_UNSUPPORTED", + 0x8CD0, "GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE", }, { - 0x8CDF, - "GL_MAX_COLOR_ATTACHMENTS_EXT", + 0x8CD1, "GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME", }, { - 0x8CE0, - "GL_COLOR_ATTACHMENT0", + 0x8CD2, "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL", }, { - 0x8CE1, - "GL_COLOR_ATTACHMENT1_EXT", + 0x8CD3, "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE", }, { - 0x8CE2, - "GL_COLOR_ATTACHMENT2_EXT", + 0x8CD4, "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_OES", }, { - 0x8CE3, - "GL_COLOR_ATTACHMENT3_EXT", + 0x8CD5, "GL_FRAMEBUFFER_COMPLETE", }, { - 0x8CE4, - "GL_COLOR_ATTACHMENT4_EXT", + 0x8CD6, "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT", }, { - 0x8CE5, - "GL_COLOR_ATTACHMENT5_EXT", + 0x8CD7, "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT", }, { - 0x8CE6, - "GL_COLOR_ATTACHMENT6_EXT", + 0x8CD9, "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS", }, { - 0x8CE7, - "GL_COLOR_ATTACHMENT7_EXT", + 0x8CDD, "GL_FRAMEBUFFER_UNSUPPORTED", }, { - 0x8CE8, - "GL_COLOR_ATTACHMENT8_EXT", + 0x8CDF, "GL_MAX_COLOR_ATTACHMENTS_EXT", }, { - 0x8CE9, - "GL_COLOR_ATTACHMENT9_EXT", + 0x8CE0, "GL_COLOR_ATTACHMENT0", }, { - 0x8CEA, - "GL_COLOR_ATTACHMENT10_EXT", + 0x8CE1, "GL_COLOR_ATTACHMENT1_EXT", }, { - 0x8CEB, - "GL_COLOR_ATTACHMENT11_EXT", + 0x8CE2, "GL_COLOR_ATTACHMENT2_EXT", }, { - 0x8CEC, - "GL_COLOR_ATTACHMENT12_EXT", + 0x8CE3, "GL_COLOR_ATTACHMENT3_EXT", }, { - 0x8CED, - "GL_COLOR_ATTACHMENT13_EXT", + 0x8CE4, "GL_COLOR_ATTACHMENT4_EXT", }, { - 0x8CEE, - "GL_COLOR_ATTACHMENT14_EXT", + 0x8CE5, "GL_COLOR_ATTACHMENT5_EXT", }, { - 0x8CEF, - "GL_COLOR_ATTACHMENT15_EXT", + 0x8CE6, "GL_COLOR_ATTACHMENT6_EXT", }, { - 0x8CF0, - "GL_COLOR_ATTACHMENT16", + 0x8CE7, "GL_COLOR_ATTACHMENT7_EXT", }, { - 0x8CF1, - "GL_COLOR_ATTACHMENT17", + 0x8CE8, "GL_COLOR_ATTACHMENT8_EXT", }, { - 0x8CF2, - "GL_COLOR_ATTACHMENT18", + 0x8CE9, "GL_COLOR_ATTACHMENT9_EXT", }, { - 0x8CF3, - "GL_COLOR_ATTACHMENT19", + 0x8CEA, "GL_COLOR_ATTACHMENT10_EXT", }, { - 0x8CF4, - "GL_COLOR_ATTACHMENT20", + 0x8CEB, "GL_COLOR_ATTACHMENT11_EXT", }, { - 0x8CF5, - "GL_COLOR_ATTACHMENT21", + 0x8CEC, "GL_COLOR_ATTACHMENT12_EXT", }, { - 0x8CF6, - "GL_COLOR_ATTACHMENT22", + 0x8CED, "GL_COLOR_ATTACHMENT13_EXT", }, { - 0x8CF7, - "GL_COLOR_ATTACHMENT23", + 0x8CEE, "GL_COLOR_ATTACHMENT14_EXT", }, { - 0x8CF8, - "GL_COLOR_ATTACHMENT24", + 0x8CEF, "GL_COLOR_ATTACHMENT15_EXT", }, { - 0x8CF9, - "GL_COLOR_ATTACHMENT25", + 0x8CF0, "GL_COLOR_ATTACHMENT16", }, { - 0x8CFA, - "GL_COLOR_ATTACHMENT26", + 0x8CF1, "GL_COLOR_ATTACHMENT17", }, { - 0x8CFB, - "GL_COLOR_ATTACHMENT27", + 0x8CF2, "GL_COLOR_ATTACHMENT18", }, { - 0x8CFC, - "GL_COLOR_ATTACHMENT28", + 0x8CF3, "GL_COLOR_ATTACHMENT19", }, { - 0x8CFD, - "GL_COLOR_ATTACHMENT29", + 0x8CF4, "GL_COLOR_ATTACHMENT20", }, { - 0x8CFE, - "GL_COLOR_ATTACHMENT30", + 0x8CF5, "GL_COLOR_ATTACHMENT21", }, { - 0x8CFF, - "GL_COLOR_ATTACHMENT31", + 0x8CF6, "GL_COLOR_ATTACHMENT22", }, { - 0x8D00, - "GL_DEPTH_ATTACHMENT", + 0x8CF7, "GL_COLOR_ATTACHMENT23", }, { - 0x8D20, - "GL_STENCIL_ATTACHMENT", + 0x8CF8, "GL_COLOR_ATTACHMENT24", }, { - 0x8D40, - "GL_FRAMEBUFFER", + 0x8CF9, "GL_COLOR_ATTACHMENT25", }, { - 0x8D41, - "GL_RENDERBUFFER", + 0x8CFA, "GL_COLOR_ATTACHMENT26", }, { - 0x8D42, - "GL_RENDERBUFFER_WIDTH", + 0x8CFB, "GL_COLOR_ATTACHMENT27", }, { - 0x8D43, - "GL_RENDERBUFFER_HEIGHT", + 0x8CFC, "GL_COLOR_ATTACHMENT28", }, { - 0x8D44, - "GL_RENDERBUFFER_INTERNAL_FORMAT", + 0x8CFD, "GL_COLOR_ATTACHMENT29", }, { - 0x8D46, - "GL_STENCIL_INDEX1_OES", + 0x8CFE, "GL_COLOR_ATTACHMENT30", }, { - 0x8D47, - "GL_STENCIL_INDEX4_OES", + 0x8CFF, "GL_COLOR_ATTACHMENT31", }, { - 0x8D48, - "GL_STENCIL_INDEX8", + 0x8D00, "GL_DEPTH_ATTACHMENT", }, { - 0x8D50, - "GL_RENDERBUFFER_RED_SIZE", + 0x8D20, "GL_STENCIL_ATTACHMENT", }, { - 0x8D51, - "GL_RENDERBUFFER_GREEN_SIZE", + 0x8D40, "GL_FRAMEBUFFER", }, { - 0x8D52, - "GL_RENDERBUFFER_BLUE_SIZE", + 0x8D41, "GL_RENDERBUFFER", }, { - 0x8D53, - "GL_RENDERBUFFER_ALPHA_SIZE", + 0x8D42, "GL_RENDERBUFFER_WIDTH", }, { - 0x8D54, - "GL_RENDERBUFFER_DEPTH_SIZE", + 0x8D43, "GL_RENDERBUFFER_HEIGHT", }, { - 0x8D55, - "GL_RENDERBUFFER_STENCIL_SIZE", + 0x8D44, "GL_RENDERBUFFER_INTERNAL_FORMAT", }, { - 0x8D56, - "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_ANGLE", + 0x8D46, "GL_STENCIL_INDEX1_OES", }, { - 0x8D57, - "GL_MAX_SAMPLES_ANGLE", + 0x8D47, "GL_STENCIL_INDEX4_OES", }, { - 0x8D61, - "GL_HALF_FLOAT_OES", + 0x8D48, "GL_STENCIL_INDEX8", }, { - 0x8D62, - "GL_RGB565", + 0x8D50, "GL_RENDERBUFFER_RED_SIZE", }, { - 0x8D64, - "GL_ETC1_RGB8_OES", + 0x8D51, "GL_RENDERBUFFER_GREEN_SIZE", }, { - 0x8D65, - "GL_TEXTURE_EXTERNAL_OES", + 0x8D52, "GL_RENDERBUFFER_BLUE_SIZE", }, { - 0x8D66, - "GL_SAMPLER_EXTERNAL_OES", + 0x8D53, "GL_RENDERBUFFER_ALPHA_SIZE", }, { - 0x8D67, - "GL_TEXTURE_BINDING_EXTERNAL_OES", + 0x8D54, "GL_RENDERBUFFER_DEPTH_SIZE", }, { - 0x8D68, - "GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES", + 0x8D55, "GL_RENDERBUFFER_STENCIL_SIZE", }, { - 0x8D69, - "GL_PRIMITIVE_RESTART_FIXED_INDEX", + 0x8D56, "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_ANGLE", }, { - 0x8D6A, - "GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT", + 0x8D57, "GL_MAX_SAMPLES_ANGLE", }, { - 0x8D6B, - "GL_MAX_ELEMENT_INDEX", + 0x8D61, "GL_HALF_FLOAT_OES", }, { - 0x8D6C, - "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_SAMPLES_EXT", + 0x8D62, "GL_RGB565", }, { - 0x8D70, - "GL_RGBA32UI", + 0x8D64, "GL_ETC1_RGB8_OES", }, { - 0x8D71, - "GL_RGB32UI", + 0x8D65, "GL_TEXTURE_EXTERNAL_OES", }, { - 0x8D76, - "GL_RGBA16UI", + 0x8D66, "GL_SAMPLER_EXTERNAL_OES", }, { - 0x8D77, - "GL_RGB16UI", + 0x8D67, "GL_TEXTURE_BINDING_EXTERNAL_OES", }, { - 0x8D7C, - "GL_RGBA8UI", + 0x8D68, "GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES", }, { - 0x8D7D, - "GL_RGB8UI", + 0x8D69, "GL_PRIMITIVE_RESTART_FIXED_INDEX", }, { - 0x8D82, - "GL_RGBA32I", + 0x8D6A, "GL_ANY_SAMPLES_PASSED_CONSERVATIVE_EXT", }, { - 0x8D83, - "GL_RGB32I", + 0x8D6B, "GL_MAX_ELEMENT_INDEX", }, { - 0x8D88, - "GL_RGBA16I", + 0x8D6C, "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_SAMPLES_EXT", }, { - 0x8D89, - "GL_RGB16I", + 0x8D70, "GL_RGBA32UI", }, { - 0x8D8E, - "GL_RGBA8I", + 0x8D71, "GL_RGB32UI", }, { - 0x8D8F, - "GL_RGB8I", + 0x8D76, "GL_RGBA16UI", }, { - 0x8D94, - "GL_RED_INTEGER", + 0x8D77, "GL_RGB16UI", }, { - 0x8D98, - "GL_RGB_INTEGER", + 0x8D7C, "GL_RGBA8UI", }, { - 0x8D99, - "GL_RGBA_INTEGER", + 0x8D7D, "GL_RGB8UI", }, { - 0x8D9F, - "GL_INT_2_10_10_10_REV", + 0x8D82, "GL_RGBA32I", }, { - 0x8DA7, - "GL_FRAMEBUFFER_ATTACHMENT_LAYERED_OES", + 0x8D83, "GL_RGB32I", }, { - 0x8DA8, - "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_OES", + 0x8D88, "GL_RGBA16I", }, { - 0x8DAD, - "GL_FLOAT_32_UNSIGNED_INT_24_8_REV", + 0x8D89, "GL_RGB16I", }, { - 0x8DB9, - "GL_FRAMEBUFFER_SRGB_EXT", + 0x8D8E, "GL_RGBA8I", }, { - 0x8DC1, - "GL_SAMPLER_2D_ARRAY", + 0x8D8F, "GL_RGB8I", }, { - 0x8DC2, - "GL_SAMPLER_BUFFER_OES", + 0x8D94, "GL_RED_INTEGER", }, { - 0x8DC4, - "GL_SAMPLER_2D_ARRAY_SHADOW_NV", + 0x8D98, "GL_RGB_INTEGER", }, { - 0x8DC5, - "GL_SAMPLER_CUBE_SHADOW_NV", + 0x8D99, "GL_RGBA_INTEGER", }, { - 0x8DC6, - "GL_UNSIGNED_INT_VEC2", + 0x8D9F, "GL_INT_2_10_10_10_REV", }, { - 0x8DC7, - "GL_UNSIGNED_INT_VEC3", + 0x8DA7, "GL_FRAMEBUFFER_ATTACHMENT_LAYERED_OES", }, { - 0x8DC8, - "GL_UNSIGNED_INT_VEC4", + 0x8DA8, "GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS_OES", }, { - 0x8DCA, - "GL_INT_SAMPLER_2D", + 0x8DAD, "GL_FLOAT_32_UNSIGNED_INT_24_8_REV", }, { - 0x8DCB, - "GL_INT_SAMPLER_3D", + 0x8DB9, "GL_FRAMEBUFFER_SRGB_EXT", }, { - 0x8DCC, - "GL_INT_SAMPLER_CUBE", + 0x8DBB, "GL_COMPRESSED_RED_RGTC1_EXT", }, { - 0x8DCF, - "GL_INT_SAMPLER_2D_ARRAY", + 0x8DBC, "GL_COMPRESSED_SIGNED_RED_RGTC1_EXT", }, { - 0x8DD0, - "GL_INT_SAMPLER_BUFFER_OES", + 0x8DBD, "GL_COMPRESSED_RED_GREEN_RGTC2_EXT", }, { - 0x8DD2, - "GL_UNSIGNED_INT_SAMPLER_2D", + 0x8DBE, "GL_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT", }, { - 0x8DD3, - "GL_UNSIGNED_INT_SAMPLER_3D", + 0x8DC1, "GL_SAMPLER_2D_ARRAY", }, { - 0x8DD4, - "GL_UNSIGNED_INT_SAMPLER_CUBE", + 0x8DC2, "GL_SAMPLER_BUFFER_OES", }, { - 0x8DD7, - "GL_UNSIGNED_INT_SAMPLER_2D_ARRAY", + 0x8DC4, "GL_SAMPLER_2D_ARRAY_SHADOW_NV", }, { - 0x8DD8, - "GL_UNSIGNED_INT_SAMPLER_BUFFER_OES", + 0x8DC5, "GL_SAMPLER_CUBE_SHADOW_NV", }, { - 0x8DD9, - "GL_GEOMETRY_SHADER_OES", + 0x8DC6, "GL_UNSIGNED_INT_VEC2", }, { - 0x8DDF, - "GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_OES", + 0x8DC7, "GL_UNSIGNED_INT_VEC3", }, { - 0x8DE0, - "GL_MAX_GEOMETRY_OUTPUT_VERTICES_OES", + 0x8DC8, "GL_UNSIGNED_INT_VEC4", }, { - 0x8DE1, - "GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_OES", + 0x8DCA, "GL_INT_SAMPLER_2D", }, { - 0x8DF0, - "GL_LOW_FLOAT", + 0x8DCB, "GL_INT_SAMPLER_3D", }, { - 0x8DF1, - "GL_MEDIUM_FLOAT", + 0x8DCC, "GL_INT_SAMPLER_CUBE", }, { - 0x8DF2, - "GL_HIGH_FLOAT", + 0x8DCF, "GL_INT_SAMPLER_2D_ARRAY", }, { - 0x8DF3, - "GL_LOW_INT", + 0x8DD0, "GL_INT_SAMPLER_BUFFER_OES", }, { - 0x8DF4, - "GL_MEDIUM_INT", + 0x8DD2, "GL_UNSIGNED_INT_SAMPLER_2D", }, { - 0x8DF5, - "GL_HIGH_INT", + 0x8DD3, "GL_UNSIGNED_INT_SAMPLER_3D", }, { - 0x8DF6, - "GL_UNSIGNED_INT_10_10_10_2_OES", + 0x8DD4, "GL_UNSIGNED_INT_SAMPLER_CUBE", }, { - 0x8DF7, - "GL_INT_10_10_10_2_OES", + 0x8DD7, "GL_UNSIGNED_INT_SAMPLER_2D_ARRAY", }, { - 0x8DF8, - "GL_SHADER_BINARY_FORMATS", + 0x8DD8, "GL_UNSIGNED_INT_SAMPLER_BUFFER_OES", }, { - 0x8DF9, - "GL_NUM_SHADER_BINARY_FORMATS", + 0x8DD9, "GL_GEOMETRY_SHADER_OES", }, { - 0x8DFA, - "GL_SHADER_COMPILER", + 0x8DDF, "GL_MAX_GEOMETRY_UNIFORM_COMPONENTS_OES", }, { - 0x8DFB, - "GL_MAX_VERTEX_UNIFORM_VECTORS", + 0x8DE0, "GL_MAX_GEOMETRY_OUTPUT_VERTICES_OES", }, { - 0x8DFC, - "GL_MAX_VARYING_VECTORS", + 0x8DE1, "GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_OES", }, { - 0x8DFD, - "GL_MAX_FRAGMENT_UNIFORM_VECTORS", + 0x8DF0, "GL_LOW_FLOAT", }, { - 0x8E13, - "GL_QUERY_WAIT_NV", + 0x8DF1, "GL_MEDIUM_FLOAT", }, { - 0x8E14, - "GL_QUERY_NO_WAIT_NV", + 0x8DF2, "GL_HIGH_FLOAT", }, { - 0x8E15, - "GL_QUERY_BY_REGION_WAIT_NV", + 0x8DF3, "GL_LOW_INT", }, { - 0x8E16, - "GL_QUERY_BY_REGION_NO_WAIT_NV", + 0x8DF4, "GL_MEDIUM_INT", }, { - 0x8E1B, - "GL_POLYGON_OFFSET_CLAMP_EXT", + 0x8DF5, "GL_HIGH_INT", }, { - 0x8E1E, - "GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS_OES", + 0x8DF6, "GL_UNSIGNED_INT_10_10_10_2_OES", }, { - 0x8E1F, - "GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS_OES", + 0x8DF7, "GL_INT_10_10_10_2_OES", }, { - 0x8E20, - "GL_COLOR_SAMPLES_NV", + 0x8DF8, "GL_SHADER_BINARY_FORMATS", }, { - 0x8E22, - "GL_TRANSFORM_FEEDBACK", + 0x8DF9, "GL_NUM_SHADER_BINARY_FORMATS", }, { - 0x8E23, - "GL_TRANSFORM_FEEDBACK_PAUSED", + 0x8DFA, "GL_SHADER_COMPILER", }, { - 0x8E24, - "GL_TRANSFORM_FEEDBACK_ACTIVE", + 0x8DFB, "GL_MAX_VERTEX_UNIFORM_VECTORS", }, { - 0x8E25, - "GL_TRANSFORM_FEEDBACK_BINDING", + 0x8DFC, "GL_MAX_VARYING_VECTORS", }, { - 0x8E28, - "GL_TIMESTAMP_EXT", + 0x8DFD, "GL_MAX_FRAGMENT_UNIFORM_VECTORS", }, { - 0x8E2C, - "GL_DEPTH_COMPONENT16_NONLINEAR_NV", + 0x8E13, "GL_QUERY_WAIT_NV", }, { - 0x8E42, - "GL_TEXTURE_SWIZZLE_R", + 0x8E14, "GL_QUERY_NO_WAIT_NV", }, { - 0x8E43, - "GL_TEXTURE_SWIZZLE_G", + 0x8E15, "GL_QUERY_BY_REGION_WAIT_NV", }, { - 0x8E44, - "GL_TEXTURE_SWIZZLE_B", + 0x8E16, "GL_QUERY_BY_REGION_NO_WAIT_NV", }, { - 0x8E45, - "GL_TEXTURE_SWIZZLE_A", + 0x8E1B, "GL_POLYGON_OFFSET_CLAMP_EXT", }, { - 0x8E4D, - "GL_FIRST_VERTEX_CONVENTION_OES", + 0x8E1E, "GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS_OES", }, { - 0x8E4E, - "GL_LAST_VERTEX_CONVENTION_OES", + 0x8E1F, "GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS_OES", }, { - 0x8E50, - "GL_SAMPLE_LOCATION_NV", + 0x8E20, "GL_COLOR_SAMPLES_NV", }, { - 0x8E51, - "GL_SAMPLE_MASK", + 0x8E22, "GL_TRANSFORM_FEEDBACK", }, { - 0x8E52, - "GL_SAMPLE_MASK_VALUE", + 0x8E23, "GL_TRANSFORM_FEEDBACK_PAUSED", }, { - 0x8E59, - "GL_MAX_SAMPLE_MASK_WORDS", + 0x8E24, "GL_TRANSFORM_FEEDBACK_ACTIVE", }, { - 0x8E5A, - "GL_MAX_GEOMETRY_SHADER_INVOCATIONS_OES", + 0x8E25, "GL_TRANSFORM_FEEDBACK_BINDING", }, { - 0x8E5B, - "GL_MIN_FRAGMENT_INTERPOLATION_OFFSET_OES", + 0x8E28, "GL_TIMESTAMP_EXT", }, { - 0x8E5C, - "GL_MAX_FRAGMENT_INTERPOLATION_OFFSET_OES", + 0x8E2C, "GL_DEPTH_COMPONENT16_NONLINEAR_NV", }, { - 0x8E5D, - "GL_FRAGMENT_INTERPOLATION_OFFSET_BITS_OES", + 0x8E42, "GL_TEXTURE_SWIZZLE_R", }, { - 0x8E5E, - "GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET", + 0x8E43, "GL_TEXTURE_SWIZZLE_G", }, { - 0x8E5F, - "GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET", + 0x8E44, "GL_TEXTURE_SWIZZLE_B", }, { - 0x8E72, - "GL_PATCH_VERTICES_OES", + 0x8E45, "GL_TEXTURE_SWIZZLE_A", }, { - 0x8E75, - "GL_TESS_CONTROL_OUTPUT_VERTICES_OES", + 0x8E4D, "GL_FIRST_VERTEX_CONVENTION_OES", }, { - 0x8E76, - "GL_TESS_GEN_MODE_OES", + 0x8E4E, "GL_LAST_VERTEX_CONVENTION_OES", }, { - 0x8E77, - "GL_TESS_GEN_SPACING_OES", + 0x8E50, "GL_SAMPLE_LOCATION_NV", }, { - 0x8E78, - "GL_TESS_GEN_VERTEX_ORDER_OES", + 0x8E51, "GL_SAMPLE_MASK", }, { - 0x8E79, - "GL_TESS_GEN_POINT_MODE_OES", + 0x8E52, "GL_SAMPLE_MASK_VALUE", }, { - 0x8E7A, - "GL_ISOLINES_OES", + 0x8E59, "GL_MAX_SAMPLE_MASK_WORDS", }, { - 0x8E7B, - "GL_FRACTIONAL_ODD_OES", + 0x8E5A, "GL_MAX_GEOMETRY_SHADER_INVOCATIONS_OES", }, { - 0x8E7C, - "GL_FRACTIONAL_EVEN_OES", + 0x8E5B, "GL_MIN_FRAGMENT_INTERPOLATION_OFFSET_OES", }, { - 0x8E7D, - "GL_MAX_PATCH_VERTICES_OES", + 0x8E5C, "GL_MAX_FRAGMENT_INTERPOLATION_OFFSET_OES", }, { - 0x8E7E, - "GL_MAX_TESS_GEN_LEVEL_OES", + 0x8E5D, "GL_FRAGMENT_INTERPOLATION_OFFSET_BITS_OES", }, { - 0x8E7F, - "GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS_OES", + 0x8E5E, "GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET", }, { - 0x8E80, - "GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS_OES", + 0x8E5F, "GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET", }, { - 0x8E81, - "GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS_OES", + 0x8E72, "GL_PATCH_VERTICES_OES", }, { - 0x8E82, - "GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS_OES", + 0x8E75, "GL_TESS_CONTROL_OUTPUT_VERTICES_OES", }, { - 0x8E83, - "GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS_OES", + 0x8E76, "GL_TESS_GEN_MODE_OES", }, { - 0x8E84, - "GL_MAX_TESS_PATCH_COMPONENTS_OES", + 0x8E77, "GL_TESS_GEN_SPACING_OES", }, { - 0x8E85, - "GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS_OES", + 0x8E78, "GL_TESS_GEN_VERTEX_ORDER_OES", }, { - 0x8E86, - "GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS_OES", + 0x8E79, "GL_TESS_GEN_POINT_MODE_OES", }, { - 0x8E87, - "GL_TESS_EVALUATION_SHADER_OES", + 0x8E7A, "GL_ISOLINES_OES", }, { - 0x8E88, - "GL_TESS_CONTROL_SHADER_OES", + 0x8E7B, "GL_FRACTIONAL_ODD_OES", }, { - 0x8E89, - "GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS_OES", + 0x8E7C, "GL_FRACTIONAL_EVEN_OES", }, { - 0x8E8A, - "GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS_OES", + 0x8E7D, "GL_MAX_PATCH_VERTICES_OES", }, { - 0x8ED0, - "GL_COVERAGE_COMPONENT_NV", + 0x8E7E, "GL_MAX_TESS_GEN_LEVEL_OES", }, { - 0x8ED1, - "GL_COVERAGE_COMPONENT4_NV", + 0x8E7F, "GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS_OES", }, { - 0x8ED2, - "GL_COVERAGE_ATTACHMENT_NV", + 0x8E80, "GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS_OES", }, { - 0x8ED3, - "GL_COVERAGE_BUFFERS_NV", + 0x8E81, "GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS_OES", }, { - 0x8ED4, - "GL_COVERAGE_SAMPLES_NV", + 0x8E82, "GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS_OES", }, { - 0x8ED5, - "GL_COVERAGE_ALL_FRAGMENTS_NV", + 0x8E83, "GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS_OES", }, { - 0x8ED6, - "GL_COVERAGE_EDGE_FRAGMENTS_NV", + 0x8E84, "GL_MAX_TESS_PATCH_COMPONENTS_OES", }, { - 0x8ED7, - "GL_COVERAGE_AUTOMATIC_NV", + 0x8E85, "GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS_OES", }, { - 0x8F10, - "GL_INCLUSIVE_EXT", + 0x8E86, "GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS_OES", }, { - 0x8F11, - "GL_EXCLUSIVE_EXT", + 0x8E87, "GL_TESS_EVALUATION_SHADER_OES", }, { - 0x8F12, - "GL_WINDOW_RECTANGLE_EXT", + 0x8E88, "GL_TESS_CONTROL_SHADER_OES", }, { - 0x8F13, - "GL_WINDOW_RECTANGLE_MODE_EXT", + 0x8E89, "GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS_OES", }, { - 0x8F14, - "GL_MAX_WINDOW_RECTANGLES_EXT", + 0x8E8A, "GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS_OES", }, { - 0x8F15, - "GL_NUM_WINDOW_RECTANGLES_EXT", + 0x8E8C, "GL_COMPRESSED_RGBA_BPTC_UNORM_EXT", }, { - 0x8F36, - "GL_COPY_READ_BUFFER_NV", + 0x8E8D, "GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_EXT", }, { - 0x8F37, - "GL_COPY_WRITE_BUFFER_NV", + 0x8E8E, "GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_EXT", }, { - 0x8F38, - "GL_MAX_IMAGE_UNITS", + 0x8E8F, "GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_EXT", }, { - 0x8F39, - "GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES", + 0x8ED0, "GL_COVERAGE_COMPONENT_NV", }, { - 0x8F3A, - "GL_IMAGE_BINDING_NAME", + 0x8ED1, "GL_COVERAGE_COMPONENT4_NV", }, { - 0x8F3B, - "GL_IMAGE_BINDING_LEVEL", + 0x8ED2, "GL_COVERAGE_ATTACHMENT_NV", }, { - 0x8F3C, - "GL_IMAGE_BINDING_LAYERED", + 0x8ED3, "GL_COVERAGE_BUFFERS_NV", }, { - 0x8F3D, - "GL_IMAGE_BINDING_LAYER", + 0x8ED4, "GL_COVERAGE_SAMPLES_NV", }, { - 0x8F3E, - "GL_IMAGE_BINDING_ACCESS", + 0x8ED5, "GL_COVERAGE_ALL_FRAGMENTS_NV", }, { - 0x8F3F, - "GL_DRAW_INDIRECT_BUFFER", + 0x8ED6, "GL_COVERAGE_EDGE_FRAGMENTS_NV", }, { - 0x8F43, - "GL_DRAW_INDIRECT_BUFFER_BINDING", + 0x8ED7, "GL_COVERAGE_AUTOMATIC_NV", }, { - 0x8F4F, - "GL_VERTEX_BINDING_BUFFER", + 0x8F10, "GL_INCLUSIVE_EXT", }, { - 0x8F60, - "GL_MALI_SHADER_BINARY_ARM", + 0x8F11, "GL_EXCLUSIVE_EXT", }, { - 0x8F61, - "GL_MALI_PROGRAM_BINARY_ARM", + 0x8F12, "GL_WINDOW_RECTANGLE_EXT", }, { - 0x8F63, - "GL_MAX_SHADER_PIXEL_LOCAL_STORAGE_FAST_SIZE_EXT", + 0x8F13, "GL_WINDOW_RECTANGLE_MODE_EXT", }, { - 0x8F64, - "GL_SHADER_PIXEL_LOCAL_STORAGE_EXT", + 0x8F14, "GL_MAX_WINDOW_RECTANGLES_EXT", }, { - 0x8F65, - "GL_FETCH_PER_SAMPLE_ARM", + 0x8F15, "GL_NUM_WINDOW_RECTANGLES_EXT", }, { - 0x8F66, - "GL_FRAGMENT_SHADER_FRAMEBUFFER_FETCH_MRT_ARM", + 0x8F36, "GL_COPY_READ_BUFFER_NV", }, { - 0x8F67, - "GL_MAX_SHADER_PIXEL_LOCAL_STORAGE_SIZE_EXT", + 0x8F37, "GL_COPY_WRITE_BUFFER_NV", }, { - 0x8F69, - "GL_TEXTURE_ASTC_DECODE_PRECISION_EXT", + 0x8F38, "GL_MAX_IMAGE_UNITS", }, { - 0x8F94, - "GL_R8_SNORM", + 0x8F39, "GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES", }, { - 0x8F95, - "GL_RG8_SNORM", + 0x8F3A, "GL_IMAGE_BINDING_NAME", }, { - 0x8F96, - "GL_RGB8_SNORM", + 0x8F3B, "GL_IMAGE_BINDING_LEVEL", }, { - 0x8F97, - "GL_RGBA8_SNORM", + 0x8F3C, "GL_IMAGE_BINDING_LAYERED", }, { - 0x8F98, - "GL_R16_SNORM_EXT", + 0x8F3D, "GL_IMAGE_BINDING_LAYER", }, { - 0x8F99, - "GL_RG16_SNORM_EXT", + 0x8F3E, "GL_IMAGE_BINDING_ACCESS", }, { - 0x8F9A, - "GL_RGB16_SNORM_EXT", + 0x8F3F, "GL_DRAW_INDIRECT_BUFFER", }, { - 0x8F9B, - "GL_RGBA16_SNORM_EXT", + 0x8F43, "GL_DRAW_INDIRECT_BUFFER_BINDING", }, { - 0x8F9C, - "GL_SIGNED_NORMALIZED", + 0x8F4F, "GL_VERTEX_BINDING_BUFFER", }, { - 0x8FA0, - "GL_PERFMON_GLOBAL_MODE_QCOM", + 0x8F60, "GL_MALI_SHADER_BINARY_ARM", }, { - 0x8FB0, - "GL_BINNING_CONTROL_HINT_QCOM", + 0x8F61, "GL_MALI_PROGRAM_BINARY_ARM", }, { - 0x8FB1, - "GL_CPU_OPTIMIZED_QCOM", + 0x8F63, "GL_MAX_SHADER_PIXEL_LOCAL_STORAGE_FAST_SIZE_EXT", }, { - 0x8FB2, - "GL_GPU_OPTIMIZED_QCOM", + 0x8F64, "GL_SHADER_PIXEL_LOCAL_STORAGE_EXT", }, { - 0x8FB3, - "GL_RENDER_DIRECT_TO_FRAMEBUFFER_QCOM", + 0x8F65, "GL_FETCH_PER_SAMPLE_ARM", }, { - 0x8FBB, - "GL_GPU_DISJOINT_EXT", + 0x8F66, "GL_FRAGMENT_SHADER_FRAMEBUFFER_FETCH_MRT_ARM", }, { - 0x8FBD, - "GL_SR8_EXT", + 0x8F67, "GL_MAX_SHADER_PIXEL_LOCAL_STORAGE_SIZE_EXT", }, { - 0x8FBE, - "GL_SRG8_EXT", + 0x8F69, "GL_TEXTURE_ASTC_DECODE_PRECISION_EXT", }, { - 0x8FC4, - "GL_SHADER_BINARY_VIV", + 0x8F94, "GL_R8_SNORM", }, { - 0x8FE0, - "GL_INT8_NV", + 0x8F95, "GL_RG8_SNORM", }, { - 0x8FE1, - "GL_INT8_VEC2_NV", + 0x8F96, "GL_RGB8_SNORM", }, { - 0x8FE2, - "GL_INT8_VEC3_NV", + 0x8F97, "GL_RGBA8_SNORM", }, { - 0x8FE3, - "GL_INT8_VEC4_NV", + 0x8F98, "GL_R16_SNORM_EXT", }, { - 0x8FE4, - "GL_INT16_NV", + 0x8F99, "GL_RG16_SNORM_EXT", }, { - 0x8FE5, - "GL_INT16_VEC2_NV", + 0x8F9A, "GL_RGB16_SNORM_EXT", }, { - 0x8FE6, - "GL_INT16_VEC3_NV", + 0x8F9B, "GL_RGBA16_SNORM_EXT", }, { - 0x8FE7, - "GL_INT16_VEC4_NV", + 0x8F9C, "GL_SIGNED_NORMALIZED", }, { - 0x8FE9, - "GL_INT64_VEC2_NV", + 0x8FA0, "GL_PERFMON_GLOBAL_MODE_QCOM", }, { - 0x8FEA, - "GL_INT64_VEC3_NV", + 0x8FB0, "GL_BINNING_CONTROL_HINT_QCOM", }, { - 0x8FEB, - "GL_INT64_VEC4_NV", + 0x8FB1, "GL_CPU_OPTIMIZED_QCOM", }, { - 0x8FEC, - "GL_UNSIGNED_INT8_NV", + 0x8FB2, "GL_GPU_OPTIMIZED_QCOM", }, { - 0x8FED, - "GL_UNSIGNED_INT8_VEC2_NV", + 0x8FB3, "GL_RENDER_DIRECT_TO_FRAMEBUFFER_QCOM", }, { - 0x8FEE, - "GL_UNSIGNED_INT8_VEC3_NV", + 0x8FBB, "GL_GPU_DISJOINT_EXT", }, { - 0x8FEF, - "GL_UNSIGNED_INT8_VEC4_NV", + 0x8FBD, "GL_SR8_EXT", }, { - 0x8FF0, - "GL_UNSIGNED_INT16_NV", + 0x8FBE, "GL_SRG8_EXT", }, { - 0x8FF1, - "GL_UNSIGNED_INT16_VEC2_NV", + 0x8FBF, "GL_TEXTURE_FORMAT_SRGB_OVERRIDE_EXT", }, { - 0x8FF2, - "GL_UNSIGNED_INT16_VEC3_NV", + 0x8FC4, "GL_SHADER_BINARY_VIV", }, { - 0x8FF3, - "GL_UNSIGNED_INT16_VEC4_NV", + 0x8FE0, "GL_INT8_NV", }, { - 0x8FF5, - "GL_UNSIGNED_INT64_VEC2_NV", + 0x8FE1, "GL_INT8_VEC2_NV", }, { - 0x8FF6, - "GL_UNSIGNED_INT64_VEC3_NV", + 0x8FE2, "GL_INT8_VEC3_NV", }, { - 0x8FF7, - "GL_UNSIGNED_INT64_VEC4_NV", + 0x8FE3, "GL_INT8_VEC4_NV", }, { - 0x8FF8, - "GL_FLOAT16_NV", + 0x8FE4, "GL_INT16_NV", }, { - 0x8FF9, - "GL_FLOAT16_VEC2_NV", + 0x8FE5, "GL_INT16_VEC2_NV", }, { - 0x8FFA, - "GL_FLOAT16_VEC3_NV", + 0x8FE6, "GL_INT16_VEC3_NV", }, { - 0x8FFB, - "GL_FLOAT16_VEC4_NV", + 0x8FE7, "GL_INT16_VEC4_NV", }, { - 0x9009, - "GL_TEXTURE_CUBE_MAP_ARRAY_OES", + 0x8FE9, "GL_INT64_VEC2_NV", }, { - 0x900A, - "GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_OES", + 0x8FEA, "GL_INT64_VEC3_NV", }, { - 0x900C, - "GL_SAMPLER_CUBE_MAP_ARRAY_OES", + 0x8FEB, "GL_INT64_VEC4_NV", }, { - 0x900D, - "GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_OES", + 0x8FEC, "GL_UNSIGNED_INT8_NV", }, { - 0x900E, - "GL_INT_SAMPLER_CUBE_MAP_ARRAY_OES", + 0x8FED, "GL_UNSIGNED_INT8_VEC2_NV", }, { - 0x900F, - "GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_OES", + 0x8FEE, "GL_UNSIGNED_INT8_VEC3_NV", }, { - 0x904D, - "GL_IMAGE_2D", + 0x8FEF, "GL_UNSIGNED_INT8_VEC4_NV", }, { - 0x904E, - "GL_IMAGE_3D", + 0x8FF0, "GL_UNSIGNED_INT16_NV", }, { - 0x9050, - "GL_IMAGE_CUBE", + 0x8FF1, "GL_UNSIGNED_INT16_VEC2_NV", }, { - 0x9051, - "GL_IMAGE_BUFFER_OES", + 0x8FF2, "GL_UNSIGNED_INT16_VEC3_NV", }, { - 0x9053, - "GL_IMAGE_2D_ARRAY", + 0x8FF3, "GL_UNSIGNED_INT16_VEC4_NV", }, { - 0x9054, - "GL_IMAGE_CUBE_MAP_ARRAY_OES", + 0x8FF5, "GL_UNSIGNED_INT64_VEC2_NV", }, { - 0x9058, - "GL_INT_IMAGE_2D", + 0x8FF6, "GL_UNSIGNED_INT64_VEC3_NV", }, { - 0x9059, - "GL_INT_IMAGE_3D", + 0x8FF7, "GL_UNSIGNED_INT64_VEC4_NV", }, { - 0x905B, - "GL_INT_IMAGE_CUBE", + 0x8FF8, "GL_FLOAT16_NV", }, { - 0x905C, - "GL_INT_IMAGE_BUFFER_OES", + 0x8FF9, "GL_FLOAT16_VEC2_NV", }, { - 0x905E, - "GL_INT_IMAGE_2D_ARRAY", + 0x8FFA, "GL_FLOAT16_VEC3_NV", }, { - 0x905F, - "GL_INT_IMAGE_CUBE_MAP_ARRAY_OES", + 0x8FFB, "GL_FLOAT16_VEC4_NV", }, { - 0x9063, - "GL_UNSIGNED_INT_IMAGE_2D", + 0x9009, "GL_TEXTURE_CUBE_MAP_ARRAY_OES", }, { - 0x9064, - "GL_UNSIGNED_INT_IMAGE_3D", + 0x900A, "GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_OES", }, { - 0x9066, - "GL_UNSIGNED_INT_IMAGE_CUBE", + 0x900C, "GL_SAMPLER_CUBE_MAP_ARRAY_OES", }, { - 0x9067, - "GL_UNSIGNED_INT_IMAGE_BUFFER_OES", + 0x900D, "GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_OES", }, { - 0x9069, - "GL_UNSIGNED_INT_IMAGE_2D_ARRAY", + 0x900E, "GL_INT_SAMPLER_CUBE_MAP_ARRAY_OES", }, { - 0x906A, - "GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY_OES", + 0x900F, "GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_OES", }, { - 0x906E, - "GL_IMAGE_BINDING_FORMAT", + 0x901C, "GL_FACTOR_MIN_AMD", }, { - 0x906F, - "GL_RGB10_A2UI", + 0x901D, "GL_FACTOR_MAX_AMD", }, { - 0x9070, - "GL_PATH_FORMAT_SVG_NV", + 0x904D, "GL_IMAGE_2D", }, { - 0x9071, - "GL_PATH_FORMAT_PS_NV", + 0x904E, "GL_IMAGE_3D", }, { - 0x9072, - "GL_STANDARD_FONT_NAME_NV", + 0x9050, "GL_IMAGE_CUBE", }, { - 0x9073, - "GL_SYSTEM_FONT_NAME_NV", + 0x9051, "GL_IMAGE_BUFFER_OES", }, { - 0x9074, - "GL_FILE_NAME_NV", + 0x9053, "GL_IMAGE_2D_ARRAY", }, { - 0x9075, - "GL_PATH_STROKE_WIDTH_NV", + 0x9054, "GL_IMAGE_CUBE_MAP_ARRAY_OES", }, { - 0x9076, - "GL_PATH_END_CAPS_NV", + 0x9058, "GL_INT_IMAGE_2D", }, { - 0x9077, - "GL_PATH_INITIAL_END_CAP_NV", + 0x9059, "GL_INT_IMAGE_3D", }, { - 0x9078, - "GL_PATH_TERMINAL_END_CAP_NV", + 0x905B, "GL_INT_IMAGE_CUBE", }, { - 0x9079, - "GL_PATH_JOIN_STYLE_NV", + 0x905C, "GL_INT_IMAGE_BUFFER_OES", }, { - 0x907A, - "GL_PATH_MITER_LIMIT_NV", + 0x905E, "GL_INT_IMAGE_2D_ARRAY", }, { - 0x907B, - "GL_PATH_DASH_CAPS_NV", + 0x905F, "GL_INT_IMAGE_CUBE_MAP_ARRAY_OES", }, { - 0x907C, - "GL_PATH_INITIAL_DASH_CAP_NV", + 0x9063, "GL_UNSIGNED_INT_IMAGE_2D", }, { - 0x907D, - "GL_PATH_TERMINAL_DASH_CAP_NV", + 0x9064, "GL_UNSIGNED_INT_IMAGE_3D", }, { - 0x907E, - "GL_PATH_DASH_OFFSET_NV", + 0x9066, "GL_UNSIGNED_INT_IMAGE_CUBE", }, { - 0x907F, - "GL_PATH_CLIENT_LENGTH_NV", + 0x9067, "GL_UNSIGNED_INT_IMAGE_BUFFER_OES", }, { - 0x907a, - "GL_PATH_MITER_LIMIT_CHROMIUM", + 0x9069, "GL_UNSIGNED_INT_IMAGE_2D_ARRAY", }, { - 0x9080, - "GL_PATH_FILL_MODE_NV", + 0x906A, "GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY_OES", }, { - 0x9081, - "GL_PATH_FILL_MASK_NV", + 0x906E, "GL_IMAGE_BINDING_FORMAT", }, { - 0x9082, - "GL_PATH_FILL_COVER_MODE_NV", + 0x906F, "GL_RGB10_A2UI", }, { - 0x9083, - "GL_PATH_STROKE_COVER_MODE_NV", + 0x9070, "GL_PATH_FORMAT_SVG_NV", }, { - 0x9084, - "GL_PATH_STROKE_MASK_NV", + 0x9071, "GL_PATH_FORMAT_PS_NV", }, { - 0x9086, - "GL_PATH_STROKE_BOUND_CHROMIUM", + 0x9072, "GL_STANDARD_FONT_NAME_NV", }, { - 0x9088, - "GL_COUNT_UP_NV", + 0x9073, "GL_SYSTEM_FONT_NAME_NV", }, { - 0x9089, - "GL_COUNT_DOWN_NV", + 0x9074, "GL_FILE_NAME_NV", }, { - 0x908A, - "GL_PATH_OBJECT_BOUNDING_BOX_NV", + 0x9075, "GL_PATH_STROKE_WIDTH_NV", }, { - 0x908B, - "GL_CONVEX_HULL_NV", + 0x9076, "GL_PATH_END_CAPS_NV", }, { - 0x908D, - "GL_BOUNDING_BOX_NV", + 0x9077, "GL_PATH_INITIAL_END_CAP_NV", }, { - 0x908E, - "GL_TRANSLATE_X_NV", + 0x9078, "GL_PATH_TERMINAL_END_CAP_NV", }, { - 0x908F, - "GL_TRANSLATE_Y_NV", + 0x9079, "GL_PATH_JOIN_STYLE_NV", }, { - 0x9090, - "GL_TRANSLATE_2D_NV", + 0x907A, "GL_PATH_MITER_LIMIT_NV", }, { - 0x9091, - "GL_TRANSLATE_3D_NV", + 0x907B, "GL_PATH_DASH_CAPS_NV", }, { - 0x9092, - "GL_AFFINE_2D_NV", + 0x907C, "GL_PATH_INITIAL_DASH_CAP_NV", }, { - 0x9094, - "GL_AFFINE_3D_NV", + 0x907D, "GL_PATH_TERMINAL_DASH_CAP_NV", }, { - 0x9096, - "GL_TRANSPOSE_AFFINE_2D_NV", + 0x907E, "GL_PATH_DASH_OFFSET_NV", }, { - 0x9098, - "GL_TRANSPOSE_AFFINE_3D_NV", + 0x907F, "GL_PATH_CLIENT_LENGTH_NV", }, { - 0x909A, - "GL_UTF8_NV", + 0x907a, "GL_PATH_MITER_LIMIT_CHROMIUM", }, { - 0x909B, - "GL_UTF16_NV", + 0x9080, "GL_PATH_FILL_MODE_NV", }, { - 0x909C, - "GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV", + 0x9081, "GL_PATH_FILL_MASK_NV", }, { - 0x909D, - "GL_PATH_COMMAND_COUNT_NV", + 0x9082, "GL_PATH_FILL_COVER_MODE_NV", }, { - 0x909E, - "GL_PATH_COORD_COUNT_NV", + 0x9083, "GL_PATH_STROKE_COVER_MODE_NV", }, { - 0x909F, - "GL_PATH_DASH_ARRAY_COUNT_NV", + 0x9084, "GL_PATH_STROKE_MASK_NV", }, { - 0x90A0, - "GL_PATH_COMPUTED_LENGTH_NV", + 0x9086, "GL_PATH_STROKE_BOUND_CHROMIUM", }, { - 0x90A1, - "GL_PATH_FILL_BOUNDING_BOX_NV", + 0x9088, "GL_COUNT_UP_NV", }, { - 0x90A2, - "GL_PATH_STROKE_BOUNDING_BOX_NV", + 0x9089, "GL_COUNT_DOWN_NV", }, { - 0x90A3, - "GL_SQUARE_NV", + 0x908A, "GL_PATH_OBJECT_BOUNDING_BOX_NV", }, { - 0x90A4, - "GL_ROUND_NV", + 0x908B, "GL_CONVEX_HULL_NV", }, { - 0x90A5, - "GL_TRIANGULAR_NV", + 0x908D, "GL_BOUNDING_BOX_NV", }, { - 0x90A6, - "GL_BEVEL_NV", + 0x908E, "GL_TRANSLATE_X_NV", }, { - 0x90A7, - "GL_MITER_REVERT_NV", + 0x908F, "GL_TRANSLATE_Y_NV", }, { - 0x90A8, - "GL_MITER_TRUNCATE_NV", + 0x9090, "GL_TRANSLATE_2D_NV", }, { - 0x90A9, - "GL_SKIP_MISSING_GLYPH_NV", + 0x9091, "GL_TRANSLATE_3D_NV", }, { - 0x90AA, - "GL_USE_MISSING_GLYPH_NV", + 0x9092, "GL_AFFINE_2D_NV", }, { - 0x90AB, - "GL_PATH_ERROR_POSITION_NV", + 0x9094, "GL_AFFINE_3D_NV", }, { - 0x90AD, - "GL_ACCUM_ADJACENT_PAIRS_NV", + 0x9096, "GL_TRANSPOSE_AFFINE_2D_NV", }, { - 0x90AE, - "GL_ADJACENT_PAIRS_NV", + 0x9098, "GL_TRANSPOSE_AFFINE_3D_NV", }, { - 0x90AF, - "GL_FIRST_TO_REST_NV", + 0x909A, "GL_UTF8_NV", }, { - 0x90B0, - "GL_PATH_GEN_MODE_NV", + 0x909B, "GL_UTF16_NV", }, { - 0x90B1, - "GL_PATH_GEN_COEFF_NV", + 0x909C, "GL_BOUNDING_BOX_OF_BOUNDING_BOXES_NV", }, { - 0x90B3, - "GL_PATH_GEN_COMPONENTS_NV", + 0x909D, "GL_PATH_COMMAND_COUNT_NV", }, { - 0x90B4, - "GL_PATH_DASH_OFFSET_RESET_NV", + 0x909E, "GL_PATH_COORD_COUNT_NV", }, { - 0x90B5, - "GL_MOVE_TO_RESETS_NV", + 0x909F, "GL_PATH_DASH_ARRAY_COUNT_NV", }, { - 0x90B6, - "GL_MOVE_TO_CONTINUES_NV", + 0x90A0, "GL_PATH_COMPUTED_LENGTH_NV", }, { - 0x90B7, - "GL_PATH_STENCIL_FUNC_NV", + 0x90A1, "GL_PATH_FILL_BOUNDING_BOX_NV", }, { - 0x90B8, - "GL_PATH_STENCIL_REF_NV", + 0x90A2, "GL_PATH_STROKE_BOUNDING_BOX_NV", }, { - 0x90B9, - "GL_PATH_STENCIL_VALUE_MASK_NV", + 0x90A3, "GL_SQUARE_NV", }, { - 0x90BD, - "GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV", + 0x90A4, "GL_ROUND_NV", }, { - 0x90BE, - "GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV", + 0x90A5, "GL_TRIANGULAR_NV", }, { - 0x90BF, - "GL_PATH_COVER_DEPTH_FUNC_NV", + 0x90A6, "GL_BEVEL_NV", }, { - 0x90C7, - "GL_IMAGE_FORMAT_COMPATIBILITY_TYPE", + 0x90A7, "GL_MITER_REVERT_NV", }, { - 0x90C8, - "GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE", + 0x90A8, "GL_MITER_TRUNCATE_NV", }, { - 0x90C9, - "GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS", + 0x90A9, "GL_SKIP_MISSING_GLYPH_NV", }, { - 0x90CA, - "GL_MAX_VERTEX_IMAGE_UNIFORMS", + 0x90AA, "GL_USE_MISSING_GLYPH_NV", }, { - 0x90CB, - "GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS_OES", + 0x90AB, "GL_PATH_ERROR_POSITION_NV", }, { - 0x90CC, - "GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS_OES", + 0x90AD, "GL_ACCUM_ADJACENT_PAIRS_NV", }, { - 0x90CD, - "GL_MAX_GEOMETRY_IMAGE_UNIFORMS_OES", + 0x90AE, "GL_ADJACENT_PAIRS_NV", }, { - 0x90CE, - "GL_MAX_FRAGMENT_IMAGE_UNIFORMS", + 0x90AF, "GL_FIRST_TO_REST_NV", }, { - 0x90CF, - "GL_MAX_COMBINED_IMAGE_UNIFORMS", + 0x90B0, "GL_PATH_GEN_MODE_NV", }, { - 0x90D2, - "GL_SHADER_STORAGE_BUFFER", + 0x90B1, "GL_PATH_GEN_COEFF_NV", }, { - 0x90D3, - "GL_SHADER_STORAGE_BUFFER_BINDING", + 0x90B3, "GL_PATH_GEN_COMPONENTS_NV", }, { - 0x90D4, - "GL_SHADER_STORAGE_BUFFER_START", + 0x90B4, "GL_PATH_DASH_OFFSET_RESET_NV", }, { - 0x90D5, - "GL_SHADER_STORAGE_BUFFER_SIZE", + 0x90B5, "GL_MOVE_TO_RESETS_NV", }, { - 0x90D6, - "GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS", + 0x90B6, "GL_MOVE_TO_CONTINUES_NV", }, { - 0x90D7, - "GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_OES", + 0x90B7, "GL_PATH_STENCIL_FUNC_NV", }, { - 0x90D8, - "GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS_OES", + 0x90B8, "GL_PATH_STENCIL_REF_NV", }, { - 0x90D9, - "GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS_OES", + 0x90B9, "GL_PATH_STENCIL_VALUE_MASK_NV", }, { - 0x90DA, - "GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS", + 0x90BD, "GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR_NV", }, { - 0x90DB, - "GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS", + 0x90BE, "GL_PATH_STENCIL_DEPTH_OFFSET_UNITS_NV", }, { - 0x90DC, - "GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS", + 0x90BF, "GL_PATH_COVER_DEPTH_FUNC_NV", }, { - 0x90DD, - "GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS", + 0x90C7, "GL_IMAGE_FORMAT_COMPATIBILITY_TYPE", }, { - 0x90DE, - "GL_MAX_SHADER_STORAGE_BLOCK_SIZE", + 0x90C8, "GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE", }, { - 0x90DF, - "GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT", + 0x90C9, "GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS", }, { - 0x90EA, - "GL_DEPTH_STENCIL_TEXTURE_MODE", + 0x90CA, "GL_MAX_VERTEX_IMAGE_UNIFORMS", }, { - 0x90EB, - "GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS", + 0x90CB, "GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS_OES", }, { - 0x90EE, - "GL_DISPATCH_INDIRECT_BUFFER", + 0x90CC, "GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS_OES", }, { - 0x90EF, - "GL_DISPATCH_INDIRECT_BUFFER_BINDING", + 0x90CD, "GL_MAX_GEOMETRY_IMAGE_UNIFORMS_OES", }, { - 0x90F0, - "GL_COLOR_ATTACHMENT_EXT", + 0x90CE, "GL_MAX_FRAGMENT_IMAGE_UNIFORMS", }, { - 0x90F1, - "GL_MULTIVIEW_EXT", + 0x90CF, "GL_MAX_COMBINED_IMAGE_UNIFORMS", }, { - 0x90F2, - "GL_MAX_MULTIVIEW_BUFFERS_EXT", + 0x90D2, "GL_SHADER_STORAGE_BUFFER", }, { - 0x90F3, - "GL_CONTEXT_ROBUST_ACCESS_KHR", + 0x90D3, "GL_SHADER_STORAGE_BUFFER_BINDING", }, { - 0x90a3, - "GL_SQUARE_CHROMIUM", + 0x90D4, "GL_SHADER_STORAGE_BUFFER_START", }, { - 0x90a4, - "GL_ROUND_CHROMIUM", + 0x90D5, "GL_SHADER_STORAGE_BUFFER_SIZE", }, { - 0x9100, - "GL_TEXTURE_2D_MULTISAMPLE", + 0x90D6, "GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS", }, { - 0x9102, - "GL_TEXTURE_2D_MULTISAMPLE_ARRAY_OES", + 0x90D7, "GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS_OES", }, { - 0x9104, - "GL_TEXTURE_BINDING_2D_MULTISAMPLE", + 0x90D8, "GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS_OES", }, { - 0x9105, - "GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY_OES", + 0x90D9, "GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS_OES", }, { - 0x9106, - "GL_TEXTURE_SAMPLES", + 0x90DA, "GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS", }, { - 0x9107, - "GL_TEXTURE_FIXED_SAMPLE_LOCATIONS", + 0x90DB, "GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS", }, { - 0x9108, - "GL_SAMPLER_2D_MULTISAMPLE", + 0x90DC, "GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS", }, { - 0x9109, - "GL_INT_SAMPLER_2D_MULTISAMPLE", + 0x90DD, "GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS", }, { - 0x910A, - "GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE", + 0x90DE, "GL_MAX_SHADER_STORAGE_BLOCK_SIZE", }, { - 0x910B, - "GL_SAMPLER_2D_MULTISAMPLE_ARRAY_OES", + 0x90DF, "GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT", }, { - 0x910C, - "GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY_OES", + 0x90EA, "GL_DEPTH_STENCIL_TEXTURE_MODE", }, { - 0x910D, - "GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY_OES", + 0x90EB, "GL_MAX_COMPUTE_WORK_GROUP_INVOCATIONS", }, { - 0x910E, - "GL_MAX_COLOR_TEXTURE_SAMPLES", + 0x90EE, "GL_DISPATCH_INDIRECT_BUFFER", }, { - 0x910F, - "GL_MAX_DEPTH_TEXTURE_SAMPLES", + 0x90EF, "GL_DISPATCH_INDIRECT_BUFFER_BINDING", }, { - 0x9110, - "GL_MAX_INTEGER_SAMPLES", + 0x90F0, "GL_COLOR_ATTACHMENT_EXT", }, { - 0x9111, - "GL_MAX_SERVER_WAIT_TIMEOUT_APPLE", + 0x90F1, "GL_MULTIVIEW_EXT", }, { - 0x9112, - "GL_OBJECT_TYPE_APPLE", + 0x90F2, "GL_MAX_MULTIVIEW_BUFFERS_EXT", }, { - 0x9113, - "GL_SYNC_CONDITION_APPLE", + 0x90F3, "GL_CONTEXT_ROBUST_ACCESS_KHR", }, { - 0x9114, - "GL_SYNC_STATUS_APPLE", + 0x90a3, "GL_SQUARE_CHROMIUM", }, { - 0x9115, - "GL_SYNC_FLAGS_APPLE", + 0x90a4, "GL_ROUND_CHROMIUM", }, { - 0x9116, - "GL_SYNC_FENCE_APPLE", + 0x9100, "GL_TEXTURE_2D_MULTISAMPLE", }, { - 0x9117, - "GL_SYNC_GPU_COMMANDS_COMPLETE_APPLE", + 0x9102, "GL_TEXTURE_2D_MULTISAMPLE_ARRAY_OES", }, { - 0x9118, - "GL_UNSIGNALED_APPLE", + 0x9104, "GL_TEXTURE_BINDING_2D_MULTISAMPLE", }, { - 0x9119, - "GL_SIGNALED_APPLE", + 0x9105, "GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY_OES", }, { - 0x911A, - "GL_ALREADY_SIGNALED_APPLE", + 0x9106, "GL_TEXTURE_SAMPLES", }, { - 0x911B, - "GL_TIMEOUT_EXPIRED_APPLE", + 0x9107, "GL_TEXTURE_FIXED_SAMPLE_LOCATIONS", }, { - 0x911C, - "GL_CONDITION_SATISFIED_APPLE", + 0x9108, "GL_SAMPLER_2D_MULTISAMPLE", }, { - 0x911D, - "GL_WAIT_FAILED_APPLE", + 0x9109, "GL_INT_SAMPLER_2D_MULTISAMPLE", }, { - 0x911F, - "GL_BUFFER_ACCESS_FLAGS", + 0x910A, "GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE", }, { - 0x9120, - "GL_BUFFER_MAP_LENGTH", + 0x910B, "GL_SAMPLER_2D_MULTISAMPLE_ARRAY_OES", }, { - 0x9121, - "GL_BUFFER_MAP_OFFSET", + 0x910C, "GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY_OES", }, { - 0x9122, - "GL_MAX_VERTEX_OUTPUT_COMPONENTS", + 0x910D, "GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY_OES", }, { - 0x9123, - "GL_MAX_GEOMETRY_INPUT_COMPONENTS_OES", + 0x910E, "GL_MAX_COLOR_TEXTURE_SAMPLES", }, { - 0x9124, - "GL_MAX_GEOMETRY_OUTPUT_COMPONENTS_OES", + 0x910F, "GL_MAX_DEPTH_TEXTURE_SAMPLES", }, { - 0x9125, - "GL_MAX_FRAGMENT_INPUT_COMPONENTS", + 0x9110, "GL_MAX_INTEGER_SAMPLES", }, { - 0x912F, - "GL_TEXTURE_IMMUTABLE_FORMAT_EXT", + 0x9111, "GL_MAX_SERVER_WAIT_TIMEOUT_APPLE", }, { - 0x9130, - "GL_SGX_PROGRAM_BINARY_IMG", + 0x9112, "GL_OBJECT_TYPE_APPLE", }, { - 0x9133, - "GL_RENDERBUFFER_SAMPLES_IMG", + 0x9113, "GL_SYNC_CONDITION_APPLE", }, { - 0x9134, - "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_IMG", + 0x9114, "GL_SYNC_STATUS_APPLE", }, { - 0x9135, - "GL_MAX_SAMPLES_IMG", + 0x9115, "GL_SYNC_FLAGS_APPLE", }, { - 0x9136, - "GL_TEXTURE_SAMPLES_IMG", + 0x9116, "GL_SYNC_FENCE_APPLE", }, { - 0x9137, - "GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG", + 0x9117, "GL_SYNC_GPU_COMMANDS_COMPLETE_APPLE", }, { - 0x9138, - "GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG", + 0x9118, "GL_UNSIGNALED_APPLE", }, { - 0x9139, - "GL_CUBIC_IMG", + 0x9119, "GL_SIGNALED_APPLE", }, { - 0x913A, - "GL_CUBIC_MIPMAP_NEAREST_IMG", + 0x911A, "GL_ALREADY_SIGNALED_APPLE", }, { - 0x913B, - "GL_CUBIC_MIPMAP_LINEAR_IMG", + 0x911B, "GL_TIMEOUT_EXPIRED_APPLE", }, { - 0x913C, - "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_AND_DOWNSAMPLE_IMG", + 0x911C, "GL_CONDITION_SATISFIED_APPLE", }, { - 0x913D, - "GL_NUM_DOWNSAMPLE_SCALES_IMG", + 0x911D, "GL_WAIT_FAILED_APPLE", }, { - 0x913E, - "GL_DOWNSAMPLE_SCALES_IMG", + 0x911F, "GL_BUFFER_ACCESS_FLAGS", }, { - 0x913F, - "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_SCALE_IMG", + 0x9120, "GL_BUFFER_MAP_LENGTH", }, { - 0x9143, - "GL_MAX_DEBUG_MESSAGE_LENGTH_KHR", + 0x9121, "GL_BUFFER_MAP_OFFSET", }, { - 0x9144, - "GL_MAX_DEBUG_LOGGED_MESSAGES_KHR", + 0x9122, "GL_MAX_VERTEX_OUTPUT_COMPONENTS", }, { - 0x9145, - "GL_DEBUG_LOGGED_MESSAGES_KHR", + 0x9123, "GL_MAX_GEOMETRY_INPUT_COMPONENTS_OES", }, { - 0x9146, - "GL_DEBUG_SEVERITY_HIGH_KHR", + 0x9124, "GL_MAX_GEOMETRY_OUTPUT_COMPONENTS_OES", }, { - 0x9147, - "GL_DEBUG_SEVERITY_MEDIUM_KHR", + 0x9125, "GL_MAX_FRAGMENT_INPUT_COMPONENTS", }, { - 0x9148, - "GL_DEBUG_SEVERITY_LOW_KHR", + 0x912F, "GL_TEXTURE_IMMUTABLE_FORMAT_EXT", }, { - 0x9151, - "GL_BUFFER_OBJECT_EXT", + 0x9130, "GL_SGX_PROGRAM_BINARY_IMG", }, { - 0x9153, - "GL_QUERY_OBJECT_EXT", + 0x9133, "GL_RENDERBUFFER_SAMPLES_IMG", }, { - 0x9154, - "GL_VERTEX_ARRAY_OBJECT_EXT", + 0x9134, "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_IMG", }, { - 0x9195, - "GL_VIRTUAL_PAGE_SIZE_X_EXT", + 0x9135, "GL_MAX_SAMPLES_IMG", }, { - 0x9196, - "GL_VIRTUAL_PAGE_SIZE_Y_EXT", + 0x9136, "GL_TEXTURE_SAMPLES_IMG", }, { - 0x9197, - "GL_VIRTUAL_PAGE_SIZE_Z_EXT", + 0x9137, "GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG", }, { - 0x9198, - "GL_MAX_SPARSE_TEXTURE_SIZE_EXT", + 0x9138, "GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG", }, { - 0x9199, - "GL_MAX_SPARSE_3D_TEXTURE_SIZE_EXT", + 0x9139, "GL_CUBIC_IMG", }, { - 0x919A, - "GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS_EXT", + 0x913A, "GL_CUBIC_MIPMAP_NEAREST_IMG", }, { - 0x919D, - "GL_TEXTURE_BUFFER_OFFSET_OES", + 0x913B, "GL_CUBIC_MIPMAP_LINEAR_IMG", }, { - 0x919E, - "GL_TEXTURE_BUFFER_SIZE_OES", + 0x913C, "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_AND_DOWNSAMPLE_IMG", }, { - 0x919F, - "GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT_OES", + 0x913D, "GL_NUM_DOWNSAMPLE_SCALES_IMG", }, { - 0x91A6, - "GL_TEXTURE_SPARSE_EXT", + 0x913E, "GL_DOWNSAMPLE_SCALES_IMG", }, { - 0x91A7, - "GL_VIRTUAL_PAGE_SIZE_INDEX_EXT", + 0x913F, "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_SCALE_IMG", }, { - 0x91A8, - "GL_NUM_VIRTUAL_PAGE_SIZES_EXT", + 0x9143, "GL_MAX_DEBUG_MESSAGE_LENGTH_KHR", }, { - 0x91A9, - "GL_SPARSE_TEXTURE_FULL_ARRAY_CUBE_MIPMAPS_EXT", + 0x9144, "GL_MAX_DEBUG_LOGGED_MESSAGES_KHR", }, { - 0x91AA, - "GL_NUM_SPARSE_LEVELS_EXT", + 0x9145, "GL_DEBUG_LOGGED_MESSAGES_KHR", }, { - 0x91B9, - "GL_COMPUTE_SHADER", + 0x9146, "GL_DEBUG_SEVERITY_HIGH_KHR", }, { - 0x91BB, - "GL_MAX_COMPUTE_UNIFORM_BLOCKS", + 0x9147, "GL_DEBUG_SEVERITY_MEDIUM_KHR", }, { - 0x91BC, - "GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS", + 0x9148, "GL_DEBUG_SEVERITY_LOW_KHR", }, { - 0x91BD, - "GL_MAX_COMPUTE_IMAGE_UNIFORMS", + 0x9151, "GL_BUFFER_OBJECT_EXT", }, { - 0x91BE, - "GL_MAX_COMPUTE_WORK_GROUP_COUNT", + 0x9153, "GL_QUERY_OBJECT_EXT", }, { - 0x91BF, - "GL_MAX_COMPUTE_WORK_GROUP_SIZE", + 0x9154, "GL_VERTEX_ARRAY_OBJECT_EXT", }, { - 0x9243, - "GL_UNPACK_COLORSPACE_CONVERSION_CHROMIUM", + 0x9195, "GL_VIRTUAL_PAGE_SIZE_X_EXT", }, { - 0x9244, - "GL_BIND_GENERATES_RESOURCE_CHROMIUM", + 0x9196, "GL_VIRTUAL_PAGE_SIZE_Y_EXT", }, { - 0x9245, - "GL_OVERLAY_TRANSFORM_NONE_CHROMIUM", + 0x9197, "GL_VIRTUAL_PAGE_SIZE_Z_EXT", }, { - 0x9246, - "GL_OVERLAY_TRANSFORM_FLIP_HORIZONTAL_CHROMIUM", + 0x9198, "GL_MAX_SPARSE_TEXTURE_SIZE_EXT", }, { - 0x9247, - "GL_OVERLAY_TRANSFORM_FLIP_VERTICAL_CHROMIUM", + 0x9199, "GL_MAX_SPARSE_3D_TEXTURE_SIZE_EXT", }, { - 0x9248, - "GL_OVERLAY_TRANSFORM_ROTATE_90_CHROMIUM", + 0x919A, "GL_MAX_SPARSE_ARRAY_TEXTURE_LAYERS_EXT", }, { - 0x9249, - "GL_OVERLAY_TRANSFORM_ROTATE_180_CHROMIUM", + 0x919D, "GL_TEXTURE_BUFFER_OFFSET_OES", }, { - 0x924A, - "GL_OVERLAY_TRANSFORM_ROTATE_270_CHROMIUM", + 0x919E, "GL_TEXTURE_BUFFER_SIZE_OES", }, { - 0x9250, - "GL_SHADER_BINARY_DMP", + 0x919F, "GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT_OES", }, { - 0x9251, - "GL_SMAPHS30_PROGRAM_BINARY_DMP", + 0x91A6, "GL_TEXTURE_SPARSE_EXT", }, { - 0x9252, - "GL_SMAPHS_PROGRAM_BINARY_DMP", + 0x91A7, "GL_VIRTUAL_PAGE_SIZE_INDEX_EXT", }, { - 0x9253, - "GL_DMP_PROGRAM_BINARY_DMP", + 0x91A8, "GL_NUM_VIRTUAL_PAGE_SIZES_EXT", }, { - 0x9260, - "GL_GCCSO_SHADER_BINARY_FJ", + 0x91A9, "GL_SPARSE_TEXTURE_FULL_ARRAY_CUBE_MIPMAPS_EXT", }, { - 0x9270, - "GL_COMPRESSED_R11_EAC", + 0x91AA, "GL_NUM_SPARSE_LEVELS_EXT", }, { - 0x9271, - "GL_COMPRESSED_SIGNED_R11_EAC", + 0x91B0, "GL_MAX_SHADER_COMPILER_THREADS_KHR", }, { - 0x9272, - "GL_COMPRESSED_RG11_EAC", + 0x91B1, "GL_COMPLETION_STATUS_KHR", }, { - 0x9273, - "GL_COMPRESSED_SIGNED_RG11_EAC", + 0x91B2, "GL_RENDERBUFFER_STORAGE_SAMPLES_AMD", }, { - 0x9274, - "GL_COMPRESSED_RGB8_ETC2", + 0x91B3, "GL_MAX_COLOR_FRAMEBUFFER_SAMPLES_AMD", }, { - 0x9275, - "GL_COMPRESSED_SRGB8_ETC2", + 0x91B4, "GL_MAX_COLOR_FRAMEBUFFER_STORAGE_SAMPLES_AMD", }, { - 0x9276, - "GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2", + 0x91B5, "GL_MAX_DEPTH_STENCIL_FRAMEBUFFER_SAMPLES_AMD", }, { - 0x9277, - "GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2", + 0x91B6, "GL_NUM_SUPPORTED_MULTISAMPLE_MODES_AMD", }, { - 0x9278, - "GL_COMPRESSED_RGBA8_ETC2_EAC", + 0x91B7, "GL_SUPPORTED_MULTISAMPLE_MODES_AMD", }, { - 0x9279, - "GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC", + 0x91B9, "GL_COMPUTE_SHADER", }, { - 0x9280, - "GL_BLEND_PREMULTIPLIED_SRC_NV", + 0x91BB, "GL_MAX_COMPUTE_UNIFORM_BLOCKS", }, { - 0x9281, - "GL_BLEND_OVERLAP_NV", + 0x91BC, "GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS", }, { - 0x9282, - "GL_UNCORRELATED_NV", + 0x91BD, "GL_MAX_COMPUTE_IMAGE_UNIFORMS", }, { - 0x9283, - "GL_DISJOINT_NV", + 0x91BE, "GL_MAX_COMPUTE_WORK_GROUP_COUNT", }, { - 0x9284, - "GL_CONJOINT_NV", + 0x91BF, "GL_MAX_COMPUTE_WORK_GROUP_SIZE", }, { - 0x9285, - "GL_BLEND_ADVANCED_COHERENT_KHR", + 0x9243, "GL_UNPACK_COLORSPACE_CONVERSION_CHROMIUM", }, { - 0x9286, - "GL_SRC_NV", + 0x9244, "GL_BIND_GENERATES_RESOURCE_CHROMIUM", }, { - 0x9287, - "GL_DST_NV", + 0x9245, "GL_OVERLAY_TRANSFORM_NONE_CHROMIUM", }, { - 0x9288, - "GL_SRC_OVER_NV", + 0x9246, "GL_OVERLAY_TRANSFORM_FLIP_HORIZONTAL_CHROMIUM", }, { - 0x9289, - "GL_DST_OVER_NV", + 0x9247, "GL_OVERLAY_TRANSFORM_FLIP_VERTICAL_CHROMIUM", }, { - 0x928A, - "GL_SRC_IN_NV", + 0x9248, "GL_OVERLAY_TRANSFORM_ROTATE_90_CHROMIUM", }, { - 0x928B, - "GL_DST_IN_NV", + 0x9249, "GL_OVERLAY_TRANSFORM_ROTATE_180_CHROMIUM", }, { - 0x928C, - "GL_SRC_OUT_NV", + 0x924A, "GL_OVERLAY_TRANSFORM_ROTATE_270_CHROMIUM", }, { - 0x928D, - "GL_DST_OUT_NV", + 0x9250, "GL_SHADER_BINARY_DMP", }, { - 0x928E, - "GL_SRC_ATOP_NV", + 0x9251, "GL_SMAPHS30_PROGRAM_BINARY_DMP", }, { - 0x928F, - "GL_DST_ATOP_NV", + 0x9252, "GL_SMAPHS_PROGRAM_BINARY_DMP", }, { - 0x9291, - "GL_PLUS_NV", + 0x9253, "GL_DMP_PROGRAM_BINARY_DMP", }, { - 0x9292, - "GL_PLUS_DARKER_NV", + 0x9260, "GL_GCCSO_SHADER_BINARY_FJ", }, { - 0x9294, - "GL_MULTIPLY_KHR", + 0x9270, "GL_COMPRESSED_R11_EAC", }, { - 0x9295, - "GL_SCREEN_KHR", + 0x9271, "GL_COMPRESSED_SIGNED_R11_EAC", }, { - 0x9296, - "GL_OVERLAY_KHR", + 0x9272, "GL_COMPRESSED_RG11_EAC", }, { - 0x9297, - "GL_DARKEN_KHR", + 0x9273, "GL_COMPRESSED_SIGNED_RG11_EAC", }, { - 0x9298, - "GL_LIGHTEN_KHR", + 0x9274, "GL_COMPRESSED_RGB8_ETC2", }, { - 0x9299, - "GL_COLORDODGE_KHR", + 0x9275, "GL_COMPRESSED_SRGB8_ETC2", }, { - 0x929A, - "GL_COLORBURN_KHR", + 0x9276, "GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2", }, { - 0x929B, - "GL_HARDLIGHT_KHR", + 0x9277, "GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2", }, { - 0x929C, - "GL_SOFTLIGHT_KHR", + 0x9278, "GL_COMPRESSED_RGBA8_ETC2_EAC", }, { - 0x929E, - "GL_DIFFERENCE_KHR", + 0x9279, "GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC", }, { - 0x929F, - "GL_MINUS_NV", + 0x9280, "GL_BLEND_PREMULTIPLIED_SRC_NV", }, { - 0x92A0, - "GL_EXCLUSION_KHR", + 0x9281, "GL_BLEND_OVERLAP_NV", }, { - 0x92A1, - "GL_CONTRAST_NV", + 0x9282, "GL_UNCORRELATED_NV", }, { - 0x92A3, - "GL_INVERT_RGB_NV", + 0x9283, "GL_DISJOINT_NV", }, { - 0x92A4, - "GL_LINEARDODGE_NV", + 0x9284, "GL_CONJOINT_NV", }, { - 0x92A5, - "GL_LINEARBURN_NV", + 0x9285, "GL_BLEND_ADVANCED_COHERENT_KHR", }, { - 0x92A6, - "GL_VIVIDLIGHT_NV", + 0x9286, "GL_SRC_NV", }, { - 0x92A7, - "GL_LINEARLIGHT_NV", + 0x9287, "GL_DST_NV", }, { - 0x92A8, - "GL_PINLIGHT_NV", + 0x9288, "GL_SRC_OVER_NV", }, { - 0x92A9, - "GL_HARDMIX_NV", + 0x9289, "GL_DST_OVER_NV", }, { - 0x92AD, - "GL_HSL_HUE_KHR", + 0x928A, "GL_SRC_IN_NV", }, { - 0x92AE, - "GL_HSL_SATURATION_KHR", + 0x928B, "GL_DST_IN_NV", }, { - 0x92AF, - "GL_HSL_COLOR_KHR", + 0x928C, "GL_SRC_OUT_NV", }, { - 0x92B0, - "GL_HSL_LUMINOSITY_KHR", + 0x928D, "GL_DST_OUT_NV", }, { - 0x92B1, - "GL_PLUS_CLAMPED_NV", + 0x928E, "GL_SRC_ATOP_NV", }, { - 0x92B2, - "GL_PLUS_CLAMPED_ALPHA_NV", + 0x928F, "GL_DST_ATOP_NV", }, { - 0x92B3, - "GL_MINUS_CLAMPED_NV", + 0x9291, "GL_PLUS_NV", }, { - 0x92B4, - "GL_INVERT_OVG_NV", + 0x9292, "GL_PLUS_DARKER_NV", }, { - 0x92BE, - "GL_PRIMITIVE_BOUNDING_BOX_OES", + 0x9294, "GL_MULTIPLY_KHR", }, { - 0x92C0, - "GL_ATOMIC_COUNTER_BUFFER", + 0x9295, "GL_SCREEN_KHR", }, { - 0x92C1, - "GL_ATOMIC_COUNTER_BUFFER_BINDING", + 0x9296, "GL_OVERLAY_KHR", }, { - 0x92C2, - "GL_ATOMIC_COUNTER_BUFFER_START", + 0x9297, "GL_DARKEN_KHR", }, { - 0x92C3, - "GL_ATOMIC_COUNTER_BUFFER_SIZE", + 0x9298, "GL_LIGHTEN_KHR", }, { - 0x92CC, - "GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS", + 0x9299, "GL_COLORDODGE_KHR", }, { - 0x92CD, - "GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS_OES", + 0x929A, "GL_COLORBURN_KHR", }, { - 0x92CE, - "GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS_OES", + 0x929B, "GL_HARDLIGHT_KHR", }, { - 0x92CF, - "GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS_OES", + 0x929C, "GL_SOFTLIGHT_KHR", }, { - 0x92D0, - "GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS", + 0x929E, "GL_DIFFERENCE_KHR", }, { - 0x92D1, - "GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS", + 0x929F, "GL_MINUS_NV", }, { - 0x92D2, - "GL_MAX_VERTEX_ATOMIC_COUNTERS", + 0x92A0, "GL_EXCLUSION_KHR", }, { - 0x92D3, - "GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS_OES", + 0x92A1, "GL_CONTRAST_NV", }, { - 0x92D4, - "GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS_OES", + 0x92A3, "GL_INVERT_RGB_NV", }, { - 0x92D5, - "GL_MAX_GEOMETRY_ATOMIC_COUNTERS_OES", + 0x92A4, "GL_LINEARDODGE_NV", }, { - 0x92D6, - "GL_MAX_FRAGMENT_ATOMIC_COUNTERS", + 0x92A5, "GL_LINEARBURN_NV", }, { - 0x92D7, - "GL_MAX_COMBINED_ATOMIC_COUNTERS", + 0x92A6, "GL_VIVIDLIGHT_NV", }, { - 0x92D8, - "GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE", + 0x92A7, "GL_LINEARLIGHT_NV", }, { - 0x92D9, - "GL_ACTIVE_ATOMIC_COUNTER_BUFFERS", + 0x92A8, "GL_PINLIGHT_NV", }, { - 0x92DB, - "GL_UNSIGNED_INT_ATOMIC_COUNTER", + 0x92A9, "GL_HARDMIX_NV", }, { - 0x92DC, - "GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS", + 0x92AD, "GL_HSL_HUE_KHR", }, { - 0x92DD, - "GL_FRAGMENT_COVERAGE_TO_COLOR_NV", + 0x92AE, "GL_HSL_SATURATION_KHR", }, { - 0x92DE, - "GL_FRAGMENT_COVERAGE_COLOR_NV", + 0x92AF, "GL_HSL_COLOR_KHR", }, { - 0x92E0, - "GL_DEBUG_OUTPUT_KHR", + 0x92B0, "GL_HSL_LUMINOSITY_KHR", }, { - 0x92E1, - "GL_UNIFORM", + 0x92B1, "GL_PLUS_CLAMPED_NV", }, { - 0x92E2, - "GL_UNIFORM_BLOCK", + 0x92B2, "GL_PLUS_CLAMPED_ALPHA_NV", }, { - 0x92E3, - "GL_PROGRAM_INPUT", + 0x92B3, "GL_MINUS_CLAMPED_NV", }, { - 0x92E4, - "GL_PROGRAM_OUTPUT", + 0x92B4, "GL_INVERT_OVG_NV", }, { - 0x92E5, - "GL_BUFFER_VARIABLE", + 0x92BE, "GL_PRIMITIVE_BOUNDING_BOX_OES", }, { - 0x92E6, - "GL_SHADER_STORAGE_BLOCK", + 0x92C0, "GL_ATOMIC_COUNTER_BUFFER", }, { - 0x92E7, - "GL_IS_PER_PATCH_OES", + 0x92C1, "GL_ATOMIC_COUNTER_BUFFER_BINDING", }, { - 0x92F4, - "GL_TRANSFORM_FEEDBACK_VARYING", + 0x92C2, "GL_ATOMIC_COUNTER_BUFFER_START", }, { - 0x92F5, - "GL_ACTIVE_RESOURCES", + 0x92C3, "GL_ATOMIC_COUNTER_BUFFER_SIZE", }, { - 0x92F6, - "GL_MAX_NAME_LENGTH", + 0x92CC, "GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS", }, { - 0x92F7, - "GL_MAX_NUM_ACTIVE_VARIABLES", + 0x92CD, "GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS_OES", }, { - 0x92F9, - "GL_NAME_LENGTH", + 0x92CE, "GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS_OES", }, { - 0x92FA, - "GL_TYPE", + 0x92CF, "GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS_OES", }, { - 0x92FB, - "GL_ARRAY_SIZE", + 0x92D0, "GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS", }, { - 0x92FC, - "GL_OFFSET", + 0x92D1, "GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS", }, { - 0x92FD, - "GL_BLOCK_INDEX", + 0x92D2, "GL_MAX_VERTEX_ATOMIC_COUNTERS", }, { - 0x92FE, - "GL_ARRAY_STRIDE", + 0x92D3, "GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS_OES", }, { - 0x92FF, - "GL_MATRIX_STRIDE", + 0x92D4, "GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS_OES", }, { - 0x9300, - "GL_IS_ROW_MAJOR", + 0x92D5, "GL_MAX_GEOMETRY_ATOMIC_COUNTERS_OES", }, { - 0x9301, - "GL_ATOMIC_COUNTER_BUFFER_INDEX", + 0x92D6, "GL_MAX_FRAGMENT_ATOMIC_COUNTERS", }, { - 0x9302, - "GL_BUFFER_BINDING", + 0x92D7, "GL_MAX_COMBINED_ATOMIC_COUNTERS", }, { - 0x9303, - "GL_BUFFER_DATA_SIZE", + 0x92D8, "GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE", }, { - 0x9304, - "GL_NUM_ACTIVE_VARIABLES", + 0x92D9, "GL_ACTIVE_ATOMIC_COUNTER_BUFFERS", }, { - 0x9305, - "GL_ACTIVE_VARIABLES", + 0x92DB, "GL_UNSIGNED_INT_ATOMIC_COUNTER", }, { - 0x9306, - "GL_REFERENCED_BY_VERTEX_SHADER", + 0x92DC, "GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS", }, { - 0x9307, - "GL_REFERENCED_BY_TESS_CONTROL_SHADER_OES", + 0x92DD, "GL_FRAGMENT_COVERAGE_TO_COLOR_NV", }, { - 0x9308, - "GL_REFERENCED_BY_TESS_EVALUATION_SHADER_OES", + 0x92DE, "GL_FRAGMENT_COVERAGE_COLOR_NV", }, { - 0x9309, - "GL_REFERENCED_BY_GEOMETRY_SHADER_OES", + 0x92E0, "GL_DEBUG_OUTPUT_KHR", }, { - 0x930A, - "GL_REFERENCED_BY_FRAGMENT_SHADER", + 0x92E1, "GL_UNIFORM", }, { - 0x930B, - "GL_REFERENCED_BY_COMPUTE_SHADER", + 0x92E2, "GL_UNIFORM_BLOCK", }, { - 0x930C, - "GL_TOP_LEVEL_ARRAY_SIZE", + 0x92E3, "GL_PROGRAM_INPUT", }, { - 0x930D, - "GL_TOP_LEVEL_ARRAY_STRIDE", + 0x92E4, "GL_PROGRAM_OUTPUT", }, { - 0x930E, - "GL_LOCATION", + 0x92E5, "GL_BUFFER_VARIABLE", }, { - 0x930F, - "GL_LOCATION_INDEX_EXT", + 0x92E6, "GL_SHADER_STORAGE_BLOCK", }, { - 0x9310, - "GL_FRAMEBUFFER_DEFAULT_WIDTH", + 0x92E7, "GL_IS_PER_PATCH_OES", }, { - 0x9311, - "GL_FRAMEBUFFER_DEFAULT_HEIGHT", + 0x92F4, "GL_TRANSFORM_FEEDBACK_VARYING", }, { - 0x9312, - "GL_FRAMEBUFFER_DEFAULT_LAYERS_OES", + 0x92F5, "GL_ACTIVE_RESOURCES", }, { - 0x9313, - "GL_FRAMEBUFFER_DEFAULT_SAMPLES", + 0x92F6, "GL_MAX_NAME_LENGTH", }, { - 0x9314, - "GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS", + 0x92F7, "GL_MAX_NUM_ACTIVE_VARIABLES", }, { - 0x9315, - "GL_MAX_FRAMEBUFFER_WIDTH", + 0x92F9, "GL_NAME_LENGTH", }, { - 0x9316, - "GL_MAX_FRAMEBUFFER_HEIGHT", + 0x92FA, "GL_TYPE", }, { - 0x9317, - "GL_MAX_FRAMEBUFFER_LAYERS_OES", + 0x92FB, "GL_ARRAY_SIZE", }, { - 0x9318, - "GL_MAX_FRAMEBUFFER_SAMPLES", + 0x92FC, "GL_OFFSET", }, { - 0x9327, - "GL_RASTER_MULTISAMPLE_EXT", + 0x92FD, "GL_BLOCK_INDEX", }, { - 0x9328, - "GL_RASTER_SAMPLES_EXT", + 0x92FE, "GL_ARRAY_STRIDE", }, { - 0x9329, - "GL_MAX_RASTER_SAMPLES_EXT", + 0x92FF, "GL_MATRIX_STRIDE", }, { - 0x932A, - "GL_RASTER_FIXED_SAMPLE_LOCATIONS_EXT", + 0x9300, "GL_IS_ROW_MAJOR", }, { - 0x932B, - "GL_MULTISAMPLE_RASTERIZATION_ALLOWED_EXT", + 0x9301, "GL_ATOMIC_COUNTER_BUFFER_INDEX", }, { - 0x932C, - "GL_EFFECTIVE_RASTER_SAMPLES_EXT", + 0x9302, "GL_BUFFER_BINDING", }, { - 0x932D, - "GL_DEPTH_SAMPLES_NV", + 0x9303, "GL_BUFFER_DATA_SIZE", }, { - 0x932E, - "GL_STENCIL_SAMPLES_NV", + 0x9304, "GL_NUM_ACTIVE_VARIABLES", }, { - 0x932F, - "GL_MIXED_DEPTH_SAMPLES_SUPPORTED_NV", + 0x9305, "GL_ACTIVE_VARIABLES", }, { - 0x9330, - "GL_MIXED_STENCIL_SAMPLES_SUPPORTED_NV", + 0x9306, "GL_REFERENCED_BY_VERTEX_SHADER", }, { - 0x9331, - "GL_COVERAGE_MODULATION_TABLE_NV", + 0x9307, "GL_REFERENCED_BY_TESS_CONTROL_SHADER_OES", }, { - 0x9332, - "GL_COVERAGE_MODULATION_NV", + 0x9308, "GL_REFERENCED_BY_TESS_EVALUATION_SHADER_OES", }, { - 0x9333, - "GL_COVERAGE_MODULATION_TABLE_SIZE_NV", + 0x9309, "GL_REFERENCED_BY_GEOMETRY_SHADER_OES", }, { - 0x933C, - "GL_FILL_RECTANGLE_NV", + 0x930A, "GL_REFERENCED_BY_FRAGMENT_SHADER", }, { - 0x933D, - "GL_SAMPLE_LOCATION_SUBPIXEL_BITS_NV", + 0x930B, "GL_REFERENCED_BY_COMPUTE_SHADER", }, { - 0x933E, - "GL_SAMPLE_LOCATION_PIXEL_GRID_WIDTH_NV", + 0x930C, "GL_TOP_LEVEL_ARRAY_SIZE", }, { - 0x933F, - "GL_SAMPLE_LOCATION_PIXEL_GRID_HEIGHT_NV", + 0x930D, "GL_TOP_LEVEL_ARRAY_STRIDE", }, { - 0x9340, - "GL_PROGRAMMABLE_SAMPLE_LOCATION_TABLE_SIZE_NV", + 0x930E, "GL_LOCATION", }, { - 0x9341, - "GL_PROGRAMMABLE_SAMPLE_LOCATION_NV", + 0x930F, "GL_LOCATION_INDEX_EXT", }, { - 0x9342, - "GL_FRAMEBUFFER_PROGRAMMABLE_SAMPLE_LOCATIONS_NV", + 0x9310, "GL_FRAMEBUFFER_DEFAULT_WIDTH", }, { - 0x9343, - "GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_NV", + 0x9311, "GL_FRAMEBUFFER_DEFAULT_HEIGHT", }, { - 0x9346, - "GL_CONSERVATIVE_RASTERIZATION_NV", + 0x9312, "GL_FRAMEBUFFER_DEFAULT_LAYERS_OES", }, { - 0x9347, - "GL_SUBPIXEL_PRECISION_BIAS_X_BITS_NV", + 0x9313, "GL_FRAMEBUFFER_DEFAULT_SAMPLES", }, { - 0x9348, - "GL_SUBPIXEL_PRECISION_BIAS_Y_BITS_NV", + 0x9314, "GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS", }, { - 0x9349, - "GL_MAX_SUBPIXEL_PRECISION_BIAS_BITS_NV", + 0x9315, "GL_MAX_FRAMEBUFFER_WIDTH", }, { - 0x9350, - "GL_VIEWPORT_SWIZZLE_POSITIVE_X_NV", + 0x9316, "GL_MAX_FRAMEBUFFER_HEIGHT", }, { - 0x9351, - "GL_VIEWPORT_SWIZZLE_NEGATIVE_X_NV", + 0x9317, "GL_MAX_FRAMEBUFFER_LAYERS_OES", }, { - 0x9352, - "GL_VIEWPORT_SWIZZLE_POSITIVE_Y_NV", + 0x9318, "GL_MAX_FRAMEBUFFER_SAMPLES", }, { - 0x9353, - "GL_VIEWPORT_SWIZZLE_NEGATIVE_Y_NV", + 0x9327, "GL_RASTER_MULTISAMPLE_EXT", }, { - 0x9354, - "GL_VIEWPORT_SWIZZLE_POSITIVE_Z_NV", + 0x9328, "GL_RASTER_SAMPLES_EXT", }, { - 0x9355, - "GL_VIEWPORT_SWIZZLE_NEGATIVE_Z_NV", + 0x9329, "GL_MAX_RASTER_SAMPLES_EXT", }, { - 0x9356, - "GL_VIEWPORT_SWIZZLE_POSITIVE_W_NV", + 0x932A, "GL_RASTER_FIXED_SAMPLE_LOCATIONS_EXT", }, { - 0x9357, - "GL_VIEWPORT_SWIZZLE_NEGATIVE_W_NV", + 0x932B, "GL_MULTISAMPLE_RASTERIZATION_ALLOWED_EXT", }, { - 0x9358, - "GL_VIEWPORT_SWIZZLE_X_NV", + 0x932C, "GL_EFFECTIVE_RASTER_SAMPLES_EXT", }, { - 0x9359, - "GL_VIEWPORT_SWIZZLE_Y_NV", + 0x932D, "GL_DEPTH_SAMPLES_NV", }, { - 0x935A, - "GL_VIEWPORT_SWIZZLE_Z_NV", + 0x932E, "GL_STENCIL_SAMPLES_NV", }, { - 0x935B, - "GL_VIEWPORT_SWIZZLE_W_NV", + 0x932F, "GL_MIXED_DEPTH_SAMPLES_SUPPORTED_NV", }, { - 0x9368, - "GL_FONT_GLYPHS_AVAILABLE_NV", + 0x9330, "GL_MIXED_STENCIL_SAMPLES_SUPPORTED_NV", }, { - 0x9369, - "GL_FONT_TARGET_UNAVAILABLE_NV", + 0x9331, "GL_COVERAGE_MODULATION_TABLE_NV", }, { - 0x936A, - "GL_FONT_UNAVAILABLE_NV", + 0x9332, "GL_COVERAGE_MODULATION_NV", }, { - 0x936B, - "GL_FONT_UNINTELLIGIBLE_NV", + 0x9333, "GL_COVERAGE_MODULATION_TABLE_SIZE_NV", }, { - 0x936C, - "GL_STANDARD_FONT_FORMAT_NV", + 0x933C, "GL_FILL_RECTANGLE_NV", }, { - 0x936D, - "GL_FRAGMENT_INPUT_NV", + 0x933D, "GL_SAMPLE_LOCATION_SUBPIXEL_BITS_NV", }, { - 0x9371, - "GL_MULTISAMPLES_NV", + 0x933E, "GL_SAMPLE_LOCATION_PIXEL_GRID_WIDTH_NV", }, { - 0x9372, - "GL_SUPERSAMPLE_SCALE_X_NV", + 0x933F, "GL_SAMPLE_LOCATION_PIXEL_GRID_HEIGHT_NV", }, { - 0x9373, - "GL_SUPERSAMPLE_SCALE_Y_NV", + 0x9340, "GL_PROGRAMMABLE_SAMPLE_LOCATION_TABLE_SIZE_NV", }, { - 0x9374, - "GL_CONFORMANT_NV", + 0x9341, "GL_PROGRAMMABLE_SAMPLE_LOCATION_NV", }, { - 0x9380, - "GL_NUM_SAMPLE_COUNTS", + 0x9342, "GL_FRAMEBUFFER_PROGRAMMABLE_SAMPLE_LOCATIONS_NV", }, { - 0x93A0, - "GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE", + 0x9343, "GL_FRAMEBUFFER_SAMPLE_LOCATION_PIXEL_GRID_NV", }, { - 0x93A1, - "GL_BGRA8_EXT", + 0x9346, "GL_CONSERVATIVE_RASTERIZATION_NV", }, { - 0x93A2, - "GL_TEXTURE_USAGE_ANGLE", + 0x9347, "GL_SUBPIXEL_PRECISION_BIAS_X_BITS_NV", }, { - 0x93A3, - "GL_FRAMEBUFFER_ATTACHMENT_ANGLE", + 0x9348, "GL_SUBPIXEL_PRECISION_BIAS_Y_BITS_NV", }, { - 0x93A4, - "GL_PACK_REVERSE_ROW_ORDER_ANGLE", + 0x9349, "GL_MAX_SUBPIXEL_PRECISION_BIAS_BITS_NV", }, { - 0x93A6, - "GL_PROGRAM_BINARY_ANGLE", + 0x9350, "GL_VIEWPORT_SWIZZLE_POSITIVE_X_NV", }, { - 0x93B0, - "GL_COMPRESSED_RGBA_ASTC_4x4_KHR", + 0x9351, "GL_VIEWPORT_SWIZZLE_NEGATIVE_X_NV", }, { - 0x93B1, - "GL_COMPRESSED_RGBA_ASTC_5x4_KHR", + 0x9352, "GL_VIEWPORT_SWIZZLE_POSITIVE_Y_NV", }, { - 0x93B2, - "GL_COMPRESSED_RGBA_ASTC_5x5_KHR", + 0x9353, "GL_VIEWPORT_SWIZZLE_NEGATIVE_Y_NV", }, { - 0x93B3, - "GL_COMPRESSED_RGBA_ASTC_6x5_KHR", + 0x9354, "GL_VIEWPORT_SWIZZLE_POSITIVE_Z_NV", }, { - 0x93B4, - "GL_COMPRESSED_RGBA_ASTC_6x6_KHR", + 0x9355, "GL_VIEWPORT_SWIZZLE_NEGATIVE_Z_NV", }, { - 0x93B5, - "GL_COMPRESSED_RGBA_ASTC_8x5_KHR", + 0x9356, "GL_VIEWPORT_SWIZZLE_POSITIVE_W_NV", }, { - 0x93B6, - "GL_COMPRESSED_RGBA_ASTC_8x6_KHR", + 0x9357, "GL_VIEWPORT_SWIZZLE_NEGATIVE_W_NV", }, { - 0x93B7, - "GL_COMPRESSED_RGBA_ASTC_8x8_KHR", + 0x9358, "GL_VIEWPORT_SWIZZLE_X_NV", }, { - 0x93B8, - "GL_COMPRESSED_RGBA_ASTC_10x5_KHR", + 0x9359, "GL_VIEWPORT_SWIZZLE_Y_NV", }, { - 0x93B9, - "GL_COMPRESSED_RGBA_ASTC_10x6_KHR", + 0x935A, "GL_VIEWPORT_SWIZZLE_Z_NV", }, { - 0x93BA, - "GL_COMPRESSED_RGBA_ASTC_10x8_KHR", + 0x935B, "GL_VIEWPORT_SWIZZLE_W_NV", }, { - 0x93BB, - "GL_COMPRESSED_RGBA_ASTC_10x10_KHR", + 0x935C, "GL_CLIP_ORIGIN_EXT", }, { - 0x93BC, - "GL_COMPRESSED_RGBA_ASTC_12x10_KHR", + 0x935D, "GL_CLIP_DEPTH_MODE_EXT", }, { - 0x93BD, - "GL_COMPRESSED_RGBA_ASTC_12x12_KHR", + 0x935E, "GL_NEGATIVE_ONE_TO_ONE_EXT", }, { - 0x93C0, - "GL_COMPRESSED_RGBA_ASTC_3x3x3_OES", + 0x935F, "GL_ZERO_TO_ONE_EXT", }, { - 0x93C1, - "GL_COMPRESSED_RGBA_ASTC_4x3x3_OES", + 0x9366, "GL_TEXTURE_REDUCTION_MODE_EXT", }, { - 0x93C2, - "GL_COMPRESSED_RGBA_ASTC_4x4x3_OES", + 0x9367, "GL_WEIGHTED_AVERAGE_EXT", }, { - 0x93C3, - "GL_COMPRESSED_RGBA_ASTC_4x4x4_OES", + 0x9368, "GL_FONT_GLYPHS_AVAILABLE_NV", }, { - 0x93C4, - "GL_COMPRESSED_RGBA_ASTC_5x4x4_OES", + 0x9369, "GL_FONT_TARGET_UNAVAILABLE_NV", }, { - 0x93C5, - "GL_COMPRESSED_RGBA_ASTC_5x5x4_OES", + 0x936A, "GL_FONT_UNAVAILABLE_NV", }, { - 0x93C6, - "GL_COMPRESSED_RGBA_ASTC_5x5x5_OES", + 0x936B, "GL_FONT_UNINTELLIGIBLE_NV", }, { - 0x93C7, - "GL_COMPRESSED_RGBA_ASTC_6x5x5_OES", + 0x936C, "GL_STANDARD_FONT_FORMAT_NV", }, { - 0x93C8, - "GL_COMPRESSED_RGBA_ASTC_6x6x5_OES", + 0x936D, "GL_FRAGMENT_INPUT_NV", }, { - 0x93C9, - "GL_COMPRESSED_RGBA_ASTC_6x6x6_OES", + 0x9371, "GL_MULTISAMPLES_NV", }, { - 0x93D0, - "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR", + 0x9372, "GL_SUPERSAMPLE_SCALE_X_NV", }, { - 0x93D1, - "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR", + 0x9373, "GL_SUPERSAMPLE_SCALE_Y_NV", }, { - 0x93D2, - "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR", + 0x9374, "GL_CONFORMANT_NV", }, { - 0x93D3, - "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR", + 0x937C, "GL_VIEWPORT_POSITION_W_SCALE_NV", }, { - 0x93D4, - "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR", + 0x937D, "GL_VIEWPORT_POSITION_W_SCALE_X_COEFF_NV", }, { - 0x93D5, - "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR", + 0x937E, "GL_VIEWPORT_POSITION_W_SCALE_Y_COEFF_NV", }, { - 0x93D6, - "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR", + 0x9380, "GL_NUM_SAMPLE_COUNTS", }, { - 0x93D7, - "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR", + 0x93A0, "GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE", }, { - 0x93D8, - "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR", + 0x93A1, "GL_BGRA8_EXT", }, { - 0x93D9, - "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR", + 0x93A2, "GL_TEXTURE_USAGE_ANGLE", }, { - 0x93DA, - "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR", + 0x93A3, "GL_FRAMEBUFFER_ATTACHMENT_ANGLE", }, { - 0x93DB, - "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR", + 0x93A4, "GL_PACK_REVERSE_ROW_ORDER_ANGLE", }, { - 0x93DC, - "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR", + 0x93A6, "GL_PROGRAM_BINARY_ANGLE", }, { - 0x93DD, - "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR", + 0x93B0, "GL_COMPRESSED_RGBA_ASTC_4x4_KHR", }, { - 0x93E0, - "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES", + 0x93B1, "GL_COMPRESSED_RGBA_ASTC_5x4_KHR", }, { - 0x93E1, - "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES", + 0x93B2, "GL_COMPRESSED_RGBA_ASTC_5x5_KHR", }, { - 0x93E2, - "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES", + 0x93B3, "GL_COMPRESSED_RGBA_ASTC_6x5_KHR", }, { - 0x93E3, - "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES", + 0x93B4, "GL_COMPRESSED_RGBA_ASTC_6x6_KHR", }, { - 0x93E4, - "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES", + 0x93B5, "GL_COMPRESSED_RGBA_ASTC_8x5_KHR", }, { - 0x93E5, - "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES", + 0x93B6, "GL_COMPRESSED_RGBA_ASTC_8x6_KHR", }, { - 0x93E6, - "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES", + 0x93B7, "GL_COMPRESSED_RGBA_ASTC_8x8_KHR", }, { - 0x93E7, - "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES", + 0x93B8, "GL_COMPRESSED_RGBA_ASTC_10x5_KHR", }, { - 0x93E8, - "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES", + 0x93B9, "GL_COMPRESSED_RGBA_ASTC_10x6_KHR", }, { - 0x93E9, - "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES", + 0x93BA, "GL_COMPRESSED_RGBA_ASTC_10x8_KHR", }, { - 0x93F0, - "GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG", + 0x93BB, "GL_COMPRESSED_RGBA_ASTC_10x10_KHR", }, { - 0x93F1, - "GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG", + 0x93BC, "GL_COMPRESSED_RGBA_ASTC_12x10_KHR", }, { - 0x94F0, - "GL_PERFQUERY_COUNTER_EVENT_INTEL", + 0x93BD, "GL_COMPRESSED_RGBA_ASTC_12x12_KHR", }, { - 0x94F1, - "GL_PERFQUERY_COUNTER_DURATION_NORM_INTEL", + 0x93C0, "GL_COMPRESSED_RGBA_ASTC_3x3x3_OES", }, { - 0x94F2, - "GL_PERFQUERY_COUNTER_DURATION_RAW_INTEL", + 0x93C1, "GL_COMPRESSED_RGBA_ASTC_4x3x3_OES", }, { - 0x94F3, - "GL_PERFQUERY_COUNTER_THROUGHPUT_INTEL", + 0x93C2, "GL_COMPRESSED_RGBA_ASTC_4x4x3_OES", }, { - 0x94F4, - "GL_PERFQUERY_COUNTER_RAW_INTEL", + 0x93C3, "GL_COMPRESSED_RGBA_ASTC_4x4x4_OES", }, { - 0x94F5, - "GL_PERFQUERY_COUNTER_TIMESTAMP_INTEL", + 0x93C4, "GL_COMPRESSED_RGBA_ASTC_5x4x4_OES", }, { - 0x94F8, - "GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL", + 0x93C5, "GL_COMPRESSED_RGBA_ASTC_5x5x4_OES", }, { - 0x94F9, - "GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL", + 0x93C6, "GL_COMPRESSED_RGBA_ASTC_5x5x5_OES", }, { - 0x94FA, - "GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL", + 0x93C7, "GL_COMPRESSED_RGBA_ASTC_6x5x5_OES", }, { - 0x94FB, - "GL_PERFQUERY_COUNTER_DATA_DOUBLE_INTEL", + 0x93C8, "GL_COMPRESSED_RGBA_ASTC_6x6x5_OES", }, { - 0x94FC, - "GL_PERFQUERY_COUNTER_DATA_BOOL32_INTEL", + 0x93C9, "GL_COMPRESSED_RGBA_ASTC_6x6x6_OES", }, { - 0x94FD, - "GL_PERFQUERY_QUERY_NAME_LENGTH_MAX_INTEL", + 0x93D0, "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR", }, { - 0x94FE, - "GL_PERFQUERY_COUNTER_NAME_LENGTH_MAX_INTEL", + 0x93D1, "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR", }, { - 0x94FF, - "GL_PERFQUERY_COUNTER_DESC_LENGTH_MAX_INTEL", + 0x93D2, "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR", }, { - 0x9500, - "GL_PERFQUERY_GPA_EXTENDED_COUNTERS_INTEL", + 0x93D3, "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR", }, { - 0x954D, - "GL_CONSERVATIVE_RASTER_MODE_NV", + 0x93D4, "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR", }, { - 0x954E, - "GL_CONSERVATIVE_RASTER_MODE_POST_SNAP_NV", + 0x93D5, "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR", }, { - 0x954F, - "GL_CONSERVATIVE_RASTER_MODE_PRE_SNAP_TRIANGLES_NV", + 0x93D6, "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR", }, { - 0x9630, - "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR", + 0x93D7, "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR", }, { - 0x9631, - "GL_MAX_VIEWS_OVR", + 0x93D8, "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR", }, { - 0x9632, - "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR", + 0x93D9, "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR", }, { - 0x9633, - "GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR", + 0x93DA, "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR", }, { - 0x9650, - "GL_MAX_SHADER_COMBINED_LOCAL_STORAGE_FAST_SIZE_EXT", + 0x93DB, "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR", }, { - 0x9651, - "GL_MAX_SHADER_COMBINED_LOCAL_STORAGE_SIZE_EXT", + 0x93DC, "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR", }, { - 0x9652, - "GL_FRAMEBUFFER_INCOMPLETE_INSUFFICIENT_SHADER_COMBINED_LOCAL_STORAGE_" - "EXT", + 0x93DD, "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR", + }, + { + 0x93E0, "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES", + }, + { + 0x93E1, "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES", + }, + { + 0x93E2, "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES", + }, + { + 0x93E3, "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES", + }, + { + 0x93E4, "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES", + }, + { + 0x93E5, "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES", + }, + { + 0x93E6, "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES", + }, + { + 0x93E7, "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES", + }, + { + 0x93E8, "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES", + }, + { + 0x93E9, "GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES", + }, + { + 0x93F0, "GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG", + }, + { + 0x93F1, "GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG", + }, + { + 0x94F0, "GL_PERFQUERY_COUNTER_EVENT_INTEL", + }, + { + 0x94F1, "GL_PERFQUERY_COUNTER_DURATION_NORM_INTEL", + }, + { + 0x94F2, "GL_PERFQUERY_COUNTER_DURATION_RAW_INTEL", + }, + { + 0x94F3, "GL_PERFQUERY_COUNTER_THROUGHPUT_INTEL", + }, + { + 0x94F4, "GL_PERFQUERY_COUNTER_RAW_INTEL", + }, + { + 0x94F5, "GL_PERFQUERY_COUNTER_TIMESTAMP_INTEL", + }, + { + 0x94F8, "GL_PERFQUERY_COUNTER_DATA_UINT32_INTEL", + }, + { + 0x94F9, "GL_PERFQUERY_COUNTER_DATA_UINT64_INTEL", + }, + { + 0x94FA, "GL_PERFQUERY_COUNTER_DATA_FLOAT_INTEL", + }, + { + 0x94FB, "GL_PERFQUERY_COUNTER_DATA_DOUBLE_INTEL", + }, + { + 0x94FC, "GL_PERFQUERY_COUNTER_DATA_BOOL32_INTEL", + }, + { + 0x94FD, "GL_PERFQUERY_QUERY_NAME_LENGTH_MAX_INTEL", + }, + { + 0x94FE, "GL_PERFQUERY_COUNTER_NAME_LENGTH_MAX_INTEL", + }, + { + 0x94FF, "GL_PERFQUERY_COUNTER_DESC_LENGTH_MAX_INTEL", + }, + { + 0x9500, "GL_PERFQUERY_GPA_EXTENDED_COUNTERS_INTEL", + }, + { + 0x9530, "GL_LAYOUT_DEPTH_READ_ONLY_STENCIL_ATTACHMENT_EXT", + }, + { + 0x9531, "GL_LAYOUT_DEPTH_ATTACHMENT_STENCIL_READ_ONLY_EXT", + }, + { + 0x954D, "GL_CONSERVATIVE_RASTER_MODE_NV", + }, + { + 0x954E, "GL_CONSERVATIVE_RASTER_MODE_POST_SNAP_NV", + }, + { + 0x954F, "GL_CONSERVATIVE_RASTER_MODE_PRE_SNAP_TRIANGLES_NV", + }, + { + 0x9550, "GL_CONSERVATIVE_RASTER_MODE_PRE_SNAP_NV", }, { - 0xC0, - "GL_SHARED_EDGE_NV", + 0x9580, "GL_TEXTURE_TILING_EXT", }, { - 0xE8, - "GL_ROUNDED_RECT_NV", + 0x9581, "GL_DEDICATED_MEMORY_OBJECT_EXT", + }, + { + 0x9582, "GL_NUM_TILING_TYPES_EXT", + }, + { + 0x9583, "GL_TILING_TYPES_EXT", + }, + { + 0x9584, "GL_OPTIMAL_TILING_EXT", + }, + { + 0x9585, "GL_LINEAR_TILING_EXT", + }, + { + 0x9586, "GL_HANDLE_TYPE_OPAQUE_FD_EXT", + }, + { + 0x9587, "GL_HANDLE_TYPE_OPAQUE_WIN32_EXT", + }, + { + 0x9588, "GL_HANDLE_TYPE_OPAQUE_WIN32_KMT_EXT", + }, + { + 0x9589, "GL_HANDLE_TYPE_D3D12_TILEPOOL_EXT", + }, + { + 0x958A, "GL_HANDLE_TYPE_D3D12_RESOURCE_EXT", + }, + { + 0x958B, "GL_HANDLE_TYPE_D3D11_IMAGE_EXT", + }, + { + 0x958C, "GL_HANDLE_TYPE_D3D11_IMAGE_KMT_EXT", + }, + { + 0x958D, "GL_LAYOUT_GENERAL_EXT", + }, + { + 0x958E, "GL_LAYOUT_COLOR_ATTACHMENT_EXT", + }, + { + 0x958F, "GL_LAYOUT_DEPTH_STENCIL_ATTACHMENT_EXT", + }, + { + 0x9590, "GL_LAYOUT_DEPTH_STENCIL_READ_ONLY_EXT", + }, + { + 0x9591, "GL_LAYOUT_SHADER_READ_ONLY_EXT", + }, + { + 0x9592, "GL_LAYOUT_TRANSFER_SRC_EXT", + }, + { + 0x9593, "GL_LAYOUT_TRANSFER_DST_EXT", + }, + { + 0x9594, "GL_HANDLE_TYPE_D3D12_FENCE_EXT", + }, + { + 0x9595, "GL_D3D12_FENCE_VALUE_EXT", + }, + { + 0x9596, "GL_NUM_DEVICE_UUIDS_EXT", + }, + { + 0x9597, "GL_DEVICE_UUID_EXT", + }, + { + 0x9598, "GL_DRIVER_UUID_EXT", + }, + { + 0x9599, "GL_DEVICE_LUID_EXT", + }, + { + 0x959A, "GL_DEVICE_NODE_MASK_EXT", + }, + { + 0x959B, "GL_PROTECTED_MEMORY_OBJECT_EXT", + }, + { + 0x9630, "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_NUM_VIEWS_OVR", + }, + { + 0x9631, "GL_MAX_VIEWS_OVR", + }, + { + 0x9632, "GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_BASE_VIEW_INDEX_OVR", + }, + { + 0x9633, "GL_FRAMEBUFFER_INCOMPLETE_VIEW_TARGETS_OVR", + }, + { + 0x9650, "GL_MAX_SHADER_COMBINED_LOCAL_STORAGE_FAST_SIZE_EXT", + }, + { + 0x9651, "GL_MAX_SHADER_COMBINED_LOCAL_STORAGE_SIZE_EXT", + }, + { + 0x9652, + "GL_FRAMEBUFFER_INCOMPLETE_INSUFFICIENT_SHADER_COMBINED_LOCAL_STORAGE_" + "EXT", }, { - 0xE9, - "GL_RELATIVE_ROUNDED_RECT_NV", + 0x96A2, "GL_FRAMEBUFFER_FETCH_NONCOHERENT_QCOM", }, { - 0xEA, - "GL_ROUNDED_RECT2_NV", + 0xC0, "GL_SHARED_EDGE_NV", }, { - 0xEB, - "GL_RELATIVE_ROUNDED_RECT2_NV", + 0xE8, "GL_ROUNDED_RECT_NV", }, { - 0xEC, - "GL_ROUNDED_RECT4_NV", + 0xE9, "GL_RELATIVE_ROUNDED_RECT_NV", }, { - 0xED, - "GL_RELATIVE_ROUNDED_RECT4_NV", + 0xEA, "GL_ROUNDED_RECT2_NV", }, { - 0xEE, - "GL_ROUNDED_RECT8_NV", + 0xEB, "GL_RELATIVE_ROUNDED_RECT2_NV", }, { - 0xEF, - "GL_RELATIVE_ROUNDED_RECT8_NV", + 0xEC, "GL_ROUNDED_RECT4_NV", }, { - 0xF0, - "GL_RESTART_PATH_NV", + 0xED, "GL_RELATIVE_ROUNDED_RECT4_NV", }, { - 0xF2, - "GL_DUP_FIRST_CUBIC_CURVE_TO_NV", + 0xEE, "GL_ROUNDED_RECT8_NV", }, { - 0xF4, - "GL_DUP_LAST_CUBIC_CURVE_TO_NV", + 0xEF, "GL_RELATIVE_ROUNDED_RECT8_NV", }, { - 0xF6, - "GL_RECT_NV", + 0xF0, "GL_RESTART_PATH_NV", }, { - 0xF7, - "GL_RELATIVE_RECT_NV", + 0xF2, "GL_DUP_FIRST_CUBIC_CURVE_TO_NV", }, { - 0xF8, - "GL_CIRCULAR_CCW_ARC_TO_NV", + 0xF4, "GL_DUP_LAST_CUBIC_CURVE_TO_NV", }, { - 0xFA, - "GL_CIRCULAR_CW_ARC_TO_NV", + 0xF6, "GL_RECT_NV", }, { - 0xFC, - "GL_CIRCULAR_TANGENT_ARC_TO_NV", + 0xF7, "GL_RELATIVE_RECT_NV", }, { - 0xFE, - "GL_ARC_TO_NV", + 0xF8, "GL_CIRCULAR_CCW_ARC_TO_NV", }, { - 0xFF, - "GL_RELATIVE_ARC_TO_NV", + 0xFA, "GL_CIRCULAR_CW_ARC_TO_NV", }, { - 0xFFFFFFFF, - "GL_ALL_SHADER_BITS_EXT", + 0xFC, "GL_CIRCULAR_TANGENT_ARC_TO_NV", }, { - 1, - "GL_GLES_PROTOTYPES", + 0xFE, "GL_ARC_TO_NV", }, { - 16, - "GL_MAILBOX_SIZE_CHROMIUM", + 0xFF, "GL_RELATIVE_ARC_TO_NV", }, { - 24, - "GL_SYNC_TOKEN_SIZE_CHROMIUM", + 0xFFFFFFFF, "GL_ALL_SHADER_BITS_EXT", }, }; diff --git a/chromium/ui/gl/gl_gl_api_implementation.cc b/chromium/ui/gl/gl_gl_api_implementation.cc index 2313b1f238e..dd97b99a53a 100644 --- a/chromium/ui/gl/gl_gl_api_implementation.cc +++ b/chromium/ui/gl/gl_gl_api_implementation.cc @@ -80,25 +80,6 @@ static inline GLenum GetTexInternalFormat(const GLVersionInfo* version, } } - if (type == GL_FLOAT && version->is_angle && version->is_es && - version->major_version == 2) { - // It's possible that the texture is using a sized internal format, and - // ANGLE exposing GLES2 API doesn't support those. - // TODO(oetuaho@nvidia.com): Remove these conversions once ANGLE has the - // support. - // http://code.google.com/p/angleproject/issues/detail?id=556 - switch (format) { - case GL_RGBA: - gl_internal_format = GL_RGBA; - break; - case GL_RGB: - gl_internal_format = GL_RGB; - break; - default: - break; - } - } - if (version->IsAtLeastGL(2, 1) || version->IsAtLeastGLES(3, 0)) { switch (internal_format) { case GL_SRGB_EXT: diff --git a/chromium/ui/gl/gl_image.cc b/chromium/ui/gl/gl_image.cc index 8525c6c95b9..48b5a04d5aa 100644 --- a/chromium/ui/gl/gl_image.cc +++ b/chromium/ui/gl/gl_image.cc @@ -19,4 +19,21 @@ GLImage::Type GLImage::GetType() const { return Type::NONE; } +#if defined(OS_ANDROID) +std::unique_ptr<GLImage::ScopedHardwareBuffer> GLImage::GetAHardwareBuffer() { + return nullptr; +} + +GLImage::ScopedHardwareBuffer::ScopedHardwareBuffer( + base::android::ScopedHardwareBufferHandle handle, + base::ScopedFD fence_fd) + : handle_(std::move(handle)), fence_fd_(std::move(fence_fd)) {} + +GLImage::ScopedHardwareBuffer::~ScopedHardwareBuffer() = default; + +base::ScopedFD GLImage::ScopedHardwareBuffer::TakeFence() { + return std::move(fence_fd_); +} +#endif + } // namespace gl diff --git a/chromium/ui/gl/gl_image.h b/chromium/ui/gl/gl_image.h index ecf8f011256..640ad8edaf1 100644 --- a/chromium/ui/gl/gl_image.h +++ b/chromium/ui/gl/gl_image.h @@ -11,6 +11,7 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "build/build_config.h" #include "ui/gfx/color_space.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" @@ -21,6 +22,12 @@ #include "ui/gfx/overlay_transform.h" #include "ui/gl/gl_export.h" +#if defined(OS_ANDROID) +#include <android/hardware_buffer.h> +#include "base/android/scoped_hardware_buffer_handle.h" +#include "base/files/scoped_file.h" +#endif + namespace base { namespace trace_event { class ProcessMemoryDump; @@ -104,6 +111,30 @@ class GL_EXPORT GLImage : public base::RefCounted<GLImage> { // removed. https://crbug.com/581777#c36 virtual bool EmulatingRGB() const; +#if defined(OS_ANDROID) + class GL_EXPORT ScopedHardwareBuffer { + public: + ScopedHardwareBuffer(base::android::ScopedHardwareBufferHandle handle, + base::ScopedFD fence_fd); + virtual ~ScopedHardwareBuffer(); + + AHardwareBuffer* buffer() const { return handle_.get(); } + base::ScopedFD TakeFence(); + + private: + base::android::ScopedHardwareBufferHandle handle_; + base::ScopedFD fence_fd_; + }; + + // Provides the buffer backing this image, if it is backed by an + // AHardwareBuffer. The ScopedHardwareBuffer returned may include a fence + // which will be signaled when all pending work for the buffer has been + // finished and it can be safely read from. + // The buffer is guaranteed to be valid until the lifetime of the object + // returned. + virtual std::unique_ptr<ScopedHardwareBuffer> GetAHardwareBuffer(); +#endif + // An identifier for subclasses. Necessary for safe downcasting. enum class Type { NONE, MEMORY, IOSURFACE, DXGI_IMAGE }; virtual Type GetType() const; diff --git a/chromium/ui/gl/gl_image_ahardwarebuffer.cc b/chromium/ui/gl/gl_image_ahardwarebuffer.cc index 273f3acb181..89e97dcf894 100644 --- a/chromium/ui/gl/gl_image_ahardwarebuffer.cc +++ b/chromium/ui/gl/gl_image_ahardwarebuffer.cc @@ -15,6 +15,7 @@ GLImageAHardwareBuffer::~GLImageAHardwareBuffer() {} bool GLImageAHardwareBuffer::Initialize(AHardwareBuffer* buffer, bool preserved) { + handle_ = base::android::ScopedHardwareBufferHandle::Create(buffer); EGLint attribs[] = {EGL_IMAGE_PRESERVED_KHR, preserved ? EGL_TRUE : EGL_FALSE, EGL_NONE}; EGLClientBuffer client_buffer = eglGetNativeClientBufferANDROID(buffer); @@ -54,4 +55,11 @@ void GLImageAHardwareBuffer::OnMemoryDump( uint64_t process_tracing_id, const std::string& dump_name) {} +std::unique_ptr<GLImage::ScopedHardwareBuffer> +GLImageAHardwareBuffer::GetAHardwareBuffer() { + return std::make_unique<ScopedHardwareBuffer>( + base::android::ScopedHardwareBufferHandle::Create(handle_.get()), + base::ScopedFD()); +} + } // namespace gl diff --git a/chromium/ui/gl/gl_image_ahardwarebuffer.h b/chromium/ui/gl/gl_image_ahardwarebuffer.h index a6245263ad6..c2241dbcc7c 100644 --- a/chromium/ui/gl/gl_image_ahardwarebuffer.h +++ b/chromium/ui/gl/gl_image_ahardwarebuffer.h @@ -5,6 +5,7 @@ #ifndef UI_GL_GL_IMAGE_AHARDWAREBUFFER_H_ #define UI_GL_GL_IMAGE_AHARDWAREBUFFER_H_ +#include "base/android/scoped_hardware_buffer_handle.h" #include "base/macros.h" #include "ui/gl/gl_bindings.h" #include "ui/gl/gl_export.h" @@ -37,11 +38,14 @@ class GL_EXPORT GLImageAHardwareBuffer : public GLImageEGL { void OnMemoryDump(base::trace_event::ProcessMemoryDump* pmd, uint64_t process_tracing_id, const std::string& dump_name) override; + std::unique_ptr<ScopedHardwareBuffer> GetAHardwareBuffer() override; protected: ~GLImageAHardwareBuffer() override; private: + base::android::ScopedHardwareBufferHandle handle_; + DISALLOW_COPY_AND_ASSIGN(GLImageAHardwareBuffer); }; diff --git a/chromium/ui/gl/gl_image_io_surface.mm b/chromium/ui/gl/gl_image_io_surface.mm index 2bfff150064..5c84656101d 100644 --- a/chromium/ui/gl/gl_image_io_surface.mm +++ b/chromium/ui/gl/gl_image_io_surface.mm @@ -78,11 +78,6 @@ GLenum TextureFormat(gfx::BufferFormat format) { // OpenGL ES 3.0, for the case) support only GL_RGBA (the hardware ignores // the alpha channel anyway), see https://crbug.com/797347. return GL_RGBA; - case gfx::BufferFormat::ATC: - case gfx::BufferFormat::ATCIA: - case gfx::BufferFormat::DXT1: - case gfx::BufferFormat::DXT5: - case gfx::BufferFormat::ETC1: case gfx::BufferFormat::BGR_565: case gfx::BufferFormat::RGBA_4444: case gfx::BufferFormat::RGBX_8888: @@ -113,11 +108,6 @@ GLenum DataFormat(gfx::BufferFormat format) { return GL_RGBA; case gfx::BufferFormat::UYVY_422: return GL_YCBCR_422_APPLE; - case gfx::BufferFormat::ATC: - case gfx::BufferFormat::ATCIA: - case gfx::BufferFormat::DXT1: - case gfx::BufferFormat::DXT5: - case gfx::BufferFormat::ETC1: case gfx::BufferFormat::BGR_565: case gfx::BufferFormat::RGBA_4444: case gfx::BufferFormat::RGBX_8888: @@ -149,11 +139,6 @@ GLenum DataType(gfx::BufferFormat format) { return GL_HALF_APPLE; case gfx::BufferFormat::UYVY_422: return GL_UNSIGNED_SHORT_8_8_APPLE; - case gfx::BufferFormat::ATC: - case gfx::BufferFormat::ATCIA: - case gfx::BufferFormat::DXT1: - case gfx::BufferFormat::DXT5: - case gfx::BufferFormat::ETC1: case gfx::BufferFormat::BGR_565: case gfx::BufferFormat::RGBA_4444: case gfx::BufferFormat::RGBX_8888: @@ -514,11 +499,6 @@ bool GLImageIOSurface::ValidFormat(gfx::BufferFormat format) { return true; case gfx::BufferFormat::R_16: case gfx::BufferFormat::RG_88: - case gfx::BufferFormat::ATC: - case gfx::BufferFormat::ATCIA: - case gfx::BufferFormat::DXT1: - case gfx::BufferFormat::DXT5: - case gfx::BufferFormat::ETC1: case gfx::BufferFormat::BGR_565: case gfx::BufferFormat::RGBA_4444: case gfx::BufferFormat::RGBX_8888: diff --git a/chromium/ui/gl/gl_image_io_surface_egl.mm b/chromium/ui/gl/gl_image_io_surface_egl.mm index b637ecc3e61..690af44dffe 100644 --- a/chromium/ui/gl/gl_image_io_surface_egl.mm +++ b/chromium/ui/gl/gl_image_io_surface_egl.mm @@ -50,11 +50,6 @@ InternalFormatType BufferFormatToInternalFormatType(gfx::BufferFormat format) { case gfx::BufferFormat::BGRX_1010102: NOTIMPLEMENTED(); return {GL_NONE, GL_NONE}; - case gfx::BufferFormat::ATC: - case gfx::BufferFormat::ATCIA: - case gfx::BufferFormat::DXT1: - case gfx::BufferFormat::DXT5: - case gfx::BufferFormat::ETC1: case gfx::BufferFormat::BGR_565: case gfx::BufferFormat::RGBA_4444: case gfx::BufferFormat::RGBX_8888: diff --git a/chromium/ui/gl/gl_image_memory.cc b/chromium/ui/gl/gl_image_memory.cc index 3b44a540293..26bc08d82cd 100644 --- a/chromium/ui/gl/gl_image_memory.cc +++ b/chromium/ui/gl/gl_image_memory.cc @@ -23,11 +23,6 @@ namespace { bool ValidInternalFormat(unsigned internalformat) { switch (internalformat) { - case GL_ATC_RGB_AMD: - case GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD: - case GL_COMPRESSED_RGB_S3TC_DXT1_EXT: - case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: - case GL_ETC1_RGB8_OES: case GL_RED: case GL_RG: case GL_RGB: @@ -40,50 +35,8 @@ bool ValidInternalFormat(unsigned internalformat) { } } -bool IsCompressedFormat(gfx::BufferFormat format) { - switch (format) { - case gfx::BufferFormat::ATC: - case gfx::BufferFormat::ATCIA: - case gfx::BufferFormat::DXT1: - case gfx::BufferFormat::DXT5: - case gfx::BufferFormat::ETC1: - return true; - case gfx::BufferFormat::R_8: - case gfx::BufferFormat::R_16: - case gfx::BufferFormat::RG_88: - case gfx::BufferFormat::BGR_565: - case gfx::BufferFormat::RGBA_4444: - case gfx::BufferFormat::RGBX_8888: - case gfx::BufferFormat::RGBA_8888: - case gfx::BufferFormat::BGRX_8888: - case gfx::BufferFormat::BGRX_1010102: - case gfx::BufferFormat::RGBX_1010102: - case gfx::BufferFormat::BGRA_8888: - case gfx::BufferFormat::RGBA_F16: - return false; - case gfx::BufferFormat::YVU_420: - case gfx::BufferFormat::YUV_420_BIPLANAR: - case gfx::BufferFormat::UYVY_422: - NOTREACHED(); - return false; - } - - NOTREACHED(); - return false; -} - GLenum TextureFormat(gfx::BufferFormat format) { switch (format) { - case gfx::BufferFormat::ATC: - return GL_ATC_RGB_AMD; - case gfx::BufferFormat::ATCIA: - return GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD; - case gfx::BufferFormat::DXT1: - return GL_COMPRESSED_RGB_S3TC_DXT1_EXT; - case gfx::BufferFormat::DXT5: - return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - case gfx::BufferFormat::ETC1: - return GL_ETC1_RGB8_OES; case gfx::BufferFormat::R_8: return GL_RED; case gfx::BufferFormat::R_16: @@ -132,11 +85,6 @@ GLenum DataFormat(gfx::BufferFormat format) { case gfx::BufferFormat::R_8: case gfx::BufferFormat::R_16: case gfx::BufferFormat::RG_88: - case gfx::BufferFormat::ATC: - case gfx::BufferFormat::ATCIA: - case gfx::BufferFormat::DXT1: - case gfx::BufferFormat::DXT5: - case gfx::BufferFormat::ETC1: return TextureFormat(format); case gfx::BufferFormat::YVU_420: case gfx::BufferFormat::YUV_420_BIPLANAR: @@ -169,11 +117,6 @@ GLenum DataType(gfx::BufferFormat format) { case gfx::BufferFormat::BGRX_1010102: case gfx::BufferFormat::RGBX_1010102: return GL_UNSIGNED_INT_2_10_10_10_REV; - case gfx::BufferFormat::ATC: - case gfx::BufferFormat::ATCIA: - case gfx::BufferFormat::DXT1: - case gfx::BufferFormat::DXT5: - case gfx::BufferFormat::ETC1: case gfx::BufferFormat::YVU_420: case gfx::BufferFormat::YUV_420_BIPLANAR: case gfx::BufferFormat::UYVY_422: @@ -203,11 +146,6 @@ GLint DataRowLength(size_t stride, gfx::BufferFormat format) { return base::checked_cast<GLint>(stride) / 8; case gfx::BufferFormat::R_8: return base::checked_cast<GLint>(stride); - case gfx::BufferFormat::ATC: - case gfx::BufferFormat::ATCIA: - case gfx::BufferFormat::DXT1: - case gfx::BufferFormat::DXT5: - case gfx::BufferFormat::ETC1: case gfx::BufferFormat::YVU_420: case gfx::BufferFormat::YUV_420_BIPLANAR: case gfx::BufferFormat::UYVY_422: @@ -335,12 +273,6 @@ std::unique_ptr<uint8_t[]> GLES2Data(const gfx::Size& size, *data_row_length = size.width(); return gles2_data; } - case gfx::BufferFormat::ATC: - case gfx::BufferFormat::ATCIA: - case gfx::BufferFormat::DXT1: - case gfx::BufferFormat::DXT5: - case gfx::BufferFormat::ETC1: - return nullptr; // No data conversion needed case gfx::BufferFormat::YVU_420: case gfx::BufferFormat::YUV_420_BIPLANAR: case gfx::BufferFormat::UYVY_422: @@ -391,8 +323,6 @@ bool GLImageMemory::Initialize(const unsigned char* memory, DCHECK(memory); DCHECK(!memory_); - DCHECK(!IsCompressedFormat(format) || size_.width() % 4 == 0); - DCHECK(!IsCompressedFormat(format) || size_.height() % 4 == 0); memory_ = memory; format_ = format; stride_ = stride; @@ -419,32 +349,25 @@ bool GLImageMemory::CopyTexImage(unsigned target) { if (target == GL_TEXTURE_EXTERNAL_OES) return false; - if (IsCompressedFormat(format_)) { - glCompressedTexImage2D( - target, 0, TextureFormat(format_), size_.width(), size_.height(), 0, - static_cast<GLsizei>(BufferSizeForBufferFormat(size_, format_)), - memory_); - } else { - GLenum data_format = DataFormat(format_); - GLenum data_type = DataType(format_); - GLint data_row_length = DataRowLength(stride_, format_); - std::unique_ptr<uint8_t[]> gles2_data; - - if (GLContext::GetCurrent()->GetVersionInfo()->is_es) { - gles2_data = GLES2Data(size_, format_, stride_, memory_, &data_format, - &data_type, &data_row_length); - } + GLenum data_format = DataFormat(format_); + GLenum data_type = DataType(format_); + GLint data_row_length = DataRowLength(stride_, format_); + std::unique_ptr<uint8_t[]> gles2_data; - if (data_row_length != size_.width()) - glPixelStorei(GL_UNPACK_ROW_LENGTH, data_row_length); + if (GLContext::GetCurrent()->GetVersionInfo()->is_es) { + gles2_data = GLES2Data(size_, format_, stride_, memory_, &data_format, + &data_type, &data_row_length); + } - glTexImage2D(target, 0, TextureFormat(format_), size_.width(), - size_.height(), 0, data_format, data_type, - gles2_data ? gles2_data.get() : memory_); + if (data_row_length != size_.width()) + glPixelStorei(GL_UNPACK_ROW_LENGTH, data_row_length); - if (data_row_length != size_.width()) - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - } + glTexImage2D(target, 0, TextureFormat(format_), size_.width(), size_.height(), + 0, data_format, data_type, + gles2_data ? gles2_data.get() : memory_); + + if (data_row_length != size_.width()) + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); return true; } @@ -464,37 +387,25 @@ bool GLImageMemory::CopyTexSubImage(unsigned target, return false; const uint8_t* data = memory_ + rect.y() * stride_; - if (IsCompressedFormat(format_)) { - // Height must be a multiple of 4. - if (rect.height() % 4) - return false; - - glCompressedTexSubImage2D( - target, 0, offset.x(), offset.y(), rect.width(), rect.height(), - DataFormat(format_), - static_cast<GLsizei>(BufferSizeForBufferFormat(rect.size(), format_)), - data); - } else { - GLenum data_format = DataFormat(format_); - GLenum data_type = DataType(format_); - GLint data_row_length = DataRowLength(stride_, format_); - std::unique_ptr<uint8_t[]> gles2_data; - - if (GLContext::GetCurrent()->GetVersionInfo()->is_es) { - gles2_data = GLES2Data(rect.size(), format_, stride_, data, &data_format, - &data_type, &data_row_length); - } + GLenum data_format = DataFormat(format_); + GLenum data_type = DataType(format_); + GLint data_row_length = DataRowLength(stride_, format_); + std::unique_ptr<uint8_t[]> gles2_data; + + if (GLContext::GetCurrent()->GetVersionInfo()->is_es) { + gles2_data = GLES2Data(rect.size(), format_, stride_, data, &data_format, + &data_type, &data_row_length); + } - if (data_row_length != rect.width()) - glPixelStorei(GL_UNPACK_ROW_LENGTH, data_row_length); + if (data_row_length != rect.width()) + glPixelStorei(GL_UNPACK_ROW_LENGTH, data_row_length); - glTexSubImage2D(target, 0, offset.x(), offset.y(), rect.width(), - rect.height(), data_format, data_type, - gles2_data ? gles2_data.get() : data); + glTexSubImage2D(target, 0, offset.x(), offset.y(), rect.width(), + rect.height(), data_format, data_type, + gles2_data ? gles2_data.get() : data); - if (data_row_length != rect.width()) - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - } + if (data_row_length != rect.width()) + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); return true; } @@ -523,11 +434,6 @@ unsigned GLImageMemory::GetInternalFormatForTesting(gfx::BufferFormat format) { // static bool GLImageMemory::ValidFormat(gfx::BufferFormat format) { switch (format) { - case gfx::BufferFormat::ATC: - case gfx::BufferFormat::ATCIA: - case gfx::BufferFormat::DXT1: - case gfx::BufferFormat::DXT5: - case gfx::BufferFormat::ETC1: case gfx::BufferFormat::R_8: case gfx::BufferFormat::R_16: case gfx::BufferFormat::RG_88: diff --git a/chromium/ui/gl/gl_image_native_pixmap.cc b/chromium/ui/gl/gl_image_native_pixmap.cc index 13f9c3febb3..9ab046fa7bb 100644 --- a/chromium/ui/gl/gl_image_native_pixmap.cc +++ b/chromium/ui/gl/gl_image_native_pixmap.cc @@ -89,11 +89,6 @@ EGLint FourCC(gfx::BufferFormat format) { return DRM_FORMAT_YVU420; case gfx::BufferFormat::YUV_420_BIPLANAR: return DRM_FORMAT_NV12; - case gfx::BufferFormat::ATC: - case gfx::BufferFormat::ATCIA: - case gfx::BufferFormat::DXT1: - case gfx::BufferFormat::DXT5: - case gfx::BufferFormat::ETC1: case gfx::BufferFormat::RGBA_4444: case gfx::BufferFormat::BGRX_1010102: case gfx::BufferFormat::RGBA_F16: @@ -316,7 +311,7 @@ gfx::NativePixmapHandle GLImageNativePixmap::ExportHandle() { // scoped_fd.release() transfers ownership to the caller so it will not // call close when going out of scope. base::FileDescriptor never closes // the fd when going out of scope. The auto_close flag is just a hint for - // the user. When true it means the user has ownership of it so he is + // the user. When true it means the user has ownership of it so they are // responsible for closing the fd. handle.fds.emplace_back( base::FileDescriptor(scoped_fd.release(), true /* auto_close */)); @@ -412,11 +407,6 @@ unsigned GLImageNativePixmap::GetInternalFormatForTesting( return GL_RGB_YCRCB_420_CHROMIUM; case gfx::BufferFormat::YUV_420_BIPLANAR: return GL_RGB_YCBCR_420V_CHROMIUM; - case gfx::BufferFormat::ATC: - case gfx::BufferFormat::ATCIA: - case gfx::BufferFormat::DXT1: - case gfx::BufferFormat::DXT5: - case gfx::BufferFormat::ETC1: case gfx::BufferFormat::RGBA_4444: case gfx::BufferFormat::BGRX_1010102: case gfx::BufferFormat::RGBA_F16: @@ -444,11 +434,6 @@ bool GLImageNativePixmap::ValidFormat(gfx::BufferFormat format) { case gfx::BufferFormat::YVU_420: case gfx::BufferFormat::YUV_420_BIPLANAR: return true; - case gfx::BufferFormat::ATC: - case gfx::BufferFormat::ATCIA: - case gfx::BufferFormat::DXT1: - case gfx::BufferFormat::DXT5: - case gfx::BufferFormat::ETC1: case gfx::BufferFormat::RGBA_4444: case gfx::BufferFormat::BGRX_1010102: case gfx::BufferFormat::RGBA_F16: diff --git a/chromium/ui/gl/gl_image_native_pixmap_unittest.cc b/chromium/ui/gl/gl_image_native_pixmap_unittest.cc index 9cc799c509c..49ed7027f17 100644 --- a/chromium/ui/gl/gl_image_native_pixmap_unittest.cc +++ b/chromium/ui/gl/gl_image_native_pixmap_unittest.cc @@ -107,6 +107,8 @@ using GLImageTestTypes = testing::Types< GLImageNativePixmapTestDelegate<gfx::BufferFormat::BGRX_8888>, GLImageNativePixmapTestDelegate<gfx::BufferFormat::BGRA_8888>>; +#if !defined(MEMORY_SANITIZER) +// Fails under MSAN: crbug.com/886995 INSTANTIATE_TYPED_TEST_CASE_P(GLImageNativePixmap, GLImageTest, GLImageTestTypes); @@ -118,6 +120,7 @@ INSTANTIATE_TYPED_TEST_CASE_P(GLImageNativePixmap, INSTANTIATE_TYPED_TEST_CASE_P(GLImageNativePixmap, GLImageNativePixmapToDmabufTest, GLImageTestTypes); +#endif } // namespace diff --git a/chromium/ui/gl/gl_mock_autogen_egl.h b/chromium/ui/gl/gl_mock_autogen_egl.h index 4718f66c85b..fac8c3197e1 100644 --- a/chromium/ui/gl/gl_mock_autogen_egl.h +++ b/chromium/ui/gl/gl_mock_autogen_egl.h @@ -167,23 +167,6 @@ MOCK_METHOD6(PostSubBufferNV, EGLint y, EGLint width, EGLint height)); -MOCK_METHOD2(ProgramCacheGetAttribANGLE, - EGLint(EGLDisplay dpy, EGLenum attrib)); -MOCK_METHOD5(ProgramCachePopulateANGLE, - void(EGLDisplay dpy, - const void* key, - EGLint keysize, - const void* binary, - EGLint binarysize)); -MOCK_METHOD6(ProgramCacheQueryANGLE, - void(EGLDisplay dpy, - EGLint index, - void* key, - EGLint* keysize, - void* binary, - EGLint* binarysize)); -MOCK_METHOD3(ProgramCacheResizeANGLE, - EGLint(EGLDisplay dpy, EGLint limit, EGLenum mode)); MOCK_METHOD0(QueryAPI, EGLenum()); MOCK_METHOD4(QueryContext, EGLBoolean(EGLDisplay dpy, @@ -215,6 +198,10 @@ MOCK_METHOD4(QuerySurfacePointerANGLE, MOCK_METHOD3(ReleaseTexImage, EGLBoolean(EGLDisplay dpy, EGLSurface surface, EGLint buffer)); MOCK_METHOD0(ReleaseThread, EGLBoolean()); +MOCK_METHOD3(SetBlobCacheFuncsANDROID, + void(EGLDisplay dpy, + EGLSetBlobFuncANDROID set, + EGLGetBlobFuncANDROID get)); MOCK_METHOD4(StreamAttribKHR, EGLBoolean(EGLDisplay dpy, EGLStreamKHR stream, diff --git a/chromium/ui/gl/gl_mock_autogen_gl.h b/chromium/ui/gl/gl_mock_autogen_gl.h index f5a8375436e..37c95a26582 100644 --- a/chromium/ui/gl/gl_mock_autogen_gl.h +++ b/chromium/ui/gl/gl_mock_autogen_gl.h @@ -103,8 +103,6 @@ MOCK_METHOD4( ColorMask, void(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)); MOCK_METHOD1(CompileShader, void(GLuint shader)); -MOCK_METHOD2(CompressedCopyTextureCHROMIUM, - void(GLuint sourceId, GLuint destId)); MOCK_METHOD8(CompressedTexImage2D, void(GLenum target, GLint level, @@ -870,6 +868,7 @@ MOCK_METHOD4(MapBufferRange, GLbitfield access)); MOCK_METHOD2(MatrixLoadfEXT, void(GLenum matrixMode, const GLfloat* m)); MOCK_METHOD1(MatrixLoadIdentityEXT, void(GLenum matrixMode)); +MOCK_METHOD1(MaxShaderCompilerThreadsKHR, void(GLuint count)); MOCK_METHOD1(MemoryBarrierByRegion, void(GLbitfield barriers)); MOCK_METHOD1(MemoryBarrierEXT, void(GLbitfield barriers)); MOCK_METHOD1(MinSampleShading, void(GLfloat value)); diff --git a/chromium/ui/gl/gl_share_group.cc b/chromium/ui/gl/gl_share_group.cc index 94708c09202..e9386c4b03d 100644 --- a/chromium/ui/gl/gl_share_group.cc +++ b/chromium/ui/gl/gl_share_group.cc @@ -42,9 +42,7 @@ void* GLShareGroup::GetHandle() { } GLContext* GLShareGroup::GetContext() { - for (ContextSet::iterator it = contexts_.begin(); - it != contexts_.end(); - ++it) { + for (auto it = contexts_.begin(); it != contexts_.end(); ++it) { if ((*it)->GetHandle()) return *it; } diff --git a/chromium/ui/gl/gl_stub_autogen_gl.h b/chromium/ui/gl/gl_stub_autogen_gl.h index a2b558328c0..3f30f249b80 100644 --- a/chromium/ui/gl/gl_stub_autogen_gl.h +++ b/chromium/ui/gl/gl_stub_autogen_gl.h @@ -118,8 +118,6 @@ void glColorMaskFn(GLboolean red, GLboolean blue, GLboolean alpha) override {} void glCompileShaderFn(GLuint shader) override {} -void glCompressedCopyTextureCHROMIUMFn(GLuint sourceId, - GLuint destId) override {} void glCompressedTexImage2DFn(GLenum target, GLint level, GLenum internalformat, @@ -878,6 +876,7 @@ void* glMapBufferRangeFn(GLenum target, GLbitfield access) override; void glMatrixLoadfEXTFn(GLenum matrixMode, const GLfloat* m) override {} void glMatrixLoadIdentityEXTFn(GLenum matrixMode) override {} +void glMaxShaderCompilerThreadsKHRFn(GLuint count) override {} void glMemoryBarrierByRegionFn(GLbitfield barriers) override {} void glMemoryBarrierEXTFn(GLbitfield barriers) override {} void glMinSampleShadingFn(GLfloat value) override {} diff --git a/chromium/ui/gl/gl_surface_egl.cc b/chromium/ui/gl/gl_surface_egl.cc index 00cdf20fd2b..bb5e68bc4ff 100644 --- a/chromium/ui/gl/gl_surface_egl.cc +++ b/chromium/ui/gl/gl_surface_egl.cc @@ -16,6 +16,7 @@ #include "base/logging.h" #include "base/macros.h" #include "base/metrics/histogram_macros.h" +#include "base/stl_util.h" #include "base/strings/string_number_conversions.h" #include "base/sys_info.h" #include "base/trace_event/trace_event.h" @@ -492,10 +493,8 @@ EGLConfig ChooseConfig(GLSurfaceFormat format, bool surfaceless) { void AddInitDisplay(std::vector<DisplayType>* init_displays, DisplayType display_type) { // Make sure to not add the same display type twice. - if (std::find(init_displays->begin(), init_displays->end(), display_type) == - init_displays->end()) { + if (!base::ContainsValue(*init_displays, display_type)) init_displays->push_back(display_type); - } } const char* GetDebugMessageTypeString(EGLint source) { @@ -1484,9 +1483,14 @@ bool NativeViewGLSurfaceEGL::GetFrameTimestampInfoIfAvailable( if (presentation_time_ns == EGL_TIMESTAMP_PENDING_ANDROID) { return false; } - *presentation_time = base::TimeTicks() + - base::TimeDelta::FromNanoseconds(presentation_time_ns); - *presentation_flags = presentation_flags_; + if (presentation_time_ns == EGL_TIMESTAMP_INVALID_ANDROID) { + *presentation_time = base::TimeTicks::Now(); + } else { + *presentation_time = base::TimeTicks() + + base::TimeDelta::FromNanoseconds(presentation_time_ns); + *presentation_flags = presentation_flags_; + } + DCHECK(!presentation_time->is_null()); return true; } diff --git a/chromium/ui/gl/gl_surface_egl_surface_control.cc b/chromium/ui/gl/gl_surface_egl_surface_control.cc new file mode 100644 index 00000000000..4a7cbb99290 --- /dev/null +++ b/chromium/ui/gl/gl_surface_egl_surface_control.cc @@ -0,0 +1,250 @@ +// 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/gl/gl_surface_egl_surface_control.h" + +#include "base/threading/thread_task_runner_handle.h" +#include "ui/gfx/geometry/rect_conversions.h" +#include "ui/gl/gl_fence_android_native_fence_sync.h" +#include "ui/gl/gl_image_ahardwarebuffer.h" + +namespace gl { +namespace { + +constexpr char kSurfaceName[] = "ChromeSurface"; + +} // namespace + +GLSurfaceEGLSurfaceControl::GLSurfaceEGLSurfaceControl(ANativeWindow* window) { + surface_composer_ = SurfaceComposer::Create(window); +} + +GLSurfaceEGLSurfaceControl::~GLSurfaceEGLSurfaceControl() = default; + +bool GLSurfaceEGLSurfaceControl::Initialize(GLSurfaceFormat format) { + format_ = format; + return true; +} + +void GLSurfaceEGLSurfaceControl::Destroy() { + pending_transaction_.reset(); + surface_list_.clear(); + surface_composer_.reset(); +} + +bool GLSurfaceEGLSurfaceControl::Resize(const gfx::Size& size, + float scale_factor, + ColorSpace color_space, + bool has_alpha) { + // Resizing requires resizing the SurfaceView in the browser. + return true; +} + +bool GLSurfaceEGLSurfaceControl::IsOffscreen() { + return false; +} + +gfx::SwapResult GLSurfaceEGLSurfaceControl::SwapBuffers( + const PresentationCallback& callback) { + NOTREACHED(); + return gfx::SwapResult::SWAP_FAILED; +} + +void GLSurfaceEGLSurfaceControl::SwapBuffersAsync( + const SwapCompletionCallback& completion_callback, + const PresentationCallback& presentation_callback) { + CommitPendingTransaction(completion_callback, presentation_callback); +} + +gfx::SwapResult GLSurfaceEGLSurfaceControl::CommitOverlayPlanes( + const PresentationCallback& callback) { + NOTREACHED(); + return gfx::SwapResult::SWAP_FAILED; +} + +void GLSurfaceEGLSurfaceControl::CommitOverlayPlanesAsync( + const SwapCompletionCallback& completion_callback, + const PresentationCallback& presentation_callback) { + CommitPendingTransaction(completion_callback, presentation_callback); +} + +void GLSurfaceEGLSurfaceControl::CommitPendingTransaction( + const SwapCompletionCallback& completion_callback, + const PresentationCallback& present_callback) { + DCHECK(pending_transaction_); + + // Release resources for the current frame once the next frame is acked. + ResourceRefs resources_to_release; + resources_to_release.swap(current_frame_resources_); + current_frame_resources_.clear(); + + // Track resources to be owned by the framework after this transaction. + current_frame_resources_.swap(pending_frame_resources_); + pending_frame_resources_.clear(); + + pending_transaction_->Apply(); + pending_transaction_.reset(); + + DCHECK_GE(surface_list_.size(), pending_surfaces_count_); + surface_list_.resize(pending_surfaces_count_); + pending_surfaces_count_ = 0u; + + // TODO(khushalsagar): Send the legit timestamp when hooking up transaction + // acks. + constexpr int64_t kRefreshIntervalInMicroseconds = + base::Time::kMicrosecondsPerSecond / 60; + gfx::PresentationFeedback feedback( + base::TimeTicks::Now(), + base::TimeDelta::FromMicroseconds(kRefreshIntervalInMicroseconds), + 0 /* flags */); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(OnTransactionAck, feedback, present_callback, + completion_callback, std::move(resources_to_release))); +} + +gfx::Size GLSurfaceEGLSurfaceControl::GetSize() { + return gfx::Size(0, 0); +} + +bool GLSurfaceEGLSurfaceControl::OnMakeCurrent(GLContext* context) { + return true; +} + +bool GLSurfaceEGLSurfaceControl::ScheduleOverlayPlane( + int z_order, + gfx::OverlayTransform transform, + GLImage* image, + const gfx::Rect& bounds_rect, + const gfx::RectF& crop_rect, + bool enable_blend, + std::unique_ptr<gfx::GpuFence> gpu_fence) { + if (!pending_transaction_) + pending_transaction_.emplace(); + + bool uninitialized = false; + if (pending_surfaces_count_ == surface_list_.size()) { + uninitialized = true; + surface_list_.emplace_back(surface_composer_.get()); + } + pending_surfaces_count_++; + auto& surface_state = surface_list_.at(pending_surfaces_count_ - 1); + + if (uninitialized || surface_state.z_order != z_order) { + surface_state.z_order = z_order; + pending_transaction_->SetZOrder(surface_state.surface, z_order); + } + + if (uninitialized || surface_state.transform != transform) { + surface_state.transform = transform; + // TODO(khushalsagar): Forward the transform once the NDK API is in place. + } + + AHardwareBuffer* hardware_buffer = nullptr; + base::ScopedFD fence_fd; + auto scoped_hardware_buffer = image->GetAHardwareBuffer(); + if (scoped_hardware_buffer) { + hardware_buffer = scoped_hardware_buffer->buffer(); + fence_fd = scoped_hardware_buffer->TakeFence(); + pending_frame_resources_.push_back(std::move(scoped_hardware_buffer)); + } + + if (uninitialized || surface_state.hardware_buffer != hardware_buffer) { + surface_state.hardware_buffer = hardware_buffer; + + if (!fence_fd.is_valid() && gpu_fence && surface_state.hardware_buffer) { + auto fence_handle = + gfx::CloneHandleForIPC(gpu_fence->GetGpuFenceHandle()); + DCHECK(!fence_handle.is_null()); + fence_fd = base::ScopedFD(fence_handle.native_fd.fd); + } + + pending_transaction_->SetBuffer(surface_state.surface, + surface_state.hardware_buffer, + std::move(fence_fd)); + } + + if (uninitialized || surface_state.bounds_rect != bounds_rect) { + surface_state.bounds_rect = bounds_rect; + pending_transaction_->SetPosition(surface_state.surface, bounds_rect.x(), + bounds_rect.y()); + pending_transaction_->SetSize(surface_state.surface, bounds_rect.width(), + bounds_rect.height()); + } + + // TODO(khushalsagar): Currently the framework refuses to the draw the buffer + // if the crop rect doesn't exactly match the buffer size. Update when fixed. + /*gfx::Rect enclosed_crop_rect = gfx::ToEnclosedRect(crop_rect); + if (uninitialized || surface_state.crop_rect != enclosed_crop_rect) { + surface_state.crop_rect = enclosed_crop_rect; + pending_transaction_->SetCropRect( + surface_state.surface, enclosed_crop_rect.x(), enclosed_crop_rect.y(), + enclosed_crop_rect.right(), enclosed_crop_rect.bottom()); + }*/ + + bool opaque = !enable_blend; + if (uninitialized || surface_state.opaque != opaque) { + surface_state.opaque = opaque; + pending_transaction_->SetOpaque(surface_state.surface, opaque); + } + + return true; +} + +bool GLSurfaceEGLSurfaceControl::IsSurfaceless() const { + return true; +} + +void* GLSurfaceEGLSurfaceControl::GetHandle() { + return nullptr; +} + +bool GLSurfaceEGLSurfaceControl::SupportsAsyncSwap() { + return true; +} + +bool GLSurfaceEGLSurfaceControl::SupportsPlaneGpuFences() const { + return true; +} + +bool GLSurfaceEGLSurfaceControl::SupportsPresentationCallback() { + return true; +} + +bool GLSurfaceEGLSurfaceControl::SupportsSwapBuffersWithBounds() { + // TODO(khushalsagar): Add support for partial swap. + return false; +} + +bool GLSurfaceEGLSurfaceControl::SupportsCommitOverlayPlanes() { + return true; +} + +// static +void GLSurfaceEGLSurfaceControl::OnTransactionAck( + const gfx::PresentationFeedback& feedback, + const PresentationCallback& present_callback, + const SwapCompletionCallback& completion_callback, + ResourceRefs resources) { + completion_callback.Run(gfx::SwapResult::SWAP_ACK, nullptr); + present_callback.Run(feedback); + resources.clear(); +} + +GLSurfaceEGLSurfaceControl::SurfaceState::SurfaceState( + SurfaceComposer* composer) + : surface(composer, + SurfaceComposer::SurfaceContentType::kAHardwareBuffer, + kSurfaceName) {} + +GLSurfaceEGLSurfaceControl::SurfaceState::SurfaceState() = default; +GLSurfaceEGLSurfaceControl::SurfaceState::SurfaceState(SurfaceState&& other) = + default; +GLSurfaceEGLSurfaceControl::SurfaceState& +GLSurfaceEGLSurfaceControl::SurfaceState::operator=(SurfaceState&& other) = + default; + +GLSurfaceEGLSurfaceControl::SurfaceState::~SurfaceState() = default; + +} // namespace gl diff --git a/chromium/ui/gl/gl_surface_egl_surface_control.h b/chromium/ui/gl/gl_surface_egl_surface_control.h new file mode 100644 index 00000000000..dcfea68b7e7 --- /dev/null +++ b/chromium/ui/gl/gl_surface_egl_surface_control.h @@ -0,0 +1,121 @@ +// 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_GL_GL_SURFACE_EGL_SURFACE_CONTROL_H_ +#define UI_GL_GL_SURFACE_EGL_SURFACE_CONTROL_H_ + +#include <android/native_window.h> + +#include "base/android/scoped_hardware_buffer_handle.h" +#include "base/memory/weak_ptr.h" +#include "base/optional.h" +#include "ui/gl/android/android_surface_composer_compat.h" +#include "ui/gl/gl_export.h" +#include "ui/gl/gl_surface_egl.h" + +namespace gl { + +class GL_EXPORT GLSurfaceEGLSurfaceControl : public gl::GLSurfaceEGL { + public: + explicit GLSurfaceEGLSurfaceControl(ANativeWindow* window); + + // GLSurface implementation. + bool Initialize(gl::GLSurfaceFormat format) override; + void Destroy() override; + bool Resize(const gfx::Size& size, + float scale_factor, + ColorSpace color_space, + bool has_alpha) override; + bool IsOffscreen() override; + gfx::SwapResult SwapBuffers(const PresentationCallback& callback) override; + void SwapBuffersAsync( + const SwapCompletionCallback& completion_callback, + const PresentationCallback& presentation_callback) override; + gfx::SwapResult CommitOverlayPlanes( + const PresentationCallback& callback) override; + void CommitOverlayPlanesAsync( + const SwapCompletionCallback& completion_callback, + const PresentationCallback& presentation_callback) override; + gfx::Size GetSize() override; + bool OnMakeCurrent(gl::GLContext* context) override; + bool ScheduleOverlayPlane(int z_order, + gfx::OverlayTransform transform, + gl::GLImage* image, + const gfx::Rect& bounds_rect, + const gfx::RectF& crop_rect, + bool enable_blend, + std::unique_ptr<gfx::GpuFence> gpu_fence) override; + bool IsSurfaceless() const override; + void* GetHandle() override; + + bool SupportsAsyncSwap() override; + bool SupportsPlaneGpuFences() const override; + bool SupportsPresentationCallback() override; + bool SupportsSwapBuffersWithBounds() override; + bool SupportsCommitOverlayPlanes() override; + + private: + ~GLSurfaceEGLSurfaceControl() override; + + struct SurfaceState { + SurfaceState(); + explicit SurfaceState(gl::SurfaceComposer* composer); + ~SurfaceState(); + + SurfaceState(SurfaceState&& other); + SurfaceState& operator=(SurfaceState&& other); + + int z_order = 0; + gfx::OverlayTransform transform = gfx::OVERLAY_TRANSFORM_INVALID; + AHardwareBuffer* hardware_buffer = nullptr; + gfx::Rect bounds_rect; + gfx::Rect crop_rect; + bool opaque = true; + + gl::SurfaceComposer::Surface surface; + }; + + using ResourceRefs = + std::vector<std::unique_ptr<GLImage::ScopedHardwareBuffer>>; + + void CommitPendingTransaction( + const SwapCompletionCallback& completion_callback, + const PresentationCallback& callback); + + static void OnTransactionAck( + const gfx::PresentationFeedback& feedback, + const PresentationCallback& present_callback, + const SwapCompletionCallback& completion_callback, + ResourceRefs resources); + + // Holds the surface state changes made since the last call to SwapBuffers. + base::Optional<gl::SurfaceComposer::Transaction> pending_transaction_; + + // The list of Surfaces and the corresponding state. The initial + // |pending_surfaces_count_| surfaces in this list are surfaces with state + // mutated since the last SwapBuffers with the updates collected in + // |pending_transaction_|. + // On the next SwapBuffers, the updates in the transaction are applied + // atomically and any surfaces in |surface_list_| which are not reused in this + // frame are destroyed. + std::vector<SurfaceState> surface_list_; + size_t pending_surfaces_count_ = 0u; + + // Resources in the pending frame, for which updates are being + // collected in |pending_transaction_|. These are resources for which the + // pending transaction has a ref but they have not been applied and + // transferred to the framework. + ResourceRefs pending_frame_resources_; + + // Resources in the current frame sent to the framework. The + // framework is assumed to retain ownership of these resources until the next + // frame update. + ResourceRefs current_frame_resources_; + + std::unique_ptr<gl::SurfaceComposer> surface_composer_; +}; + +} // namespace gl + +#endif // UI_GL_GL_SURFACE_EGL_SURFACE_CONTROL_H_ diff --git a/chromium/ui/gl/gl_surface_egl_unittest.cc b/chromium/ui/gl/gl_surface_egl_unittest.cc index b8788771500..b397b43705f 100644 --- a/chromium/ui/gl/gl_surface_egl_unittest.cc +++ b/chromium/ui/gl/gl_surface_egl_unittest.cc @@ -23,6 +23,8 @@ namespace { class GLSurfaceEGLTest : public testing::Test {}; +#if !defined(MEMORY_SANITIZER) +// Fails under MSAN: crbug.com/886995 TEST(GLSurfaceEGLTest, SurfaceFormatTest) { GLSurfaceTestSupport::InitializeOneOffImplementation( GLImplementation::kGLImplementationEGLGLES2, true); @@ -46,6 +48,7 @@ TEST(GLSurfaceEGLTest, SurfaceFormatTest) { eglGetConfigAttrib(surface->GetDisplay(), config, EGL_SAMPLES, &attrib); EXPECT_EQ(0, attrib); } +#endif #if defined(OS_WIN) diff --git a/chromium/ui/gl/gpu_timing_fake.cc b/chromium/ui/gl/gpu_timing_fake.cc index 4a3fc2ee571..f446617799f 100644 --- a/chromium/ui/gl/gpu_timing_fake.cc +++ b/chromium/ui/gl/gpu_timing_fake.cc @@ -223,7 +223,7 @@ void GPUTimingFake::FakeGLGetQueryObjectuiv(GLuint id, GLenum pname, GLuint* params) { switch (pname) { case GL_QUERY_RESULT_AVAILABLE: { - std::map<GLuint, QueryResult>::iterator it = query_results_.find(id); + auto it = query_results_.find(id); if (it != query_results_.end() && it->second.value_ <= current_gl_time_) *params = 1; else @@ -262,7 +262,7 @@ void GPUTimingFake::FakeGLGetQueryObjectui64v(GLuint id, GLenum pname, GLuint64* params) { switch (pname) { case GL_QUERY_RESULT: { - std::map<GLuint, QueryResult>::iterator it = query_results_.find(id); + auto it = query_results_.find(id); ASSERT_TRUE(it != query_results_.end()); switch (it->second.type_) { case QueryResult::kQueryResultType_TimeStamp: diff --git a/chromium/ui/gl/init/BUILD.gn b/chromium/ui/gl/init/BUILD.gn index d708e6c468b..b66913884f7 100644 --- a/chromium/ui/gl/init/BUILD.gn +++ b/chromium/ui/gl/init/BUILD.gn @@ -4,6 +4,7 @@ import("//build/config/jumbo.gni") import("//build/config/ui.gni") +import("//ui/gl/features.gni") jumbo_component("init") { output_name = "gl_init" @@ -38,6 +39,10 @@ jumbo_component("init") { "gl_factory_android.cc", "gl_initializer_android.cc", ] + + if (use_static_angle) { + deps += [ "//third_party/angle:libEGL_static" ] + } } else if (is_win && !use_ozone) { sources += [ "gl_factory_win.cc", diff --git a/chromium/ui/gl/init/create_gr_gl_interface.cc b/chromium/ui/gl/init/create_gr_gl_interface.cc index 005e6eb758c..598b98cdfd9 100644 --- a/chromium/ui/gl/init/create_gr_gl_interface.cc +++ b/chromium/ui/gl/init/create_gr_gl_interface.cc @@ -5,6 +5,7 @@ #include "ui/gl/init/create_gr_gl_interface.h" #include "ui/gl/gl_bindings.h" #include "ui/gl/gl_version_info.h" +#include "ui/gl/progress_reporter.h" namespace gl { namespace init { @@ -12,11 +13,33 @@ namespace init { namespace { template <typename R, typename... Args> -GrGLFunction<R (*)(Args...)> bind(R (gl::GLApi::*func)(Args...), - gl::GLApi* api) { +GrGLFunction<R GR_GL_FUNCTION_TYPE(Args...)> bind(R (gl::GLApi::*func)(Args...), + gl::GLApi* api) { return [func, api](Args... args) { return (api->*func)(args...); }; } +class ScopedProgressReporter { + public: + ScopedProgressReporter(gl::ProgressReporter* progress_reporter) + : progress_reporter_(progress_reporter) {} + ~ScopedProgressReporter() { progress_reporter_->ReportProgress(); } + + private: + gl::ProgressReporter* progress_reporter_; +}; + +template <typename R, typename... Args> +GrGLFunction<R GR_GL_FUNCTION_TYPE(Args...)> bind_slow( + R(GL_BINDING_CALL* func)(Args...), + gl::ProgressReporter* progress_reporter) { + if (!progress_reporter) + return func; + return [func, progress_reporter](Args... args) { + ScopedProgressReporter scoped_reporter(progress_reporter); + return func(args...); + }; +}; + const GLubyte* GetStringHook(const char* version_string, GLenum name) { switch (name) { case GL_VERSION: @@ -47,7 +70,8 @@ const char* kBlacklistExtensions[] = { sk_sp<GrGLInterface> CreateGrGLInterface( const gl::GLVersionInfo& version_info, - bool use_version_es2) { + bool use_version_es2, + gl::ProgressReporter* progress_reporter) { // Can't fake ES with desktop GL. use_version_es2 &= version_info.is_es; @@ -63,7 +87,7 @@ sk_sp<GrGLInterface> CreateGrGLInterface( // by the bindings (GL 4.1 or ES 3.0), and blacklist extensions that skia // handles but bindings don't. // TODO(piman): add bindings for missing entrypoints. - GrGLFunction<GrGLGetStringProc> get_string; + GrGLFunction<GrGLGetStringFn> get_string; const bool apply_version_override = use_version_es2 || version_info.IsAtLeastGL(4, 2) || version_info.IsAtLeastGLES(3, 1); @@ -119,17 +143,23 @@ sk_sp<GrGLInterface> CreateGrGLInterface( // functions->fClearTexSubImage = nullptr; functions->fColorMask = gl->glColorMaskFn; - functions->fCompileShader = gl->glCompileShaderFn; - functions->fCompressedTexImage2D = gl->glCompressedTexImage2DFn; - functions->fCompressedTexSubImage2D = gl->glCompressedTexSubImage2DFn; - functions->fCopyTexSubImage2D = gl->glCopyTexSubImage2DFn; + functions->fCompileShader = + bind_slow(gl->glCompileShaderFn, progress_reporter); + functions->fCompressedTexImage2D = + bind_slow(gl->glCompressedTexImage2DFn, progress_reporter); + functions->fCompressedTexSubImage2D = + bind_slow(gl->glCompressedTexSubImage2DFn, progress_reporter); + functions->fCopyTexSubImage2D = + bind_slow(gl->glCopyTexSubImage2DFn, progress_reporter); functions->fCreateProgram = gl->glCreateProgramFn; functions->fCreateShader = gl->glCreateShaderFn; functions->fCullFace = gl->glCullFaceFn; - functions->fDeleteBuffers = gl->glDeleteBuffersARBFn; - functions->fDeleteProgram = gl->glDeleteProgramFn; + functions->fDeleteBuffers = + bind_slow(gl->glDeleteBuffersARBFn, progress_reporter); + functions->fDeleteProgram = + bind_slow(gl->glDeleteProgramFn, progress_reporter); functions->fDeleteQueries = gl->glDeleteQueriesFn; - functions->fDeleteShader = gl->glDeleteShaderFn; + functions->fDeleteShader = bind_slow(gl->glDeleteShaderFn, progress_reporter); functions->fDeleteTextures = gl->glDeleteTexturesFn; functions->fDepthMask = gl->glDepthMaskFn; functions->fDisable = gl->glDisableFn; @@ -151,8 +181,8 @@ sk_sp<GrGLInterface> CreateGrGLInterface( functions->fEnable = gl->glEnableFn; functions->fEnableVertexAttribArray = gl->glEnableVertexAttribArrayFn; functions->fEndQuery = gl->glEndQueryFn; - functions->fFinish = gl->glFinishFn; - functions->fFlush = gl->glFlushFn; + functions->fFinish = bind_slow(gl->glFinishFn, progress_reporter); + functions->fFlush = bind_slow(gl->glFlushFn, progress_reporter); functions->fFrontFace = gl->glFrontFaceFn; functions->fGenBuffers = gl->glGenBuffersARBFn; functions->fGetBufferParameteriv = gl->glGetBufferParameterivFn; @@ -179,7 +209,7 @@ sk_sp<GrGLInterface> CreateGrGLInterface( functions->fGetUniformLocation = gl->glGetUniformLocationFn; functions->fIsTexture = gl->glIsTextureFn; functions->fLineWidth = gl->glLineWidthFn; - functions->fLinkProgram = gl->glLinkProgramFn; + functions->fLinkProgram = bind_slow(gl->glLinkProgramFn, progress_reporter); functions->fMapBuffer = gl->glMapBufferFn; // GL 4.3 or GL_ARB_multi_draw_indirect or ES+GL_EXT_multi_draw_indirect @@ -206,7 +236,7 @@ sk_sp<GrGLInterface> CreateGrGLInterface( functions->fStencilOpSeparate = gl->glStencilOpSeparateFn; functions->fTexBuffer = gl->glTexBufferFn; functions->fTexBufferRange = gl->glTexBufferRangeFn; - functions->fTexImage2D = gl->glTexImage2DFn; + functions->fTexImage2D = bind_slow(gl->glTexImage2DFn, progress_reporter); functions->fTexParameteri = gl->glTexParameteriFn; functions->fTexParameteriv = gl->glTexParameterivFn; functions->fTexStorage2D = gl->glTexStorage2DEXTFn; @@ -266,7 +296,8 @@ sk_sp<GrGLInterface> CreateGrGLInterface( functions->fBindFramebuffer = gl->glBindFramebufferEXTFn; functions->fFramebufferTexture2D = gl->glFramebufferTexture2DEXTFn; functions->fCheckFramebufferStatus = gl->glCheckFramebufferStatusEXTFn; - functions->fDeleteFramebuffers = gl->glDeleteFramebuffersEXTFn; + functions->fDeleteFramebuffers = + bind_slow(gl->glDeleteFramebuffersEXTFn, progress_reporter); functions->fRenderbufferStorage = gl->glRenderbufferStorageEXTFn; functions->fGenRenderbuffers = gl->glGenRenderbuffersEXTFn; functions->fDeleteRenderbuffers = gl->glDeleteRenderbuffersEXTFn; diff --git a/chromium/ui/gl/init/create_gr_gl_interface.h b/chromium/ui/gl/init/create_gr_gl_interface.h index 4b23ee7fe90..9243fbea018 100644 --- a/chromium/ui/gl/init/create_gr_gl_interface.h +++ b/chromium/ui/gl/init/create_gr_gl_interface.h @@ -11,6 +11,7 @@ namespace gl { struct GLVersionInfo; +class ProgressReporter; } namespace gl { @@ -20,7 +21,8 @@ namespace init { // GL bindings. GL_INIT_EXPORT sk_sp<GrGLInterface> CreateGrGLInterface( const gl::GLVersionInfo& version_info, - bool use_version_es2); + bool use_version_es2, + gl::ProgressReporter* progress_reporter = nullptr); } // namespace init } // namespace gl diff --git a/chromium/ui/gl/init/gl_initializer_android.cc b/chromium/ui/gl/init/gl_initializer_android.cc index 46ec1c7e0a9..22660c9207d 100644 --- a/chromium/ui/gl/init/gl_initializer_android.cc +++ b/chromium/ui/gl/init/gl_initializer_android.cc @@ -24,13 +24,17 @@ namespace init { namespace { -bool InitializeStaticEGLInternal() { #if BUILDFLAG(USE_STATIC_ANGLE) +bool InitializeStaticANGLEEGLInternal() { #pragma push_macro("eglGetProcAddress") #undef eglGetProcAddress SetGLGetProcAddressProc(&eglGetProcAddress); #pragma pop_macro("eglGetProcAddress") -#else // BUILDFLAG(USE_STATIC_ANGLE) + return true; +} +#endif // BUILDFLAG(USE_STATIC_ANGLE) + +bool InitializeStaticNativeEGLInternal() { base::NativeLibrary gles_library = LoadLibraryAndPrintError("libGLESv2.so"); if (!gles_library) return false; @@ -54,7 +58,32 @@ bool InitializeStaticEGLInternal() { SetGLGetProcAddressProc(get_proc_address); AddGLNativeLibrary(egl_library); AddGLNativeLibrary(gles_library); + + return true; +} + +bool InitializeStaticEGLInternal() { + bool initialized = false; + +#if BUILDFLAG(USE_STATIC_ANGLE) + const base::CommandLine* command_line = + base::CommandLine::ForCurrentProcess(); + // Use ANGLE if it is requested via the --use-gl=angle flag and it is + // statically linked + if (command_line->GetSwitchValueASCII(switches::kUseGL) == + kGLImplementationANGLEName) { + initialized = InitializeStaticANGLEEGLInternal(); + } #endif // BUILDFLAG(USE_STATIC_ANGLE) + + if (!initialized) { + initialized = InitializeStaticNativeEGLInternal(); + } + + if (!initialized) { + return false; + } + SetGLImplementation(kGLImplementationEGLGLES2); InitializeStaticGLBindingsGL(); diff --git a/chromium/ui/gl/progress_reporter.h b/chromium/ui/gl/progress_reporter.h new file mode 100644 index 00000000000..5ebcfe01e98 --- /dev/null +++ b/chromium/ui/gl/progress_reporter.h @@ -0,0 +1,22 @@ +// Copyright (c) 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_GL_PROGRESS_REPORTER_H_ +#define UI_GL_PROGRESS_REPORTER_H_ + +namespace gl { + +// ProgressReporter is used by ContextGroup and GrGLInterface to report when it +// is making forward progress in execution, delaying activation of the watchdog +// timeout. +class ProgressReporter { + public: + virtual ~ProgressReporter() = default; + + virtual void ReportProgress() = 0; +}; + +} // namespace gl + +#endif // UI_GL_PROGRESS_REPORTER_H_ diff --git a/chromium/ui/gl/trace_util.cc b/chromium/ui/gl/trace_util.cc index cd8b41fec0e..45a8dfafd52 100644 --- a/chromium/ui/gl/trace_util.cc +++ b/chromium/ui/gl/trace_util.cc @@ -10,34 +10,33 @@ namespace gl { base::trace_event::MemoryAllocatorDumpGuid GetGLTextureClientGUIDForTracing( - uint64_t share_group_guid, + uint64_t context_group_tracing_id, uint32_t texture_id) { return base::trace_event::MemoryAllocatorDumpGuid( base::StringPrintf("gl-texture-client-x-process/%" PRIx64 "/%d", - share_group_guid, texture_id)); + context_group_tracing_id, texture_id)); } base::trace_event::MemoryAllocatorDumpGuid GetGLTextureServiceGUIDForTracing( - uint64_t share_group_guid, uint32_t texture_id) { return base::trace_event::MemoryAllocatorDumpGuid( - base::StringPrintf("gl-texture-service-x-process/%" PRIx64 "/%d", - share_group_guid, texture_id)); + base::StringPrintf("gl-texture-service-x-process/%d", texture_id)); } base::trace_event::MemoryAllocatorDumpGuid GetGLBufferGUIDForTracing( - uint64_t share_group_guid, + uint64_t context_group_tracing_id, uint32_t buffer_id) { - return base::trace_event::MemoryAllocatorDumpGuid(base::StringPrintf( - "gl-buffer-x-process/%" PRIx64 "/%d", share_group_guid, buffer_id)); + return base::trace_event::MemoryAllocatorDumpGuid( + base::StringPrintf("gl-buffer-x-process/%" PRIx64 "/%d", + context_group_tracing_id, buffer_id)); } base::trace_event::MemoryAllocatorDumpGuid GetGLRenderbufferGUIDForTracing( - uint64_t share_group_guid, + uint64_t context_group_tracing_id, uint32_t renderbuffer_id) { return base::trace_event::MemoryAllocatorDumpGuid( base::StringPrintf("gl-renderbuffer-x-process/%" PRIx64 "/%d", - share_group_guid, renderbuffer_id)); + context_group_tracing_id, renderbuffer_id)); } base::trace_event::MemoryAllocatorDumpGuid GetGLTextureRasterGUIDForTracing( diff --git a/chromium/ui/gl/trace_util.h b/chromium/ui/gl/trace_util.h index 1cc3f0c4b37..2284c6153b2 100644 --- a/chromium/ui/gl/trace_util.h +++ b/chromium/ui/gl/trace_util.h @@ -13,19 +13,18 @@ namespace gl { GL_EXPORT base::trace_event::MemoryAllocatorDumpGuid -GetGLTextureClientGUIDForTracing(uint64_t share_group_guid, +GetGLTextureClientGUIDForTracing(uint64_t context_group_tracing_id, uint32_t texture_client_id); GL_EXPORT base::trace_event::MemoryAllocatorDumpGuid -GetGLRenderbufferGUIDForTracing(uint64_t share_group_guid, +GetGLRenderbufferGUIDForTracing(uint64_t context_group_tracing_id, uint32_t renderbuffer_id); GL_EXPORT base::trace_event::MemoryAllocatorDumpGuid -GetGLTextureServiceGUIDForTracing(uint64_t share_group_guid, - uint32_t texture_service_id); +GetGLTextureServiceGUIDForTracing(uint32_t texture_service_id); GL_EXPORT base::trace_event::MemoryAllocatorDumpGuid GetGLBufferGUIDForTracing( - uint64_t share_group_guid, + uint64_t context_group_tracing_id, uint32_t buffer_id); GL_EXPORT base::trace_event::MemoryAllocatorDumpGuid diff --git a/chromium/ui/keyboard/BUILD.gn b/chromium/ui/keyboard/BUILD.gn index deaa465f4d6..f478b0b0910 100644 --- a/chromium/ui/keyboard/BUILD.gn +++ b/chromium/ui/keyboard/BUILD.gn @@ -55,8 +55,10 @@ jumbo_component("keyboard") { defines = [ "KEYBOARD_IMPLEMENTATION" ] deps = [ + ":mojom", ":resources", "//base", + "//chromeos", "//services/metrics/public/cpp:ukm_builders", "//ui/aura", "//ui/base", @@ -139,10 +141,7 @@ build_closure("inputview") { mojom("mojom") { sources = [ - "keyboard.mojom", - ] - deps = [ - "//ui/gfx/geometry/mojo", + "public/keyboard_config.mojom", ] } @@ -160,11 +159,12 @@ test("keyboard_unittests") { deps = [ ":keyboard", + ":mojom", ":test_support", "//base", "//base/test:test_support", "//components/ukm:test_support", - "//mojo/core/embedder", + "//services/service_manager/public/cpp", "//testing/gmock", "//testing/gtest", "//ui/aura:test_support", diff --git a/chromium/ui/keyboard/DEPS b/chromium/ui/keyboard/DEPS index 97e3446ef1c..286a31b2fd2 100644 --- a/chromium/ui/keyboard/DEPS +++ b/chromium/ui/keyboard/DEPS @@ -1,6 +1,9 @@ include_rules = [ + "+chromeos/chromeos_features.h", "+components/ukm", + "+mojo/public", "+services/metrics/public/cpp", + "+services/service_manager/public", "+ui/aura", "+ui/base", "+ui/compositor", diff --git a/chromium/ui/keyboard/keyboard.mojom b/chromium/ui/keyboard/keyboard.mojom deleted file mode 100644 index 5850df46a45..00000000000 --- a/chromium/ui/keyboard/keyboard.mojom +++ /dev/null @@ -1,25 +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. - -module keyboard.mojom; - -import "ui/gfx/geometry/mojo/geometry.mojom"; - -interface KeyboardObserver { - // Sent any time state changes in the keyboard. - OnKeyboardStateChanged(bool is_enabled, - bool is_visible, - uint64 display_id, - gfx.mojom.Rect bounds); -}; - -interface Keyboard { - // TODO(sky): needs display id. - Show(); - Hide(); - - // Adds an observer. OnKeyboardStateChanged() is immediately called to give - // the initial state. - AddObserver(KeyboardObserver observer); -}; diff --git a/chromium/ui/keyboard/keyboard_controller.cc b/chromium/ui/keyboard/keyboard_controller.cc index 4093ebd2006..a2adfbf40c9 100644 --- a/chromium/ui/keyboard/keyboard_controller.cc +++ b/chromium/ui/keyboard/keyboard_controller.cc @@ -13,6 +13,7 @@ #include "base/metrics/histogram_macros.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" +#include "ui/aura/client/aura_constants.h" #include "ui/aura/env.h" #include "ui/aura/window.h" #include "ui/aura/window_delegate.h" @@ -36,6 +37,7 @@ #include "ui/keyboard/display_util.h" #include "ui/keyboard/keyboard_controller_observer.h" #include "ui/keyboard/keyboard_layout_manager.h" +#include "ui/keyboard/keyboard_switches.h" #include "ui/keyboard/keyboard_ui.h" #include "ui/keyboard/keyboard_util.h" #include "ui/keyboard/notification_manager.h" @@ -46,10 +48,12 @@ #include "ui/ozone/public/ozone_platform.h" #include "ui/wm/core/window_animations.h" +namespace keyboard { + namespace { // Owned by ash::Shell. -keyboard::KeyboardController* g_keyboard_controller = nullptr; +KeyboardController* g_keyboard_controller = nullptr; constexpr int kHideKeyboardDelayMs = 100; @@ -65,43 +69,36 @@ constexpr int kReportLingeringStateDelayMs = 5000; constexpr int kTransientBlurThresholdMs = 3500; // State transition diagram (document linked from crbug.com/719905) -bool isAllowedStateTransition(keyboard::KeyboardControllerState from, - keyboard::KeyboardControllerState to) { - static const std::set<std::pair<keyboard::KeyboardControllerState, - keyboard::KeyboardControllerState>> +bool IsAllowedStateTransition(KeyboardControllerState from, + KeyboardControllerState to) { + static const std::set< + std::pair<KeyboardControllerState, KeyboardControllerState>> kAllowedStateTransition = { // The initial ShowKeyboard scenario // INITIAL -> LOADING_EXTENSION -> HIDDEN -> SHOWN. - {keyboard::KeyboardControllerState::UNKNOWN, - keyboard::KeyboardControllerState::INITIAL}, - {keyboard::KeyboardControllerState::INITIAL, - keyboard::KeyboardControllerState::LOADING_EXTENSION}, - {keyboard::KeyboardControllerState::LOADING_EXTENSION, - keyboard::KeyboardControllerState::HIDDEN}, - {keyboard::KeyboardControllerState::HIDDEN, - keyboard::KeyboardControllerState::SHOWN}, + {KeyboardControllerState::UNKNOWN, KeyboardControllerState::INITIAL}, + {KeyboardControllerState::INITIAL, + KeyboardControllerState::LOADING_EXTENSION}, + {KeyboardControllerState::LOADING_EXTENSION, + KeyboardControllerState::HIDDEN}, + {KeyboardControllerState::HIDDEN, KeyboardControllerState::SHOWN}, // Hide scenario // SHOWN -> WILL_HIDE -> HIDDEN. - {keyboard::KeyboardControllerState::SHOWN, - keyboard::KeyboardControllerState::WILL_HIDE}, - {keyboard::KeyboardControllerState::WILL_HIDE, - keyboard::KeyboardControllerState::HIDDEN}, + {KeyboardControllerState::SHOWN, KeyboardControllerState::WILL_HIDE}, + {KeyboardControllerState::WILL_HIDE, KeyboardControllerState::HIDDEN}, // Focus transition scenario // SHOWN -> WILL_HIDE -> SHOWN. - {keyboard::KeyboardControllerState::WILL_HIDE, - keyboard::KeyboardControllerState::SHOWN}, + {KeyboardControllerState::WILL_HIDE, KeyboardControllerState::SHOWN}, // HideKeyboard can be called at anytime for example on shutdown. - {keyboard::KeyboardControllerState::SHOWN, - keyboard::KeyboardControllerState::HIDDEN}, + {KeyboardControllerState::SHOWN, KeyboardControllerState::HIDDEN}, // Return to INITIAL when keyboard is disabled. - {keyboard::KeyboardControllerState::LOADING_EXTENSION, - keyboard::KeyboardControllerState::INITIAL}, - {keyboard::KeyboardControllerState::HIDDEN, - keyboard::KeyboardControllerState::INITIAL}, + {KeyboardControllerState::LOADING_EXTENSION, + KeyboardControllerState::INITIAL}, + {KeyboardControllerState::HIDDEN, KeyboardControllerState::INITIAL}, }; return kAllowedStateTransition.count(std::make_pair(from, to)) == 1; }; @@ -117,21 +114,21 @@ void SetTouchEventLogging(bool enable) { controller->SetTouchEventLoggingEnabled(enable); } -std::string StateToStr(keyboard::KeyboardControllerState state) { +std::string StateToStr(KeyboardControllerState state) { switch (state) { - case keyboard::KeyboardControllerState::UNKNOWN: + case KeyboardControllerState::UNKNOWN: return "UNKNOWN"; - case keyboard::KeyboardControllerState::SHOWN: + case KeyboardControllerState::SHOWN: return "SHOWN"; - case keyboard::KeyboardControllerState::LOADING_EXTENSION: + case KeyboardControllerState::LOADING_EXTENSION: return "LOADING_EXTENSION"; - case keyboard::KeyboardControllerState::WILL_HIDE: + case KeyboardControllerState::WILL_HIDE: return "WILL_HIDE"; - case keyboard::KeyboardControllerState::HIDDEN: + case KeyboardControllerState::HIDDEN: return "HIDDEN"; - case keyboard::KeyboardControllerState::INITIAL: + case KeyboardControllerState::INITIAL: return "INITIAL"; - case keyboard::KeyboardControllerState::COUNT: + case KeyboardControllerState::COUNT: NOTREACHED(); } NOTREACHED() << "Unknownstate: " << static_cast<int>(state); @@ -139,9 +136,20 @@ std::string StateToStr(keyboard::KeyboardControllerState state) { return ""; } -} // namespace +// An enumeration of different keyboard control events that should be logged. +enum KeyboardControlEvent { + KEYBOARD_CONTROL_SHOW = 0, + KEYBOARD_CONTROL_HIDE_AUTO, + KEYBOARD_CONTROL_HIDE_USER, + KEYBOARD_CONTROL_MAX, +}; -namespace keyboard { +void LogKeyboardControlEvent(KeyboardControlEvent event) { + UMA_HISTOGRAM_ENUMERATION("VirtualKeyboard.KeyboardControlEvent", event, + KEYBOARD_CONTROL_MAX); +} + +} // namespace // Observer for both keyboard show and hide animations. It should be owned by // KeyboardController. @@ -170,7 +178,8 @@ class CallbackAnimationObserver : public ui::ImplicitAnimationObserver { }; KeyboardController::KeyboardController() - : weak_factory_report_lingering_state_(this), + : ime_observer_(this), + weak_factory_report_lingering_state_(this), weak_factory_will_hide_(this) { DCHECK_EQ(g_keyboard_controller, nullptr); g_keyboard_controller = this; @@ -178,14 +187,25 @@ KeyboardController::KeyboardController() KeyboardController::~KeyboardController() { DCHECK(g_keyboard_controller); - DCHECK(!enabled()) - << "Keyboard must be disabled before KeyboardController is destroyed"; + DCHECK(!ui_) + << "Keyboard UI must be destroyed before KeyboardController is destroyed"; g_keyboard_controller = nullptr; } +// static +KeyboardController* KeyboardController::Get() { + DCHECK(g_keyboard_controller); + return g_keyboard_controller; +} + +// static +bool KeyboardController::HasInstance() { + return g_keyboard_controller; +} + void KeyboardController::EnableKeyboard(std::unique_ptr<KeyboardUI> ui, KeyboardLayoutDelegate* delegate) { - if (enabled()) + if (ui_) DisableKeyboard(); ui_ = std::move(ui); @@ -195,16 +215,16 @@ void KeyboardController::EnableKeyboard(std::unique_ptr<KeyboardUI> ui, show_on_keyboard_window_load_ = false; keyboard_locked_ = false; state_ = KeyboardControllerState::UNKNOWN; - ui_->GetInputMethod()->AddObserver(this); ui_->SetController(this); SetContainerBehaviorInternal(ContainerType::FULL_WIDTH); ChangeState(KeyboardControllerState::INITIAL); visual_bounds_in_screen_ = gfx::Rect(); time_of_last_blur_ = base::Time::UnixEpoch(); + UpdateInputMethodObserver(); } void KeyboardController::DisableKeyboard() { - if (!enabled()) + if (!ui_) return; if (parent_container_) @@ -222,7 +242,7 @@ void KeyboardController::DisableKeyboard() { container_behavior_.reset(); animation_observer_.reset(); - ui_->GetInputMethod()->RemoveObserver(this); + ime_observer_.RemoveAll(); for (KeyboardControllerObserver& observer : observer_list_) observer.OnKeyboardDisabled(); ui_->SetController(nullptr); @@ -236,8 +256,7 @@ void KeyboardController::ActivateKeyboardInContainer(aura::Window* parent) { // Observe changes to root window bounds. parent_container_->GetRootWindow()->AddObserver(this); - // TODO(https://crbug.com/845780): Investigate whether this does anything. - OnTextInputStateChanged(ui_->GetInputMethod()->GetTextInputClient()); + UpdateInputMethodObserver(); if (GetKeyboardWindow()) { DCHECK(!GetKeyboardWindow()->parent()); @@ -259,17 +278,6 @@ void KeyboardController::DeactivateKeyboard() { parent_container_ = nullptr; } -// static -KeyboardController* KeyboardController::Get() { - DCHECK(g_keyboard_controller); - return g_keyboard_controller; -} - -// static -bool KeyboardController::HasInstance() { - return g_keyboard_controller; -} - aura::Window* KeyboardController::GetKeyboardWindow() const { return ui_ && ui_->HasKeyboardWindow() ? ui_->GetKeyboardWindow() : nullptr; } @@ -278,22 +286,26 @@ aura::Window* KeyboardController::GetRootWindow() { return parent_container_ ? parent_container_->GetRootWindow() : nullptr; } +// private void KeyboardController::NotifyKeyboardBoundsChanging( const gfx::Rect& new_bounds) { visual_bounds_in_screen_ = new_bounds; - if (ui_->HasKeyboardWindow() && ui_->GetKeyboardWindow()->IsVisible()) { + aura::Window* window = GetKeyboardWindow(); + if (window && window->IsVisible()) { const gfx::Rect occluded_bounds_in_screen = GetWorkspaceOccludedBounds(); notification_manager_.SendNotifications( container_behavior_->OccludedBoundsAffectWorkspaceLayout(), new_bounds, occluded_bounds_in_screen, observer_list_); - if (keyboard::IsKeyboardOverscrollEnabled()) + if (IsKeyboardOverscrollEnabled()) ui_->InitInsets(occluded_bounds_in_screen); else ui_->ResetInsets(); } else { visual_bounds_in_screen_ = gfx::Rect(); } + + EnsureCaretInWorkArea(GetWorkspaceOccludedBounds()); } void KeyboardController::MoveKeyboard(const gfx::Rect& new_bounds) { @@ -329,6 +341,16 @@ void KeyboardController::NotifyKeyboardWindowLoaded() { } } +void KeyboardController::Reload() { + if (!GetKeyboardWindow()) + return; + + // A reload should never try to show virtual keyboard. If keyboard is not + // visible before reload, it should stay invisible after reload. + show_on_keyboard_window_load_ = false; + ui_->ReloadKeyboardIfNeeded(); +} + void KeyboardController::AddObserver(KeyboardControllerObserver* observer) { observer_list_.AddObserver(observer); } @@ -346,6 +368,54 @@ ui::TextInputClient* KeyboardController::GetTextInputClient() { return ui_->GetInputMethod()->GetTextInputClient(); } +bool KeyboardController::InsertText(const base::string16& text) { + if (!ui_) + return false; + + ui::InputMethod* input_method = ui_->GetInputMethod(); + if (!input_method) + return false; + + ui::TextInputClient* tic = input_method->GetTextInputClient(); + if (!tic || tic->GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) + return false; + + tic->InsertText(text); + + return true; +} + +bool KeyboardController::UpdateKeyboardConfig( + const mojom::KeyboardConfig& config) { + if (config.Equals(keyboard_config_)) + return false; + keyboard_config_ = config; + if (IsEnabled()) + NotifyKeyboardConfigChanged(); + return true; +} + +bool KeyboardController::IsKeyboardOverscrollEnabled() const { + if (!keyboard::IsKeyboardEnabled()) + return false; + + // Users of the sticky accessibility on-screen keyboard are likely to be using + // mouse input, which may interfere with overscrolling. + if (IsEnabled() && !IsOverscrollAllowed()) + return false; + + // If overscroll enabled behavior is set, use it instead. Currently + // login / out-of-box disable keyboard overscroll. http://crbug.com/363635 + if (keyboard_config_.overscroll_behavior != + mojom::KeyboardOverscrollBehavior::kDefault) { + return keyboard_config_.overscroll_behavior == + mojom::KeyboardOverscrollBehavior::kEnabled; + } + + return !base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDisableVirtualKeyboardOverscroll); +} + void KeyboardController::MoveToDisplayWithTransition( display::Display display, gfx::Rect new_bounds_in_local) { @@ -354,10 +424,12 @@ void KeyboardController::MoveToDisplayWithTransition( HideKeyboardTemporarilyForTransition(); } +// private void KeyboardController::HideKeyboard(HideReason reason) { TRACE_EVENT0("vk", "HideKeyboard"); switch (state_) { + case KeyboardControllerState::UNKNOWN: case KeyboardControllerState::INITIAL: case KeyboardControllerState::HIDDEN: return; @@ -374,12 +446,11 @@ void KeyboardController::HideKeyboard(HideReason reason) { case HIDE_REASON_SYSTEM_EXPLICIT: case HIDE_REASON_SYSTEM_IMPLICIT: case HIDE_REASON_SYSTEM_TEMPORARY: - keyboard::LogKeyboardControlEvent( - keyboard::KEYBOARD_CONTROL_HIDE_AUTO); + LogKeyboardControlEvent(KEYBOARD_CONTROL_HIDE_AUTO); break; case HIDE_REASON_USER_EXPLICIT: - keyboard::LogKeyboardControlEvent( - keyboard::KEYBOARD_CONTROL_HIDE_USER); + case HIDE_REASON_USER_IMPLICIT: + LogKeyboardControlEvent(KEYBOARD_CONTROL_HIDE_USER); break; } @@ -393,6 +464,7 @@ void KeyboardController::HideKeyboard(HideReason reason) { case HIDE_REASON_SYSTEM_TEMPORARY: case HIDE_REASON_SYSTEM_EXPLICIT: case HIDE_REASON_USER_EXPLICIT: + case HIDE_REASON_USER_IMPLICIT: time_of_last_blur_ = base::Time::UnixEpoch(); break; } @@ -422,11 +494,10 @@ void KeyboardController::HideKeyboard(HideReason reason) { for (KeyboardControllerObserver& observer : observer_list_) observer.OnKeyboardHidden(reason == HIDE_REASON_SYSTEM_TEMPORARY); - ui_->EnsureCaretInWorkArea(gfx::Rect()); break; } - default: + case KeyboardControllerState::COUNT: NOTREACHED(); } } @@ -435,6 +506,11 @@ void KeyboardController::HideKeyboardByUser() { HideKeyboard(HIDE_REASON_USER_EXPLICIT); } +void KeyboardController::HideKeyboardImplicitlyByUser() { + if (!keyboard_locked()) + HideKeyboard(HIDE_REASON_USER_IMPLICIT); +} + void KeyboardController::HideKeyboardTemporarilyForTransition() { HideKeyboard(HIDE_REASON_SYSTEM_TEMPORARY); } @@ -461,6 +537,7 @@ void KeyboardController::DismissVirtualKeyboard() { HideKeyboardByUser(); } +// private void KeyboardController::HideAnimationFinished() { if (state_ == KeyboardControllerState::HIDDEN) { if (queued_container_type_) { @@ -482,15 +559,16 @@ void KeyboardController::HideAnimationFinished() { } } +// private void KeyboardController::ShowAnimationFinished() { MarkKeyboardLoadFinished(); // Notify observers after animation finished to prevent reveal desktop // background during animation. NotifyKeyboardBoundsChanging(GetKeyboardWindow()->bounds()); - ui_->EnsureCaretInWorkArea(GetWorkspaceOccludedBounds()); } +// private void KeyboardController::SetContainerBehaviorInternal( const ContainerType type) { // Reset the hit test event targeter because the hit test bounds will @@ -524,6 +602,16 @@ void KeyboardController::ShowKeyboardInDisplay( ShowKeyboardInternal(display); } +void KeyboardController::LoadKeyboardWindowInBackground() { + // ShowKeyboardInternal may trigger RootControllerWindow::ActiveKeyboard which + // will cause LoadKeyboardWindowInBackground to potentially run even though + // the keyboard has been initialized. + if (state_ != KeyboardControllerState::INITIAL) + return; + + PopulateKeyboardContent(display::Display(), false); +} + void KeyboardController::OnWindowAddedToRootWindow(aura::Window* window) { container_behavior_->SetCanonicalBounds(GetKeyboardWindow(), GetRootWindow()->bounds()); @@ -545,13 +633,10 @@ void KeyboardController::OnWindowBoundsChanged( NotifyKeyboardBoundsChanging(new_bounds); } -void KeyboardController::Reload() { - if (ui_->HasKeyboardWindow()) { - // A reload should never try to show virtual keyboard. If keyboard is not - // visible before reload, it should stay invisible after reload. - show_on_keyboard_window_load_ = false; - ui_->ReloadKeyboardIfNeeded(); - } +void KeyboardController::OnInputMethodDestroyed( + const ui::InputMethod* input_method) { + ime_observer_.RemoveAll(); + OnTextInputStateChanged(nullptr); } void KeyboardController::OnTextInputStateChanged( @@ -609,23 +694,14 @@ void KeyboardController::ShowKeyboardIfWithinTransientBlurThreshold() { void KeyboardController::OnShowVirtualKeyboardIfEnabled() { // Calling |ShowKeyboardInternal| may move the keyboard to another display. - if (IsKeyboardEnabled() && !keyboard_locked()) + if (keyboard::IsKeyboardEnabled() && !keyboard_locked()) ShowKeyboardInternal(display::Display()); } -void KeyboardController::LoadKeyboardWindowInBackground() { - // ShowKeyboardInternal may trigger RootControllerWindow::ActiveKeyboard which - // will cause LoadKeyboardWindowInBackground to potentially run even though - // the keyboard has been initialized. - if (state_ != KeyboardControllerState::INITIAL) - return; - - PopulateKeyboardContent(display::Display(), false); -} - void KeyboardController::ShowKeyboardInternal(const display::Display& display) { - keyboard::MarkKeyboardLoadStarted(); + MarkKeyboardLoadStarted(); PopulateKeyboardContent(display, true); + UpdateInputMethodObserver(); } void KeyboardController::PopulateKeyboardContent( @@ -637,13 +713,15 @@ void KeyboardController::PopulateKeyboardContent( if (parent_container_->children().empty()) { DCHECK_EQ(state_, KeyboardControllerState::INITIAL); + // TODO(https://crbug.com/845780): This call will create and load the + // virtual keyboard window. Redesign the KeyboardUI interface so that + // loading is explicit. aura::Window* keyboard_window = ui_->GetKeyboardWindow(); keyboard_window->AddPreTargetHandler(&event_filter_); keyboard_window->AddObserver(this); parent_container_->AddChild(keyboard_window); } - DCHECK(ui_->HasKeyboardWindow()); if (layout_delegate_ != nullptr) { if (display.is_valid()) layout_delegate_->MoveKeyboardToDisplay(display); @@ -651,7 +729,8 @@ void KeyboardController::PopulateKeyboardContent( layout_delegate_->MoveKeyboardToTouchableDisplay(); } - aura::Window* keyboard_window = ui_->GetKeyboardWindow(); + aura::Window* keyboard_window = GetKeyboardWindow(); + DCHECK(keyboard_window); DCHECK_EQ(parent_container_, keyboard_window->parent()); switch (state_) { @@ -670,7 +749,7 @@ void KeyboardController::PopulateKeyboardContent( switch (state_) { case KeyboardControllerState::INITIAL: - DCHECK_EQ(ui_->GetKeyboardWindow()->bounds().height(), 0); + DCHECK_EQ(keyboard_window->bounds().height(), 0); show_on_keyboard_window_load_ = show_keyboard; ChangeState(KeyboardControllerState::LOADING_EXTENSION); return; @@ -687,7 +766,7 @@ void KeyboardController::PopulateKeyboardContent( // are at begin states for animation. container_behavior_->InitializeShowAnimationStartingState(keyboard_window); - keyboard::LogKeyboardControlEvent(keyboard::KEYBOARD_CONTROL_SHOW); + LogKeyboardControlEvent(KEYBOARD_CONTROL_SHOW); RecordUkmKeyboardShown(); ui::LayerAnimator* container_animator = @@ -729,7 +808,7 @@ void KeyboardController::NotifyKeyboardConfigChanged() { void KeyboardController::CheckStateTransition(KeyboardControllerState prev, KeyboardControllerState next) { std::stringstream error_message; - const bool valid_transition = isAllowedStateTransition(prev, next); + const bool valid_transition = IsAllowedStateTransition(prev, next); if (!valid_transition) error_message << "Unexpected transition"; @@ -783,7 +862,7 @@ void KeyboardController::ReportLingeringState() { } gfx::Rect KeyboardController::GetWorkspaceOccludedBounds() const { - if (!enabled()) + if (!ui_) return gfx::Rect(); const gfx::Rect visual_bounds_in_window(visual_bounds_in_screen_.size()); @@ -798,7 +877,7 @@ gfx::Rect KeyboardController::GetKeyboardLockScreenOffsetBounds() const { // Overscroll is generally dependent on lock state, however, its behavior // temporarily overridden by a static field in certain lock screen contexts. // Furthermore, floating keyboard should never affect layout. - if (!keyboard::IsKeyboardOverscrollEnabled() && + if (!IsKeyboardOverscrollEnabled() && container_behavior_->GetType() != ContainerType::FLOATING && container_behavior_->GetType() != ContainerType::FULLSCREEN) { return visual_bounds_in_screen_; @@ -866,6 +945,15 @@ void KeyboardController::SetContainerType( } } +ui::InputMethod* KeyboardController::GetInputMethodForTest() { + return ui_->GetInputMethod(); +} + +void KeyboardController::EnsureCaretInWorkAreaForTest( + const gfx::Rect& occluded_bounds) { + EnsureCaretInWorkArea(occluded_bounds); +} + void KeyboardController::RecordUkmKeyboardShown() { ui::TextInputClient* text_input_client = GetTextInputClient(); if (!text_input_client) @@ -882,7 +970,7 @@ bool KeyboardController::SetDraggableArea(const gfx::Rect& rect) { bool KeyboardController::DisplayVirtualKeyboard() { // Calling |ShowKeyboardInternal| may move the keyboard to another display. - if (IsKeyboardEnabled() && !keyboard_locked()) { + if (keyboard::IsKeyboardEnabled() && !keyboard_locked()) { ShowKeyboardInternal(display::Display()); return true; } @@ -899,7 +987,65 @@ void KeyboardController::RemoveObserver( } bool KeyboardController::IsKeyboardVisible() { - return state_ == KeyboardControllerState::SHOWN; + if (state_ == KeyboardControllerState::SHOWN) { + DCHECK(IsEnabled()); + return true; + } + return false; +} + +void KeyboardController::UpdateInputMethodObserver() { + ui::InputMethod* ime = ui_->GetInputMethod(); + + // IME could be null during initialization. Ignoring the case is okay because + // UpdateInputMethodObserver() will be called later on. + if (!ime) + return; + + if (ime_observer_.IsObserving(ime)) + return; + + // Only observes the current active IME. + ime_observer_.RemoveAll(); + ime_observer_.Add(ime); + + // TODO(https://crbug.com/845780): Investigate whether this does anything. + OnTextInputStateChanged(ime->GetTextInputClient()); +} + +void KeyboardController::EnsureCaretInWorkArea( + const gfx::Rect& occluded_bounds) { + ui::InputMethod* ime = ui_->GetInputMethod(); + if (!ime) + return; + + TRACE_EVENT0("vk", "EnsureCaretInWorkArea"); + + if (IsOverscrollAllowed()) { + ime->SetOnScreenKeyboardBounds(occluded_bounds); + } else if (ime->GetTextInputClient()) { + ime->GetTextInputClient()->EnsureCaretNotInRect(occluded_bounds); + } +} + +void KeyboardController::MarkKeyboardLoadStarted() { + if (!keyboard_load_time_logged_) + keyboard_load_time_start_ = base::Time::Now(); +} + +void KeyboardController::MarkKeyboardLoadFinished() { + // Possible to get a load finished without a start if navigating directly to + // chrome://keyboard. + if (keyboard_load_time_start_.is_null()) + return; + + if (keyboard_load_time_logged_) + return; + + // Log the delta only once. + UMA_HISTOGRAM_TIMES("VirtualKeyboard.InitLatency.FirstLoad", + base::Time::Now() - keyboard_load_time_start_); + keyboard_load_time_logged_ = true; } } // namespace keyboard diff --git a/chromium/ui/keyboard/keyboard_controller.h b/chromium/ui/keyboard/keyboard_controller.h index bac50bc898f..94e0b6c85dc 100644 --- a/chromium/ui/keyboard/keyboard_controller.h +++ b/chromium/ui/keyboard/keyboard_controller.h @@ -9,6 +9,7 @@ #include "base/macros.h" #include "base/observer_list.h" +#include "base/scoped_observer.h" #include "base/time/time.h" #include "ui/aura/window_observer.h" #include "ui/base/ime/input_method_keyboard_controller.h" @@ -24,8 +25,8 @@ #include "ui/keyboard/keyboard_export.h" #include "ui/keyboard/keyboard_layout_delegate.h" #include "ui/keyboard/keyboard_ukm_recorder.h" -#include "ui/keyboard/keyboard_util.h" #include "ui/keyboard/notification_manager.h" +#include "ui/keyboard/public/keyboard_config.mojom.h" #include "ui/keyboard/queued_container_type.h" #include "ui/keyboard/queued_display_change.h" @@ -35,7 +36,7 @@ class Window; namespace ui { class InputMethod; class TextInputClient; -} +} // namespace ui namespace keyboard { @@ -71,10 +72,19 @@ class KEYBOARD_EXPORT KeyboardController public aura::WindowObserver, public ui::InputMethodKeyboardController { public: - KeyboardController(); ~KeyboardController() override; + // Retrieves the active keyboard controller. Guaranteed to not be null while + // there is an ash::Shell. + // TODO(stevenjb/shuchen/shend): Remove all access from src/chrome. + // https://crbug.com/843332. + static KeyboardController* Get(); + + // Returns true if there is a valid KeyboardController instance (e.g. while + // there is an ash::Shell). + static bool HasInstance(); + // Enables the virtual keyboard with a specified |ui| and |delegate|. // Disables and re-enables the keyboard if it is already enabled. void EnableKeyboard(std::unique_ptr<KeyboardUI> ui, @@ -103,6 +113,16 @@ class KEYBOARD_EXPORT KeyboardController // null if the keyboard has not been attached to any root window. aura::Window* GetRootWindow(); + // Moves an already loaded keyboard. + void MoveKeyboard(const gfx::Rect& new_bounds); + + // Sets the bounds of the keyboard window. + void SetKeyboardWindowBounds(const gfx::Rect& new_bounds); + + // Called by KeyboardUI when the keyboard window has loaded. Shows + // the keyboard if show_on_keyboard_window_load_ is true. + void NotifyKeyboardWindowLoaded(); + // Reloads the content of the keyboard. No-op if the keyboard content is not // loaded yet. void Reload(); @@ -112,19 +132,40 @@ class KEYBOARD_EXPORT KeyboardController bool HasObserver(KeyboardControllerObserver* observer) const; void RemoveObserver(KeyboardControllerObserver* observer); - KeyboardUI* ui() { return ui_.get(); } - // Gets the currently focused text input client. ui::TextInputClient* GetTextInputClient(); + // Insert |text| into the active TextInputClient if there is one. Returns true + // if |text| was successfully inserted. + bool InsertText(const base::string16& text); + + // Updates |keyboard_config_| with |config|. Returns |false| if there is no + // change, otherwise returns true and notifies observers if this is enabled(). + bool UpdateKeyboardConfig(const mojom::KeyboardConfig& config); + const mojom::KeyboardConfig& keyboard_config() { return keyboard_config_; } + + // Returns true if keyboard overscroll is enabled. + bool IsKeyboardOverscrollEnabled() const; + void set_keyboard_locked(bool lock) { keyboard_locked_ = lock; } bool keyboard_locked() const { return keyboard_locked_; } + void MoveToDisplayWithTransition(display::Display display, + gfx::Rect new_bounds_in_local); + // Hide the keyboard because the user has chosen to specifically hide the // keyboard, such as pressing the dismiss button. + // TODO(https://crbug.com/845780): Rename this to + // HideKeyboardExplicitlyByUser. + // TODO(https://crbug.com/845780): Audit and switch callers to + // HideKeyboardImplicitlyByUser where appropriate. void HideKeyboardByUser(); + // Hide the keyboard as a secondary effect of a user action, such as tapping + // the shelf. The keyboard should not hide if it's locked. + void HideKeyboardImplicitlyByUser(); + // Hide the keyboard due to some internally generated change to change the // state of the keyboard. For example, moving from the docked keyboard to the // floating keyboard. @@ -144,21 +185,13 @@ class KEYBOARD_EXPORT KeyboardController // |lock| is true. void ShowKeyboard(bool lock); - // Loads the keyboard window in the background, but does not display - // the keyboard. - void LoadKeyboardWindowInBackground(); - // Force the keyboard to show up in the specific display if not showing and // lock the keyboard void ShowKeyboardInDisplay(const display::Display& display); - // Retrieves the active keyboard controller. Guaranteed to not be null while - // there is an ash::Shell. - static KeyboardController* Get(); - - // Returns true if there is a valid KeyboardController instance (e.g. while - // there is an ash::Shell). - static bool HasInstance(); + // Loads the keyboard window in the background, but does not display + // the keyboard. + void LoadKeyboardWindowInBackground(); // Returns the bounds in screen for the visible portion of the keyboard. An // empty rectangle will get returned when the keyboard is hidden. @@ -182,8 +215,6 @@ class KEYBOARD_EXPORT KeyboardController // Does not do anything if there is no keyboard window. void SetHitTestBounds(const std::vector<gfx::Rect>& bounds); - KeyboardControllerState GetStateForTest() const { return state_; } - ContainerType GetActiveContainerType() const { return container_behavior_->GetType(); } @@ -195,16 +226,14 @@ class KEYBOARD_EXPORT KeyboardController // container behavior. bool IsOverscrollAllowed() const; - // Whether the keyboard is enabled. - bool enabled() const { return ui_ != nullptr; } + // Whether the keyboard has been enabled, i.e. EnableKeyboard() has been + // called. + bool IsEnabled() const { return ui_ != nullptr; } // Handle mouse and touch events on the keyboard. The effects of this method // will not stop propagation to the keyboard extension. bool HandlePointerEvent(const ui::LocatedEvent& event); - // Moves an already loaded keyboard. - void MoveKeyboard(const gfx::Rect& new_bounds); - // Sets the active container type. If the keyboard is currently shown, this // will trigger a hide animation and a subsequent show animation. Otherwise // the ContainerBehavior change is synchronous. @@ -215,13 +244,6 @@ class KEYBOARD_EXPORT KeyboardController // Sets floating keyboard draggable rect. bool SetDraggableArea(const gfx::Rect& rect); - void MoveToDisplayWithTransition(display::Display display, - gfx::Rect new_bounds_in_local); - - // Called by KeyboardUI when the keyboard window has loaded. Shows - // the keyboard if show_on_keyboard_window_load_ is true. - void NotifyKeyboardWindowLoaded(); - // InputMethodKeyboardController overrides. bool DisplayVirtualKeyboard() override; void DismissVirtualKeyboard() override; @@ -231,14 +253,14 @@ class KEYBOARD_EXPORT KeyboardController ui::InputMethodKeyboardControllerObserver* observer) override; bool IsKeyboardVisible() override; + KeyboardControllerState GetStateForTest() const { return state_; } + ui::InputMethod* GetInputMethodForTest(); + void EnsureCaretInWorkAreaForTest(const gfx::Rect& occluded_bounds); + private: // For access to Observer methods for simulation. friend class KeyboardControllerTest; - // For access to NotifyKeyboardConfigChanged - friend bool keyboard::UpdateKeyboardConfig( - const keyboard::KeyboardConfig& config); - // Different ways to hide the keyboard. enum HideReason { // System initiated due to an active event, where the user does not want @@ -259,9 +281,14 @@ class KEYBOARD_EXPORT KeyboardController // floating) HIDE_REASON_SYSTEM_TEMPORARY, - // User initiated. + // User explicitly hiding the keyboard via the close button. Also hides + // locked keyboards. HIDE_REASON_USER_EXPLICIT, + // Keyboard is hidden as an indirect consequence of some user action. + // Examples include opening the window overview mode, or tapping on the + // shelf status area. Does not hide locked keyboards. + HIDE_REASON_USER_IMPLICIT, }; // aura::WindowObserver overrides @@ -275,13 +302,10 @@ class KEYBOARD_EXPORT KeyboardController void OnBlur() override {} void OnCaretBoundsChanged(const ui::TextInputClient* client) override {} void OnFocus() override {} - void OnInputMethodDestroyed(const ui::InputMethod* input_method) override {} + void OnInputMethodDestroyed(const ui::InputMethod* input_method) override; void OnTextInputStateChanged(const ui::TextInputClient* client) override; void OnShowVirtualKeyboardIfEnabled() override; - // Sets the bounds of the keyboard window. - void SetKeyboardWindowBounds(const gfx::Rect& new_bounds); - // Show virtual keyboard immediately with animation. void ShowKeyboardInternal(const display::Display& display); void PopulateKeyboardContent(const display::Display& display, @@ -327,8 +351,25 @@ class KEYBOARD_EXPORT KeyboardController // Records that keyboard was shown on the currently focused UKM source. void RecordUkmKeyboardShown(); + // Ensures that the current IME is observed if it is changed. + void UpdateInputMethodObserver(); + + // Ensures caret in current work area (not occluded by virtual keyboard + // window). + void EnsureCaretInWorkArea(const gfx::Rect& occluded_bounds); + + // Marks that the keyboard load has started. This is used to measure the time + // it takes to fully load the keyboard. This should be called before + // MarkKeyboardLoadFinished. + void MarkKeyboardLoadStarted(); + + // Marks that the keyboard load has ended. This finishes measuring that the + // keyboard is loaded. + void MarkKeyboardLoadFinished(); + std::unique_ptr<KeyboardUI> ui_; - KeyboardLayoutDelegate* layout_delegate_; + KeyboardLayoutDelegate* layout_delegate_ = nullptr; + ScopedObserver<ui::InputMethod, ui::InputMethodObserver> ime_observer_; // Container window that the keyboard window is a child of. aura::Window* parent_container_ = nullptr; @@ -344,10 +385,10 @@ class KEYBOARD_EXPORT KeyboardController std::unique_ptr<QueuedDisplayChange> queued_display_change_; // If true, show the keyboard window when it loads. - bool show_on_keyboard_window_load_; + bool show_on_keyboard_window_load_ = false; // If true, the keyboard is always visible even if no window has input focus. - bool keyboard_locked_; + bool keyboard_locked_ = false; KeyboardEventFilter event_filter_; base::ObserverList<KeyboardControllerObserver>::Unchecked observer_list_; @@ -357,7 +398,10 @@ class KEYBOARD_EXPORT KeyboardController // keyboard window. If not, this should be empty. gfx::Rect visual_bounds_in_screen_; - KeyboardControllerState state_; + KeyboardControllerState state_ = KeyboardControllerState::UNKNOWN; + + // Keyboard configuration associated with the controller. + mojom::KeyboardConfig keyboard_config_; NotificationManager notification_manager_; @@ -365,6 +409,9 @@ class KEYBOARD_EXPORT KeyboardController DisplayUtil display_util_; + bool keyboard_load_time_logged_ = false; + base::Time keyboard_load_time_start_; + base::WeakPtrFactory<KeyboardController> weak_factory_report_lingering_state_; base::WeakPtrFactory<KeyboardController> weak_factory_will_hide_; diff --git a/chromium/ui/keyboard/keyboard_controller_observer.h b/chromium/ui/keyboard/keyboard_controller_observer.h index 217417b0a5a..f3ffe74a35d 100644 --- a/chromium/ui/keyboard/keyboard_controller_observer.h +++ b/chromium/ui/keyboard/keyboard_controller_observer.h @@ -67,8 +67,9 @@ class KEYBOARD_EXPORT KeyboardControllerObserver { virtual void OnKeyboardAppearanceChanged( const KeyboardStateDescriptor& state) {} - // Called when the keyboard was disabled (e.g. when user switches convertible - // to laptop mode) + // Called when the keyboard is effectively disabled (i.e. when the UI / window + // is destroyed, not when keyboard::IsKeyboardEnabled() changes), e.g. when + // user switches convertible to laptop mode or the active user changes. virtual void OnKeyboardDisabled() {} // Called when the keyboard has been hidden and the hiding animation finished diff --git a/chromium/ui/keyboard/keyboard_controller_unittest.cc b/chromium/ui/keyboard/keyboard_controller_unittest.cc index 12854f0bec9..afcc0822f8d 100644 --- a/chromium/ui/keyboard/keyboard_controller_unittest.cc +++ b/chromium/ui/keyboard/keyboard_controller_unittest.cc @@ -185,7 +185,6 @@ class KeyboardControllerTest : public aura::test::AuraTestBase, aura::test::AuraTestBase::TearDown(); } - KeyboardUI* ui() { return controller_.ui(); } KeyboardController& controller() { return controller_; } KeyboardLayoutDelegate* layout_delegate() { return layout_delegate_.get(); } @@ -243,14 +242,14 @@ class KeyboardControllerTest : public aura::test::AuraTestBase, } void SetFocus(ui::TextInputClient* client) { - ui::InputMethod* input_method = ui()->GetInputMethod(); + ui::InputMethod* input_method = controller().GetInputMethodForTest(); input_method->SetFocusedTextInputClient(client); if (client && client->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE && client->GetTextInputMode() != ui::TEXT_INPUT_MODE_NONE) { input_method->ShowVirtualKeyboardIfEnabled(); - if (controller_.ui()->GetKeyboardWindow()->bounds().height() == 0) { + if (controller().GetKeyboardWindow()->bounds().height() == 0) { // Set initial bounds for test keyboard window. - controller_.ui()->GetKeyboardWindow()->SetBounds( + controller().GetKeyboardWindow()->SetBounds( KeyboardBoundsFromRootBounds(root_window()->bounds(), kDefaultVirtualKeyboardHeight)); // Simulate the keyboard contents finish loading @@ -262,9 +261,9 @@ class KeyboardControllerTest : public aura::test::AuraTestBase, bool WillHideKeyboard() { return controller_.WillHideKeyboard(); } bool ShouldEnableInsets(aura::Window* window) { - aura::Window* contents_window = controller_.ui()->GetKeyboardWindow(); + aura::Window* contents_window = controller().GetKeyboardWindow(); return (contents_window->GetRootWindow() == window->GetRootWindow() && - keyboard::IsKeyboardOverscrollEnabled() && + controller_.IsKeyboardOverscrollEnabled() && contents_window->IsVisible() && controller_.IsKeyboardVisible()); } @@ -458,17 +457,17 @@ TEST_F(KeyboardControllerTest, CheckOverscrollInsetDuringVisibilityChange) { ui::DummyTextInputClient no_input_client(ui::TEXT_INPUT_TYPE_NONE); // Enable touch keyboard / overscroll mode to test insets. - EXPECT_TRUE(keyboard::IsKeyboardOverscrollEnabled()); + EXPECT_TRUE(controller().IsKeyboardOverscrollEnabled()); SetFocus(&input_client); SetFocus(&no_input_client); // Insets should not be enabled for new windows while keyboard is in the // process of hiding when overscroll is enabled. - EXPECT_FALSE(ShouldEnableInsets(ui()->GetKeyboardWindow())); + EXPECT_FALSE(ShouldEnableInsets(controller().GetKeyboardWindow())); // Cancel keyboard hide. SetFocus(&input_client); // Insets should be enabled for new windows as hide was cancelled. - EXPECT_TRUE(ShouldEnableInsets(ui()->GetKeyboardWindow())); + EXPECT_TRUE(ShouldEnableInsets(controller().GetKeyboardWindow())); } TEST_F(KeyboardControllerTest, AlwaysVisibleWhenLocked) { @@ -579,7 +578,7 @@ class KeyboardControllerAnimationTest : public KeyboardControllerTest { } protected: - aura::Window* keyboard_window() { return ui()->GetKeyboardWindow(); } + aura::Window* keyboard_window() { return controller().GetKeyboardWindow(); } private: DISALLOW_COPY_AND_ASSIGN(KeyboardControllerAnimationTest); @@ -711,7 +710,7 @@ TEST_F(KeyboardControllerAnimationTest, ShowKeyboard(); RunAnimationForLayer(layer); - ASSERT_TRUE(controller().ui()); + ASSERT_TRUE(keyboard_window()); controller().DeactivateKeyboard(); @@ -795,7 +794,8 @@ TEST_F(KeyboardControllerAnimationTest, FloatingKeyboardEnsureCaretInWorkArea) { EXPECT_TRUE(keyboard_window()->IsVisible()); // Unfocus from the MockTextInputClient before destroying it. - ui()->GetInputMethod()->DetachTextInputClient(&mock_input_client); + controller().GetInputMethodForTest()->DetachTextInputClient( + &mock_input_client); } // Checks DisableKeyboard() doesn't clear the observer list. diff --git a/chromium/ui/keyboard/keyboard_event_filter.cc b/chromium/ui/keyboard/keyboard_event_filter.cc index dd59c566c7d..1d903f21679 100644 --- a/chromium/ui/keyboard/keyboard_event_filter.cc +++ b/chromium/ui/keyboard/keyboard_event_filter.cc @@ -32,7 +32,7 @@ void KeyboardEventFilter::OnTouchEvent(ui::TouchEvent* event) { void KeyboardEventFilter::ProcessPointerEvent(ui::LocatedEvent* event) { auto* controller = KeyboardController::Get(); - if (controller->enabled() && controller->HandlePointerEvent(*event)) + if (controller->IsEnabled() && controller->HandlePointerEvent(*event)) event->SetHandled(); } diff --git a/chromium/ui/keyboard/keyboard_layout_manager.cc b/chromium/ui/keyboard/keyboard_layout_manager.cc index 32d34a504dd..59af679b08f 100644 --- a/chromium/ui/keyboard/keyboard_layout_manager.cc +++ b/chromium/ui/keyboard/keyboard_layout_manager.cc @@ -8,7 +8,6 @@ #include "ui/display/display.h" #include "ui/display/screen.h" #include "ui/keyboard/keyboard_controller.h" -#include "ui/keyboard/keyboard_util.h" namespace keyboard { diff --git a/chromium/ui/keyboard/keyboard_switches.cc b/chromium/ui/keyboard/keyboard_switches.cc index 32a4beec533..224cb1c35b1 100644 --- a/chromium/ui/keyboard/keyboard_switches.cc +++ b/chromium/ui/keyboard/keyboard_switches.cc @@ -8,8 +8,6 @@ namespace keyboard { namespace switches { const char kDisableInputView[] = "disable-input-view"; -const char kEnableExperimentalInputViewFeatures[] = - "enable-experimental-input-view-features"; const char kDisableVoiceInput[] = "disable-voice-input"; const char kDisableGestureTyping[] = "disable-gesture-typing"; const char kDisableGestureEditing[] = "disable-gesture-editing"; diff --git a/chromium/ui/keyboard/keyboard_switches.h b/chromium/ui/keyboard/keyboard_switches.h index d3a88cd0ed3..bde216b0a3a 100644 --- a/chromium/ui/keyboard/keyboard_switches.h +++ b/chromium/ui/keyboard/keyboard_switches.h @@ -17,9 +17,6 @@ KEYBOARD_EXPORT extern const char kDisableInputView[]; // Disables voice input. KEYBOARD_EXPORT extern const char kDisableVoiceInput[]; -// Enables experimental features for IME extensions. -KEYBOARD_EXPORT extern const char kEnableExperimentalInputViewFeatures[]; - // Flag which disables gesture typing for the virtual keyboard. KEYBOARD_EXPORT extern const char kDisableGestureTyping[]; diff --git a/chromium/ui/keyboard/keyboard_ui.cc b/chromium/ui/keyboard/keyboard_ui.cc index bf591ed04cd..16d24e63da9 100644 --- a/chromium/ui/keyboard/keyboard_ui.cc +++ b/chromium/ui/keyboard/keyboard_ui.cc @@ -29,20 +29,6 @@ void KeyboardUI::HideKeyboardWindow() { GetKeyboardWindow()->Hide(); } -void KeyboardUI::EnsureCaretInWorkArea(const gfx::Rect& occluded_bounds) { - if (!GetInputMethod()) - return; - - TRACE_EVENT0("vk", "EnsureCaretInWorkArea"); - - if (keyboard_controller_->IsOverscrollAllowed()) { - GetInputMethod()->SetOnScreenKeyboardBounds(occluded_bounds); - } else if (GetInputMethod()->GetTextInputClient()) { - GetInputMethod()->GetTextInputClient()->EnsureCaretNotInRect( - occluded_bounds); - } -} - void KeyboardUI::SetController(KeyboardController* controller) { keyboard_controller_ = controller; } diff --git a/chromium/ui/keyboard/keyboard_ui.h b/chromium/ui/keyboard/keyboard_ui.h index 08fc72af382..a96fc780c49 100644 --- a/chromium/ui/keyboard/keyboard_ui.h +++ b/chromium/ui/keyboard/keyboard_ui.h @@ -52,15 +52,6 @@ class KEYBOARD_EXPORT KeyboardUI { // the visibility change. virtual void HideKeyboardWindow(); - // Ensures caret in current work area (not occluded by virtual keyboard - // window). - virtual void EnsureCaretInWorkArea(const gfx::Rect& occluded_bounds); - - // KeyboardController owns the KeyboardUI instance so KeyboardUI subclasses - // should not take ownership of the |controller|. |controller| can be null - // when KeyboardController is destroying. - virtual void SetController(KeyboardController* controller); - // Reloads virtual keyboard URL if the current keyboard's web content URL is // different. The URL can be different if user switch from password field to // any other type input field. @@ -79,6 +70,9 @@ class KEYBOARD_EXPORT KeyboardUI { // Resets insets for affected windows. virtual void ResetInsets() = 0; + // |controller| may be null when KeyboardController is being destroyed. + void SetController(KeyboardController* controller); + protected: KeyboardController* keyboard_controller() { return keyboard_controller_; } diff --git a/chromium/ui/keyboard/keyboard_util.cc b/chromium/ui/keyboard/keyboard_util.cc index 875c7d59eae..3e3020fcaab 100644 --- a/chromium/ui/keyboard/keyboard_util.cc +++ b/chromium/ui/keyboard/keyboard_util.cc @@ -7,28 +7,18 @@ #include <string> #include "base/command_line.h" -#include "base/feature_list.h" -#include "base/lazy_instance.h" -#include "base/logging.h" #include "base/metrics/histogram_macros.h" -#include "base/strings/string16.h" -#include "ui/aura/client/aura_constants.h" #include "ui/aura/window_tree_host.h" +#include "ui/base/ime/constants.h" #include "ui/base/ime/input_method.h" -#include "ui/base/ime/input_method_base.h" #include "ui/base/ime/text_input_client.h" -#include "ui/base/ime/text_input_flags.h" -#include "ui/base/ui_base_features.h" -#include "ui/base/ui_base_switches.h" #include "ui/events/event_sink.h" #include "ui/events/event_utils.h" #include "ui/events/keycodes/dom/dom_code.h" #include "ui/events/keycodes/dom/dom_key.h" #include "ui/events/keycodes/dom/keycode_converter.h" #include "ui/events/keycodes/keyboard_code_conversion.h" -#include "ui/keyboard/keyboard_controller.h" #include "ui/keyboard/keyboard_switches.h" -#include "ui/keyboard/keyboard_ui.h" namespace keyboard { @@ -47,43 +37,18 @@ void SendProcessKeyEvent(ui::EventType type, CHECK(!details.dispatcher_destroyed); } -bool g_keyboard_load_time_logged = false; -base::LazyInstance<base::Time>::DestructorAtExit g_keyboard_load_time_start = - LAZY_INSTANCE_INITIALIZER; - -struct keyboard::KeyboardConfig g_keyboard_config; - bool g_accessibility_keyboard_enabled = false; -bool g_hotrod_keyboard_enabled = false; - bool g_keyboard_enabled_from_shelf = false; bool g_touch_keyboard_enabled = false; KeyboardState g_requested_keyboard_state = KEYBOARD_STATE_AUTO; -KeyboardOverscrolOverride g_keyboard_overscroll_override = - KEYBOARD_OVERSCROLL_OVERRIDE_NONE; - KeyboardShowOverride g_keyboard_show_override = KEYBOARD_SHOW_OVERRIDE_NONE; } // namespace -bool UpdateKeyboardConfig(const KeyboardConfig& keyboard_config) { - if (g_keyboard_config == keyboard_config) - return false; - g_keyboard_config = keyboard_config; - auto* controller = KeyboardController::Get(); - if (controller->enabled()) - controller->NotifyKeyboardConfigChanged(); - return true; -} - -const KeyboardConfig& GetKeyboardConfig() { - return g_keyboard_config; -} - void SetAccessibilityKeyboardEnabled(bool enabled) { g_accessibility_keyboard_enabled = enabled; } @@ -92,14 +57,6 @@ bool GetAccessibilityKeyboardEnabled() { return g_accessibility_keyboard_enabled; } -void SetHotrodKeyboardEnabled(bool enabled) { - g_hotrod_keyboard_enabled = enabled; -} - -bool GetHotrodKeyboardEnabled() { - return g_hotrod_keyboard_enabled; -} - void SetKeyboardEnabledFromShelf(bool enabled) { g_keyboard_enabled_from_shelf = enabled; } @@ -120,10 +77,6 @@ void SetRequestedKeyboardState(KeyboardState state) { g_requested_keyboard_state = state; } -KeyboardState GetKeyboardRequestedState() { - return g_requested_keyboard_state; -} - std::string GetKeyboardLayout() { // TODO(bshe): layout string is currently hard coded. We should use more // standard keyboard layouts. @@ -155,97 +108,8 @@ bool IsKeyboardEnabled() { g_requested_keyboard_state == KEYBOARD_STATE_ENABLED; } -bool IsKeyboardVisible() { - auto* keyboard_controller = keyboard::KeyboardController::Get(); - return keyboard_controller->enabled() && - keyboard_controller->IsKeyboardVisible(); -} - -bool IsKeyboardOverscrollEnabled() { - if (!IsKeyboardEnabled()) - return false; - - // Users of the sticky accessibility on-screen keyboard are likely to be using - // mouse input, which may interfere with overscrolling. - if (keyboard::KeyboardController::Get()->enabled() && - !keyboard::KeyboardController::Get()->IsOverscrollAllowed()) - return false; - - // If overscroll enabled override is set, use it instead. Currently - // login / out-of-box disable keyboard overscroll. http://crbug.com/363635 - if (g_keyboard_overscroll_override != KEYBOARD_OVERSCROLL_OVERRIDE_NONE) { - return g_keyboard_overscroll_override == - KEYBOARD_OVERSCROLL_OVERRIDE_ENABLED; - } - - if (base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableVirtualKeyboardOverscroll)) { - return false; - } - return true; -} - -void SetKeyboardOverscrollOverride(KeyboardOverscrolOverride override) { - g_keyboard_overscroll_override = override; -} - -void SetKeyboardShowOverride(KeyboardShowOverride override) { - g_keyboard_show_override = override; -} - -bool IsInputViewEnabled() { - return !base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableInputView); -} - -bool IsExperimentalInputViewEnabled() { - return base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kEnableExperimentalInputViewFeatures); -} - -bool IsFloatingVirtualKeyboardEnabled() { - return base::FeatureList::IsEnabled(features::kEnableFloatingVirtualKeyboard); -} - -bool IsFullscreenHandwritingVirtualKeyboardEnabled() { - return base::FeatureList::IsEnabled( - features::kEnableFullscreenHandwritingVirtualKeyboard); -} - -bool IsStylusVirtualKeyboardEnabled() { - return base::FeatureList::IsEnabled(features::kEnableStylusVirtualKeyboard); -} - -bool IsVirtualKeyboardMdUiEnabled() { - return base::FeatureList::IsEnabled(features::kEnableVirtualKeyboardMdUi); -} - -bool IsGestureTypingEnabled() { - return !base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableGestureTyping); -} - -bool IsGestureEditingEnabled() { - return !base::CommandLine::ForCurrentProcess()->HasSwitch( - switches::kDisableGestureEditing); -} - -bool InsertText(const base::string16& text) { - auto* controller = KeyboardController::Get(); - if (!controller->enabled()) - return false; - - ui::InputMethod* input_method = controller->ui()->GetInputMethod(); - if (!input_method) - return false; - - ui::TextInputClient* tic = input_method->GetTextInputClient(); - if (!tic || tic->GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) - return false; - - tic->InsertText(text); - - return true; +void SetKeyboardShowOverride(KeyboardShowOverride show_override) { + g_keyboard_show_override = show_override; } bool SendKeyEvent(const std::string type, @@ -307,6 +171,12 @@ bool SendKeyEvent(const std::string type, code, dom_code, modifiers); + + // Marks the simulated key event is from the Virtual Keyboard. + ui::Event::Properties properties; + properties[ui::kPropertyFromVK] = std::vector<uint8_t>(); + event.SetProperties(properties); + ui::EventDispatchDetails details = host->event_sink()->OnEventFromSource(&event); CHECK(!details.dispatcher_destroyed); @@ -314,29 +184,4 @@ bool SendKeyEvent(const std::string type, return true; } -void MarkKeyboardLoadStarted() { - if (!g_keyboard_load_time_logged) - g_keyboard_load_time_start.Get() = base::Time::Now(); -} - -void MarkKeyboardLoadFinished() { - // Possible to get a load finished without a start if navigating directly to - // chrome://keyboard. - if (g_keyboard_load_time_start.Get().is_null()) - return; - - if (!g_keyboard_load_time_logged) { - // Log the delta only once. - UMA_HISTOGRAM_TIMES( - "VirtualKeyboard.InitLatency.FirstLoad", - base::Time::Now() - g_keyboard_load_time_start.Get()); - g_keyboard_load_time_logged = true; - } -} - -void LogKeyboardControlEvent(KeyboardControlEvent event) { - UMA_HISTOGRAM_ENUMERATION("VirtualKeyboard.KeyboardControlEvent", event, - KEYBOARD_CONTROL_MAX); -} - } // namespace keyboard diff --git a/chromium/ui/keyboard/keyboard_util.h b/chromium/ui/keyboard/keyboard_util.h index 44caaf4a205..740259d2967 100644 --- a/chromium/ui/keyboard/keyboard_util.h +++ b/chromium/ui/keyboard/keyboard_util.h @@ -10,47 +10,16 @@ #include "base/strings/string16.h" #include "ui/keyboard/keyboard_export.h" +// Global utility functions for the virtual keyboard. +// TODO(stevenjb/shuchen/shend): Many of these are accessed from both Chrome +// and Ash. We need to remove any Chrome dependencies. htpps://crbug.com/843332 + namespace aura { class WindowTreeHost; } namespace keyboard { -// For virtual keyboard IME extension. -struct KeyboardConfig { - bool auto_complete = true; - bool auto_correct = true; - bool auto_capitalize = true; - bool handwriting = true; - bool spell_check = true; - // It denotes the preferred value, and can be true even if there is no actual - // audio input device. - bool voice_input = true; - - bool operator==(const keyboard::KeyboardConfig& rhs) const { - return auto_complete == rhs.auto_complete && - auto_correct == rhs.auto_correct && - auto_capitalize == rhs.auto_capitalize && - handwriting == rhs.handwriting && spell_check == rhs.spell_check && - voice_input == rhs.voice_input; - } -}; - -// An enumeration of different keyboard control events that should be logged. -enum KeyboardControlEvent { - KEYBOARD_CONTROL_SHOW = 0, - KEYBOARD_CONTROL_HIDE_AUTO, - KEYBOARD_CONTROL_HIDE_USER, - KEYBOARD_CONTROL_MAX, -}; - -// An enumeration of keyboard overscroll override value. -enum KeyboardOverscrolOverride { - KEYBOARD_OVERSCROLL_OVERRIDE_DISABLED = 0, - KEYBOARD_OVERSCROLL_OVERRIDE_ENABLED, - KEYBOARD_OVERSCROLL_OVERRIDE_NONE, -}; - // An enumeration of keyboard policy settings. enum KeyboardShowOverride { KEYBOARD_SHOW_OVERRIDE_DISABLED = 0, @@ -68,14 +37,6 @@ enum KeyboardState { KEYBOARD_STATE_DISABLED, }; -// Updates the current keyboard config with the given config is they are -// different, notifying to observers. Returns whether update happened. -KEYBOARD_EXPORT bool UpdateKeyboardConfig( - const keyboard::KeyboardConfig& keyboard_config); - -// Gets the current virtual keyboard IME config. -KEYBOARD_EXPORT const keyboard::KeyboardConfig& GetKeyboardConfig(); - // Sets the state of the a11y onscreen keyboard. KEYBOARD_EXPORT void SetAccessibilityKeyboardEnabled(bool enabled); @@ -112,22 +73,9 @@ KEYBOARD_EXPORT std::string GetKeyboardLayout(); // Returns true if the virtual keyboard is enabled. KEYBOARD_EXPORT bool IsKeyboardEnabled(); -// Returns true if the virtual keyboard is currently visible. -KEYBOARD_EXPORT bool IsKeyboardVisible(); - -// Returns true if keyboard overscroll mode is enabled. -KEYBOARD_EXPORT bool IsKeyboardOverscrollEnabled(); - -// Sets temporary keyboard overscroll override. -KEYBOARD_EXPORT void SetKeyboardOverscrollOverride( - KeyboardOverscrolOverride override); - // Sets policy override on whether to show the keyboard. -KEYBOARD_EXPORT void SetKeyboardShowOverride(KeyboardShowOverride override); - -// Returns true if an IME extension can specify a custom input view for the -// virtual keyboard window. -KEYBOARD_EXPORT bool IsInputViewEnabled(); +KEYBOARD_EXPORT void SetKeyboardShowOverride( + KeyboardShowOverride show_override); // Sets whehther the keyboards is in restricted state - state where advanced // virtual keyboard features are disabled. @@ -136,36 +84,6 @@ KEYBOARD_EXPORT void SetKeyboardRestricted(bool restricted); // Returns whether the keyboard is in restricted state. KEYBOARD_EXPORT bool GetKeyboardRestricted(); -// Returns true if experimental features are enabled for IME input-views. -KEYBOARD_EXPORT bool IsExperimentalInputViewEnabled(); - -// Returns true if floating virtual keyboard feature is enabled. -KEYBOARD_EXPORT bool IsFloatingVirtualKeyboardEnabled(); - -// Returns true if fullscreen handwriting virtual keyboard feature is enabled. -KEYBOARD_EXPORT bool IsFullscreenHandwritingVirtualKeyboardEnabled(); - -// Returns true if stylus virtual keyboard feature is enabled. -KEYBOARD_EXPORT bool IsStylusVirtualKeyboardEnabled(); - -// Returns true if virtual keyboard md ui feature is enabled. -KEYBOARD_EXPORT bool IsVirtualKeyboardMdUiEnabled(); - -// Returns true if gesture typing option is enabled for virtual keyboard. -KEYBOARD_EXPORT bool IsGestureTypingEnabled(); - -// Returns true if gesture editing option is enabled for virtual keyboard. -KEYBOARD_EXPORT bool IsGestureEditingEnabled(); - -// Returns true if voice input is not disabled for the keyboard by the command -// line switch. It's up to the client to check if there is an input device -// available. -KEYBOARD_EXPORT bool IsVoiceInputEnabled(); - -// Insert |text| into the active TextInputClient if there is one. Returns true -// if |text| was successfully inserted. -KEYBOARD_EXPORT bool InsertText(const base::string16& text); - // Sends a fabricated key event, where |type| is the event type, |key_value| // is the unicode value of the character, |key_code| is the legacy key code // value, |key_name| is the name of the key as defined in the DOM3 key event @@ -179,18 +97,6 @@ KEYBOARD_EXPORT bool SendKeyEvent(std::string type, int modifiers, aura::WindowTreeHost* host); -// Marks that the keyboard load has started. This is used to measure the time it -// takes to fully load the keyboard. This should be called before -// MarkKeyboardLoadFinished. -KEYBOARD_EXPORT void MarkKeyboardLoadStarted(); - -// Marks that the keyboard load has ended. This finishes measuring that the -// keyboard is loaded. -KEYBOARD_EXPORT void MarkKeyboardLoadFinished(); - -// Logs the keyboard control event as a UMA stat. -void LogKeyboardControlEvent(KeyboardControlEvent event); - } // namespace keyboard #endif // UI_KEYBOARD_KEYBOARD_UTIL_H_ diff --git a/chromium/ui/keyboard/keyboard_util_unittest.cc b/chromium/ui/keyboard/keyboard_util_unittest.cc index ed5df410df1..5361b06259b 100644 --- a/chromium/ui/keyboard/keyboard_util_unittest.cc +++ b/chromium/ui/keyboard/keyboard_util_unittest.cc @@ -8,6 +8,7 @@ #include "testing/gtest/include/gtest/gtest.h" #include "ui/keyboard/keyboard_controller.h" #include "ui/keyboard/keyboard_ui.h" +#include "ui/keyboard/keyboard_util.h" #include "ui/keyboard/test/keyboard_test_util.h" namespace keyboard { @@ -125,33 +126,38 @@ TEST_F(KeyboardUtilTest, HideKeyboardWhenTouchEnabled) { TEST_F(KeyboardUtilTest, UpdateKeyboardConfig) { ResetAllFlags(); - keyboard::KeyboardConfig config = keyboard::GetKeyboardConfig(); + auto config = keyboard_controller_.keyboard_config(); EXPECT_TRUE(config.spell_check); - EXPECT_FALSE(keyboard::UpdateKeyboardConfig(config)); + EXPECT_FALSE(keyboard_controller_.UpdateKeyboardConfig(config)); config.spell_check = false; - EXPECT_TRUE(keyboard::UpdateKeyboardConfig(config)); - EXPECT_FALSE(keyboard::GetKeyboardConfig().spell_check); + EXPECT_TRUE(keyboard_controller_.UpdateKeyboardConfig(config)); + EXPECT_FALSE(keyboard_controller_.keyboard_config().spell_check); - EXPECT_FALSE(keyboard::UpdateKeyboardConfig(config)); + EXPECT_FALSE(keyboard_controller_.UpdateKeyboardConfig(config)); } TEST_F(KeyboardUtilTest, IsOverscrollEnabled) { ResetAllFlags(); // Return false when keyboard is disabled. - EXPECT_FALSE(keyboard::IsKeyboardOverscrollEnabled()); + EXPECT_FALSE(keyboard_controller_.IsKeyboardOverscrollEnabled()); // Enable the virtual keyboard. keyboard::SetTouchKeyboardEnabled(true); - EXPECT_TRUE(keyboard::IsKeyboardOverscrollEnabled()); + EXPECT_TRUE(keyboard_controller_.IsKeyboardOverscrollEnabled()); + + // Set overscroll enabled state. + auto config = keyboard::KeyboardController::Get()->keyboard_config(); + config.overscroll_behavior = + keyboard::mojom::KeyboardOverscrollBehavior::kDisabled; + keyboard::KeyboardController::Get()->UpdateKeyboardConfig(config); + EXPECT_FALSE(keyboard_controller_.IsKeyboardOverscrollEnabled()); - // Override overscroll enabled state. - keyboard::SetKeyboardOverscrollOverride( - KEYBOARD_OVERSCROLL_OVERRIDE_DISABLED); - EXPECT_FALSE(keyboard::IsKeyboardOverscrollEnabled()); - keyboard::SetKeyboardOverscrollOverride(KEYBOARD_OVERSCROLL_OVERRIDE_NONE); - EXPECT_TRUE(keyboard::IsKeyboardOverscrollEnabled()); + config.overscroll_behavior = + keyboard::mojom::KeyboardOverscrollBehavior::kDefault; + keyboard::KeyboardController::Get()->UpdateKeyboardConfig(config); + EXPECT_TRUE(keyboard_controller_.IsKeyboardOverscrollEnabled()); // Set keyboard_locked() to true. ui::DummyInputMethod input_method; @@ -159,7 +165,7 @@ TEST_F(KeyboardUtilTest, IsOverscrollEnabled) { std::make_unique<TestKeyboardUI>(&input_method), nullptr); keyboard_controller_.set_keyboard_locked(true); EXPECT_TRUE(keyboard_controller_.keyboard_locked()); - EXPECT_FALSE(keyboard::IsKeyboardOverscrollEnabled()); + EXPECT_FALSE(keyboard_controller_.IsKeyboardOverscrollEnabled()); keyboard_controller_.DisableKeyboard(); } diff --git a/chromium/ui/keyboard/public/OWNERS b/chromium/ui/keyboard/public/OWNERS new file mode 100644 index 00000000000..08850f42120 --- /dev/null +++ b/chromium/ui/keyboard/public/OWNERS @@ -0,0 +1,2 @@ +per-file *.mojom=set noparent +per-file *.mojom=file://ipc/SECURITY_OWNERS diff --git a/chromium/ui/keyboard/public/keyboard_config.mojom b/chromium/ui/keyboard/public/keyboard_config.mojom new file mode 100644 index 00000000000..1c37f572c43 --- /dev/null +++ b/chromium/ui/keyboard/public/keyboard_config.mojom @@ -0,0 +1,40 @@ +// 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. + +module keyboard.mojom; + +// Determines how the keyboard overscroll enabled state is set, +enum KeyboardOverscrollBehavior { + // Use the default behavior. + kDefault, + + // Enable keyboard overscroll if allowed. + kEnabled, + + // Do not enable keyboard overscroll. + kDisabled, +}; + +struct KeyboardConfig { + // Whether the virtual keyboard can provide auto-complete. + bool auto_complete = true; + + // Whether the virtual keyboard can provide auto-correct. + bool auto_correct = true; + + // Whether the virtual keyboard can provide auto-capitalization. + bool auto_capitalize = true; + + // Whether the virtual keyboard can provide input via handwriting recognition. + bool handwriting = true; + + // Whether the virtual keyboard can provide spell-check. + bool spell_check = true; + + // Whether the virtual keyboard can provide voice input. + bool voice_input = true; + + // Whether overscroll is currently allowed by the active keyboard container. + KeyboardOverscrollBehavior overscroll_behavior = kDefault; +}; diff --git a/chromium/ui/latency/frame_metrics.cc b/chromium/ui/latency/frame_metrics.cc index 7a1c5ed049a..27f26b9de71 100644 --- a/chromium/ui/latency/frame_metrics.cc +++ b/chromium/ui/latency/frame_metrics.cc @@ -8,6 +8,7 @@ #include <limits> #include <vector> +#include "base/bit_cast.h" #include "base/trace_event/trace_event.h" #include "base/trace_event/trace_event_argument.h" @@ -18,7 +19,7 @@ namespace { // How often to report results. // This needs to be short enough to avoid overflow in the accumulators. constexpr base::TimeDelta kDefaultReportPeriod = - base::TimeDelta::FromMinutes(1); + base::TimeDelta::FromSeconds(1); // Gives the histogram for skips the highest precision just above a // skipped:produced ratio of 1. @@ -308,10 +309,10 @@ void FrameMetrics::AddFrameDisplayed(base::TimeTicks source_timestamp, latency.InMicroseconds() * kFixedPointMultiplierLatency; latency_analyzer_.AddSample(CapValue(latency_value), kLatencySampleWeight); - // Only calculate velocity if there's enough history. - if (latencies_added_ >= 1) { - base::TimeDelta latency_delta = latency - latency_prev_; - base::TimeDelta source_duration = source_timestamp - source_timestamp_prev_; + base::TimeDelta latency_delta = latency - latency_prev_; + base::TimeDelta source_duration = source_timestamp - source_timestamp_prev_; + // Only calculate speed if there's enough history. + if (latencies_added_ >= 1 && settings_.is_frame_latency_speed_on()) { int64_t latency_velocity = (latency_delta * kFixedPointMultiplierLatencySpeed) / source_duration; @@ -319,35 +320,29 @@ void FrameMetrics::AddFrameDisplayed(base::TimeTicks source_timestamp, // entries to avoid overflow in the accumulators just in case. latency_speed_analyzer_.AddSample(CapValue(latency_velocity), CapDuration(source_duration)); + } - // Only calculate acceleration if there's enough history. - if (latencies_added_ >= 2) { - base::TimeDelta source_duration_average = - (source_duration + source_duration_prev_) / 2; - int64_t latency_acceleration = - (((latency_delta * kFixedPointMultiplierLatencyAcceleration) / - source_duration) - - ((latency_delta_prev_ * kFixedPointMultiplierLatencyAcceleration) / - source_duration_prev_)) / - source_duration_average.InMicroseconds(); - latency_acceleration_analyzer_.AddSample( - CapValue(latency_acceleration), CapDuration(source_duration_average)); - } - - // Update history. + // Only calculate acceleration if there's enough history. + if (latencies_added_ >= 2 && settings_.is_frame_latency_acceleration_on()) { + base::TimeDelta source_duration_average = + (source_duration + source_duration_prev_) / 2; + int64_t latency_acceleration = + (((latency_delta * kFixedPointMultiplierLatencyAcceleration) / + source_duration) - + ((latency_delta_prev_ * kFixedPointMultiplierLatencyAcceleration) / + source_duration_prev_)) / + source_duration_average.InMicroseconds(); + latency_acceleration_analyzer_.AddSample( + CapValue(latency_acceleration), CapDuration(source_duration_average)); + } + // Update history. + if (latencies_added_ >= 1) { source_duration_prev_ = source_duration; latency_delta_prev_ = latency_delta; } - - // Update history. source_timestamp_prev_ = source_timestamp; latency_prev_ = latency; latencies_added_++; - - bool tracing_enabled = 0; - TRACE_EVENT_CATEGORY_GROUP_ENABLED(kTraceCategories, &tracing_enabled); - if (tracing_enabled) - TraceStats(); } void FrameMetrics::Reset() { @@ -366,8 +361,10 @@ void FrameMetrics::Reset() { frame_skips_analyzer_.Reset(); latency_analyzer_.Reset(); - latency_speed_analyzer_.Reset(); - latency_acceleration_analyzer_.Reset(); + if (settings_.is_frame_latency_speed_on()) + latency_speed_analyzer_.Reset(); + if (settings_.is_frame_latency_acceleration_on()) + latency_acceleration_analyzer_.Reset(); } // Reset analyzers, but don't reset resent latency history so we can get @@ -379,6 +376,11 @@ void FrameMetrics::Reset() { void FrameMetrics::StartNewReportPeriod() { TRACE_EVENT0(kTraceCategories, "FrameMetrics::StartNewReportPeriod"); + bool tracing_enabled = 0; + TRACE_EVENT_CATEGORY_GROUP_ENABLED(kTraceCategories, &tracing_enabled); + if (tracing_enabled) + TraceStats(); + time_since_start_of_report_period_ = base::TimeDelta(); frames_produced_since_start_of_report_period_ = 0; @@ -388,6 +390,27 @@ void FrameMetrics::StartNewReportPeriod() { latency_acceleration_analyzer_.StartNewReportPeriod(); } +double FrameMetrics::FastApproximateSqrt(double x) { + if (x <= 0) + return 0; + // Basically performs x*fastinvSqrt(x) - using high precision (3 steps) + double y = x; + double xhalf = 0.5f * x; + float xf = static_cast<float>(x); + int32_t i = bit_cast<int32_t>(xf); + // Magic Number for initial guess. Reference: + // http://www.lomont.org/Math/Papers/2003/InvSqrt.pdf. + i = 0x5f3759df - (i >> 1); + x = static_cast<double>(bit_cast<float>(i)); + // Newton step. + x = x * (1.5 - xhalf * x * x); + // Newton step. + x = x * (1.5 - xhalf * x * x); + // Newton step. + x = x * (1.5 - xhalf * x * x); + return y * x; +} + namespace { // FrameMetricsTraceData delegates tracing logic to TracedValue in a deferred @@ -417,13 +440,17 @@ class FrameMetricsTraceData latency.AsValueInto(&state); state.EndDictionary(); - state.BeginDictionary("Speed"); - speed.AsValueInto(&state); - state.EndDictionary(); + if (settings.is_frame_latency_speed_on()) { + state.BeginDictionary("Speed"); + speed.AsValueInto(&state); + state.EndDictionary(); + } - state.BeginDictionary("Acceleration"); - acceleration.AsValueInto(&state); - state.EndDictionary(); + if (settings.is_frame_latency_acceleration_on()) { + state.BeginDictionary("Acceleration"); + acceleration.AsValueInto(&state); + state.EndDictionary(); + } state.AppendAsTraceFormat(out); } @@ -447,8 +474,10 @@ void FrameMetrics::TraceStats() const { trace_data->settings = settings_; frame_skips_analyzer_.ComputeSummary(&trace_data->skips); latency_analyzer_.ComputeSummary(&trace_data->latency); - latency_speed_analyzer_.ComputeSummary(&trace_data->speed); - latency_acceleration_analyzer_.ComputeSummary(&trace_data->acceleration); + if (settings_.is_frame_latency_speed_on()) + latency_speed_analyzer_.ComputeSummary(&trace_data->speed); + if (settings_.is_frame_latency_acceleration_on()) + latency_acceleration_analyzer_.ComputeSummary(&trace_data->acceleration); } TRACE_EVENT_INSTANT1(kTraceCategories, "FrameMetrics", TRACE_EVENT_SCOPE_THREAD, "Info", std::move(trace_data)); diff --git a/chromium/ui/latency/frame_metrics.h b/chromium/ui/latency/frame_metrics.h index d680541c26b..d0f6f351d6b 100644 --- a/chromium/ui/latency/frame_metrics.h +++ b/chromium/ui/latency/frame_metrics.h @@ -73,6 +73,18 @@ struct FrameMetricsSettings { trace_results_every_frame(trace_results_every_frame), max_window_size(max_window_size) {} + void set_is_frame_latency_speed_on(bool is_speed_on) { + is_frame_latency_speed_on_ = is_speed_on; + } + void set_is_frame_latency_acceleration_on(bool is_acceleration_on) { + is_frame_latency_acceleration_on_ = is_acceleration_on; + } + + bool is_frame_latency_speed_on() const { return is_frame_latency_speed_on_; } + bool is_frame_latency_acceleration_on() const { + return is_frame_latency_acceleration_on_; + } + // Source configuration. FrameMetricsSource source; FrameMetricsSourceThread source_thread; @@ -86,6 +98,13 @@ struct FrameMetricsSettings { size_t max_window_size; void AsValueInto(base::trace_event::TracedValue* state) const; + + private: + // Switch for frame latency speed measurements control. + bool is_frame_latency_speed_on_ = false; + + // Switch for frame latency acceleration measurements control. + bool is_frame_latency_acceleration_on_ = false; }; // Calculates all metrics for a frame source. @@ -103,15 +122,22 @@ class FrameMetrics : public SkippedFrameTracker::Client { void Reset(); // AddFrameProduced should be called every time a source produces a frame. - // The information added here affects the number of frames skipped. - // Note: If the FrameMetrics class is hooked up to an optional - // SkippedFrameTracker, the client should not call this directly. + // |source_timestamp| is when frame time in BeginFrameArgs(i.e. when the frame + // is produced); |amount_produced| is the expected time interval between 2 + // consecutive frames; |amount_skipped| is number of frame skipped before + // producing this frame multiplies by the interval, i.e., if 1 frame is + // skipped in 30 fps setting, then |amount_skipped| is 33.33ms; if 1 frame is + // skipped in 60FPS setting, then the |amount_skipped| is 16.67ms. Note: If + // the FrameMetrics class is hooked up to an optional SkippedFrameTracker, the + // client should not call this directly. void AddFrameProduced(base::TimeTicks source_timestamp, base::TimeDelta amount_produced, base::TimeDelta amount_skipped) override; // AddFrameDisplayed should be called whenever a frame causes damage and - // we know when the result became visible on the display. + // we know when the result became visible on the display. |source_timestamp| + // is when frame time in BeginFrameArgs(i.e. when the frame is produced); + // |display_timestamp| is when the frame is displayed on screen. // This will affect all latency derived metrics, including latency speed, // latency acceleration, and latency itself. // If a frame is produced but not displayed, do not call this; there was @@ -120,17 +146,25 @@ class FrameMetrics : public SkippedFrameTracker::Client { void AddFrameDisplayed(base::TimeTicks source_timestamp, base::TimeTicks display_timestamp); + // Compute the square root by using method described in paper: + // http://www.lomont.org/Math/Papers/2003/InvSqrt.pdf. + // It finds a result within 0.0001 and 0.1 of the true square root for |x| < + // 100 and |x| < 2^15 respectively. It's more than 2 times faster for Nexus 4 + // and other lower end android devices and ~3-5% faster on desktop. Crash when + // x is less than 0. + static double FastApproximateSqrt(double x); + protected: void TraceStats() const; // virtual for testing. virtual base::TimeDelta ReportPeriod(); - // Starts a new reporting period that resets the various accumulators - // and memory of worst regions encountered, but does not destroy recent - // sample history in the windowed analyzers and in the derivatives - // for latency speed and latency acceleration. This avoids small gaps - // in coverage when starting a new reporting period. + // Starts a new reporting period after |kDefaultReportPeriod| time that resets + // the various accumulators and memory of worst regions encountered, but does + // not destroy recent sample history in the windowed analyzers and in the + // derivatives for latency speed and latency acceleration. This avoids small + // gaps in coverage when starting a new reporting period. void StartNewReportPeriod(); FrameMetricsSettings settings_; diff --git a/chromium/ui/latency/frame_metrics_test_common.h b/chromium/ui/latency/frame_metrics_test_common.h index c48e81452b4..5ee17c4c89b 100644 --- a/chromium/ui/latency/frame_metrics_test_common.h +++ b/chromium/ui/latency/frame_metrics_test_common.h @@ -15,10 +15,8 @@ // Some convenience macros for checking expected error. #define EXPECT_ABS_LT(a, b) EXPECT_LT(std::abs(a), std::abs(b)) #define EXPECT_ABS_LE(a, b) EXPECT_LE(std::abs(a), std::abs(b)) -#define EXPECT_NEAR_SMR(expected, actual, weight) \ - EXPECT_NEAR(expected, actual, MaxErrorSMR(expected, weight)) -#define EXPECT_NEAR_VARIANCE_OF_ROOT(expected, actual, mean, weight) \ - EXPECT_NEAR(expected, actual, MaxErrorSMR(mean, weight)); +#define EXPECT_NEAR_SQRT_APPROX(expected, actual) \ + EXPECT_NEAR(expected, actual, MaxErrorSQRTApprox(expected, actual)) namespace ui { namespace frame_metrics { @@ -76,21 +74,6 @@ void AddPatternHelper(SharedWindowedAnalyzerClient* shared_client, } } -// Same as AddPatternHelper, but uses each value (+1) as its own weight. -// The "Cubed" name comes from the fact that the squared_accumulator -// for the RMS will effectively be a "cubed accumulator". -template <typename AnalyzerType> -void AddCubedPatternHelper(SharedWindowedAnalyzerClient* shared_client, - AnalyzerType* analyzer, - const std::vector<uint32_t>& values) { - for (auto i : values) { - shared_client->window_begin += base::TimeDelta::FromMicroseconds(1); - shared_client->window_end += base::TimeDelta::FromMicroseconds(1); - // weight is i+1 to avoid divide by zero. - AddSamplesHelper(analyzer, i, i + 1, 1); - } -} - // Mean and RMS can be exact for most values, however SMR loses a bit of // precision internally when accumulating the roots. Make sure the SMR // precision is at least within .5 (i.e. rounded to the nearest integer @@ -99,8 +82,8 @@ void AddCubedPatternHelper(SharedWindowedAnalyzerClient* shared_client, // between ~5 and ~13 decimal places. // The precision should be even better when the sample's |weight| > 1 since // the implementation should only do any rounding after scaling by weight. -inline double MaxErrorSMR(double expected_value, uint64_t weight) { - return std::max(.5, 1e-8 * expected_value / weight); +inline double MaxErrorSQRTApprox(double expected_value, double value) { + return std::max(0.5, std::max(expected_value, value) * 1e-3); } // This class initializes the ratio boundaries on construction in a way that diff --git a/chromium/ui/latency/frame_metrics_unittest.cc b/chromium/ui/latency/frame_metrics_unittest.cc index dd142de6571..55f43a7aecf 100644 --- a/chromium/ui/latency/frame_metrics_unittest.cc +++ b/chromium/ui/latency/frame_metrics_unittest.cc @@ -5,6 +5,7 @@ #include "ui/latency/frame_metrics.h" #include "base/bind.h" +#include "base/rand_util.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/latency/frame_metrics_test_common.h" @@ -131,7 +132,10 @@ class FrameMetricsTest : public testing::Test { FrameMetricsTest() : settings(ui::FrameMetricsSource::UnitTest, ui::FrameMetricsSourceThread::Unknown, - ui::FrameMetricsCompileTarget::Unknown) {} + ui::FrameMetricsCompileTarget::Unknown) { + settings.set_is_frame_latency_speed_on(true); + settings.set_is_frame_latency_acceleration_on(true); + } void SetUp() override { // Make sure we don't get an unexpected call to StartNewReportPeriod. @@ -237,15 +241,13 @@ TEST_F(FrameMetricsTest, PerfectLatencyScores) { TestStreamAnalysis r = LatencyAnalysis(); EXPECT_DOUBLE_EQ(latency.InSecondsF(), r.mean); - EXPECT_DOUBLE_EQ(latency.InSecondsF(), r.rms); - EXPECT_NEAR_SMR(r.smr, latency.InSecondsF(), produced.InMicroseconds()); + EXPECT_NEAR_SQRT_APPROX(latency.InSecondsF(), r.rms); + EXPECT_NEAR_SQRT_APPROX(r.smr, latency.InSecondsF()); EXPECT_EQ(0, r.std_dev); - EXPECT_NEAR_VARIANCE_OF_ROOT(0, r.variance_of_roots, 0, - produced.InMicroseconds()); + EXPECT_NEAR_SQRT_APPROX(0, r.variance_of_roots); EXPECT_DOUBLE_EQ(latency.InSecondsF(), r.worst_mean.value); - EXPECT_DOUBLE_EQ(latency.InSecondsF(), r.worst_rms.value); - EXPECT_NEAR_SMR(r.worst_smr.value, latency.InSecondsF(), - produced.InMicroseconds()); + EXPECT_NEAR_SQRT_APPROX(latency.InSecondsF(), r.worst_rms.value); + EXPECT_NEAR_SQRT_APPROX(r.worst_smr.value, latency.InSecondsF()); } // Apply a saw tooth pattern to the frame skips with values that are easy to @@ -273,8 +275,8 @@ TEST_F(FrameMetricsTest, SawToothShapedSkips) { const double expected_skip_to_produce_rms = std::sqrt(expected_skip_to_produce_mean_square); const double expected_skip_rms = SkipTransform(expected_skip_to_produce_rms); - EXPECT_EQ(expected_skip_rms, r.rms); - EXPECT_EQ(expected_skip_rms, r.worst_rms.value); + EXPECT_NEAR_SQRT_APPROX(expected_skip_rms, r.rms); + EXPECT_NEAR_SQRT_APPROX(expected_skip_rms, r.worst_rms.value); const double expected_expected_skip_to_produce_mean_root = (0 + 1.0) / 2; const double expected_expected_skip_to_produce_smr = @@ -282,13 +284,13 @@ TEST_F(FrameMetricsTest, SawToothShapedSkips) { expected_expected_skip_to_produce_mean_root; const double expected_skip_smr = SkipTransform(expected_expected_skip_to_produce_smr); - EXPECT_EQ(expected_skip_smr, r.smr); - EXPECT_EQ(expected_skip_smr, r.worst_smr.value); + EXPECT_NEAR_SQRT_APPROX(expected_skip_smr, r.smr); + EXPECT_NEAR_SQRT_APPROX(expected_skip_smr, r.worst_smr.value); const double expected_skip_to_produce_std_dev = (0.5 + 0.5) / 2; const double expected_skip_std_dev = SkipTransform(expected_skip_to_produce_std_dev); - EXPECT_EQ(expected_skip_std_dev, r.std_dev); + EXPECT_NEAR_SQRT_APPROX(expected_skip_std_dev, r.std_dev); const double expected_skip_to_produce_std_dev_of_roots = (0.5 + 0.5) / 2; const double expected_skip_to_produce_variance_of_roots = @@ -296,7 +298,7 @@ TEST_F(FrameMetricsTest, SawToothShapedSkips) { expected_skip_to_produce_std_dev_of_roots; const double expected_skip_variance_of_roots = SkipTransform(expected_skip_to_produce_variance_of_roots); - EXPECT_EQ(expected_skip_variance_of_roots, r.variance_of_roots); + EXPECT_NEAR_SQRT_APPROX(expected_skip_variance_of_roots, r.variance_of_roots); } // Apply a saw tooth pattern to the latency with values that are easy to @@ -319,34 +321,35 @@ TEST_F(FrameMetricsTest, SawToothShapedLatency) { const double expected_latency_mean_square = (100.0 * 100 + 36 * 36) / 2; const double expected_latency_rms = std::sqrt(expected_latency_mean_square); - EXPECT_DOUBLE_EQ(expected_latency_rms, r.rms); - EXPECT_DOUBLE_EQ(expected_latency_rms, r.worst_rms.value); + EXPECT_NEAR_SQRT_APPROX(expected_latency_rms, r.rms); + EXPECT_NEAR_SQRT_APPROX(expected_latency_rms, r.worst_rms.value); const double expected_latency_mean_root = (10.0 + 6) / 2; const double expected_latency_smr = expected_latency_mean_root * expected_latency_mean_root; - EXPECT_DOUBLE_EQ(expected_latency_smr, r.smr); - EXPECT_DOUBLE_EQ(expected_latency_smr, r.worst_smr.value); + EXPECT_NEAR_SQRT_APPROX(expected_latency_smr, r.smr); + EXPECT_NEAR_SQRT_APPROX(expected_latency_smr, r.worst_smr.value); const double expected_latency_std_dev = (100.0 - 36) / 2; - EXPECT_DOUBLE_EQ(expected_latency_std_dev, r.std_dev); + EXPECT_NEAR_SQRT_APPROX(expected_latency_std_dev, r.std_dev); const double expected_latency_std_dev_of_roots = (10.0 - 6) / 2; const double expected_latency_variance_of_roots = expected_latency_std_dev_of_roots * expected_latency_std_dev_of_roots; - EXPECT_DOUBLE_EQ(expected_latency_variance_of_roots, r.variance_of_roots); + EXPECT_NEAR_SQRT_APPROX(expected_latency_variance_of_roots, + r.variance_of_roots); // Verify latency speed, where mean, RMS, SMR, etc. should be equal. r = SpeedAnalysis(); const double expected_speed = 64; EXPECT_DOUBLE_EQ(expected_speed, r.mean); - EXPECT_DOUBLE_EQ(expected_speed, r.rms); - EXPECT_DOUBLE_EQ(expected_speed, r.smr); + EXPECT_NEAR_SQRT_APPROX(expected_speed, r.rms); + EXPECT_NEAR_SQRT_APPROX(expected_speed, r.smr); EXPECT_DOUBLE_EQ(0, r.std_dev); - EXPECT_DOUBLE_EQ(0, r.variance_of_roots); + EXPECT_NEAR_SQRT_APPROX(0, r.variance_of_roots); EXPECT_DOUBLE_EQ(expected_speed, r.worst_mean.value); - EXPECT_DOUBLE_EQ(expected_speed, r.worst_rms.value); - EXPECT_DOUBLE_EQ(expected_speed, r.worst_smr.value); + EXPECT_NEAR_SQRT_APPROX(expected_speed, r.worst_rms.value); + EXPECT_NEAR_SQRT_APPROX(expected_speed, r.worst_smr.value); // Verify latency accelleration, where mean, RMS, SMR, etc. should be equal. // The slack is relatively large since the frame durations are so long, which @@ -839,9 +842,9 @@ void FrameMetricsTest::StartNewReportPeriodAvoidsOverflowTest( TEST_F(FrameMetricsTest, StartNewReportPeriodAvoidsOverflowForSkips) { base::TimeDelta produced = base::TimeDelta::FromMicroseconds(1); base::TimeDelta latency = base::TimeDelta::FromMilliseconds(1); - base::TimeDelta skipped = base::TimeDelta::FromSeconds(66); + base::TimeDelta skipped = base::TimeDelta::FromSeconds(2); - frame_metrics->UseDefaultReportPeriodScaled(4); + frame_metrics->UseDefaultReportPeriodScaled(7); StartNewReportPeriodAvoidsOverflowTest(produced, skipped, latency, latency, kSkipSaturationMin, &FrameMetricsTest::SkipAnalysis); @@ -886,6 +889,30 @@ TEST_F(FrameMetricsTest, StartNewReportPeriodAvoidsOverflowForAcceleration) { &FrameMetricsTest::AccelerationAnalysis); } +// Test the accuracy of the Newton's approximate square root calculation. +// Since suqare_rooot is always used on small numbers in cc, this test only test +// accuracy of small |x| value. A random number |x| between (0 - 100) is +// generated, Test if the difference of square roots obtained from +// FastApproximateSqrt and std::sqrt is less than |error_rage| (0.0001); +TEST_F(FrameMetricsTest, SquareRootApproximation) { + const double slack = 0.001; + for (int i = 0; i < 3; i++) { + int x = base::RandInt(0, 100); + double sol1 = std::sqrt(x); + double sol2 = FrameMetrics::FastApproximateSqrt(x); + EXPECT_NEAR(sol1, sol2, slack) + << "failed to give a good approximate square root of " << x; + } + + for (int i = 0; i < 3; i++) { + double x = double{base::RandUint64()} / base::RandomBitGenerator::max(); + double sol1 = std::sqrt(x); + double sol2 = FrameMetrics::FastApproximateSqrt(x); + EXPECT_NEAR(sol1, sol2, slack) + << "failed to give a good approximate square root of " << x; + } +} + } // namespace } // namespace frame_metrics } // namespace ui diff --git a/chromium/ui/latency/latency_info.cc b/chromium/ui/latency/latency_info.cc index 089a3a9527f..aa09366e00f 100644 --- a/chromium/ui/latency/latency_info.cc +++ b/chromium/ui/latency/latency_info.cc @@ -283,7 +283,7 @@ void LatencyInfo::AddLatencyNumberWithTimestampImpl( "trace_id", trace_id_); } - LatencyMap::iterator it = latency_components_.find(component); + auto it = latency_components_.find(component); DCHECK(it == latency_components_.end()); latency_components_[component] = time; @@ -327,7 +327,7 @@ LatencyInfo::AsTraceableData() { bool LatencyInfo::FindLatency(LatencyComponentType type, base::TimeTicks* output) const { - LatencyMap::const_iterator it = latency_components_.find(type); + auto it = latency_components_.find(type); if (it == latency_components_.end()) return false; if (output) diff --git a/chromium/ui/latency/latency_tracker.cc b/chromium/ui/latency/latency_tracker.cc index 35c320bb5f4..ef6ce7210cd 100644 --- a/chromium/ui/latency/latency_tracker.cc +++ b/chromium/ui/latency/latency_tracker.cc @@ -322,26 +322,31 @@ void LatencyTracker::ComputeEndToEndLatencyHistograms( } base::TimeTicks renderer_swap_timestamp; - bool found_component = + bool found_renderer_swap_component = latency.FindLatency(ui::INPUT_EVENT_LATENCY_RENDERER_SWAP_COMPONENT, &renderer_swap_timestamp); - DCHECK_AND_RETURN_ON_FAIL(found_component); - - UMA_HISTOGRAM_SCROLL_LATENCY_LONG_2( - "Event.Latency." + scroll_name + "." + input_modality + - ".HandledToRendererSwap2_" + thread_name, - rendering_scheduled_timestamp, renderer_swap_timestamp); base::TimeTicks browser_received_swap_timestamp; - found_component = + bool found_received_frame_component = latency.FindLatency(ui::DISPLAY_COMPOSITOR_RECEIVED_FRAME_COMPONENT, &browser_received_swap_timestamp); - DCHECK_AND_RETURN_ON_FAIL(found_component); + DCHECK_AND_RETURN_ON_FAIL(found_received_frame_component); - UMA_HISTOGRAM_SCROLL_LATENCY_SHORT_2( - "Event.Latency." + scroll_name + "." + input_modality + - ".RendererSwapToBrowserNotified2", - renderer_swap_timestamp, browser_received_swap_timestamp); + // If we're committing to the active tree, there will never be a renderer + // swap. In this case, don't record the two histogram values for the periods + // surrounding the renderer swap. We could assign the total time to one or the + // other of them, but that would likely skew statistics. + if (found_renderer_swap_component) { + UMA_HISTOGRAM_SCROLL_LATENCY_LONG_2( + "Event.Latency." + scroll_name + "." + input_modality + + ".HandledToRendererSwap2_" + thread_name, + rendering_scheduled_timestamp, renderer_swap_timestamp); + + UMA_HISTOGRAM_SCROLL_LATENCY_SHORT_2( + "Event.Latency." + scroll_name + "." + input_modality + + ".RendererSwapToBrowserNotified2", + renderer_swap_timestamp, browser_received_swap_timestamp); + } UMA_HISTOGRAM_SCROLL_LATENCY_LONG_2( "Event.Latency." + scroll_name + "." + input_modality + diff --git a/chromium/ui/latency/mojo/BUILD.gn b/chromium/ui/latency/mojo/BUILD.gn index e8e2ba300ca..17c27b18f64 100644 --- a/chromium/ui/latency/mojo/BUILD.gn +++ b/chromium/ui/latency/mojo/BUILD.gn @@ -11,7 +11,6 @@ mojom("interfaces") { public_deps = [ "//mojo/public/mojom/base", - "//ui/gfx/geometry/mojo", ] } diff --git a/chromium/ui/latency/mojo/DEPS b/chromium/ui/latency/mojo/DEPS index 77a276283fe..733587edbfe 100644 --- a/chromium/ui/latency/mojo/DEPS +++ b/chromium/ui/latency/mojo/DEPS @@ -1,5 +1,4 @@ include_rules = [ "+mojo/public", - "+ui/gfx/geometry/mojo", "+ui/latency", ] diff --git a/chromium/ui/latency/mojo/latency_info.mojom b/chromium/ui/latency/mojo/latency_info.mojom index a39a8ed7a3c..ee082c716b5 100644 --- a/chromium/ui/latency/mojo/latency_info.mojom +++ b/chromium/ui/latency/mojo/latency_info.mojom @@ -5,7 +5,6 @@ module ui.mojom; import "mojo/public/mojom/base/time.mojom"; -import "ui/gfx/geometry/mojo/geometry.mojom"; enum LatencyComponentType { // ---------------------------BEGIN COMPONENT------------------------------- diff --git a/chromium/ui/latency/mojo/latency_info.typemap b/chromium/ui/latency/mojo/latency_info.typemap index 53c9bf68ed7..4450bfe7530 100644 --- a/chromium/ui/latency/mojo/latency_info.typemap +++ b/chromium/ui/latency/mojo/latency_info.typemap @@ -13,7 +13,6 @@ sources = [ "latency_info_struct_traits.h", ] public_deps = [ - "//ui/gfx/geometry/mojo:struct_traits", "//ui/latency", ] deps = [ diff --git a/chromium/ui/latency/mojo/latency_info_struct_traits.h b/chromium/ui/latency/mojo/latency_info_struct_traits.h index e668e4dbca1..00ab6f2069f 100644 --- a/chromium/ui/latency/mojo/latency_info_struct_traits.h +++ b/chromium/ui/latency/mojo/latency_info_struct_traits.h @@ -5,7 +5,6 @@ #ifndef UI_LATENCY_MOJO_LATENCY_INFO_STRUCT_TRAITS_H_ #define UI_LATENCY_MOJO_LATENCY_INFO_STRUCT_TRAITS_H_ -#include "ui/gfx/geometry/mojo/geometry_struct_traits.h" #include "ui/latency/latency_info.h" #include "ui/latency/mojo/latency_info.mojom-shared.h" diff --git a/chromium/ui/latency/stream_analyzer.cc b/chromium/ui/latency/stream_analyzer.cc index 0453e27570f..2623e46adf0 100644 --- a/chromium/ui/latency/stream_analyzer.cc +++ b/chromium/ui/latency/stream_analyzer.cc @@ -4,6 +4,8 @@ #include "ui/latency/stream_analyzer.h" +#include "ui/latency/frame_metrics.h" + namespace ui { StreamAnalysis::StreamAnalysis() = default; @@ -89,8 +91,9 @@ void StreamAnalyzer::AddSample(const uint32_t value, const uint32_t weight) { DCHECK_GT(weight, 0u); const uint64_t weighted_value = static_cast<uint64_t>(weight) * value; - const uint64_t weighted_root = weight * std::sqrt(static_cast<double>(value) * - kFixedPointRootMultiplier); + const uint64_t weighted_root = + weight * FrameMetrics::FastApproximateSqrt(static_cast<double>(value) * + kFixedPointRootMultiplier); const Accumulator96b weighted_square(value, weight); // Verify overflow isn't an issue. @@ -128,7 +131,7 @@ double StreamAnalyzer::ComputeMean() const { double StreamAnalyzer::ComputeRMS() const { double mean_square = square_accumulator_.ToDouble() / total_weight_; - double result = std::sqrt(mean_square); + double result = FrameMetrics::FastApproximateSqrt(mean_square); return client_->TransformResult(result); } @@ -152,7 +155,7 @@ double StreamAnalyzer::VarianceHelper(double accum, double square_accum) const { double StreamAnalyzer::ComputeStdDev() const { double variance = VarianceHelper(accumulator_, square_accumulator_.ToDouble()); - double std_dev = std::sqrt(variance); + double std_dev = FrameMetrics::FrameMetrics::FastApproximateSqrt(variance); return client_->TransformResult(std_dev); } diff --git a/chromium/ui/latency/stream_analyzer_unittest.cc b/chromium/ui/latency/stream_analyzer_unittest.cc index 08410961907..5aca77c7610 100644 --- a/chromium/ui/latency/stream_analyzer_unittest.cc +++ b/chromium/ui/latency/stream_analyzer_unittest.cc @@ -44,6 +44,7 @@ class StreamAnalyzerTest : public testing::Test { }; TEST_F(StreamAnalyzerTest, AllResultsTheSame) { + const double approx_sqrt_error = 0.0001; // Try adding a single sample vs. multiple samples. for (size_t samples : {1u, 100u}) { // A power of 2 sweep for both the value and weight dimensions. @@ -57,17 +58,18 @@ TEST_F(StreamAnalyzerTest, AllResultsTheSame) { uint64_t expected_value = value * TestStreamAnalyzerClient::result_scale; EXPECT_EQ(expected_value, analyzer_->ComputeMean()); - EXPECT_EQ(expected_value, analyzer_->ComputeRMS()); - EXPECT_NEAR_SMR(analyzer_->ComputeSMR(), expected_value, weight); - EXPECT_DOUBLE_EQ(0, analyzer_->ComputeStdDev()); - EXPECT_NEAR_VARIANCE_OF_ROOT(0, analyzer_->ComputeVarianceOfRoots(), - expected_value, weight); + EXPECT_NEAR_SQRT_APPROX(expected_value, analyzer_->ComputeRMS()); + EXPECT_NEAR_SQRT_APPROX(analyzer_->ComputeSMR(), expected_value); + EXPECT_NEAR_SQRT_APPROX(0, analyzer_->ComputeStdDev()); + EXPECT_NEAR(0, analyzer_->ComputeVarianceOfRoots(), + approx_sqrt_error * value); // Verify values are forwarded to the WindowedAnalyzer. EXPECT_EQ(expected_value, analyzer_->window().ComputeWorstMean().value); - EXPECT_EQ(expected_value, analyzer_->window().ComputeWorstRMS().value); - EXPECT_NEAR_SMR(expected_value, - analyzer_->window().ComputeWorstSMR().value, weight); + EXPECT_NEAR_SQRT_APPROX(expected_value, + analyzer_->window().ComputeWorstRMS().value); + EXPECT_NEAR_SQRT_APPROX(expected_value, + analyzer_->window().ComputeWorstSMR().value); } } } @@ -83,17 +85,18 @@ TEST_F(StreamAnalyzerTest, AllResultsTheSame) { uint64_t expected_value = value * TestStreamAnalyzerClient::result_scale; // Makes sure our precision is good enough. EXPECT_EQ(expected_value, analyzer_->ComputeMean()); - EXPECT_EQ(expected_value, analyzer_->ComputeRMS()); - EXPECT_NEAR_SMR(expected_value, analyzer_->ComputeSMR(), weight); - EXPECT_DOUBLE_EQ(0, analyzer_->ComputeStdDev()); - EXPECT_NEAR_VARIANCE_OF_ROOT(0, analyzer_->ComputeVarianceOfRoots(), - expected_value, weight); + EXPECT_NEAR_SQRT_APPROX(expected_value, analyzer_->ComputeRMS()); + EXPECT_NEAR_SQRT_APPROX(expected_value, analyzer_->ComputeSMR()); + EXPECT_NEAR_SQRT_APPROX(0, analyzer_->ComputeStdDev()); + EXPECT_NEAR(0, analyzer_->ComputeVarianceOfRoots(), + approx_sqrt_error * value); // Verify values are forwarded to the WindowedAnalyzer. EXPECT_EQ(expected_value, analyzer_->window().ComputeWorstMean().value); - EXPECT_EQ(expected_value, analyzer_->window().ComputeWorstRMS().value); - EXPECT_NEAR_SMR(expected_value, - analyzer_->window().ComputeWorstSMR().value, weight); + EXPECT_NEAR_SQRT_APPROX(expected_value, + analyzer_->window().ComputeWorstRMS().value); + EXPECT_NEAR_SQRT_APPROX(expected_value, + analyzer_->window().ComputeWorstSMR().value); } } } @@ -133,11 +136,12 @@ TEST_F(StreamAnalyzerTest, AllResultsDifferent) { for (size_t i = 0; i < 1000; i++) { AddPatternHelper(&shared_client_, analyzer(), pattern49, kSampleWeight); EXPECT_DOUBLE_EQ(expected_mean, analyzer_->ComputeMean()); - EXPECT_NEAR_SMR(expected_smr, analyzer_->ComputeSMR(), kSampleWeight); - EXPECT_DOUBLE_EQ(expected_rms, analyzer_->ComputeRMS()); - EXPECT_DOUBLE_EQ(expected_std_dev, analyzer_->ComputeStdDev()); - EXPECT_DOUBLE_EQ(expected_variance_of_roots, - analyzer_->ComputeVarianceOfRoots()); + // since each value creates a difference of 0.001, the result should + EXPECT_NEAR_SQRT_APPROX(expected_smr, analyzer_->ComputeSMR()); + EXPECT_NEAR_SQRT_APPROX(expected_rms, analyzer_->ComputeRMS()); + EXPECT_NEAR_SQRT_APPROX(expected_std_dev, analyzer_->ComputeStdDev()); + EXPECT_NEAR_SQRT_APPROX(expected_variance_of_roots, + analyzer_->ComputeVarianceOfRoots()); } thresholds = analyzer_->ComputeThresholds(); ASSERT_EQ(3u, thresholds.size()); @@ -158,11 +162,11 @@ TEST_F(StreamAnalyzerTest, AllResultsDifferent) { } thresholds = analyzer_->ComputeThresholds(); EXPECT_DOUBLE_EQ(expected_mean, analyzer_->ComputeMean()); - EXPECT_NEAR_SMR(expected_smr, analyzer_->ComputeSMR(), kSampleWeight); - EXPECT_DOUBLE_EQ(expected_rms, analyzer_->ComputeRMS()); - EXPECT_DOUBLE_EQ(expected_std_dev, analyzer_->ComputeStdDev()); - EXPECT_DOUBLE_EQ(expected_variance_of_roots, - analyzer_->ComputeVarianceOfRoots()); + EXPECT_NEAR_SQRT_APPROX(expected_smr, analyzer_->ComputeSMR()); + EXPECT_NEAR_SQRT_APPROX(expected_rms, analyzer_->ComputeRMS()); + EXPECT_NEAR_SQRT_APPROX(expected_std_dev, analyzer_->ComputeStdDev()); + EXPECT_NEAR_SQRT_APPROX(expected_variance_of_roots, + analyzer_->ComputeVarianceOfRoots()); EXPECT_EQ(client_.TransformResult(thresholds_[0]), thresholds[0].threshold); EXPECT_EQ(client_.TransformResult(thresholds_[1]), thresholds[1].threshold); EXPECT_EQ(client_.TransformResult(thresholds_[2]), thresholds[2].threshold); @@ -180,11 +184,11 @@ TEST_F(StreamAnalyzerTest, AllResultsDifferent) { } thresholds = analyzer_->ComputeThresholds(); EXPECT_DOUBLE_EQ(expected_mean, analyzer_->ComputeMean()); - EXPECT_NEAR_SMR(expected_smr, analyzer_->ComputeSMR(), kSampleWeight); - EXPECT_DOUBLE_EQ(expected_rms, analyzer_->ComputeRMS()); - EXPECT_DOUBLE_EQ(expected_std_dev, analyzer_->ComputeStdDev()); - EXPECT_DOUBLE_EQ(expected_variance_of_roots, - analyzer_->ComputeVarianceOfRoots()); + EXPECT_NEAR_SQRT_APPROX(expected_smr, analyzer_->ComputeSMR()); + EXPECT_NEAR_SQRT_APPROX(expected_rms, analyzer_->ComputeRMS()); + EXPECT_NEAR_SQRT_APPROX(expected_std_dev, analyzer_->ComputeStdDev()); + EXPECT_NEAR_SQRT_APPROX(expected_variance_of_roots, + analyzer_->ComputeVarianceOfRoots()); EXPECT_EQ(client_.TransformResult(thresholds_[0]), thresholds[0].threshold); EXPECT_EQ(client_.TransformResult(thresholds_[1]), thresholds[1].threshold); EXPECT_EQ(client_.TransformResult(thresholds_[2]), thresholds[2].threshold); @@ -295,7 +299,7 @@ TEST_F(StreamAnalyzerTest, Precision) { double expected_rms = client_.TransformResult(std::sqrt(mean_square)); EXPECT_ABS_LT(expected_rms * .001, expected_rms - naive_analyzer.ComputeRMS()); - EXPECT_DOUBLE_EQ(expected_rms, analyzer_->ComputeRMS()); + EXPECT_NEAR_SQRT_APPROX(expected_rms, analyzer_->ComputeRMS()); double large_value_root = std::sqrt(large_value_f) * large_weight; double small_value_root = std::sqrt(small_value_f) * small_weight; @@ -304,7 +308,7 @@ TEST_F(StreamAnalyzerTest, Precision) { double expected_smr = client_.TransformResult(mean_root * mean_root); EXPECT_ABS_LT(expected_smr * .001, expected_smr - naive_analyzer.ComputeSMR()); - EXPECT_NEAR_SMR(expected_smr, analyzer_->ComputeSMR(), 1); + EXPECT_NEAR_SQRT_APPROX(expected_smr, analyzer_->ComputeSMR()); } } // namespace diff --git a/chromium/ui/latency/windowed_analyzer.cc b/chromium/ui/latency/windowed_analyzer.cc index a78d003532c..cd718d87a4d 100644 --- a/chromium/ui/latency/windowed_analyzer.cc +++ b/chromium/ui/latency/windowed_analyzer.cc @@ -4,6 +4,8 @@ #include "ui/latency/windowed_analyzer.h" +#include "ui/latency/frame_metrics.h" + namespace ui { void FrameRegionResult::AsValueInto( @@ -57,9 +59,10 @@ void WindowedAnalyzer::AddSample(uint32_t value, accumulator_ -= static_cast<uint64_t>(old_weight) * old_value; // Casting the whole rhs is important to ensure rounding happens at a place // equivalent to when it was added. - root_accumulator_ -= static_cast<uint64_t>( - old_weight * - std::sqrt(static_cast<uint64_t>(old_value) << kFixedPointRootShift)); + root_accumulator_ -= + static_cast<uint64_t>(old_weight * FrameMetrics::FastApproximateSqrt( + static_cast<uint64_t>(old_value) + << kFixedPointRootShift)); square_accumulator_.Subtract(Accumulator96b(old_value, old_weight)); } @@ -107,7 +110,8 @@ FrameRegionResult WindowedAnalyzer::ComputeWorstRMS() const { } else { UpdateWorst(square_accumulator_, &result, true); } - result.value = client_->TransformResult(std::sqrt(result.value)); + result.value = + client_->TransformResult(FrameMetrics::FastApproximateSqrt(result.value)); return result; } diff --git a/chromium/ui/latency/windowed_analyzer_unittest.cc b/chromium/ui/latency/windowed_analyzer_unittest.cc index 71b42480548..5aa05034910 100644 --- a/chromium/ui/latency/windowed_analyzer_unittest.cc +++ b/chromium/ui/latency/windowed_analyzer_unittest.cc @@ -6,6 +6,7 @@ #include "base/time/time.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/latency/fixed_point.h" #include "ui/latency/frame_metrics_test_common.h" namespace ui { @@ -35,10 +36,11 @@ TEST(FrameMetricsWindowedAnalyzerTest, AllResultsTheSame) { value * TestWindowedAnalyzerClient::result_scale; EXPECT_EQ(analyzer.ComputeWorstMean().value, expected_value) << value << " x " << weight; - EXPECT_EQ(analyzer.ComputeWorstRMS().value, expected_value) + EXPECT_NEAR_SQRT_APPROX(analyzer.ComputeWorstRMS().value, + expected_value) << value << " x " << weight; - EXPECT_NEAR_SMR(analyzer.ComputeWorstSMR().value, expected_value, - weight) + EXPECT_NEAR_SQRT_APPROX(analyzer.ComputeWorstSMR().value, + expected_value) << value << " x " << weight; } } @@ -57,9 +59,9 @@ TEST(FrameMetricsWindowedAnalyzerTest, AllResultsTheSame) { // Makes sure our precision is good enough. EXPECT_EQ(analyzer.ComputeWorstMean().value, expected_value) << value << " x " << weight; - EXPECT_EQ(analyzer.ComputeWorstRMS().value, expected_value) + EXPECT_NEAR_SQRT_APPROX(analyzer.ComputeWorstRMS().value, expected_value) << value << " x " << weight; - EXPECT_NEAR_SMR(analyzer.ComputeWorstSMR().value, expected_value, weight) + EXPECT_NEAR_SQRT_APPROX(analyzer.ComputeWorstSMR().value, expected_value) << value << " x " << weight; } } @@ -125,12 +127,12 @@ TEST(FrameMetricsWindowedAnalyzerTest, AllResultsDifferent) { EXPECT_EQ(worst_mean_client.window_end, worst_mean.window_end); FrameRegionResult worst_smr = analyzer.ComputeWorstSMR(); - EXPECT_NEAR_SMR(expected_worst_smr, worst_smr.value, kSampleWeight); + EXPECT_NEAR_SQRT_APPROX(expected_worst_smr, worst_smr.value); EXPECT_EQ(worst_smr_client.window_begin, worst_smr.window_begin); EXPECT_EQ(worst_smr_client.window_end, worst_smr.window_end); FrameRegionResult worst_rms = analyzer.ComputeWorstRMS(); - EXPECT_DOUBLE_EQ(expected_worst_rms, worst_rms.value); + EXPECT_NEAR_SQRT_APPROX(expected_worst_rms, worst_rms.value); EXPECT_EQ(worst_rms_client.window_begin, worst_rms.window_begin); EXPECT_EQ(worst_rms_client.window_end, worst_rms.window_end); } @@ -159,12 +161,12 @@ TEST(FrameMetricsWindowedAnalyzerTest, SmallSampleSize) { EXPECT_EQ(short_client.window_end, worst_mean.window_end); FrameRegionResult worst_smr = analyzer.ComputeWorstSMR(); - EXPECT_NEAR_SMR(expected_initial_value, worst_smr.value, kSampleWeight); + EXPECT_NEAR_SQRT_APPROX(expected_initial_value, worst_smr.value); EXPECT_EQ(short_client.window_begin, worst_smr.window_begin); EXPECT_EQ(short_client.window_end, worst_smr.window_end); FrameRegionResult worst_rms = analyzer.ComputeWorstRMS(); - EXPECT_DOUBLE_EQ(expected_initial_value, worst_rms.value); + EXPECT_NEAR_SQRT_APPROX(expected_initial_value, worst_rms.value); EXPECT_EQ(short_client.window_begin, worst_rms.window_begin); EXPECT_EQ(short_client.window_end, worst_rms.window_end); } @@ -196,12 +198,12 @@ TEST(FrameMetricsWindowedAnalyzerTest, BadFirstSamples) { EXPECT_EQ(short_client.window_end, worst_mean.window_end); worst_smr = analyzer.ComputeWorstSMR(); - EXPECT_NEAR_SMR(expected_initial_value, worst_smr.value, kSampleWeight); + EXPECT_NEAR_SQRT_APPROX(expected_initial_value, worst_smr.value); EXPECT_EQ(short_client.window_begin, worst_smr.window_begin); EXPECT_EQ(short_client.window_end, worst_smr.window_end); worst_rms = analyzer.ComputeWorstRMS(); - EXPECT_DOUBLE_EQ(expected_initial_value, worst_rms.value); + EXPECT_NEAR_SQRT_APPROX(expected_initial_value, worst_rms.value); EXPECT_EQ(short_client.window_begin, worst_rms.window_begin); EXPECT_EQ(short_client.window_end, worst_rms.window_end); @@ -222,12 +224,12 @@ TEST(FrameMetricsWindowedAnalyzerTest, BadFirstSamples) { EXPECT_EQ(long_client.window_end, worst_mean.window_end); worst_smr = analyzer.ComputeWorstSMR(); - EXPECT_NEAR_SMR(expected_final_value, worst_smr.value, kSampleWeight); + EXPECT_NEAR_SQRT_APPROX(expected_final_value, worst_smr.value); EXPECT_EQ(long_client.window_begin, worst_smr.window_begin); EXPECT_EQ(long_client.window_end, worst_smr.window_end); worst_rms = analyzer.ComputeWorstRMS(); - EXPECT_DOUBLE_EQ(expected_final_value, worst_rms.value); + EXPECT_NEAR_SQRT_APPROX(expected_final_value, worst_rms.value); EXPECT_EQ(long_client.window_begin, worst_rms.window_begin); EXPECT_EQ(long_client.window_end, worst_rms.window_end); } @@ -257,12 +259,12 @@ TEST(FrameMetricsWindowedAnalyzerTest, ResetWorstValues) { EXPECT_EQ(initial_client.window_end, worst_mean.window_end); worst_smr = analyzer.ComputeWorstSMR(); - EXPECT_NEAR_SMR(expected_initial_value, worst_smr.value, kSampleWeight); + EXPECT_NEAR_SQRT_APPROX(expected_initial_value, worst_smr.value); EXPECT_EQ(initial_client.window_begin, worst_smr.window_begin); EXPECT_EQ(initial_client.window_end, worst_smr.window_end); worst_rms = analyzer.ComputeWorstRMS(); - EXPECT_DOUBLE_EQ(expected_initial_value, worst_rms.value); + EXPECT_NEAR_SQRT_APPROX(expected_initial_value, worst_rms.value); EXPECT_EQ(initial_client.window_begin, worst_rms.window_begin); EXPECT_EQ(initial_client.window_end, worst_rms.window_end); @@ -277,12 +279,12 @@ TEST(FrameMetricsWindowedAnalyzerTest, ResetWorstValues) { EXPECT_EQ(initial_client.window_end, worst_mean.window_end); worst_smr = analyzer.ComputeWorstSMR(); - EXPECT_NEAR_SMR(expected_initial_value, worst_smr.value, kSampleWeight); + EXPECT_NEAR_SQRT_APPROX(expected_initial_value, worst_smr.value); EXPECT_EQ(initial_client.window_begin, worst_smr.window_begin); EXPECT_EQ(initial_client.window_end, worst_smr.window_end); worst_rms = analyzer.ComputeWorstRMS(); - EXPECT_DOUBLE_EQ(expected_initial_value, worst_rms.value); + EXPECT_NEAR_SQRT_APPROX(expected_initial_value, worst_rms.value); EXPECT_EQ(initial_client.window_begin, worst_rms.window_begin); EXPECT_EQ(initial_client.window_end, worst_rms.window_end); @@ -308,12 +310,12 @@ TEST(FrameMetricsWindowedAnalyzerTest, ResetWorstValues) { EXPECT_EQ(final_client.window_end, worst_mean.window_end); worst_smr = analyzer.ComputeWorstSMR(); - EXPECT_NEAR_SMR(expected_final_value, worst_smr.value, kSampleWeight); + EXPECT_NEAR_SQRT_APPROX(expected_final_value, worst_smr.value); EXPECT_EQ(final_client.window_begin, worst_smr.window_begin); EXPECT_EQ(final_client.window_end, worst_smr.window_end); worst_rms = analyzer.ComputeWorstRMS(); - EXPECT_DOUBLE_EQ(expected_final_value, worst_rms.value); + EXPECT_NEAR_SQRT_APPROX(expected_final_value, worst_rms.value); EXPECT_EQ(final_client.window_begin, worst_rms.window_begin); EXPECT_EQ(final_client.window_end, worst_rms.window_end); } @@ -351,6 +353,22 @@ class WindowedAnalyzerNaive { naive_total_weight_ += weight; } + // Same as AddPatternHelper, but uses each value (+1) as its own weight. + // The "Cubed" name comes from the fact that the squared_accumulator + // for the RMS will effectively be a "cubed accumulator". + void AddCubedPatternHelper(SharedWindowedAnalyzerClient* shared_client, + const std::vector<uint32_t>& values) { + for (auto i : values) { + shared_client->window_begin += base::TimeDelta::FromMicroseconds(1); + shared_client->window_end += base::TimeDelta::FromMicroseconds(1); + uint64_t weighted_value = (i * (i + 1)); + uint64_t updated_value = static_cast<uint64_t>(i); + uint64_t weighted_root = (i + 1) * std::sqrt(updated_value << 32); + Accumulator96b weighted_square(i, (i + 1)); + AddSample(i, (i + 1), weighted_value, weighted_root, weighted_square); + } + } + struct Sample { uint32_t value; uint32_t weight; @@ -412,8 +430,8 @@ void TestNoAccumulatedPrecisionError(uint32_t big_value, double naive_root_accumulator_prev = 0; double naive_square_accumulator_prev = 0; for (size_t i = 1; i <= kRuns; i++) { - AddCubedPatternHelper(&shared_client_naive, &analyzer_naive, pattern_bad); - AddCubedPatternHelper(&shared_client_naive, &analyzer_naive, pattern_clear); + analyzer_naive.AddCubedPatternHelper(&shared_client_naive, pattern_bad); + analyzer_naive.AddCubedPatternHelper(&shared_client_naive, pattern_clear); EXPECT_EQ(0, analyzer_naive.naive_accumulator_); EXPECT_ABS_LT(naive_root_accumulator_prev, analyzer_naive.naive_root_accumulator_); @@ -427,15 +445,6 @@ void TestNoAccumulatedPrecisionError(uint32_t big_value, analyzer_naive.naive_root_accumulator_); EXPECT_ABS_LE(naive_square_error_floor * kRuns, analyzer_naive.naive_square_accumulator_); - - // Verify actual implementation has no error. - for (size_t i = 1; i <= kRuns; i++) { - AddCubedPatternHelper(&shared_client_impl, &analyzer_impl, pattern_bad); - AddCubedPatternHelper(&shared_client_impl, &analyzer_impl, pattern_clear); - EXPECT_EQ(0, analyzer_impl.CurrentAccumulator()); - EXPECT_EQ(0, analyzer_impl.CurrentRootAccumulator()); - EXPECT_EQ(0, analyzer_impl.CurrentSquareAccumulator()); - } } // This is a synthetic example that is just outside the dynamic range of a diff --git a/chromium/ui/login/account_picker/md_user_pod_template.html b/chromium/ui/login/account_picker/md_user_pod_template.html index 29b2ade6b78..965870022f7 100644 --- a/chromium/ui/login/account_picker/md_user_pod_template.html +++ b/chromium/ui/login/account_picker/md_user_pod_template.html @@ -50,7 +50,8 @@ </div> <if expr="chromeos"> <div class="pin-container pin-disabled pin-tag"> - <pin-keyboard enable-password hide-input></pin-keyboard> + <pin-keyboard enable-password hide-input enable-placeholder> + </pin-keyboard> </div> </if> <div class="main-pane"> diff --git a/chromium/ui/login/account_picker/user_pod_template.html b/chromium/ui/login/account_picker/user_pod_template.html index 9d233226952..367374fca85 100644 --- a/chromium/ui/login/account_picker/user_pod_template.html +++ b/chromium/ui/login/account_picker/user_pod_template.html @@ -34,7 +34,7 @@ </div> <if expr="chromeos"> <div class="pin-container pin-disabled pin-tag"> - <pin-keyboard enable-password hide-input></pin-keyboard> + <pin-keyboard enable-password hide-input enable-placeholder></pin-keyboard> </div> </if> <div class="main-pane"> diff --git a/chromium/ui/login/display_manager.js b/chromium/ui/login/display_manager.js index 7a948a1565a..53ef9909a6b 100644 --- a/chromium/ui/login/display_manager.js +++ b/chromium/ui/login/display_manager.js @@ -25,8 +25,6 @@ /** @const */ var SCREEN_ERROR_MESSAGE = 'error-message'; /** @const */ var SCREEN_TPM_ERROR = 'tpm-error-message'; /** @const */ var SCREEN_PASSWORD_CHANGED = 'password-changed'; -/** @const */ var SCREEN_CREATE_SUPERVISED_USER_FLOW = - 'supervised-user-creation'; /** @const */ var SCREEN_APP_LAUNCH_SPLASH = 'app-launch-splash'; /** @const */ var SCREEN_ARC_KIOSK_SPLASH = 'arc-kiosk-splash'; /** @const */ var SCREEN_CONFIRM_PASSWORD = 'confirm-password'; @@ -73,7 +71,7 @@ GAIA_SIGNIN: 1, ACCOUNT_PICKER: 2, WRONG_HWID_WARNING: 3, - SUPERVISED_USER_CREATION_FLOW: 4, + DEPRECATED_SUPERVISED_USER_CREATION_FLOW: 4, SAML_PASSWORD_CONFIRM: 5, PASSWORD_CHANGED: 6, ENROLLMENT: 7, @@ -85,7 +83,6 @@ UNKNOWN: 'ui-state-unknown', UPDATE: 'ui-state-update', SIGNIN: 'ui-state-signin', - SUPERVISED_USER_CREATION_FLOW: 'ui-state-supervised', KIOSK_MODE: 'ui-state-kiosk-mode', LOCAL_STATE_ERROR: 'ui-state-local-state-error', AUTO_ENROLLMENT_ERROR: 'ui-state-auto-enrollment-error', @@ -273,9 +270,9 @@ cr.define('cr.ui.login', function() { /** * Stored OOBE configuration for newly registered screens. - * @type {dictionary} + * @type {!OobeTypes.OobeConfiguration} */ - oobe_configuration_: {}, + oobe_configuration_: undefined, /** * Detects multi-tap gesture that invokes demo mode setup in OOBE. @@ -415,7 +412,7 @@ cr.define('cr.ui.login', function() { /** * Returns current OOBE configuration. - * @return {dictionary} + * @return {!OobeTypes.OobeConfiguration} */ getOobeConfiguration: function() { return this.oobe_configuration_; @@ -796,8 +793,8 @@ cr.define('cr.ui.login', function() { $('header-sections').appendChild(header); this.appendButtons_(el.buttons, screenId); - if (el.updateOobeConfiguration) - el.updateOobeConfiguration(oobe_configuration_); + if (el.updateOobeConfiguration && this.oobe_configuration_) + el.updateOobeConfiguration(this.oobe_configuration_); }, /** @@ -879,16 +876,16 @@ cr.define('cr.ui.login', function() { /** * Updates Oobe configuration for screens. - * @param {dictionary} configuration OOBE configuration. + * @param {!OobeTypes.OobeConfiguration} configuration OOBE configuration. */ updateOobeConfiguration_: function(configuration) { + this.oobe_configuration_ = configuration; for (let i = 0; i < this.screens_.length; ++i) { let screenId = this.screens_[i]; var screen = $(screenId); if (screen.updateOobeConfiguration) screen.updateOobeConfiguration(configuration); } - this.oobe_configuration_ = configuration; }, /** @@ -1263,13 +1260,6 @@ cr.define('cr.ui.login', function() { }; /** - * Shows dialog to create a supervised user. - */ - DisplayManager.showSupervisedUserCreationScreen = function() { - login.SupervisedUserCreationScreen.show(); - }; - - /** * Shows TPM error screen. */ DisplayManager.showTpmError = function() { diff --git a/chromium/ui/message_center/BUILD.gn b/chromium/ui/message_center/BUILD.gn index d165deb5c79..99243819620 100644 --- a/chromium/ui/message_center/BUILD.gn +++ b/chromium/ui/message_center/BUILD.gn @@ -144,8 +144,6 @@ jumbo_component("message_center") { "views/popup_alignment_delegate.h", "views/proportional_image_view.cc", "views/proportional_image_view.h", - "views/slidable_message_view.cc", - "views/slidable_message_view.h", "views/slide_out_controller.cc", "views/slide_out_controller.h", ] diff --git a/chromium/ui/message_center/message_center_impl.cc b/chromium/ui/message_center/message_center_impl.cc index c6f7e14a82c..1c113d6d4e1 100644 --- a/chromium/ui/message_center/message_center_impl.cc +++ b/chromium/ui/message_center/message_center_impl.cc @@ -64,8 +64,7 @@ void MessageCenterImpl::AddNotificationBlocker(NotificationBlocker* blocker) { void MessageCenterImpl::RemoveNotificationBlocker( NotificationBlocker* blocker) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - std::vector<NotificationBlocker*>::iterator iter = - std::find(blockers_.begin(), blockers_.end(), blocker); + auto iter = std::find(blockers_.begin(), blockers_.end(), blocker); if (iter == blockers_.end()) return; blocker->RemoveObserver(this); diff --git a/chromium/ui/message_center/message_center_impl_unittest.cc b/chromium/ui/message_center/message_center_impl_unittest.cc index 430c2ce1e48..6d85eb7ed32 100644 --- a/chromium/ui/message_center/message_center_impl_unittest.cc +++ b/chromium/ui/message_center/message_center_impl_unittest.cc @@ -291,8 +291,7 @@ class TotalNotificationBlocker : public PopupNotificationBlocker { bool PopupNotificationsContain( const NotificationList::PopupNotifications& popups, const std::string& id) { - for (NotificationList::PopupNotifications::const_iterator iter = - popups.begin(); iter != popups.end(); ++iter) { + for (auto iter = popups.begin(); iter != popups.end(); ++iter) { if ((*iter)->id() == id) return true; } @@ -303,8 +302,7 @@ bool PopupNotificationsContain( bool NotificationsContain( const NotificationList::Notifications& notifications, const std::string& id) { - for (NotificationList::Notifications::const_iterator iter = - notifications.begin(); iter != notifications.end(); ++iter) { + for (auto iter = notifications.begin(); iter != notifications.end(); ++iter) { if ((*iter)->id() == id) return true; } diff --git a/chromium/ui/message_center/message_center_stats_collector.cc b/chromium/ui/message_center/message_center_stats_collector.cc index 076da87c8f7..4f3faa699ad 100644 --- a/chromium/ui/message_center/message_center_stats_collector.cc +++ b/chromium/ui/message_center/message_center_stats_collector.cc @@ -61,7 +61,7 @@ void MessageCenterStatsCollector::OnNotificationAdded( const std::string& notification_id) { stats_[notification_id] = NotificationStats(notification_id); - StatsCollection::iterator iter = stats_.find(notification_id); + auto iter = stats_.find(notification_id); DCHECK(iter != stats_.end()); stats_[notification_id].CollectAction(NOTIFICATION_ACTION_ADD); @@ -70,7 +70,7 @@ void MessageCenterStatsCollector::OnNotificationAdded( void MessageCenterStatsCollector::OnNotificationRemoved( const std::string& notification_id, bool by_user) { - StatsCollection::iterator iter = stats_.find(notification_id); + auto iter = stats_.find(notification_id); if (iter == stats_.end()) return; NotificationStats& notification_stat = iter->second; @@ -83,7 +83,7 @@ void MessageCenterStatsCollector::OnNotificationRemoved( void MessageCenterStatsCollector::OnNotificationUpdated( const std::string& notification_id) { - StatsCollection::iterator iter = stats_.find(notification_id); + auto iter = stats_.find(notification_id); if (iter == stats_.end()) return; NotificationStats& notification_stat = iter->second; @@ -95,7 +95,7 @@ void MessageCenterStatsCollector::OnNotificationClicked( const std::string& notification_id, const base::Optional<int>& button_index, const base::Optional<base::string16>& reply) { - StatsCollection::iterator iter = stats_.find(notification_id); + auto iter = stats_.find(notification_id); if (iter == stats_.end()) return; NotificationStats& notification_stat = iter->second; @@ -112,7 +112,7 @@ void MessageCenterStatsCollector::OnNotificationSettingsClicked(bool handled) { void MessageCenterStatsCollector::OnNotificationDisplayed( const std::string& notification_id, const DisplaySource source) { - StatsCollection::iterator iter = stats_.find(notification_id); + auto iter = stats_.find(notification_id); if (iter == stats_.end()) return; NotificationStats& notification_stat = iter->second; diff --git a/chromium/ui/message_center/notification_list_unittest.cc b/chromium/ui/message_center/notification_list_unittest.cc index 227681ff283..71432f104a3 100644 --- a/chromium/ui/message_center/notification_list_unittest.cc +++ b/chromium/ui/message_center/notification_list_unittest.cc @@ -118,8 +118,7 @@ class NotificationListTest : public testing::Test { bool IsInNotifications(const NotificationList::Notifications& notifications, const std::string& id) { - for (NotificationList::Notifications::const_iterator iter = - notifications.begin(); iter != notifications.end(); ++iter) { + for (auto iter = notifications.begin(); iter != notifications.end(); ++iter) { if ((*iter)->id() == id) return true; } @@ -305,14 +304,12 @@ TEST_F(NotificationListTest, OldPopupShouldNotBeHidden) { // one should come earlier in the popup list. It means, the last element // of |popups| should be the firstly added one, and so on. EXPECT_EQ(kMaxVisiblePopupNotifications, popups.size()); - NotificationList::PopupNotifications::const_reverse_iterator iter = - popups.rbegin(); + auto iter = popups.rbegin(); for (size_t i = 0; i < kMaxVisiblePopupNotifications; ++i, ++iter) { EXPECT_EQ(ids[i], (*iter)->id()) << i; } - for (NotificationList::PopupNotifications::const_iterator iter = - popups.begin(); iter != popups.end(); ++iter) { + for (auto iter = popups.begin(); iter != popups.end(); ++iter) { notification_list_->MarkSinglePopupAsShown((*iter)->id(), false); } popups.clear(); @@ -346,8 +343,7 @@ TEST_F(NotificationListTest, Priority) { NotificationList::Notifications notifications = notification_list_->GetVisibleNotifications(blockers_); - for (NotificationList::Notifications::const_iterator iter = - notifications.begin(); iter != notifications.end(); ++iter) { + for (auto iter = notifications.begin(); iter != notifications.end(); ++iter) { notification_list_->RemoveNotification((*iter)->id()); } @@ -582,7 +578,7 @@ TEST_F(NotificationListTest, NotificationOrderAndPriority) { // Popups: latest comes first. NotificationList::PopupNotifications popups = GetPopups(); EXPECT_EQ(3u, popups.size()); - NotificationList::PopupNotifications::const_iterator iter = popups.begin(); + auto iter = popups.begin(); EXPECT_EQ(default_id, (*iter)->id()); iter++; EXPECT_EQ(high_id, (*iter)->id()); @@ -594,8 +590,7 @@ TEST_F(NotificationListTest, NotificationOrderAndPriority) { const NotificationList::Notifications notifications = notification_list_->GetVisibleNotifications(blockers_); EXPECT_EQ(3u, notifications.size()); - NotificationList::Notifications::const_iterator iter = - notifications.begin(); + auto iter = notifications.begin(); EXPECT_EQ(max_id, (*iter)->id()); iter++; EXPECT_EQ(high_id, (*iter)->id()); @@ -625,7 +620,7 @@ TEST_F(NotificationListTest, MarkSinglePopupAsShown) { // The notifications in the NotificationCenter are unaffected by popups shown. NotificationList::Notifications notifications = notification_list_->GetVisibleNotifications(blockers_); - NotificationList::Notifications::const_iterator iter = notifications.begin(); + auto iter = notifications.begin(); EXPECT_EQ(id3, (*iter)->id()); iter++; EXPECT_EQ(id2, (*iter)->id()); diff --git a/chromium/ui/message_center/popup_timers_controller.cc b/chromium/ui/message_center/popup_timers_controller.cc index 359f1049129..0c5eeadf3fd 100644 --- a/chromium/ui/message_center/popup_timers_controller.cc +++ b/chromium/ui/message_center/popup_timers_controller.cc @@ -98,8 +98,7 @@ void PopupTimersController::OnNotificationUpdated(const std::string& id) { return; } - NotificationList::PopupNotifications::const_iterator iter = - popup_notifications.begin(); + auto iter = popup_notifications.begin(); for (; iter != popup_notifications.end(); ++iter) { if ((*iter)->id() == id) break; diff --git a/chromium/ui/message_center/public/cpp/features.cc b/chromium/ui/message_center/public/cpp/features.cc index 220a51220b1..079e49bfdf5 100644 --- a/chromium/ui/message_center/public/cpp/features.cc +++ b/chromium/ui/message_center/public/cpp/features.cc @@ -15,7 +15,4 @@ const base::Feature kNewStyleNotifications { #endif }; -const base::Feature kNotificationSwipeControl{ - "NotificationSwipeControl", base::FEATURE_DISABLED_BY_DEFAULT}; - } // namespace message_center diff --git a/chromium/ui/message_center/public/cpp/features.h b/chromium/ui/message_center/public/cpp/features.h index 14a3fc2a563..dadaddbb22e 100644 --- a/chromium/ui/message_center/public/cpp/features.h +++ b/chromium/ui/message_center/public/cpp/features.h @@ -14,10 +14,6 @@ namespace message_center { // should be used. MESSAGE_CENTER_PUBLIC_EXPORT extern const base::Feature kNewStyleNotifications; -// Whether the swipe control on notifications should be enabled. -MESSAGE_CENTER_PUBLIC_EXPORT extern const base::Feature - kNotificationSwipeControl; - } // namespace message_center #endif // UI_MESSAGE_CENTER_PUBLIC_CPP_FEATURES_H_ diff --git a/chromium/ui/message_center/views/message_popup_view.cc b/chromium/ui/message_center/views/message_popup_view.cc index 877abffae71..ebae9ce9f20 100644 --- a/chromium/ui/message_center/views/message_popup_view.cc +++ b/chromium/ui/message_center/views/message_popup_view.cc @@ -31,7 +31,7 @@ namespace message_center { MessagePopupView::MessagePopupView(const Notification& notification, PopupAlignmentDelegate* alignment_delegate, MessagePopupCollection* popup_collection) - : message_view_(MessageViewFactory::Create(notification, true)), + : message_view_(MessageViewFactory::Create(notification)), alignment_delegate_(alignment_delegate), popup_collection_(popup_collection), a11y_feedback_on_init_( diff --git a/chromium/ui/message_center/views/message_view.cc b/chromium/ui/message_center/views/message_view.cc index 46e73a822c1..a3782fe4bb0 100644 --- a/chromium/ui/message_center/views/message_view.cc +++ b/chromium/ui/message_center/views/message_view.cc @@ -6,10 +6,12 @@ #include "base/feature_list.h" #include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" #include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_node_data.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/models/simple_menu_model.h" +#include "ui/compositor/paint_recorder.h" #include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/gfx/canvas.h" #include "ui/gfx/image/image_skia_operations.h" @@ -29,13 +31,15 @@ #include "ui/views/focus/focus_manager.h" #include "ui/views/widget/widget.h" +#if defined(OS_WIN) +#include "ui/base/win/shell.h" +#endif + namespace message_center { namespace { -const SkColor kBorderColor = SkColorSetARGB(0x1F, 0x0, 0x0, 0x0); -const int kShadowCornerRadius = 0; -const int kShadowElevation = 2; +constexpr SkColor kBorderColor = SkColorSetARGB(0x1F, 0x0, 0x0, 0x0); // Creates a text for spoken feedback from the data contained in the // notification. @@ -55,8 +59,12 @@ base::string16 CreateAccessibleName(const Notification& notification) { return base::JoinString(accessible_lines, base::ASCIIToUTF16("\n")); } -bool ShouldRoundMessageViewCorners() { - return base::FeatureList::IsEnabled(message_center::kNewStyleNotifications); +bool ShouldShowAeroShadowBorder() { +#if defined(OS_WIN) + return ui::win::IsAeroGlassEnabled(); +#else + return false; +#endif } } // namespace @@ -72,22 +80,22 @@ MessageView::MessageView(const Notification& notification) SetPaintToLayer(); layer()->SetFillsBoundsOpaquely(false); - // Create the opaque background that's above the view's shadow. - background_view_ = new views::View(); - - // ChromeOS rounds the corners of the message view. TODO(estade): should we do - // this for all platforms? - if (ShouldRoundMessageViewCorners()) - UpdateCornerRadius(kNotificationCornerRadius, kNotificationCornerRadius); - else - UpdateCornerRadius(0, 0); - - AddChildView(background_view_); - focus_painter_ = views::Painter::CreateSolidFocusPainter( kFocusBorderColor, gfx::Insets(0, 0, 1, 1)); UpdateWithNotification(notification); + + UpdateCornerRadius(0, 0); + + // If Aero is enabled, set shadow border. + if (ShouldShowAeroShadowBorder()) { + const auto& shadow = gfx::ShadowDetails::Get(2, 0); + gfx::Insets ninebox_insets = gfx::ShadowValue::GetBlurRegion(shadow.values); + SetBorder(views::CreateBorderPainter( + views::Painter::CreateImagePainter(shadow.ninebox_image, + ninebox_insets), + -gfx::ShadowValue::GetMargin(shadow.values))); + } } MessageView::~MessageView() { @@ -111,23 +119,9 @@ void MessageView::SetIsNested() { // Update enability since it might be changed by "is_nested" flag. slide_out_controller_.set_slide_mode(CalculateSlideMode()); - if (ShouldRoundMessageViewCorners()) { - SetBorder(views::CreateRoundedRectBorder( - kNotificationBorderThickness, kNotificationCornerRadius, kBorderColor)); - } else { - const auto& shadow = - gfx::ShadowDetails::Get(kShadowElevation, kShadowCornerRadius); - gfx::Insets ninebox_insets = - gfx::ShadowValue::GetBlurRegion(shadow.values) + - gfx::Insets(kShadowCornerRadius); - SetBorder(views::CreateBorderPainter( - views::Painter::CreateImagePainter(shadow.ninebox_image, - ninebox_insets), - -gfx::ShadowValue::GetMargin(shadow.values))); - } + SetBorder(views::CreateRoundedRectBorder( + kNotificationBorderThickness, kNotificationCornerRadius, kBorderColor)); - if (!base::FeatureList::IsEnabled(message_center::kNotificationSwipeControl)) - return; auto* control_buttons_view = GetControlButtonsView(); if (control_buttons_view) { int control_button_count = @@ -183,7 +177,7 @@ void MessageView::SetManuallyExpandedOrCollapsed(bool value) { } void MessageView::UpdateCornerRadius(int top_radius, int bottom_radius) { - background_view_->SetBackground(views::CreateBackgroundFromPainter( + SetBackground(views::CreateBackgroundFromPainter( std::make_unique<NotificationBackgroundPainter>(top_radius, bottom_radius))); SchedulePaint(); @@ -248,9 +242,23 @@ bool MessageView::OnKeyReleased(const ui::KeyEvent& event) { return true; } +void MessageView::PaintChildren(const views::PaintInfo& paint_info) { + views::View::PaintChildren(paint_info); + + // Paint focus ring on top of all the children. + ui::PaintRecorder recorder(paint_info.context(), size()); + views::Painter::PaintFocusPainter(this, recorder.canvas(), + focus_painter_.get()); +} + void MessageView::OnPaint(gfx::Canvas* canvas) { - views::View::OnPaint(canvas); - views::Painter::PaintFocusPainter(this, canvas, focus_painter_.get()); + if (ShouldShowAeroShadowBorder()) { + // If the border is shadow, paint border first. + OnPaintBorder(canvas); + OnPaintBackground(canvas); + } else { + views::View::OnPaint(canvas); + } } void MessageView::OnFocus() { @@ -265,15 +273,6 @@ void MessageView::OnBlur() { SchedulePaint(); } -void MessageView::Layout() { - views::View::Layout(); - - gfx::Rect content_bounds = GetContentsBounds(); - - // Background. - background_view_->SetBoundsRect(content_bounds); -} - const char* MessageView::GetClassName() const { return kViewClassName; } @@ -409,8 +408,8 @@ void MessageView::OnSnoozeButtonPressed(const ui::Event& event) { } void MessageView::SetDrawBackgroundAsActive(bool active) { - background_view_->background()->SetNativeControlColor( - active ? kHoveredButtonBackgroundColor : kNotificationBackgroundColor); + background()->SetNativeControlColor(active ? kHoveredButtonBackgroundColor + : kNotificationBackgroundColor); SchedulePaint(); } diff --git a/chromium/ui/message_center/views/message_view.h b/chromium/ui/message_center/views/message_view.h index 32d006b2217..49138c6780b 100644 --- a/chromium/ui/message_center/views/message_view.h +++ b/chromium/ui/message_center/views/message_view.h @@ -111,10 +111,10 @@ class MESSAGE_CENTER_EXPORT MessageView : public views::InkDropHostView, void OnMouseReleased(const ui::MouseEvent& event) override; bool OnKeyPressed(const ui::KeyEvent& event) override; bool OnKeyReleased(const ui::KeyEvent& event) override; + void PaintChildren(const views::PaintInfo& paint_info) override; void OnPaint(gfx::Canvas* canvas) override; void OnFocus() override; void OnBlur() override; - void Layout() override; void OnGestureEvent(ui::GestureEvent* event) override; void RemovedFromWidget() override; void AddedToWidget() override; @@ -155,11 +155,9 @@ class MESSAGE_CENTER_EXPORT MessageView : public views::InkDropHostView, virtual void UpdateControlButtonsVisibility() = 0; - // Changes the background color being used by |background_view_| and schedules - // a paint. + // Changes the background color and schedules a paint. virtual void SetDrawBackgroundAsActive(bool active); - views::View* background_view() { return background_view_; } views::ScrollView* scroller() { return scroller_; } bool is_nested() const { return is_nested_; } @@ -171,7 +169,6 @@ class MESSAGE_CENTER_EXPORT MessageView : public views::InkDropHostView, SlideOutController::SlideMode CalculateSlideMode() const; std::string notification_id_; - views::View* background_view_ = nullptr; // Owned by views hierarchy. views::ScrollView* scroller_ = nullptr; base::string16 accessible_name_; diff --git a/chromium/ui/message_center/views/message_view_factory.cc b/chromium/ui/message_center/views/message_view_factory.cc index 07ed14273ea..ce4ef494d27 100644 --- a/chromium/ui/message_center/views/message_view_factory.cc +++ b/chromium/ui/message_center/views/message_view_factory.cc @@ -12,10 +12,6 @@ #include "ui/message_center/views/notification_view.h" #include "ui/message_center/views/notification_view_md.h" -#if defined(OS_WIN) -#include "ui/base/win/shell.h" -#endif - namespace message_center { namespace { @@ -26,8 +22,7 @@ base::LazyInstance<MessageViewFactory::CustomMessageViewFactoryFunction>::Leaky } // namespace // static -MessageView* MessageViewFactory::Create(const Notification& notification, - bool top_level) { +MessageView* MessageViewFactory::Create(const Notification& notification) { MessageView* notification_view = nullptr; switch (notification.type()) { case NOTIFICATION_TYPE_BASE_FORMAT: @@ -56,21 +51,6 @@ MessageView* MessageViewFactory::Create(const Notification& notification, << ". Falling back to simple notification type."; notification_view = new NotificationView(notification); } - -#if defined(OS_LINUX) - // Don't create shadows for notification toasts on Linux or CrOS. - if (top_level) - return notification_view; -#endif - -#if defined(OS_WIN) - // Don't create shadows for notifications on Windows under classic theme. - if (top_level && !ui::win::IsAeroGlassEnabled()) { - return notification_view; - } -#endif // OS_WIN - - notification_view->SetIsNested(); return notification_view; } diff --git a/chromium/ui/message_center/views/message_view_factory.h b/chromium/ui/message_center/views/message_view_factory.h index 883257ebff7..30c8520fdcd 100644 --- a/chromium/ui/message_center/views/message_view_factory.h +++ b/chromium/ui/message_center/views/message_view_factory.h @@ -27,7 +27,7 @@ class MESSAGE_CENTER_EXPORT MessageViewFactory { typedef base::Callback<std::unique_ptr<MessageView>(const Notification&)> CustomMessageViewFactoryFunction; - static MessageView* Create(const Notification& notification, bool top_level); + static MessageView* Create(const Notification& notification); // Sets the function that will be invoked to create a custom notification // view. This should be a repeating callback. It's an error to attempt to show diff --git a/chromium/ui/message_center/views/notification_background_painter.cc b/chromium/ui/message_center/views/notification_background_painter.cc index 691d24933bd..1e33c6f30c0 100644 --- a/chromium/ui/message_center/views/notification_background_painter.cc +++ b/chromium/ui/message_center/views/notification_background_painter.cc @@ -9,14 +9,15 @@ #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/skia_util.h" -#include "ui/message_center/public/cpp/message_center_constants.h" namespace message_center { NotificationBackgroundPainter::NotificationBackgroundPainter(int top_radius, - int bottom_radius) + int bottom_radius, + SkColor color) : top_radius_(SkIntToScalar(top_radius)), - bottom_radius_(SkIntToScalar(bottom_radius)) {} + bottom_radius_(SkIntToScalar(bottom_radius)), + color_(color) {} NotificationBackgroundPainter::~NotificationBackgroundPainter() = default; @@ -37,7 +38,7 @@ void NotificationBackgroundPainter::Paint(gfx::Canvas* canvas, cc::PaintFlags flags; flags.setAntiAlias(true); flags.setStyle(cc::PaintFlags::kFill_Style); - flags.setColor(kNotificationBackgroundColor); + flags.setColor(color_); canvas->DrawPath(path, flags); } diff --git a/chromium/ui/message_center/views/notification_background_painter.h b/chromium/ui/message_center/views/notification_background_painter.h index aba4f1d70bc..a31dd1ffec5 100644 --- a/chromium/ui/message_center/views/notification_background_painter.h +++ b/chromium/ui/message_center/views/notification_background_painter.h @@ -6,6 +6,7 @@ #define UI_MESSAGE_CENTER_VIEWS_NOTIFICATION_BACKGROUND_PAINTER_H_ #include "ui/message_center/message_center_export.h" +#include "ui/message_center/public/cpp/message_center_constants.h" #include "ui/views/painter.h" namespace message_center { @@ -16,12 +17,13 @@ namespace message_center { class MESSAGE_CENTER_EXPORT NotificationBackgroundPainter : public views::Painter { public: - NotificationBackgroundPainter(int top_radius, int bottom_radius); + NotificationBackgroundPainter(int top_radius, + int bottom_radius, + SkColor color = kNotificationBackgroundColor); ~NotificationBackgroundPainter() override; // views::Painter gfx::Size GetMinimumSize() const override; - void Paint(gfx::Canvas* canvas, const gfx::Size& size) override; void set_insets(const gfx::Insets& insets) { insets_ = insets; } @@ -29,6 +31,7 @@ class MESSAGE_CENTER_EXPORT NotificationBackgroundPainter private: const SkScalar top_radius_; const SkScalar bottom_radius_; + const SkColor color_; gfx::Insets insets_; diff --git a/chromium/ui/message_center/views/notification_view_md.cc b/chromium/ui/message_center/views/notification_view_md.cc index 3c6a29187d8..c27bfd96816 100644 --- a/chromium/ui/message_center/views/notification_view_md.cc +++ b/chromium/ui/message_center/views/notification_view_md.cc @@ -28,6 +28,7 @@ #include "ui/message_center/public/cpp/notification_types.h" #include "ui/message_center/vector_icons.h" #include "ui/message_center/views/bounded_label.h" +#include "ui/message_center/views/notification_background_painter.h" #include "ui/message_center/views/notification_control_buttons_view.h" #include "ui/message_center/views/notification_header_view.h" #include "ui/message_center/views/padded_button.h" @@ -549,8 +550,6 @@ NotificationViewMD::NotificationViewMD(const Notification& notification) action_buttons_row_->SetLayoutManager(std::make_unique<views::BoxLayout>( views::BoxLayout::kHorizontal, kActionsRowPadding, kActionsRowHorizontalSpacing)); - action_buttons_row_->SetBackground( - views::CreateSolidBackground(kActionsRowBackgroundColor)); action_buttons_row_->SetVisible(false); actions_row_->AddChildView(action_buttons_row_); @@ -570,6 +569,8 @@ NotificationViewMD::NotificationViewMD(const Notification& notification) // textfield click in native notification. // - To make it look similar to ArcNotificationContentView::EventForwarder. AddPreTargetHandler(click_activator_.get()); + + UpdateCornerRadius(kNotificationCornerRadius, kNotificationCornerRadius); } NotificationViewMD::~NotificationViewMD() { @@ -1231,6 +1232,13 @@ void NotificationViewMD::UpdateControlButtonsVisibility() { control_buttons_view_->SetVisible(target_visibility); } +void NotificationViewMD::UpdateCornerRadius(int top_radius, int bottom_radius) { + MessageView::UpdateCornerRadius(top_radius, bottom_radius); + action_buttons_row_->SetBackground(views::CreateBackgroundFromPainter( + std::make_unique<NotificationBackgroundPainter>( + 0, bottom_radius, kActionsRowBackgroundColor))); +} + NotificationControlButtonsView* NotificationViewMD::GetControlButtonsView() const { return control_buttons_view_.get(); diff --git a/chromium/ui/message_center/views/notification_view_md.h b/chromium/ui/message_center/views/notification_view_md.h index c51c1abe6ee..4ead77eeb3c 100644 --- a/chromium/ui/message_center/views/notification_view_md.h +++ b/chromium/ui/message_center/views/notification_view_md.h @@ -188,6 +188,7 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD void UpdateWithNotification(const Notification& notification) override; void ButtonPressed(views::Button* sender, const ui::Event& event) override; void UpdateControlButtonsVisibility() override; + void UpdateCornerRadius(int top_radius, int bottom_radius) override; NotificationControlButtonsView* GetControlButtonsView() const override; bool IsExpanded() const override; void SetExpanded(bool expanded) override; diff --git a/chromium/ui/message_center/views/notification_view_md_unittest.cc b/chromium/ui/message_center/views/notification_view_md_unittest.cc index e30fac2465c..b39c64ce3b2 100644 --- a/chromium/ui/message_center/views/notification_view_md_unittest.cc +++ b/chromium/ui/message_center/views/notification_view_md_unittest.cc @@ -130,7 +130,7 @@ class NotificationViewMDTest void UpdateNotificationViews(const Notification& notification); float GetNotificationSlideAmount() const; - bool IsRemoved(const std::string& notification_id) const; + bool IsRemovedAfterIdle(const std::string& notification_id) const; void DispatchGesture(const ui::GestureEventDetails& details); void BeginScroll(); void EndScroll(); @@ -284,8 +284,9 @@ float NotificationViewMDTest::GetNotificationSlideAmount() const { .x(); } -bool NotificationViewMDTest::IsRemoved( +bool NotificationViewMDTest::IsRemovedAfterIdle( const std::string& notification_id) const { + base::RunLoop().RunUntilIdle(); return !MessageCenter::Get()->FindVisibleNotificationById(notification_id); } @@ -591,22 +592,22 @@ TEST_F(NotificationViewMDTest, SlideOut) { ui::ScopedAnimationDurationScaleMode zero_duration_scope( ui::ScopedAnimationDurationScaleMode::ZERO_DURATION); - EXPECT_FALSE(IsRemoved(kDefaultNotificationId)); + EXPECT_FALSE(IsRemovedAfterIdle(kDefaultNotificationId)); BeginScroll(); ScrollBy(-10); - EXPECT_FALSE(IsRemoved(kDefaultNotificationId)); + EXPECT_FALSE(IsRemovedAfterIdle(kDefaultNotificationId)); EXPECT_EQ(-10.f, GetNotificationSlideAmount()); EndScroll(); - EXPECT_FALSE(IsRemoved(kDefaultNotificationId)); + EXPECT_FALSE(IsRemovedAfterIdle(kDefaultNotificationId)); EXPECT_EQ(0.f, GetNotificationSlideAmount()); BeginScroll(); ScrollBy(-200); - EXPECT_FALSE(IsRemoved(kDefaultNotificationId)); + EXPECT_FALSE(IsRemovedAfterIdle(kDefaultNotificationId)); EXPECT_EQ(-200.f, GetNotificationSlideAmount()); EndScroll(); - EXPECT_TRUE(IsRemoved(kDefaultNotificationId)); + EXPECT_TRUE(IsRemovedAfterIdle(kDefaultNotificationId)); } TEST_F(NotificationViewMDTest, SlideOutNested) { @@ -615,18 +616,18 @@ TEST_F(NotificationViewMDTest, SlideOutNested) { BeginScroll(); ScrollBy(-10); - EXPECT_FALSE(IsRemoved(kDefaultNotificationId)); + EXPECT_FALSE(IsRemovedAfterIdle(kDefaultNotificationId)); EXPECT_EQ(-10.f, GetNotificationSlideAmount()); EndScroll(); - EXPECT_FALSE(IsRemoved(kDefaultNotificationId)); + EXPECT_FALSE(IsRemovedAfterIdle(kDefaultNotificationId)); EXPECT_EQ(0.f, GetNotificationSlideAmount()); BeginScroll(); ScrollBy(-200); - EXPECT_FALSE(IsRemoved(kDefaultNotificationId)); + EXPECT_FALSE(IsRemovedAfterIdle(kDefaultNotificationId)); EXPECT_EQ(-200.f, GetNotificationSlideAmount()); EndScroll(); - EXPECT_TRUE(IsRemoved(kDefaultNotificationId)); + EXPECT_TRUE(IsRemovedAfterIdle(kDefaultNotificationId)); } TEST_F(NotificationViewMDTest, DisableSlideForcibly) { @@ -637,20 +638,20 @@ TEST_F(NotificationViewMDTest, DisableSlideForcibly) { BeginScroll(); ScrollBy(-10); - EXPECT_FALSE(IsRemoved(kDefaultNotificationId)); + EXPECT_FALSE(IsRemovedAfterIdle(kDefaultNotificationId)); EXPECT_EQ(0.f, GetNotificationSlideAmount()); EndScroll(); - EXPECT_FALSE(IsRemoved(kDefaultNotificationId)); + EXPECT_FALSE(IsRemovedAfterIdle(kDefaultNotificationId)); EXPECT_EQ(0.f, GetNotificationSlideAmount()); notification_view()->DisableSlideForcibly(false); BeginScroll(); ScrollBy(-10); - EXPECT_FALSE(IsRemoved(kDefaultNotificationId)); + EXPECT_FALSE(IsRemovedAfterIdle(kDefaultNotificationId)); EXPECT_EQ(-10.f, GetNotificationSlideAmount()); EndScroll(); - EXPECT_FALSE(IsRemoved(kDefaultNotificationId)); + EXPECT_FALSE(IsRemovedAfterIdle(kDefaultNotificationId)); } // Pinning notification is ChromeOS only feature. @@ -666,10 +667,10 @@ TEST_F(NotificationViewMDTest, SlideOutPinned) { BeginScroll(); ScrollBy(-200); - EXPECT_FALSE(IsRemoved(kDefaultNotificationId)); + EXPECT_FALSE(IsRemovedAfterIdle(kDefaultNotificationId)); EXPECT_LT(-200.f, GetNotificationSlideAmount()); EndScroll(); - EXPECT_FALSE(IsRemoved(kDefaultNotificationId)); + EXPECT_FALSE(IsRemovedAfterIdle(kDefaultNotificationId)); } TEST_F(NotificationViewMDTest, Pinned) { @@ -707,10 +708,10 @@ TEST_F(NotificationViewMDTest, FixedViewMode) { BeginScroll(); ScrollBy(-200); - EXPECT_FALSE(IsRemoved(notification_id)); + EXPECT_FALSE(IsRemovedAfterIdle(notification_id)); EXPECT_EQ(0.f, GetNotificationSlideAmount()); EndScroll(); - EXPECT_FALSE(IsRemoved(notification_id)); + EXPECT_FALSE(IsRemovedAfterIdle(notification_id)); EXPECT_EQ(MessageView::Mode::SETTING, notification_view()->GetMode()); } diff --git a/chromium/ui/message_center/views/notification_view_unittest.cc b/chromium/ui/message_center/views/notification_view_unittest.cc index 8a5890a165a..b79aae0c2c0 100644 --- a/chromium/ui/message_center/views/notification_view_unittest.cc +++ b/chromium/ui/message_center/views/notification_view_unittest.cc @@ -128,8 +128,8 @@ class NotificationViewTest : public views::ViewsTestBase { std::copy(notification_view()->action_buttons_.begin(), notification_view()->action_buttons_.end(), std::back_inserter(vertical_order)); - std::vector<views::View*>::iterator current = vertical_order.begin(); - std::vector<views::View*>::iterator last = current++; + auto current = vertical_order.begin(); + auto last = current++; while (current != vertical_order.end()) { gfx::Point last_point = (*last)->origin(); views::View::ConvertPointToTarget( @@ -165,7 +165,8 @@ class NotificationViewTest : public views::ViewsTestBase { .x(); } - bool IsRemoved(const std::string& notification_id) const { + bool IsRemovedAfterIdle(const std::string& notification_id) const { + base::RunLoop().RunUntilIdle(); return !MessageCenter::Get()->FindVisibleNotificationById(notification_id); } @@ -625,18 +626,18 @@ TEST_F(NotificationViewTest, SlideOut) { BeginScroll(); ScrollBy(-10); - EXPECT_FALSE(IsRemoved(notification_id)); + EXPECT_FALSE(IsRemovedAfterIdle(notification_id)); EXPECT_EQ(-10.f, GetNotificationSlideAmount()); EndScroll(); - EXPECT_FALSE(IsRemoved(notification_id)); + EXPECT_FALSE(IsRemovedAfterIdle(notification_id)); EXPECT_EQ(0.f, GetNotificationSlideAmount()); BeginScroll(); ScrollBy(-200); - EXPECT_FALSE(IsRemoved(notification_id)); + EXPECT_FALSE(IsRemovedAfterIdle(notification_id)); EXPECT_EQ(-200.f, GetNotificationSlideAmount()); EndScroll(); - EXPECT_TRUE(IsRemoved(notification_id)); + EXPECT_TRUE(IsRemovedAfterIdle(notification_id)); } TEST_F(NotificationViewTest, SlideOutNested) { @@ -648,18 +649,18 @@ TEST_F(NotificationViewTest, SlideOutNested) { BeginScroll(); ScrollBy(-10); - EXPECT_FALSE(IsRemoved(notification_id)); + EXPECT_FALSE(IsRemovedAfterIdle(notification_id)); EXPECT_EQ(-10.f, GetNotificationSlideAmount()); EndScroll(); - EXPECT_FALSE(IsRemoved(notification_id)); + EXPECT_FALSE(IsRemovedAfterIdle(notification_id)); EXPECT_EQ(0.f, GetNotificationSlideAmount()); BeginScroll(); ScrollBy(-200); - EXPECT_FALSE(IsRemoved(notification_id)); + EXPECT_FALSE(IsRemovedAfterIdle(notification_id)); EXPECT_EQ(-200.f, GetNotificationSlideAmount()); EndScroll(); - EXPECT_TRUE(IsRemoved(notification_id)); + EXPECT_TRUE(IsRemovedAfterIdle(notification_id)); } // Pinning notification is ChromeOS only feature. @@ -676,10 +677,10 @@ TEST_F(NotificationViewTest, SlideOutPinned) { BeginScroll(); ScrollBy(-200); - EXPECT_FALSE(IsRemoved(notification_id)); + EXPECT_FALSE(IsRemovedAfterIdle(notification_id)); EXPECT_LT(-200.f, GetNotificationSlideAmount()); EndScroll(); - EXPECT_FALSE(IsRemoved(notification_id)); + EXPECT_FALSE(IsRemovedAfterIdle(notification_id)); } TEST_F(NotificationViewTest, PopupsCantPin) { @@ -701,11 +702,11 @@ TEST_F(NotificationViewTest, PopupsCantPin) { BeginScroll(); ScrollBy(-200); - EXPECT_FALSE(IsRemoved(notification_id)); + EXPECT_FALSE(IsRemovedAfterIdle(notification_id)); EXPECT_TRUE(shown_as_popup(notification_id)); EXPECT_EQ(-200.f, GetNotificationSlideAmount()); EndScroll(); - EXPECT_FALSE(IsRemoved(notification_id)); + EXPECT_FALSE(IsRemovedAfterIdle(notification_id)); EXPECT_FALSE(shown_as_popup(notification_id)); } diff --git a/chromium/ui/message_center/views/slidable_message_view.cc b/chromium/ui/message_center/views/slidable_message_view.cc deleted file mode 100644 index aef4f35bbef..00000000000 --- a/chromium/ui/message_center/views/slidable_message_view.cc +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) 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/message_center/views/slidable_message_view.h" - -#include "ui/message_center/public/cpp/features.h" -#include "ui/message_center/public/cpp/message_center_constants.h" -#include "ui/message_center/views/message_view.h" -#include "ui/views/background.h" -#include "ui/views/layout/fill_layout.h" -#include "ui/views/view.h" - -namespace message_center { - -SlidableMessageView::SlidableMessageView(MessageView* message_view) - : message_view_(message_view) { - SetFocusBehavior(views::View::FocusBehavior::NEVER); - - // Draw on its own layer to allow bound animation. - SetPaintToLayer(); - layer()->SetFillsBoundsOpaquely(false); - if (base::FeatureList::IsEnabled(message_center::kNotificationSwipeControl)) - SetBackground(views::CreateSolidBackground(kSwipeControlBackgroundColor)); - - SetLayoutManager(std::make_unique<views::FillLayout>()); - - // TODO(crbug.com/840497): Add button container child view. - - message_view_->AddSlideObserver(this); - AddChildView(message_view); -} - -SlidableMessageView::~SlidableMessageView() = default; - -void SlidableMessageView::OnSlideChanged(const std::string& notification_id) { - // TODO(crbug.com/840497): Show/hide control buttons. -} - -void SlidableMessageView::CloseSwipeControl() { - message_view_->CloseSwipeControl(); -} - -void SlidableMessageView::ChildPreferredSizeChanged(views::View* child) { - PreferredSizeChanged(); - InvalidateLayout(); -} - -void SlidableMessageView::ChildVisibilityChanged(views::View* child) { - PreferredSizeChanged(); - InvalidateLayout(); -} - -void SlidableMessageView::UpdateWithNotification( - const Notification& notification) { - message_view_->UpdateWithNotification(notification); -} - -gfx::Size SlidableMessageView::CalculatePreferredSize() const { - return message_view_->GetPreferredSize(); -} - -int SlidableMessageView::GetHeightForWidth(int width) const { - return message_view_->GetHeightForWidth(width); -} - -} // namespace message_center diff --git a/chromium/ui/message_center/views/slidable_message_view.h b/chromium/ui/message_center/views/slidable_message_view.h deleted file mode 100644 index 4811123a1c2..00000000000 --- a/chromium/ui/message_center/views/slidable_message_view.h +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright (c) 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_MESSAGE_CENTER_VIEWS_SLIDABLE_MESSAGE_VIEW_H_ -#define UI_MESSAGE_CENTER_VIEWS_SLIDABLE_MESSAGE_VIEW_H_ - -#include "ui/message_center/views/message_view.h" -#include "ui/views/view.h" - -namespace message_center { - -class MESSAGE_CENTER_EXPORT SlidableMessageView - : public views::View, - public MessageView::SlideObserver { - public: - SlidableMessageView(message_center::MessageView* message_view); - ~SlidableMessageView() override; - - MessageView* GetMessageView() const { return message_view_; } - - // MessageView::SlideObserver - void OnSlideChanged(const std::string& notification_id) override; - - void SetExpanded(bool expanded) { - return message_view_->SetExpanded(expanded); - } - - bool IsExpanded() const { return message_view_->IsExpanded(); } - - bool IsAutoExpandingAllowed() const { - return message_view_->IsAutoExpandingAllowed(); - } - - bool IsCloseButtonFocused() const { - return message_view_->IsCloseButtonFocused(); - } - - bool IsManuallyExpandedOrCollapsed() const { - return message_view_->IsManuallyExpandedOrCollapsed(); - } - - void SetManuallyExpandedOrCollapsed(bool value) { - return message_view_->SetManuallyExpandedOrCollapsed(value); - } - - // Updates this view with the new data contained in the notification. - void UpdateWithNotification(const Notification& notification); - - std::string notification_id() const { - return message_view_->notification_id(); - } - - MessageView::Mode GetMode() const { return message_view_->GetMode(); } - - void CloseSwipeControl(); - - void StartDismissAnimation(); - - // views::View - void ChildPreferredSizeChanged(views::View* child) override; - void ChildVisibilityChanged(views::View* child) override; - gfx::Size CalculatePreferredSize() const override; - int GetHeightForWidth(int width) const override; - - private: - MessageView* message_view_; - // TODO(crbug.com/840497): Add child view containing settings and snooze - // buttons. -}; - -} // namespace message_center - -#endif // UI_MESSAGE_CENTER_VIEWS_SLIDABLE_MESSAGE_VIEW_H_ diff --git a/chromium/ui/message_center/views/slide_out_controller.cc b/chromium/ui/message_center/views/slide_out_controller.cc index c42f6779713..f338931e7fc 100644 --- a/chromium/ui/message_center/views/slide_out_controller.cc +++ b/chromium/ui/message_center/views/slide_out_controller.cc @@ -160,7 +160,13 @@ void SlideOutController::SlideOutAndClose(int direction) { void SlideOutController::OnImplicitAnimationsCompleted() { delegate_->OnSlideChanged(); - delegate_->OnSlideOut(); + + // OnImplicitAnimationsCompleted is called from BeginMainFrame, so we should + // delay operation that might result in deletion of LayerTreeHost. + // https://crbug.com/895883 + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, + base::BindOnce(&Delegate::OnSlideOut, base::Unretained(delegate_))); } void SlideOutController::EnableSwipeControl(int button_count) { diff --git a/chromium/ui/message_center/views/slide_out_controller.h b/chromium/ui/message_center/views/slide_out_controller.h index 40c77b145f8..61048583093 100644 --- a/chromium/ui/message_center/views/slide_out_controller.h +++ b/chromium/ui/message_center/views/slide_out_controller.h @@ -53,6 +53,7 @@ class MESSAGE_CENTER_EXPORT SlideOutController // Enables the swipe control. Buttons will appea behind the view as user // slides it partially and it's kept open after the gesture. void EnableSwipeControl(int button_count); + float GetGestureAmount() const { return gesture_amount_; } // Moves slide back to the center position to closes the swipe control. // Effective only when swipe control is enabled by EnableSwipeControl(). diff --git a/chromium/ui/native_theme/common_theme.cc b/chromium/ui/native_theme/common_theme.cc index accf8288582..6b4f08797ba 100644 --- a/chromium/ui/native_theme/common_theme.cc +++ b/chromium/ui/native_theme/common_theme.cc @@ -24,18 +24,13 @@ SkColor GetAuraColor(NativeTheme::ColorId color_id, switch (color_id) { case NativeTheme::kColorId_ButtonEnabledColor: case NativeTheme::kColorId_ButtonHoverColor: - return SK_ColorBLACK; case NativeTheme::kColorId_MenuBorderColor: case NativeTheme::kColorId_MenuSeparatorColor: - return SK_ColorBLACK; case NativeTheme::kColorId_SeparatorColor: - return SK_ColorBLACK; - case NativeTheme::kColorId_FocusedBorderColor: - return gfx::kGoogleBlue900; case NativeTheme::kColorId_UnfocusedBorderColor: - return SK_ColorBLACK; case NativeTheme::kColorId_TabBottomBorder: return SK_ColorBLACK; + case NativeTheme::kColorId_FocusedBorderColor: case NativeTheme::kColorId_ProminentButtonColor: return gfx::kGoogleBlue900; default: @@ -44,7 +39,7 @@ SkColor GetAuraColor(NativeTheme::ColorId color_id, } // Second wave of MD colors (colors that only appear in secondary UI). - static const SkColor kPrimaryTextColor = SK_ColorBLACK; + constexpr SkColor kPrimaryTextColor = gfx::kGoogleGrey900; switch (color_id) { // Labels @@ -74,96 +69,22 @@ SkColor GetAuraColor(NativeTheme::ColorId color_id, } // Shared constant for disabled text. - static const SkColor kDisabledTextColor = SkColorSetRGB(0xA1, 0xA1, 0x92); + constexpr SkColor kDisabledTextColor = SkColorSetRGB(0xA1, 0xA1, 0x92); - // Dialogs: - static const SkColor kDialogBackgroundColor = SK_ColorWHITE; // Buttons: - static const SkColor kButtonEnabledColor = gfx::kChromeIconGrey; - static const SkColor kProminentButtonColor = gfx::kGoogleBlue500; - static const SkColor kProminentButtonTextColor = SK_ColorWHITE; - static const SkColor kBlueButtonTextColor = SK_ColorWHITE; - static const SkColor kBlueButtonShadowColor = SkColorSetRGB(0x53, 0x8C, 0xEA); - // MenuItem: - static const SkColor kTouchableMenuItemLabelColor = - SkColorSetRGB(0x20, 0x21, 0x24); - static const SkColor kActionableSubmenuVerticalSeparatorColor = - SkColorSetARGB(0x24, 0x20, 0x21, 0x24); - static const SkColor kMenuBackgroundColor = SK_ColorWHITE; - static const SkColor kMenuHighlightBackgroundColor = - SkColorSetA(SK_ColorBLACK, 0x14); - static const SkColor kSelectedMenuItemForegroundColor = SK_ColorBLACK; - static const SkColor kMenuBorderColor = SkColorSetRGB(0xBA, 0xBA, 0xBA); - static const SkColor kMenuSeparatorColor = SkColorSetRGB(0xE9, 0xE9, 0xE9); - static const SkColor kEnabledMenuItemForegroundColor = SK_ColorBLACK; - static const SkColor kMenuItemMinorTextColor = - SkColorSetA(SK_ColorBLACK, 0x89); - // Separator: - static const SkColor kSeparatorColor = SkColorSetRGB(0xE9, 0xE9, 0xE9); - // Link: - static const SkColor kLinkEnabledColor = gfx::kGoogleBlue700; + constexpr SkColor kButtonEnabledColor = gfx::kChromeIconGrey; // Text selection colors: - static const SkColor kTextSelectionBackgroundFocused = + constexpr SkColor kTextSelectionBackgroundFocused = SkColorSetARGB(0x54, 0x60, 0xA8, 0xEB); static const SkColor kTextSelectionColor = color_utils::AlphaBlend( SK_ColorBLACK, kTextSelectionBackgroundFocused, 0xdd); - // Textfield: - static const SkColor kTextfieldDefaultColor = SK_ColorBLACK; - static const SkColor kTextfieldDefaultBackground = SK_ColorWHITE; - static const SkColor kTextfieldReadOnlyColor = kDisabledTextColor; - static const SkColor kTextfieldReadOnlyBackground = SK_ColorWHITE; - // Results tables: - static const SkColor kResultsTableText = SK_ColorBLACK; - static const SkColor kResultsTableDimmedText = - SkColorSetRGB(0x64, 0x64, 0x64); - static const SkColor kResultsTableHoveredBackground = color_utils::AlphaBlend( - kTextSelectionBackgroundFocused, kTextfieldDefaultBackground, 0x40); - const SkColor kPositiveTextColor = SkColorSetRGB(0x0b, 0x80, 0x43); - const SkColor kNegativeTextColor = SkColorSetRGB(0xc5, 0x39, 0x29); - static const SkColor kResultsTablePositiveText = color_utils::AlphaBlend( - kPositiveTextColor, kTextfieldDefaultBackground, 0xDD); - static const SkColor kResultsTablePositiveHoveredText = - color_utils::AlphaBlend(kPositiveTextColor, - kResultsTableHoveredBackground, 0xDD); - static const SkColor kResultsTablePositiveSelectedText = - color_utils::AlphaBlend(kPositiveTextColor, - kTextSelectionBackgroundFocused, 0xDD); - static const SkColor kResultsTableNegativeText = color_utils::AlphaBlend( - kNegativeTextColor, kTextfieldDefaultBackground, 0xDD); - static const SkColor kResultsTableNegativeHoveredText = - color_utils::AlphaBlend(kNegativeTextColor, - kResultsTableHoveredBackground, 0xDD); - static const SkColor kResultsTableNegativeSelectedText = - color_utils::AlphaBlend(kNegativeTextColor, - kTextSelectionBackgroundFocused, 0xDD); - // Tooltip: - static const SkColor kTooltipBackground = SkColorSetA(SK_ColorBLACK, 0xCC); - static const SkColor kTooltipTextColor = SkColorSetA(SK_ColorWHITE, 0xDE); - // Tree: - static const SkColor kTreeBackground = SK_ColorWHITE; - static const SkColor kTreeTextColor = SK_ColorBLACK; - static const SkColor kTreeSelectedTextColor = SK_ColorBLACK; - static const SkColor kTreeSelectionBackgroundColor = - SkColorSetRGB(0xEE, 0xEE, 0xEE); - // Table: - static const SkColor kTableBackground = SK_ColorWHITE; - static const SkColor kTableTextColor = SK_ColorBLACK; - static const SkColor kTableSelectedTextColor = SK_ColorBLACK; - static const SkColor kTableSelectionBackgroundColor = - SkColorSetRGB(0xEE, 0xEE, 0xEE); - static const SkColor kTableGroupingIndicatorColor = - SkColorSetRGB(0xCC, 0xCC, 0xCC); - // Material spinner/throbber: - static const SkColor kThrobberSpinningColor = gfx::kGoogleBlue600; - static const SkColor kThrobberWaitingColor = SkColorSetRGB(0xA6, 0xA6, 0xA6); - static const SkColor kThrobberLightColor = SkColorSetRGB(0xF4, 0xF8, 0xFD); switch (color_id) { // Dialogs case NativeTheme::kColorId_WindowBackground: case NativeTheme::kColorId_DialogBackground: case NativeTheme::kColorId_BubbleBackground: - return kDialogBackgroundColor; + return SK_ColorWHITE; // Buttons case NativeTheme::kColorId_ButtonEnabledColor: @@ -174,13 +95,13 @@ SkColor GetAuraColor(NativeTheme::ColorId color_id, case NativeTheme::kColorId_BlueButtonDisabledColor: case NativeTheme::kColorId_BlueButtonPressedColor: case NativeTheme::kColorId_BlueButtonHoverColor: - return kBlueButtonTextColor; + return SK_ColorWHITE; case NativeTheme::kColorId_BlueButtonShadowColor: - return kBlueButtonShadowColor; + return SkColorSetRGB(0x53, 0x8C, 0xEA); case NativeTheme::kColorId_ProminentButtonColor: - return kProminentButtonColor; + return gfx::kGoogleBlue500; case NativeTheme::kColorId_TextOnProminentButtonColor: - return kProminentButtonTextColor; + return SK_ColorWHITE; case NativeTheme::kColorId_ButtonPressedShade: return SK_ColorTRANSPARENT; case NativeTheme::kColorId_ButtonDisabledColor: @@ -188,25 +109,24 @@ SkColor GetAuraColor(NativeTheme::ColorId color_id, // MenuItem case NativeTheme::kColorId_TouchableMenuItemLabelColor: - return kTouchableMenuItemLabelColor; + return gfx::kGoogleGrey900; case NativeTheme::kColorId_ActionableSubmenuVerticalSeparatorColor: - return kActionableSubmenuVerticalSeparatorColor; + return SkColorSetA(gfx::kGoogleGrey900, 0x24); case NativeTheme::kColorId_SelectedMenuItemForegroundColor: - return kSelectedMenuItemForegroundColor; + case NativeTheme::kColorId_EnabledMenuItemForegroundColor: + return SK_ColorBLACK; case NativeTheme::kColorId_MenuBorderColor: - return kMenuBorderColor; + return SkColorSetRGB(0xBA, 0xBA, 0xBA); case NativeTheme::kColorId_MenuSeparatorColor: - return kMenuSeparatorColor; + return SkColorSetRGB(0xE9, 0xE9, 0xE9); case NativeTheme::kColorId_MenuBackgroundColor: - return kMenuBackgroundColor; + return SK_ColorWHITE; case NativeTheme::kColorId_FocusedMenuItemBackgroundColor: - return kMenuHighlightBackgroundColor; - case NativeTheme::kColorId_EnabledMenuItemForegroundColor: - return kEnabledMenuItemForegroundColor; + return SkColorSetA(SK_ColorBLACK, 0x14); case NativeTheme::kColorId_DisabledMenuItemForegroundColor: return kDisabledTextColor; case NativeTheme::kColorId_MenuItemMinorTextColor: - return kMenuItemMinorTextColor; + return SkColorSetA(SK_ColorBLACK, 0x89); // Label case NativeTheme::kColorId_LabelEnabledColor: @@ -226,11 +146,11 @@ SkColor GetAuraColor(NativeTheme::ColorId color_id, case NativeTheme::kColorId_LinkEnabled: case NativeTheme::kColorId_LinkPressed: - return kLinkEnabledColor; + return gfx::kGoogleBlue700; // Separator case NativeTheme::kColorId_SeparatorColor: - return kSeparatorColor; + return SkColorSetRGB(0xE9, 0xE9, 0xE9); // TabbedPane case NativeTheme::kColorId_TabTitleColorActive: @@ -242,13 +162,12 @@ SkColor GetAuraColor(NativeTheme::ColorId color_id, // Textfield case NativeTheme::kColorId_TextfieldDefaultColor: - return kTextfieldDefaultColor; + return SK_ColorBLACK; case NativeTheme::kColorId_TextfieldDefaultBackground: - return kTextfieldDefaultBackground; - case NativeTheme::kColorId_TextfieldReadOnlyColor: - return kTextfieldReadOnlyColor; case NativeTheme::kColorId_TextfieldReadOnlyBackground: - return kTextfieldReadOnlyBackground; + return SK_ColorWHITE; + case NativeTheme::kColorId_TextfieldReadOnlyColor: + return kDisabledTextColor; case NativeTheme::kColorId_TextfieldSelectionColor: return kTextSelectionColor; case NativeTheme::kColorId_TextfieldSelectionBackgroundFocused: @@ -256,35 +175,33 @@ SkColor GetAuraColor(NativeTheme::ColorId color_id, // Tooltip case NativeTheme::kColorId_TooltipBackground: - return kTooltipBackground; + return SkColorSetA(SK_ColorBLACK, 0xCC); case NativeTheme::kColorId_TooltipText: - return kTooltipTextColor; + return SkColorSetA(SK_ColorWHITE, 0xDE); // Tree case NativeTheme::kColorId_TreeBackground: - return kTreeBackground; + return SK_ColorWHITE; case NativeTheme::kColorId_TreeText: - return kTreeTextColor; case NativeTheme::kColorId_TreeSelectedText: case NativeTheme::kColorId_TreeSelectedTextUnfocused: - return kTreeSelectedTextColor; + return SK_ColorBLACK; case NativeTheme::kColorId_TreeSelectionBackgroundFocused: case NativeTheme::kColorId_TreeSelectionBackgroundUnfocused: - return kTreeSelectionBackgroundColor; + return SkColorSetRGB(0xEE, 0xEE, 0xEE); // Table case NativeTheme::kColorId_TableBackground: - return kTableBackground; + return SK_ColorWHITE; case NativeTheme::kColorId_TableText: - return kTableTextColor; case NativeTheme::kColorId_TableSelectedText: case NativeTheme::kColorId_TableSelectedTextUnfocused: - return kTableSelectedTextColor; + return SK_ColorBLACK; case NativeTheme::kColorId_TableSelectionBackgroundFocused: case NativeTheme::kColorId_TableSelectionBackgroundUnfocused: - return kTableSelectionBackgroundColor; + return SkColorSetRGB(0xEE, 0xEE, 0xEE); case NativeTheme::kColorId_TableGroupingIndicatorColor: - return kTableGroupingIndicatorColor; + return SkColorSetRGB(0xCC, 0xCC, 0xCC); // Table Header case NativeTheme::kColorId_TableHeaderText: @@ -304,47 +221,23 @@ SkColor GetAuraColor(NativeTheme::ColorId color_id, // Results Tables case NativeTheme::kColorId_ResultsTableNormalBackground: - return kTextfieldDefaultBackground; + return SK_ColorWHITE; case NativeTheme::kColorId_ResultsTableHoveredBackground: return SkColorSetA(base_theme->GetSystemColor( NativeTheme::kColorId_ResultsTableNormalText), 0x0D); - case NativeTheme::kColorId_ResultsTableSelectedBackground: - return SkColorSetA(base_theme->GetSystemColor( - NativeTheme::kColorId_ResultsTableNormalText), - 0x14); case NativeTheme::kColorId_ResultsTableNormalText: - case NativeTheme::kColorId_ResultsTableHoveredText: - case NativeTheme::kColorId_ResultsTableSelectedText: - return kResultsTableText; - case NativeTheme::kColorId_ResultsTableNormalDimmedText: - case NativeTheme::kColorId_ResultsTableHoveredDimmedText: - case NativeTheme::kColorId_ResultsTableSelectedDimmedText: - return kResultsTableDimmedText; - case NativeTheme::kColorId_ResultsTableNormalUrl: - case NativeTheme::kColorId_ResultsTableHoveredUrl: - case NativeTheme::kColorId_ResultsTableSelectedUrl: - return base_theme->GetSystemColor(NativeTheme::kColorId_LinkEnabled); - case NativeTheme::kColorId_ResultsTablePositiveText: - return kResultsTablePositiveText; - case NativeTheme::kColorId_ResultsTablePositiveHoveredText: - return kResultsTablePositiveHoveredText; - case NativeTheme::kColorId_ResultsTablePositiveSelectedText: - return kResultsTablePositiveSelectedText; - case NativeTheme::kColorId_ResultsTableNegativeText: - return kResultsTableNegativeText; - case NativeTheme::kColorId_ResultsTableNegativeHoveredText: - return kResultsTableNegativeHoveredText; - case NativeTheme::kColorId_ResultsTableNegativeSelectedText: - return kResultsTableNegativeSelectedText; + return SK_ColorBLACK; + case NativeTheme::kColorId_ResultsTableDimmedText: + return SkColorSetRGB(0x64, 0x64, 0x64); // Material spinner/throbber case NativeTheme::kColorId_ThrobberSpinningColor: - return kThrobberSpinningColor; + return gfx::kGoogleBlue600; case NativeTheme::kColorId_ThrobberWaitingColor: - return kThrobberWaitingColor; + return SkColorSetRGB(0xA6, 0xA6, 0xA6); case NativeTheme::kColorId_ThrobberLightColor: - return kThrobberLightColor; + return SkColorSetRGB(0xF4, 0xF8, 0xFD); // Alert icon colors case NativeTheme::kColorId_AlertSeverityLow: diff --git a/chromium/ui/native_theme/native_theme.h b/chromium/ui/native_theme/native_theme.h index 36ead2eed3c..b2284ad5da7 100644 --- a/chromium/ui/native_theme/native_theme.h +++ b/chromium/ui/native_theme/native_theme.h @@ -368,26 +368,8 @@ class NATIVE_THEME_EXPORT NativeTheme { // Results Tables, such as the omnibox. kColorId_ResultsTableNormalBackground, kColorId_ResultsTableHoveredBackground, - kColorId_ResultsTableSelectedBackground, kColorId_ResultsTableNormalText, - kColorId_ResultsTableHoveredText, - kColorId_ResultsTableSelectedText, - kColorId_ResultsTableNormalDimmedText, - kColorId_ResultsTableHoveredDimmedText, - kColorId_ResultsTableSelectedDimmedText, - kColorId_ResultsTableNormalUrl, - kColorId_ResultsTableHoveredUrl, - kColorId_ResultsTableSelectedUrl, - // Positive text refers to good (often rendered in green) text, such as the - // stock value went up. - kColorId_ResultsTablePositiveText, - kColorId_ResultsTablePositiveHoveredText, - kColorId_ResultsTablePositiveSelectedText, - // Negative text refers to something alarming (often rendered in red), such - // as the stock value went down. - kColorId_ResultsTableNegativeText, - kColorId_ResultsTableNegativeHoveredText, - kColorId_ResultsTableNegativeSelectedText, + kColorId_ResultsTableDimmedText, // Colors for the material spinner (aka throbber). kColorId_ThrobberSpinningColor, kColorId_ThrobberWaitingColor, diff --git a/chromium/ui/native_theme/native_theme_android.cc b/chromium/ui/native_theme/native_theme_android.cc index cd6bc1d04aa..3ddb6434ec4 100644 --- a/chromium/ui/native_theme/native_theme_android.cc +++ b/chromium/ui/native_theme/native_theme_android.cc @@ -29,8 +29,8 @@ NativeTheme* NativeTheme::GetInstanceForNativeUi() { // static NativeThemeAndroid* NativeThemeAndroid::instance() { - CR_DEFINE_STATIC_LOCAL(NativeThemeAndroid, s_native_theme, ()); - return &s_native_theme; + static base::NoDestructor<NativeThemeAndroid> s_native_theme; + return s_native_theme.get(); } gfx::Size NativeThemeAndroid::GetPartSize(Part part, diff --git a/chromium/ui/native_theme/native_theme_android.h b/chromium/ui/native_theme/native_theme_android.h index 52b42e59b72..5736f9712ea 100644 --- a/chromium/ui/native_theme/native_theme_android.h +++ b/chromium/ui/native_theme/native_theme_android.h @@ -6,6 +6,7 @@ #define UI_NATIVE_THEME_NATIVE_THEME_ANDROID_H_ #include "base/macros.h" +#include "base/no_destructor.h" #include "ui/native_theme/native_theme_base.h" namespace ui { @@ -21,6 +22,7 @@ class NativeThemeAndroid : public NativeThemeBase { protected: friend class NativeTheme; + friend class base::NoDestructor<NativeThemeAndroid>; static NativeThemeAndroid* instance(); // NativeThemeBase: diff --git a/chromium/ui/native_theme/native_theme_aura.cc b/chromium/ui/native_theme/native_theme_aura.cc index e5ec3bb2013..4409bb629bb 100644 --- a/chromium/ui/native_theme/native_theme_aura.cc +++ b/chromium/ui/native_theme/native_theme_aura.cc @@ -88,15 +88,15 @@ NativeThemeAura::~NativeThemeAura() {} // static NativeThemeAura* NativeThemeAura::instance() { - CR_DEFINE_STATIC_LOCAL(NativeThemeAura, s_native_theme, (false)); - return &s_native_theme; + static base::NoDestructor<NativeThemeAura> s_native_theme(false); + return s_native_theme.get(); } // static NativeThemeAura* NativeThemeAura::web_instance() { - CR_DEFINE_STATIC_LOCAL(NativeThemeAura, s_native_theme_for_web, - (IsOverlayScrollbarEnabled())); - return &s_native_theme_for_web; + static base::NoDestructor<NativeThemeAura> s_native_theme_for_web( + IsOverlayScrollbarEnabled()); + return s_native_theme_for_web.get(); } // This implementation returns hardcoded colors. diff --git a/chromium/ui/native_theme/native_theme_aura.h b/chromium/ui/native_theme/native_theme_aura.h index eeebd051e3c..86185143513 100644 --- a/chromium/ui/native_theme/native_theme_aura.h +++ b/chromium/ui/native_theme/native_theme_aura.h @@ -6,6 +6,7 @@ #define UI_NATIVE_THEME_NATIVE_THEME_AURA_H_ #include "base/macros.h" +#include "base/no_destructor.h" #include "ui/native_theme/native_theme_base.h" namespace ui { @@ -15,6 +16,7 @@ class NATIVE_THEME_EXPORT NativeThemeAura : public NativeThemeBase { protected: friend class NativeTheme; friend class NativeThemeAuraTest; + friend class base::NoDestructor<NativeThemeAura>; explicit NativeThemeAura(bool use_overlay_scrollbars); ~NativeThemeAura() override; diff --git a/chromium/ui/native_theme/native_theme_dark_aura.cc b/chromium/ui/native_theme/native_theme_dark_aura.cc index 885cba0ac53..6ab77cd84e7 100644 --- a/chromium/ui/native_theme/native_theme_dark_aura.cc +++ b/chromium/ui/native_theme/native_theme_dark_aura.cc @@ -9,27 +9,14 @@ namespace ui { NativeThemeDarkAura* NativeThemeDarkAura::instance() { - CR_DEFINE_STATIC_LOCAL(NativeThemeDarkAura, s_native_theme, ()); - return &s_native_theme; + static base::NoDestructor<NativeThemeDarkAura> s_native_theme; + return s_native_theme.get(); } SkColor NativeThemeDarkAura::GetSystemColor(ColorId color_id) const { - static const SkColor kPrimaryTextColor = SK_ColorWHITE; - - static const SkColor kButtonEnabledColor = SK_ColorWHITE; - - static const SkColor kTextfieldDefaultColor = SK_ColorWHITE; - static const SkColor kTextfieldDefaultBackground = - SkColorSetRGB(0x62, 0x62, 0x62); - static const SkColor kTextSelectionBackgroundFocused = + constexpr SkColor kTextSelectionBackgroundFocused = SkColorSetA(gfx::kGoogleBlue700, 0xCC); - static const SkColor kResultsTableNormalBackground = - SkColorSetRGB(0x28, 0x28, 0x28); - static const SkColor kResultsTableText = SK_ColorWHITE; - static const SkColor kResultsTableDimmedText = - SkColorSetA(kResultsTableText, 0x80); - switch (color_id) { // Window case kColorId_WindowBackground: @@ -39,14 +26,14 @@ SkColor NativeThemeDarkAura::GetSystemColor(ColorId color_id) const { // Button case kColorId_ButtonEnabledColor: - return kButtonEnabledColor; + return SK_ColorWHITE; case kColorId_ProminentButtonColor: return gfx::kGoogleBlue600; // Label case kColorId_LabelEnabledColor: case kColorId_LabelTextSelectionColor: - return kPrimaryTextColor; + return SK_ColorWHITE; case kColorId_LabelTextSelectionBackgroundFocused: return kTextSelectionBackgroundFocused; @@ -58,23 +45,19 @@ SkColor NativeThemeDarkAura::GetSystemColor(ColorId color_id) const { // Textfield case kColorId_TextfieldDefaultColor: case kColorId_TextfieldSelectionColor: - return kTextfieldDefaultColor; + return SK_ColorWHITE; case kColorId_TextfieldDefaultBackground: - return kTextfieldDefaultBackground; + return SkColorSetRGB(0x62, 0x62, 0x62); case kColorId_TextfieldSelectionBackgroundFocused: return kTextSelectionBackgroundFocused; // Results Tables case kColorId_ResultsTableNormalBackground: - return kResultsTableNormalBackground; + return SkColorSetRGB(0x28, 0x28, 0x28); case kColorId_ResultsTableNormalText: - case kColorId_ResultsTableHoveredText: - case kColorId_ResultsTableSelectedText: - return kResultsTableText; - case kColorId_ResultsTableNormalDimmedText: - case kColorId_ResultsTableHoveredDimmedText: - case kColorId_ResultsTableSelectedDimmedText: - return kResultsTableDimmedText; + return SK_ColorWHITE; + case kColorId_ResultsTableDimmedText: + return SkColorSetA(GetSystemColor(kColorId_ResultsTableNormalText), 0x80); // FocusableBorder case kColorId_FocusedBorderColor: @@ -93,10 +76,6 @@ SkColor NativeThemeDarkAura::GetSystemColor(ColorId color_id) const { case kColorId_TextOnProminentButtonColor: case kColorId_ButtonPressedShade: case kColorId_ResultsTableHoveredBackground: - case kColorId_ResultsTableSelectedBackground: - case kColorId_ResultsTableNormalUrl: - case kColorId_ResultsTableHoveredUrl: - case kColorId_ResultsTableSelectedUrl: return NativeThemeAura::GetSystemColor(color_id); // Any other color is not defined and shouldn't be used in a dark theme. @@ -142,12 +121,6 @@ SkColor NativeThemeDarkAura::GetSystemColor(ColorId color_id) const { case kColorId_TableHeaderText: case kColorId_TableHeaderBackground: case kColorId_TableHeaderSeparator: - case kColorId_ResultsTablePositiveText: - case kColorId_ResultsTablePositiveHoveredText: - case kColorId_ResultsTablePositiveSelectedText: - case kColorId_ResultsTableNegativeText: - case kColorId_ResultsTableNegativeHoveredText: - case kColorId_ResultsTableNegativeSelectedText: case kColorId_SeparatorColor: case kColorId_ThrobberSpinningColor: case kColorId_ThrobberWaitingColor: diff --git a/chromium/ui/native_theme/native_theme_dark_aura.h b/chromium/ui/native_theme/native_theme_dark_aura.h index a014d7338f7..980df049507 100644 --- a/chromium/ui/native_theme/native_theme_dark_aura.h +++ b/chromium/ui/native_theme/native_theme_dark_aura.h @@ -6,6 +6,7 @@ #define UI_NATIVE_THEME_NATIVE_THEME_DARK_AURA_H_ #include "base/macros.h" +#include "base/no_destructor.h" #include "ui/native_theme/native_theme_aura.h" namespace ui { @@ -20,6 +21,8 @@ class NATIVE_THEME_EXPORT NativeThemeDarkAura : public NativeThemeAura { SkColor GetSystemColor(ColorId color_id) const override; private: + friend class base::NoDestructor<NativeThemeDarkAura>; + NativeThemeDarkAura(); ~NativeThemeDarkAura() override; diff --git a/chromium/ui/native_theme/native_theme_mac.h b/chromium/ui/native_theme/native_theme_mac.h index 478afb67fca..83007af4466 100644 --- a/chromium/ui/native_theme/native_theme_mac.h +++ b/chromium/ui/native_theme/native_theme_mac.h @@ -6,6 +6,7 @@ #define UI_NATIVE_THEME_NATIVE_THEME_MAC_H_ #include "base/macros.h" +#include "base/no_destructor.h" #include "ui/native_theme/native_theme_base.h" #include "ui/native_theme/native_theme_export.h" @@ -60,6 +61,7 @@ class NATIVE_THEME_EXPORT NativeThemeMac : public NativeThemeBase { protected: friend class NativeTheme; + friend class base::NoDestructor<NativeThemeMac>; static NativeThemeMac* instance(); private: diff --git a/chromium/ui/native_theme/native_theme_mac.mm b/chromium/ui/native_theme/native_theme_mac.mm index eb567ffcf31..17036c74f8f 100644 --- a/chromium/ui/native_theme/native_theme_mac.mm +++ b/chromium/ui/native_theme/native_theme_mac.mm @@ -99,8 +99,8 @@ NativeTheme* NativeTheme::GetInstanceForNativeUi() { // static NativeThemeMac* NativeThemeMac::instance() { - CR_DEFINE_STATIC_LOCAL(NativeThemeMac, s_native_theme, ()); - return &s_native_theme; + static base::NoDestructor<NativeThemeMac> s_native_theme; + return s_native_theme.get(); } // static diff --git a/chromium/ui/native_theme/native_theme_win.cc b/chromium/ui/native_theme/native_theme_win.cc index 839974db22b..c0344b4337d 100644 --- a/chromium/ui/native_theme/native_theme_win.cc +++ b/chromium/ui/native_theme/native_theme_win.cc @@ -157,8 +157,8 @@ void NativeThemeWin::CloseHandles() { // static NativeThemeWin* NativeThemeWin::instance() { - CR_DEFINE_STATIC_LOCAL(NativeThemeWin, s_native_theme, ()); - return &s_native_theme; + static base::NoDestructor<NativeThemeWin> s_native_theme; + return s_native_theme.get(); } gfx::Size NativeThemeWin::GetPartSize(Part part, @@ -416,21 +416,16 @@ void NativeThemeWin::PaintDirect(SkCanvas* destination_canvas, } SkColor NativeThemeWin::GetSystemColor(ColorId color_id) const { - // TODO: Obtain the correct colors using GetSysColor. + // TODO: Obtain the correct colors for these using GetSysColor. // Button: - const SkColor kButtonHoverColor = SkColorSetRGB(6, 45, 117); - const SkColor kProminentButtonColorInvert = gfx::kGoogleBlue300; + constexpr SkColor kButtonHoverColor = SkColorSetRGB(6, 45, 117); + constexpr SkColor kProminentButtonColorInvert = gfx::kGoogleBlue300; // MenuItem: - const SkColor kMenuSchemeHighlightBackgroundColorInvert = + constexpr SkColor kMenuSchemeHighlightBackgroundColorInvert = SkColorSetRGB(0x30, 0x30, 0x30); - // Table: - const SkColor kPositiveTextColor = SkColorSetRGB(0x0b, 0x80, 0x43); - const SkColor kNegativeTextColor = SkColorSetRGB(0xc5, 0x39, 0x29); - // Results Tables: - const SkColor kResultsTableUrlColor = gfx::kGoogleBlue700; - const SkColor kResultsTableSelectedUrlColor = SK_ColorWHITE; // Label: - const SkColor kLabelTextSelectionBackgroundFocusedColor = gfx::kGoogleBlue700; + constexpr SkColor kLabelTextSelectionBackgroundFocusedColor = + gfx::kGoogleBlue700; switch (color_id) { // Windows @@ -444,7 +439,6 @@ SkColor NativeThemeWin::GetSystemColor(ColorId color_id) const { // FocusableBorder case kColorId_FocusedBorderColor: - break; case kColorId_UnfocusedBorderColor: break; @@ -523,57 +517,11 @@ SkColor NativeThemeWin::GetSystemColor(ColorId color_id) const { case kColorId_ResultsTableHoveredBackground: return color_utils::AlphaBlend(system_colors_[COLOR_HIGHLIGHT], system_colors_[COLOR_WINDOW], 0x40); - case kColorId_ResultsTableSelectedBackground: - return system_colors_[COLOR_HIGHLIGHT]; case kColorId_ResultsTableNormalText: return system_colors_[COLOR_WINDOWTEXT]; - case kColorId_ResultsTableHoveredText: - return color_utils::GetReadableColor( - system_colors_[COLOR_WINDOWTEXT], - GetSystemColor(kColorId_ResultsTableHoveredBackground)); - case kColorId_ResultsTableSelectedText: - return system_colors_[COLOR_HIGHLIGHTTEXT]; - case kColorId_ResultsTableNormalDimmedText: + case kColorId_ResultsTableDimmedText: return color_utils::AlphaBlend(system_colors_[COLOR_WINDOWTEXT], system_colors_[COLOR_WINDOW], 0x80); - case kColorId_ResultsTableHoveredDimmedText: - return color_utils::AlphaBlend( - system_colors_[COLOR_WINDOWTEXT], - GetSystemColor(kColorId_ResultsTableHoveredBackground), 0x80); - case kColorId_ResultsTableSelectedDimmedText: - return color_utils::AlphaBlend(system_colors_[COLOR_HIGHLIGHTTEXT], - system_colors_[COLOR_HIGHLIGHT], 0x80); - case kColorId_ResultsTableNormalUrl: - return color_utils::GetReadableColor(kResultsTableUrlColor, - system_colors_[COLOR_WINDOW]); - case kColorId_ResultsTableHoveredUrl: - return color_utils::PickContrastingColor( - kResultsTableUrlColor, kResultsTableSelectedUrlColor, - GetSystemColor(kColorId_ResultsTableHoveredBackground)); - case kColorId_ResultsTableSelectedUrl: - return color_utils::PickContrastingColor( - kResultsTableUrlColor, kResultsTableSelectedUrlColor, - system_colors_[COLOR_HIGHLIGHT]); - case kColorId_ResultsTablePositiveText: - return color_utils::GetReadableColor(kPositiveTextColor, - system_colors_[COLOR_WINDOW]); - case kColorId_ResultsTablePositiveHoveredText: - return color_utils::GetReadableColor( - kPositiveTextColor, - GetSystemColor(kColorId_ResultsTableHoveredBackground)); - case kColorId_ResultsTablePositiveSelectedText: - return color_utils::GetReadableColor(kPositiveTextColor, - system_colors_[COLOR_HIGHLIGHT]); - case kColorId_ResultsTableNegativeText: - return color_utils::GetReadableColor(kNegativeTextColor, - system_colors_[COLOR_WINDOW]); - case kColorId_ResultsTableNegativeHoveredText: - return color_utils::GetReadableColor( - kNegativeTextColor, - GetSystemColor(kColorId_ResultsTableHoveredBackground)); - case kColorId_ResultsTableNegativeSelectedText: - return color_utils::GetReadableColor(kNegativeTextColor, - system_colors_[COLOR_HIGHLIGHT]); default: break; } @@ -666,7 +614,7 @@ void NativeThemeWin::PaintIndirect(cc::PaintCanvas* destination_canvas, // To work-around this, mark all pixels with a placeholder value, to detect // which pixels get touched by the paint operation. After paint, set any // pixels that have alpha 0 to opaque and placeholders to fully-transparent. - const SkColor placeholder = SkColorSetARGB(1, 0, 0, 0); + constexpr SkColor placeholder = SkColorSetARGB(1, 0, 0, 0); offscreen_canvas->clear(placeholder); // Offset destination rects to have origin (0,0). diff --git a/chromium/ui/native_theme/native_theme_win.h b/chromium/ui/native_theme/native_theme_win.h index ab7f78cfcbd..9ee7350fa64 100644 --- a/chromium/ui/native_theme/native_theme_win.h +++ b/chromium/ui/native_theme/native_theme_win.h @@ -18,6 +18,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" +#include "base/no_destructor.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/sys_color_change_listener.h" @@ -82,6 +83,7 @@ class NATIVE_THEME_EXPORT NativeThemeWin : public NativeTheme, protected: friend class NativeTheme; + friend class base::NoDestructor<NativeThemeWin>; // Gets our singleton instance. static NativeThemeWin* instance(); diff --git a/chromium/ui/ozone/common/linux/OWNERS b/chromium/ui/ozone/common/linux/OWNERS index 363a93a5f95..d047913b027 100644 --- a/chromium/ui/ozone/common/linux/OWNERS +++ b/chromium/ui/ozone/common/linux/OWNERS @@ -1,2 +1,3 @@ dcastagna@chromium.org dnicoara@chromium.org +msisov@igalia.com diff --git a/chromium/ui/ozone/common/linux/gbm_buffer.h b/chromium/ui/ozone/common/linux/gbm_buffer.h index 12c13bb2d67..c6e0daceafb 100644 --- a/chromium/ui/ozone/common/linux/gbm_buffer.h +++ b/chromium/ui/ozone/common/linux/gbm_buffer.h @@ -7,10 +7,13 @@ #include <inttypes.h> +#include "third_party/skia/include/core/SkRefCnt.h" #include "ui/gfx/buffer_types.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/native_pixmap_handle.h" +class SkSurface; + namespace ui { class GbmBuffer { @@ -34,6 +37,7 @@ class GbmBuffer { virtual size_t GetPlaneSize(size_t plane) const = 0; virtual uint32_t GetHandle() const = 0; virtual gfx::NativePixmapHandle ExportHandle() const = 0; + virtual sk_sp<SkSurface> GetSurface() = 0; }; } // namespace ui diff --git a/chromium/ui/ozone/common/linux/gbm_wrapper.cc b/chromium/ui/ozone/common/linux/gbm_wrapper.cc index d05fe026143..ba55b0e5685 100644 --- a/chromium/ui/ozone/common/linux/gbm_wrapper.cc +++ b/chromium/ui/ozone/common/linux/gbm_wrapper.cc @@ -7,6 +7,7 @@ #include <gbm.h> #include "base/posix/eintr_wrapper.h" +#include "third_party/skia/include/core/SkSurface.h" #include "ui/gfx/buffer_format_util.h" #include "ui/ozone/common/linux/drm_util_linux.h" #include "ui/ozone/common/linux/gbm_buffer.h" @@ -31,7 +32,10 @@ class Buffer final : public ui::GbmBuffer { size_(size), planes_(std::move(planes)) {} - ~Buffer() override { gbm_bo_destroy(bo_); } + ~Buffer() override { + DCHECK(!mmap_data_); + gbm_bo_destroy(bo_); + } uint32_t GetFormat() const override { return format_; } uint64_t GetFormatModifier() const override { return format_modifier_; } @@ -97,8 +101,24 @@ class Buffer final : public ui::GbmBuffer { return handle; } + sk_sp<SkSurface> GetSurface() override { + DCHECK(!mmap_data_); + uint32_t stride; + void* addr; + addr = gbm_bo_map(bo_, 0, 0, gbm_bo_get_width(bo_), gbm_bo_get_height(bo_), + GBM_BO_TRANSFER_READ_WRITE, &stride, &mmap_data_, 0); + + if (!addr) + return nullptr; + SkImageInfo info = + SkImageInfo::MakeN32Premul(size_.width(), size_.height()); + return SkSurface::MakeRasterDirectReleaseProc(info, addr, stride, + &Buffer::UnmapGbmBo, this); + } + private: gbm_bo* bo_ = nullptr; + void* mmap_data_ = nullptr; uint32_t format_ = 0; uint64_t format_modifier_ = 0; @@ -110,6 +130,12 @@ class Buffer final : public ui::GbmBuffer { std::vector<gfx::NativePixmapPlane> planes_; + static void UnmapGbmBo(void* pixels, void* context) { + Buffer* buffer = static_cast<Buffer*>(context); + gbm_bo_unmap(buffer->bo_, buffer->mmap_data_); + buffer->mmap_data_ = nullptr; + } + DISALLOW_COPY_AND_ASSIGN(Buffer); }; diff --git a/chromium/ui/ozone/demo/BUILD.gn b/chromium/ui/ozone/demo/BUILD.gn index e76631b05d4..5b7e6059daf 100644 --- a/chromium/ui/ozone/demo/BUILD.gn +++ b/chromium/ui/ozone/demo/BUILD.gn @@ -39,7 +39,7 @@ source_set("ozone_demo_lib") { ] if (is_fuchsia) { - deps += [ "//third_party/fuchsia-sdk:policy" ] + deps += [ "//third_party/fuchsia-sdk/sdk:policy" ] } } @@ -101,11 +101,13 @@ executable("skia_demo") { if (is_fuchsia) { fuchsia_package("ozone_demo_pkg") { + testonly = true binary = ":ozone_demo" package_name_override = "ozone_demo" } fuchsia_package_runner("ozone_demo_fuchsia") { + testonly = true package = ":ozone_demo_pkg" package_name_override = "ozone_demo" } diff --git a/chromium/ui/ozone/demo/demo_window.cc b/chromium/ui/ozone/demo/demo_window.cc index a3397d5fad1..38deff0067c 100644 --- a/chromium/ui/ozone/demo/demo_window.cc +++ b/chromium/ui/ozone/demo/demo_window.cc @@ -66,16 +66,16 @@ gfx::Size DemoWindow::GetSize() { } void DemoWindow::Start() { - base::ThreadTaskRunnerHandle::Get()->PostTask( - FROM_HERE, - base::BindOnce(&DemoWindow::StartOnGpu, weak_ptr_factory_.GetWeakPtr())); + StartRendererIfNecessary(); } void DemoWindow::Quit() { window_manager_->Quit(); } -void DemoWindow::OnBoundsChanged(const gfx::Rect& new_bounds) {} +void DemoWindow::OnBoundsChanged(const gfx::Rect& new_bounds) { + StartRendererIfNecessary(); +} void DemoWindow::OnDamageRect(const gfx::Rect& damaged_region) {} @@ -105,7 +105,9 @@ void DemoWindow::OnAcceleratedWidgetDestroyed() { void DemoWindow::OnActivationChanged(bool active) {} -void DemoWindow::StartOnGpu() { +void DemoWindow::StartRendererIfNecessary() { + if (renderer_ || GetSize().IsEmpty()) + return; renderer_ = renderer_factory_->CreateRenderer(GetAcceleratedWidget(), GetSize()); if (!renderer_->Initialize()) diff --git a/chromium/ui/ozone/demo/demo_window.h b/chromium/ui/ozone/demo/demo_window.h index 720ac4cd808..a0be6aead25 100644 --- a/chromium/ui/ozone/demo/demo_window.h +++ b/chromium/ui/ozone/demo/demo_window.h @@ -47,7 +47,7 @@ class DemoWindow : public PlatformWindowDelegate { private: // Since we pretend to have a GPU process, we should also pretend to // initialize the GPU resources via a posted task. - void StartOnGpu(); + void StartRendererIfNecessary(); WindowManager* window_manager_; // Not owned. RendererFactory* renderer_factory_; // Not owned. diff --git a/chromium/ui/ozone/demo/simple_renderer_factory.cc b/chromium/ui/ozone/demo/simple_renderer_factory.cc index 7345ab9d467..328cfe419e0 100644 --- a/chromium/ui/ozone/demo/simple_renderer_factory.cc +++ b/chromium/ui/ozone/demo/simple_renderer_factory.cc @@ -18,6 +18,7 @@ #if BUILDFLAG(ENABLE_VULKAN) #include "gpu/vulkan/init/vulkan_factory.h" +#include "gpu/vulkan/vulkan_surface.h" #include "ui/ozone/demo/vulkan_overlay_renderer.h" #include "ui/ozone/demo/vulkan_renderer.h" #endif @@ -102,7 +103,10 @@ std::unique_ptr<Renderer> SimpleRendererFactory::CreateRenderer( std::move(overlay_surface), surface_factory_ozone, vulkan_implementation_.get(), widget, size); } - return std::make_unique<VulkanRenderer>(vulkan_implementation_.get(), + std::unique_ptr<gpu::VulkanSurface> vulkan_surface = + vulkan_implementation_->CreateViewSurface(widget); + return std::make_unique<VulkanRenderer>(std::move(vulkan_surface), + vulkan_implementation_.get(), widget, size); } #endif diff --git a/chromium/ui/ozone/demo/vulkan_overlay_renderer.cc b/chromium/ui/ozone/demo/vulkan_overlay_renderer.cc index 4bfc3167c75..5f4d6125b0c 100644 --- a/chromium/ui/ozone/demo/vulkan_overlay_renderer.cc +++ b/chromium/ui/ozone/demo/vulkan_overlay_renderer.cc @@ -15,7 +15,6 @@ #include "gpu/vulkan/vulkan_device_queue.h" #include "gpu/vulkan/vulkan_function_pointers.h" #include "gpu/vulkan/vulkan_implementation.h" -#include "gpu/vulkan/vulkan_render_pass.h" #include "gpu/vulkan/vulkan_surface.h" #include "gpu/vulkan/vulkan_swap_chain.h" #include "ui/display/types/display_snapshot.h" @@ -48,6 +47,7 @@ VulkanOverlayRenderer::VulkanOverlayRenderer( VulkanOverlayRenderer::~VulkanOverlayRenderer() { DestroyBuffers(); + DestroyRenderPass(); command_pool_->Destroy(); command_pool_.reset(); device_queue_->Destroy(); @@ -64,35 +64,44 @@ bool VulkanOverlayRenderer::Initialize() { } VkAttachmentDescription render_pass_attachments[] = {{ - .format = VK_FORMAT_B8G8R8A8_SRGB, - .samples = VK_SAMPLE_COUNT_1_BIT, - .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + /* .flags = */ 0, + /* .format = */ VK_FORMAT_B8G8R8A8_SRGB, + /* .samples = */ VK_SAMPLE_COUNT_1_BIT, + /* .loadOp = */ VK_ATTACHMENT_LOAD_OP_CLEAR, + /* .storeOp = */ VK_ATTACHMENT_STORE_OP_STORE, + /* .stencilLoadOp = */ VK_ATTACHMENT_LOAD_OP_DONT_CARE, + /* .stencilStoreOp = */ VK_ATTACHMENT_STORE_OP_DONT_CARE, + /* .initialLayout = */ VK_IMAGE_LAYOUT_UNDEFINED, + /* .finalLayout = */ VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, }}; VkAttachmentReference color_attachment_references[] = { - {.attachment = 0, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}}; + {/* .attachment = */ 0, + /* .layout = */ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}}; VkSubpassDescription render_pass_subpasses[] = {{ - .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, - .inputAttachmentCount = 0, - .colorAttachmentCount = base::size(color_attachment_references), - .pColorAttachments = color_attachment_references, - .pResolveAttachments = nullptr, - .pDepthStencilAttachment = nullptr, - .preserveAttachmentCount = 0, - .pPreserveAttachments = nullptr, + /* .flags = */ 0, + /* .pipelineBindPoint = */ VK_PIPELINE_BIND_POINT_GRAPHICS, + /* .inputAttachmentCount = */ 0, + /* .pInputAttachments = */ nullptr, + /* .colorAttachmentCount = */ base::size(color_attachment_references), + /* .pColorAttachments = */ color_attachment_references, + /* .pResolveAttachments = */ nullptr, + /* .pDepthStencilAttachment = */ nullptr, + /* .preserveAttachmentCount = */ 0, + /* .pPreserveAttachments = */ nullptr, }}; VkRenderPassCreateInfo render_pass_create_info = { - .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, - .attachmentCount = base::size(render_pass_attachments), - .pAttachments = render_pass_attachments, - .subpassCount = base::size(render_pass_subpasses), - .pSubpasses = render_pass_subpasses, - .dependencyCount = 0, + /* .sType = */ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + /* .pNext = */ nullptr, + /* .flags = */ 0, + /* .attachmentCount = */ base::size(render_pass_attachments), + /* .pAttachments = */ render_pass_attachments, + /* .subpassCount = */ base::size(render_pass_subpasses), + /* .pSubpasses = */ render_pass_subpasses, + /* .dependencyCount = */ 0, + /* .pDependencies = */ nullptr, }; CHECK_EQ(vkCreateRenderPass(device_queue_->GetVulkanDevice(), @@ -109,6 +118,14 @@ bool VulkanOverlayRenderer::Initialize() { return true; } +void VulkanOverlayRenderer::DestroyRenderPass() { + if (render_pass_ == VK_NULL_HANDLE) + return; + + vkDestroyRenderPass(device_queue_->GetVulkanDevice(), render_pass_, nullptr); + render_pass_ = VK_NULL_HANDLE; +} + void VulkanOverlayRenderer::DestroyBuffers() { VkDevice vk_device = device_queue_->GetVulkanDevice(); @@ -146,7 +163,7 @@ void VulkanOverlayRenderer::RenderFrame() { TRACE_EVENT0("ozone", "VulkanOverlayRenderer::RenderFrame"); VkClearValue clear_value = { - .color = {.float32 = {.5f, 1.f - NextFraction(), .5f, 1.f}}}; + /* .color = */ {/* .float32 = */ {.5f, 1.f - NextFraction(), .5f, 1.f}}}; const Buffer& buffer = *buffers_[next_buffer_]; next_buffer_++; @@ -159,14 +176,26 @@ void VulkanOverlayRenderer::RenderFrame() { { gpu::ScopedSingleUseCommandBufferRecorder recorder(command_buffer); - VkRenderPassBeginInfo begin_info = {}; - begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - begin_info.renderPass = render_pass_; - begin_info.framebuffer = buffer.vk_framebuffer(); - begin_info.renderArea.extent.width = buffer.size().width(); - begin_info.renderArea.extent.height = buffer.size().height(); - begin_info.clearValueCount = 1; - begin_info.pClearValues = &clear_value; + VkRenderPassBeginInfo begin_info = { + /* .sType = */ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + /* .pNext = */ nullptr, + /* .renderPass = */ render_pass_, + /* .framebuffer = */ buffer.vk_framebuffer(), + /* .renderArea = */ + { + /* .offset = */ { + /* .x = */ 0, + /* .y = */ 0, + }, + /* .extent = */ + { + /* .width = */ buffer.size().width(), + /* .height = */ buffer.size().height(), + }, + }, + /* .clearValueCount = */ 1, + /* .pClearValues = */ &clear_value, + }; vkCmdBeginRenderPass(recorder.handle(), &begin_info, VK_SUBPASS_CONTENTS_INLINE); @@ -303,25 +332,27 @@ VulkanOverlayRenderer::Buffer::Create( } VkImageViewCreateInfo vk_image_view_create_info = { - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .image = vk_image, - .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = VK_FORMAT_B8G8R8A8_SRGB, - .components = - { - .r = VK_COMPONENT_SWIZZLE_IDENTITY, - .b = VK_COMPONENT_SWIZZLE_IDENTITY, - .g = VK_COMPONENT_SWIZZLE_IDENTITY, - .a = VK_COMPONENT_SWIZZLE_IDENTITY, - }, - .subresourceRange = - { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, + /* .sType = */ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + /* .pNext = */ nullptr, + /* .flags = */ 0, + /* .image = */ vk_image, + /* .viewType = */ VK_IMAGE_VIEW_TYPE_2D, + /* .format = */ VK_FORMAT_B8G8R8A8_SRGB, + /* .components = */ + { + /* .r = */ VK_COMPONENT_SWIZZLE_IDENTITY, + /* .b = */ VK_COMPONENT_SWIZZLE_IDENTITY, + /* .g = */ VK_COMPONENT_SWIZZLE_IDENTITY, + /* .a = */ VK_COMPONENT_SWIZZLE_IDENTITY, + }, + /* .subresourceRange = */ + { + /* .aspectMask = */ VK_IMAGE_ASPECT_COLOR_BIT, + /* .baseMipLevel = */ 0, + /* .levelCount = */ 1, + /* .baseArrayLayer = */ 0, + /* .layerCount = */ 1, + }, }; VkResult result; @@ -332,13 +363,15 @@ VulkanOverlayRenderer::Buffer::Create( LOG(FATAL) << "Failed to create a Vulkan image view."; } VkFramebufferCreateInfo vk_framebuffer_create_info = { - .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, - .renderPass = vk_render_pass, - .attachmentCount = 1, - .pAttachments = &vk_image_view, - .width = size.width(), - .height = size.height(), - .layers = 1, + /* .sType = */ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + /* .pNext = */ nullptr, + /* .flags = */ 0, + /* .renderPass = */ vk_render_pass, + /* .attachmentCount = */ 1, + /* .pAttachments = */ &vk_image_view, + /* .width = */ size.width(), + /* .height = */ size.height(), + /* .layers = */ 1, }; VkFramebuffer vk_framebuffer = VK_NULL_HANDLE; diff --git a/chromium/ui/ozone/demo/vulkan_overlay_renderer.h b/chromium/ui/ozone/demo/vulkan_overlay_renderer.h index 8a8a7c575ca..37b75125198 100644 --- a/chromium/ui/ozone/demo/vulkan_overlay_renderer.h +++ b/chromium/ui/ozone/demo/vulkan_overlay_renderer.h @@ -89,6 +89,7 @@ class VulkanOverlayRenderer : public RendererBase { const VkFence fence_; }; + void DestroyRenderPass(); void DestroyBuffers(); void RecreateBuffers(); void RenderFrame(); @@ -111,7 +112,7 @@ class VulkanOverlayRenderer : public RendererBase { std::unique_ptr<gpu::VulkanCommandPool> command_pool_; std::unique_ptr<OverlaySurface> overlay_surface_; - VkRenderPass render_pass_; + VkRenderPass render_pass_ = VK_NULL_HANDLE; base::WeakPtrFactory<VulkanOverlayRenderer> weak_ptr_factory_; diff --git a/chromium/ui/ozone/demo/vulkan_renderer.cc b/chromium/ui/ozone/demo/vulkan_renderer.cc index 89cbc215b0c..9a69457e318 100644 --- a/chromium/ui/ozone/demo/vulkan_renderer.cc +++ b/chromium/ui/ozone/demo/vulkan_renderer.cc @@ -11,97 +11,196 @@ #include "base/trace_event/trace_event.h" #include "gpu/vulkan/init/vulkan_factory.h" #include "gpu/vulkan/vulkan_command_buffer.h" +#include "gpu/vulkan/vulkan_command_pool.h" #include "gpu/vulkan/vulkan_device_queue.h" +#include "gpu/vulkan/vulkan_function_pointers.h" #include "gpu/vulkan/vulkan_implementation.h" -#include "gpu/vulkan/vulkan_render_pass.h" #include "gpu/vulkan/vulkan_surface.h" #include "gpu/vulkan/vulkan_swap_chain.h" namespace ui { -VulkanRenderer::VulkanRenderer(gpu::VulkanImplementation* vulkan_implementation, +VulkanRenderer::VulkanRenderer(std::unique_ptr<gpu::VulkanSurface> surface, + gpu::VulkanImplementation* vulkan_implementation, gfx::AcceleratedWidget widget, const gfx::Size& size) : RendererBase(widget, size), vulkan_implementation_(vulkan_implementation), + surface_(std::move(surface)), + size_(size), weak_ptr_factory_(this) {} VulkanRenderer::~VulkanRenderer() { - surface_->Finish(); - render_pass_->Destroy(); + DestroyFramebuffers(); + DestroyRenderPass(); surface_->Destroy(); surface_.reset(); + command_pool_->Destroy(); + command_pool_.reset(); device_queue_->Destroy(); device_queue_.reset(); } bool VulkanRenderer::Initialize() { + TRACE_EVENT1("ozone", "VulkanRenderer::Initialize", "widget", widget_); + device_queue_ = gpu::CreateVulkanDeviceQueue( vulkan_implementation_, gpu::VulkanDeviceQueue::GRAPHICS_QUEUE_FLAG | gpu::VulkanDeviceQueue::PRESENTATION_SUPPORT_QUEUE_FLAG); - CHECK(device_queue_); - - surface_ = vulkan_implementation_->CreateViewSurface(widget_); - if (!surface_) - LOG(FATAL) << "Vulkan surface not supported by platform"; - CHECK(surface_->Initialize(device_queue_.get(), - gpu::VulkanSurface::DEFAULT_SURFACE_FORMAT)); - - gpu::VulkanRenderPass::RenderPassData render_pass_data; - - render_pass_data.attachments.resize(1); - gpu::VulkanRenderPass::AttachmentData* attachment = - &render_pass_data.attachments[0]; - attachment->attachment_type = - gpu::VulkanRenderPass::AttachmentType::ATTACHMENT_TYPE_SWAP_IMAGE; - attachment->sample_count = VK_SAMPLE_COUNT_1_BIT; - attachment->load_op = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachment->store_op = VK_ATTACHMENT_STORE_OP_STORE; - attachment->stencil_load_op = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachment->stencil_store_op = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachment->start_layout = - gpu::VulkanRenderPass::ImageLayoutType::IMAGE_LAYOUT_UNDEFINED; - attachment->end_layout = - gpu::VulkanRenderPass::ImageLayoutType::IMAGE_LAYOUT_TYPE_PRESENT; - - render_pass_data.subpass_datas.resize(1); - gpu::VulkanRenderPass::SubpassData* subpass_data = - &render_pass_data.subpass_datas[0]; - - subpass_data->subpass_attachments.resize(1); - gpu::VulkanRenderPass::SubpassAttachment* subpass_attachment = - &subpass_data->subpass_attachments[0]; - subpass_attachment->attachment_index = 0; - subpass_attachment->subpass_layout = - gpu::VulkanRenderPass::ImageLayoutType::IMAGE_LAYOUT_TYPE_IMAGE_VIEW; - - gpu::VulkanSwapChain* swap_chain = surface_->GetSwapChain(); - CHECK(render_pass_data.ValidateData(swap_chain)); - - render_pass_ = std::make_unique<gpu::VulkanRenderPass>(device_queue_.get()); - CHECK(render_pass_->Initialize(swap_chain, render_pass_data)); + if (!device_queue_) { + LOG(FATAL) << "Failed to init device queue"; + } + + if (!surface_->Initialize(device_queue_.get(), + gpu::VulkanSurface::DEFAULT_SURFACE_FORMAT)) { + LOG(FATAL) << "Failed to init surface"; + } + + VkAttachmentDescription render_pass_attachments[] = {{ + /* .flags = */ 0, + /* .format = */ surface_->surface_format().format, + /* .samples = */ VK_SAMPLE_COUNT_1_BIT, + /* .loadOp = */ VK_ATTACHMENT_LOAD_OP_CLEAR, + /* .storeOp = */ VK_ATTACHMENT_STORE_OP_STORE, + /* .stencilLoadOp = */ VK_ATTACHMENT_LOAD_OP_DONT_CARE, + /* .stencilStoreOp = */ VK_ATTACHMENT_STORE_OP_DONT_CARE, + /* .initialLayout = */ VK_IMAGE_LAYOUT_UNDEFINED, + /* .finalLayout = */ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + }}; + + VkAttachmentReference color_attachment_references[] = { + {/* .attachment = */ 0, + /* .layout = */ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL}}; + + VkSubpassDescription render_pass_subpasses[] = {{ + /* .flags = */ 0, + /* .pipelineBindPoint = */ VK_PIPELINE_BIND_POINT_GRAPHICS, + /* .inputAttachmentCount = */ 0, + /* .pInputAttachments = */ nullptr, + /* .colorAttachmentCount = */ base::size(color_attachment_references), + /* .pColorAttachments = */ color_attachment_references, + /* .pResolveAttachments = */ nullptr, + /* .pDepthStencilAttachment = */ nullptr, + /* .preserveAttachmentCount = */ 0, + /* .pPreserveAttachments = */ nullptr, + }}; + + VkRenderPassCreateInfo render_pass_create_info = { + /* .sType = */ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + /* .pNext = */ nullptr, + /* .flags = */ 0, + /* .attachmentCount = */ base::size(render_pass_attachments), + /* .pAttachments = */ render_pass_attachments, + /* .subpassCount = */ base::size(render_pass_subpasses), + /* .pSubpasses = */ render_pass_subpasses, + /* .dependencyCount = */ 0, + /* .pDependencies = */ nullptr, + }; + + CHECK_EQ(vkCreateRenderPass(device_queue_->GetVulkanDevice(), + &render_pass_create_info, nullptr, &render_pass_), + VK_SUCCESS); + + command_pool_ = std::make_unique<gpu::VulkanCommandPool>(device_queue_.get()); + CHECK(command_pool_->Initialize()); + + RecreateFramebuffers(); // Schedule the initial render. PostRenderFrameTask(); return true; } +void VulkanRenderer::DestroyRenderPass() { + if (render_pass_ == VK_NULL_HANDLE) + return; + + vkDestroyRenderPass(device_queue_->GetVulkanDevice(), render_pass_, nullptr); + render_pass_ = VK_NULL_HANDLE; +} + +void VulkanRenderer::DestroyFramebuffers() { + VkDevice vk_device = device_queue_->GetVulkanDevice(); + + VkResult result = vkQueueWaitIdle(device_queue_->GetVulkanQueue()); + CHECK_EQ(result, VK_SUCCESS); + + for (std::unique_ptr<Framebuffer>& framebuffer : framebuffers_) { + if (!framebuffer) + continue; + + framebuffer->command_buffer()->Destroy(); + vkDestroyFramebuffer(vk_device, framebuffer->vk_framebuffer(), nullptr); + vkDestroyImageView(vk_device, framebuffer->vk_image_view(), nullptr); + framebuffer.reset(); + } +} + +void VulkanRenderer::RecreateFramebuffers() { + TRACE_EVENT0("ozone", "VulkanRenderer::RecreateFramebuffers"); + + DestroyFramebuffers(); + + surface_->SetSize(size_); + + gpu::VulkanSwapChain* vulkan_swap_chain = surface_->GetSwapChain(); + const uint32_t num_images = vulkan_swap_chain->num_images(); + framebuffers_.resize(num_images); + + for (uint32_t image = 0; image < num_images; ++image) { + framebuffers_[image] = + Framebuffer::Create(device_queue_.get(), command_pool_.get(), + render_pass_, surface_.get(), image); + CHECK(framebuffers_[image]); + } +} + void VulkanRenderer::RenderFrame() { TRACE_EVENT0("ozone", "VulkanRenderer::RenderFrame"); - VkClearValue clear_value = {.color = {{.5f, 1.f - NextFraction(), .5f, 1.f}}}; - render_pass_->SetClearValue(0, clear_value); + VkClearValue clear_value = { + /* .color = */ {/* .float32 = */ {.5f, 1.f - NextFraction(), .5f, 1.f}}}; + + gpu::VulkanSwapChain* vulkan_swap_chain = surface_->GetSwapChain(); + const uint32_t image = vulkan_swap_chain->current_image(); + const Framebuffer& framebuffer = *framebuffers_[image]; + + gpu::VulkanCommandBuffer& command_buffer = *framebuffer.command_buffer(); - gpu::VulkanCommandBuffer* command_buffer = - surface_->GetSwapChain()->GetCurrentCommandBuffer(); { - gpu::ScopedSingleUseCommandBufferRecorder recorder(*command_buffer); - render_pass_->BeginRenderPass(recorder, true); - render_pass_->EndRenderPass(recorder); + gpu::ScopedSingleUseCommandBufferRecorder recorder(command_buffer); + + VkRenderPassBeginInfo begin_info = { + /* .sType = */ VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + /* .pNext = */ nullptr, + /* .renderPass = */ render_pass_, + /* .framebuffer = */ framebuffer.vk_framebuffer(), + /* .renderArea = */ + { + /* .offset = */ { + /* .x = */ 0, + /* .y = */ 0, + }, + /* .extent = */ + { + /* .width = */ vulkan_swap_chain->size().width(), + /* .height = */ vulkan_swap_chain->size().height(), + }, + }, + /* .clearValueCount = */ 1, + /* .pClearValues = */ &clear_value, + }; + + vkCmdBeginRenderPass(recorder.handle(), &begin_info, + VK_SUBPASS_CONTENTS_INLINE); + + vkCmdEndRenderPass(recorder.handle()); } - CHECK_EQ(surface_->SwapBuffers(), gfx::SwapResult::SWAP_ACK); + CHECK(command_buffer.Submit(0, nullptr, 0, nullptr)); + + vulkan_swap_chain->SwapBuffers(); PostRenderFrameTask(); } @@ -112,4 +211,80 @@ void VulkanRenderer::PostRenderFrameTask() { weak_ptr_factory_.GetWeakPtr())); } +VulkanRenderer::Framebuffer::Framebuffer( + VkImageView vk_image_view, + VkFramebuffer vk_framebuffer, + std::unique_ptr<gpu::VulkanCommandBuffer> command_buffer) + : vk_image_view_(vk_image_view), + vk_framebuffer_(vk_framebuffer), + command_buffer_(std::move(command_buffer)) {} + +VulkanRenderer::Framebuffer::~Framebuffer() {} + +std::unique_ptr<VulkanRenderer::Framebuffer> +VulkanRenderer::Framebuffer::Create(gpu::VulkanDeviceQueue* vulkan_device_queue, + gpu::VulkanCommandPool* vulkan_command_pool, + VkRenderPass vk_render_pass, + gpu::VulkanSurface* vulkan_surface, + uint32_t vulkan_swap_chain_image_index) { + gpu::VulkanSwapChain* vulkan_swap_chain = vulkan_surface->GetSwapChain(); + const VkDevice vk_device = vulkan_device_queue->GetVulkanDevice(); + VkImageViewCreateInfo vk_image_view_create_info = { + /* .sType = */ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + /* .pNext = */ nullptr, + /* .flags = */ 0, + /* .image = */ vulkan_swap_chain->GetImage(vulkan_swap_chain_image_index), + /* .viewType = */ VK_IMAGE_VIEW_TYPE_2D, + /* .format = */ vulkan_surface->surface_format().format, + /* .components = */ + { + /* .r = */ VK_COMPONENT_SWIZZLE_IDENTITY, + /* .b = */ VK_COMPONENT_SWIZZLE_IDENTITY, + /* .g = */ VK_COMPONENT_SWIZZLE_IDENTITY, + /* .a = */ VK_COMPONENT_SWIZZLE_IDENTITY, + }, + /* .subresourceRange = */ + { + /* .aspectMask = */ VK_IMAGE_ASPECT_COLOR_BIT, + /* .baseMipLevel = */ 0, + /* .levelCount = */ 1, + /* .baseArrayLayer = */ 0, + /* .layerCount = */ 1, + }, + }; + + VkResult result; + VkImageView vk_image_view = VK_NULL_HANDLE; + result = vkCreateImageView(vk_device, &vk_image_view_create_info, nullptr, + &vk_image_view); + if (result != VK_SUCCESS) { + LOG(FATAL) << "Failed to create a Vulkan image view."; + } + VkFramebufferCreateInfo vk_framebuffer_create_info = { + /* .sType = */ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + /* .pNext = */ nullptr, + /* .flags = */ 0, + /* .renderPass = */ vk_render_pass, + /* .attachmentCount = */ 1, + /* .pAttachments = */ &vk_image_view, + /* .width = */ vulkan_swap_chain->size().width(), + /* .height = */ vulkan_swap_chain->size().height(), + /* .layers = */ 1, + }; + + VkFramebuffer vk_framebuffer = VK_NULL_HANDLE; + result = vkCreateFramebuffer(vk_device, &vk_framebuffer_create_info, nullptr, + &vk_framebuffer); + if (result != VK_SUCCESS) { + LOG(FATAL) << "Failed to create a Vulkan framebuffer."; + } + + auto command_buffer = std::make_unique<gpu::VulkanCommandBuffer>( + vulkan_device_queue, vulkan_command_pool, true /* primary */); + CHECK(command_buffer->Initialize()); + + return std::make_unique<VulkanRenderer::Framebuffer>( + vk_image_view, vk_framebuffer, std::move(command_buffer)); +} + } // namespace ui diff --git a/chromium/ui/ozone/demo/vulkan_renderer.h b/chromium/ui/ozone/demo/vulkan_renderer.h index f372affc550..cd7e6554a9a 100644 --- a/chromium/ui/ozone/demo/vulkan_renderer.h +++ b/chromium/ui/ozone/demo/vulkan_renderer.h @@ -5,16 +5,21 @@ #ifndef UI_OZONE_DEMO_VULKAN_RENDERER_H_ #define UI_OZONE_DEMO_VULKAN_RENDERER_H_ +#include <vulkan/vulkan.h> + #include "base/macros.h" #include "base/memory/ref_counted.h" #include "base/memory/weak_ptr.h" +#include "ui/gfx/buffer_types.h" +#include "ui/gfx/presentation_feedback.h" #include "ui/gfx/swap_result.h" #include "ui/ozone/demo/renderer_base.h" namespace gpu { class VulkanDeviceQueue; class VulkanImplementation; -class VulkanRenderPass; +class VulkanCommandBuffer; +class VulkanCommandPool; class VulkanSurface; } // namespace gpu @@ -22,7 +27,8 @@ namespace ui { class VulkanRenderer : public RendererBase { public: - VulkanRenderer(gpu::VulkanImplementation* vulkan_instance, + VulkanRenderer(std::unique_ptr<gpu::VulkanSurface> surface, + gpu::VulkanImplementation* vulkan_instance, gfx::AcceleratedWidget widget, const gfx::Size& size); ~VulkanRenderer() override; @@ -31,13 +37,47 @@ class VulkanRenderer : public RendererBase { bool Initialize() override; private: + class Framebuffer { + public: + Framebuffer(VkImageView vk_image_view, + VkFramebuffer vk_framebuffer, + std::unique_ptr<gpu::VulkanCommandBuffer> command_buffer); + ~Framebuffer(); + + static std::unique_ptr<Framebuffer> Create( + gpu::VulkanDeviceQueue* vulkan_device_queue, + gpu::VulkanCommandPool* vulkan_command_pool, + VkRenderPass vk_render_pass, + gpu::VulkanSurface* vulkan_surface, + uint32_t vulkan_swap_chain_image_index); + + VkImageView vk_image_view() const { return vk_image_view_; } + VkFramebuffer vk_framebuffer() const { return vk_framebuffer_; } + gpu::VulkanCommandBuffer* command_buffer() const { + return command_buffer_.get(); + } + + private: + const VkImageView vk_image_view_; + const VkFramebuffer vk_framebuffer_; + const std::unique_ptr<gpu::VulkanCommandBuffer> command_buffer_; + }; + + void DestroyRenderPass(); + void DestroyFramebuffers(); + void RecreateFramebuffers(); void RenderFrame(); void PostRenderFrameTask(); + std::vector<std::unique_ptr<Framebuffer>> framebuffers_; + gpu::VulkanImplementation* const vulkan_implementation_; std::unique_ptr<gpu::VulkanDeviceQueue> device_queue_; + std::unique_ptr<gpu::VulkanCommandPool> command_pool_; std::unique_ptr<gpu::VulkanSurface> surface_; - std::unique_ptr<gpu::VulkanRenderPass> render_pass_; + gfx::Size size_; + + VkRenderPass render_pass_ = VK_NULL_HANDLE; base::WeakPtrFactory<VulkanRenderer> weak_ptr_factory_; diff --git a/chromium/ui/ozone/platform/drm/BUILD.gn b/chromium/ui/ozone/platform/drm/BUILD.gn index 832a6c00ead..e1bfe916873 100644 --- a/chromium/ui/ozone/platform/drm/BUILD.gn +++ b/chromium/ui/ozone/platform/drm/BUILD.gn @@ -26,10 +26,6 @@ source_set("gbm") { "common/scoped_drm_types.h", "gpu/crtc_controller.cc", "gpu/crtc_controller.h", - "gpu/drm_buffer.cc", - "gpu/drm_buffer.h", - "gpu/drm_console_buffer.cc", - "gpu/drm_console_buffer.h", "gpu/drm_device.cc", "gpu/drm_device.h", "gpu/drm_device_generator.cc", @@ -38,6 +34,8 @@ source_set("gbm") { "gpu/drm_device_manager.h", "gpu/drm_display.cc", "gpu/drm_display.h", + "gpu/drm_dumb_buffer.cc", + "gpu/drm_dumb_buffer.h", "gpu/drm_framebuffer.cc", "gpu/drm_framebuffer.h", "gpu/drm_gpu_display_manager.cc", @@ -54,8 +52,6 @@ source_set("gbm") { "gpu/drm_thread_message_proxy.h", "gpu/drm_thread_proxy.cc", "gpu/drm_thread_proxy.h", - "gpu/drm_vsync_provider.cc", - "gpu/drm_vsync_provider.h", "gpu/drm_window.cc", "gpu/drm_window.h", "gpu/drm_window_proxy.cc", @@ -64,8 +60,6 @@ source_set("gbm") { "gpu/gbm_overlay_surface.h", "gpu/gbm_pixmap.cc", "gpu/gbm_pixmap.h", - "gpu/gbm_surface.cc", - "gpu/gbm_surface.h", "gpu/gbm_surface_factory.cc", "gpu/gbm_surface_factory.h", "gpu/gbm_surfaceless.cc", diff --git a/chromium/ui/ozone/platform/drm/gpu/crtc_controller.cc b/chromium/ui/ozone/platform/drm/gpu/crtc_controller.cc index 3c0dc26eee0..aea5c918d4d 100644 --- a/chromium/ui/ozone/platform/drm/gpu/crtc_controller.cc +++ b/chromium/ui/ozone/platform/drm/gpu/crtc_controller.cc @@ -7,8 +7,8 @@ #include "base/logging.h" #include "base/time/time.h" #include "ui/gfx/presentation_feedback.h" -#include "ui/ozone/platform/drm/gpu/drm_buffer.h" #include "ui/ozone/platform/drm/gpu/drm_device.h" +#include "ui/ozone/platform/drm/gpu/drm_dumb_buffer.h" #include "ui/ozone/platform/drm/gpu/drm_framebuffer.h" #include "ui/ozone/platform/drm/gpu/hardware_display_plane.h" #include "ui/ozone/platform/drm/gpu/page_flip_request.h" @@ -53,6 +53,12 @@ bool CrtcController::Modeset(const DrmOverlayPlane& plane, mode_ = mode; is_disabled_ = false; + // Hold modeset buffer until page flip. This fixes a crash on entering + // hardware mirror mode in some circumstances (bug 888553). + // TODO(spang): Fix this better by changing how mirrors are set up (bug + // 899352). + modeset_framebuffer_ = plane.buffer; + return true; } @@ -107,6 +113,10 @@ void CrtcController::MoveCursor(const gfx::Point& location) { drm_->MoveCursor(crtc_, location); } +void CrtcController::OnPageFlipComplete() { + modeset_framebuffer_ = nullptr; +} + void CrtcController::DisableCursor() { if (!drm_->SetCursor(crtc_, 0, gfx::Size())) { PLOG(ERROR) << "drmModeSetCursor: device " << drm_->device_path().value() diff --git a/chromium/ui/ozone/platform/drm/gpu/crtc_controller.h b/chromium/ui/ozone/platform/drm/gpu/crtc_controller.h index b0162614eef..670ed0dd41c 100644 --- a/chromium/ui/ozone/platform/drm/gpu/crtc_controller.h +++ b/chromium/ui/ozone/platform/drm/gpu/crtc_controller.h @@ -20,7 +20,6 @@ namespace ui { -class DrmBuffer; class DrmDevice; // Wrapper around a CRTC. @@ -64,18 +63,22 @@ class CrtcController { void SetCursor(uint32_t handle, const gfx::Size& size); void MoveCursor(const gfx::Point& location); + void OnPageFlipComplete(); + private: void DisableCursor(); const scoped_refptr<DrmDevice> drm_; - uint32_t crtc_; + const uint32_t crtc_; // TODO(dnicoara) Add support for hardware mirroring (multiple connectors). - uint32_t connector_; + const uint32_t connector_; drmModeModeInfo mode_ = {}; + scoped_refptr<DrmFramebuffer> modeset_framebuffer_; + // Keeps track of the CRTC state. If a surface has been bound, then the value // is set to false. Otherwise it is true. bool is_disabled_ = true; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_buffer.cc b/chromium/ui/ozone/platform/drm/gpu/drm_buffer.cc deleted file mode 100644 index 63f53ee3822..00000000000 --- a/chromium/ui/ozone/platform/drm/gpu/drm_buffer.cc +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2014 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/ozone/platform/drm/gpu/drm_buffer.h" - -#include <drm_fourcc.h> - -#include "base/logging.h" -#include "third_party/skia/include/core/SkSurface.h" -#include "ui/ozone/platform/drm/gpu/drm_device.h" - -namespace ui { - -namespace { - -} // namespace - -DrmBuffer::DrmBuffer(const scoped_refptr<DrmDevice>& drm) : drm_(drm) { -} - -DrmBuffer::~DrmBuffer() { - if (mmap_base_ && !drm_->UnmapDumbBuffer(mmap_base_, mmap_size_)) - PLOG(ERROR) << "DrmBuffer: UnmapDumbBuffer: handle " << handle_; - - if (handle_ && !drm_->DestroyDumbBuffer(handle_)) - PLOG(ERROR) << "DrmBuffer: DestroyDumbBuffer: handle " << handle_; -} - -bool DrmBuffer::Initialize(const SkImageInfo& info) { - if (!drm_->CreateDumbBuffer(info, &handle_, &stride_)) { - PLOG(ERROR) << "DrmBuffer: CreateDumbBuffer: width " << info.width() - << " height " << info.height(); - return false; - } - - mmap_size_ = info.computeByteSize(stride_); - if (!drm_->MapDumbBuffer(handle_, mmap_size_, &mmap_base_)) { - PLOG(ERROR) << "DrmBuffer: MapDumbBuffer: handle " << handle_; - return false; - } - - surface_ = SkSurface::MakeRasterDirect(info, mmap_base_, stride_); - if (!surface_) { - LOG(ERROR) << "DrmBuffer: Failed to create SkSurface: handle " << handle_; - return false; - } - - return true; -} - -SkCanvas* DrmBuffer::GetCanvas() const { - return surface_->getCanvas(); -} - -uint32_t DrmBuffer::GetHandle() const { - return handle_; -} - -gfx::Size DrmBuffer::GetSize() const { - return gfx::Size(surface_->width(), surface_->height()); -} - -} // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_console_buffer.cc b/chromium/ui/ozone/platform/drm/gpu/drm_console_buffer.cc deleted file mode 100644 index 05b7f129610..00000000000 --- a/chromium/ui/ozone/platform/drm/gpu/drm_console_buffer.cc +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2014 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/ozone/platform/drm/gpu/drm_console_buffer.h" - -#include <sys/mman.h> -#include <xf86drmMode.h> - -#include "ui/ozone/platform/drm/common/scoped_drm_types.h" -#include "ui/ozone/platform/drm/gpu/drm_device.h" - -namespace ui { - -DrmConsoleBuffer::DrmConsoleBuffer(const scoped_refptr<DrmDevice>& drm, - uint32_t framebuffer) - : drm_(drm), framebuffer_(framebuffer) { -} - -DrmConsoleBuffer::~DrmConsoleBuffer() { - if (mmap_base_) - if (munmap(mmap_base_, mmap_size_)) - PLOG(ERROR) << "munmap"; - - if (handle_ && !drm_->CloseBufferHandle(handle_)) - PLOG(ERROR) << "DrmConsoleBuffer: CloseBufferHandle: handle " << handle_; -} - -bool DrmConsoleBuffer::Initialize() { - ScopedDrmFramebufferPtr fb(drm_->GetFramebuffer(framebuffer_)); - - if (!fb) - return false; - - handle_ = fb->handle; - stride_ = fb->pitch; - SkImageInfo info = SkImageInfo::MakeN32Premul(fb->width, fb->height); - - mmap_size_ = info.computeByteSize(stride_); - - if (!drm_->MapDumbBuffer(fb->handle, mmap_size_, &mmap_base_)) { - mmap_base_ = NULL; - return false; - } - - surface_ = SkSurface::MakeRasterDirect(info, mmap_base_, stride_); - if (!surface_) - return false; - - return true; -} - -} // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_console_buffer.h b/chromium/ui/ozone/platform/drm/gpu/drm_console_buffer.h deleted file mode 100644 index 2c432930e27..00000000000 --- a/chromium/ui/ozone/platform/drm/gpu/drm_console_buffer.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2014 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_OZONE_PLATFORM_DRM_GPU_DRM_CONSOLE_BUFFER_H_ -#define UI_OZONE_PLATFORM_DRM_GPU_DRM_CONSOLE_BUFFER_H_ - -#include <stddef.h> -#include <stdint.h> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "third_party/skia/include/core/SkImage.h" -#include "third_party/skia/include/core/SkSurface.h" - -class SkCanvas; - -namespace ui { - -class DrmDevice; - -// Wrapper for the console buffer. This is the buffer that is allocated by -// default by the system and is used when no application is controlling the -// CRTC. Keeps track of the native properties of the buffer and wraps the pixel -// memory into a SkSurface which can be used to draw into using Skia. -class DrmConsoleBuffer { - public: - DrmConsoleBuffer(const scoped_refptr<DrmDevice>& drm, uint32_t framebuffer); - ~DrmConsoleBuffer(); - - SkCanvas* canvas() { return surface_->getCanvas(); } - sk_sp<SkImage> image() { return surface_->makeImageSnapshot(); } - - // Memory map the backing pixels and wrap them in |surface_|. - bool Initialize(); - - protected: - scoped_refptr<DrmDevice> drm_; - - // Wrapper around the native pixel memory. - sk_sp<SkSurface> surface_; - - // Length of a row of pixels. - uint32_t stride_ = 0; - - // Buffer handle used by the DRM allocator. - uint32_t handle_ = 0; - - // Buffer ID used by the DRM modesettings API. - uint32_t framebuffer_ = 0; - - // Memory map base address. - void* mmap_base_ = nullptr; - - // Memory map size. - size_t mmap_size_ = 0; - - DISALLOW_COPY_AND_ASSIGN(DrmConsoleBuffer); -}; - -} // namespace ui - -#endif // UI_OZONE_PLATFORM_DRM_GPU_DRM_CONSOLE_BUFFER_H_ diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_device.cc b/chromium/ui/ozone/platform/drm/gpu/drm_device.cc index 5b737f6d44a..f555f84c1a8 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_device.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_device.cc @@ -260,10 +260,10 @@ bool DrmDevice::Initialize() { // Use atomic only if kernel allows it. is_atomic_ = SetCapability(DRM_CLIENT_CAP_ATOMIC, 1); if (is_atomic_) - plane_manager_.reset(new HardwareDisplayPlaneManagerAtomic()); + plane_manager_.reset(new HardwareDisplayPlaneManagerAtomic(this)); else - plane_manager_.reset(new HardwareDisplayPlaneManagerLegacy()); - if (!plane_manager_->Initialize(this)) { + plane_manager_.reset(new HardwareDisplayPlaneManagerLegacy(this)); + if (!plane_manager_->Initialize()) { LOG(ERROR) << "Failed to initialize the plane manager for " << device_path_.value(); plane_manager_.reset(); @@ -384,20 +384,6 @@ bool DrmDevice::PageFlip(uint32_t crtc_id, return false; } -bool DrmDevice::PageFlipOverlay(uint32_t crtc_id, - uint32_t framebuffer, - const gfx::Rect& location, - const gfx::Rect& source, - int overlay_plane) { - DCHECK(file_.IsValid()); - TRACE_EVENT2("drm", "DrmDevice::PageFlipOverlay", "crtc", crtc_id, - "framebuffer", framebuffer); - return !drmModeSetPlane(file_.GetPlatformFile(), overlay_plane, crtc_id, - framebuffer, 0, location.x(), location.y(), - location.width(), location.height(), source.x(), - source.y(), source.width(), source.height()); -} - ScopedDrmFramebufferPtr DrmDevice::GetFramebuffer(uint32_t framebuffer) { DCHECK(file_.IsValid()); TRACE_EVENT1("drm", "DrmDevice::GetFramebuffer", "framebuffer", framebuffer); diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_device.h b/chromium/ui/ozone/platform/drm/gpu/drm_device.h index 1e3cfcef247..1e62f1891c9 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_device.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_device.h @@ -147,15 +147,6 @@ class DrmDevice : public base::RefCountedThreadSafe<DrmDevice> { uint32_t framebuffer, scoped_refptr<PageFlipRequest> page_flip_request); - // Schedule an overlay to be show during the page flip for CRTC |crtc_id|. - // |source| location from |framebuffer| will be shown on overlay - // |overlay_plane|, in the bounds specified by |location| on the screen. - virtual bool PageFlipOverlay(uint32_t crtc_id, - uint32_t framebuffer, - const gfx::Rect& location, - const gfx::Rect& source, - int overlay_plane); - // Returns the list of all planes available on this DRM device. virtual ScopedDrmPlaneResPtr GetPlaneResources(); @@ -266,20 +257,20 @@ class DrmDevice : public base::RefCountedThreadSafe<DrmDevice> { const base::FilePath device_path_; // DRM device. - base::File file_; + const base::File file_; std::unique_ptr<PageFlipManager> page_flip_manager_; // Watcher for |fd_| listening for page flip events. std::unique_ptr<IOWatcher> watcher_; - bool is_primary_device_; + const bool is_primary_device_; bool is_atomic_ = false; bool allow_addfb2_modifiers_ = false; - std::unique_ptr<GbmDevice> gbm_; + const std::unique_ptr<GbmDevice> gbm_; DISALLOW_COPY_AND_ASSIGN(DrmDevice); }; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_device_manager.cc b/chromium/ui/ozone/platform/drm/gpu/drm_device_manager.cc index b165fe18be9..6b3214aad24 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_device_manager.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_device_manager.cc @@ -54,8 +54,10 @@ bool DrmDeviceManager::AddDrmDevice(const base::FilePath& path, return false; } - if (!primary_device_) + if (!primary_device_) { + VLOG(1) << "Primary DRM device added: " << path; primary_device_ = device; + } devices_.push_back(device); return true; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_device_manager.h b/chromium/ui/ozone/platform/drm/gpu/drm_device_manager.h index f780063fb9e..f34170a3f58 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_device_manager.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_device_manager.h @@ -50,7 +50,7 @@ class DrmDeviceManager { const DrmDeviceVector& GetDrmDevices() const; private: - std::unique_ptr<DrmDeviceGenerator> drm_device_generator_; + const std::unique_ptr<DrmDeviceGenerator> drm_device_generator_; DrmDeviceVector devices_; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_display.h b/chromium/ui/ozone/platform/drm/gpu/drm_display.h index b7ad5bde172..2ffc4d2e235 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_display.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_display.h @@ -57,7 +57,7 @@ class DrmDisplay { ScreenManager* screen_manager_; // Not owned. int64_t display_id_ = -1; - scoped_refptr<DrmDevice> drm_; + const scoped_refptr<DrmDevice> drm_; uint32_t crtc_ = 0; uint32_t connector_ = 0; std::vector<drmModeModeInfo> modes_; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_dumb_buffer.cc b/chromium/ui/ozone/platform/drm/gpu/drm_dumb_buffer.cc new file mode 100644 index 00000000000..889fca40606 --- /dev/null +++ b/chromium/ui/ozone/platform/drm/gpu/drm_dumb_buffer.cc @@ -0,0 +1,101 @@ +// Copyright 2014 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/ozone/platform/drm/gpu/drm_dumb_buffer.h" + +#include <drm_fourcc.h> +#include <xf86drmMode.h> + +#include "base/logging.h" +#include "third_party/skia/include/core/SkSurface.h" +#include "ui/ozone/platform/drm/gpu/drm_device.h" + +namespace ui { + +namespace { + +bool DestroyDumbBuffer(const scoped_refptr<DrmDevice>& drm_device, + uint32_t handle, + DrmDumbBuffer::HandleCloser handle_closer) { + switch (handle_closer) { + case DrmDumbBuffer::HandleCloser::DESTROY_DUMB: + return drm_device->DestroyDumbBuffer(handle); + case DrmDumbBuffer::HandleCloser::GEM_CLOSE: + return drm_device->CloseBufferHandle(handle); + } +} + +} // namespace + +DrmDumbBuffer::DrmDumbBuffer(const scoped_refptr<DrmDevice>& drm) : drm_(drm) {} + +DrmDumbBuffer::~DrmDumbBuffer() { + if (mmap_base_ && !drm_->UnmapDumbBuffer(mmap_base_, mmap_size_)) + PLOG(ERROR) << "DrmDumbBuffer: UnmapDumbBuffer: handle " << handle_; + + if (handle_ && !DestroyDumbBuffer(drm_, handle_, handle_closer_)) + PLOG(ERROR) << "DrmDumbBuffer: DestroyDumbBuffer: handle " << handle_; +} + +bool DrmDumbBuffer::Initialize(const SkImageInfo& info) { + DCHECK(!handle_); + + if (!drm_->CreateDumbBuffer(info, &handle_, &stride_)) { + PLOG(ERROR) << "DrmDumbBuffer: CreateDumbBuffer: width " << info.width() + << " height " << info.height(); + return false; + } + + handle_closer_ = HandleCloser::DESTROY_DUMB; + + return MapDumbBuffer(info); +} + +bool DrmDumbBuffer::InitializeFromFramebuffer(uint32_t framebuffer_id) { + DCHECK(!handle_); + + ScopedDrmFramebufferPtr framebuffer(drm_->GetFramebuffer(framebuffer_id)); + if (!framebuffer) + return false; + + handle_ = framebuffer->handle; + stride_ = framebuffer->pitch; + SkImageInfo info = + SkImageInfo::MakeN32Premul(framebuffer->width, framebuffer->height); + + handle_closer_ = HandleCloser::GEM_CLOSE; + + return MapDumbBuffer(info); +} + +SkCanvas* DrmDumbBuffer::GetCanvas() const { + return surface_->getCanvas(); +} + +uint32_t DrmDumbBuffer::GetHandle() const { + return handle_; +} + +gfx::Size DrmDumbBuffer::GetSize() const { + return gfx::Size(surface_->width(), surface_->height()); +} + +bool DrmDumbBuffer::MapDumbBuffer(const SkImageInfo& info) { + mmap_size_ = info.computeByteSize(stride_); + if (!drm_->MapDumbBuffer(handle_, mmap_size_, &mmap_base_)) { + PLOG(ERROR) << "DrmDumbBuffer: MapDumbBuffer: handle " << handle_; + return false; + } + + surface_ = SkSurface::MakeRasterDirect(info, mmap_base_, stride_); + if (!surface_) { + LOG(ERROR) << "DrmDumbBuffer: Failed to create SkSurface: handle " + << handle_; + return false; + } + + return true; +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_buffer.h b/chromium/ui/ozone/platform/drm/gpu/drm_dumb_buffer.h index 233bf14fd80..689ffb14e02 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_buffer.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_dumb_buffer.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_OZONE_PLATFORM_DRM_GPU_DRM_BUFFER_H_ -#define UI_OZONE_PLATFORM_DRM_GPU_DRM_BUFFER_H_ +#ifndef UI_OZONE_PLATFORM_DRM_GPU_DRM_DUMB_BUFFER_H_ +#define UI_OZONE_PLATFORM_DRM_GPU_DRM_DUMB_BUFFER_H_ #include <stddef.h> #include <stdint.h> @@ -23,22 +23,33 @@ class DrmDevice; // Wrapper for a DRM allocated buffer. Keeps track of the native properties of // the buffer and wraps the pixel memory into a SkSurface which can be used to // draw into using Skia. -class DrmBuffer { +class DrmDumbBuffer { public: - DrmBuffer(const scoped_refptr<DrmDevice>& drm); - ~DrmBuffer(); + enum class HandleCloser { + DESTROY_DUMB, + GEM_CLOSE, + }; - // Allocates the backing pixels and wraps them in |surface_|. |info| is used - // to describe the buffer characteristics (size, color format). + DrmDumbBuffer(const scoped_refptr<DrmDevice>& drm); + ~DrmDumbBuffer(); + + // Allocates a new dumb buffer, maps it, and wraps it in an SkSurface. + // |info| determines the buffer characteristics (size, color format). bool Initialize(const SkImageInfo& info); + // Imports an existing framebuffer, maps it, and wraps it in an SkSurface. + bool InitializeFromFramebuffer(uint32_t framebuffer_id); + SkCanvas* GetCanvas() const; + SkSurface* surface() const { return surface_.get(); } uint32_t GetHandle() const; gfx::Size GetSize() const; uint32_t stride() const { return stride_; } - protected: + private: + bool MapDumbBuffer(const SkImageInfo& info); + const scoped_refptr<DrmDevice> drm_; // Length of a row of pixels. @@ -47,6 +58,9 @@ class DrmBuffer { // Buffer handle used by the DRM allocator. uint32_t handle_ = 0; + // Method of closing |handle_|. + HandleCloser handle_closer_ = HandleCloser::DESTROY_DUMB; + // Base address for memory mapping. void* mmap_base_ = 0; @@ -56,9 +70,9 @@ class DrmBuffer { // Wrapper around the native pixel memory. sk_sp<SkSurface> surface_; - DISALLOW_COPY_AND_ASSIGN(DrmBuffer); + DISALLOW_COPY_AND_ASSIGN(DrmDumbBuffer); }; } // namespace ui -#endif // UI_OZONE_PLATFORM_DRM_GPU_DRM_BUFFER_H_ +#endif // UI_OZONE_PLATFORM_DRM_GPU_DRM_DUMB_BUFFER_H_ diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_framebuffer.h b/chromium/ui/ozone/platform/drm/gpu/drm_framebuffer.h index bb5e6e34b0b..bf8831a9c76 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_framebuffer.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_framebuffer.h @@ -80,17 +80,17 @@ class DrmFramebuffer : public base::RefCountedThreadSafe<DrmFramebuffer> { private: ~DrmFramebuffer(); - scoped_refptr<DrmDevice> drm_device_; + const scoped_refptr<DrmDevice> drm_device_; - uint32_t framebuffer_id_ = 0; - uint32_t framebuffer_pixel_format_ = 0; + const uint32_t framebuffer_id_; + const uint32_t framebuffer_pixel_format_; // If |opaque_framebuffer_pixel_format_| differs from // |framebuffer_pixel_format_| the following member is set to a valid fb, // otherwise it is set to 0. - uint32_t opaque_framebuffer_id_ = 0; - uint32_t opaque_framebuffer_pixel_format_ = 0; - uint64_t format_modifier_ = 0; - gfx::Size size_; + const uint32_t opaque_framebuffer_id_; + const uint32_t opaque_framebuffer_pixel_format_; + const uint64_t format_modifier_; + const gfx::Size size_; friend class base::RefCountedThreadSafe<DrmFramebuffer>; }; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h b/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h index ccfbd9f412b..25ae9a0b919 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h @@ -67,8 +67,8 @@ class DrmGpuDisplayManager { const std::vector<std::unique_ptr<DrmDisplay>>& new_displays, const std::vector<std::unique_ptr<DrmDisplay>>& old_displays) const; - ScreenManager* screen_manager_; // Not owned. - DrmDeviceManager* drm_device_manager_; // Not owned. + ScreenManager* const screen_manager_; // Not owned. + DrmDeviceManager* const drm_device_manager_; // Not owned. std::vector<std::unique_ptr<DrmDisplay>> displays_; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator.h b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator.h index 50306509785..0d7bbdaabf5 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator.h @@ -27,7 +27,7 @@ class DrmOverlayValidator { const DrmOverlayPlaneList& last_used_planes); private: - DrmWindow* window_; // Not owned. + DrmWindow* const window_; // Not owned. DISALLOW_COPY_AND_ASSIGN(DrmOverlayValidator); }; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc index c2a99d74911..7aa42855b80 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_overlay_validator_unittest.cc @@ -115,9 +115,9 @@ void DrmOverlayValidatorTest::SetUp() { gbm_ = gbm.get(); drm_ = new ui::MockDrmDevice(std::move(gbm)); - CrtcState crtc_state = {.planes = { - {.formats = {DRM_FORMAT_XRGB8888}}, - }}; + CrtcState crtc_state = {/* .planes = */ { + {/* .formats = */ {DRM_FORMAT_XRGB8888}}, + }}; InitializeDrmState({crtc_state}); screen_manager_.reset(new ui::ScreenManager()); @@ -205,7 +205,7 @@ void DrmOverlayValidatorTest::InitializeDrmState( } crtc_plane_properties[plane_idx].properties.push_back( - {.id = pair.first, .value = value}); + {/* .id = */ pair.first, /* .value = */ value}); } } @@ -279,11 +279,11 @@ TEST_F(DrmOverlayValidatorTest, OverlayFormat_XRGB) { plane_list_.back().display_bounds = overlay_rect_; CrtcState state = { - .planes = - { - {.formats = {DRM_FORMAT_XRGB8888, DRM_FORMAT_UYVY}}, - {.formats = {DRM_FORMAT_XRGB8888}}, - }, + /* .planes = */ + { + {/* .formats = */ {DRM_FORMAT_XRGB8888, DRM_FORMAT_UYVY}}, + {/* .formats = */ {DRM_FORMAT_XRGB8888}}, + }, }; InitializeDrmState(std::vector<CrtcState>(1, state)); @@ -308,11 +308,11 @@ TEST_F(DrmOverlayValidatorTest, OverlayFormat_YUV) { AddPlane(overlay_params_.back()); CrtcState state = { - .planes = - { - {.formats = {DRM_FORMAT_XRGB8888}}, - {.formats = {DRM_FORMAT_XRGB8888, DRM_FORMAT_UYVY}}, - }, + /* .planes = */ + { + {/* .formats = */ {DRM_FORMAT_XRGB8888}}, + {/* .formats = */ {DRM_FORMAT_XRGB8888, DRM_FORMAT_UYVY}}, + }, }; InitializeDrmState(std::vector<CrtcState>(1, state)); @@ -334,11 +334,11 @@ TEST_F(DrmOverlayValidatorTest, RejectYUVBuffersIfNotSupported) { AddPlane(overlay_params_.back()); CrtcState state = { - .planes = - { - {.formats = {DRM_FORMAT_XRGB8888}}, - {.formats = {DRM_FORMAT_XRGB8888}}, - }, + /* .planes = */ + { + {/* .formats = */ {DRM_FORMAT_XRGB8888}}, + {/* .formats = */ {DRM_FORMAT_XRGB8888}}, + }, }; InitializeDrmState(std::vector<CrtcState>(1, state)); @@ -354,18 +354,18 @@ TEST_F(DrmOverlayValidatorTest, RejectYUVBuffersIfNotSupported_MirroredControllers) { std::vector<CrtcState> crtc_states = { { - .planes = - { - {.formats = {DRM_FORMAT_XRGB8888}}, - {.formats = {DRM_FORMAT_XRGB8888, DRM_FORMAT_UYVY}}, - }, + /* .planes = */ + { + {/* .formats = */ {DRM_FORMAT_XRGB8888}}, + {/* .formats = */ {DRM_FORMAT_XRGB8888, DRM_FORMAT_UYVY}}, + }, }, { - .planes = - { - {.formats = {DRM_FORMAT_XRGB8888}}, - {.formats = {DRM_FORMAT_XRGB8888, DRM_FORMAT_UYVY}}, - }, + /* .planes = */ + { + {/* .formats = */ {DRM_FORMAT_XRGB8888}}, + {/* .formats = */ {DRM_FORMAT_XRGB8888, DRM_FORMAT_UYVY}}, + }, }, }; InitializeDrmState(crtc_states); @@ -421,18 +421,18 @@ TEST_F(DrmOverlayValidatorTest, TEST_F(DrmOverlayValidatorTest, OptimalFormatXRGB_MirroredControllers) { std::vector<CrtcState> crtc_states = { { - .planes = - { - {.formats = {DRM_FORMAT_XRGB8888}}, - {.formats = {DRM_FORMAT_XRGB8888, DRM_FORMAT_UYVY}}, - }, + /* .planes = */ + { + {/* .formats = */ {DRM_FORMAT_XRGB8888}}, + {/* .formats = */ {DRM_FORMAT_XRGB8888, DRM_FORMAT_UYVY}}, + }, }, { - .planes = - { - {.formats = {DRM_FORMAT_XRGB8888}}, - {.formats = {DRM_FORMAT_XRGB8888, DRM_FORMAT_UYVY}}, - }, + /* .planes = */ + { + {/* .formats = */ {DRM_FORMAT_XRGB8888}}, + {/* .formats = */ {DRM_FORMAT_XRGB8888, DRM_FORMAT_UYVY}}, + }, }, }; InitializeDrmState(crtc_states); diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc b/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc index 15b6ac3c7ac..09a96c43baa 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_thread.cc @@ -19,9 +19,9 @@ #include "ui/ozone/common/linux/gbm_device.h" #include "ui/ozone/common/linux/gbm_wrapper.h" #include "ui/ozone/platform/drm/common/drm_util.h" -#include "ui/ozone/platform/drm/gpu/drm_buffer.h" #include "ui/ozone/platform/drm/gpu/drm_device_generator.h" #include "ui/ozone/platform/drm/gpu/drm_device_manager.h" +#include "ui/ozone/platform/drm/gpu/drm_dumb_buffer.h" #include "ui/ozone/platform/drm/gpu/drm_gpu_display_manager.h" #include "ui/ozone/platform/drm/gpu/drm_window.h" #include "ui/ozone/platform/drm/gpu/drm_window_proxy.h" @@ -152,7 +152,7 @@ void DrmThread::CreateBuffer(gfx::AcceleratedWidget widget, std::unique_ptr<GbmBuffer>* buffer, scoped_refptr<DrmFramebuffer>* framebuffer) { scoped_refptr<ui::DrmDevice> drm = device_manager_->GetDrmDevice(widget); - DCHECK(drm); + CHECK(drm) << "No devices available for buffer allocation."; DrmWindow* window = screen_manager_->GetWindow(widget); uint32_t flags = BufferUsageToGbmFlags(usage); @@ -244,17 +244,6 @@ void DrmThread::OnPlanesReadyForPageFlip( } } -void DrmThread::GetVSyncParameters( - gfx::AcceleratedWidget widget, - const gfx::VSyncProvider::UpdateVSyncCallback& callback) { - DrmWindow* window = screen_manager_->GetWindow(widget); - // No need to call the callback if there isn't a window since the vsync - // provider doesn't require the callback to be called if there isn't a vsync - // data source. - if (window) - window->GetVSyncParameters(callback); -} - void DrmThread::IsDeviceAtomic(gfx::AcceleratedWidget widget, bool* is_atomic) { scoped_refptr<ui::DrmDevice> drm_device = device_manager_->GetDrmDevice(widget); diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_thread.h b/chromium/ui/ozone/platform/drm/gpu/drm_thread.h index 79685b1e029..c6fcabc3a89 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_thread.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_thread.h @@ -90,9 +90,6 @@ class DrmThread : public base::Thread, std::vector<DrmOverlayPlane> planes, SwapCompletionOnceCallback submission_callback, PresentationOnceCallback presentation_callback); - void GetVSyncParameters( - gfx::AcceleratedWidget widget, - const gfx::VSyncProvider::UpdateVSyncCallback& callback); void IsDeviceAtomic(gfx::AcceleratedWidget widget, bool* is_atomic); diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_thread_message_proxy.h b/chromium/ui/ozone/platform/drm/gpu/drm_thread_message_proxy.h index f21fd244d7a..970a645eda4 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_thread_message_proxy.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_thread_message_proxy.h @@ -94,7 +94,7 @@ class DrmThreadMessageProxy : public IPC::MessageFilter, display::HDCPState state) const; void OnSetHDCPStateCallback(int64_t display_id, bool success) const; - DrmThread* drm_thread_; + DrmThread* drm_thread_ = nullptr; IPC::Sender* sender_ = nullptr; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_vsync_provider.cc b/chromium/ui/ozone/platform/drm/gpu/drm_vsync_provider.cc deleted file mode 100644 index 4b153e21255..00000000000 --- a/chromium/ui/ozone/platform/drm/gpu/drm_vsync_provider.cc +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2014 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/ozone/platform/drm/gpu/drm_vsync_provider.h" - -#include "ui/ozone/platform/drm/gpu/drm_window_proxy.h" - -namespace ui { - -DrmVSyncProvider::DrmVSyncProvider(DrmWindowProxy* window) : window_(window) {} - -DrmVSyncProvider::~DrmVSyncProvider() { -} - -void DrmVSyncProvider::GetVSyncParameters(const UpdateVSyncCallback& callback) { - window_->GetVSyncParameters(callback); -} - -bool DrmVSyncProvider::GetVSyncParametersIfAvailable( - base::TimeTicks* timebase, - base::TimeDelta* interval) { - return false; -} - -bool DrmVSyncProvider::SupportGetVSyncParametersIfAvailable() const { - return false; -} - -bool DrmVSyncProvider::IsHWClock() const { - return true; -} - -} // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_vsync_provider.h b/chromium/ui/ozone/platform/drm/gpu/drm_vsync_provider.h deleted file mode 100644 index d0ec9a6e142..00000000000 --- a/chromium/ui/ozone/platform/drm/gpu/drm_vsync_provider.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2014 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_OZONE_PLATFORM_IMPL_DRM_VSYNC_PROVIDER_H_ -#define UI_OZONE_PLATFORM_IMPL_DRM_VSYNC_PROVIDER_H_ - -#include "base/macros.h" -#include "ui/gfx/native_widget_types.h" -#include "ui/gfx/vsync_provider.h" - -namespace ui { - -class DrmWindowProxy; - -class DrmVSyncProvider : public gfx::VSyncProvider { - public: - DrmVSyncProvider(DrmWindowProxy* window); - ~DrmVSyncProvider() override; - - void GetVSyncParameters(const UpdateVSyncCallback& callback) override; - bool GetVSyncParametersIfAvailable(base::TimeTicks* timebase, - base::TimeDelta* interval) override; - bool SupportGetVSyncParametersIfAvailable() const override; - bool IsHWClock() const override; - - private: - DrmWindowProxy* window_; - - DISALLOW_COPY_AND_ASSIGN(DrmVSyncProvider); -}; - -} // namespace ui - -#endif // UI_OZONE_PLATFORM_IMPL_DRM_VSYNC_PROVIDER_H_ diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_window.cc b/chromium/ui/ozone/platform/drm/gpu/drm_window.cc index 04f31690b85..c2aadb98bf1 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_window.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_window.cc @@ -18,9 +18,9 @@ #include "ui/ozone/common/gpu/ozone_gpu_message_params.h" #include "ui/ozone/platform/drm/common/drm_util.h" #include "ui/ozone/platform/drm/gpu/crtc_controller.h" -#include "ui/ozone/platform/drm/gpu/drm_buffer.h" #include "ui/ozone/platform/drm/gpu/drm_device.h" #include "ui/ozone/platform/drm/gpu/drm_device_manager.h" +#include "ui/ozone/platform/drm/gpu/drm_dumb_buffer.h" #include "ui/ozone/platform/drm/gpu/drm_overlay_validator.h" #include "ui/ozone/platform/drm/gpu/screen_manager.h" @@ -137,21 +137,6 @@ const DrmOverlayPlane* DrmWindow::GetLastModesetBuffer() { return DrmOverlayPlane::GetPrimaryPlane(last_submitted_planes_); } -void DrmWindow::GetVSyncParameters( - const gfx::VSyncProvider::UpdateVSyncCallback& callback) const { - if (!controller_) - return; - - // If we're in mirror mode the 2 CRTCs should have similar modes with the same - // refresh rates. - CrtcController* crtc = controller_->crtc_controllers()[0].get(); - const base::TimeTicks last_flip = controller_->GetTimeOfLastFlip(); - if (last_flip == base::TimeTicks() || crtc->mode().vrefresh == 0) - return; // The value is invalid, so we can't update the parameters. - callback.Run(last_flip, - base::TimeDelta::FromSeconds(1) / crtc->mode().vrefresh); -} - void DrmWindow::UpdateCursorImage() { if (!controller_) return; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_window.h b/chromium/ui/ozone/platform/drm/gpu/drm_window.h index b44d416de4f..ad34c452b9a 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_window.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_window.h @@ -27,7 +27,6 @@ class Rect; namespace ui { -class DrmBuffer; class DrmDeviceManager; class DrmOverlayValidator; class HardwareDisplayController; @@ -88,17 +87,20 @@ class DrmWindow { // Returns the last buffer associated with this window. const DrmOverlayPlane* GetLastModesetBuffer(); - void GetVSyncParameters( - const gfx::VSyncProvider::UpdateVSyncCallback& callback) const; - private: // Draw next frame in an animated cursor. void OnCursorAnimationTimeout(); - gfx::AcceleratedWidget widget_; + void UpdateCursorImage(); + void UpdateCursorLocation(); + + // Draw the last set cursor & update the cursor plane. + void ResetCursor(); + + const gfx::AcceleratedWidget widget_; - DrmDeviceManager* device_manager_; // Not owned. - ScreenManager* screen_manager_; // Not owned. + DrmDeviceManager* const device_manager_; // Not owned. + ScreenManager* const screen_manager_; // Not owned. // The current bounds of the window. gfx::Rect bounds_; @@ -108,12 +110,6 @@ class DrmWindow { HardwareDisplayController* controller_ = nullptr; std::unique_ptr<DrmOverlayValidator> overlay_validator_; - void UpdateCursorImage(); - void UpdateCursorLocation(); - - // Draw the last set cursor & update the cursor plane. - void ResetCursor(); - base::RepeatingTimer cursor_timer_; std::vector<SkBitmap> cursor_bitmaps_; gfx::Point cursor_location_; diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_window_proxy.cc b/chromium/ui/ozone/platform/drm/gpu/drm_window_proxy.cc index bde6d1dda8a..ce35578edd2 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_window_proxy.cc +++ b/chromium/ui/ozone/platform/drm/gpu/drm_window_proxy.cc @@ -34,14 +34,6 @@ void DrmWindowProxy::SchedulePageFlip( CreateSafeOnceCallback(std::move(presentation_callback)))); } -void DrmWindowProxy::GetVSyncParameters( - const gfx::VSyncProvider::UpdateVSyncCallback& callback) { - drm_thread_->task_runner()->PostTask( - FROM_HERE, base::BindOnce(&DrmThread::GetVSyncParameters, - base::Unretained(drm_thread_), widget_, - CreateSafeCallback(callback))); -} - bool DrmWindowProxy::SupportsGpuFences() const { bool is_atomic = false; PostSyncTask( diff --git a/chromium/ui/ozone/platform/drm/gpu/drm_window_proxy.h b/chromium/ui/ozone/platform/drm/gpu/drm_window_proxy.h index 080561f83cd..303051c1c25 100644 --- a/chromium/ui/ozone/platform/drm/gpu/drm_window_proxy.h +++ b/chromium/ui/ozone/platform/drm/gpu/drm_window_proxy.h @@ -28,15 +28,12 @@ class DrmWindowProxy { SwapCompletionOnceCallback submission_callback, PresentationOnceCallback presentation_callback); - void GetVSyncParameters( - const gfx::VSyncProvider::UpdateVSyncCallback& callback); - bool SupportsGpuFences() const; private: - gfx::AcceleratedWidget widget_; + const gfx::AcceleratedWidget widget_; - DrmThread* drm_thread_; + DrmThread* const drm_thread_; DISALLOW_COPY_AND_ASSIGN(DrmWindowProxy); }; diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_overlay_surface.h b/chromium/ui/ozone/platform/drm/gpu/gbm_overlay_surface.h index a827ee08294..abde483b459 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_overlay_surface.h +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_overlay_surface.h @@ -49,7 +49,7 @@ class GbmOverlaySurface : public OverlaySurface { std::unique_ptr<gfx::GpuFence> out_fence); void OnPresentation(const gfx::PresentationFeedback& presentation_feedback); - std::unique_ptr<DrmWindowProxy> window_; + const std::unique_ptr<DrmWindowProxy> window_; Frame unsubmitted_frame_; Frame submitted_frame_; Frame presented_frame_; diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_pixmap.h b/chromium/ui/ozone/platform/drm/gpu/gbm_pixmap.h index 41194f7cdb4..91b9b93f222 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_pixmap.h +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_pixmap.h @@ -50,9 +50,9 @@ class GbmPixmap : public gfx::NativePixmap { private: ~GbmPixmap() override; - GbmSurfaceFactory* surface_manager_; - std::unique_ptr<GbmBuffer> buffer_; - scoped_refptr<DrmFramebuffer> framebuffer_; + GbmSurfaceFactory* const surface_manager_; + const std::unique_ptr<GbmBuffer> buffer_; + const scoped_refptr<DrmFramebuffer> framebuffer_; DISALLOW_COPY_AND_ASSIGN(GbmPixmap); }; diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_surface.cc b/chromium/ui/ozone/platform/drm/gpu/gbm_surface.cc deleted file mode 100644 index 8588a55d7ac..00000000000 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_surface.cc +++ /dev/null @@ -1,155 +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. - -#include "ui/ozone/platform/drm/gpu/gbm_surface.h" - -#include <utility> - -#include "base/logging.h" -#include "ui/display/types/display_snapshot.h" -#include "ui/gfx/gpu_fence.h" -#include "ui/gfx/gpu_fence_handle.h" -#include "ui/gfx/native_pixmap.h" -#include "ui/gl/gl_image_native_pixmap.h" -#include "ui/gl/gl_surface_egl.h" -#include "ui/ozone/platform/drm/gpu/drm_window_proxy.h" -#include "ui/ozone/platform/drm/gpu/gbm_surface_factory.h" - -namespace ui { - -GbmSurface::GbmSurface(GbmSurfaceFactory* surface_factory, - std::unique_ptr<DrmWindowProxy> window, - gfx::AcceleratedWidget widget) - : GbmSurfaceless(surface_factory, std::move(window), widget) { - for (auto& texture : textures_) - texture = 0; -} - -unsigned int GbmSurface::GetBackingFramebufferObject() { - return fbo_; -} - -bool GbmSurface::OnMakeCurrent(gl::GLContext* context) { - DCHECK(!context_ || context == context_); - context_ = context; - if (!fbo_) { - glGenFramebuffersEXT(1, &fbo_); - if (!fbo_) - return false; - glGenTextures(arraysize(textures_), textures_); - if (!CreatePixmaps()) - return false; - } - BindFramebuffer(); - glBindFramebufferEXT(GL_FRAMEBUFFER, fbo_); - return SurfacelessEGL::OnMakeCurrent(context); -} - -bool GbmSurface::Resize(const gfx::Size& size, - float scale_factor, - ColorSpace color_space, - bool has_alpha) { - if (size == GetSize()) - return true; - // Alpha value isn't actually used in allocating buffers yet, so always use - // true instead. - return GbmSurfaceless::Resize(size, scale_factor, color_space, true) && - CreatePixmaps(); -} - -bool GbmSurface::SupportsPostSubBuffer() { - return false; -} - -void GbmSurface::SwapBuffersAsync( - const SwapCompletionCallback& completion_callback, - const PresentationCallback& presentation_callback) { - if (!images_[current_surface_]->ScheduleOverlayPlane( - widget(), 0, gfx::OverlayTransform::OVERLAY_TRANSFORM_NONE, - gfx::Rect(GetSize()), gfx::RectF(1, 1), /* enable_blend */ false, - /* gpu_fence */ nullptr)) { - completion_callback.Run(gfx::SwapResult::SWAP_FAILED, nullptr); - // Notify the caller, the buffer is never presented on a screen. - presentation_callback.Run(gfx::PresentationFeedback::Failure()); - return; - } - GbmSurfaceless::SwapBuffersAsync(completion_callback, presentation_callback); - current_surface_ ^= 1; - BindFramebuffer(); -} - -void GbmSurface::Destroy() { - if (!context_) - return; - scoped_refptr<gl::GLContext> previous_context = gl::GLContext::GetCurrent(); - scoped_refptr<GLSurface> previous_surface; - - bool was_current = previous_context && previous_context->IsCurrent(nullptr) && - GLSurface::GetCurrent() == this; - if (!was_current) { - // Only take a reference to previous surface if it's not |this| - // because otherwise we can take a self reference from our own dtor. - previous_surface = GLSurface::GetCurrent(); - context_->MakeCurrent(this); - } - - glBindFramebufferEXT(GL_FRAMEBUFFER, 0); - if (fbo_) { - glDeleteTextures(arraysize(textures_), textures_); - for (auto& texture : textures_) - texture = 0; - glDeleteFramebuffersEXT(1, &fbo_); - fbo_ = 0; - } - for (auto& image : images_) - image = nullptr; - - if (!was_current) { - if (previous_context) { - previous_context->MakeCurrent(previous_surface.get()); - } else { - context_->ReleaseCurrent(this); - } - } -} - -bool GbmSurface::IsSurfaceless() const { - return false; -} - -GbmSurface::~GbmSurface() { - Destroy(); -} - -void GbmSurface::BindFramebuffer() { - gl::ScopedFramebufferBinder fb(fbo_); - glFramebufferTexture2DEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - textures_[current_surface_], 0); -} - -bool GbmSurface::CreatePixmaps() { - if (!fbo_) - return true; - for (size_t i = 0; i < arraysize(textures_); i++) { - scoped_refptr<gfx::NativePixmap> pixmap = - surface_factory()->CreateNativePixmap( - widget(), GetSize(), display::DisplaySnapshot::PrimaryFormat(), - gfx::BufferUsage::SCANOUT); - if (!pixmap) - return false; - scoped_refptr<gl::GLImageNativePixmap> image = - new gl::GLImageNativePixmap(GetSize(), GL_BGRA_EXT); - if (!image->Initialize(pixmap.get(), - display::DisplaySnapshot::PrimaryFormat())) - return false; - images_[i] = image; - // Bind image to texture. - gl::ScopedTextureBinder binder(GL_TEXTURE_2D, textures_[i]); - if (!images_[i]->BindTexImage(GL_TEXTURE_2D)) - return false; - } - return true; -} - -} // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_surface.h b/chromium/ui/ozone/platform/drm/gpu/gbm_surface.h deleted file mode 100644 index be90a6dff8b..00000000000 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_surface.h +++ /dev/null @@ -1,60 +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_OZONE_PLATFORM_DRM_GPU_GBM_SURFACE_H_ -#define UI_OZONE_PLATFORM_DRM_GPU_GBM_SURFACE_H_ - -#include <memory> - -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "ui/gl/gl_context.h" -#include "ui/gl/gl_image.h" -#include "ui/ozone/platform/drm/gpu/gbm_surfaceless.h" - -namespace ui { - -class DrmWindowProxy; -class GbmSurfaceFactory; - -// A GLSurface for GBM Ozone platform provides surface-like semantics -// implemented through surfaceless. A framebuffer is bound automatically. -class GbmSurface : public GbmSurfaceless { - public: - GbmSurface(GbmSurfaceFactory* surface_factory, - std::unique_ptr<DrmWindowProxy> window, - gfx::AcceleratedWidget widget); - - // gl::GLSurface: - unsigned int GetBackingFramebufferObject() override; - bool OnMakeCurrent(gl::GLContext* context) override; - bool Resize(const gfx::Size& size, - float scale_factor, - ColorSpace color_space, - bool has_alpha) override; - bool SupportsPostSubBuffer() override; - void SwapBuffersAsync( - const SwapCompletionCallback& completion_callback, - const PresentationCallback& presentation_callback) override; - void Destroy() override; - bool IsSurfaceless() const override; - - private: - ~GbmSurface() override; - - void BindFramebuffer(); - bool CreatePixmaps(); - - scoped_refptr<gl::GLContext> context_; - GLuint fbo_ = 0; - GLuint textures_[2]; - scoped_refptr<gl::GLImage> images_[2]; - int current_surface_ = 0; - - DISALLOW_COPY_AND_ASSIGN(GbmSurface); -}; - -} // namespace ui - -#endif // UI_OZONE_PLATFORM_DRM_GPU_GBM_SURFACE_H_ diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc b/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc index fc0315c3056..dffec5db847 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.cc @@ -21,7 +21,6 @@ #include "ui/ozone/platform/drm/gpu/drm_window_proxy.h" #include "ui/ozone/platform/drm/gpu/gbm_overlay_surface.h" #include "ui/ozone/platform/drm/gpu/gbm_pixmap.h" -#include "ui/ozone/platform/drm/gpu/gbm_surface.h" #include "ui/ozone/platform/drm/gpu/gbm_surfaceless.h" #include "ui/ozone/platform/drm/gpu/proxy_helpers.h" #include "ui/ozone/platform/drm/gpu/screen_manager.h" @@ -64,9 +63,7 @@ class GLOzoneEGLGbm : public GLOzoneEGL { scoped_refptr<gl::GLSurface> CreateViewGLSurface( gfx::AcceleratedWidget window) override { - return gl::InitializeGLSurface(new GbmSurface( - surface_factory_, drm_thread_proxy_->CreateDrmWindowProxy(window), - window)); + return nullptr; } scoped_refptr<gl::GLSurface> CreateSurfacelessViewGLSurface( @@ -183,13 +180,23 @@ scoped_refptr<gfx::NativePixmap> GbmSurfaceFactory::CreateNativePixmapForVulkan( base::ScopedFD vk_image_fd(dup(buffer->GetPlaneFd(0))); DCHECK(vk_image_fd.is_valid()); + // TODO(spang): Fix this for formats other than gfx::BufferFormat::BGRA_8888 + DCHECK_EQ(format, display::DisplaySnapshot::PrimaryFormat()); + VkFormat vk_format = VK_FORMAT_B8G8R8A8_SRGB; + VkDmaBufImageCreateInfo dma_buf_image_create_info = { - .sType = static_cast<VkStructureType>( + /* .sType = */ static_cast<VkStructureType>( VK_STRUCTURE_TYPE_DMA_BUF_IMAGE_CREATE_INFO_INTEL), - .fd = vk_image_fd.release(), - .format = VK_FORMAT_B8G8R8A8_SRGB, - .extent = (VkExtent3D){size.width(), size.height(), 1}, - .strideInBytes = buffer->GetPlaneStride(0), + /* .pNext = */ nullptr, + /* .fd = */ vk_image_fd.release(), + /* .format = */ vk_format, + /* .extent = */ + { + /* .width = */ size.width(), + /* .height = */ size.height(), + /* .depth = */ 1, + }, + /* .strideInBytes = */ buffer->GetPlaneStride(0), }; VkResult result = diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.h b/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.h index 17ade46e881..c12508523b1 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.h +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_surface_factory.h @@ -87,7 +87,7 @@ class GbmSurfaceFactory : public SurfaceFactoryOzone { base::ThreadChecker thread_checker_; - DrmThreadProxy* drm_thread_proxy_; + DrmThreadProxy* const drm_thread_proxy_; std::map<gfx::AcceleratedWidget, GbmSurfaceless*> widget_to_surface_map_; diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc b/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc index fdd70fd8fbe..6ee9357e8cc 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.cc @@ -15,7 +15,6 @@ #include "ui/ozone/common/egl_util.h" #include "ui/ozone/platform/drm/gpu/drm_device.h" #include "ui/ozone/platform/drm/gpu/drm_framebuffer.h" -#include "ui/ozone/platform/drm/gpu/drm_vsync_provider.h" #include "ui/ozone/platform/drm/gpu/drm_window_proxy.h" #include "ui/ozone/platform/drm/gpu/gbm_surface_factory.h" @@ -54,9 +53,6 @@ void GbmSurfaceless::QueueOverlayPlane(DrmOverlayPlane plane) { bool GbmSurfaceless::Initialize(gl::GLSurfaceFormat format) { if (!SurfacelessEGL::Initialize(format)) return false; - vsync_provider_ = std::make_unique<DrmVSyncProvider>(window_.get()); - if (!vsync_provider_) - return false; return true; } @@ -84,10 +80,6 @@ bool GbmSurfaceless::IsOffscreen() { return false; } -gfx::VSyncProvider* GbmSurfaceless::GetVSyncProvider() { - return vsync_provider_.get(); -} - bool GbmSurfaceless::SupportsPresentationCallback() { return true; } diff --git a/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.h b/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.h index 31ba0d3f784..8923a624a59 100644 --- a/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.h +++ b/chromium/ui/ozone/platform/drm/gpu/gbm_surfaceless.h @@ -45,7 +45,6 @@ class GbmSurfaceless : public gl::SurfacelessEGL { bool enable_blend, std::unique_ptr<gfx::GpuFence> gpu_fence) override; bool IsOffscreen() override; - gfx::VSyncProvider* GetVSyncProvider() override; bool SupportsPresentationCallback() override; bool SupportsAsyncSwap() override; bool SupportsPostSubBuffer() override; @@ -98,16 +97,16 @@ class GbmSurfaceless : public gl::SurfacelessEGL { std::unique_ptr<gfx::GpuFence> out_fence); void OnPresentation(const gfx::PresentationFeedback& feedback); - GbmSurfaceFactory* surface_factory_; - std::unique_ptr<DrmWindowProxy> window_; + GbmSurfaceFactory* const surface_factory_; + const std::unique_ptr<DrmWindowProxy> window_; std::vector<DrmOverlayPlane> planes_; // The native surface. Deleting this is allowed to free the EGLNativeWindow. - gfx::AcceleratedWidget widget_; + const gfx::AcceleratedWidget widget_; std::unique_ptr<gfx::VSyncProvider> vsync_provider_; std::vector<std::unique_ptr<PendingFrame>> unsubmitted_frames_; std::unique_ptr<PendingFrame> submitted_frame_; - bool has_implicit_external_sync_; + const bool has_implicit_external_sync_; bool last_swap_buffers_result_ = true; bool supports_plane_gpu_fences_ = false; bool use_egl_fence_sync_ = true; diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.cc index 19a1b42bd36..297ff19a6df 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.cc @@ -21,8 +21,8 @@ #include "ui/gfx/swap_result.h" #include "ui/ozone/platform/drm/common/drm_util.h" #include "ui/ozone/platform/drm/gpu/crtc_controller.h" -#include "ui/ozone/platform/drm/gpu/drm_buffer.h" #include "ui/ozone/platform/drm/gpu/drm_device.h" +#include "ui/ozone/platform/drm/gpu/drm_dumb_buffer.h" #include "ui/ozone/platform/drm/gpu/hardware_display_plane.h" #include "ui/ozone/platform/drm/gpu/page_flip_request.h" @@ -42,7 +42,7 @@ void CompletePageFlip( std::move(callback).Run(presentation_feedback); } -void DrawCursor(DrmBuffer* cursor, const SkBitmap& image) { +void DrawCursor(DrmDumbBuffer* cursor, const SkBitmap& image) { SkRect damage; image.getBounds(&damage); @@ -309,13 +309,9 @@ gfx::Size HardwareDisplayController::GetModeSize() const { crtc_controllers_[0]->mode().vdisplay); } -uint32_t HardwareDisplayController::GetRefreshRate() const { - // If there are multiple CRTCs they should all have the same size. - return crtc_controllers_[0]->mode().vrefresh; -} - base::TimeDelta HardwareDisplayController::GetRefreshInterval() const { - uint32_t vrefresh = GetRefreshRate(); + // If there are multiple CRTCs they should all have the same refresh rate. + float vrefresh = ModeRefreshRate(crtc_controllers_[0]->mode()); return vrefresh ? base::TimeDelta::FromSeconds(1) / vrefresh : base::TimeDelta(); } @@ -338,6 +334,8 @@ void HardwareDisplayController::OnPageFlipComplete( return; // Modeset occured during this page flip. time_of_last_flip_ = presentation_feedback.timestamp; current_planes_ = std::move(pending_planes); + for (const auto& controller : crtc_controllers_) + controller->OnPageFlipComplete(); page_flip_request_ = nullptr; } @@ -359,7 +357,7 @@ void HardwareDisplayController::AllocateCursorBuffers() { SkImageInfo info = SkImageInfo::MakeN32Premul(max_cursor_size.width(), max_cursor_size.height()); for (size_t i = 0; i < arraysize(cursor_buffers_); ++i) { - cursor_buffers_[i] = std::make_unique<DrmBuffer>(GetDrmDevice()); + cursor_buffers_[i] = std::make_unique<DrmDumbBuffer>(GetDrmDevice()); // Don't register a framebuffer for cursors since they are special (they // aren't modesetting buffers and drivers may fail to register them due to // their small sizes). @@ -370,7 +368,7 @@ void HardwareDisplayController::AllocateCursorBuffers() { } } -DrmBuffer* HardwareDisplayController::NextCursorBuffer() { +DrmDumbBuffer* HardwareDisplayController::NextCursorBuffer() { ++cursor_frontbuffer_; cursor_frontbuffer_ %= base::size(cursor_buffers_); return cursor_buffers_[cursor_frontbuffer_].get(); diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.h b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.h index c232ee54326..1031f12cb09 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.h +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller.h @@ -31,7 +31,7 @@ namespace ui { class CrtcController; class DrmFramebuffer; -class DrmBuffer; +class DrmDumbBuffer; class DrmDevice; // The HDCOz will handle modesettings and scannout operations for hardware @@ -156,7 +156,6 @@ class HardwareDisplayController { gfx::Point origin() const { return origin_; } void set_origin(const gfx::Point& origin) { origin_ = origin; } - uint32_t GetRefreshRate() const; base::TimeDelta GetRefreshInterval() const; base::TimeTicks GetTimeOfLastFlip() const; @@ -176,7 +175,7 @@ class HardwareDisplayController { scoped_refptr<PageFlipRequest> page_flip_request, std::unique_ptr<gfx::GpuFence>* out_fence); void AllocateCursorBuffers(); - DrmBuffer* NextCursorBuffer(); + DrmDumbBuffer* NextCursorBuffer(); void UpdateCursorImage(); void UpdateCursorLocation(); void ResetCursor(); @@ -195,10 +194,10 @@ class HardwareDisplayController { DrmOverlayPlaneList current_planes_; base::TimeTicks time_of_last_flip_; - std::unique_ptr<DrmBuffer> cursor_buffers_[2]; + std::unique_ptr<DrmDumbBuffer> cursor_buffers_[2]; gfx::Point cursor_location_; int cursor_frontbuffer_ = 0; - DrmBuffer* current_cursor_ = nullptr; + DrmDumbBuffer* current_cursor_ = nullptr; bool is_disabled_; diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller_unittest.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller_unittest.cc index 662d7232d19..28c210a8222 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller_unittest.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_controller_unittest.cc @@ -141,7 +141,8 @@ void HardwareDisplayControllerTest::InitializeDrmDevice(bool use_atomic) { else if (pair.first == kInFormatsPropId) value = kInFormatsBlobPropId; - plane.properties.push_back({.id = pair.first, .value = value}); + plane.properties.push_back( + {/* .id = */ pair.first, /*.value = */ value}); }; drm_->SetPropertyBlob(ui::MockDrmDevice::AllocateInFormatsBlob( diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.h b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.h index 80609c61417..58c4dafc88e 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.h +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane.h @@ -70,7 +70,7 @@ class HardwareDisplayPlane { DrmDevice::Property plane_ctm; }; - uint32_t id_; + const uint32_t id_; uint32_t crtc_mask_ = 0; Properties properties_ = {}; diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc index 7b711d134d3..69a2c6bd3b4 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.cc @@ -49,15 +49,13 @@ HardwareDisplayPlaneManager::CrtcState::~CrtcState() = default; HardwareDisplayPlaneManager::CrtcState::CrtcState(CrtcState&&) = default; -HardwareDisplayPlaneManager::HardwareDisplayPlaneManager() : drm_(nullptr) { -} +HardwareDisplayPlaneManager::HardwareDisplayPlaneManager(DrmDevice* drm) + : drm_(drm) {} HardwareDisplayPlaneManager::~HardwareDisplayPlaneManager() { } -bool HardwareDisplayPlaneManager::Initialize(DrmDevice* drm) { - drm_ = drm; - +bool HardwareDisplayPlaneManager::Initialize() { // Try to get all of the planes if possible, so we don't have to try to // discover hidden primary planes. #if defined(DRM_CLIENT_CAP_UNIVERSAL_PLANES) @@ -65,10 +63,10 @@ bool HardwareDisplayPlaneManager::Initialize(DrmDevice* drm) { drm_->SetCapability(DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); #endif - if (!InitializeCrtcState(drm)) + if (!InitializeCrtcState()) return false; - if (!InitializePlanes(drm)) + if (!InitializePlanes()) return false; std::sort(planes_.begin(), planes_.end(), @@ -304,8 +302,8 @@ bool HardwareDisplayPlaneManager::SetGammaCorrection( return CommitGammaCorrection(*crtc_props); } -bool HardwareDisplayPlaneManager::InitializeCrtcState(DrmDevice* drm) { - ScopedDrmResourcesPtr resources(drm->GetResources()); +bool HardwareDisplayPlaneManager::InitializeCrtcState() { + ScopedDrmResourcesPtr resources(drm_->GetResources()); if (!resources) { PLOG(ERROR) << "Failed to get resources."; return false; @@ -318,7 +316,7 @@ bool HardwareDisplayPlaneManager::InitializeCrtcState(DrmDevice* drm) { state.properties.id = resources->crtcs[i]; ScopedDrmObjectPropertyPtr props( - drm->GetObjectProperties(resources->crtcs[i], DRM_MODE_OBJECT_CRTC)); + drm_->GetObjectProperties(resources->crtcs[i], DRM_MODE_OBJECT_CRTC)); if (!props) { PLOG(ERROR) << "Failed to get CRTC properties for crtc_id=" << state.properties.id; @@ -327,16 +325,16 @@ bool HardwareDisplayPlaneManager::InitializeCrtcState(DrmDevice* drm) { // These properties are optional. If they don't exist we can tell by the // invalid ID. - GetDrmPropertyForName(drm, props.get(), "CTM", &state.properties.ctm); - GetDrmPropertyForName(drm, props.get(), "GAMMA_LUT", + GetDrmPropertyForName(drm_, props.get(), "CTM", &state.properties.ctm); + GetDrmPropertyForName(drm_, props.get(), "GAMMA_LUT", &state.properties.gamma_lut); - GetDrmPropertyForName(drm, props.get(), "GAMMA_LUT_SIZE", + GetDrmPropertyForName(drm_, props.get(), "GAMMA_LUT_SIZE", &state.properties.gamma_lut_size); - GetDrmPropertyForName(drm, props.get(), "DEGAMMA_LUT", + GetDrmPropertyForName(drm_, props.get(), "DEGAMMA_LUT", &state.properties.degamma_lut); - GetDrmPropertyForName(drm, props.get(), "DEGAMMA_LUT_SIZE", + GetDrmPropertyForName(drm_, props.get(), "DEGAMMA_LUT_SIZE", &state.properties.degamma_lut_size); - GetDrmPropertyForName(drm, props.get(), "OUT_FENCE_PTR", + GetDrmPropertyForName(drm_, props.get(), "OUT_FENCE_PTR", &state.properties.out_fence_ptr); num_crtcs_with_out_fence_ptr += (state.properties.out_fence_ptr.id != 0); diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h index e60ddb68960..e4de8f55139 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager.h @@ -56,12 +56,12 @@ struct HardwareDisplayPlaneList { class HardwareDisplayPlaneManager { public: - HardwareDisplayPlaneManager(); + HardwareDisplayPlaneManager(DrmDevice* drm); virtual ~HardwareDisplayPlaneManager(); // This parses information from the drm driver, adding any new planes // or crtcs found. - bool Initialize(DrmDevice* drm); + bool Initialize(); // Clears old frame state out. Must be called before any AssignOverlayPlanes // calls. @@ -159,9 +159,9 @@ class HardwareDisplayPlaneManager { DISALLOW_COPY_AND_ASSIGN(CrtcState); }; - bool InitializeCrtcState(DrmDevice* drm); + bool InitializeCrtcState(); - virtual bool InitializePlanes(DrmDevice* drm) = 0; + virtual bool InitializePlanes() = 0; virtual bool SetPlaneData(HardwareDisplayPlaneList* plane_list, HardwareDisplayPlane* hw_plane, @@ -199,7 +199,7 @@ class HardwareDisplayPlaneManager { // Object containing the connection to the graphics device and wraps the API // calls to control it. Not owned. - DrmDevice* drm_; + DrmDevice* const drm_; bool has_universal_planes_ = false; diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc index c5d93398039..fe898efb2e6 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.cc @@ -51,8 +51,9 @@ std::unique_ptr<gfx::GpuFence> CreateMergedGpuFenceFromFDs( } // namespace -HardwareDisplayPlaneManagerAtomic::HardwareDisplayPlaneManagerAtomic() { -} +HardwareDisplayPlaneManagerAtomic::HardwareDisplayPlaneManagerAtomic( + DrmDevice* drm) + : HardwareDisplayPlaneManager(drm) {} HardwareDisplayPlaneManagerAtomic::~HardwareDisplayPlaneManagerAtomic() { } @@ -247,8 +248,8 @@ bool HardwareDisplayPlaneManagerAtomic::SetPlaneData( return true; } -bool HardwareDisplayPlaneManagerAtomic::InitializePlanes(DrmDevice* drm) { - ScopedDrmPlaneResPtr plane_resources = drm->GetPlaneResources(); +bool HardwareDisplayPlaneManagerAtomic::InitializePlanes() { + ScopedDrmPlaneResPtr plane_resources = drm_->GetPlaneResources(); if (!plane_resources) { PLOG(ERROR) << "Failed to get plane resources."; return false; @@ -258,7 +259,7 @@ bool HardwareDisplayPlaneManagerAtomic::InitializePlanes(DrmDevice* drm) { std::unique_ptr<HardwareDisplayPlane> plane( CreatePlane(plane_resources->planes[i])); - if (plane->Initialize(drm)) + if (plane->Initialize(drm_)) planes_.push_back(std::move(plane)); } diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h index 3c8d1e5089c..ae44ee124c8 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h @@ -14,7 +14,7 @@ namespace ui { class HardwareDisplayPlaneManagerAtomic : public HardwareDisplayPlaneManager { public: - HardwareDisplayPlaneManagerAtomic(); + HardwareDisplayPlaneManagerAtomic(DrmDevice* drm); ~HardwareDisplayPlaneManagerAtomic() override; // HardwareDisplayPlaneManager: @@ -41,7 +41,7 @@ class HardwareDisplayPlaneManagerAtomic : public HardwareDisplayPlaneManager { CrtcController* crtc) override; private: - bool InitializePlanes(DrmDevice* drm) override; + bool InitializePlanes() override; std::unique_ptr<HardwareDisplayPlane> CreatePlane(uint32_t plane_id) override; bool CommitColorMatrix(const CrtcProperties& crtc_props) override; bool CommitGammaCorrection(const CrtcProperties& crtc_props) override; diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc index 492d2201734..498aec547fc 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.cc @@ -36,8 +36,9 @@ ui::DrmOverlayPlaneList WaitForPlaneFences(ui::DrmOverlayPlaneList planes) { } // namespace -HardwareDisplayPlaneManagerLegacy::HardwareDisplayPlaneManagerLegacy() { -} +HardwareDisplayPlaneManagerLegacy::HardwareDisplayPlaneManagerLegacy( + DrmDevice* drm) + : HardwareDisplayPlaneManager(drm) {} HardwareDisplayPlaneManagerLegacy::~HardwareDisplayPlaneManagerLegacy() { } @@ -125,8 +126,8 @@ void HardwareDisplayPlaneManagerLegacy::RequestPlanesReadyCallback( std::move(callback)); } -bool HardwareDisplayPlaneManagerLegacy::InitializePlanes(DrmDevice* drm) { - ScopedDrmPlaneResPtr plane_resources = drm->GetPlaneResources(); +bool HardwareDisplayPlaneManagerLegacy::InitializePlanes() { + ScopedDrmPlaneResPtr plane_resources = drm_->GetPlaneResources(); if (!plane_resources) { PLOG(ERROR) << "Failed to get plane resources."; return false; @@ -136,7 +137,7 @@ bool HardwareDisplayPlaneManagerLegacy::InitializePlanes(DrmDevice* drm) { std::unique_ptr<HardwareDisplayPlane> plane( CreatePlane(plane_resources->planes[i])); - if (!plane->Initialize(drm)) + if (!plane->Initialize(drm_)) continue; // Overlays are not supported on the legacy path, so ignore all overlay @@ -159,7 +160,7 @@ bool HardwareDisplayPlaneManagerLegacy::InitializePlanes(DrmDevice* drm) { }) == planes_.end()) { std::unique_ptr<HardwareDisplayPlane> dummy_plane( new HardwareDisplayPlaneDummy(id, 1 << i)); - if (dummy_plane->Initialize(drm)) { + if (dummy_plane->Initialize(drm_)) { planes_.push_back(std::move(dummy_plane)); } } diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h index 171f0717e87..a40413ff2d0 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h @@ -14,7 +14,7 @@ namespace ui { class HardwareDisplayPlaneManagerLegacy : public HardwareDisplayPlaneManager { public: - HardwareDisplayPlaneManagerLegacy(); + HardwareDisplayPlaneManagerLegacy(DrmDevice* device); ~HardwareDisplayPlaneManagerLegacy() override; // HardwareDisplayPlaneManager: @@ -35,7 +35,7 @@ class HardwareDisplayPlaneManagerLegacy : public HardwareDisplayPlaneManager { base::OnceCallback<void(DrmOverlayPlaneList)> callback) override; protected: - bool InitializePlanes(DrmDevice* drm) override; + bool InitializePlanes() override; bool SetPlaneData(HardwareDisplayPlaneList* plane_list, HardwareDisplayPlane* hw_plane, const DrmOverlayPlane& overlay, diff --git a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc index 177a262ad90..ded5335acaa 100644 --- a/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc +++ b/chromium/ui/ozone/platform/drm/gpu/hardware_display_plane_manager_unittest.cc @@ -144,7 +144,8 @@ void HardwareDisplayPlaneManagerTest::InitializeDrmState( } else if (pair.first == kInFormatsPropId) { value = kInFormatsBlobPropId; } - plane_prop.properties.push_back({.id = pair.first, .value = value}); + plane_prop.properties.push_back( + {/* .id = */ pair.first, /* .value = */ value}); }; plane_properties_.emplace_back(std::move(plane_prop)); @@ -365,8 +366,8 @@ TEST_P(HardwareDisplayPlaneManagerAtomicTest, SharedPlanes) { plane_prop.id = 102; plane_prop.crtc_mask = (1 << 0) | (1 << 1); plane_prop.properties = { - {.id = kTypePropId, .value = DRM_PLANE_TYPE_OVERLAY}, - {.id = kInFormatsPropId, .value = kInFormatsBlobPropId}, + {/* .id = */ kTypePropId, /* .value = */ DRM_PLANE_TYPE_OVERLAY}, + {/* .id = */ kInFormatsPropId, /* .value = */ kInFormatsBlobPropId}, }; plane_properties_.emplace_back(std::move(plane_prop)); fake_drm_->InitializeState(crtc_properties_, plane_properties_, @@ -459,8 +460,10 @@ TEST_P(HardwareDisplayPlaneManagerAtomicTest, MultipleFramesDifferentPlanes) { TEST_P(HardwareDisplayPlaneManagerAtomicTest, SetColorCorrectionOnAllCrtcPlanes_Success) { InitializeDrmState(/*crtc_count=*/1, /*planes_per_crtc=*/1); - plane_properties_[0].properties.push_back({.id = kPlaneCtmId, .value = 0}); - plane_properties_[1].properties.push_back({.id = kPlaneCtmId, .value = 0}); + plane_properties_[0].properties.push_back( + {/* .id = */ kPlaneCtmId, /* .value = */ 0}); + plane_properties_[1].properties.push_back( + {/* .id = */ kPlaneCtmId, /* .value = */ 0}); fake_drm_->InitializeState(crtc_properties_, plane_properties_, property_names_, use_atomic_); @@ -485,7 +488,8 @@ TEST_P(HardwareDisplayPlaneManagerAtomicTest, TEST_P(HardwareDisplayPlaneManagerAtomicTest, SetColorCorrectionOnAllCrtcPlanes_OnePlaneMissingCtmProperty) { InitializeDrmState(/*crtc_count=*/1, /*planes_per_crtc=*/2); - plane_properties_[0].properties.push_back({.id = kPlaneCtmId, .value = 0}); + plane_properties_[0].properties.push_back( + {/* .id = */ kPlaneCtmId, /* .value = */ 0}); fake_drm_->InitializeState(crtc_properties_, plane_properties_, property_names_, use_atomic_); @@ -497,7 +501,8 @@ TEST_P(HardwareDisplayPlaneManagerAtomicTest, TEST_P(HardwareDisplayPlaneManagerTest, SetColorMatrix_Success) { InitializeDrmState(/*crtc_count=*/1, /*planes_per_crtc=*/1); - crtc_properties_[0].properties.push_back({.id = kCtmPropId, .value = 0}); + crtc_properties_[0].properties.push_back( + {/* .id = */ kCtmPropId, /* .value = */ 0}); fake_drm_->InitializeState(crtc_properties_, plane_properties_, property_names_, use_atomic_); @@ -519,7 +524,8 @@ TEST_P(HardwareDisplayPlaneManagerTest, SetColorMatrix_Success) { TEST_P(HardwareDisplayPlaneManagerTest, SetColorMatrix_ErrorEmptyCtm) { InitializeDrmState(/*crtc_count=*/1, /*planes_per_crtc=*/1); - crtc_properties_[0].properties.push_back({.id = kCtmPropId, .value = 0}); + crtc_properties_[0].properties.push_back( + {/* .id = */ kCtmPropId, /* .value = */ 0}); fake_drm_->InitializeState(crtc_properties_, plane_properties_, property_names_, use_atomic_); @@ -537,7 +543,8 @@ TEST_P(HardwareDisplayPlaneManagerTest, SetColorMatrix_ErrorEmptyCtm) { TEST_P(HardwareDisplayPlaneManagerTest, SetGammaCorrection_MissingDegamma) { InitializeDrmState(/*crtc_count=*/1, /*planes_per_crtc=*/1); - crtc_properties_[0].properties.push_back({.id = kCtmPropId, .value = 0}); + crtc_properties_[0].properties.push_back( + {/* .id = */ kCtmPropId, /* .value = */ 0}); fake_drm_->InitializeState(crtc_properties_, plane_properties_, property_names_, use_atomic_); @@ -553,7 +560,7 @@ TEST_P(HardwareDisplayPlaneManagerTest, SetGammaCorrection_MissingDegamma) { } crtc_properties_[0].properties.push_back( - {.id = kDegammaLutSizePropId, .value = 1}); + {/* .id = */ kDegammaLutSizePropId, /* .value = */ 1}); fake_drm_->InitializeState(crtc_properties_, plane_properties_, property_names_, /*use_atomic=*/true); @@ -571,7 +578,8 @@ TEST_P(HardwareDisplayPlaneManagerTest, SetGammaCorrection_MissingDegamma) { TEST_P(HardwareDisplayPlaneManagerTest, SetGammaCorrection_MissingGamma) { InitializeDrmState(/*crtc_count=*/1, /*planes_per_crtc=*/1); - crtc_properties_[0].properties.push_back({.id = kCtmPropId, .value = 0}); + crtc_properties_[0].properties.push_back( + {/* .id = */ kCtmPropId, /* .value = */ 0}); fake_drm_->InitializeState(crtc_properties_, plane_properties_, property_names_, use_atomic_); @@ -587,7 +595,7 @@ TEST_P(HardwareDisplayPlaneManagerTest, SetGammaCorrection_MissingGamma) { } crtc_properties_[0].properties.push_back( - {.id = kGammaLutSizePropId, .value = 1}); + {/* .id = */ kGammaLutSizePropId, /* .value = */ 1}); fake_drm_->InitializeState(crtc_properties_, plane_properties_, property_names_, /*use_atomic=*/true); @@ -625,7 +633,8 @@ TEST_P(HardwareDisplayPlaneManagerTest, SetGammaCorrection_LegacyGamma) { TEST_P(HardwareDisplayPlaneManagerTest, SetGammaCorrection_Success) { InitializeDrmState(/*crtc_count=*/1, /*planes_per_crtc=*/1); - crtc_properties_[0].properties.push_back({.id = kCtmPropId, .value = 0}); + crtc_properties_[0].properties.push_back( + {/* .id = */ kCtmPropId, /* .value = */ 0}); fake_drm_->InitializeState(crtc_properties_, plane_properties_, property_names_, use_atomic_); @@ -634,12 +643,13 @@ TEST_P(HardwareDisplayPlaneManagerTest, SetGammaCorrection_Success) { EXPECT_EQ(0, fake_drm_->get_commit_count()); crtc_properties_[0].properties.push_back( - {.id = kDegammaLutSizePropId, .value = 1}); + {/* .id = */ kDegammaLutSizePropId, /* .value = */ 1}); + crtc_properties_[0].properties.push_back( + {/* .id = */ kDegammaLutPropId, /* .value = */ 0}); crtc_properties_[0].properties.push_back( - {.id = kDegammaLutPropId, .value = 0}); + {/* .id = */ kGammaLutSizePropId, /* .value = */ 1}); crtc_properties_[0].properties.push_back( - {.id = kGammaLutSizePropId, .value = 1}); - crtc_properties_[0].properties.push_back({.id = kGammaLutPropId, .value = 0}); + {/* .id = */ kGammaLutPropId, /* .value = */ 0}); fake_drm_->InitializeState(crtc_properties_, plane_properties_, property_names_, use_atomic_); @@ -712,9 +722,9 @@ TEST_P(HardwareDisplayPlaneManagerTest, InitializationFailsIfSupportForOutFencePropertiesIsPartial) { InitializeDrmState(/*crtc_count=*/3, /*planes_per_crtc=*/1); crtc_properties_[0].properties.push_back( - {.id = kOutFencePtrPropId, .value = 1}); + {/* .id = */ kOutFencePtrPropId, /* .value = */ 1}); crtc_properties_[2].properties.push_back( - {.id = kOutFencePtrPropId, .value = 2}); + {/* .id = */ kOutFencePtrPropId, /* .value = */ 2}); EXPECT_FALSE(fake_drm_->InitializeStateWithResult( crtc_properties_, plane_properties_, property_names_, use_atomic_)); @@ -724,11 +734,11 @@ TEST_P(HardwareDisplayPlaneManagerTest, InitializationSucceedsIfSupportForOutFencePropertiesIsComplete) { InitializeDrmState(/*crtc_count=*/3, /*planes_per_crtc=*/1); crtc_properties_[0].properties.push_back( - {.id = kOutFencePtrPropId, .value = 1}); + {/* .id = */ kOutFencePtrPropId, /* .value = */ 1}); crtc_properties_[1].properties.push_back( - {.id = kOutFencePtrPropId, .value = 2}); + {/* .id = */ kOutFencePtrPropId, /* .value = */ 2}); crtc_properties_[2].properties.push_back( - {.id = kOutFencePtrPropId, .value = 3}); + {/* .id = */ kOutFencePtrPropId, /* .value = */ 3}); EXPECT_TRUE(fake_drm_->InitializeStateWithResult( crtc_properties_, plane_properties_, property_names_, use_atomic_)); @@ -844,11 +854,13 @@ void HardwareDisplayPlaneManagerPlanesReadyTest::RequestPlanesReady( } void HardwareDisplayPlaneManagerPlanesReadyTest::UseLegacyManager() { - plane_manager_ = std::make_unique<ui::HardwareDisplayPlaneManagerLegacy>(); + plane_manager_ = + std::make_unique<ui::HardwareDisplayPlaneManagerLegacy>(fake_drm_.get()); } void HardwareDisplayPlaneManagerPlanesReadyTest::UseAtomicManager() { - plane_manager_ = std::make_unique<ui::HardwareDisplayPlaneManagerAtomic>(); + plane_manager_ = + std::make_unique<ui::HardwareDisplayPlaneManagerAtomic>(fake_drm_.get()); } TEST_F(HardwareDisplayPlaneManagerPlanesReadyTest, @@ -926,11 +938,11 @@ class HardwareDisplayPlaneAtomicMock : public ui::HardwareDisplayPlaneAtomic { }; TEST(HardwareDisplayPlaneManagerAtomic, EnableBlend) { - auto plane_manager = - std::make_unique<ui::HardwareDisplayPlaneManagerAtomic>(); auto gbm_device = std::make_unique<ui::MockGbmDevice>(); auto drm_device = base::MakeRefCounted<ui::MockDrmDevice>(std::move(gbm_device)); + auto plane_manager = + std::make_unique<ui::HardwareDisplayPlaneManagerAtomic>(drm_device.get()); ui::HardwareDisplayPlaneList plane_list; HardwareDisplayPlaneAtomicMock hw_plane; std::unique_ptr<ui::GbmBuffer> buffer = diff --git a/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.cc b/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.cc index 4b7f5a46d36..e9dfffbfb41 100644 --- a/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.cc +++ b/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.cc @@ -84,7 +84,6 @@ MockDrmDevice::MockDrmDevice(std::unique_ptr<GbmDevice> gbm_device) add_framebuffer_call_count_(0), remove_framebuffer_call_count_(0), page_flip_call_count_(0), - overlay_flip_call_count_(0), overlay_clear_call_count_(0), allocate_buffer_count_(0), set_crtc_expectation_(true), @@ -92,7 +91,7 @@ MockDrmDevice::MockDrmDevice(std::unique_ptr<GbmDevice> gbm_device) page_flip_expectation_(true), create_dumb_buffer_expectation_(true), current_framebuffer_(0) { - plane_manager_.reset(new HardwareDisplayPlaneManagerLegacy()); + plane_manager_.reset(new HardwareDisplayPlaneManagerLegacy(this)); } // static @@ -141,12 +140,12 @@ bool MockDrmDevice::InitializeStateWithResult( plane_properties_ = plane_properties; property_names_ = property_names; if (use_atomic) { - plane_manager_.reset(new HardwareDisplayPlaneManagerAtomic()); + plane_manager_.reset(new HardwareDisplayPlaneManagerAtomic(this)); } else { - plane_manager_.reset(new HardwareDisplayPlaneManagerLegacy()); + plane_manager_.reset(new HardwareDisplayPlaneManagerLegacy(this)); } - return plane_manager_->Initialize(this); + return plane_manager_->Initialize(); } MockDrmDevice::~MockDrmDevice() {} @@ -198,6 +197,7 @@ bool MockDrmDevice::SetCrtc(uint32_t crtc_id, uint32_t framebuffer, std::vector<uint32_t> connectors, drmModeModeInfo* mode) { + crtc_fb_[crtc_id] = framebuffer; current_framebuffer_ = framebuffer; set_crtc_call_count_++; return set_crtc_expectation_; @@ -229,11 +229,24 @@ bool MockDrmDevice::AddFramebuffer2(uint32_t width, uint32_t flags) { add_framebuffer_call_count_++; *framebuffer = add_framebuffer_call_count_; + framebuffer_ids_.insert(*framebuffer); return add_framebuffer_expectation_; } bool MockDrmDevice::RemoveFramebuffer(uint32_t framebuffer) { + { + auto it = framebuffer_ids_.find(framebuffer); + CHECK(it != framebuffer_ids_.end()); + framebuffer_ids_.erase(it); + } remove_framebuffer_call_count_++; + std::vector<uint32_t> crtcs_to_clear; + for (auto crtc_fb : crtc_fb_) { + if (crtc_fb.second == framebuffer) + crtcs_to_clear.push_back(crtc_fb.first); + } + for (auto crtc : crtcs_to_clear) + crtc_fb_[crtc] = 0; return true; } @@ -246,23 +259,13 @@ bool MockDrmDevice::PageFlip(uint32_t crtc_id, scoped_refptr<PageFlipRequest> page_flip_request) { page_flip_call_count_++; DCHECK(page_flip_request); + crtc_fb_[crtc_id] = framebuffer; current_framebuffer_ = framebuffer; if (page_flip_expectation_) callbacks_.push(page_flip_request->AddPageFlip()); return page_flip_expectation_; } -bool MockDrmDevice::PageFlipOverlay(uint32_t crtc_id, - uint32_t framebuffer, - const gfx::Rect& location, - const gfx::Rect& source, - int overlay_plane) { - if (!framebuffer) - overlay_clear_call_count_++; - overlay_flip_call_count_++; - return true; -} - ScopedDrmPlanePtr MockDrmDevice::GetPlane(uint32_t plane_id) { PlaneProperties* properties = FindObjectById(plane_id, plane_properties_); if (!properties) @@ -434,6 +437,11 @@ bool MockDrmDevice::SetCapability(uint64_t capability, uint64_t value) { return true; } +uint32_t MockDrmDevice::GetFramebufferForCrtc(uint32_t crtc_id) const { + auto it = crtc_fb_.find(crtc_id); + return it != crtc_fb_.end() ? it->second : 0u; +} + void MockDrmDevice::RunCallbacks() { while (!callbacks_.empty()) { PageFlipCallback callback = std::move(callbacks_.front()); diff --git a/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.h b/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.h index 2e705d00ad5..64279da0cfd 100644 --- a/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.h +++ b/chromium/ui/ozone/platform/drm/gpu/mock_drm_device.h @@ -61,7 +61,6 @@ class MockDrmDevice : public DrmDevice { return remove_framebuffer_call_count_; } int get_page_flip_call_count() const { return page_flip_call_count_; } - int get_overlay_flip_call_count() const { return overlay_flip_call_count_; } int get_overlay_clear_call_count() const { return overlay_clear_call_count_; } int get_commit_count() const { return commit_count_; } int get_set_object_property_count() const { @@ -131,11 +130,6 @@ class MockDrmDevice : public DrmDevice { bool PageFlip(uint32_t crtc_id, uint32_t framebuffer, scoped_refptr<PageFlipRequest> page_flip_request) override; - bool PageFlipOverlay(uint32_t crtc_id, - uint32_t framebuffer, - const gfx::Rect& location, - const gfx::Rect& source, - int overlay_plane) override; ScopedDrmPlanePtr GetPlane(uint32_t plane_id) override; ScopedDrmPropertyPtr GetProperty(drmModeConnector* connector, const char* name) override; @@ -172,6 +166,7 @@ class MockDrmDevice : public DrmDevice { uint32_t crtc_id, const std::vector<display::GammaRampRGBEntry>& lut) override; bool SetCapability(uint64_t capability, uint64_t value) override; + uint32_t GetFramebufferForCrtc(uint32_t crtc_id) const; private: ~MockDrmDevice() override; @@ -190,7 +185,6 @@ class MockDrmDevice : public DrmDevice { int add_framebuffer_call_count_; int remove_framebuffer_call_count_; int page_flip_call_count_; - int overlay_flip_call_count_; int overlay_clear_call_count_; int allocate_buffer_count_; int commit_count_ = 0; @@ -212,6 +206,9 @@ class MockDrmDevice : public DrmDevice { std::map<uint32_t, ScopedDrmPropertyBlobPtr> blob_property_map_; + std::set<uint32_t> framebuffer_ids_; + std::map<uint32_t, uint32_t> crtc_fb_; + base::queue<PageFlipCallback> callbacks_; std::vector<CrtcProperties> crtc_properties_; diff --git a/chromium/ui/ozone/platform/drm/gpu/mock_gbm_device.cc b/chromium/ui/ozone/platform/drm/gpu/mock_gbm_device.cc index d7b298f9d51..dc640dd1f38 100644 --- a/chromium/ui/ozone/platform/drm/gpu/mock_gbm_device.cc +++ b/chromium/ui/ozone/platform/drm/gpu/mock_gbm_device.cc @@ -10,6 +10,7 @@ #include "base/logging.h" #include "base/numerics/safe_math.h" #include "testing/gtest/include/gtest/gtest.h" +#include "third_party/skia/include/core/SkSurface.h" #include "ui/ozone/common/linux/drm_util_linux.h" #include "ui/ozone/common/linux/gbm_buffer.h" @@ -69,6 +70,8 @@ class MockGbmBuffer final : public ui::GbmBuffer { return gfx::NativePixmapHandle(); } + sk_sp<SkSurface> GetSurface() override { return nullptr; } + private: uint32_t format_ = 0; uint64_t format_modifier_ = 0; diff --git a/chromium/ui/ozone/platform/drm/gpu/page_flip_request.h b/chromium/ui/ozone/platform/drm/gpu/page_flip_request.h index 2aad6034057..d24d6bbc0da 100644 --- a/chromium/ui/ozone/platform/drm/gpu/page_flip_request.h +++ b/chromium/ui/ozone/platform/drm/gpu/page_flip_request.h @@ -45,8 +45,7 @@ class PageFlipRequest : public base::RefCounted<PageFlipRequest> { PresentationOnceCallback callback_; int page_flip_count_ = 0; - base::TimeDelta refresh_interval_; - gfx::SwapResult result_ = gfx::SwapResult::SWAP_ACK; + const base::TimeDelta refresh_interval_; DISALLOW_COPY_AND_ASSIGN(PageFlipRequest); }; diff --git a/chromium/ui/ozone/platform/drm/gpu/screen_manager.cc b/chromium/ui/ozone/platform/drm/gpu/screen_manager.cc index 17a2d1b50f3..5fe1773665d 100644 --- a/chromium/ui/ozone/platform/drm/gpu/screen_manager.cc +++ b/chromium/ui/ozone/platform/drm/gpu/screen_manager.cc @@ -10,16 +10,18 @@ #include "base/files/platform_file.h" #include "third_party/skia/include/core/SkCanvas.h" +#include "third_party/skia/include/core/SkSurface.h" #include "ui/display/types/display_snapshot.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/gpu_fence.h" +#include "ui/gfx/skia_util.h" #include "ui/ozone/common/linux/gbm_buffer.h" #include "ui/ozone/platform/drm/common/drm_util.h" #include "ui/ozone/platform/drm/gpu/crtc_controller.h" -#include "ui/ozone/platform/drm/gpu/drm_console_buffer.h" #include "ui/ozone/platform/drm/gpu/drm_device.h" +#include "ui/ozone/platform/drm/gpu/drm_dumb_buffer.h" #include "ui/ozone/platform/drm/gpu/drm_framebuffer.h" #include "ui/ozone/platform/drm/gpu/drm_window.h" #include "ui/ozone/platform/drm/gpu/hardware_display_controller.h" @@ -29,25 +31,19 @@ namespace ui { namespace { // Copies the contents of the saved framebuffer from the CRTCs in |controller| -// to the new modeset buffer |buffer|. -void FillModesetBuffer(const scoped_refptr<DrmDevice>& drm, +// to the surface for the new modeset buffer |surface|. +bool FillModesetBuffer(const scoped_refptr<DrmDevice>& drm, HardwareDisplayController* controller, - DrmFramebuffer* buffer) { - DrmConsoleBuffer modeset_buffer(drm, buffer->opaque_framebuffer_id()); - if (!modeset_buffer.Initialize()) { - VLOG(2) << "Failed to grab framebuffer " << buffer->opaque_framebuffer_id(); - return; - } - + SkSurface* surface, + uint32_t fourcc_format) { DCHECK(!controller->crtc_controllers().empty()); CrtcController* first_crtc = controller->crtc_controllers()[0].get(); ScopedDrmCrtcPtr saved_crtc(drm->GetCrtc(first_crtc->crtc())); if (!saved_crtc || !saved_crtc->buffer_id) { VLOG(2) << "Crtc has no saved state or wasn't modeset"; - return; + return false; } - uint32_t fourcc_format = buffer->framebuffer_pixel_format(); const auto& modifiers = controller->GetFormatModifiers(fourcc_format); for (const uint64_t modifier : modifiers) { // A value of 0 means DRM_FORMAT_MOD_NONE. If the CRTC has any other @@ -56,30 +52,32 @@ void FillModesetBuffer(const scoped_refptr<DrmDevice>& drm, if (modifier) { VLOG(2) << "Crtc has a modifier and we might not know how to interpret " "the fb."; - return; + return false; } } // If the display controller is in mirror mode, the CRTCs should be sharing // the same framebuffer. - DrmConsoleBuffer saved_buffer(drm, saved_crtc->buffer_id); - if (!saved_buffer.Initialize()) { + DrmDumbBuffer saved_buffer(drm); + if (!saved_buffer.InitializeFromFramebuffer(saved_crtc->buffer_id)) { VLOG(2) << "Failed to grab saved framebuffer " << saved_crtc->buffer_id; - return; + return false; } // Don't copy anything if the sizes mismatch. This can happen when the user // changes modes. - if (saved_buffer.canvas()->getBaseLayerSize() != - modeset_buffer.canvas()->getBaseLayerSize()) { + if (saved_buffer.GetCanvas()->getBaseLayerSize() != + surface->getCanvas()->getBaseLayerSize()) { VLOG(2) << "Previous buffer has a different size than modeset buffer"; - return; + return false; } SkPaint paint; // Copy the source buffer. Do not perform any blending. paint.setBlendMode(SkBlendMode::kSrc); - modeset_buffer.canvas()->drawImage(saved_buffer.image(), 0, 0, &paint); + surface->getCanvas()->drawImage(saved_buffer.surface()->makeImageSnapshot(), + 0, 0, &paint); + return true; } CrtcController* GetCrtcController(HardwareDisplayController* controller, @@ -345,8 +343,8 @@ void ScreenManager::UpdateControllerToWindowMapping() { if (it != window_to_controller_map.end()) controller = it->second; - bool should_enable = - controller && pair.second->GetController() != controller; + bool should_enable = controller && pair.second->GetController() && + pair.second->GetController() != controller; pair.second->SetController(controller); // If we're moving windows between controllers modeset the controller @@ -400,7 +398,16 @@ DrmOverlayPlane ScreenManager::GetModesetBuffer( LOG(ERROR) << "Failed to add framebuffer for scanout buffer"; return DrmOverlayPlane::Error(); } - FillModesetBuffer(drm, controller, framebuffer.get()); + + sk_sp<SkSurface> surface = buffer->GetSurface(); + if (!surface) { + VLOG(2) << "Can't get a SkSurface from the modeset gbm buffer."; + } else if (!FillModesetBuffer(drm, controller, surface.get(), + buffer->GetFormat())) { + // If we fail to fill the modeset buffer, clear it black to avoid displaying + // an uninitialized framebuffer. + surface->getCanvas()->clear(SK_ColorBLACK); + } return DrmOverlayPlane(framebuffer, nullptr); } diff --git a/chromium/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc b/chromium/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc index 1d05b1d9d76..e8cae25799c 100644 --- a/chromium/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc +++ b/chromium/ui/ozone/platform/drm/gpu/screen_manager_unittest.cc @@ -572,6 +572,26 @@ TEST_F(ScreenManagerTest, DISABLED_RejectBufferWithIncompatibleModifiers) { window->Shutdown(); } +TEST_F(ScreenManagerTest, ConfigureDisplayControllerShouldModesetOnce) { + std::unique_ptr<ui::DrmWindow> window( + new ui::DrmWindow(1, device_manager_.get(), screen_manager_.get())); + window->Initialize(); + window->SetBounds(GetPrimaryBounds()); + screen_manager_->AddWindow(1, std::move(window)); + + screen_manager_->AddDisplayController(drm_, kPrimaryCrtc, kPrimaryConnector); + screen_manager_->ConfigureDisplayController( + drm_, kPrimaryCrtc, kPrimaryConnector, GetPrimaryBounds().origin(), + kDefaultMode); + + // When a window that had no controller becomes associated with a new + // controller, expect the crtc to be modeset once. + EXPECT_EQ(drm_->get_set_crtc_call_count(), 1); + + window = screen_manager_->RemoveWindow(1); + window->Shutdown(); +} + TEST(ScreenManagerTest2, ShouldNotHardwareMirrorDifferentDrmDevices) { auto gbm_device1 = std::make_unique<MockGbmDevice>(); auto drm_device1 = @@ -681,4 +701,56 @@ TEST(ScreenManagerTest2, ShouldNotHardwareMirrorDifferentDrmDevices) { screen_manager.RemoveWindow(3)->Shutdown(); } +// crbug.com/888553 +TEST(ScreenManagerTest2, ShouldNotUnbindFramebufferOnJoiningMirror) { + auto gbm_device = std::make_unique<MockGbmDevice>(); + auto drm_device = base::MakeRefCounted<MockDrmDevice>(std::move(gbm_device)); + DrmDeviceManager drm_device_manager(nullptr); + ScreenManager screen_manager; + + constexpr uint32_t kCrtc39 = 39; + constexpr uint32_t kConnector43 = 43; + constexpr uint32_t kCrtc41 = 41; + constexpr uint32_t kConnector46 = 46; + + constexpr drmModeModeInfo kMode1080p60 = { + /* clock= */ 148500, + /* hdisplay= */ 1920, + /* hsync_start= */ 2008, + /* hsync_end= */ 2052, + /* htotal= */ 2200, + /* hskew= */ 0, + /* vdisplay= */ 1080, + /* vsync_start= */ 1084, + /* vsync_end= */ 1089, + /* vtotal= */ 1125, + /* vscan= */ 0, + /* vrefresh= */ 60, + /* flags= */ 0xa, + /* type= */ 64, + /* name= */ "1920x1080", + }; + + // Both displays connect at startup. + { + auto window1 = + std::make_unique<DrmWindow>(1, &drm_device_manager, &screen_manager); + window1->Initialize(); + screen_manager.AddWindow(1, std::move(window1)); + screen_manager.GetWindow(1)->SetBounds(gfx::Rect(0, 0, 1920, 1080)); + screen_manager.AddDisplayController(drm_device, kCrtc39, kConnector43); + screen_manager.AddDisplayController(drm_device, kCrtc41, kConnector46); + screen_manager.ConfigureDisplayController(drm_device, kCrtc39, kConnector43, + gfx::Point(0, 0), kMode1080p60); + screen_manager.ConfigureDisplayController(drm_device, kCrtc41, kConnector46, + gfx::Point(0, 0), kMode1080p60); + } + + EXPECT_NE(0u, drm_device->GetFramebufferForCrtc(kCrtc39)); + EXPECT_NE(0u, drm_device->GetFramebufferForCrtc(kCrtc41)); + + // Cleanup. + screen_manager.RemoveWindow(1)->Shutdown(); +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc b/chromium/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc index b517393492c..276002409b7 100644 --- a/chromium/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc +++ b/chromium/ui/ozone/platform/drm/gpu/vulkan_implementation_gbm.cc @@ -31,7 +31,7 @@ bool VulkanImplementationGbm::InitializeVulkanInstance() { "VK_KHR_external_fence_capabilities", "VK_KHR_get_physical_device_properties2", }; - if (!vulkan_instance_.Initialize(required_extensions)) { + if (!vulkan_instance_.Initialize(required_extensions, {})) { vulkan_instance_.Destroy(); return false; } diff --git a/chromium/ui/ozone/platform/drm/host/drm_cursor.h b/chromium/ui/ozone/platform/drm/host/drm_cursor.h index a31f27f2422..40f751db193 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_cursor.h +++ b/chromium/ui/ozone/platform/drm/host/drm_cursor.h @@ -111,7 +111,7 @@ class DrmCursor : public CursorDelegateEvdev { // The bounds that the cursor is confined to in |window|. gfx::Rect confined_bounds_; - DrmWindowHostManager* window_manager_; // Not owned. + DrmWindowHostManager* const window_manager_; // Not owned. std::unique_ptr<DrmCursorProxy> proxy_; diff --git a/chromium/ui/ozone/platform/drm/host/drm_device_connector.h b/chromium/ui/ozone/platform/drm/host/drm_device_connector.h index 8de058eba6d..9c2f5d9f4b1 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_device_connector.h +++ b/chromium/ui/ozone/platform/drm/host/drm_device_connector.h @@ -58,13 +58,13 @@ class DrmDeviceConnector : public GpuPlatformSupportHost { bool am_running_in_ws_mode() { return !!ws_runner_; } // This will be present if the Viz host has a service manager. - service_manager::Connector* connector_; + service_manager::Connector* const connector_; // This will be used if we are operating under content/gpu without a service // manager. GpuHostBindInterfaceCallback binder_callback_; - scoped_refptr<HostDrmDevice> host_drm_device_; + const scoped_refptr<HostDrmDevice> host_drm_device_; scoped_refptr<base::SingleThreadTaskRunner> ws_runner_; DISALLOW_COPY_AND_ASSIGN(DrmDeviceConnector); diff --git a/chromium/ui/ozone/platform/drm/host/drm_display_host.h b/chromium/ui/ozone/platform/drm/host/drm_display_host.h index 55b5de5bf7b..1311e66db3d 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_display_host.h +++ b/chromium/ui/ozone/platform/drm/host/drm_display_host.h @@ -56,7 +56,7 @@ class DrmDisplayHost : public GpuThreadObserver { // Calls all the callbacks with failure. void ClearCallbacks(); - GpuThreadAdapter* sender_; // Not owned. + GpuThreadAdapter* const sender_; // Not owned. std::unique_ptr<display::DisplaySnapshot> snapshot_; diff --git a/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.h b/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.h index dae7f3bbc1e..3b77e9bc79a 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.h +++ b/chromium/ui/ozone/platform/drm/host/drm_display_host_manager.h @@ -98,17 +98,17 @@ class DrmDisplayHostManager : public DeviceEventObserver, GpuThreadObserver { void NotifyDisplayDelegate() const; - GpuThreadAdapter* proxy_; // Not owned. - DeviceManager* device_manager_; // Not owned. - DrmOverlayManager* overlay_manager_; // Not owned. - InputControllerEvdev* input_controller_; // Not owned. + GpuThreadAdapter* const proxy_; // Not owned. + DeviceManager* const device_manager_; // Not owned. + DrmOverlayManager* const overlay_manager_; // Not owned. + InputControllerEvdev* const input_controller_; // Not owned. DrmNativeDisplayDelegate* delegate_ = nullptr; // Not owned. // File path for the primary graphics card which is opened by default in the // GPU process. We'll avoid opening this in hotplug events since it will race // with the GPU process trying to open it and aquire DRM master. - base::FilePath primary_graphics_card_path_; + const base::FilePath primary_graphics_card_path_; // Keeps track if there is a dummy display. This happens on initialization // when there is no connection to the GPU to update the displays. diff --git a/chromium/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h b/chromium/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h index e31327b7515..45d45b8a8ef 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h +++ b/chromium/ui/ozone/platform/drm/host/drm_gpu_platform_support_host.h @@ -128,7 +128,7 @@ class DrmGpuPlatformSupportHost : public GpuPlatformSupportHost, DrmDisplayHostManager* display_manager_; // Not owned. DrmOverlayManager* overlay_manager_; // Not owned. - DrmCursor* cursor_; // Not owned. + DrmCursor* const cursor_; // Not owned. base::ObserverList<GpuThreadObserver>::Unchecked gpu_thread_observers_; base::WeakPtr<DrmGpuPlatformSupportHost> weak_ptr_; diff --git a/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.h b/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.h index 843d7e3d586..ce322d3c10b 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.h +++ b/chromium/ui/ozone/platform/drm/host/drm_native_display_delegate.h @@ -49,7 +49,7 @@ class DrmNativeDisplayDelegate : public display::NativeDisplayDelegate { display::FakeDisplayController* GetFakeDisplayController() override; private: - DrmDisplayHostManager* display_manager_; // Not owned. + DrmDisplayHostManager* const display_manager_; // Not owned. base::ObserverList<display::NativeDisplayObserver>::Unchecked observers_; diff --git a/chromium/ui/ozone/platform/drm/host/drm_overlay_candidates_host.h b/chromium/ui/ozone/platform/drm/host/drm_overlay_candidates_host.h index 9e2c8bce831..6e591602314 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_overlay_candidates_host.h +++ b/chromium/ui/ozone/platform/drm/host/drm_overlay_candidates_host.h @@ -40,8 +40,8 @@ class DrmOverlayCandidatesHost : public OverlayCandidatesOzone { void CheckOverlaySupport(OverlaySurfaceCandidateList* candidates) override; private: - DrmOverlayManager* overlay_manager_; // Not owned. - gfx::AcceleratedWidget widget_; + DrmOverlayManager* const overlay_manager_; // Not owned. + const gfx::AcceleratedWidget widget_; DISALLOW_COPY_AND_ASSIGN(DrmOverlayCandidatesHost); }; diff --git a/chromium/ui/ozone/platform/drm/host/drm_overlay_manager.h b/chromium/ui/ozone/platform/drm/host/drm_overlay_manager.h index 31578262453..ea39854d464 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_overlay_manager.h +++ b/chromium/ui/ozone/platform/drm/host/drm_overlay_manager.h @@ -70,8 +70,8 @@ class DrmOverlayManager : public OverlayManagerOzone { // Whether we have DRM atomic capabilities and we can support HW overlays. bool supports_overlays_ = false; - GpuThreadAdapter* proxy_; // Not owned. - DrmWindowHostManager* window_manager_; // Not owned. + GpuThreadAdapter* const proxy_; // Not owned. + DrmWindowHostManager* const window_manager_; // Not owned. // List of all OverlaySurfaceCandidate instances which have been requested // for validation and/or validated. diff --git a/chromium/ui/ozone/platform/drm/host/drm_window_host.h b/chromium/ui/ozone/platform/drm/host/drm_window_host.h index 073a1ade8e8..c935c7b9f2a 100644 --- a/chromium/ui/ozone/platform/drm/host/drm_window_host.h +++ b/chromium/ui/ozone/platform/drm/host/drm_window_host.h @@ -92,16 +92,16 @@ class DrmWindowHost : public PlatformWindow, private: void SendBoundsChange(); - PlatformWindowDelegate* delegate_; // Not owned. - GpuThreadAdapter* sender_; // Not owned. - EventFactoryEvdev* event_factory_; // Not owned. - DrmCursor* cursor_; // Not owned. - DrmWindowHostManager* window_manager_; // Not owned. - DrmDisplayHostManager* display_manager_; // Not owned. - DrmOverlayManager* overlay_manager_; // Not owned. + PlatformWindowDelegate* const delegate_; // Not owned. + GpuThreadAdapter* const sender_; // Not owned. + EventFactoryEvdev* const event_factory_; // Not owned. + DrmCursor* const cursor_; // Not owned. + DrmWindowHostManager* const window_manager_; // Not owned. + DrmDisplayHostManager* const display_manager_; // Not owned. + DrmOverlayManager* const overlay_manager_; // Not owned. gfx::Rect bounds_; - gfx::AcceleratedWidget widget_; + const gfx::AcceleratedWidget widget_; gfx::Rect cursor_confined_bounds_; diff --git a/chromium/ui/ozone/platform/drm/host/host_drm_device.h b/chromium/ui/ozone/platform/drm/host/host_drm_device.h index 5ec11b186b9..3372987d2bb 100644 --- a/chromium/ui/ozone/platform/drm/host/host_drm_device.h +++ b/chromium/ui/ozone/platform/drm/host/host_drm_device.h @@ -151,7 +151,7 @@ class HostDrmDevice : public base::RefCountedThreadSafe<HostDrmDevice>, DrmDisplayHostManager* display_manager_; // Not owned. DrmOverlayManager* overlay_manager_; // Not owned. - DrmCursor* cursor_; // Not owned. + DrmCursor* const cursor_; // Not owned. std::unique_ptr<HostCursorProxy> cursor_proxy_; diff --git a/chromium/ui/ozone/platform/scenic/BUILD.gn b/chromium/ui/ozone/platform/scenic/BUILD.gn index 4ba231915dd..3722c13aa84 100644 --- a/chromium/ui/ozone/platform/scenic/BUILD.gn +++ b/chromium/ui/ozone/platform/scenic/BUILD.gn @@ -4,6 +4,10 @@ assert(is_fuchsia) +import("//gpu/vulkan/features.gni") + +visibility = [ "//ui/ozone/*" ] + source_set("scenic") { sources = [ "client_native_pixmap_factory_scenic.cc", @@ -29,12 +33,12 @@ source_set("scenic") { deps = [ "//base", "//skia", - "//third_party/fuchsia-sdk:gfx", - "//third_party/fuchsia-sdk:images", - "//third_party/fuchsia-sdk:mem", - "//third_party/fuchsia-sdk:scenic", - "//third_party/fuchsia-sdk:viewsv1", - "//third_party/fuchsia-sdk:viewsv1token", + "//third_party/fuchsia-sdk/sdk:gfx", + "//third_party/fuchsia-sdk/sdk:images", + "//third_party/fuchsia-sdk/sdk:mem", + "//third_party/fuchsia-sdk/sdk:scenic", + "//third_party/fuchsia-sdk/sdk:viewsv1", + "//third_party/fuchsia-sdk/sdk:viewsv1token", "//ui/base", "//ui/display/manager", "//ui/events:dom_keycode_converter", @@ -44,4 +48,13 @@ source_set("scenic") { "//ui/ozone/common", "//ui/platform_window", ] + + if (enable_vulkan) { + sources += [ + "vulkan_implementation_scenic.cc", + "vulkan_implementation_scenic.h", + "vulkan_magma.h", + ] + defines += [ "VK_USE_PLATFORM_MAGMA_KHR" ] + } } diff --git a/chromium/ui/ozone/platform/scenic/DEPS b/chromium/ui/ozone/platform/scenic/DEPS index 41cd997bd46..defb45e8062 100644 --- a/chromium/ui/ozone/platform/scenic/DEPS +++ b/chromium/ui/ozone/platform/scenic/DEPS @@ -1,3 +1,4 @@ include_rules = [ "+third_party/skia/include", + "+ui/base/ime", ] diff --git a/chromium/ui/ozone/platform/scenic/ozone_platform_scenic.cc b/chromium/ui/ozone/platform/scenic/ozone_platform_scenic.cc index 6100a7f4ff4..fde7eea29c9 100644 --- a/chromium/ui/ozone/platform/scenic/ozone_platform_scenic.cc +++ b/chromium/ui/ozone/platform/scenic/ozone_platform_scenic.cc @@ -4,9 +4,14 @@ #include "ui/ozone/platform/scenic/ozone_platform_scenic.h" +#include <memory> +#include <utility> +#include <vector> + #include "base/logging.h" #include "base/macros.h" #include "base/memory/ptr_util.h" +#include "base/message_loop/message_loop_current.h" #include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h" #include "ui/display/manager/fake_display_delegate.h" #include "ui/events/ozone/layout/keyboard_layout_engine_manager.h" @@ -33,6 +38,7 @@ const OzonePlatform::PlatformProperties kScenicPlatformProperties( /*needs_view_owner_request=*/true, /*custom_frame_pref_default=*/false, /*use_system_title_bar=*/false, + /*requires_mojo=*/false, std::vector<gfx::BufferFormat>()); class ScenicPlatformEventSource : public ui::PlatformEventSource { @@ -45,13 +51,15 @@ class ScenicPlatformEventSource : public ui::PlatformEventSource { }; // OzonePlatform for Scenic. -class OzonePlatformScenic : public OzonePlatform { +class OzonePlatformScenic + : public OzonePlatform, + public base::MessageLoopCurrent::DestructionObserver { public: - OzonePlatformScenic() : surface_factory_(&window_manager_) {} + OzonePlatformScenic() + : window_manager_(std::make_unique<ScenicWindowManager>()), + surface_factory_(window_manager_.get()) {} ~OzonePlatformScenic() override = default; - ScenicWindowManager* window_manager() { return &window_manager_; } - // OzonePlatform implementation. ui::SurfaceFactoryOzone* GetSurfaceFactoryOzone() override { return &surface_factory_; @@ -86,7 +94,8 @@ class OzonePlatformScenic : public OzonePlatform { return nullptr; } return std::make_unique<ScenicWindow>( - &window_manager_, delegate, std::move(properties.view_owner_request)); + window_manager_.get(), delegate, + std::move(properties.view_owner_request)); } const PlatformProperties& GetPlatformProperties() override { @@ -100,7 +109,7 @@ class OzonePlatformScenic : public OzonePlatform { } std::unique_ptr<PlatformScreen> CreateScreen() override { - return window_manager_.CreateScreen(); + return window_manager_->CreateScreen(); } void InitializeUI(const InitParams& params) override { @@ -113,12 +122,20 @@ class OzonePlatformScenic : public OzonePlatform { input_controller_ = CreateStubInputController(); cursor_factory_ozone_ = std::make_unique<BitmapCursorFactoryOzone>(); gpu_platform_support_host_.reset(CreateStubGpuPlatformSupportHost()); + + base::MessageLoopCurrent::Get()->AddDestructionObserver(this); } void InitializeGPU(const InitParams& params) override {} private: - ScenicWindowManager window_manager_; + // Performs graceful cleanup tasks on main message loop teardown. + void Shutdown() { window_manager_.reset(); } + + // base::MessageLoopCurrent::DestructionObserver implementation. + void WillDestroyCurrentMessageLoop() override { Shutdown(); } + + std::unique_ptr<ScenicWindowManager> window_manager_; ScenicSurfaceFactory surface_factory_; std::unique_ptr<PlatformEventSource> platform_event_source_; diff --git a/chromium/ui/ozone/platform/scenic/scenic_screen.h b/chromium/ui/ozone/platform/scenic/scenic_screen.h index be61a051f4f..b55e4bafbc9 100644 --- a/chromium/ui/ozone/platform/scenic/scenic_screen.h +++ b/chromium/ui/ozone/platform/scenic/scenic_screen.h @@ -49,7 +49,7 @@ class ScenicScreen : public PlatformScreen { DisplayVector displays_; - base::ObserverList<display::DisplayObserver>::Unchecked observers_; + base::ObserverList<display::DisplayObserver> observers_; base::WeakPtrFactory<ScenicScreen> weak_factory_; diff --git a/chromium/ui/ozone/platform/scenic/scenic_session.cc b/chromium/ui/ozone/platform/scenic/scenic_session.cc index ff23a494ee3..c29bc82cc3e 100644 --- a/chromium/ui/ozone/platform/scenic/scenic_session.cc +++ b/chromium/ui/ozone/platform/scenic/scenic_session.cc @@ -103,6 +103,21 @@ ScenicSession::ResourceId ScenicSession::CreateImage( return image_id; } +ScenicSession::ResourceId ScenicSession::CreateImagePipe( + fidl::InterfaceRequest<fuchsia::images::ImagePipe> request) { + fuchsia::ui::gfx::ImagePipeArgs image_pipe; + image_pipe.image_pipe_request = std::move(request); + + fuchsia::ui::gfx::ResourceArgs resource; + resource.set_image_pipe(std::move(image_pipe)); + + ResourceId image_pipe_id = AllocateResourceId(); + EnqueueGfxCommand( + NewCreateResourceCommand(image_pipe_id, std::move(resource))); + + return image_pipe_id; +} + ScenicSession::ResourceId ScenicSession::ImportResource( fuchsia::ui::gfx::ImportSpec spec, zx::eventpair import_token) { @@ -261,12 +276,12 @@ void ScenicSession::Close() { session_listener_binding_.Unbind(); } -void ScenicSession::OnError(fidl::StringPtr error) { +void ScenicSession::OnScenicError(fidl::StringPtr error) { Close(); listener_->OnScenicError(error); } -void ScenicSession::OnEvent( +void ScenicSession::OnScenicEvent( fidl::VectorPtr<fuchsia::ui::scenic::Event> events) { listener_->OnScenicEvents(events.get()); } diff --git a/chromium/ui/ozone/platform/scenic/scenic_session.h b/chromium/ui/ozone/platform/scenic/scenic_session.h index 7e8b7ddfd23..ad81b47a523 100644 --- a/chromium/ui/ozone/platform/scenic/scenic_session.h +++ b/chromium/ui/ozone/platform/scenic/scenic_session.h @@ -59,6 +59,8 @@ class ScenicSession : public fuchsia::ui::scenic::SessionListener { ResourceId CreateImage(ResourceId memory_id, ResourceId memory_offset, fuchsia::images::ImageInfo info); + ResourceId CreateImagePipe( + fidl::InterfaceRequest<fuchsia::images::ImagePipe> request); ResourceId ImportResource(fuchsia::ui::gfx::ImportSpec spec, zx::eventpair import_token); ResourceId CreateEntityNode(); @@ -83,8 +85,9 @@ class ScenicSession : public fuchsia::ui::scenic::SessionListener { private: // fuchsia::ui::scenic::SessionListener interface. - void OnError(fidl::StringPtr error) override; - void OnEvent(fidl::VectorPtr<fuchsia::ui::scenic::Event> events) override; + void OnScenicError(fidl::StringPtr error) override; + void OnScenicEvent( + fidl::VectorPtr<fuchsia::ui::scenic::Event> events) override; // Allocates a new unique resource id. ResourceId AllocateResourceId(); diff --git a/chromium/ui/ozone/platform/scenic/scenic_surface_factory.cc b/chromium/ui/ozone/platform/scenic/scenic_surface_factory.cc index 636f3a81ba4..7b444c46bdd 100644 --- a/chromium/ui/ozone/platform/scenic/scenic_surface_factory.cc +++ b/chromium/ui/ozone/platform/scenic/scenic_surface_factory.cc @@ -15,6 +15,10 @@ #include "ui/ozone/platform/scenic/scenic_window_canvas.h" #include "ui/ozone/platform/scenic/scenic_window_manager.h" +#if BUILDFLAG(ENABLE_VULKAN) +#include "ui/ozone/platform/scenic/vulkan_implementation_scenic.h" +#endif + namespace ui { namespace { @@ -100,4 +104,11 @@ scoped_refptr<gfx::NativePixmap> ScenicSurfaceFactory::CreateNativePixmap( return new ScenicPixmap(widget, size, format); } +#if BUILDFLAG(ENABLE_VULKAN) +std::unique_ptr<gpu::VulkanImplementation> +ScenicSurfaceFactory::CreateVulkanImplementation() { + return std::make_unique<ui::VulkanImplementationScenic>(window_manager_); +} +#endif + } // namespace ui diff --git a/chromium/ui/ozone/platform/scenic/scenic_surface_factory.h b/chromium/ui/ozone/platform/scenic/scenic_surface_factory.h index 077b5131ba1..016b09f2378 100644 --- a/chromium/ui/ozone/platform/scenic/scenic_surface_factory.h +++ b/chromium/ui/ozone/platform/scenic/scenic_surface_factory.h @@ -9,6 +9,7 @@ #include <vector> #include "base/macros.h" +#include "gpu/vulkan/buildflags.h" #include "ui/ozone/public/gl_ozone.h" #include "ui/ozone/public/surface_factory_ozone.h" @@ -33,6 +34,10 @@ class ScenicSurfaceFactory : public SurfaceFactoryOzone { gfx::Size size, gfx::BufferFormat format, gfx::BufferUsage usage) override; +#if BUILDFLAG(ENABLE_VULKAN) + std::unique_ptr<gpu::VulkanImplementation> CreateVulkanImplementation() + override; +#endif private: ScenicWindowManager* const window_manager_; diff --git a/chromium/ui/ozone/platform/scenic/scenic_window.cc b/chromium/ui/ozone/platform/scenic/scenic_window.cc index 13b3d78d265..c8622cd46ad 100644 --- a/chromium/ui/ozone/platform/scenic/scenic_window.cc +++ b/chromium/ui/ozone/platform/scenic/scenic_window.cc @@ -6,6 +6,7 @@ #include <fuchsia/sys/cpp/fidl.h> #include <algorithm> +#include <memory> #include <string> #include <utility> #include <vector> @@ -13,7 +14,6 @@ #include "base/fuchsia/fuchsia_logging.h" #include "ui/events/event.h" #include "ui/events/event_constants.h" -#include "ui/events/keycodes/dom/keycode_converter.h" #include "ui/events/keycodes/keyboard_code_conversion.h" #include "ui/events/ozone/events_ozone.h" #include "ui/events/platform/platform_event_source.h" @@ -22,24 +22,6 @@ namespace ui { -namespace { - -const uint32_t kUsbHidKeyboardPage = 0x07; - -int KeyModifiersToFlags(int modifiers) { - int flags = 0; - if (modifiers & fuchsia::ui::input::kModifierShift) - flags |= EF_SHIFT_DOWN; - if (modifiers & fuchsia::ui::input::kModifierControl) - flags |= EF_CONTROL_DOWN; - if (modifiers & fuchsia::ui::input::kModifierAlt) - flags |= EF_ALT_DOWN; - // TODO(crbug.com/850697): Add AltGraph support. - return flags; -} - -} // namespace - ScenicWindow::ScenicWindow( ScenicWindowManager* window_manager, PlatformWindowDelegate* delegate, @@ -48,6 +30,7 @@ ScenicWindow::ScenicWindow( : manager_(window_manager), delegate_(delegate), window_id_(manager_->AddWindow(this)), + event_dispatcher_(this), view_listener_binding_(this), scenic_session_(manager_->GetScenic(), this), input_listener_binding_(this) { @@ -68,7 +51,7 @@ ScenicWindow::ScenicWindow( scenic_session_.AddNodeChild(parent_node_id_, node_id_); // Subscribe to metrics events from the parent node. These events are used to - // get |device_pixel_ratio_| for the screen. + // get the device pixel ratio for the screen. scenic_session_.SetEventMask(parent_node_id_, fuchsia::ui::gfx::kMetricsEventMask); @@ -82,6 +65,8 @@ ScenicWindow::ScenicWindow( fit::bind_member(this, &ScenicWindow::OnViewError)); // Setup input event listener. + // TODO(crbug.com/881591): Migrate off InputConnection and use IMEService + // for receiving keyboard input instead. fuchsia::sys::ServiceProviderPtr view_service_provider; view_->GetServiceProvider(view_service_provider.NewRequest()); view_service_provider->ConnectToService( @@ -225,6 +210,7 @@ void ScenicWindow::UpdateSize() { scenic_session_.SetNodeShape(shape_id_, rect_id); scenic_session_.SetNodeTranslation(shape_id_, translation); scenic_session_.ReleaseResource(rect_id); + scenic_session_.Present(); delegate_->OnBoundsChanged(size_rect); } @@ -273,32 +259,11 @@ void ScenicWindow::OnEvent(fuchsia::ui::input::InputEvent event, OnEventCallback callback) { bool result = false; - switch (event.Which()) { - case fuchsia::ui::input::InputEvent::Tag::kPointer: - switch (event.pointer().type) { - case fuchsia::ui::input::PointerEventType::MOUSE: - result = OnMouseEvent(event.pointer()); - break; - case fuchsia::ui::input::PointerEventType::TOUCH: - result = OnTouchEvent(event.pointer()); - break; - case fuchsia::ui::input::PointerEventType::STYLUS: - case fuchsia::ui::input::PointerEventType::INVERTED_STYLUS: - NOTIMPLEMENTED() << "Stylus input is not yet supported."; - break; - } - break; - - case fuchsia::ui::input::InputEvent::Tag::kKeyboard: - result = OnKeyboardEvent(event.keyboard()); - break; - - case fuchsia::ui::input::InputEvent::Tag::kFocus: - result = OnFocusEvent(event.focus()); - break; - - case fuchsia::ui::input::InputEvent::Tag::Invalid: - break; + if (event.is_focus()) { + delegate_->OnActivationChanged(event.focus().focused); + result = true; + } else { + result = event_dispatcher_.ProcessEvent(event); } callback(result); @@ -309,131 +274,14 @@ void ScenicWindow::OnViewError() { delegate_->OnClosed(); } -bool ScenicWindow::OnMouseEvent(const fuchsia::ui::input::PointerEvent& event) { - int flags = 0; - if (event.buttons & 1) - flags |= EF_LEFT_MOUSE_BUTTON; - if (event.buttons & 2) - flags |= EF_RIGHT_MOUSE_BUTTON; - if (event.buttons & 4) - flags |= EF_MIDDLE_MOUSE_BUTTON; - - EventType event_type; - - switch (event.phase) { - case fuchsia::ui::input::PointerEventPhase::DOWN: - event_type = ET_MOUSE_PRESSED; - break; - case fuchsia::ui::input::PointerEventPhase::MOVE: - event_type = flags ? ET_MOUSE_DRAGGED : ET_MOUSE_MOVED; - break; - case fuchsia::ui::input::PointerEventPhase::UP: - event_type = ET_MOUSE_RELEASED; - break; - - // Following phases are not expected for mouse events. - case fuchsia::ui::input::PointerEventPhase::HOVER: - case fuchsia::ui::input::PointerEventPhase::CANCEL: - case fuchsia::ui::input::PointerEventPhase::ADD: - case fuchsia::ui::input::PointerEventPhase::REMOVE: - NOTREACHED() << "Unexpected mouse phase " - << fidl::ToUnderlying(event.phase); - return false; +void ScenicWindow::DispatchEvent(ui::Event* event) { + if (event->IsLocatedEvent()) { + ui::LocatedEvent* located_event = event->AsLocatedEvent(); + gfx::PointF location = located_event->location_f(); + location.Scale(device_pixel_ratio_); + located_event->set_location_f(location); } - - gfx::Point location = - gfx::Point(event.x * device_pixel_ratio_, event.y * device_pixel_ratio_); - ui::MouseEvent mouse_event(event_type, location, location, - base::TimeTicks::FromZxTime(event.event_time), - flags, 0); - delegate_->DispatchEvent(&mouse_event); - return true; -} - -bool ScenicWindow::OnTouchEvent(const fuchsia::ui::input::PointerEvent& event) { - EventType event_type; - - switch (event.phase) { - case fuchsia::ui::input::PointerEventPhase::DOWN: - event_type = ET_TOUCH_PRESSED; - break; - case fuchsia::ui::input::PointerEventPhase::MOVE: - event_type = ET_TOUCH_MOVED; - break; - case fuchsia::ui::input::PointerEventPhase::CANCEL: - event_type = ET_TOUCH_CANCELLED; - break; - case fuchsia::ui::input::PointerEventPhase::UP: - event_type = ET_TOUCH_RELEASED; - break; - case fuchsia::ui::input::PointerEventPhase::ADD: - case fuchsia::ui::input::PointerEventPhase::REMOVE: - case fuchsia::ui::input::PointerEventPhase::HOVER: - return false; - } - - // TODO(crbug.com/876933): Add more detailed fields such as - // force/orientation/tilt once they are added to PointerEvent. - ui::PointerDetails pointer_details(ui::EventPointerType::POINTER_TYPE_TOUCH, - event.pointer_id); - - gfx::Point location = - gfx::Point(event.x * device_pixel_ratio_, event.y * device_pixel_ratio_); - ui::TouchEvent touch_event(event_type, location, - base::TimeTicks::FromZxTime(event.event_time), - pointer_details); - - delegate_->DispatchEvent(&touch_event); - return true; -} - -bool ScenicWindow::OnKeyboardEvent( - const fuchsia::ui::input::KeyboardEvent& event) { - EventType event_type; - - switch (event.phase) { - case fuchsia::ui::input::KeyboardEventPhase::PRESSED: - case fuchsia::ui::input::KeyboardEventPhase::REPEAT: - event_type = ET_KEY_PRESSED; - break; - - case fuchsia::ui::input::KeyboardEventPhase::RELEASED: - event_type = ET_KEY_RELEASED; - break; - - case fuchsia::ui::input::KeyboardEventPhase::CANCELLED: - NOTIMPLEMENTED() << "Key event cancellation is not supported."; - event_type = ET_KEY_RELEASED; - break; - } - - // Currently KeyboardEvent doesn't specify HID Usage page. |hid_usage| - // field always contains values from the Keyboard page. See - // https://fuchsia.atlassian.net/browse/SCN-762 . - DomCode dom_code = KeycodeConverter::UsbKeycodeToDomCode( - (kUsbHidKeyboardPage << 16) | event.hid_usage); - DomKey dom_key; - KeyboardCode key_code; - if (!DomCodeToUsLayoutDomKey(dom_code, KeyModifiersToFlags(event.modifiers), - &dom_key, &key_code)) { - LOG(ERROR) << "DomCodeToUsLayoutDomKey() failed for usb_key: " - << event.hid_usage; - key_code = VKEY_UNKNOWN; - } - - if (event.code_point) - dom_key = DomKey::FromCharacter(event.code_point); - - KeyEvent key_event(event_type, key_code, dom_code, - KeyModifiersToFlags(event.modifiers), dom_key, - base::TimeTicks::FromZxTime(event.event_time)); - delegate_->DispatchEvent(&key_event); - return true; -} - -bool ScenicWindow::OnFocusEvent(const fuchsia::ui::input::FocusEvent& event) { - delegate_->OnActivationChanged(event.focused); - return true; + delegate_->DispatchEvent(event); } } // namespace ui diff --git a/chromium/ui/ozone/platform/scenic/scenic_window.h b/chromium/ui/ozone/platform/scenic/scenic_window.h index 912fd04fab5..9dba22b7e9b 100644 --- a/chromium/ui/ozone/platform/scenic/scenic_window.h +++ b/chromium/ui/ozone/platform/scenic/scenic_window.h @@ -11,6 +11,8 @@ #include <vector> #include "base/macros.h" +#include "ui/events/fuchsia/input_event_dispatcher.h" +#include "ui/events/fuchsia/input_event_dispatcher_delegate.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/geometry/size_f.h" @@ -27,7 +29,8 @@ class PlatformWindowDelegate; class OZONE_EXPORT ScenicWindow : public PlatformWindow, public ScenicSessionListener, public fuchsia::ui::viewsv1::ViewListener, - public fuchsia::ui::input::InputListener { + public fuchsia::ui::input::InputListener, + public InputEventDispatcherDelegate { public: // Both |window_manager| and |delegate| must outlive the ScenicWindow. // |view_owner_request| is passed to the view managed when creating the @@ -41,10 +44,8 @@ class OZONE_EXPORT ScenicWindow : public PlatformWindow, ScenicSession* scenic_session() { return &scenic_session_; } ScenicSession::ResourceId node_id() const { return node_id_; } - float device_pixel_ratio() const { return device_pixel_ratio_; } - // Overrides texture of the window. This is used by ScenicWindowCanvas. - // TODO(spang): Deprecate software rendering on fuchsia. + // Sets texture of the window to a scenic resource. void SetTexture(ScenicSession::ResourceId texture); // PlatformWindow implementation. @@ -84,22 +85,22 @@ class OZONE_EXPORT ScenicWindow : public PlatformWindow, void OnScenicEvents( const std::vector<fuchsia::ui::scenic::Event>& events) override; + // InputEventDispatcher::Delegate interface. + void DispatchEvent(ui::Event* event) override; + // Error handler for |view_|. This error normally indicates the View was // destroyed (e.g. dropping ViewOwner). void OnViewError(); void UpdateSize(); - // Handlers for Fuchsia input event specializations. - bool OnMouseEvent(const fuchsia::ui::input::PointerEvent& event); - bool OnKeyboardEvent(const fuchsia::ui::input::KeyboardEvent& event); - bool OnTouchEvent(const fuchsia::ui::input::PointerEvent& event); - bool OnFocusEvent(const fuchsia::ui::input::FocusEvent& event); - ScenicWindowManager* const manager_; PlatformWindowDelegate* const delegate_; gfx::AcceleratedWidget const window_id_; + // Dispatches Scenic input events as Chrome ui::Events. + InputEventDispatcher event_dispatcher_; + // Underlying View in the view_manager. fuchsia::ui::viewsv1::ViewPtr view_; fidl::Binding<fuchsia::ui::viewsv1::ViewListener> view_listener_binding_; @@ -119,16 +120,16 @@ class OZONE_EXPORT ScenicWindow : public PlatformWindow, ScenicSession::ResourceId shape_id_; ScenicSession::ResourceId material_id_; + // The ratio used for translating device-independent coordinates to absolute + // pixel coordinates. + float device_pixel_ratio_; + // Current view size in DIPs. gfx::SizeF size_dips_; // Current view size in device pixels. gfx::Size size_pixels_; - // Device pixel ratio for the current device. Initialized in - // OnPropertiesChanged(). - float device_pixel_ratio_ = 0.0; - // InputConnection and InputListener binding used to receive input events from // the view. fuchsia::ui::input::InputConnectionPtr input_connection_; diff --git a/chromium/ui/ozone/platform/scenic/scenic_window_manager.cc b/chromium/ui/ozone/platform/scenic/scenic_window_manager.cc index 9db874b8b28..cf900feccd1 100644 --- a/chromium/ui/ozone/platform/scenic/scenic_window_manager.cc +++ b/chromium/ui/ozone/platform/scenic/scenic_window_manager.cc @@ -5,6 +5,7 @@ #include "ui/ozone/platform/scenic/scenic_window_manager.h" #include "base/fuchsia/component_context.h" +#include "ui/ozone/platform/scenic/ozone_platform_scenic.h" namespace ui { @@ -22,8 +23,9 @@ fuchsia::ui::viewsv1::ViewManager* ScenicWindowManager::GetViewManager() { if (!view_manager_) { view_manager_ = base::fuchsia::ComponentContext::GetDefault() ->ConnectToService<fuchsia::ui::viewsv1::ViewManager>(); - view_manager_.set_error_handler( - [this]() { LOG(FATAL) << "ViewManager connection failed."; }); + view_manager_.set_error_handler([this]() { + LOG(ERROR) << "The ViewManager channel was unexpectedly terminated."; + }); } return view_manager_.get(); @@ -32,8 +34,9 @@ fuchsia::ui::viewsv1::ViewManager* ScenicWindowManager::GetViewManager() { fuchsia::ui::scenic::Scenic* ScenicWindowManager::GetScenic() { if (!scenic_) { GetViewManager()->GetScenic(scenic_.NewRequest()); - scenic_.set_error_handler( - [this]() { LOG(FATAL) << "Scenic connection failed."; }); + scenic_.set_error_handler([this]() { + LOG(ERROR) << "The Scenic channel was unexpectedly terminated."; + }); } return scenic_.get(); } diff --git a/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc b/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc new file mode 100644 index 00000000000..a8af3f8b847 --- /dev/null +++ b/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.cc @@ -0,0 +1,132 @@ +// 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/ozone/platform/scenic/vulkan_implementation_scenic.h" + +#include <lib/zx/channel.h> + +#include "base/files/file_path.h" +#include "base/fuchsia/fuchsia_logging.h" +#include "base/native_library.h" +#include "gpu/vulkan/vulkan_function_pointers.h" +#include "gpu/vulkan/vulkan_instance.h" +#include "gpu/vulkan/vulkan_surface.h" +#include "ui/gfx/gpu_fence.h" +#include "ui/ozone/platform/scenic/scenic_window.h" +#include "ui/ozone/platform/scenic/scenic_window_manager.h" +#include "ui/ozone/platform/scenic/vulkan_magma.h" + +namespace ui { + +VulkanImplementationScenic::VulkanImplementationScenic( + ScenicWindowManager* scenic_window_manager) + : scenic_window_manager_(scenic_window_manager) {} + +VulkanImplementationScenic::~VulkanImplementationScenic() = default; + +bool VulkanImplementationScenic::InitializeVulkanInstance() { + base::NativeLibraryLoadError error; + base::NativeLibrary handle = + base::LoadNativeLibrary(base::FilePath("libvulkan.so"), &error); + if (!handle) { + LOG(ERROR) << "Failed to load vulkan: " << error.ToString(); + return false; + } + + gpu::VulkanFunctionPointers* vulkan_function_pointers = + gpu::GetVulkanFunctionPointers(); + vulkan_function_pointers->vulkan_loader_library_ = handle; + std::vector<const char*> required_extensions = { + VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_MAGMA_SURFACE_EXTENSION_NAME, + }; + std::vector<const char*> required_layers = { + "VK_LAYER_GOOGLE_image_pipe_swapchain", + }; + if (!vulkan_instance_.Initialize(required_extensions, required_layers)) { + vulkan_instance_.Destroy(); + return false; + } + + vkCreateMagmaSurfaceKHR_ = vkGetInstanceProcAddr( + vulkan_instance_.vk_instance(), "vkCreateMagmaSurfaceKHR"); + if (!vkCreateMagmaSurfaceKHR_) { + vulkan_instance_.Destroy(); + return false; + } + + vkGetPhysicalDeviceMagmaPresentationSupportKHR_ = + vkGetInstanceProcAddr(vulkan_instance_.vk_instance(), + "vkGetPhysicalDeviceMagmaPresentationSupportKHR"); + if (!vkGetPhysicalDeviceMagmaPresentationSupportKHR_) { + vulkan_instance_.Destroy(); + return false; + } + + return true; +} + +VkInstance VulkanImplementationScenic::GetVulkanInstance() { + return vulkan_instance_.vk_instance(); +} + +std::unique_ptr<gpu::VulkanSurface> +VulkanImplementationScenic::CreateViewSurface(gfx::AcceleratedWidget window) { + ScenicWindow* scenic_window = scenic_window_manager_->GetWindow(window); + if (!scenic_window) + return nullptr; + ScenicSession* scenic_session = scenic_window->scenic_session(); + fuchsia::images::ImagePipePtr image_pipe; + ScenicSession::ResourceId image_pipe_id = + scenic_session->CreateImagePipe(image_pipe.NewRequest()); + scenic_window->SetTexture(image_pipe_id); + scenic_session->ReleaseResource(image_pipe_id); + scenic_session->Present(); + + VkSurfaceKHR surface; + VkMagmaSurfaceCreateInfoKHR surface_create_info = {}; + surface_create_info.sType = VK_STRUCTURE_TYPE_MAGMA_SURFACE_CREATE_INFO_KHR; + surface_create_info.imagePipeHandle = + image_pipe.Unbind().TakeChannel().release(); + + VkResult result = + reinterpret_cast<PFN_vkCreateMagmaSurfaceKHR>(vkCreateMagmaSurfaceKHR_)( + GetVulkanInstance(), &surface_create_info, nullptr, &surface); + if (result != VK_SUCCESS) { + // This shouldn't fail, and we don't know whether imagePipeHandle was closed + // if it does. + LOG(FATAL) << "vkCreateMagmaSurfaceKHR failed: " << result; + } + + return std::make_unique<gpu::VulkanSurface>(GetVulkanInstance(), surface); +} + +bool VulkanImplementationScenic::GetPhysicalDevicePresentationSupport( + VkPhysicalDevice physical_device, + const std::vector<VkQueueFamilyProperties>& queue_family_properties, + uint32_t queue_family_index) { + // TODO(spang): vkGetPhysicalDeviceMagmaPresentationSupportKHR returns false + // here. Use it once it is fixed. + NOTIMPLEMENTED(); + return true; +} + +std::vector<const char*> +VulkanImplementationScenic::GetRequiredDeviceExtensions() { + return {VK_KHR_SWAPCHAIN_EXTENSION_NAME}; +} + +VkFence VulkanImplementationScenic::CreateVkFenceForGpuFence( + VkDevice vk_device) { + NOTIMPLEMENTED(); + return VK_NULL_HANDLE; +} + +std::unique_ptr<gfx::GpuFence> +VulkanImplementationScenic::ExportVkFenceToGpuFence(VkDevice vk_device, + VkFence vk_fence) { + NOTIMPLEMENTED(); + return nullptr; +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.h b/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.h new file mode 100644 index 00000000000..ecd61cd04f6 --- /dev/null +++ b/chromium/ui/ozone/platform/scenic/vulkan_implementation_scenic.h @@ -0,0 +1,49 @@ +// 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_OZONE_PLATFORM_SCENIC_VULKAN_IMPLEMENTATION_SCENIC_H_ +#define UI_OZONE_PLATFORM_SCENIC_VULKAN_IMPLEMENTATION_SCENIC_H_ + +#include <memory> + +#include "gpu/vulkan/vulkan_implementation.h" +#include "gpu/vulkan/vulkan_instance.h" + +namespace ui { + +class ScenicWindowManager; + +class VulkanImplementationScenic : public gpu::VulkanImplementation { + public: + VulkanImplementationScenic(ScenicWindowManager* scenic_window_manager); + ~VulkanImplementationScenic() override; + + // VulkanImplementation: + bool InitializeVulkanInstance() override; + VkInstance GetVulkanInstance() override; + std::unique_ptr<gpu::VulkanSurface> CreateViewSurface( + gfx::AcceleratedWidget window) override; + bool GetPhysicalDevicePresentationSupport( + VkPhysicalDevice device, + const std::vector<VkQueueFamilyProperties>& queue_family_properties, + uint32_t queue_family_index) override; + std::vector<const char*> GetRequiredDeviceExtensions() override; + VkFence CreateVkFenceForGpuFence(VkDevice vk_device) override; + std::unique_ptr<gfx::GpuFence> ExportVkFenceToGpuFence( + VkDevice vk_device, + VkFence vk_fence) override; + + private: + ScenicWindowManager* const scenic_window_manager_; + gpu::VulkanInstance vulkan_instance_; + + PFN_vkVoidFunction vkCreateMagmaSurfaceKHR_ = nullptr; + PFN_vkVoidFunction vkGetPhysicalDeviceMagmaPresentationSupportKHR_ = nullptr; + + DISALLOW_COPY_AND_ASSIGN(VulkanImplementationScenic); +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_SCENIC_VULKAN_IMPLEMENTATION_SCENIC_H_ diff --git a/chromium/ui/ozone/platform/scenic/vulkan_magma.h b/chromium/ui/ozone/platform/scenic/vulkan_magma.h new file mode 100644 index 00000000000..df222e83efc --- /dev/null +++ b/chromium/ui/ozone/platform/scenic/vulkan_magma.h @@ -0,0 +1,159 @@ +// 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_OZONE_PLATFORM_SCENIC_VULKAN_MAGMA_H_ +#define UI_OZONE_PLATFORM_SCENIC_VULKAN_MAGMA_H_ + +// These definitions are from +// https://fuchsia.googlesource.com/third_party/vulkan_loader_and_validation_layers/+/master/include/vulkan/vulkan.h +// TODO(spang): Remove these once the definitions go upstream. + +#include <vulkan/vulkan.h> + +#if !defined(VK_KHR_external_memory_fuchsia) +#define VK_KHR_external_memory_fuchsia 1 +#define VK_KHR_EXTERNAL_MEMORY_FUCHSIA_SPEC_VERSION 1 +#define VK_KHR_EXTERNAL_MEMORY_FUCHSIA_EXTENSION_NAME \ + "VK_KHR_external_memory_fuchsia" + +typedef struct VkImportMemoryFuchsiaHandleInfoKHR { + VkStructureType sType; + const void* pNext; + VkExternalMemoryHandleTypeFlagBitsKHR handleType; + uint32_t handle; +} VkImportMemoryFuchsiaHandleInfoKHR; + +typedef struct VkMemoryFuchsiaHandlePropertiesKHR { + VkStructureType sType; + void* pNext; + uint32_t memoryTypeBits; +} VkMemoryFuchsiaHandlePropertiesKHR; + +typedef struct VkMemoryGetFuchsiaHandleInfoKHR { + VkStructureType sType; + const void* pNext; + VkDeviceMemory memory; + VkExternalMemoryHandleTypeFlagBitsKHR handleType; +} VkMemoryGetFuchsiaHandleInfoKHR; + +typedef VkResult(VKAPI_PTR* PFN_vkGetMemoryFuchsiaHandleKHR)( + VkDevice device, + const VkMemoryGetFuchsiaHandleInfoKHR* pGetFuchsiaHandleInfo, + uint32_t* pFuchsiaHandle); +typedef VkResult(VKAPI_PTR* PFN_vkGetMemoryFuchsiaHandlePropertiesKHR)( + VkDevice device, + VkExternalMemoryHandleTypeFlagBitsKHR handleType, + uint32_t fuchsiaHandle, + VkMemoryFuchsiaHandlePropertiesKHR* pMemoryFuchsiaHandleProperties); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryFuchsiaHandleKHR( + VkDevice device, + const VkMemoryGetFuchsiaHandleInfoKHR* pGetFuchsiaHandleInfo, + uint32_t* pFuchsiaHandle); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetMemoryFuchsiaHandlePropertiesKHR( + VkDevice device, + VkExternalMemoryHandleTypeFlagBitsKHR handleType, + uint32_t fuchsiaHandle, + VkMemoryFuchsiaHandlePropertiesKHR* pMemoryFuchsiaHandleProperties); +#endif + +#endif // !defined(VK_KHR_external_memory_fuchsia) + +#if !defined(VK_KHR_external_semaphore_fuchsia) +#define VK_KHR_external_semaphore_fuchsia 1 +#define VK_KHR_EXTERNAL_SEMAPHORE_FUCHSIA_SPEC_VERSION 1 +#define VK_KHR_EXTERNAL_SEMAPHORE_FUCHSIA_EXTENSION_NAME \ + "VK_KHR_external_semaphore_fuchsia" + +typedef struct VkImportSemaphoreFuchsiaHandleInfoKHR { + VkStructureType sType; + const void* pNext; + VkSemaphore semaphore; + VkSemaphoreImportFlagsKHR flags; + VkExternalSemaphoreHandleTypeFlagBitsKHR handleType; + uint32_t handle; +} VkImportSemaphoreFuchsiaHandleInfoKHR; + +typedef struct VkSemaphoreGetFuchsiaHandleInfoKHR { + VkStructureType sType; + const void* pNext; + VkSemaphore semaphore; + VkExternalSemaphoreHandleTypeFlagBitsKHR handleType; +} VkSemaphoreGetFuchsiaHandleInfoKHR; + +typedef VkResult(VKAPI_PTR* PFN_vkImportSemaphoreFuchsiaHandleKHR)( + VkDevice device, + const VkImportSemaphoreFuchsiaHandleInfoKHR* + pImportSemaphoreFuchsiaHandleInfo); +typedef VkResult(VKAPI_PTR* PFN_vkGetSemaphoreFuchsiaHandleKHR)( + VkDevice device, + const VkSemaphoreGetFuchsiaHandleInfoKHR* pGetFuchsiaHandleInfo, + uint32_t* pFuchsiaHandle); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL +vkImportSemaphoreFuchsiaHandleKHR(VkDevice device, + const VkImportSemaphoreFuchsiaHandleInfoKHR* + pImportSemaphoreFuchsiaHandleInfo); + +VKAPI_ATTR VkResult VKAPI_CALL vkGetSemaphoreFuchsiaHandleKHR( + VkDevice device, + const VkSemaphoreGetFuchsiaHandleInfoKHR* pGetFuchsiaHandleInfo, + uint32_t* pFuchsiaHandle); +#endif + +#endif // !defined(VK_KHR_external_semaphore_fuchsia) + +#if defined(VK_USE_PLATFORM_MAGMA_KHR) && !defined(VK_KHR_magma_surface) +#define VK_KHR_magma_surface 1 +#define VK_KHR_MAGMA_SURFACE_SPEC_VERSION 1 +#define VK_KHR_MAGMA_SURFACE_EXTENSION_NAME "VK_KHR_magma_surface" + +typedef VkFlags VkMagmaSurfaceCreateFlagsKHR; + +typedef struct VkMagmaSurfaceCreateInfoKHR { + VkStructureType sType; + const void* pNext; + uint32_t imagePipeHandle; + uint32_t width; + uint32_t height; +} VkMagmaSurfaceCreateInfoKHR; + +typedef VkResult(VKAPI_PTR* PFN_vkCreateMagmaSurfaceKHR)( + VkInstance instance, + const VkMagmaSurfaceCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); +typedef VkBool32(VKAPI_PTR* PFN_vkGetPhysicalDeviceMagmaPresentationSupportKHR)( + VkPhysicalDevice physicalDevice, + uint32_t queueFamilyIndex); + +#ifndef VK_NO_PROTOTYPES +VKAPI_ATTR VkResult VKAPI_CALL +vkCreateMagmaSurfaceKHR(VkInstance instance, + const VkMagmaSurfaceCreateInfoKHR* pCreateInfo, + const VkAllocationCallbacks* pAllocator, + VkSurfaceKHR* pSurface); + +VKAPI_ATTR VkBool32 VKAPI_CALL +vkGetPhysicalDeviceMagmaPresentationSupportKHR(VkPhysicalDevice physicalDevice, + uint32_t queueFamilyIndex); +#endif +#endif // defined(VK_USE_PLATFORM_MAGMA_KHR) && !defined(VK_KHR_magma_surface) + +#if !defined(VK_GOOGLE_image_usage_scanout) + +#define VK_GOOGLE_image_usage_scanout 1 +#define VK_GOOGLE_IMAGE_USAGE_SCANOUT_SPEC_VERSION 1 +#define VK_GOOGLE_IMAGE_USAGE_SCANOUT_EXTENSION_NAME \ + "VK_GOOGLE_image_usage_scanout" + +#define VK_STRUCTURE_TYPE_MAGMA_SURFACE_CREATE_INFO_KHR \ + (static_cast<VkStructureType>(1001002000)) + +#endif // !defined(VK_GOOGLE_image_usage_scanout) + +#endif // UI_OZONE_PLATFORM_MAGMA_VULKAN_MAGMA_H_ diff --git a/chromium/ui/ozone/platform/wayland/BUILD.gn b/chromium/ui/ozone/platform/wayland/BUILD.gn index 7a846766f84..9740ad91b26 100644 --- a/chromium/ui/ozone/platform/wayland/BUILD.gn +++ b/chromium/ui/ozone/platform/wayland/BUILD.gn @@ -37,16 +37,22 @@ source_set("wayland") { "wayland_data_offer.h", "wayland_data_source.cc", "wayland_data_source.h", + "wayland_input_method_context.cc", + "wayland_input_method_context.h", + "wayland_input_method_context_factory.cc", + "wayland_input_method_context_factory.h", "wayland_keyboard.cc", "wayland_keyboard.h", - "wayland_native_display_delegate.cc", - "wayland_native_display_delegate.h", "wayland_object.cc", "wayland_object.h", "wayland_output.cc", "wayland_output.h", + "wayland_output_manager.cc", + "wayland_output_manager.h", "wayland_pointer.cc", "wayland_pointer.h", + "wayland_screen.cc", + "wayland_screen.h", "wayland_surface_factory.cc", "wayland_surface_factory.h", "wayland_touch.cc", @@ -66,6 +72,9 @@ source_set("wayland") { "xdg_surface_wrapper_v5.h", "xdg_surface_wrapper_v6.cc", "xdg_surface_wrapper_v6.h", + "zwp_text_input_wrapper.h", + "zwp_text_input_wrapper_v1.cc", + "zwp_text_input_wrapper_v1.h", ] import("//ui/base/ui_features.gni") @@ -85,12 +94,16 @@ source_set("wayland") { "//third_party/minigbm", "//third_party/wayland:wayland_client", "//third_party/wayland-protocols:linux_dmabuf_protocol", + "//third_party/wayland-protocols:presentation_time_protocol", + "//third_party/wayland-protocols:text_input_protocol", "//third_party/wayland-protocols:xdg_shell_protocol", "//ui/base", "//ui/base:ui_features", + "//ui/base/ime/linux", "//ui/display/manager", "//ui/events", "//ui/events:dom_keycode_converter", + "//ui/events/keycodes:xkb", "//ui/events/ozone:events_ozone", "//ui/events/ozone:events_ozone_evdev", "//ui/events/ozone:events_ozone_layout", @@ -103,6 +116,7 @@ source_set("wayland") { "//ui/ozone/common/linux", "//ui/ozone/public/interfaces/wayland:wayland_interfaces", "//ui/platform_window", + "//ui/platform_window/platform_window_handler", ] defines = [ "OZONE_IMPLEMENTATION" ] @@ -142,8 +156,10 @@ source_set("wayland_unittests") { "fake_server.h", "wayland_connection_unittest.cc", "wayland_data_device_unittest.cc", + "wayland_input_method_context_unittest.cc", "wayland_keyboard_unittest.cc", "wayland_pointer_unittest.cc", + "wayland_screen_unittest.cc", "wayland_surface_factory_unittest.cc", "wayland_test.cc", "wayland_test.h", @@ -157,7 +173,9 @@ source_set("wayland_unittests") { "//testing/gtest", "//third_party/wayland:wayland_server", "//third_party/wayland-protocols:xdg_shell_protocol", + "//ui/base", "//ui/base:ui_features", + "//ui/base/ime/linux", "//ui/events/ozone:events_ozone_layout", "//ui/ozone:platform", "//ui/ozone:test_support", diff --git a/chromium/ui/ozone/platform/wayland/DEPS b/chromium/ui/ozone/platform/wayland/DEPS index fde6dba3616..56a383a0459 100644 --- a/chromium/ui/ozone/platform/wayland/DEPS +++ b/chromium/ui/ozone/platform/wayland/DEPS @@ -1,5 +1,12 @@ include_rules = [ + "+ui/base/hit_test.h", # UI hit test doesn't bring in all of ui/base. "+ui/base/ui_features.h", # UI features doesn't bring in all of ui/base. + "+ui/base/ui_base_features.h", + "+ui/base/ime/composition_text.h", + "+ui/base/ime/linux/linux_input_method_context.h", + "+ui/base/ime/linux/linux_input_method_context_factory.h", "+mojo/public", + "+ui/base/dragdrop/drag_drop_types.h", + "+ui/base/dragdrop/os_exchange_data.h", + "+ui/base/dragdrop/os_exchange_data_provider_aura.h", ] - diff --git a/chromium/ui/ozone/platform/wayland/client_native_pixmap_factory_wayland.cc b/chromium/ui/ozone/platform/wayland/client_native_pixmap_factory_wayland.cc index d64b36694ef..110dc9f3ab2 100644 --- a/chromium/ui/ozone/platform/wayland/client_native_pixmap_factory_wayland.cc +++ b/chromium/ui/ozone/platform/wayland/client_native_pixmap_factory_wayland.cc @@ -25,12 +25,6 @@ class ClientNativePixmapFactoryWayland : public gfx::ClientNativePixmapFactory { gfx::BufferUsage usage) const override { OzonePlatform::PlatformProperties properties = OzonePlatform::GetInstance()->GetPlatformProperties(); - if (properties.supported_buffer_formats.empty()) { - // If the compositor did not announce supported buffer formats, do our - // best and assume those are supported. - return dmabuf_factory_->IsConfigurationSupported(format, usage); - } - for (auto buffer_format : properties.supported_buffer_formats) { if (buffer_format == format) return dmabuf_factory_->IsConfigurationSupported(format, usage); diff --git a/chromium/ui/ozone/platform/wayland/fake_server.cc b/chromium/ui/ozone/platform/wayland/fake_server.cc index 6a63f3a7cf6..62e7f8c16c1 100644 --- a/chromium/ui/ozone/platform/wayland/fake_server.cc +++ b/chromium/ui/ozone/platform/wayland/fake_server.cc @@ -3,7 +3,9 @@ // found in the LICENSE file. #include "ui/ozone/platform/wayland/fake_server.h" + #include <sys/socket.h> +#include <text-input-unstable-v1-server-protocol.h> #include <wayland-server.h> #include <xdg-shell-unstable-v5-server-protocol.h> #include <xdg-shell-unstable-v6-server-protocol.h> @@ -21,11 +23,12 @@ namespace wl { namespace { -const uint32_t kCompositorVersion = 4; -const uint32_t kOutputVersion = 2; -const uint32_t kDataDeviceManagerVersion = 3; -const uint32_t kSeatVersion = 4; -const uint32_t kXdgShellVersion = 1; +constexpr uint32_t kCompositorVersion = 4; +constexpr uint32_t kOutputVersion = 2; +constexpr uint32_t kDataDeviceManagerVersion = 3; +constexpr uint32_t kSeatVersion = 4; +constexpr uint32_t kTextInputManagerVersion = 1; +constexpr uint32_t kXdgShellVersion = 1; bool ResourceHasImplementation(wl_resource* resource, const wl_interface* interface, @@ -290,6 +293,15 @@ const struct zxdg_positioner_v6_interface zxdg_positioner_v6_impl = { // wl_data_device +void DataDeviceStartDrag(wl_client* client, + wl_resource* resource, + wl_resource* source, + wl_resource* origin, + wl_resource* icon, + uint32_t serial) { + NOTIMPLEMENTED(); +} + void DataDeviceSetSelection(wl_client* client, wl_resource* resource, wl_resource* data_source, @@ -304,8 +316,7 @@ void DataDeviceRelease(wl_client* client, wl_resource* resource) { } const struct wl_data_device_interface data_device_impl = { - nullptr /*data_device_start_drag*/, &DataDeviceSetSelection, - &DataDeviceRelease}; + &DataDeviceStartDrag, &DataDeviceSetSelection, &DataDeviceRelease}; // wl_data_device_manager @@ -347,6 +358,13 @@ const struct wl_data_device_manager_interface data_device_manager_impl = { // wl_data_offer +void DataOfferAccept(wl_client* client, + wl_resource* resource, + uint32_t serial, + const char* mime_type) { + NOTIMPLEMENTED(); +} + void DataOfferReceive(wl_client* client, wl_resource* resource, const char* mime_type, @@ -359,10 +377,20 @@ void DataOfferDestroy(wl_client* client, wl_resource* resource) { wl_resource_destroy(resource); } +void DataOfferFinish(wl_client* client, wl_resource* resource) { + NOTIMPLEMENTED(); +} + +void DataOfferSetActions(wl_client* client, + wl_resource* resource, + uint32_t dnd_actions, + uint32_t preferred_action) { + NOTIMPLEMENTED(); +} + const struct wl_data_offer_interface data_offer_impl = { - nullptr /* data_offer_accept*/, DataOfferReceive, - nullptr /*data_offer_finish*/, DataOfferDestroy, - nullptr /*data_offer_set_actions*/}; + DataOfferAccept, DataOfferReceive, DataOfferDestroy, DataOfferFinish, + DataOfferSetActions}; // wl_data_source @@ -376,8 +404,14 @@ void DataSourceDestroy(wl_client* client, wl_resource* resource) { wl_resource_destroy(resource); } +void SetActions(wl_client* client, + wl_resource* resource, + uint32_t dnd_actions) { + NOTIMPLEMENTED(); +} + const struct wl_data_source_interface data_source_impl = { - DataSourceOffer, DataSourceDestroy, nullptr /*data_source_set_actions*/}; + DataSourceOffer, DataSourceDestroy, SetActions}; // wl_seat @@ -440,6 +474,84 @@ const struct wl_touch_interface touch_impl = { &DestroyResource, // release }; +// zwp_text_input_v1 + +void TextInputV1Activate(wl_client* client, + wl_resource* resource, + wl_resource* seat, + wl_resource* surface) { + static_cast<MockZwpTextInput*>(wl_resource_get_user_data(resource)) + ->Activate(surface); +} + +void TextInputV1Deactivate(wl_client* client, + wl_resource* resource, + wl_resource* seat) { + static_cast<MockZwpTextInput*>(wl_resource_get_user_data(resource)) + ->Deactivate(); +} + +void TextInputV1ShowInputPanel(wl_client* client, wl_resource* resource) { + static_cast<MockZwpTextInput*>(wl_resource_get_user_data(resource)) + ->ShowInputPanel(); +} + +void TextInputV1HideInputPanel(wl_client* client, wl_resource* resource) { + static_cast<MockZwpTextInput*>(wl_resource_get_user_data(resource)) + ->HideInputPanel(); +} + +void TextInputV1Reset(wl_client* client, wl_resource* resource) { + static_cast<MockZwpTextInput*>(wl_resource_get_user_data(resource))->Reset(); +} + +void TextInputV1SetCursorRectangle(wl_client* client, + wl_resource* resource, + int32_t x, + int32_t y, + int32_t width, + int32_t height) { + static_cast<MockZwpTextInput*>(wl_resource_get_user_data(resource)) + ->SetCursorRect(x, y, width, height); +} + +const struct zwp_text_input_v1_interface zwp_text_input_v1_impl = { + &TextInputV1Activate, // activate + &TextInputV1Deactivate, // deactivate + &TextInputV1ShowInputPanel, // show_input_panel + &TextInputV1HideInputPanel, // hide_input_panel + &TextInputV1Reset, // reset + nullptr, // set_surrounding_text + nullptr, // set_content_type + &TextInputV1SetCursorRectangle, // set_cursor_rectangle + nullptr, // set_preferred_language + nullptr, // commit_state + nullptr, // invoke_action +}; + +// zwp_text_input_manager_v1 + +void CreateTextInput(struct wl_client* client, + struct wl_resource* resource, + uint32_t id) { + auto* im = + static_cast<MockTextInputManagerV1*>(wl_resource_get_user_data(resource)); + wl_resource* text_resource = + wl_resource_create(client, &zwp_text_input_v1_interface, + wl_resource_get_version(resource), id); + if (!text_resource) { + wl_client_post_no_memory(client); + return; + } + im->text_input.reset( + new MockZwpTextInput(text_resource, &zwp_text_input_v1_impl)); +} + +const struct zwp_text_input_manager_v1_interface + zwp_text_input_manager_v1_impl = { + &CreateTextInput, // create_text_input +}; + // xdg_surface, zxdg_surface_v6 and zxdg_toplevel shared methods. void SetTitle(wl_client* client, wl_resource* resource, const char* title) { @@ -450,6 +562,21 @@ void SetAppId(wl_client* client, wl_resource* resource, const char* app_id) { GetUserDataAs<MockXdgSurface>(resource)->SetAppId(app_id); } +void Move(wl_client* client, + wl_resource* resource, + wl_resource* seat, + uint32_t serial) { + GetUserDataAs<MockXdgSurface>(resource)->Move(serial); +} + +void Resize(wl_client* client, + wl_resource* resource, + wl_resource* seat, + uint32_t serial, + uint32_t edges) { + GetUserDataAs<MockXdgSurface>(resource)->Resize(serial, edges); +} + void AckConfigure(wl_client* client, wl_resource* resource, uint32_t serial) { GetUserDataAs<MockXdgSurface>(resource)->AckConfigure(serial); } @@ -492,8 +619,8 @@ const struct xdg_surface_interface xdg_surface_impl = { &SetTitle, // set_title &SetAppId, // set_app_id nullptr, // show_window_menu - nullptr, // move - nullptr, // resize + &Move, // move + &Resize, // resize &AckConfigure, // ack_configure &SetWindowGeometry, // set_window_geometry &SetMaximized, // set_maximized @@ -595,8 +722,8 @@ const struct zxdg_toplevel_v6_interface zxdg_toplevel_v6_impl = { &SetTitle, // set_title &SetAppId, // set_app_id nullptr, // show_window_menu - nullptr, // move - nullptr, // resize + &Move, // move + &Resize, // resize nullptr, // set_max_size nullptr, // set_min_size &SetMaximized, // set_maximized @@ -769,6 +896,15 @@ MockTouch::MockTouch(wl_resource* resource) : ServerObject(resource) { MockTouch::~MockTouch() {} +MockZwpTextInput::MockZwpTextInput(wl_resource* resource, + const void* implementation) + : ServerObject(resource) { + wl_resource_set_implementation(resource, implementation, this, + &ServerObject::OnResourceDestroyed); +} + +MockZwpTextInput::~MockZwpTextInput() {} + MockDataOffer::MockDataOffer(wl_resource* resource) : ServerObject(resource), io_thread_("Worker thread"), @@ -784,10 +920,15 @@ MockDataOffer::~MockDataOffer() {} void MockDataOffer::Receive(const std::string& mime_type, base::ScopedFD fd) { DCHECK(fd.is_valid()); - std::string text_utf8(kSampleClipboardText); + std::string text_data; + if (mime_type == kTextMimeTypeUtf8) + text_data = kSampleClipboardText; + else if (mime_type == kTextMimeTypeText) + text_data = kSampleTextForDragAndDrop; + io_thread_.task_runner()->PostTask( FROM_HERE, - base::BindOnce(&WriteDataOnWorkerThread, std::move(fd), text_utf8)); + base::BindOnce(&WriteDataOnWorkerThread, std::move(fd), text_data)); } void MockDataOffer::OnOffer(const std::string& mime_type) { @@ -816,6 +957,27 @@ MockDataOffer* MockDataDevice::OnDataOffer() { return GetUserDataAs<MockDataOffer>(data_offer_resource); } +void MockDataDevice::OnEnter(uint32_t serial, + wl_resource* surface, + wl_fixed_t x, + wl_fixed_t y, + MockDataOffer& data_offer) { + wl_data_device_send_enter(resource(), serial, surface, x, y, + data_offer.resource()); +} + +void MockDataDevice::OnLeave() { + wl_data_device_send_leave(resource()); +} + +void MockDataDevice::OnMotion(uint32_t time, wl_fixed_t x, wl_fixed_t y) { + wl_data_device_send_motion(resource(), time, x, y); +} + +void MockDataDevice::OnDrop() { + wl_data_device_send_drop(resource()); +} + void MockDataDevice::OnSelection(MockDataOffer& data_offer) { wl_data_device_send_selection(resource(), data_offer.resource()); } @@ -921,7 +1083,8 @@ MockDataDeviceManager::MockDataDeviceManager() MockDataDeviceManager::~MockDataDeviceManager() {} MockOutput::MockOutput() - : Global(&wl_output_interface, nullptr, kOutputVersion) {} + : Global(&wl_output_interface, nullptr, kOutputVersion), + rect_(gfx::Rect(0, 0, 800, 600)) {} MockOutput::~MockOutput() {} @@ -949,6 +1112,13 @@ MockXdgShellV6::MockXdgShellV6() MockXdgShellV6::~MockXdgShellV6() {} +MockTextInputManagerV1::MockTextInputManagerV1() + : Global(&zwp_text_input_manager_v1_interface, + &zwp_text_input_manager_v1_impl, + kTextInputManagerVersion) {} + +MockTextInputManagerV1::~MockTextInputManagerV1() {} + void DisplayDeleter::operator()(wl_display* display) { wl_display_destroy(display); } @@ -995,6 +1165,8 @@ bool FakeServer::Start(uint32_t shell_version) { if (!zxdg_shell_v6_.Initialize(display_.get())) return false; } + if (!zwp_text_input_manager_v1_.Initialize(display_.get())) + return false; client_ = wl_client_create(display_.get(), server_fd.get()); if (!client_) diff --git a/chromium/ui/ozone/platform/wayland/fake_server.h b/chromium/ui/ozone/platform/wayland/fake_server.h index 7739e6d62f2..ddda5748a19 100644 --- a/chromium/ui/ozone/platform/wayland/fake_server.h +++ b/chromium/ui/ozone/platform/wayland/fake_server.h @@ -24,7 +24,10 @@ struct wl_resource; namespace wl { constexpr char kTextMimeTypeUtf8[] = "text/plain;charset=utf-8"; +constexpr char kTextMimeTypeText[] = "text/plain"; constexpr char kSampleClipboardText[] = "This is a sample text for clipboard."; +constexpr char kSampleTextForDragAndDrop[] = + "This is a sample text for drag-and-drop."; // Base class for managing the life cycle of server objects. class ServerObject { @@ -56,6 +59,8 @@ class MockXdgSurface : public ServerObject { MOCK_METHOD1(SetParent, void(wl_resource* parent)); MOCK_METHOD1(SetTitle, void(const char* title)); MOCK_METHOD1(SetAppId, void(const char* app_id)); + MOCK_METHOD1(Move, void(uint32_t serial)); + MOCK_METHOD2(Resize, void(uint32_t serial, uint32_t edges)); MOCK_METHOD1(AckConfigure, void(uint32_t serial)); MOCK_METHOD4(SetWindowGeometry, void(int32_t x, int32_t y, int32_t width, int32_t height)); @@ -179,6 +184,24 @@ class MockTouch : public ServerObject { DISALLOW_COPY_AND_ASSIGN(MockTouch); }; +// Manage zwp_text_input_v1. +class MockZwpTextInput : public ServerObject { + public: + MockZwpTextInput(wl_resource* resource, const void* implementation); + ~MockZwpTextInput() override; + + MOCK_METHOD0(Reset, void()); + MOCK_METHOD1(Activate, void(wl_resource* window)); + MOCK_METHOD0(Deactivate, void()); + MOCK_METHOD0(ShowInputPanel, void()); + MOCK_METHOD0(HideInputPanel, void()); + MOCK_METHOD4(SetCursorRect, + void(int32_t x, int32_t y, int32_t width, int32_t height)); + + private: + DISALLOW_COPY_AND_ASSIGN(MockZwpTextInput); +}; + class MockDataOffer : public ServerObject { public: explicit MockDataOffer(wl_resource* resource); @@ -222,6 +245,14 @@ class MockDataDevice : public ServerObject { void SetSelection(MockDataSource* data_source, uint32_t serial); MockDataOffer* OnDataOffer(); + void OnEnter(uint32_t serial, + wl_resource* surface, + wl_fixed_t x, + wl_fixed_t y, + MockDataOffer& data_offer); + void OnLeave(); + void OnMotion(uint32_t time, wl_fixed_t x, wl_fixed_t y); + void OnDrop(); void OnSelection(MockDataOffer& data_offer); private: @@ -381,6 +412,18 @@ class MockXdgShellV6 : public Global { DISALLOW_COPY_AND_ASSIGN(MockXdgShellV6); }; +// Manage zwp_text_input_manager_v1 object. +class MockTextInputManagerV1 : public Global { + public: + MockTextInputManagerV1(); + ~MockTextInputManagerV1() override; + + std::unique_ptr<MockZwpTextInput> text_input; + + private: + DISALLOW_COPY_AND_ASSIGN(MockTextInputManagerV1); +}; + struct DisplayDeleter { void operator()(wl_display* display); }; @@ -410,10 +453,21 @@ class FakeServer : public base::Thread, base::MessagePumpLibevent::FdWatcher { return resource ? T::FromResource(resource) : nullptr; } + void CreateAndInitializeOutput() { + auto output = std::make_unique<MockOutput>(); + output->Initialize(display()); + globals_.push_back(std::move(output)); + } + MockDataDeviceManager* data_device_manager() { return &data_device_manager_; } MockSeat* seat() { return &seat_; } MockXdgShell* xdg_shell() { return &xdg_shell_; } MockOutput* output() { return &output_; } + MockTextInputManagerV1* text_input_manager_v1() { + return &zwp_text_input_manager_v1_; + } + + wl_display* display() const { return display_.get(); } private: void DoPause(); @@ -438,6 +492,9 @@ class FakeServer : public base::Thread, base::MessagePumpLibevent::FdWatcher { MockSeat seat_; MockXdgShell xdg_shell_; MockXdgShellV6 zxdg_shell_v6_; + MockTextInputManagerV1 zwp_text_input_manager_v1_; + + std::vector<std::unique_ptr<Global>> globals_; base::MessagePumpLibevent::FdWatchController controller_; diff --git a/chromium/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc b/chromium/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc index 8c3d01b4839..c08798ab0f5 100644 --- a/chromium/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc +++ b/chromium/ui/ozone/platform/wayland/gpu/gbm_pixmap_wayland.cc @@ -103,11 +103,7 @@ int GbmPixmapWayland::GetDmaBufOffset(size_t plane) const { } uint64_t GbmPixmapWayland::GetDmaBufModifier(size_t plane) const { - // TODO(msisov): figure out why returning format modifier results in - // EGL_BAD_ALLOC. - // - // return gbm_bo_->get_format_modifier(); - return 0; + return gbm_bo_->GetFormatModifier(); } gfx::BufferFormat GbmPixmapWayland::GetBufferFormat() const { diff --git a/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc b/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc index 54037394b0f..ce339c395c4 100644 --- a/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc +++ b/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.cc @@ -59,8 +59,6 @@ bool GbmSurfacelessWayland::IsOffscreen() { } bool GbmSurfacelessWayland::SupportsPresentationCallback() { - // TODO(msisov): enable a real presentation callback for wayland. For now, we - // just blindly say it was successful. https://crbug.com/859012. return true; } @@ -69,8 +67,7 @@ bool GbmSurfacelessWayland::SupportsAsyncSwap() { } bool GbmSurfacelessWayland::SupportsPostSubBuffer() { - // TODO(msisov): figure out how to enable subbuffers with wayland/dmabuf. - return false; + return true; } gfx::SwapResult GbmSurfacelessWayland::PostSubBuffer( @@ -135,8 +132,10 @@ void GbmSurfacelessWayland::PostSubBufferAsync( int height, const SwapCompletionCallback& completion_callback, const PresentationCallback& presentation_callback) { - // See the comment in SupportsPostSubBuffer. - NOTREACHED(); + PendingFrame* frame = unsubmitted_frames_.back().get(); + frame->damage_region_ = gfx::Rect(x, y, width, height); + + SwapBuffersAsync(completion_callback, presentation_callback); } EGLConfig GbmSurfacelessWayland::GetConfig() { @@ -198,16 +197,13 @@ void GbmSurfacelessWayland::SubmitFrame() { return; } + auto callback = + base::BindOnce(&GbmSurfacelessWayland::OnScheduleBufferSwapDone, + weak_factory_.GetWeakPtr()); uint32_t buffer_id = planes_.back().pixmap->GetUniqueId(); - surface_factory_->ScheduleBufferSwap(widget_, buffer_id); - - // Check comment in ::SupportsPresentationCallback. - OnSubmission(gfx::SwapResult::SWAP_ACK, nullptr); - OnPresentation( - gfx::PresentationFeedback(base::TimeTicks::Now(), base::TimeDelta(), - gfx::PresentationFeedback::kZeroCopy)); - - planes_.clear(); + surface_factory_->ScheduleBufferSwap(widget_, buffer_id, + submitted_frame_->damage_region_, + std::move(callback)); } } @@ -224,6 +220,14 @@ void GbmSurfacelessWayland::FenceRetired(PendingFrame* frame) { SubmitFrame(); } +void GbmSurfacelessWayland::OnScheduleBufferSwapDone( + gfx::SwapResult result, + const gfx::PresentationFeedback& feedback) { + OnSubmission(result, nullptr); + OnPresentation(feedback); + planes_.clear(); +} + void GbmSurfacelessWayland::OnSubmission( gfx::SwapResult result, std::unique_ptr<gfx::GpuFence> out_fence) { diff --git a/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h b/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h index cc660f3936f..bfb8c9f99a1 100644 --- a/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h +++ b/chromium/ui/ozone/platform/wayland/gpu/gbm_surfaceless_wayland.h @@ -68,6 +68,10 @@ class GbmSurfacelessWayland : public gl::SurfacelessEGL { bool ready = false; gfx::SwapResult swap_result = gfx::SwapResult::SWAP_FAILED; + // A region of the updated content in a corresponding frame. It's used to + // advice Wayland which part of a buffer is going to be updated. Passing {0, + // 0, 0, 0} results in a whole buffer update on the Wayland compositor side. + gfx::Rect damage_region_ = gfx::Rect(); std::vector<gl::GLSurfaceOverlay> overlays; SwapCompletionCallback completion_callback; PresentationCallback presentation_callback; @@ -78,6 +82,8 @@ class GbmSurfacelessWayland : public gl::SurfacelessEGL { EGLSyncKHR InsertFence(bool implicit); void FenceRetired(PendingFrame* frame); + void OnScheduleBufferSwapDone(gfx::SwapResult result, + const gfx::PresentationFeedback& feedback); void OnSubmission(gfx::SwapResult result, std::unique_ptr<gfx::GpuFence> out_fence); void OnPresentation(const gfx::PresentationFeedback& feedback); diff --git a/chromium/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.cc b/chromium/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.cc index 88b95156ee5..226f6d42ccf 100644 --- a/chromium/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.cc +++ b/chromium/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.cc @@ -86,11 +86,15 @@ void WaylandConnectionProxy::DestroyZwpLinuxDmabufInternal(uint32_t buffer_id) { wc_ptr_->DestroyZwpLinuxDmabuf(buffer_id); } -void WaylandConnectionProxy::ScheduleBufferSwap(gfx::AcceleratedWidget widget, - uint32_t buffer_id) { +void WaylandConnectionProxy::ScheduleBufferSwap( + gfx::AcceleratedWidget widget, + uint32_t buffer_id, + const gfx::Rect& damage_region, + wl::BufferSwapCallback callback) { DCHECK(gpu_thread_runner_->BelongsToCurrentThread()); DCHECK(wc_ptr_); - wc_ptr_->ScheduleBufferSwap(widget, buffer_id); + wc_ptr_->ScheduleBufferSwap(widget, buffer_id, damage_region, + std::move(callback)); } WaylandWindow* WaylandConnectionProxy::GetWindow( diff --git a/chromium/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.h b/chromium/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.h index 9126716d6b9..f06f9b9aada 100644 --- a/chromium/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.h +++ b/chromium/ui/ozone/platform/wayland/gpu/wayland_connection_proxy.h @@ -11,6 +11,7 @@ #include "mojo/public/cpp/bindings/binding_set.h" #include "ui/gfx/native_widget_types.h" #include "ui/ozone/platform/wayland/wayland_connection.h" +#include "ui/ozone/platform/wayland/wayland_util.h" #include "ui/ozone/public/interfaces/wayland/wayland_connection.mojom.h" #if defined(WAYLAND_GBM) @@ -19,6 +20,11 @@ struct wl_shm; +namespace gfx { +enum class SwapResult; +class Rect; +} // namespace gfx + namespace ui { class WaylandConnection; @@ -58,7 +64,12 @@ class WaylandConnectionProxy : public ozone::mojom::WaylandConnectionClient { // Asks Wayland to find a wl_buffer with the |buffer_id| and schedule a // buffer swap for a WaylandWindow, which backs the following |widget|. - void ScheduleBufferSwap(gfx::AcceleratedWidget widget, uint32_t buffer_id); + // The |callback| is called once a frame callback from the Wayland server + // is received. + void ScheduleBufferSwap(gfx::AcceleratedWidget widget, + uint32_t buffer_id, + const gfx::Rect& damage_region, + wl::BufferSwapCallback callback); #if defined(WAYLAND_GBM) // Returns a gbm_device based on a DRM render node. diff --git a/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc b/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc index 44763577a5c..ddddd861527 100644 --- a/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc +++ b/chromium/ui/ozone/platform/wayland/ozone_platform_wayland.cc @@ -14,12 +14,14 @@ #include "ui/ozone/platform/wayland/gpu/wayland_connection_proxy.h" #include "ui/ozone/platform/wayland/wayland_connection.h" #include "ui/ozone/platform/wayland/wayland_connection_connector.h" -#include "ui/ozone/platform/wayland/wayland_native_display_delegate.h" +#include "ui/ozone/platform/wayland/wayland_input_method_context_factory.h" +#include "ui/ozone/platform/wayland/wayland_output_manager.h" #include "ui/ozone/platform/wayland/wayland_surface_factory.h" #include "ui/ozone/platform/wayland/wayland_window.h" #include "ui/ozone/public/gpu_platform_support_host.h" #include "ui/ozone/public/input_controller.h" #include "ui/ozone/public/ozone_platform.h" +#include "ui/ozone/public/platform_screen.h" #include "ui/platform_window/platform_window_init_properties.h" #if BUILDFLAG(USE_XKBCOMMON) @@ -30,6 +32,7 @@ #endif #if defined(WAYLAND_GBM) +#include "ui/base/ui_base_features.h" #include "ui/ozone/common/linux/gbm_wrapper.h" #include "ui/ozone/platform/wayland/gpu/drm_render_node_handle.h" #include "ui/ozone/platform/wayland/gpu/drm_render_node_path_finder.h" @@ -49,6 +52,10 @@ class OzonePlatformWayland : public OzonePlatform { // https://github.com/wayland-project/wayland-protocols/commit/76d1ae8c65739eff3434ef219c58a913ad34e988 properties_.custom_frame_pref_default = true; properties_.use_system_title_bar = false; + // Ozone/Wayland relies on the mojo communication when running in + // !single_process. + // TODO(msisov, rjkroege): Remove after http://crbug.com/806092. + properties_.requires_mojo = true; } ~OzonePlatformWayland() override {} @@ -80,6 +87,15 @@ class OzonePlatformWayland : public OzonePlatform { std::unique_ptr<PlatformWindow> CreatePlatformWindow( PlatformWindowDelegate* delegate, PlatformWindowInitProperties properties) override { + // Some unit tests may try to set custom input method context factory + // after InitializeUI. Thus instead of creating factory in InitializeUI + // it is set at this point if none exists + if (!LinuxInputMethodContextFactory::instance() && + !wayland_input_method_context_factory_) { + wayland_input_method_context_factory_.reset( + new WaylandInputMethodContextFactory(connection_.get())); + } + auto window = std::make_unique<WaylandWindow>(delegate, connection_.get()); if (!window->Initialize(std::move(properties))) return nullptr; @@ -88,7 +104,14 @@ class OzonePlatformWayland : public OzonePlatform { std::unique_ptr<display::NativeDisplayDelegate> CreateNativeDisplayDelegate() override { - return std::make_unique<WaylandNativeDisplayDelegate>(connection_.get()); + return std::make_unique<display::FakeDisplayDelegate>(); + } + + std::unique_ptr<PlatformScreen> CreateScreen() override { + // The WaylandConnection and the WaylandOutputManager must be created before + // PlatformScreen. + DCHECK(connection_ && connection_->wayland_output_manager()); + return connection_->wayland_output_manager()->CreateWaylandScreen(); } void InitializeUI(const InitParams& args) override { @@ -141,8 +164,7 @@ class OzonePlatformWayland : public OzonePlatform { } const PlatformProperties& GetPlatformProperties() override { - DCHECK(connection_.get()); - if (properties_.supported_buffer_formats.empty()) { + if (connection_ && properties_.supported_buffer_formats.empty()) { properties_.supported_buffer_formats = connection_->GetSupportedBufferFormats(); } @@ -168,6 +190,8 @@ class OzonePlatformWayland : public OzonePlatform { std::unique_ptr<StubOverlayManager> overlay_manager_; std::unique_ptr<InputController> input_controller_; std::unique_ptr<GpuPlatformSupportHost> gpu_platform_support_host_; + std::unique_ptr<WaylandInputMethodContextFactory> + wayland_input_method_context_factory_; #if BUILDFLAG(USE_XKBCOMMON) XkbEvdevCodes xkb_evdev_code_converter_; diff --git a/chromium/ui/ozone/platform/wayland/wayland_buffer_manager.cc b/chromium/ui/ozone/platform/wayland/wayland_buffer_manager.cc index 3dc7a12af7e..59f2b128297 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_buffer_manager.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_buffer_manager.cc @@ -5,8 +5,8 @@ #include "ui/ozone/platform/wayland/wayland_buffer_manager.h" #include <drm_fourcc.h> - #include <linux-dmabuf-unstable-v1-client-protocol.h> +#include <presentation-time-client-protocol.h> #include "base/trace_event/trace_event.h" #include "ui/ozone/common/linux/drm_util_linux.h" @@ -15,6 +15,40 @@ namespace ui { +namespace { + +uint32_t GetPresentationKindFlags(uint32_t flags) { + uint32_t presentation_flags = 0; + if (flags & WP_PRESENTATION_FEEDBACK_KIND_VSYNC) + presentation_flags |= gfx::PresentationFeedback::kVSync; + if (flags & WP_PRESENTATION_FEEDBACK_KIND_HW_CLOCK) + presentation_flags |= gfx::PresentationFeedback::kHWClock; + if (flags & WP_PRESENTATION_FEEDBACK_KIND_HW_COMPLETION) + presentation_flags |= gfx::PresentationFeedback::kHWCompletion; + if (flags & WP_PRESENTATION_FEEDBACK_KIND_ZERO_COPY) + presentation_flags |= gfx::PresentationFeedback::kZeroCopy; + + return presentation_flags; +} + +base::TimeTicks GetPresentationFeedbackTimeStamp(uint32_t tv_sec_hi, + uint32_t tv_sec_lo, + uint32_t tv_nsec) { + const int64_t seconds = (static_cast<int64_t>(tv_sec_hi) << 32) + tv_sec_lo; + const int64_t microseconds = seconds * base::Time::kMicrosecondsPerSecond + + tv_nsec / base::Time::kNanosecondsPerMicrosecond; + return base::TimeTicks() + base::TimeDelta::FromMicroseconds(microseconds); +} + +} // namespace + +WaylandBufferManager::Buffer::Buffer() = default; +WaylandBufferManager::Buffer::Buffer(uint32_t id, + zwp_linux_buffer_params_v1* zwp_params, + const gfx::Size& buffer_size) + : buffer_id(id), size(buffer_size), params(zwp_params) {} +WaylandBufferManager::Buffer::~Buffer() = default; + WaylandBufferManager::WaylandBufferManager( zwp_linux_dmabuf_v1* zwp_linux_dmabuf, WaylandConnection* connection) @@ -31,8 +65,7 @@ WaylandBufferManager::WaylandBufferManager( } WaylandBufferManager::~WaylandBufferManager() { - DCHECK(pending_buffer_map_.empty() && params_to_id_map_.empty() && - buffers_.empty()); + DCHECK(buffers_.empty()); } bool WaylandBufferManager::CreateBuffer(base::File file, @@ -64,14 +97,17 @@ bool WaylandBufferManager::CreateBuffer(base::File file, DCHECK(zwp_linux_dmabuf_); struct zwp_linux_buffer_params_v1* params = zwp_linux_dmabuf_v1_create_params(zwp_linux_dmabuf_.get()); - params_to_id_map_.insert( - std::pair<struct zwp_linux_buffer_params_v1*, uint32_t>(params, - buffer_id)); + + std::unique_ptr<Buffer> buffer = + std::make_unique<Buffer>(buffer_id, params, gfx::Size(width, height)); + buffers_.insert(std::pair<uint32_t, std::unique_ptr<Buffer>>( + buffer_id, std::move(buffer))); + uint32_t fd = file.TakePlatformFile(); for (size_t i = 0; i < planes_count; i++) { zwp_linux_buffer_params_v1_add(params, fd, i /* plane id */, offsets[i], - strides[i], 0 /* modifier hi */, - 0 /* modifier lo */); + strides[i], modifiers[i] >> 32, + modifiers[i] & UINT32_MAX); } zwp_linux_buffer_params_v1_add_listener(params, ¶ms_listener, this); zwp_linux_buffer_params_v1_create(params, width, height, format, 0); @@ -81,48 +117,37 @@ bool WaylandBufferManager::CreateBuffer(base::File file, } // TODO(msisov): handle buffer swap failure or success. -bool WaylandBufferManager::SwapBuffer(gfx::AcceleratedWidget widget, - uint32_t buffer_id) { - TRACE_EVENT1("Wayland", "WaylandBufferManager::SwapBuffer", "Buffer id", - buffer_id); +bool WaylandBufferManager::ScheduleBufferSwap(gfx::AcceleratedWidget widget, + uint32_t buffer_id, + const gfx::Rect& damage_region, + wl::BufferSwapCallback callback) { + TRACE_EVENT1("Wayland", "WaylandBufferManager::ScheduleSwapBuffer", + "Buffer id", buffer_id); if (!ValidateDataFromGpu(widget, buffer_id)) return false; auto it = buffers_.find(buffer_id); - // A buffer might not exist by this time. So, store the request and process - // it once it is created. if (it == buffers_.end()) { - auto pending_buffers_it = pending_buffer_map_.find(buffer_id); - if (pending_buffers_it != pending_buffer_map_.end()) { - // If a buffer didn't exist and second call for a swap comes, buffer must - // be associated with the same widget. - DCHECK_EQ(pending_buffers_it->second, widget); - } else { - pending_buffer_map_.insert( - std::pair<uint32_t, gfx::AcceleratedWidget>(buffer_id, widget)); - } - return true; - } - struct wl_buffer* buffer = it->second.get(); - - WaylandWindow* window = connection_->GetWindow(widget); - if (!window) { - error_message_ = "A WaylandWindow with current widget does not exist"; + error_message_ = + "Buffer with " + std::to_string(buffer_id) + " id does not exist"; return false; } - // TODO(msisov): it would be beneficial to use real damage regions to improve - // performance. - // - // TODO(msisov): also start using wl_surface_frame callbacks for better - // performance. - wl_surface_damage(window->surface(), 0, 0, window->GetBounds().width(), - window->GetBounds().height()); - wl_surface_attach(window->surface(), buffer, 0, 0); - wl_surface_commit(window->surface()); + Buffer* buffer = it->second.get(); + DCHECK(buffer); - connection_->ScheduleFlush(); + // Assign a widget to this buffer, which is used to find a corresponding + // WaylandWindow. + buffer->widget = widget; + buffer->buffer_swap_callback = std::move(callback); + buffer->damage_region = damage_region; + + if (buffer->wl_buffer) { + // A wl_buffer might not exist by this time. Silently return. + // TODO: check this. + return SwapBuffer(buffer); + } return true; } @@ -135,6 +160,15 @@ bool WaylandBufferManager::DestroyBuffer(uint32_t buffer_id) { error_message_ = "Trying to destroy non-existing buffer"; return false; } + // It can happen that a buffer is destroyed before a frame callback comes. + // Thus, just mark this as a successful swap, which is ok to do. + Buffer* buffer = it->second.get(); + if (!buffer->buffer_swap_callback.is_null()) { + std::move(buffer->buffer_swap_callback) + .Run(gfx::SwapResult::SWAP_ACK, + gfx::PresentationFeedback(base::TimeTicks::Now(), + base::TimeDelta(), 0)); + } buffers_.erase(it); connection_->ScheduleFlush(); @@ -143,8 +177,55 @@ bool WaylandBufferManager::DestroyBuffer(uint32_t buffer_id) { void WaylandBufferManager::ClearState() { buffers_.clear(); - params_to_id_map_.clear(); - pending_buffer_map_.clear(); +} + +// TODO(msisov): handle buffer swap failure or success. +bool WaylandBufferManager::SwapBuffer(Buffer* buffer) { + TRACE_EVENT1("Wayland", "WaylandBufferManager::SwapBuffer", "Buffer id", + buffer->buffer_id); + + WaylandWindow* window = connection_->GetWindow(buffer->widget); + if (!window) { + error_message_ = "A WaylandWindow with current widget does not exist"; + return false; + } + + gfx::Rect damage_region = buffer->damage_region; + // If the size of the damage region is empty, wl_surface_damage must be + // supplied with the actual size of the buffer, which is going to be + // committed. + if (damage_region.size().IsEmpty()) + damage_region.set_size(buffer->size); + + wl_surface_damage_buffer(window->surface(), damage_region.x(), + damage_region.y(), damage_region.width(), + damage_region.height()); + wl_surface_attach(window->surface(), buffer->wl_buffer.get(), 0, 0); + + static const wl_callback_listener frame_listener = { + WaylandBufferManager::FrameCallbackDone}; + DCHECK(!buffer->wl_frame_callback); + buffer->wl_frame_callback.reset(wl_surface_frame(window->surface())); + wl_callback_add_listener(buffer->wl_frame_callback.get(), &frame_listener, + this); + + // Set up presentation feedback. + static const wp_presentation_feedback_listener feedback_listener = { + WaylandBufferManager::FeedbackSyncOutput, + WaylandBufferManager::FeedbackPresented, + WaylandBufferManager::FeedbackDiscarded}; + if (connection_->presentation()) { + DCHECK(!buffer->wp_presentation_feedback); + buffer->wp_presentation_feedback.reset(wp_presentation_feedback( + connection_->presentation(), window->surface())); + wp_presentation_feedback_add_listener( + buffer->wp_presentation_feedback.get(), &feedback_listener, this); + } + + wl_surface_commit(window->surface()); + + connection_->ScheduleFlush(); + return true; } bool WaylandBufferManager::ValidateDataFromGpu( @@ -187,6 +268,12 @@ bool WaylandBufferManager::ValidateDataFromGpu( if (buffer_id < 1) reason = "Invalid buffer id: " + std::to_string(buffer_id); + auto it = buffers_.find(buffer_id); + if (it != buffers_.end()) { + reason = "A buffer with " + std::to_string(buffer_id) + + " id has already existed"; + } + if (!reason.empty()) { error_message_ = std::move(reason); return false; @@ -218,24 +305,27 @@ void WaylandBufferManager::CreateSucceededInternal( struct wl_buffer* new_buffer) { // Find which buffer id |params| belong to and store wl_buffer // with that id. - auto it = params_to_id_map_.find(params); - CHECK(it != params_to_id_map_.end()); - uint32_t buffer_id = it->second; - params_to_id_map_.erase(params); - zwp_linux_buffer_params_v1_destroy(params); + Buffer* buffer = nullptr; + for (auto& item : buffers_) { + if (item.second.get()->params == params) { + buffer = item.second.get(); + break; + } + } - buffers_.insert(std::pair<uint32_t, wl::Object<wl_buffer>>( - buffer_id, wl::Object<wl_buffer>(new_buffer))); + DCHECK(buffer); + buffer->wl_buffer.reset(new_buffer); + buffer->params = nullptr; + zwp_linux_buffer_params_v1_destroy(params); - TRACE_EVENT1("Wayland", "WaylandBufferManager::CreateSucceeded", "Buffer id", - buffer_id); + if (buffer->widget != gfx::kNullAcceleratedWidget) + SwapBuffer(buffer); +} - auto pending_buffers_it = pending_buffer_map_.find(buffer_id); - if (pending_buffers_it != pending_buffer_map_.end()) { - gfx::AcceleratedWidget widget = pending_buffers_it->second; - pending_buffer_map_.erase(pending_buffers_it); - SwapBuffer(widget, buffer_id); - } +void WaylandBufferManager::OnBufferSwapped(Buffer* buffer) { + DCHECK(!buffer->buffer_swap_callback.is_null()); + std::move(buffer->buffer_swap_callback) + .Run(buffer->swap_result, std::move(buffer->feedback)); } // static @@ -279,4 +369,95 @@ void WaylandBufferManager::CreateFailed( LOG(FATAL) << "zwp_linux_buffer_params.create failed"; } +// static +void WaylandBufferManager::FrameCallbackDone(void* data, + wl_callback* callback, + uint32_t time) { + WaylandBufferManager* self = static_cast<WaylandBufferManager*>(data); + DCHECK(self); + for (auto& item : self->buffers_) { + Buffer* buffer = item.second.get(); + if (buffer->wl_frame_callback.get() == callback) { + buffer->swap_result = gfx::SwapResult::SWAP_ACK; + buffer->wl_frame_callback.reset(); + + // If presentation feedback is not supported, use fake feedback and + // trigger the callback. + if (!self->connection_->presentation()) { + buffer->feedback = gfx::PresentationFeedback(base::TimeTicks::Now(), + base::TimeDelta(), 0); + self->OnBufferSwapped(buffer); + } + return; + } + } + + NOTREACHED(); +} + +// static +void WaylandBufferManager::FeedbackSyncOutput( + void* data, + struct wp_presentation_feedback* wp_presentation_feedback, + struct wl_output* output) { + NOTIMPLEMENTED_LOG_ONCE(); +} + +// static +void WaylandBufferManager::FeedbackPresented( + void* data, + struct wp_presentation_feedback* wp_presentation_feedback, + uint32_t tv_sec_hi, + uint32_t tv_sec_lo, + uint32_t tv_nsec, + uint32_t refresh, + uint32_t seq_hi, + uint32_t seq_lo, + uint32_t flags) { + WaylandBufferManager* self = static_cast<WaylandBufferManager*>(data); + DCHECK(self); + + for (auto& item : self->buffers_) { + Buffer* buffer = item.second.get(); + if (buffer->wp_presentation_feedback.get() == wp_presentation_feedback) { + // Frame callback must come before a feedback is presented. + DCHECK(!buffer->wl_frame_callback); + + buffer->feedback = gfx::PresentationFeedback( + GetPresentationFeedbackTimeStamp(tv_sec_hi, tv_sec_lo, tv_nsec), + base::TimeDelta::FromNanoseconds(refresh), + GetPresentationKindFlags(flags)); + + buffer->wp_presentation_feedback.reset(); + self->OnBufferSwapped(buffer); + return; + } + } + + NOTREACHED(); +} + +// static +void WaylandBufferManager::FeedbackDiscarded( + void* data, + struct wp_presentation_feedback* wp_presentation_feedback) { + WaylandBufferManager* self = static_cast<WaylandBufferManager*>(data); + DCHECK(self); + + for (auto& item : self->buffers_) { + Buffer* buffer = item.second.get(); + if (buffer->wp_presentation_feedback.get() == wp_presentation_feedback) { + // Frame callback must come before a feedback is presented. + DCHECK(!buffer->wl_frame_callback); + buffer->feedback = gfx::PresentationFeedback::Failure(); + + buffer->wp_presentation_feedback.reset(); + self->OnBufferSwapped(buffer); + return; + } + } + + NOTREACHED(); +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/wayland_buffer_manager.h b/chromium/ui/ozone/platform/wayland/wayland_buffer_manager.h index a12b6565bf3..f2917fd47e7 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_buffer_manager.h +++ b/chromium/ui/ozone/platform/wayland/wayland_buffer_manager.h @@ -11,11 +11,16 @@ #include "base/containers/flat_map.h" #include "base/files/file.h" #include "base/macros.h" +#include "ui/gfx/geometry/rect.h" #include "ui/gfx/native_widget_types.h" +#include "ui/gfx/presentation_feedback.h" +#include "ui/gfx/swap_result.h" #include "ui/ozone/platform/wayland/wayland_object.h" +#include "ui/ozone/platform/wayland/wayland_util.h" struct zwp_linux_dmabuf_v1; struct zwp_linux_buffer_params_v1; +struct wp_presentation_feedback; namespace gfx { enum class BufferFormat; @@ -52,8 +57,13 @@ class WaylandBufferManager { uint32_t buffer_id); // Assigns a wl_buffer with |buffer_id| to a window with the same |widget|. On - // error, false is returned and |error_message_| is set. - bool SwapBuffer(gfx::AcceleratedWidget widget, uint32_t buffer_id); + // error, false is returned and |error_message_| is set. A |damage_region| + // identifies which part of the buffer is updated. If an empty region is + // provided, the whole buffer is updated. + bool ScheduleBufferSwap(gfx::AcceleratedWidget widget, + uint32_t buffer_id, + const gfx::Rect& damage_region, + wl::BufferSwapCallback callback); // Destroys a buffer with |buffer_id| in |buffers_|. On error, false is // returned and |error_message_| is set. @@ -63,6 +73,65 @@ class WaylandBufferManager { void ClearState(); private: + // This is an internal helper representation of a wayland buffer object, which + // the GPU process creates when CreateBuffer is called. It's used for + // asynchronous buffer creation and stores |params| parameter to find out, + // which Buffer the wl_buffer corresponds to when CreateSucceeded is called. + // What is more, the Buffer stores such information as a widget it is attached + // to, its buffer id for simplier buffer management and other members specific + // to this Buffer object on run-time. + struct Buffer { + Buffer(); + Buffer(uint32_t id, + zwp_linux_buffer_params_v1* zwp_params, + const gfx::Size& buffer_size); + ~Buffer(); + + // GPU GbmPixmapWayland corresponding buffer id. + uint32_t buffer_id = 0; + + // Actual buffer size. + const gfx::Size size; + + // Widget to attached/being attach WaylandWindow. + gfx::AcceleratedWidget widget = gfx::kNullAcceleratedWidget; + + // Describes the region where the pending buffer is different from the + // current surface contents, and where the surface therefore needs to be + // repainted. + gfx::Rect damage_region; + + // A buffer swap result once the buffer is committed. + gfx::SwapResult swap_result; + + // A feedback, which is received if a presentation feedback protocol is + // supported. + gfx::PresentationFeedback feedback; + + // Params that are used to create a wl_buffer. + zwp_linux_buffer_params_v1* params = nullptr; + + // A wl_buffer backed by a dmabuf created on the GPU side. + wl::Object<struct wl_buffer> wl_buffer; + + // A callback, which is called once the |wl_frame_callback| from the server + // is received. + wl::BufferSwapCallback buffer_swap_callback; + + // A Wayland callback, which is triggered once wl_buffer has been committed + // and it is right time to notify the GPU that it can start a new drawing + // operation. + wl::Object<wl_callback> wl_frame_callback; + + // A presentation feedback provided by the Wayland server once frame is + // shown. + wl::Object<struct wp_presentation_feedback> wp_presentation_feedback; + + DISALLOW_COPY_AND_ASSIGN(Buffer); + }; + + bool SwapBuffer(Buffer* buffer); + // Validates data sent from GPU. If invalid, returns false and sets an error // message to |error_message_|. bool ValidateDataFromGpu(const base::File& file, @@ -80,6 +149,8 @@ class WaylandBufferManager { void CreateSucceededInternal(struct zwp_linux_buffer_params_v1* params, struct wl_buffer* new_buffer); + void OnBufferSwapped(Buffer* buffer); + // zwp_linux_dmabuf_v1_listener static void Modifiers(void* data, struct zwp_linux_dmabuf_v1* zwp_linux_dmabuf, @@ -97,25 +168,36 @@ class WaylandBufferManager { static void CreateFailed(void* data, struct zwp_linux_buffer_params_v1* params); - // Stores a wl_buffer and it's id provided by the GbmBuffer object on the - // GPU process side. - base::flat_map<uint32_t, wl::Object<wl_buffer>> buffers_; - - // A temporary params-to-buffer id map, which is used to identify which - // id wl_buffer should be assigned when storing it in the |buffers_| map - // during CreateSucceeded call. - base::flat_map<struct zwp_linux_buffer_params_v1*, uint32_t> - params_to_id_map_; - - // It might happen that GPU asks to swap buffers, when a wl_buffer hasn't - // been created yet. Thus, store the request in a pending map. Once a buffer - // is created, it will be attached to the requested WaylandWindow based on the - // gfx::AcceleratedWidget. - base::flat_map<uint32_t, gfx::AcceleratedWidget> pending_buffer_map_; + // wl_callback_listener + static void FrameCallbackDone(void* data, + wl_callback* callback, + uint32_t time); + + // wp_presentation_feedback_listener + static void FeedbackSyncOutput( + void* data, + struct wp_presentation_feedback* wp_presentation_feedback, + struct wl_output* output); + static void FeedbackPresented( + void* data, + struct wp_presentation_feedback* wp_presentation_feedback, + uint32_t tv_sec_hi, + uint32_t tv_sec_lo, + uint32_t tv_nsec, + uint32_t refresh, + uint32_t seq_hi, + uint32_t seq_lo, + uint32_t flags); + static void FeedbackDiscarded( + void* data, + struct wp_presentation_feedback* wp_presentation_feedback); // Stores announced buffer formats supported by the compositor. std::vector<gfx::BufferFormat> supported_buffer_formats_; + // A container of created buffers. + base::flat_map<uint32_t, std::unique_ptr<Buffer>> buffers_; + // Set when invalid data is received from the GPU process. std::string error_message_; diff --git a/chromium/ui/ozone/platform/wayland/wayland_connection.cc b/chromium/ui/ozone/platform/wayland/wayland_connection.cc index 690504fb031..d4ce8fb5668 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_connection.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_connection.cc @@ -14,8 +14,11 @@ #include "base/message_loop/message_loop_current.h" #include "base/strings/string_util.h" #include "base/threading/thread_task_runner_handle.h" +#include "ui/gfx/swap_result.h" #include "ui/ozone/platform/wayland/wayland_buffer_manager.h" +#include "ui/ozone/platform/wayland/wayland_input_method_context.h" #include "ui/ozone/platform/wayland/wayland_object.h" +#include "ui/ozone/platform/wayland/wayland_output_manager.h" #include "ui/ozone/platform/wayland/wayland_window.h" static_assert(XDG_SHELL_VERSION_CURRENT == 5, "Unsupported xdg-shell version"); @@ -23,11 +26,21 @@ static_assert(XDG_SHELL_VERSION_CURRENT == 5, "Unsupported xdg-shell version"); namespace ui { namespace { -const uint32_t kMaxCompositorVersion = 4; -const uint32_t kMaxLinuxDmabufVersion = 1; -const uint32_t kMaxSeatVersion = 4; -const uint32_t kMaxShmVersion = 1; -const uint32_t kMaxXdgShellVersion = 1; +constexpr uint32_t kMaxCompositorVersion = 4; +constexpr uint32_t kMaxLinuxDmabufVersion = 1; +constexpr uint32_t kMaxSeatVersion = 4; +constexpr uint32_t kMaxShmVersion = 1; +constexpr uint32_t kMaxXdgShellVersion = 1; +constexpr uint32_t kMaxDeviceManagerVersion = 3; +constexpr uint32_t kMaxWpPresentationVersion = 1; +constexpr uint32_t kMaxTextInputManagerVersion = 1; + +std::unique_ptr<WaylandDataSource> CreateWaylandDataSource( + WaylandDataDeviceManager* data_device_manager, + WaylandConnection* connection) { + wl_data_source* data_source = data_device_manager->CreateSource(); + return std::make_unique<WaylandDataSource>(data_source, connection); +} } // namespace WaylandConnection::WaylandConnection() @@ -53,9 +66,10 @@ bool WaylandConnection::Initialize() { } wl_registry_add_listener(registry_.get(), ®istry_listener, this); - - while (!PrimaryOutput() || !PrimaryOutput()->is_ready()) + while (!wayland_output_manager_ || + !wayland_output_manager_->IsPrimaryOutputReady()) { wl_display_roundtrip(display_.get()); + } if (!compositor_) { LOG(ERROR) << "No wl_compositor object"; @@ -118,6 +132,15 @@ WaylandWindow* WaylandConnection::GetCurrentFocusedWindow() { return nullptr; } +WaylandWindow* WaylandConnection::GetCurrentKeyboardFocusedWindow() { + for (auto entry : window_map_) { + WaylandWindow* window = entry.second; + if (window->has_keyboard_focus()) + return window; + } + return nullptr; +} + void WaylandConnection::AddWindow(gfx::AcceleratedWidget widget, WaylandWindow* window) { window_map_[widget] = window; @@ -129,12 +152,6 @@ void WaylandConnection::RemoveWindow(gfx::AcceleratedWidget widget) { window_map_.erase(widget); } -WaylandOutput* WaylandConnection::PrimaryOutput() const { - if (!output_list_.size()) - return nullptr; - return output_list_.front().get(); -} - void WaylandConnection::SetCursorBitmap(const std::vector<SkBitmap>& bitmaps, const gfx::Point& location) { if (!pointer_ || !pointer_->cursor()) @@ -174,10 +191,14 @@ void WaylandConnection::DestroyZwpLinuxDmabuf(uint32_t buffer_id) { } } -void WaylandConnection::ScheduleBufferSwap(gfx::AcceleratedWidget widget, - uint32_t buffer_id) { +void WaylandConnection::ScheduleBufferSwap( + gfx::AcceleratedWidget widget, + uint32_t buffer_id, + const gfx::Rect& damage_region, + ScheduleBufferSwapCallback callback) { DCHECK(base::MessageLoopForUI::IsCurrent()); - if (!buffer_manager_->SwapBuffer(widget, buffer_id)) { + if (!buffer_manager_->ScheduleBufferSwap(widget, buffer_id, damage_region, + std::move(callback))) { TerminateGpuProcess(buffer_manager_->error_message()); } } @@ -190,9 +211,7 @@ void WaylandConnection::OfferClipboardData( const ClipboardDelegate::DataMap& data_map, ClipboardDelegate::OfferDataClosure callback) { if (!data_source_) { - wl_data_source* data_source = data_device_manager_->CreateSource(); - data_source_.reset(new WaylandDataSource(data_source)); - data_source_->set_connection(this); + data_source_ = CreateWaylandDataSource(data_device_manager_.get(), this); data_source_->WriteToClipboard(data_map); } data_source_->UpdataDataMap(data_map); @@ -234,6 +253,41 @@ void WaylandConnection::SetTerminateGpuCallback( terminate_gpu_cb_ = std::move(terminate_callback); } +void WaylandConnection::StartDrag(const ui::OSExchangeData& data, + int operation) { + if (!drag_data_source_) { + drag_data_source_ = + CreateWaylandDataSource(data_device_manager_.get(), this); + } + drag_data_source_->Offer(data); + drag_data_source_->SetAction(operation); + data_device_->StartDrag(*(drag_data_source_->data_source()), data); +} + +void WaylandConnection::FinishDragSession(uint32_t dnd_action, + WaylandWindow* source_window) { + if (source_window) + source_window->OnDragSessionClose(dnd_action); + data_device_->ResetSourceData(); + drag_data_source_.reset(); +} + +void WaylandConnection::DeliverDragData(const std::string& mime_type, + std::string* buffer) { + data_device_->DeliverDragData(mime_type, buffer); +} + +void WaylandConnection::RequestDragData( + const std::string& mime_type, + base::OnceCallback<void(const std::string&)> callback) { + data_device_->RequestDragData(mime_type, std::move(callback)); +} + +void WaylandConnection::ResetPointerFlags() { + if (pointer_) + pointer_->ResetFlags(); +} + void WaylandConnection::GetAvailableMimeTypes( ClipboardDelegate::GetMimeTypesClosure callback) { std::move(callback).Run(data_device_->GetAvailableMimeTypes()); @@ -287,11 +341,6 @@ void WaylandConnection::TerminateGpuProcess(std::string reason) { buffer_manager_->ClearState(); } -const std::vector<std::unique_ptr<WaylandOutput>>& -WaylandConnection::GetOutputList() const { - return output_list_; -} - // static void WaylandConnection::Global(void* data, wl_registry* registry, @@ -375,15 +424,17 @@ void WaylandConnection::Global(void* data, return; } - if (!connection->output_list_.empty()) - NOTIMPLEMENTED() << "Multiple screens support is not implemented"; - - connection->output_list_.push_back(base::WrapUnique(new WaylandOutput( - connection->get_next_display_id(), output.release()))); + if (!connection->wayland_output_manager_) { + connection->wayland_output_manager_ = + std::make_unique<WaylandOutputManager>(); + } + connection->wayland_output_manager_->AddWaylandOutput(name, + output.release()); } else if (!connection->data_device_manager_ && strcmp(interface, "wl_data_device_manager") == 0) { wl::Object<wl_data_device_manager> data_device_manager = - wl::Bind<wl_data_device_manager>(registry, name, 1); + wl::Bind<wl_data_device_manager>( + registry, name, std::min(version, kMaxDeviceManagerVersion)); if (!data_device_manager) { LOG(ERROR) << "Failed to bind to wl_data_device_manager global"; return; @@ -398,6 +449,18 @@ void WaylandConnection::Global(void* data, registry, name, std::min(version, kMaxLinuxDmabufVersion)); connection->buffer_manager_.reset( new WaylandBufferManager(zwp_linux_dmabuf.release(), connection)); + } else if (!connection->presentation_ && + (strcmp(interface, "wp_presentation") == 0)) { + connection->presentation_ = + wl::Bind<wp_presentation>(registry, name, kMaxWpPresentationVersion); + } else if (!connection->text_input_manager_v1_ && + strcmp(interface, "zwp_text_input_manager_v1") == 0) { + connection->text_input_manager_v1_ = wl::Bind<zwp_text_input_manager_v1>( + registry, name, std::min(version, kMaxTextInputManagerVersion)); + if (!connection->text_input_manager_v1_) { + LOG(ERROR) << "Failed to bind to zwp_text_input_manager_v1 global"; + return; + } } connection->ScheduleFlush(); @@ -407,7 +470,15 @@ void WaylandConnection::Global(void* data, void WaylandConnection::GlobalRemove(void* data, wl_registry* registry, uint32_t name) { - NOTIMPLEMENTED(); + WaylandConnection* connection = static_cast<WaylandConnection*>(data); + // The Wayland protocol distinguishes global objects by unique numeric names, + // which the WaylandOutputManager uses as unique output ids. But, it is only + // possible to figure out, what global object is going to be removed on the + // WaylandConnection::GlobalRemove call. Thus, whatever unique |name| comes, + // it's forwarded to the WaylandOutputManager, which checks if such a global + // output object exists and removes it. + if (connection->wayland_output_manager_) + connection->wayland_output_manager_->RemoveWaylandOutput(name); } // static diff --git a/chromium/ui/ozone/platform/wayland/wayland_connection.h b/chromium/ui/ozone/platform/wayland/wayland_connection.h index 521f957e331..d2487cd967b 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_connection.h +++ b/chromium/ui/ozone/platform/wayland/wayland_connection.h @@ -26,8 +26,9 @@ namespace ui { -class WaylandWindow; class WaylandBufferManager; +class WaylandOutputManager; +class WaylandWindow; class WaylandConnection : public PlatformEventSource, public ClipboardDelegate, @@ -60,7 +61,9 @@ class WaylandConnection : public PlatformEventSource, // Called by the GPU and asks to attach a wl_buffer with a |buffer_id| to a // WaylandWindow with the specified |widget|. void ScheduleBufferSwap(gfx::AcceleratedWidget widget, - uint32_t buffer_id) override; + uint32_t buffer_id, + const gfx::Rect& damage_region, + ScheduleBufferSwapCallback callback) override; // Schedules a flush of the Wayland connection. void ScheduleFlush(); @@ -69,20 +72,21 @@ class WaylandConnection : public PlatformEventSource, wl_compositor* compositor() { return compositor_.get(); } wl_subcompositor* subcompositor() { return subcompositor_.get(); } wl_shm* shm() { return shm_.get(); } - xdg_shell* shell() { return shell_.get(); } - zxdg_shell_v6* shell_v6() { return shell_v6_.get(); } + xdg_shell* shell() const { return shell_.get(); } + zxdg_shell_v6* shell_v6() const { return shell_v6_.get(); } wl_seat* seat() { return seat_.get(); } wl_data_device* data_device() { return data_device_->data_device(); } + wp_presentation* presentation() const { return presentation_.get(); } + zwp_text_input_manager_v1* text_input_manager_v1() { + return text_input_manager_v1_.get(); + } WaylandWindow* GetWindow(gfx::AcceleratedWidget widget); WaylandWindow* GetCurrentFocusedWindow(); + WaylandWindow* GetCurrentKeyboardFocusedWindow(); void AddWindow(gfx::AcceleratedWidget widget, WaylandWindow* window); void RemoveWindow(gfx::AcceleratedWidget widget); - int64_t get_next_display_id() { return next_display_id_++; } - const std::vector<std::unique_ptr<WaylandOutput>>& GetOutputList() const; - WaylandOutput* PrimaryOutput() const; - void set_serial(uint32_t serial) { serial_ = serial; } uint32_t serial() { return serial_; } @@ -94,6 +98,12 @@ class WaylandConnection : public PlatformEventSource, // Returns the current pointer, which may be null. WaylandPointer* pointer() { return pointer_.get(); } + WaylandDataSource* drag_data_source() { return drag_data_source_.get(); } + + WaylandOutputManager* wayland_output_manager() const { + return wayland_output_manager_.get(); + } + // Clipboard implementation. ClipboardDelegate* GetClipboardDelegate(); void DataSourceCancelled(); @@ -120,7 +130,34 @@ class WaylandConnection : public PlatformEventSource, void SetTerminateGpuCallback( base::OnceCallback<void(std::string)> terminate_gpu_cb); + // Starts drag with |data| to be delivered, |operation| supported by the + // source side initiated the dragging. + void StartDrag(const ui::OSExchangeData& data, int operation); + // Finishes drag and drop session. It happens when WaylandDataSource gets + // 'OnDnDFinished' or 'OnCancel', which means the drop is performed or + // canceled on others. + void FinishDragSession(uint32_t dnd_action, WaylandWindow* source_window); + // Delivers the data owned by Chromium which initiates drag-and-drop. |buffer| + // is an output parameter and it should be filled with the data corresponding + // to mime_type. + void DeliverDragData(const std::string& mime_type, std::string* buffer); + // Requests the data to the platform when Chromium gets drag-and-drop started + // by others. Once reading the data from platform is done, |callback| should + // be called with the data. + void RequestDragData(const std::string& mime_type, + base::OnceCallback<void(const std::string&)> callback); + + // Resets flags and keyboard modifiers. + // + // This method is specially handy for cases when the WaylandPointer state is + // modified by a POINTER_DOWN event, but the respective POINTER_UP event is + // not delivered. + void ResetPointerFlags(); + private: + // WaylandInputMethodContextFactory needs access to DispatchUiEvent + friend class WaylandInputMethodContextFactory; + void Flush(); void DispatchUiEvent(Event* event); @@ -162,12 +199,16 @@ class WaylandConnection : public PlatformEventSource, wl::Object<wl_shm> shm_; wl::Object<xdg_shell> shell_; wl::Object<zxdg_shell_v6> shell_v6_; + wl::Object<wp_presentation> presentation_; + wl::Object<zwp_text_input_manager_v1> text_input_manager_v1_; std::unique_ptr<WaylandDataDeviceManager> data_device_manager_; std::unique_ptr<WaylandDataDevice> data_device_; std::unique_ptr<WaylandDataSource> data_source_; - std::unique_ptr<WaylandPointer> pointer_; + std::unique_ptr<WaylandDataSource> drag_data_source_; std::unique_ptr<WaylandKeyboard> keyboard_; + std::unique_ptr<WaylandOutputManager> wayland_output_manager_; + std::unique_ptr<WaylandPointer> pointer_; std::unique_ptr<WaylandTouch> touch_; // Objects that are using when GPU runs in own process. @@ -179,9 +220,6 @@ class WaylandConnection : public PlatformEventSource, uint32_t serial_ = 0; - int64_t next_display_id_ = 0; - std::vector<std::unique_ptr<WaylandOutput>> output_list_; - // Holds a temporary instance of the client's clipboard content // so that we can asynchronously write to it. ClipboardDelegate::DataMap* data_map_ = nullptr; diff --git a/chromium/ui/ozone/platform/wayland/wayland_connection_unittest.cc b/chromium/ui/ozone/platform/wayland/wayland_connection_unittest.cc index dea153089ed..9c6cc51d948 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_connection_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_connection_unittest.cc @@ -8,42 +8,14 @@ #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "testing/gtest/include/gtest/gtest.h" -#include "ui/display/types/display_snapshot.h" #include "ui/ozone/platform/wayland/fake_server.h" #include "ui/ozone/platform/wayland/wayland_connection.h" -#include "ui/ozone/platform/wayland/wayland_output.h" namespace ui { namespace { - -const uint32_t kXdgVersion5 = 5; -const uint32_t kNumOfDisplays = 1; -const uint32_t kWidth = 800; -const uint32_t kHeight = 600; - -void CheckDisplaySize(const std::vector<display::DisplaySnapshot*>& displays) { - ASSERT_TRUE(displays.size() == kNumOfDisplays); - - // TODO(msisov): add multiple displays support. - display::DisplaySnapshot* display_snapshot = displays.front(); - ASSERT_TRUE(display_snapshot->current_mode()->size() == - gfx::Size(kWidth, kHeight)); +constexpr uint32_t kXdgVersion5 = 5; } -} - -class OutputObserver : public WaylandOutput::Observer { - public: - explicit OutputObserver(const base::Closure& closure) : closure_(closure) {} - - void OnOutputReadyForUse() override { - if (!closure_.is_null()) - closure_.Run(); - } - - private: - const base::Closure closure_; -}; TEST(WaylandConnectionTest, UseUnstableVersion) { base::MessageLoopForUI message_loop; @@ -78,26 +50,4 @@ TEST(WaylandConnectionTest, Ping) { server.Pause(); } -TEST(WaylandConnectionTest, Output) { - base::MessageLoopForUI message_loop; - wl::FakeServer server; - ASSERT_TRUE(server.Start(kXdgVersion5)); - server.output()->SetRect(gfx::Rect(0, 0, kWidth, kHeight)); - WaylandConnection connection; - ASSERT_TRUE(connection.Initialize()); - connection.StartProcessingEvents(); - - base::RunLoop run_loop; - OutputObserver observer(run_loop.QuitClosure()); - connection.PrimaryOutput()->SetObserver(&observer); - run_loop.Run(); - - connection.PrimaryOutput()->GetDisplaysSnapshot( - base::BindOnce(&CheckDisplaySize)); - - server.Resume(); - base::RunLoop().RunUntilIdle(); - server.Pause(); -} - } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/wayland_data_device.cc b/chromium/ui/ozone/platform/wayland/wayland_data_device.cc index f0f144c479e..067742691e7 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_data_device.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_data_device.cc @@ -5,10 +5,67 @@ #include "ui/ozone/platform/wayland/wayland_data_device.h" #include "base/bind.h" +#include "base/memory/shared_memory.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "third_party/skia/include/core/SkBitmap.h" +#include "third_party/skia/include/core/SkCanvas.h" +#include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/base/dragdrop/os_exchange_data_provider_aura.h" #include "ui/ozone/platform/wayland/wayland_connection.h" +#include "ui/ozone/platform/wayland/wayland_util.h" +#include "ui/ozone/platform/wayland/wayland_window.h" namespace ui { +namespace { + +constexpr char kMimeTypeText[] = "text/plain"; +constexpr char kMimeTypeTextUTF8[] = "text/plain;charset=utf-8"; + +int GetOperation(uint32_t source_actions, uint32_t dnd_action) { + uint32_t action = dnd_action != WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE + ? dnd_action + : source_actions; + + int operation = DragDropTypes::DRAG_NONE; + if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) + operation |= DragDropTypes::DRAG_COPY; + if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) + operation |= DragDropTypes::DRAG_MOVE; + // TODO(jkim): Implement branch for WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK + if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) + operation |= DragDropTypes::DRAG_COPY; + return operation; +} + +void AddStringToOSExchangeData(const std::string& data, + OSExchangeData* os_exchange_data) { + DCHECK(os_exchange_data); + if (data.empty()) + return; + + base::string16 string16 = base::UTF8ToUTF16(data); + os_exchange_data->SetString(string16); +} + +void AddToOSExchangeData(const std::string& data, + const std::string& mime_type, + OSExchangeData* os_exchange_data) { + DCHECK(os_exchange_data); + if ((mime_type == kMimeTypeText || mime_type == kMimeTypeTextUTF8)) { + DCHECK(!os_exchange_data->HasString()); + AddStringToOSExchangeData(data, os_exchange_data); + return; + } + + // TODO(jkim): Handle other mime types as well. + NOTREACHED(); +} + +} // namespace + // static const wl_callback_listener WaylandDataDevice::callback_listener_ = { WaylandDataDevice::SyncCallback, @@ -16,18 +73,22 @@ const wl_callback_listener WaylandDataDevice::callback_listener_ = { WaylandDataDevice::WaylandDataDevice(WaylandConnection* connection, wl_data_device* data_device) - : data_device_(data_device), connection_(connection) { + : data_device_(data_device), + connection_(connection), + shared_memory_(new base::SharedMemory()) { static const struct wl_data_device_listener kDataDeviceListener = { - WaylandDataDevice::OnDataOffer, - nullptr /*OnEnter*/, - nullptr /*OnLeave*/, - nullptr /*OnMotion*/, - nullptr /*OnDrop*/, - WaylandDataDevice::OnSelection}; + WaylandDataDevice::OnDataOffer, WaylandDataDevice::OnEnter, + WaylandDataDevice::OnLeave, WaylandDataDevice::OnMotion, + WaylandDataDevice::OnDrop, WaylandDataDevice::OnSelection}; wl_data_device_add_listener(data_device_.get(), &kDataDeviceListener, this); } -WaylandDataDevice::~WaylandDataDevice() {} +WaylandDataDevice::~WaylandDataDevice() { + if (!shared_memory_->handle().GetHandle()) + return; + shared_memory_->Unmap(); + shared_memory_->Close(); +} void WaylandDataDevice::RequestSelectionData(const std::string& mime_type) { base::ScopedFD fd = selection_offer_->Receive(mime_type); @@ -44,6 +105,81 @@ void WaylandDataDevice::RequestSelectionData(const std::string& mime_type) { RegisterSyncCallback(); } +void WaylandDataDevice::RequestDragData( + const std::string& mime_type, + base::OnceCallback<void(const std::string&)> callback) { + base::ScopedFD fd = drag_offer_->Receive(mime_type); + if (!fd.is_valid()) { + LOG(ERROR) << "Failed to open file descriptor."; + return; + } + + // Ensure there is not pending operation to be performed by the compositor, + // otherwise read(..) can block awaiting data to be sent to pipe. + read_from_fd_closure_ = base::BindOnce(&WaylandDataDevice::ReadDragDataFromFD, + base::Unretained(this), std::move(fd), + std::move(callback)); + RegisterSyncCallback(); +} + +void WaylandDataDevice::DeliverDragData(const std::string& mime_type, + std::string* buffer) { + DCHECK(buffer); + DCHECK(source_data_); + + if (mime_type != kMimeTypeText && mime_type != kMimeTypeTextUTF8) + return; + + const OSExchangeData::FilenameToURLPolicy policy = + OSExchangeData::FilenameToURLPolicy::DO_NOT_CONVERT_FILENAMES; + // TODO(jkim): Handle other data format as well. + if (source_data_->HasURL(policy)) { + GURL url; + base::string16 title; + source_data_->GetURLAndTitle(policy, &url, &title); + buffer->append(url.spec()); + return; + } + + if (source_data_->HasString()) { + base::string16 data; + source_data_->GetString(&data); + buffer->append(base::UTF16ToUTF8(data)); + return; + } +} + +void WaylandDataDevice::StartDrag(const wl_data_source& data_source, + const ui::OSExchangeData& data) { + WaylandWindow* window = connection_->GetCurrentFocusedWindow(); + if (!window) { + LOG(ERROR) << "Failed to get focused window."; + return; + } + + wl_surface* surface = window->surface(); + const SkBitmap* icon = data.provider().GetDragImage().bitmap(); + if (icon && !icon->empty()) + CreateDragImage(icon); + + source_data_ = std::make_unique<ui::OSExchangeData>(data.provider().Clone()); + wl_data_device_start_drag(data_device_.get(), + const_cast<wl_data_source*>(&data_source), surface, + icon_surface_.get(), connection_->serial()); + connection_->ScheduleFlush(); +} + +void WaylandDataDevice::ResetSourceData() { + source_data_.reset(); +} + +std::vector<std::string> WaylandDataDevice::GetAvailableMimeTypes() { + if (selection_offer_) + return selection_offer_->GetAvailableMimeTypes(); + + return std::vector<std::string>(); +} + void WaylandDataDevice::ReadClipboardDataFromFD(base::ScopedFD fd, const std::string& mime_type) { std::string contents; @@ -51,6 +187,14 @@ void WaylandDataDevice::ReadClipboardDataFromFD(base::ScopedFD fd, connection_->SetClipboardData(contents, mime_type); } +void WaylandDataDevice::ReadDragDataFromFD( + base::ScopedFD fd, + base::OnceCallback<void(const std::string&)> callback) { + std::string contents; + ReadDataFromFD(std::move(fd), &contents); + std::move(callback).Run(contents); +} + void WaylandDataDevice::RegisterSyncCallback() { DCHECK(!sync_callback_); sync_callback_.reset(wl_display_sync(connection_->display())); @@ -67,11 +211,11 @@ void WaylandDataDevice::ReadDataFromFD(base::ScopedFD fd, contents->append(buffer, length); } -std::vector<std::string> WaylandDataDevice::GetAvailableMimeTypes() { - if (selection_offer_) - return selection_offer_->GetAvailableMimeTypes(); +void WaylandDataDevice::HandleDeferredLeaveIfNeeded() { + if (!is_leaving_) + return; - return std::vector<std::string>(); + OnLeave(this, data_device_.get()); } // static @@ -84,6 +228,116 @@ void WaylandDataDevice::OnDataOffer(void* data, self->new_offer_.reset(new WaylandDataOffer(offer)); } +void WaylandDataDevice::OnEnter(void* data, + wl_data_device* data_device, + uint32_t serial, + wl_surface* surface, + wl_fixed_t x, + wl_fixed_t y, + wl_data_offer* offer) { + WaylandWindow* window = + static_cast<WaylandWindow*>(wl_surface_get_user_data(surface)); + if (!window) { + LOG(ERROR) << "Failed to get window."; + return; + } + + auto* self = static_cast<WaylandDataDevice*>(data); + DCHECK(self->new_offer_); + DCHECK(!self->drag_offer_); + self->drag_offer_ = std::move(self->new_offer_); + self->window_ = window; + + // TODO(jkim): Set mime type the client can accept. Now it sets all mime types + // offered because current implementation doesn't decide action based on mime + // type. + const std::vector<std::string>& mime_types = + self->drag_offer_->GetAvailableMimeTypes(); + for (auto mime : mime_types) + self->drag_offer_->Accept(serial, mime); + + std::copy(mime_types.begin(), mime_types.end(), + std::insert_iterator<std::list<std::string>>( + self->unprocessed_mime_types_, + self->unprocessed_mime_types_.begin())); + + int operation = GetOperation(self->drag_offer_->source_actions(), + self->drag_offer_->dnd_action()); + gfx::PointF point(wl_fixed_to_double(x), wl_fixed_to_double(y)); + + // If it has |source_data_|, it means that the dragging is started from the + // same window and it doesn't need to read the data through Wayland. + if (self->source_data_) { + std::unique_ptr<OSExchangeData> data = std::make_unique<OSExchangeData>( + self->source_data_->provider().Clone()); + self->window_->OnDragEnter(point, std::move(data), operation); + return; + } + + self->window_->OnDragEnter(point, nullptr, operation); +} + +void WaylandDataDevice::OnMotion(void* data, + wl_data_device* data_device, + uint32_t time, + wl_fixed_t x, + wl_fixed_t y) { + auto* self = static_cast<WaylandDataDevice*>(data); + if (!self->window_) { + LOG(ERROR) << "Failed to get window."; + return; + } + + int operation = GetOperation(self->drag_offer_->source_actions(), + self->drag_offer_->dnd_action()); + gfx::PointF point(wl_fixed_to_double(x), wl_fixed_to_double(y)); + int client_operation = self->window_->OnDragMotion(point, time, operation); + self->SetOperation(client_operation); +} + +void WaylandDataDevice::OnDrop(void* data, wl_data_device* data_device) { + auto* self = static_cast<WaylandDataDevice*>(data); + if (!self->window_) { + LOG(ERROR) << "Failed to get window."; + return; + } + + // Creates buffer to receive data from Wayland. + self->received_data_ = std::make_unique<OSExchangeData>( + std::make_unique<OSExchangeDataProviderAura>()); + + // Starts to read the data on Drop event because read(..) API blocks + // awaiting data to be sent to pipe if we try to read the data on OnEnter. + // 'Weston' also reads data on OnDrop event and other examples do as well. + self->HandleNextMimeType(); + + // In order to guarantee all data received, it sets + // |is_handling_dropped_data_| and defers OnLeave event handling if it gets + // OnLeave event before completing to read the data. + self->is_handling_dropped_data_ = true; +} + +void WaylandDataDevice::OnLeave(void* data, wl_data_device* data_device) { + // While reading data, it could get OnLeave event. We don't handle OnLeave + // event directly if |is_handling_dropped_data_| is set. + auto* self = static_cast<WaylandDataDevice*>(data); + if (!self->window_) { + LOG(ERROR) << "Failed to get window."; + return; + } + + if (self->is_handling_dropped_data_) { + self->is_leaving_ = true; + return; + } + + self->window_->OnDragLeave(); + self->window_ = nullptr; + self->drag_offer_.reset(); + self->is_handling_dropped_data_ = false; + self->is_leaving_ = false; +} + // static void WaylandDataDevice::OnSelection(void* data, wl_data_device* data_device, @@ -118,4 +372,88 @@ void WaylandDataDevice::SyncCallback(void* data, data_device->sync_callback_.reset(); } +void WaylandDataDevice::CreateDragImage(const SkBitmap* bitmap) { + DCHECK(bitmap); + gfx::Size size(bitmap->width(), bitmap->height()); + + if (size != icon_buffer_size_) { + wl_buffer* buffer = + wl::CreateSHMBuffer(size, shared_memory_.get(), connection_->shm()); + if (!buffer) + return; + + buffer_.reset(buffer); + icon_buffer_size_ = size; + } + wl::DrawBitmapToSHMB(icon_buffer_size_, *shared_memory_, *bitmap); + + icon_surface_.reset(wl_compositor_create_surface(connection_->compositor())); + wl_surface_attach(icon_surface_.get(), buffer_.get(), 0, 0); + wl_surface_damage(icon_surface_.get(), 0, 0, icon_buffer_size_.width(), + icon_buffer_size_.height()); + wl_surface_commit(icon_surface_.get()); +} + +void WaylandDataDevice::OnDragDataReceived(const std::string& contents) { + if (!contents.empty()) { + AddToOSExchangeData(contents, unprocessed_mime_types_.front(), + received_data_.get()); + } + + unprocessed_mime_types_.erase(unprocessed_mime_types_.begin()); + + // Read next data corresponding to the mime type. + HandleNextMimeType(); +} + +void WaylandDataDevice::OnDragDataCollected() { + unprocessed_mime_types_.clear(); + window_->OnDragDrop(std::move(received_data_)); + drag_offer_->FinishOffer(); + is_handling_dropped_data_ = false; + + HandleDeferredLeaveIfNeeded(); +} + +std::string WaylandDataDevice::SelectNextMimeType() { + while (!unprocessed_mime_types_.empty()) { + std::string& mime_type = unprocessed_mime_types_.front(); + if ((mime_type == kMimeTypeText || mime_type == kMimeTypeTextUTF8) && + !received_data_->HasString()) { + return mime_type; + } + // TODO(jkim): Handle other mime types as well. + unprocessed_mime_types_.erase(unprocessed_mime_types_.begin()); + } + return std::string(); +} + +void WaylandDataDevice::HandleNextMimeType() { + std::string mime_type = SelectNextMimeType(); + if (!mime_type.empty()) { + RequestDragData(mime_type, + base::BindOnce(&WaylandDataDevice::OnDragDataReceived, + base::Unretained(this))); + } else { + OnDragDataCollected(); + } +} + +void WaylandDataDevice::SetOperation(const int operation) { + uint32_t dnd_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + uint32_t preferred_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + + if (operation & DragDropTypes::DRAG_COPY) { + dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + preferred_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + } + + if (operation & DragDropTypes::DRAG_MOVE) { + dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + if (preferred_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE) + preferred_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + } + drag_offer_->SetAction(dnd_actions, preferred_action); +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/wayland_data_device.h b/chromium/ui/ozone/platform/wayland/wayland_data_device.h index 3d1eecde15b..f0986709246 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_data_device.h +++ b/chromium/ui/ozone/platform/wayland/wayland_data_device.h @@ -6,23 +6,31 @@ #define UI_OZONE_PLATFORM_WAYLAND_WAYLAND_DATA_DEVICE_H_ #include <wayland-client.h> +#include <list> #include <string> #include "base/callback.h" #include "base/files/scoped_file.h" #include "base/macros.h" +#include "ui/gfx/geometry/size.h" #include "ui/ozone/platform/wayland/wayland_data_offer.h" #include "ui/ozone/platform/wayland/wayland_object.h" +class SkBitmap; + +namespace base { +class SharedMemory; +} + namespace ui { +class OSExchangeData; class WaylandDataOffer; class WaylandConnection; +class WaylandWindow; // This class provides access to inter-client data transfer mechanisms // such as copy-and-paste and drag-and-drop mechanisms. -// -// TODO(tonikitoo,msisov): Add drag&drop support. class WaylandDataDevice { public: WaylandDataDevice(WaylandConnection* connection, wl_data_device* data_device); @@ -30,23 +38,66 @@ class WaylandDataDevice { void RequestSelectionData(const std::string& mime_type); - wl_data_device* data_device() { return data_device_.get(); } + // Requests the data to the platform when Chromium gets drag-and-drop started + // by others. Once reading the data from platform is done, |callback| should + // be called with the data. + void RequestDragData(const std::string& mime_type, + base::OnceCallback<void(const std::string&)> callback); + // Delivers the data owned by Chromium which initiates drag-and-drop. |buffer| + // is an output parameter and it should be filled with the data corresponding + // to mime_type. + void DeliverDragData(const std::string& mime_type, std::string* buffer); + // Starts drag with |data| to be delivered, |operation| supported by the + // source side initiated the dragging. + void StartDrag(const wl_data_source& data_source, + const ui::OSExchangeData& data); + // Resets |source_data_| when the dragging is finished. + void ResetSourceData(); std::vector<std::string> GetAvailableMimeTypes(); + wl_data_device* data_device() const { return data_device_.get(); } + private: void ReadClipboardDataFromFD(base::ScopedFD fd, const std::string& mime_type); + void ReadDragDataFromFD( + base::ScopedFD fd, + base::OnceCallback<void(const std::string&)> callback); + // Registers display sync callback. Once it's called, it's reset. void RegisterSyncCallback(); // Helper function to read data from fd. void ReadDataFromFD(base::ScopedFD fd, std::string* contents); + // If OnLeave event occurs while it's reading drag data, it defers handling + // it. Once reading data is completed, it's handled. + void HandleDeferredLeaveIfNeeded(); + // wl_data_device_listener callbacks static void OnDataOffer(void* data, wl_data_device* data_device, wl_data_offer* id); + + static void OnEnter(void* data, + wl_data_device* data_device, + uint32_t serial, + wl_surface* surface, + wl_fixed_t x, + wl_fixed_t y, + wl_data_offer* offer); + + static void OnMotion(void* data, + struct wl_data_device* data_device, + uint32_t time, + wl_fixed_t x, + wl_fixed_t y); + + static void OnDrop(void* data, struct wl_data_device* data_device); + + static void OnLeave(void* data, struct wl_data_device* data_device); + // Called by the compositor when the window gets pointer or keyboard focus, // or clipboard content changes behind the scenes. // @@ -57,6 +108,22 @@ class WaylandDataDevice { static void SyncCallback(void* data, struct wl_callback* cb, uint32_t time); + bool CreateSHMBuffer(const gfx::Size& size); + void CreateDragImage(const SkBitmap* bitmap); + + void OnDragDataReceived(const std::string& contents); + void OnDragDataCollected(); + + // Returns the next MIME type to be received from the source process, or an + // empty string if there are no more interesting MIME types left to process. + std::string SelectNextMimeType(); + // If it has |unprocessed_mime_types_|, it takes the mime type in front and + // requests the data corresponding to the mime type to wayland. + void HandleNextMimeType(); + + // Set drag operation decided by client. + void SetOperation(const int operation); + // The wl_data_device wrapped by this WaylandDataDevice. wl::Object<wl_data_device> data_device_; @@ -75,11 +142,36 @@ class WaylandDataDevice { // clipboard data is available. std::unique_ptr<WaylandDataOffer> selection_offer_; + // Offer to receive data from another process via drag-and-drop, or null if no + // drag-and-drop from another process is in progress. + std::unique_ptr<WaylandDataOffer> drag_offer_; + + WaylandWindow* window_ = nullptr; + // Make sure server has written data on the pipe, before block on read(). static const wl_callback_listener callback_listener_; base::OnceClosure read_from_fd_closure_; wl::Object<wl_callback> sync_callback_; + bool is_handling_dropped_data_ = false; + bool is_leaving_ = false; + + std::unique_ptr<base::SharedMemory> shared_memory_; + + wl::Object<wl_buffer> buffer_; + wl::Object<wl_surface> icon_surface_; + gfx::Size icon_buffer_size_; + + // Mime types to be handled. + std::list<std::string> unprocessed_mime_types_; + + // The data delivered from Wayland + std::unique_ptr<ui::OSExchangeData> received_data_; + + // When dragging is started from Chromium, |source_data_| is forwarded to + // Wayland when they are ready to get the data. + std::unique_ptr<ui::OSExchangeData> source_data_; + DISALLOW_COPY_AND_ASSIGN(WaylandDataDevice); }; diff --git a/chromium/ui/ozone/platform/wayland/wayland_data_device_unittest.cc b/chromium/ui/ozone/platform/wayland/wayland_data_device_unittest.cc index 03c567e1bc4..f8fe5abf620 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_data_device_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_data_device_unittest.cc @@ -5,6 +5,9 @@ #include <wayland-server.h> #include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/events/base_event_utils.h" #include "ui/ozone/platform/wayland/fake_server.h" #include "ui/ozone/platform/wayland/wayland_test.h" #include "ui/ozone/public/clipboard_delegate.h" @@ -131,6 +134,63 @@ TEST_P(WaylandDataDeviceManagerTest, IsSelectionOwner) { ASSERT_FALSE(clipboard_client_->IsSelectionOwner()); } +TEST_P(WaylandDataDeviceManagerTest, StartDrag) { + bool restored_focus = window_->has_pointer_focus(); + window_->set_pointer_focus(true); + + // The client starts dragging. + std::unique_ptr<OSExchangeData> os_exchange_data = + std::make_unique<OSExchangeData>(); + int operation = DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_MOVE; + connection_->StartDrag(*os_exchange_data, operation); + + WaylandDataSource::DragDataMap data; + data[wl::kTextMimeTypeText] = wl::kSampleTextForDragAndDrop; + connection_->drag_data_source()->SetDragData(data); + + Sync(); + // The server reads the data and the callback gets it. + data_device_manager_->data_source()->ReadData( + base::BindOnce([](const std::vector<uint8_t>& data) { + std::string string_data(data.begin(), data.end()); + EXPECT_EQ(wl::kSampleTextForDragAndDrop, string_data); + })); + + window_->set_pointer_focus(restored_focus); +} + +TEST_P(WaylandDataDeviceManagerTest, ReceiveDrag) { + auto* data_offer = data_device_manager_->data_device()->OnDataOffer(); + data_offer->OnOffer(wl::kTextMimeTypeText); + + gfx::Point entered_point(10, 10); + // The server sends an enter event. + data_device_manager_->data_device()->OnEnter( + 1002, surface_->resource(), wl_fixed_from_int(entered_point.x()), + wl_fixed_from_int(entered_point.y()), *data_offer); + + int64_t time = + (ui::EventTimeForNow() - base::TimeTicks()).InMilliseconds() & UINT32_MAX; + gfx::Point motion_point(11, 11); + + // The server sends an motion event. + data_device_manager_->data_device()->OnMotion( + time, wl_fixed_from_int(motion_point.x()), + wl_fixed_from_int(motion_point.y())); + + Sync(); + + auto callback = base::BindOnce([](const std::string& contents) { + EXPECT_EQ(wl::kSampleTextForDragAndDrop, contents); + }); + + // The client requests the data and gets callback with it. + connection_->RequestDragData(wl::kTextMimeTypeText, std::move(callback)); + Sync(); + + data_device_manager_->data_device()->OnLeave(); +} + INSTANTIATE_TEST_CASE_P(XdgVersionV5Test, WaylandDataDeviceManagerTest, ::testing::Values(kXdgShellV5)); diff --git a/chromium/ui/ozone/platform/wayland/wayland_data_offer.cc b/chromium/ui/ozone/platform/wayland/wayland_data_offer.cc index e68554ef483..a3900dd2341 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_data_offer.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_data_offer.cc @@ -30,9 +30,12 @@ void CreatePipe(base::ScopedFD* read_pipe, base::ScopedFD* write_pipe) { } // namespace WaylandDataOffer::WaylandDataOffer(wl_data_offer* data_offer) - : data_offer_(data_offer) { + : data_offer_(data_offer), + source_actions_(WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE), + dnd_action_(WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE) { static const struct wl_data_offer_listener kDataOfferListener = { - WaylandDataOffer::OnOffer}; + WaylandDataOffer::OnOffer, WaylandDataOffer::OnSourceAction, + WaylandDataOffer::OnAction}; wl_data_offer_add_listener(data_offer, &kDataOfferListener, this); } @@ -40,6 +43,23 @@ WaylandDataOffer::~WaylandDataOffer() { data_offer_.reset(); } +void WaylandDataOffer::SetAction(uint32_t dnd_actions, + uint32_t preferred_action) { + if (wl_data_offer_get_version(data_offer_.get()) >= + WL_DATA_OFFER_SET_ACTIONS_SINCE_VERSION) { + wl_data_offer_set_actions(data_offer_.get(), dnd_actions, preferred_action); + } +} + +void WaylandDataOffer::Accept(uint32_t serial, const std::string& mime_type) { + wl_data_offer_accept(data_offer_.get(), serial, mime_type.c_str()); +} + +void WaylandDataOffer::Reject(uint32_t serial) { + // Passing a null MIME type means "reject." + wl_data_offer_accept(data_offer_.get(), serial, nullptr); +} + void WaylandDataOffer::EnsureTextMimeTypeIfNeeded() { if (base::ContainsValue(mime_types_, kTextPlain)) return; @@ -76,6 +96,20 @@ base::ScopedFD WaylandDataOffer::Receive(const std::string& mime_type) { return read_fd; } +void WaylandDataOffer::FinishOffer() { + if (wl_data_offer_get_version(data_offer_.get()) >= + WL_DATA_OFFER_FINISH_SINCE_VERSION) + wl_data_offer_finish(data_offer_.get()); +} + +uint32_t WaylandDataOffer::source_actions() const { + return source_actions_; +} + +uint32_t WaylandDataOffer::dnd_action() const { + return dnd_action_; +} + // static void WaylandDataOffer::OnOffer(void* data, wl_data_offer* data_offer, @@ -84,4 +118,18 @@ void WaylandDataOffer::OnOffer(void* data, self->mime_types_.push_back(mime_type); } +void WaylandDataOffer::OnSourceAction(void* data, + wl_data_offer* offer, + uint32_t source_actions) { + auto* self = static_cast<WaylandDataOffer*>(data); + self->source_actions_ = source_actions; +} + +void WaylandDataOffer::OnAction(void* data, + wl_data_offer* offer, + uint32_t dnd_action) { + auto* self = static_cast<WaylandDataOffer*>(data); + self->dnd_action_ = dnd_action; +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/wayland_data_offer.h b/chromium/ui/ozone/platform/wayland/wayland_data_offer.h index 3b9e06f23c2..a2e8170534b 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_data_offer.h +++ b/chromium/ui/ozone/platform/wayland/wayland_data_offer.h @@ -23,8 +23,6 @@ namespace ui { // The offer describes the different mime types that the data can be // converted to and provides the mechanism for transferring the data // directly from the source client. -// -// TODO(tonikitoo,msisov): Add drag&drop support. class WaylandDataOffer { public: // Takes ownership of data_offer. @@ -42,20 +40,36 @@ class WaylandDataOffer { // list of provided mime types so that Chrome clipboard's machinery // works fine. void EnsureTextMimeTypeIfNeeded(); + void SetAction(uint32_t dnd_actions, uint32_t preferred_action); + void Accept(uint32_t serial, const std::string& mime_type); + void Reject(uint32_t serial); // Creates a pipe (read & write FDs), passing the write-end of to pipe // to the compositor (via wl_data_offer_receive) and returning the // read-end to the pipe. base::ScopedFD Receive(const std::string& mime_type); + void FinishOffer(); + uint32_t source_actions() const; + uint32_t dnd_action() const; private: // wl_data_offer_listener callbacks. static void OnOffer(void* data, wl_data_offer* data_offer, const char* mime_type); + // Notifies the source-side available actions + static void OnSourceAction(void* data, + wl_data_offer* offer, + uint32_t source_actions); + // Notifies the selected action + static void OnAction(void* data, wl_data_offer* offer, uint32_t dnd_action); wl::Object<wl_data_offer> data_offer_; std::vector<std::string> mime_types_; + // Actions offered by the data source + uint32_t source_actions_; + // Action selected by the compositor + uint32_t dnd_action_; bool text_plain_mime_type_inserted_ = false; diff --git a/chromium/ui/ozone/platform/wayland/wayland_data_source.cc b/chromium/ui/ozone/platform/wayland/wayland_data_source.cc index 7b61ad24c02..e1fe23b6c19 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_data_source.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_data_source.cc @@ -5,17 +5,22 @@ #include "ui/ozone/platform/wayland/wayland_data_source.h" #include "base/files/file_util.h" +#include "ui/base/dragdrop/drag_drop_types.h" #include "ui/ozone/platform/wayland/wayland_connection.h" +#include "ui/ozone/platform/wayland/wayland_window.h" namespace ui { +constexpr char kTextMimeType[] = "text/plain"; constexpr char kTextMimeTypeUtf8[] = "text/plain;charset=utf-8"; -WaylandDataSource::WaylandDataSource(wl_data_source* data_source) - : data_source_(data_source) { +WaylandDataSource::WaylandDataSource(wl_data_source* data_source, + WaylandConnection* connection) + : data_source_(data_source), connection_(connection) { static const struct wl_data_source_listener kDataSourceListener = { - WaylandDataSource::OnTarget, WaylandDataSource::OnSend, - WaylandDataSource::OnCancel}; + WaylandDataSource::OnTarget, WaylandDataSource::OnSend, + WaylandDataSource::OnCancel, WaylandDataSource::OnDnDDropPerformed, + WaylandDataSource::OnDnDFinished, WaylandDataSource::OnAction}; wl_data_source_add_listener(data_source, &kDataSourceListener, this); } @@ -25,7 +30,7 @@ void WaylandDataSource::WriteToClipboard( const ClipboardDelegate::DataMap& data_map) { for (const auto& data : data_map) { wl_data_source_offer(data_source_.get(), data.first.c_str()); - if (strcmp(data.first.c_str(), "text/plain") == 0) + if (strcmp(data.first.c_str(), kTextMimeType) == 0) wl_data_source_offer(data_source_.get(), kTextMimeTypeUtf8); } wl_data_device_set_selection(connection_->data_device(), data_source_.get(), @@ -39,11 +44,39 @@ void WaylandDataSource::UpdataDataMap( data_map_ = data_map; } +void WaylandDataSource::Offer(const ui::OSExchangeData& data) { + // TODO(jkim): Handle mime types based on data. + std::vector<std::string> mime_types; + mime_types.push_back(kTextMimeType); + mime_types.push_back(kTextMimeTypeUtf8); + + source_window_ = connection_->GetCurrentFocusedWindow(); + for (auto& mime_type : mime_types) + wl_data_source_offer(data_source_.get(), mime_type.data()); +} + +void WaylandDataSource::SetDragData(const DragDataMap& data_map) { + DCHECK(drag_data_map_.empty()); + drag_data_map_ = data_map; +} + +void WaylandDataSource::SetAction(int operation) { + if (wl_data_source_get_version(data_source_.get()) >= + WL_DATA_SOURCE_SET_ACTIONS_SINCE_VERSION) { + uint32_t dnd_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + if (operation & DragDropTypes::DRAG_COPY) + dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + if (operation & DragDropTypes::DRAG_MOVE) + dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + wl_data_source_set_actions(data_source_.get(), dnd_actions); + } +} + // static void WaylandDataSource::OnTarget(void* data, wl_data_source* source, const char* mime_type) { - NOTIMPLEMENTED(); + NOTIMPLEMENTED_LOG_ONCE(); } // static @@ -52,12 +85,18 @@ void WaylandDataSource::OnSend(void* data, const char* mime_type, int32_t fd) { WaylandDataSource* self = static_cast<WaylandDataSource*>(data); - base::Optional<std::vector<uint8_t>> mime_data; - self->GetClipboardData(mime_type, &mime_data); - if (!mime_data.has_value() && strcmp(mime_type, kTextMimeTypeUtf8) == 0) - self->GetClipboardData("text/plain", &mime_data); - - std::string contents(mime_data->begin(), mime_data->end()); + std::string contents; + if (self->source_window_) { + // If |source_window_| is valid when OnSend() is called, it means that DnD + // is working. + self->GetDragData(mime_type, &contents); + } else { + base::Optional<std::vector<uint8_t>> mime_data; + self->GetClipboardData(mime_type, &mime_data); + if (!mime_data.has_value() && strcmp(mime_type, kTextMimeTypeUtf8) == 0) + self->GetClipboardData(kTextMimeType, &mime_data); + contents.assign(mime_data->begin(), mime_data->end()); + } bool result = base::WriteFileDescriptor(fd, contents.data(), contents.length()); DCHECK(result); @@ -67,7 +106,30 @@ void WaylandDataSource::OnSend(void* data, // static void WaylandDataSource::OnCancel(void* data, wl_data_source* source) { WaylandDataSource* self = static_cast<WaylandDataSource*>(data); - self->connection_->DataSourceCancelled(); + if (self->source_window_) { + // If it has |source_window_|, it is in the middle of 'drag and drop'. it + // cancels 'drag and drop'. + self->connection_->FinishDragSession(self->dnd_action_, + self->source_window_); + } else { + self->connection_->DataSourceCancelled(); + } +} + +void WaylandDataSource::OnDnDDropPerformed(void* data, wl_data_source* source) { + NOTIMPLEMENTED_LOG_ONCE(); +} + +void WaylandDataSource::OnDnDFinished(void* data, wl_data_source* source) { + WaylandDataSource* self = static_cast<WaylandDataSource*>(data); + self->connection_->FinishDragSession(self->dnd_action_, self->source_window_); +} + +void WaylandDataSource::OnAction(void* data, + wl_data_source* source, + uint32_t dnd_action) { + WaylandDataSource* self = static_cast<WaylandDataSource*>(data); + self->dnd_action_ = dnd_action; } void WaylandDataSource::GetClipboardData( @@ -81,4 +143,15 @@ void WaylandDataSource::GetClipboardData( } } +void WaylandDataSource::GetDragData(const std::string& mime_type, + std::string* contents) { + auto it = drag_data_map_.find(mime_type); + if (it != drag_data_map_.end()) { + *contents = it->second; + return; + } + + connection_->DeliverDragData(mime_type, contents); +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/wayland_data_source.h b/chromium/ui/ozone/platform/wayland/wayland_data_source.h index 9ee33971a93..33dfeb2f912 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_data_source.h +++ b/chromium/ui/ozone/platform/wayland/wayland_data_source.h @@ -7,8 +7,8 @@ #include <wayland-client.h> +#include <map> #include <string> -#include <unordered_map> #include <vector> #include "base/logging.h" @@ -19,7 +19,9 @@ namespace ui { +class OSExchangeData; class WaylandConnection; +class WaylandWindow; // The WaylandDataSource object represents the source side of a // WaylandDataOffer. It is created by the source client in a data @@ -28,8 +30,11 @@ class WaylandConnection; // transfer the data (OnSend listener). class WaylandDataSource { public: + using DragDataMap = std::map<std::string, std::string>; + // Takes ownership of data_source. - explicit WaylandDataSource(wl_data_source* data_source); + explicit WaylandDataSource(wl_data_source* data_source, + WaylandConnection* connection); ~WaylandDataSource(); void set_connection(WaylandConnection* connection) { @@ -39,6 +44,11 @@ class WaylandDataSource { void WriteToClipboard(const ClipboardDelegate::DataMap& data_map); void UpdataDataMap(const ClipboardDelegate::DataMap& data_map); + void Offer(const ui::OSExchangeData& data); + void SetAction(int operation); + void SetDragData(const DragDataMap& data_map); + + const wl_data_source* data_source() const { return data_source_.get(); } private: static void OnTarget(void* data, @@ -49,14 +59,22 @@ class WaylandDataSource { const char* mime_type, int32_t fd); static void OnCancel(void* data, wl_data_source* source); + static void OnDnDDropPerformed(void* data, wl_data_source* source); + static void OnDnDFinished(void* data, wl_data_source* source); + static void OnAction(void* data, wl_data_source* source, uint32_t dnd_action); void GetClipboardData(const std::string& mime_type, base::Optional<std::vector<uint8_t>>* data); + void GetDragData(const std::string& mime_type, std::string* contents); wl::Object<wl_data_source> data_source_; WaylandConnection* connection_ = nullptr; + WaylandWindow* source_window_ = nullptr; ClipboardDelegate::DataMap data_map_; + DragDataMap drag_data_map_; + // Action selected by the compositor + uint32_t dnd_action_; DISALLOW_COPY_AND_ASSIGN(WaylandDataSource); }; diff --git a/chromium/ui/ozone/platform/wayland/wayland_input_method_context.cc b/chromium/ui/ozone/platform/wayland/wayland_input_method_context.cc new file mode 100644 index 00000000000..959a8e331db --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland_input_method_context.cc @@ -0,0 +1,151 @@ +// 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/ozone/platform/wayland/wayland_input_method_context.h" + +#include "base/bind.h" +#include "base/command_line.h" +#include "base/memory/ptr_util.h" +#include "base/strings/string_util.h" +#include "base/strings/utf_string_conversions.h" +#include "ui/base/ime/composition_text.h" +#include "ui/events/base_event_utils.h" +#include "ui/events/event.h" +#include "ui/events/keycodes/dom/dom_code.h" +#include "ui/events/keycodes/dom/keycode_converter.h" +#include "ui/events/keycodes/keyboard_code_conversion.h" +#include "ui/events/keycodes/keyboard_code_conversion_xkb.h" +#include "ui/events/ozone/layout/keyboard_layout_engine.h" +#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h" +#include "ui/gfx/range/range.h" +#include "ui/ozone/platform/wayland/wayland_connection.h" +#include "ui/ozone/platform/wayland/zwp_text_input_wrapper_v1.h" +#include "ui/ozone/public/ozone_switches.h" + +namespace ui { + +namespace { + +constexpr int kXkbKeycodeOffset = 8; + +} // namespace + +WaylandInputMethodContext::WaylandInputMethodContext( + WaylandConnection* connection, + LinuxInputMethodContextDelegate* delegate, + bool is_simple, + const EventDispatchCallback& callback) + : connection_(connection), + delegate_(delegate), + is_simple_(is_simple), + callback_(callback), + text_input_(nullptr) { + Init(); +} + +WaylandInputMethodContext::~WaylandInputMethodContext() { + if (text_input_) { + text_input_->Deactivate(); + text_input_->HideInputPanel(); + } +} + +void WaylandInputMethodContext::Init(bool initialize_for_testing) { + bool use_ozone_wayland_vkb = + initialize_for_testing || + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kEnableWaylandIme); + + // If text input instance is not created then all ime context operations + // are noop. This option is because in some environments someone might not + // want to enable ime/virtual keyboard even if it's available. + if (use_ozone_wayland_vkb && !is_simple_ && !text_input_ && + connection_->text_input_manager_v1()) { + text_input_ = std::make_unique<ZWPTextInputWrapperV1>( + connection_->text_input_manager_v1()); + text_input_->Initialize(connection_, this); + } +} + +bool WaylandInputMethodContext::DispatchKeyEvent( + const ui::KeyEvent& key_event) { + return false; +} + +void WaylandInputMethodContext::Reset() { + if (text_input_) + text_input_->Reset(); +} + +void WaylandInputMethodContext::Focus() { + WaylandWindow* window = connection_->GetCurrentKeyboardFocusedWindow(); + if (!text_input_ || !window) + return; + + text_input_->Activate(window); + text_input_->ShowInputPanel(); +} + +void WaylandInputMethodContext::Blur() { + if (text_input_) { + text_input_->Deactivate(); + text_input_->HideInputPanel(); + } +} + +void WaylandInputMethodContext::SetCursorLocation(const gfx::Rect& rect) { + if (text_input_) + text_input_->SetCursorRect(rect); +} + +void WaylandInputMethodContext::SetSurroundingText( + const base::string16& text, + const gfx::Range& selection_range) { + if (text_input_) + text_input_->SetSurroundingText(text, selection_range); +} + +void WaylandInputMethodContext::OnPreeditString(const std::string& text, + int preedit_cursor) { + gfx::Range selection_range = gfx::Range::InvalidRange(); + + if (!selection_range.IsValid()) { + int cursor_pos = (preedit_cursor) ? text.length() : preedit_cursor; + selection_range.set_start(cursor_pos); + selection_range.set_end(cursor_pos); + } + + ui::CompositionText composition_text; + composition_text.text = base::UTF8ToUTF16(text); + composition_text.selection = selection_range; + delegate_->OnPreeditChanged(composition_text); +} + +void WaylandInputMethodContext::OnCommitString(const std::string& text) { + delegate_->OnCommit(base::UTF8ToUTF16(text)); +} + +void WaylandInputMethodContext::OnDeleteSurroundingText(int32_t index, + uint32_t length) { + delegate_->OnDeleteSurroundingText(index, length); +} + +void WaylandInputMethodContext::OnKeysym(uint32_t key, + uint32_t state, + uint32_t modifiers) { + uint8_t flags = 0; // for now ignore modifiers + DomKey dom_key = NonPrintableXKeySymToDomKey(key); + KeyboardCode key_code = NonPrintableDomKeyToKeyboardCode(dom_key); + DomCode dom_code = + KeycodeConverter::NativeKeycodeToDomCode(key_code + kXkbKeycodeOffset); + if (dom_code == ui::DomCode::NONE) + return; + + bool down = state == WL_KEYBOARD_KEY_STATE_PRESSED; + ui::KeyEvent event(down ? ET_KEY_PRESSED : ET_KEY_RELEASED, key_code, + dom_code, flags, dom_key, EventTimeForNow()); + callback_.Run(&event); +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/wayland_input_method_context.h b/chromium/ui/ozone/platform/wayland/wayland_input_method_context.h new file mode 100644 index 00000000000..1023ecca585 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland_input_method_context.h @@ -0,0 +1,59 @@ +// 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_OZONE_PLATFORM_WAYLAND_WAYLAND_INPUT_METHOD_CONTEXT_H_ +#define UI_OZONE_PLATFORM_WAYLAND_WAYLAND_INPUT_METHOD_CONTEXT_H_ + +#include "base/macros.h" +#include "ui/base/ime/linux/linux_input_method_context.h" +#include "ui/events/ozone/evdev/event_dispatch_callback.h" +#include "ui/ozone/platform/wayland/zwp_text_input_wrapper.h" + +namespace ui { + +class WaylandConnection; +class ZWPTextInputWrapper; + +class WaylandInputMethodContext : public LinuxInputMethodContext, + public ZWPTextInputWrapperClient { + public: + WaylandInputMethodContext(WaylandConnection* connection, + LinuxInputMethodContextDelegate* delegate, + bool is_simple, + const EventDispatchCallback& callback); + ~WaylandInputMethodContext() override; + + void Init(bool initialize_for_testing = false); + + // LinuxInputMethodContext overrides: + bool DispatchKeyEvent(const ui::KeyEvent& key_event) override; + void SetCursorLocation(const gfx::Rect& rect) override; + void SetSurroundingText(const base::string16& text, + const gfx::Range& selection_range) override; + void Reset() override; + void Focus() override; + void Blur() override; + + // ui::ZWPTextInputWrapperClient + void OnPreeditString(const std::string& text, int preedit_cursor) override; + void OnCommitString(const std::string& text) override; + void OnDeleteSurroundingText(int32_t index, uint32_t length) override; + void OnKeysym(uint32_t key, uint32_t state, uint32_t modifiers) override; + + private: + WaylandConnection* connection_ = nullptr; // TODO(jani) Handle this better + + // Delegate interface back to IME code in ui. + LinuxInputMethodContextDelegate* delegate_; + bool is_simple_; + EventDispatchCallback callback_; + + std::unique_ptr<ZWPTextInputWrapper> text_input_; + + DISALLOW_COPY_AND_ASSIGN(WaylandInputMethodContext); +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_WAYLAND_WAYLAND_INPUT_METHOD_CONTEXT_H_ diff --git a/chromium/ui/ozone/platform/wayland/wayland_input_method_context_factory.cc b/chromium/ui/ozone/platform/wayland/wayland_input_method_context_factory.cc new file mode 100644 index 00000000000..1eaf6fae245 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland_input_method_context_factory.cc @@ -0,0 +1,39 @@ +// 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/ozone/platform/wayland/wayland_input_method_context_factory.h" + +#include "ui/ozone/platform/wayland/wayland_connection.h" +#include "ui/ozone/platform/wayland/wayland_input_method_context.h" + +namespace ui { + +WaylandInputMethodContextFactory::WaylandInputMethodContextFactory( + WaylandConnection* connection) + : connection_(connection) { + LinuxInputMethodContextFactory::SetInstance(this); +} + +WaylandInputMethodContextFactory::~WaylandInputMethodContextFactory() { + LinuxInputMethodContextFactory::SetInstance(nullptr); +} + +std::unique_ptr<LinuxInputMethodContext> +WaylandInputMethodContextFactory::CreateInputMethodContext( + LinuxInputMethodContextDelegate* delegate, + bool is_simple) const { + return CreateWaylandInputMethodContext(delegate, is_simple); +} + +std::unique_ptr<WaylandInputMethodContext> +WaylandInputMethodContextFactory::CreateWaylandInputMethodContext( + ui::LinuxInputMethodContextDelegate* delegate, + bool is_simple) const { + return std::make_unique<WaylandInputMethodContext>( + connection_, delegate, is_simple, + base::BindRepeating(&WaylandConnection::DispatchUiEvent, + base::Unretained(connection_))); +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/wayland_input_method_context_factory.h b/chromium/ui/ozone/platform/wayland/wayland_input_method_context_factory.h new file mode 100644 index 00000000000..413f3a4a500 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland_input_method_context_factory.h @@ -0,0 +1,38 @@ +// 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_OZONE_PLATFORM_WAYLAND_WAYLAND_INPUT_METHOD_CONTEXT_FACTORY_H_ +#define UI_OZONE_PLATFORM_WAYLAND_WAYLAND_INPUT_METHOD_CONTEXT_FACTORY_H_ + +#include "base/macros.h" +#include "ui/base/ime/linux/linux_input_method_context_factory.h" + +namespace ui { + +class WaylandConnection; +class WaylandInputMethodContext; + +class WaylandInputMethodContextFactory : public LinuxInputMethodContextFactory { + public: + explicit WaylandInputMethodContextFactory(WaylandConnection* connection); + ~WaylandInputMethodContextFactory() override; + + std::unique_ptr<LinuxInputMethodContext> CreateInputMethodContext( + ui::LinuxInputMethodContextDelegate* delegate, + bool is_simple) const override; + + // Exposed for unit tests but also called by CreateInputMethodContext + std::unique_ptr<WaylandInputMethodContext> CreateWaylandInputMethodContext( + ui::LinuxInputMethodContextDelegate* delegate, + bool is_simple) const; + + private: + WaylandConnection* connection_; + + DISALLOW_COPY_AND_ASSIGN(WaylandInputMethodContextFactory); +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_WAYLAND_WAYLAND_INPUT_METHOD_CONTEXT_FACTORY_H_ diff --git a/chromium/ui/ozone/platform/wayland/wayland_input_method_context_unittest.cc b/chromium/ui/ozone/platform/wayland/wayland_input_method_context_unittest.cc new file mode 100644 index 00000000000..666910c4110 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland_input_method_context_unittest.cc @@ -0,0 +1,140 @@ +// 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 <text-input-unstable-v1-server-protocol.h> +#include <wayland-server.h> + +#include "mojo/public/cpp/bindings/binding.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/ime/linux/linux_input_method_context.h" +#include "ui/events/event.h" +#include "ui/ozone/platform/wayland/fake_server.h" +#include "ui/ozone/platform/wayland/wayland_input_method_context.h" +#include "ui/ozone/platform/wayland/wayland_input_method_context_factory.h" +#include "ui/ozone/platform/wayland/wayland_test.h" +#include "ui/ozone/platform/wayland/wayland_window.h" + +using ::testing::SaveArg; +using ::testing::_; + +namespace ui { + +class TestInputMethodContextDelegate : public LinuxInputMethodContextDelegate { + public: + TestInputMethodContextDelegate() {} + ~TestInputMethodContextDelegate() override {} + + void OnCommit(const base::string16& text) override { + was_on_commit_called_ = true; + } + void OnPreeditChanged(const ui::CompositionText& composition_text) override { + was_on_preedit_changed_called_ = true; + } + void OnPreeditEnd() override {} + void OnPreeditStart() override {} + void OnDeleteSurroundingText(int32_t index, uint32_t length) override{}; + + bool was_on_commit_called() { return was_on_commit_called_; } + + bool was_on_preedit_changed_called() { + return was_on_preedit_changed_called_; + } + + private: + bool was_on_commit_called_ = false; + bool was_on_preedit_changed_called_ = false; + + DISALLOW_COPY_AND_ASSIGN(TestInputMethodContextDelegate); +}; + +class WaylandInputMethodContextTest : public WaylandTest { + public: + WaylandInputMethodContextTest() {} + + void SetUp() override { + WaylandTest::SetUp(); + + Sync(); + + input_method_context_delegate_ = + std::make_unique<TestInputMethodContextDelegate>(); + + WaylandInputMethodContextFactory factory(connection_.get()); + input_method_context_ = factory.CreateWaylandInputMethodContext( + input_method_context_delegate_.get(), false); + input_method_context_->Init(true); + connection_->ScheduleFlush(); + + Sync(); + + zwp_text_input_ = server_.text_input_manager_v1()->text_input.get(); + window_->set_keyboard_focus(true); + + ASSERT_TRUE(connection_->text_input_manager_v1()); + ASSERT_TRUE(zwp_text_input_); + } + + protected: + std::unique_ptr<TestInputMethodContextDelegate> + input_method_context_delegate_; + std::unique_ptr<WaylandInputMethodContext> input_method_context_; + wl::MockZwpTextInput* zwp_text_input_ = nullptr; + + private: + DISALLOW_COPY_AND_ASSIGN(WaylandInputMethodContextTest); +}; + +TEST_P(WaylandInputMethodContextTest, Focus) { + EXPECT_CALL(*zwp_text_input_, Activate(surface_->resource())); + EXPECT_CALL(*zwp_text_input_, ShowInputPanel()); + input_method_context_->Focus(); + connection_->ScheduleFlush(); + Sync(); +} + +TEST_P(WaylandInputMethodContextTest, Blur) { + EXPECT_CALL(*zwp_text_input_, Deactivate()); + EXPECT_CALL(*zwp_text_input_, HideInputPanel()); + input_method_context_->Blur(); + connection_->ScheduleFlush(); + Sync(); +} + +TEST_P(WaylandInputMethodContextTest, Reset) { + EXPECT_CALL(*zwp_text_input_, Reset()); + input_method_context_->Reset(); + connection_->ScheduleFlush(); + Sync(); +} + +TEST_P(WaylandInputMethodContextTest, SetCursorLocation) { + EXPECT_CALL(*zwp_text_input_, SetCursorRect(50, 0, 1, 1)); + input_method_context_->SetCursorLocation(gfx::Rect(50, 0, 1, 1)); + connection_->ScheduleFlush(); + Sync(); +} + +TEST_P(WaylandInputMethodContextTest, OnPreeditChanged) { + zwp_text_input_v1_send_preedit_string(zwp_text_input_->resource(), 0, + "PreeditString", ""); + Sync(); + EXPECT_TRUE(input_method_context_delegate_->was_on_preedit_changed_called()); +} + +TEST_P(WaylandInputMethodContextTest, OnCommit) { + zwp_text_input_v1_send_commit_string(zwp_text_input_->resource(), 0, + "CommitString"); + Sync(); + EXPECT_TRUE(input_method_context_delegate_->was_on_commit_called()); +} + +INSTANTIATE_TEST_CASE_P(XdgVersionV5Test, + WaylandInputMethodContextTest, + ::testing::Values(kXdgShellV5)); +INSTANTIATE_TEST_CASE_P(XdgVersionV6Test, + WaylandInputMethodContextTest, + ::testing::Values(kXdgShellV6)); + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/wayland_keyboard.cc b/chromium/ui/ozone/platform/wayland/wayland_keyboard.cc index 396c640ddf0..b6568596683 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_keyboard.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_keyboard.cc @@ -8,6 +8,7 @@ #include "base/files/scoped_file.h" #include "ui/base/ui_features.h" +#include "ui/events/base_event_utils.h" #include "ui/events/event.h" #include "ui/events/keycodes/dom/dom_code.h" #include "ui/events/keycodes/dom/keycode_converter.h" @@ -120,9 +121,8 @@ void WaylandKeyboard::Key(void* data, key, down, false /*suppress_auto_repeat*/, device_id); // TODO(tonikitoo,msisov): Handler 'repeat' parameter below. - keyboard->DispatchKey( - key, down, false /*repeat*/, - base::TimeTicks() + base::TimeDelta::FromMilliseconds(time), device_id); + keyboard->DispatchKey(key, down, false /*repeat*/, EventTimeForNow(), + device_id); } void WaylandKeyboard::Modifiers(void* data, diff --git a/chromium/ui/ozone/platform/wayland/wayland_native_display_delegate.cc b/chromium/ui/ozone/platform/wayland/wayland_native_display_delegate.cc deleted file mode 100644 index 029da54058b..00000000000 --- a/chromium/ui/ozone/platform/wayland/wayland_native_display_delegate.cc +++ /dev/null @@ -1,105 +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/ozone/platform/wayland/wayland_native_display_delegate.h" - -#include "ui/display/types/display_snapshot.h" -#include "ui/display/types/native_display_observer.h" -#include "ui/ozone/platform/wayland/wayland_connection.h" - -namespace ui { - -WaylandNativeDisplayDelegate::WaylandNativeDisplayDelegate( - WaylandConnection* connection) - : connection_(connection) {} - -WaylandNativeDisplayDelegate::~WaylandNativeDisplayDelegate() { - connection_->PrimaryOutput()->SetObserver(nullptr); -} - -void WaylandNativeDisplayDelegate::Initialize() { - // TODO(msisov): Add support for secondary output. - WaylandOutput* primary_output = connection_->PrimaryOutput(); - if (!primary_output) - NOTREACHED() << "Asynchronous display data fetching is not available"; - - primary_output->SetObserver(this); -} - -void WaylandNativeDisplayDelegate::TakeDisplayControl( - display::DisplayControlCallback callback) { - NOTREACHED(); -} - -void WaylandNativeDisplayDelegate::RelinquishDisplayControl( - display::DisplayControlCallback callback) { - NOTREACHED(); -} - -void WaylandNativeDisplayDelegate::GetDisplays( - display::GetDisplaysCallback callback) { - if (displays_ready_) - connection_->PrimaryOutput()->GetDisplaysSnapshot(std::move(callback)); -} - -void WaylandNativeDisplayDelegate::Configure( - const display::DisplaySnapshot& output, - const display::DisplayMode* mode, - const gfx::Point& origin, - display::ConfigureCallback callback) { - NOTREACHED(); -} - -void WaylandNativeDisplayDelegate::GetHDCPState( - const display::DisplaySnapshot& output, - display::GetHDCPStateCallback callback) { - NOTREACHED(); -} - -void WaylandNativeDisplayDelegate::SetHDCPState( - const display::DisplaySnapshot& output, - display::HDCPState state, - display::SetHDCPStateCallback callback) { - NOTREACHED(); -} - -bool WaylandNativeDisplayDelegate::SetColorMatrix( - int64_t display_id, - const std::vector<float>& color_matrix) { - NOTREACHED(); - return false; -} - -bool WaylandNativeDisplayDelegate::SetGammaCorrection( - int64_t display_id, - const std::vector<display::GammaRampRGBEntry>& degamma_lut, - const std::vector<display::GammaRampRGBEntry>& gamma_lut) { - NOTREACHED(); - return false; -} - -void WaylandNativeDisplayDelegate::AddObserver( - display::NativeDisplayObserver* observer) { - observers_.AddObserver(observer); -} - -void WaylandNativeDisplayDelegate::RemoveObserver( - display::NativeDisplayObserver* observer) { - observers_.RemoveObserver(observer); -} - -display::FakeDisplayController* -WaylandNativeDisplayDelegate::GetFakeDisplayController() { - return nullptr; -} - -void WaylandNativeDisplayDelegate::OnOutputReadyForUse() { - if (!displays_ready_) - displays_ready_ = true; - - for (display::NativeDisplayObserver& observer : observers_) - observer.OnConfigurationChanged(); -} - -} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/wayland_native_display_delegate.h b/chromium/ui/ozone/platform/wayland/wayland_native_display_delegate.h deleted file mode 100644 index e0559cb3e59..00000000000 --- a/chromium/ui/ozone/platform/wayland/wayland_native_display_delegate.h +++ /dev/null @@ -1,65 +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_OZONE_PLATFORM_WAYLAND_WAYLAND_NATIVE_DISPLAY_DELEGATE_H_ -#define UI_OZONE_PLATFORM_WAYLAND_WAYLAND_NATIVE_DISPLAY_DELEGATE_H_ - -#include <stdint.h> - -#include "base/macros.h" -#include "base/observer_list.h" -#include "ui/display/types/native_display_delegate.h" -#include "ui/ozone/platform/wayland/wayland_output.h" - -namespace ui { - -class WaylandConnection; - -class WaylandNativeDisplayDelegate : public display::NativeDisplayDelegate, - public WaylandOutput::Observer { - public: - explicit WaylandNativeDisplayDelegate(WaylandConnection* connection); - ~WaylandNativeDisplayDelegate() override; - - // display::NativeDisplayDelegate overrides: - void Initialize() override; - void TakeDisplayControl(display::DisplayControlCallback callback) override; - void RelinquishDisplayControl( - display::DisplayControlCallback callback) override; - void GetDisplays(display::GetDisplaysCallback callback) override; - void Configure(const display::DisplaySnapshot& output, - const display::DisplayMode* mode, - const gfx::Point& origin, - display::ConfigureCallback callback) override; - void GetHDCPState(const display::DisplaySnapshot& output, - display::GetHDCPStateCallback callback) override; - void SetHDCPState(const display::DisplaySnapshot& output, - display::HDCPState state, - display::SetHDCPStateCallback callback) override; - bool SetColorMatrix(int64_t display_id, - const std::vector<float>& color_matrix) override; - bool SetGammaCorrection( - int64_t display_id, - const std::vector<display::GammaRampRGBEntry>& degamma_lut, - const std::vector<display::GammaRampRGBEntry>& gamma_lut) override; - void AddObserver(display::NativeDisplayObserver* observer) override; - void RemoveObserver(display::NativeDisplayObserver* observer) override; - display::FakeDisplayController* GetFakeDisplayController() override; - - // WaylandOutput::Observer overrides: - void OnOutputReadyForUse() override; - - private: - WaylandConnection* connection_; // Not owned. - - base::ObserverList<display::NativeDisplayObserver>::Unchecked observers_; - - bool displays_ready_ = false; - - DISALLOW_COPY_AND_ASSIGN(WaylandNativeDisplayDelegate); -}; - -} // namespace ui - -#endif // UI_OZONE_PLATFORM_WAYLAND_WAYLAND_NATIVE_DISPLAY_DELEGATE_H_ diff --git a/chromium/ui/ozone/platform/wayland/wayland_object.cc b/chromium/ui/ozone/platform/wayland/wayland_object.cc index ec41d326824..ed4de394dd0 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_object.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_object.cc @@ -5,6 +5,8 @@ #include "ui/ozone/platform/wayland/wayland_object.h" #include <linux-dmabuf-unstable-v1-client-protocol.h> +#include <presentation-time-client-protocol.h> +#include <text-input-unstable-v1-client-protocol.h> #include <wayland-client.h> #include <xdg-shell-unstable-v5-client-protocol.h> #include <xdg-shell-unstable-v6-client-protocol.h> @@ -40,6 +42,15 @@ void delete_touch(wl_touch* touch) { wl_touch_destroy(touch); } +void delete_data_device(wl_data_device* data_device) { + if (wl_data_device_get_version(data_device) >= + WL_DATA_DEVICE_RELEASE_SINCE_VERSION) { + wl_data_device_release(data_device); + } else { + wl_data_device_destroy(data_device); + } +} + } // namespace const wl_interface* ObjectTraits<wl_buffer>::interface = &wl_buffer_interface; @@ -62,7 +73,7 @@ void (*ObjectTraits<wl_data_device_manager>::deleter)(wl_data_device_manager*) = const wl_interface* ObjectTraits<wl_data_device>::interface = &wl_data_device_interface; void (*ObjectTraits<wl_data_device>::deleter)(wl_data_device*) = - &wl_data_device_destroy; + &delete_data_device; const wl_interface* ObjectTraits<wl_data_offer>::interface = &wl_data_offer_interface; @@ -117,6 +128,16 @@ void (*ObjectTraits<wl_subsurface>::deleter)(wl_subsurface*) = const wl_interface* ObjectTraits<wl_touch>::interface = &wl_touch_interface; void (*ObjectTraits<wl_touch>::deleter)(wl_touch*) = &delete_touch; +const wl_interface* ObjectTraits<wp_presentation>::interface = + &wp_presentation_interface; +void (*ObjectTraits<wp_presentation>::deleter)(wp_presentation*) = + &wp_presentation_destroy; + +const wl_interface* ObjectTraits<struct wp_presentation_feedback>::interface = + &wp_presentation_feedback_interface; +void (*ObjectTraits<struct wp_presentation_feedback>::deleter)( + struct wp_presentation_feedback*) = &wp_presentation_feedback_destroy; + const wl_interface* ObjectTraits<xdg_shell>::interface = &xdg_shell_interface; void (*ObjectTraits<xdg_shell>::deleter)(xdg_shell*) = &xdg_shell_destroy; @@ -157,4 +178,14 @@ const wl_interface* ObjectTraits<zxdg_positioner_v6>::interface = void (*ObjectTraits<zxdg_positioner_v6>::deleter)(zxdg_positioner_v6*) = &zxdg_positioner_v6_destroy; +const wl_interface* ObjectTraits<zwp_text_input_manager_v1>::interface = + &zwp_text_input_manager_v1_interface; +void (*ObjectTraits<zwp_text_input_manager_v1>::deleter)( + zwp_text_input_manager_v1*) = &zwp_text_input_manager_v1_destroy; + +const wl_interface* ObjectTraits<zwp_text_input_v1>::interface = + &zwp_text_input_v1_interface; +void (*ObjectTraits<zwp_text_input_v1>::deleter)(zwp_text_input_v1*) = + &zwp_text_input_v1_destroy; + } // namespace wl diff --git a/chromium/ui/ozone/platform/wayland/wayland_object.h b/chromium/ui/ozone/platform/wayland/wayland_object.h index df3f16e62f3..34984d3296a 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_object.h +++ b/chromium/ui/ozone/platform/wayland/wayland_object.h @@ -27,6 +27,8 @@ struct wl_subcompositor; struct wl_subsurface; struct wl_surface; struct wl_touch; +struct wp_presentation; +struct wp_presentation_feedback; struct xdg_shell; struct xdg_surface; struct xdg_popup; @@ -36,6 +38,8 @@ struct zxdg_surface_v6; struct zxdg_toplevel_v6; struct zxdg_popup_v6; struct zxdg_positioner_v6; +struct zwp_text_input_manager_v1; +struct zwp_text_input_v1; namespace wl { @@ -157,6 +161,18 @@ struct ObjectTraits<wl_touch> { }; template <> +struct ObjectTraits<wp_presentation> { + static const wl_interface* interface; + static void (*deleter)(wp_presentation*); +}; + +template <> +struct ObjectTraits<wp_presentation_feedback> { + static const wl_interface* interface; + static void (*deleter)(wp_presentation_feedback*); +}; + +template <> struct ObjectTraits<xdg_shell> { static const wl_interface* interface; static void (*deleter)(xdg_shell*); @@ -210,6 +226,18 @@ struct ObjectTraits<zxdg_positioner_v6> { static void (*deleter)(zxdg_positioner_v6*); }; +template <> +struct ObjectTraits<zwp_text_input_manager_v1> { + static const wl_interface* interface; + static void (*deleter)(zwp_text_input_manager_v1*); +}; + +template <> +struct ObjectTraits<zwp_text_input_v1> { + static const wl_interface* interface; + static void (*deleter)(zwp_text_input_v1*); +}; + struct Deleter { template <typename T> void operator()(T* obj) { diff --git a/chromium/ui/ozone/platform/wayland/wayland_output.cc b/chromium/ui/ozone/platform/wayland/wayland_output.cc index d9f8c44fd6c..fd14f45d633 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_output.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_output.cc @@ -11,26 +11,32 @@ namespace ui { -WaylandOutput::WaylandOutput(const int64_t display_id, wl_output* output) - : display_id_(display_id), output_(output), observer_(nullptr) { - static const wl_output_listener output_listener = { - &WaylandOutput::OutputHandleGeometry, &WaylandOutput::OutputHandleMode, - }; - wl_output_add_listener(output, &output_listener, this); +namespace { +constexpr float kDefaultScaleFactor = 1.0f; } -WaylandOutput::~WaylandOutput() {} +WaylandOutput::WaylandOutput(const uint32_t output_id, wl_output* output) + : output_id_(output_id), + output_(output), + device_scale_factor_(kDefaultScaleFactor), + rect_in_physical_pixels_(gfx::Rect()) {} + +WaylandOutput::~WaylandOutput() = default; -void WaylandOutput::SetObserver(Observer* observer) { - observer_ = observer; - if (current_mode_) - observer_->OnOutputReadyForUse(); +void WaylandOutput::Initialize(Delegate* delegate) { + DCHECK(!delegate_); + delegate_ = delegate; + static const wl_output_listener output_listener = { + &WaylandOutput::OutputHandleGeometry, &WaylandOutput::OutputHandleMode, + &WaylandOutput::OutputHandleDone, &WaylandOutput::OutputHandleScale, + }; + wl_output_add_listener(output_.get(), &output_listener, this); } -void WaylandOutput::GetDisplaysSnapshot(display::GetDisplaysCallback callback) { - std::vector<display::DisplaySnapshot*> snapshot; - snapshot.push_back(current_snapshot_.get()); - std::move(callback).Run(snapshot); +void WaylandOutput::TriggerDelegateNotification() const { + DCHECK(!rect_in_physical_pixels_.IsEmpty()); + delegate_->OnOutputHandleMetrics(output_id_, rect_in_physical_pixels_, + device_scale_factor_); } // static @@ -45,13 +51,8 @@ void WaylandOutput::OutputHandleGeometry(void* data, const char* model, int32_t output_transform) { WaylandOutput* wayland_output = static_cast<WaylandOutput*>(data); - wayland_output->current_snapshot_.reset(new display::DisplaySnapshot( - wayland_output->display_id_, gfx::Point(x, y), - gfx::Size(physical_width, physical_height), - display::DisplayConnectionType::DISPLAY_CONNECTION_TYPE_NONE, false, - false, false, false, gfx::ColorSpace(), model, base::FilePath(), - display::DisplaySnapshot::DisplayModeList(), std::vector<uint8_t>(), - nullptr, nullptr, 0, 0, gfx::Size())); + if (wayland_output) + wayland_output->rect_in_physical_pixels_.set_origin(gfx::Point(x, y)); } // static @@ -61,17 +62,27 @@ void WaylandOutput::OutputHandleMode(void* data, int32_t width, int32_t height, int32_t refresh) { - WaylandOutput* output = static_cast<WaylandOutput*>(data); + WaylandOutput* wayland_output = static_cast<WaylandOutput*>(data); + if (wayland_output && (flags & WL_OUTPUT_MODE_CURRENT)) { + wayland_output->rect_in_physical_pixels_.set_width(width); + wayland_output->rect_in_physical_pixels_.set_height(height); + wayland_output->TriggerDelegateNotification(); + } +} - if (flags & WL_OUTPUT_MODE_CURRENT) { - std::unique_ptr<display::DisplayMode> previous_mode = - std::move(output->current_mode_); - output->current_mode_.reset( - new display::DisplayMode(gfx::Size(width, height), false, refresh)); - output->current_snapshot_->set_current_mode(output->current_mode_.get()); +// static +void WaylandOutput::OutputHandleDone(void* data, struct wl_output* wl_output) { + NOTIMPLEMENTED_LOG_ONCE(); +} - if (output->observer()) - output->observer()->OnOutputReadyForUse(); +// static +void WaylandOutput::OutputHandleScale(void* data, + struct wl_output* wl_output, + int32_t factor) { + WaylandOutput* wayland_output = static_cast<WaylandOutput*>(data); + if (wayland_output) { + wayland_output->device_scale_factor_ = factor; + wayland_output->TriggerDelegateNotification(); } } diff --git a/chromium/ui/ozone/platform/wayland/wayland_output.h b/chromium/ui/ozone/platform/wayland/wayland_output.h index 94088ca16ab..ddf7946b194 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_output.h +++ b/chromium/ui/ozone/platform/wayland/wayland_output.h @@ -18,21 +18,27 @@ namespace ui { // that are available to the application. class WaylandOutput { public: - class Observer { + class Delegate { public: - // Will be called when wl_output is available. - virtual void OnOutputReadyForUse() = 0; + virtual ~Delegate() {} + + virtual void OnOutputHandleMetrics(uint32_t output_id, + const gfx::Rect& new_bounds, + int32_t scale_factor) = 0; }; - WaylandOutput(const int64_t display_id, wl_output* output); + WaylandOutput(const uint32_t output_id, wl_output* output); ~WaylandOutput(); - void SetObserver(Observer* observer); - Observer* observer() { return observer_; } + void Initialize(Delegate* delegate); + + void TriggerDelegateNotification() const; - bool is_ready() const { return !!current_mode_; } + uint32_t output_id() const { return output_id_; } - void GetDisplaysSnapshot(display::GetDisplaysCallback callback); + // Tells if the output has already received physical screen dimensions in the + // global compositor space. + bool is_ready() const { return !rect_in_physical_pixels_.IsEmpty(); } private: // Callback functions used for setting geometric properties of the output @@ -54,14 +60,17 @@ class WaylandOutput { int32_t width, int32_t height, int32_t refresh); + static void OutputHandleDone(void* data, struct wl_output* wl_output); + static void OutputHandleScale(void* data, + struct wl_output* wl_output, + int32_t factor); - const int64_t display_id_ = 0; + const uint32_t output_id_ = 0; wl::Object<wl_output> output_; + float device_scale_factor_; + gfx::Rect rect_in_physical_pixels_; - Observer* observer_; - - std::unique_ptr<display::DisplaySnapshot> current_snapshot_; - std::unique_ptr<display::DisplayMode> current_mode_; + Delegate* delegate_ = nullptr; DISALLOW_COPY_AND_ASSIGN(WaylandOutput); }; diff --git a/chromium/ui/ozone/platform/wayland/wayland_output_manager.cc b/chromium/ui/ozone/platform/wayland/wayland_output_manager.cc new file mode 100644 index 00000000000..c9db25c35b4 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland_output_manager.cc @@ -0,0 +1,117 @@ +// 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/ozone/platform/wayland/wayland_output_manager.h" + +#include "ui/ozone/platform/wayland/wayland_connection.h" +#include "ui/ozone/platform/wayland/wayland_output.h" + +namespace ui { + +WaylandOutputManager::WaylandOutputManager() = default; + +WaylandOutputManager::~WaylandOutputManager() = default; + +bool WaylandOutputManager::IsPrimaryOutputReady() const { + if (output_list_.empty()) + return false; + + // The very first output in the list is always treated as a primary output. + const auto& primary_output = output_list_.front(); + return primary_output->is_ready(); +} + +void WaylandOutputManager::AddWaylandOutput(const uint32_t output_id, + wl_output* output) { + // Make sure an output with |output_id| has not been added yet. It's very + // unlikely to happen, unless a compositor has a bug in the numeric names + // representation of global objects. + auto output_it = std::find_if(output_list_.begin(), output_list_.end(), + [output_id](const auto& output) { + return output->output_id() == output_id; + }); + DCHECK(output_it == output_list_.end()); + auto wayland_output = std::make_unique<WaylandOutput>(output_id, output); + WaylandOutput* wayland_output_ptr = wayland_output.get(); + output_list_.push_back(std::move(wayland_output)); + + OnWaylandOutputAdded(output_id); + + // If WaylandScreen has already been created, the output can be initialized, + // which results in setting up a wl_listener and getting the geometry and the + // scaling factor from the Wayland Compositor. + wayland_output_ptr->Initialize(this); +} + +void WaylandOutputManager::RemoveWaylandOutput(const uint32_t output_id) { + auto output_it = std::find_if(output_list_.begin(), output_list_.end(), + [output_id](const auto& output) { + return output->output_id() == output_id; + }); + + // Check the comment in the WaylandConnetion::GlobalRemove. + if (output_it == output_list_.end()) + return; + + bool was_primary_output = IsPrimaryOutput(output_id); + output_list_.erase(output_it); + + // If it was a primary output removed, make sure the second output, which + // became a primary one, announces that to observers. + if (was_primary_output && !output_list_.empty()) + output_list_.front()->TriggerDelegateNotification(); + + OnWaylandOutputRemoved(output_id); +} + +std::unique_ptr<WaylandScreen> WaylandOutputManager::CreateWaylandScreen() { + auto wayland_screen = std::make_unique<WaylandScreen>(); + wayland_screen_ = wayland_screen->GetWeakPtr(); + + // As long as |wl_output| sends geometry and other events asynchronously (that + // is, the initial configuration is sent once the interface is bound), we'll + // have to tell each output to manually inform the delegate about available + // geometry, scale factor and etc, which will result in feeding the + // WaylandScreen with the data through OnOutputHandleGeometry and + // OutOutputHandleScale. All the other hot geometry and scale changes are done + // automatically, and the |wayland_screen_| is notified immediately about the + // changes. + if (!output_list_.empty()) { + for (auto& output : output_list_) { + OnWaylandOutputAdded(output->output_id()); + output->TriggerDelegateNotification(); + } + } + + return wayland_screen; +} + +void WaylandOutputManager::OnWaylandOutputAdded(uint32_t output_id) { + if (wayland_screen_) + wayland_screen_->OnOutputAdded(output_id, IsPrimaryOutput(output_id)); +} + +void WaylandOutputManager::OnWaylandOutputRemoved(uint32_t output_id) { + if (wayland_screen_) + wayland_screen_->OnOutputRemoved(output_id); +} + +bool WaylandOutputManager::IsPrimaryOutput(uint32_t output_id) const { + DCHECK(!output_list_.empty()); + // The very first object in the |output_list_| is always treated as a primary + // output. + const auto& primary_output = output_list_.front(); + return primary_output->output_id() == output_id; +} + +void WaylandOutputManager::OnOutputHandleMetrics(uint32_t output_id, + const gfx::Rect& new_bounds, + int32_t scale_factor) { + if (wayland_screen_) { + wayland_screen_->OnOutputMetricsChanged(output_id, new_bounds, scale_factor, + IsPrimaryOutput(output_id)); + } +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/wayland_output_manager.h b/chromium/ui/ozone/platform/wayland/wayland_output_manager.h new file mode 100644 index 00000000000..5fcdced8a04 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland_output_manager.h @@ -0,0 +1,59 @@ +// 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_OZONE_PLATFORM_WAYLAND_WAYLAND_OUTPUT_MANAGER_H_ +#define UI_OZONE_PLATFORM_WAYLAND_WAYLAND_OUTPUT_MANAGER_H_ + +#include "ui/ozone/platform/wayland/wayland_object.h" + +#include <memory> +#include <vector> + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "ui/ozone/platform/wayland/wayland_output.h" +#include "ui/ozone/platform/wayland/wayland_screen.h" + +struct wl_output; + +namespace ui { + +class WaylandOutput; + +class WaylandOutputManager : public WaylandOutput::Delegate { + public: + WaylandOutputManager(); + ~WaylandOutputManager() override; + + // The first output in the vector is always a primary output. + bool IsPrimaryOutputReady() const; + + void AddWaylandOutput(const uint32_t output_id, wl_output* output); + void RemoveWaylandOutput(const uint32_t output_id); + + // Creates a platform screen and feeds it with existing outputs. + std::unique_ptr<WaylandScreen> CreateWaylandScreen(); + + private: + void OnWaylandOutputAdded(uint32_t output_id); + void OnWaylandOutputRemoved(uint32_t output_id); + + bool IsPrimaryOutput(uint32_t output_id) const; + + // WaylandOutput::Delegate: + void OnOutputHandleMetrics(uint32_t output_id, + const gfx::Rect& new_bounds, + int32_t scale_factor) override; + + std::vector<std::unique_ptr<WaylandOutput>> output_list_; + + // Non-owned wayland screen instance. + base::WeakPtr<WaylandScreen> wayland_screen_; + + DISALLOW_COPY_AND_ASSIGN(WaylandOutputManager); +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_WAYLAND_WAYLAND_OUTPUT_MANAGER_H_ diff --git a/chromium/ui/ozone/platform/wayland/wayland_pointer.cc b/chromium/ui/ozone/platform/wayland/wayland_pointer.cc index 8079524d6d9..f977f636681 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_pointer.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_pointer.cc @@ -94,8 +94,8 @@ void WaylandPointer::Motion(void* data, pointer->location_.SetPoint(wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y)); MouseEvent event(ET_MOUSE_MOVED, gfx::Point(), gfx::Point(), - base::TimeTicks() + base::TimeDelta::FromMilliseconds(time), - pointer->GetFlagsWithKeyboardModifiers(), 0); + EventTimeForNow(), pointer->GetFlagsWithKeyboardModifiers(), + 0); event.set_location_f(pointer->location_); event.set_root_location_f(pointer->location_); pointer->callback_.Run(&event); @@ -146,10 +146,8 @@ void WaylandPointer::Button(void* data, // MouseEvent's flags should contain the button that was released too. const int flags = pointer->GetFlagsWithKeyboardModifiers() | changed_button; - MouseEvent event(type, gfx::Point(), gfx::Point(), - base::TimeTicks() + base::TimeDelta::FromMilliseconds(time), - flags, changed_button); - + MouseEvent event(type, gfx::Point(), gfx::Point(), EventTimeForNow(), flags, + changed_button); event.set_location_f(pointer->location_); event.set_root_location_f(pointer->location_); @@ -187,10 +185,8 @@ void WaylandPointer::Axis(void* data, MouseWheelEvent::kWheelDelta); else return; - MouseWheelEvent event( - offset, gfx::Point(), gfx::Point(), - base::TimeTicks() + base::TimeDelta::FromMilliseconds(time), - pointer->GetFlagsWithKeyboardModifiers(), 0); + MouseWheelEvent event(offset, gfx::Point(), gfx::Point(), EventTimeForNow(), + pointer->GetFlagsWithKeyboardModifiers(), 0); event.set_location_f(pointer->location_); event.set_root_location_f(pointer->location_); pointer->callback_.Run(&event); @@ -216,4 +212,9 @@ int WaylandPointer::GetFlagsWithKeyboardModifiers() { return flags_; } +void WaylandPointer::ResetFlags() { + flags_ = 0; + keyboard_modifiers_ = 0; +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/wayland_pointer.h b/chromium/ui/ozone/platform/wayland/wayland_pointer.h index 50f60f4a903..0c398d19a83 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_pointer.h +++ b/chromium/ui/ozone/platform/wayland/wayland_pointer.h @@ -27,6 +27,7 @@ class WaylandPointer { } int GetFlagsWithKeyboardModifiers(); + void ResetFlags(); WaylandCursor* cursor() { return cursor_.get(); } diff --git a/chromium/ui/ozone/platform/wayland/wayland_screen.cc b/chromium/ui/ozone/platform/wayland/wayland_screen.cc new file mode 100644 index 00000000000..98ef35e3f61 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland_screen.cc @@ -0,0 +1,107 @@ +// 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/ozone/platform/wayland/wayland_screen.h" + +#include "ui/display/display.h" +#include "ui/display/display_finder.h" +#include "ui/display/display_observer.h" +#include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/size.h" + +namespace ui { + +WaylandScreen::WaylandScreen() : weak_factory_(this) {} + +WaylandScreen::~WaylandScreen() = default; + +void WaylandScreen::OnOutputAdded(uint32_t output_id, bool is_primary) { + display::Display new_display(output_id); + display_list_.AddDisplay(std::move(new_display), + is_primary + ? display::DisplayList::Type::PRIMARY + : display::DisplayList::Type::NOT_PRIMARY); +} + +void WaylandScreen::OnOutputRemoved(uint32_t output_id) { + display_list_.RemoveDisplay(output_id); +} + +void WaylandScreen::OnOutputMetricsChanged(uint32_t output_id, + const gfx::Rect& new_bounds, + float device_pixel_ratio, + bool is_primary) { + display::Display changed_display(output_id); + changed_display.set_device_scale_factor(device_pixel_ratio); + changed_display.set_bounds(new_bounds); + changed_display.set_work_area(new_bounds); + + display_list_.UpdateDisplay( + changed_display, is_primary ? display::DisplayList::Type::PRIMARY + : display::DisplayList::Type::NOT_PRIMARY); +} + +base::WeakPtr<WaylandScreen> WaylandScreen::GetWeakPtr() { + return weak_factory_.GetWeakPtr(); +} + +const std::vector<display::Display>& WaylandScreen::GetAllDisplays() const { + return display_list_.displays(); +} + +display::Display WaylandScreen::GetPrimaryDisplay() const { + auto iter = display_list_.GetPrimaryDisplayIterator(); + if (iter == display_list_.displays().end()) + return display::Display::GetDefaultDisplay(); + return *iter; +} + +display::Display WaylandScreen::GetDisplayForAcceleratedWidget( + gfx::AcceleratedWidget widget) const { + // TODO(msisov): implement wl_surface_listener::enter and + // wl_surface_listener::leave for a wl_surface to know what surface the window + // is located on. + // + // https://crbug.com/890271 + NOTIMPLEMENTED_LOG_ONCE(); + return GetPrimaryDisplay(); +} + +gfx::Point WaylandScreen::GetCursorScreenPoint() const { + NOTIMPLEMENTED_LOG_ONCE(); + return gfx::Point(); +} + +gfx::AcceleratedWidget WaylandScreen::GetAcceleratedWidgetAtScreenPoint( + const gfx::Point& point) const { + // TODO(msisov): implement this once wl_surface_listener::enter and ::leave + // are used. + // + // https://crbug.com/890271 + NOTIMPLEMENTED_LOG_ONCE(); + return gfx::kNullAcceleratedWidget; +} + +display::Display WaylandScreen::GetDisplayNearestPoint( + const gfx::Point& point) const { + NOTIMPLEMENTED_LOG_ONCE(); + return GetPrimaryDisplay(); +} + +display::Display WaylandScreen::GetDisplayMatching( + const gfx::Rect& match_rect) const { + // TODO(msisov): https://crbug.com/890272 + NOTIMPLEMENTED_LOG_ONCE(); + return GetPrimaryDisplay(); +} + +void WaylandScreen::AddObserver(display::DisplayObserver* observer) { + display_list_.AddObserver(observer); +} + +void WaylandScreen::RemoveObserver(display::DisplayObserver* observer) { + display_list_.RemoveObserver(observer); +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/wayland_screen.h b/chromium/ui/ozone/platform/wayland/wayland_screen.h new file mode 100644 index 00000000000..f2de8d4dbb9 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland_screen.h @@ -0,0 +1,62 @@ +// 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_OZONE_PLATFORM_WAYLAND_WAYLAND_SCREEN_H_ +#define UI_OZONE_PLATFORM_WAYLAND_WAYLAND_SCREEN_H_ + +#include <vector> + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "base/observer_list.h" +#include "ui/display/display_list.h" +#include "ui/ozone/platform/wayland/wayland_output.h" +#include "ui/ozone/public/ozone_platform.h" +#include "ui/ozone/public/platform_screen.h" + +namespace ui { + +// A PlatformScreen implementation for Wayland. +class WaylandScreen : public PlatformScreen { + public: + WaylandScreen(); + ~WaylandScreen() override; + + void OnOutputAdded(uint32_t output_id, bool is_primary); + void OnOutputRemoved(uint32_t output_id); + void OnOutputMetricsChanged(uint32_t output_id, + const gfx::Rect& bounds, + float device_pixel_ratio, + bool is_primary); + + base::WeakPtr<WaylandScreen> GetWeakPtr(); + + // display::Screen implementation. + const std::vector<display::Display>& GetAllDisplays() const override; + display::Display GetPrimaryDisplay() const override; + display::Display GetDisplayForAcceleratedWidget( + gfx::AcceleratedWidget widget) const override; + gfx::Point GetCursorScreenPoint() const override; + gfx::AcceleratedWidget GetAcceleratedWidgetAtScreenPoint( + const gfx::Point& point) const override; + display::Display GetDisplayNearestPoint( + const gfx::Point& point) const override; + display::Display GetDisplayMatching( + const gfx::Rect& match_rect) const override; + void AddObserver(display::DisplayObserver* observer) override; + void RemoveObserver(display::DisplayObserver* observer) override; + + private: + display::DisplayList display_list_; + + base::ObserverList<display::DisplayObserver> observers_; + + base::WeakPtrFactory<WaylandScreen> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(WaylandScreen); +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_WAYLAND_WAYLAND_SCREEN_H_ diff --git a/chromium/ui/ozone/platform/wayland/wayland_screen_unittest.cc b/chromium/ui/ozone/platform/wayland/wayland_screen_unittest.cc new file mode 100644 index 00000000000..7eb9350f298 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/wayland_screen_unittest.cc @@ -0,0 +1,187 @@ +// 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 <wayland-server.h> + +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/display/display_observer.h" +#include "ui/ozone/platform/wayland/fake_server.h" +#include "ui/ozone/platform/wayland/wayland_connection.h" +#include "ui/ozone/platform/wayland/wayland_output_manager.h" +#include "ui/ozone/platform/wayland/wayland_screen.h" +#include "ui/ozone/platform/wayland/wayland_test.h" + +namespace ui { + +namespace { + +constexpr uint32_t kNumberOfDisplays = 1; +constexpr uint32_t kOutputWidth = 1024; +constexpr uint32_t kOutputHeight = 768; + +class TestDisplayObserver : public display::DisplayObserver { + public: + TestDisplayObserver() {} + ~TestDisplayObserver() override {} + + display::Display GetDisplay() { return std::move(display_); } + uint32_t GetAndClearChangedMetrics() { + uint32_t changed_metrics = changed_metrics_; + changed_metrics_ = 0; + return changed_metrics; + } + + // display::DisplayObserver: + void OnDisplayAdded(const display::Display& new_display) override { + display_ = new_display; + } + + void OnDisplayRemoved(const display::Display& old_display) override { + display_ = old_display; + } + + void OnDisplayMetricsChanged(const display::Display& display, + uint32_t changed_metrics) override { + changed_metrics_ = changed_metrics; + display_ = display; + } + + private: + uint32_t changed_metrics_ = 0; + display::Display display_; + + DISALLOW_COPY_AND_ASSIGN(TestDisplayObserver); +}; + +} // namespace + +class WaylandScreenTest : public WaylandTest { + public: + WaylandScreenTest() {} + ~WaylandScreenTest() override {} + + void SetUp() override { + output_ = server_.output(); + output_->SetRect(gfx::Rect(0, 0, kOutputWidth, kOutputHeight)); + + WaylandTest::SetUp(); + + output_manager_ = connection_->wayland_output_manager(); + ASSERT_TRUE(output_manager_); + } + + protected: + wl::MockOutput* output_ = nullptr; + WaylandOutputManager* output_manager_ = nullptr; + + private: + DISALLOW_COPY_AND_ASSIGN(WaylandScreenTest); +}; + +// Tests whether a primary output has been initialized before PlatformScreen is +// created. +TEST_P(WaylandScreenTest, OutputBaseTest) { + EXPECT_TRUE(output_manager_->IsPrimaryOutputReady()); + + std::unique_ptr<WaylandScreen> platform_screen = + output_manager_->CreateWaylandScreen(); + + // Ensure there is only one display, which is the primary one. + auto& all_displays = platform_screen->GetAllDisplays(); + EXPECT_EQ(all_displays.size(), kNumberOfDisplays); + + // Ensure the size property of the primary display. + EXPECT_EQ(platform_screen->GetPrimaryDisplay().bounds(), + gfx::Rect(0, 0, kOutputWidth, kOutputHeight)); +} + +TEST_P(WaylandScreenTest, MultipleOutputsAddedAndRemoved) { + EXPECT_TRUE(output_manager_->IsPrimaryOutputReady()); + std::unique_ptr<WaylandScreen> platform_screen = + output_manager_->CreateWaylandScreen(); + + TestDisplayObserver observer; + platform_screen->AddObserver(&observer); + + // Add a second display. + server_.CreateAndInitializeOutput(); + + Sync(); + + // Ensure that second display is not a primary one and have a different id. + int64_t added_display_id = observer.GetDisplay().id(); + EXPECT_NE(platform_screen->GetPrimaryDisplay().id(), added_display_id); + + // Remove the second output. + output_manager_->RemoveWaylandOutput(added_display_id); + + Sync(); + + // Ensure that removed display has correct id. + int64_t removed_display_id = observer.GetDisplay().id(); + EXPECT_EQ(added_display_id, removed_display_id); + + // Create another display again. + server_.CreateAndInitializeOutput(); + + Sync(); + + // The newly added display is not a primary yet. + added_display_id = observer.GetDisplay().id(); + EXPECT_NE(platform_screen->GetPrimaryDisplay().id(), added_display_id); + + // Make sure the geometry changes are sent by syncing one more time again. + Sync(); + + int64_t old_primary_display_id = platform_screen->GetPrimaryDisplay().id(); + output_manager_->RemoveWaylandOutput(old_primary_display_id); + + // Ensure that previously added display is now a primary one. + EXPECT_EQ(platform_screen->GetPrimaryDisplay().id(), added_display_id); + // Ensure that the removed display was the one, which was a primary display. + EXPECT_EQ(observer.GetDisplay().id(), old_primary_display_id); +} + +TEST_P(WaylandScreenTest, OutputPropertyChanges) { + std::unique_ptr<WaylandScreen> platform_screen = + output_manager_->CreateWaylandScreen(); + TestDisplayObserver observer; + platform_screen->AddObserver(&observer); + + const gfx::Rect new_rect(0, 0, 800, 600); + wl_output_send_geometry(output_->resource(), new_rect.x(), new_rect.y(), + 0 /* physical_width */, 0 /* physical_height */, + 0 /* subpixel */, "unkown_make", "unknown_model", + 0 /* transform */); + wl_output_send_mode(output_->resource(), WL_OUTPUT_MODE_CURRENT, + new_rect.width(), new_rect.height(), 0 /* refresh */); + + Sync(); + + uint32_t changed_values = 0; + changed_values |= display::DisplayObserver::DISPLAY_METRIC_BOUNDS; + changed_values |= display::DisplayObserver::DISPLAY_METRIC_WORK_AREA; + EXPECT_EQ(observer.GetAndClearChangedMetrics(), changed_values); + EXPECT_EQ(observer.GetDisplay().bounds(), new_rect); + + const float new_scale_value = 2.0f; + wl_output_send_scale(output_->resource(), new_scale_value); + + Sync(); + + changed_values = 0; + changed_values |= + display::DisplayObserver::DISPLAY_METRIC_DEVICE_SCALE_FACTOR; + EXPECT_EQ(observer.GetAndClearChangedMetrics(), changed_values); + EXPECT_EQ(observer.GetDisplay().device_scale_factor(), new_scale_value); +} + +INSTANTIATE_TEST_CASE_P(XdgVersionV5Test, + WaylandScreenTest, + ::testing::Values(kXdgShellV5)); +INSTANTIATE_TEST_CASE_P(XdgVersionV6Test, + WaylandScreenTest, + ::testing::Values(kXdgShellV6)); + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/wayland_surface_factory.cc b/chromium/ui/ozone/platform/wayland/wayland_surface_factory.cc index 751d7c2913d..a5b1dec0d3b 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_surface_factory.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_surface_factory.cc @@ -231,9 +231,13 @@ GbmSurfacelessWayland* WaylandSurfaceFactory::GetSurface( return it->second; } -void WaylandSurfaceFactory::ScheduleBufferSwap(gfx::AcceleratedWidget widget, - uint32_t buffer_id) { - connection_->ScheduleBufferSwap(widget, buffer_id); +void WaylandSurfaceFactory::ScheduleBufferSwap( + gfx::AcceleratedWidget widget, + uint32_t buffer_id, + const gfx::Rect& damage_region, + wl::BufferSwapCallback callback) { + connection_->ScheduleBufferSwap(widget, buffer_id, damage_region, + std::move(callback)); } std::unique_ptr<SurfaceOzoneCanvas> diff --git a/chromium/ui/ozone/platform/wayland/wayland_surface_factory.h b/chromium/ui/ozone/platform/wayland/wayland_surface_factory.h index 3b569890eac..d20c3f903b8 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_surface_factory.h +++ b/chromium/ui/ozone/platform/wayland/wayland_surface_factory.h @@ -7,17 +7,21 @@ #include "base/macros.h" #include "base/memory/ref_counted.h" -#include "ui/gl/gl_surface.h" -#include "ui/ozone/public/surface_factory_ozone.h" - #include "base/posix/eintr_wrapper.h" #include "base/single_thread_task_runner.h" #include "base/threading/sequenced_task_runner_handle.h" +#include "ui/gl/gl_surface.h" +#include "ui/ozone/platform/wayland/wayland_util.h" +#include "ui/ozone/public/surface_factory_ozone.h" + +namespace gfx { +class Rect; +} // namespace gfx namespace ui { -class WaylandConnectionProxy; class GbmSurfacelessWayland; +class WaylandConnectionProxy; class WaylandSurfaceFactory : public SurfaceFactoryOzone { public: @@ -25,7 +29,10 @@ class WaylandSurfaceFactory : public SurfaceFactoryOzone { ~WaylandSurfaceFactory() override; // These methods are used, when a dmabuf based approach is used. - void ScheduleBufferSwap(gfx::AcceleratedWidget widget, uint32_t buffer_id); + void ScheduleBufferSwap(gfx::AcceleratedWidget widget, + uint32_t buffer_id, + const gfx::Rect& damage_region_, + wl::BufferSwapCallback callback); void RegisterSurface(gfx::AcceleratedWidget widget, GbmSurfacelessWayland* surface); void UnregisterSurface(gfx::AcceleratedWidget widget); diff --git a/chromium/ui/ozone/platform/wayland/wayland_util.cc b/chromium/ui/ozone/platform/wayland/wayland_util.cc index 1ad2b556fb5..0cd05346df8 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_util.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_util.cc @@ -4,10 +4,15 @@ #include "ui/ozone/platform/wayland/wayland_util.h" +#include <xdg-shell-unstable-v5-client-protocol.h> +#include <xdg-shell-unstable-v6-client-protocol.h> + #include "base/memory/shared_memory.h" #include "third_party/skia/include/core/SkCanvas.h" #include "third_party/skia/include/core/SkSurface.h" +#include "ui/base/hit_test.h" #include "ui/gfx/skia_util.h" +#include "ui/ozone/platform/wayland/wayland_connection.h" namespace wl { @@ -16,6 +21,84 @@ namespace { const uint32_t kShmFormat = WL_SHM_FORMAT_ARGB8888; const SkColorType kColorType = kBGRA_8888_SkColorType; +uint32_t IdentifyDirectionV5(int hittest) { + uint32_t direction = 0; + switch (hittest) { + case HTBOTTOM: + direction = xdg_surface_resize_edge::XDG_SURFACE_RESIZE_EDGE_BOTTOM; + break; + case HTBOTTOMLEFT: + direction = xdg_surface_resize_edge::XDG_SURFACE_RESIZE_EDGE_BOTTOM_LEFT; + break; + case HTBOTTOMRIGHT: + direction = xdg_surface_resize_edge::XDG_SURFACE_RESIZE_EDGE_BOTTOM_RIGHT; + break; + case HTLEFT: + direction = xdg_surface_resize_edge::XDG_SURFACE_RESIZE_EDGE_LEFT; + break; + case HTRIGHT: + direction = xdg_surface_resize_edge::XDG_SURFACE_RESIZE_EDGE_RIGHT; + break; + case HTTOP: + direction = xdg_surface_resize_edge::XDG_SURFACE_RESIZE_EDGE_TOP; + break; + case HTTOPLEFT: + direction = xdg_surface_resize_edge::XDG_SURFACE_RESIZE_EDGE_TOP_LEFT; + break; + case HTTOPRIGHT: + direction = xdg_surface_resize_edge::XDG_SURFACE_RESIZE_EDGE_TOP_RIGHT; + break; + default: + direction = xdg_surface_resize_edge::XDG_SURFACE_RESIZE_EDGE_NONE; + break; + ; + } + return direction; +} + +uint32_t IdentifyDirectionV6(int hittest) { + uint32_t direction = 0; + switch (hittest) { + case HTBOTTOM: + direction = + zxdg_toplevel_v6_resize_edge::ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM; + break; + case HTBOTTOMLEFT: + direction = zxdg_toplevel_v6_resize_edge:: + ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_LEFT; + break; + case HTBOTTOMRIGHT: + direction = zxdg_toplevel_v6_resize_edge:: + ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM_RIGHT; + break; + case HTLEFT: + direction = + zxdg_toplevel_v6_resize_edge::ZXDG_TOPLEVEL_V6_RESIZE_EDGE_LEFT; + break; + case HTRIGHT: + direction = + zxdg_toplevel_v6_resize_edge::ZXDG_TOPLEVEL_V6_RESIZE_EDGE_RIGHT; + break; + case HTTOP: + direction = + zxdg_toplevel_v6_resize_edge::ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP; + break; + case HTTOPLEFT: + direction = + zxdg_toplevel_v6_resize_edge::ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_LEFT; + break; + case HTTOPRIGHT: + direction = + zxdg_toplevel_v6_resize_edge::ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP_RIGHT; + break; + default: + direction = + zxdg_toplevel_v6_resize_edge::ZXDG_TOPLEVEL_V6_RESIZE_EDGE_NONE; + break; + } + return direction; +} + } // namespace wl_buffer* CreateSHMBuffer(const gfx::Size& size, @@ -68,4 +151,12 @@ void DrawBitmapToSHMB(const gfx::Size& size, canvas->drawBitmapRect(bitmap, damage, nullptr); } +uint32_t IdentifyDirection(const ui::WaylandConnection& connection, + int hittest) { + if (connection.shell_v6()) + return IdentifyDirectionV6(hittest); + DCHECK(connection.shell()); + return IdentifyDirectionV5(hittest); +} + } // namespace wl diff --git a/chromium/ui/ozone/platform/wayland/wayland_util.h b/chromium/ui/ozone/platform/wayland/wayland_util.h index 0b77426698b..2a2db3aba1d 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_util.h +++ b/chromium/ui/ozone/platform/wayland/wayland_util.h @@ -7,6 +7,9 @@ #include <wayland-client.h> +#include <stdint.h> + +#include "base/callback.h" #include "base/macros.h" #include "ui/ozone/platform/wayland/wayland_object.h" @@ -16,12 +19,22 @@ namespace base { class SharedMemory; } +namespace ui { +class WaylandConnection; +} + namespace gfx { class Size; +enum class SwapResult; +struct PresentationFeedback; } namespace wl { +// Corresponds to mojom::WaylandConnection::ScheduleBufferSwapCallback. +using BufferSwapCallback = + base::OnceCallback<void(gfx::SwapResult, const gfx::PresentationFeedback&)>; + wl_buffer* CreateSHMBuffer(const gfx::Size& size, base::SharedMemory* shared_memory, wl_shm* shm); @@ -29,6 +42,11 @@ void DrawBitmapToSHMB(const gfx::Size& size, const base::SharedMemory& shared_memory, const SkBitmap& bitmap); +// Identifies the direction of the "hittest" for Wayland. |connection| +// is used to identify whether values from shell v5 or v6 must be used. +uint32_t IdentifyDirection(const ui::WaylandConnection& connection, + int hittest); + } // namespace wl #endif // UI_OZONE_PLATFORM_WAYLAND_WAYLAND_UTIL_H_ diff --git a/chromium/ui/ozone/platform/wayland/wayland_window.cc b/chromium/ui/ozone/platform/wayland/wayland_window.cc index c0d8847a028..27ddd18e8cd 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_window.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_window.cc @@ -8,9 +8,12 @@ #include "base/bind.h" #include "ui/base/cursor/ozone/bitmap_cursor_factory_ozone.h" +#include "ui/base/dragdrop/os_exchange_data.h" +#include "ui/base/hit_test.h" #include "ui/events/event.h" #include "ui/events/event_utils.h" #include "ui/events/ozone/events_ozone.h" +#include "ui/gfx/geometry/point_f.h" #include "ui/ozone/platform/wayland/wayland_connection.h" #include "ui/ozone/platform/wayland/wayland_pointer.h" #include "ui/ozone/platform/wayland/xdg_popup_wrapper_v5.h" @@ -71,7 +74,11 @@ WaylandWindow::WaylandWindow(PlatformWindowDelegate* delegate, : delegate_(delegate), connection_(connection), xdg_shell_objects_factory_(new XDGShellObjectFactory()), - state_(PlatformWindowState::PLATFORM_WINDOW_STATE_NORMAL) {} + state_(PlatformWindowState::PLATFORM_WINDOW_STATE_NORMAL) { + // Set a class property key, which allows |this| to be used for interactive + // events, e.g. move or resize. + SetWmMoveResizeHandler(this, AsWmMoveResizeHandler()); +} WaylandWindow::~WaylandWindow() { PlatformEventSource::GetInstance()->RemovePlatformEventDispatcher(this); @@ -194,7 +201,24 @@ void WaylandWindow::ApplyPendingBounds() { connection_->ScheduleFlush(); } +void WaylandWindow::DispatchHostWindowDragMovement( + int hittest, + const gfx::Point& pointer_location) { + DCHECK(xdg_surface_); + + connection_->ResetPointerFlags(); + if (hittest == HTCAPTION) + xdg_surface_->SurfaceMove(connection_); + else + xdg_surface_->SurfaceResize(connection_, hittest); + + connection_->ScheduleFlush(); +} + void WaylandWindow::Show() { + if (!is_tooltip_) // Tooltip windows should not get keyboard focus + set_keyboard_focus(true); + if (xdg_surface_) return; if (is_tooltip_) { @@ -510,6 +534,31 @@ void WaylandWindow::OnCloseRequest() { delegate_->OnCloseRequest(); } +void WaylandWindow::OnDragEnter(const gfx::PointF& point, + std::unique_ptr<OSExchangeData> data, + int operation) { + NOTIMPLEMENTED_LOG_ONCE(); +} + +int WaylandWindow::OnDragMotion(const gfx::PointF& point, + uint32_t time, + int operation) { + NOTIMPLEMENTED_LOG_ONCE(); + return 0; +} + +void WaylandWindow::OnDragDrop(std::unique_ptr<OSExchangeData> data) { + NOTIMPLEMENTED_LOG_ONCE(); +} + +void WaylandWindow::OnDragLeave() { + NOTIMPLEMENTED_LOG_ONCE(); +} + +void WaylandWindow::OnDragSessionClose(uint32_t dnd_action) { + NOTIMPLEMENTED_LOG_ONCE(); +} + bool WaylandWindow::IsMinimized() const { return state_ == PlatformWindowState::PLATFORM_WINDOW_STATE_MINIMIZED; } @@ -542,4 +591,8 @@ WaylandWindow* WaylandWindow::GetParentWindow( return parent_window; } +WmMoveResizeHandler* WaylandWindow::AsWmMoveResizeHandler() { + return static_cast<WmMoveResizeHandler*>(this); +} + } // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/wayland_window.h b/chromium/ui/ozone/platform/wayland/wayland_window.h index d652e896aef..e1cb0f6b85d 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_window.h +++ b/chromium/ui/ozone/platform/wayland/wayland_window.h @@ -12,10 +12,16 @@ #include "ui/ozone/platform/wayland/wayland_object.h" #include "ui/platform_window/platform_window.h" #include "ui/platform_window/platform_window_delegate.h" +#include "ui/platform_window/platform_window_handler/wm_move_resize_handler.h" + +namespace gfx { +class PointF; +} namespace ui { class BitmapCursorOzone; +class OSExchangeData; class PlatformWindowDelegate; class WaylandConnection; class XDGPopupWrapper; @@ -27,7 +33,9 @@ namespace { class XDGShellObjectFactory; } // namespace -class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { +class WaylandWindow : public PlatformWindow, + public PlatformEventDispatcher, + public WmMoveResizeHandler { public: WaylandWindow(PlatformWindowDelegate* delegate, WaylandConnection* connection); @@ -52,6 +60,8 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { // Set whether this window has keyboard focus and should dispatch key events. void set_keyboard_focus(bool focus) { has_keyboard_focus_ = focus; } + bool has_keyboard_focus() const { return has_keyboard_focus_; } + // Set whether this window has touch focus and should dispatch touch events. void set_touch_focus(bool focus) { has_touch_focus_ = focus; } @@ -66,6 +76,11 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { bool is_active() const { return is_active_; } + // WmMoveResizeHandler + void DispatchHostWindowDragMovement( + int hittest, + const gfx::Point& pointer_location) override; + // PlatformWindow void Show() override; void Hide() override; @@ -101,6 +116,14 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { void OnCloseRequest(); + void OnDragEnter(const gfx::PointF& point, + std::unique_ptr<OSExchangeData> data, + int operation); + int OnDragMotion(const gfx::PointF& point, uint32_t time, int operation); + void OnDragDrop(std::unique_ptr<OSExchangeData> data); + void OnDragLeave(); + void OnDragSessionClose(uint32_t dnd_action); + private: bool IsMinimized() const; bool IsMaximized() const; @@ -116,6 +139,8 @@ class WaylandWindow : public PlatformWindow, public PlatformEventDispatcher { // Gets a parent window for this window. WaylandWindow* GetParentWindow(gfx::AcceleratedWidget parent_widget); + WmMoveResizeHandler* AsWmMoveResizeHandler(); + PlatformWindowDelegate* delegate_; WaylandConnection* connection_; WaylandWindow* parent_window_ = nullptr; diff --git a/chromium/ui/ozone/platform/wayland/wayland_window_unittest.cc b/chromium/ui/ozone/platform/wayland/wayland_window_unittest.cc index 2d885e35ac9..a97b92ebc94 100644 --- a/chromium/ui/ozone/platform/wayland/wayland_window_unittest.cc +++ b/chromium/ui/ozone/platform/wayland/wayland_window_unittest.cc @@ -13,10 +13,12 @@ #include "base/strings/utf_string_conversions.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/hit_test.h" #include "ui/events/base_event_utils.h" #include "ui/events/event.h" #include "ui/ozone/platform/wayland/fake_server.h" #include "ui/ozone/platform/wayland/wayland_test.h" +#include "ui/ozone/platform/wayland/wayland_util.h" #include "ui/ozone/test/mock_platform_window_delegate.h" #include "ui/platform_window/platform_window_init_properties.h" @@ -132,6 +134,17 @@ class WaylandWindowTest : public WaylandTest { return window; } + void InitializeWithSupportedHitTestValues(std::vector<int>* hit_tests) { + hit_tests->push_back(static_cast<int>(HTBOTTOM)); + hit_tests->push_back(static_cast<int>(HTBOTTOMLEFT)); + hit_tests->push_back(static_cast<int>(HTBOTTOMRIGHT)); + hit_tests->push_back(static_cast<int>(HTLEFT)); + hit_tests->push_back(static_cast<int>(HTRIGHT)); + hit_tests->push_back(static_cast<int>(HTTOP)); + hit_tests->push_back(static_cast<int>(HTTOPLEFT)); + hit_tests->push_back(static_cast<int>(HTTOPRIGHT)); + } + wl::MockXdgSurface* xdg_surface_; MouseEvent test_mouse_event_; @@ -649,6 +662,25 @@ TEST_P(WaylandWindowTest, CanDispatchEventToMenuWindowNested) { Sync(); } +TEST_P(WaylandWindowTest, DispatchWindowMove) { + EXPECT_CALL(*GetXdgSurface(), Move(_)); + window_->DispatchHostWindowDragMovement(HTCAPTION, gfx::Point()); +} + +// Makes sure hit tests are converted into right edges. +TEST_P(WaylandWindowTest, DispatchWindowResize) { + std::vector<int> hit_test_values; + InitializeWithSupportedHitTestValues(&hit_test_values); + + for (const int value : hit_test_values) { + { + uint32_t direction = wl::IdentifyDirection(*(connection_.get()), value); + EXPECT_CALL(*GetXdgSurface(), Resize(_, Eq(direction))); + window_->DispatchHostWindowDragMovement(value, gfx::Point()); + } + } +} + INSTANTIATE_TEST_CASE_P(XdgVersionV5Test, WaylandWindowTest, ::testing::Values(kXdgShellV5)); diff --git a/chromium/ui/ozone/platform/wayland/xdg_surface_wrapper_v5.cc b/chromium/ui/ozone/platform/wayland/xdg_surface_wrapper_v5.cc index eb8f289f019..7f9cdf85ad2 100644 --- a/chromium/ui/ozone/platform/wayland/xdg_surface_wrapper_v5.cc +++ b/chromium/ui/ozone/platform/wayland/xdg_surface_wrapper_v5.cc @@ -7,7 +7,9 @@ #include <xdg-shell-unstable-v5-client-protocol.h> #include "base/strings/utf_string_conversions.h" +#include "ui/base/hit_test.h" #include "ui/ozone/platform/wayland/wayland_connection.h" +#include "ui/ozone/platform/wayland/wayland_util.h" #include "ui/ozone/platform/wayland/wayland_window.h" namespace ui { @@ -53,20 +55,15 @@ void XDGSurfaceWrapperV5::SetMinimized() { } void XDGSurfaceWrapperV5::SurfaceMove(WaylandConnection* connection) { - NOTIMPLEMENTED(); + xdg_surface_move(xdg_surface_.get(), connection->seat(), + connection->serial()); } void XDGSurfaceWrapperV5::SurfaceResize(WaylandConnection* connection, uint32_t hittest) { - // TODO(msisov): implement resizing. - /* - * int direction; - * if (!IdentifyDirection(hittest, &direction)) - * return; - * xdg_surface_resize(xdg_surface_.get(), connection->seat(), - * connection->serial(), direction); - */ - NOTIMPLEMENTED(); + xdg_surface_resize(xdg_surface_.get(), connection->seat(), + connection->serial(), + wl::IdentifyDirection(*connection, hittest)); } void XDGSurfaceWrapperV5::SetTitle(const base::string16& title) { diff --git a/chromium/ui/ozone/platform/wayland/xdg_surface_wrapper_v6.cc b/chromium/ui/ozone/platform/wayland/xdg_surface_wrapper_v6.cc index 14ab84344cf..f913d208b63 100644 --- a/chromium/ui/ozone/platform/wayland/xdg_surface_wrapper_v6.cc +++ b/chromium/ui/ozone/platform/wayland/xdg_surface_wrapper_v6.cc @@ -7,7 +7,9 @@ #include <xdg-shell-unstable-v6-client-protocol.h> #include "base/strings/utf_string_conversions.h" +#include "ui/base/hit_test.h" #include "ui/ozone/platform/wayland/wayland_connection.h" +#include "ui/ozone/platform/wayland/wayland_util.h" #include "ui/ozone/platform/wayland/wayland_window.h" namespace ui { @@ -82,20 +84,17 @@ void XDGSurfaceWrapperV6::SetMinimized() { } void XDGSurfaceWrapperV6::SurfaceMove(WaylandConnection* connection) { - NOTIMPLEMENTED(); + DCHECK(zxdg_toplevel_v6_); + zxdg_toplevel_v6_move(zxdg_toplevel_v6_.get(), connection->seat(), + connection->serial()); } void XDGSurfaceWrapperV6::SurfaceResize(WaylandConnection* connection, uint32_t hittest) { - // TODO(msisov): implement resizing. - /* - * int direction; - * if (!IdentifyDirection(hittest, &direction)) - * return; - * xdg_surface_resize(xdg_surface_.get(), connection->seat(), - * connection->serial(), direction); - */ - NOTIMPLEMENTED(); + DCHECK(zxdg_toplevel_v6_); + zxdg_toplevel_v6_resize(zxdg_toplevel_v6_.get(), connection->seat(), + connection->serial(), + wl::IdentifyDirection(*connection, hittest)); } void XDGSurfaceWrapperV6::SetTitle(const base::string16& title) { diff --git a/chromium/ui/ozone/platform/wayland/zwp_text_input_wrapper.h b/chromium/ui/ozone/platform/wayland/zwp_text_input_wrapper.h new file mode 100644 index 00000000000..8b11a95b779 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/zwp_text_input_wrapper.h @@ -0,0 +1,74 @@ +// 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_OZONE_PLATFORM_WAYLAND_ZWP_TEXT_INPUT_WRAPPER_H_ +#define UI_OZONE_PLATFORM_WAYLAND_ZWP_TEXT_INPUT_WRAPPER_H_ + +#include "ui/ozone/platform/wayland/wayland_object.h" + +#include "base/strings/string16.h" + +namespace gfx { +class Rect; +class Range; +} // namespace gfx + +namespace ui { + +class WaylandConnection; +class WaylandWindow; + +// Client interface which handles wayland text input callbacks +class ZWPTextInputWrapperClient { + public: + virtual ~ZWPTextInputWrapperClient() {} + + // Called when a new composing text (pre-edit) should be set around the + // current cursor position. Any previously set composing text should + // be removed. + virtual void OnPreeditString(const std::string& text, + int32_t preedit_cursor) = 0; + + // Called when a complete input sequence has been entered. The text to + // commit could be either just a single character after a key press or the + // result of some composing (pre-edit). + virtual void OnCommitString(const std::string& text) = 0; + + // Called when client needs to delete all or part of the text surrounding + // the cursor + virtual void OnDeleteSurroundingText(int32_t index, uint32_t length) = 0; + + // Notify when a key event was sent. Key events should not be used + // for normal text input operations, which should be done with + // commit_string, delete_surrounding_text, etc. + virtual void OnKeysym(uint32_t key, uint32_t state, uint32_t modifiers) = 0; +}; + +// A wrapper around different versions of wayland text input protocols. +// Wayland compositors support various different text input protocols which +// all from Chromium point of view provide the functionality needed by Chromium +// IME. This interface collects the functionality behind one wrapper API. +class ZWPTextInputWrapper { + public: + virtual ~ZWPTextInputWrapper() {} + + virtual void Initialize(WaylandConnection* connection, + ZWPTextInputWrapperClient* client) = 0; + + virtual void Reset() = 0; + + virtual void Activate(WaylandWindow* window) = 0; + virtual void Deactivate() = 0; + + virtual void ShowInputPanel() = 0; + virtual void HideInputPanel() = 0; + + virtual void SetCursorRect(const gfx::Rect& rect) = 0; + virtual void SetSurroundingText(const base::string16& text, + const gfx::Range& selection_range) = 0; +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_WAYLAND_ZWP_TEXT_INPUT_WRAPPER_H_ diff --git a/chromium/ui/ozone/platform/wayland/zwp_text_input_wrapper_v1.cc b/chromium/ui/ozone/platform/wayland/zwp_text_input_wrapper_v1.cc new file mode 100644 index 00000000000..4a12ace4011 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/zwp_text_input_wrapper_v1.cc @@ -0,0 +1,197 @@ +// 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/ozone/platform/wayland/zwp_text_input_wrapper_v1.h" + +#include "base/memory/ptr_util.h" +#include "base/strings/string16.h" +#include "base/strings/utf_string_conversions.h" +#include "ui/gfx/range/range.h" +#include "ui/ozone/platform/wayland/wayland_connection.h" +#include "ui/ozone/platform/wayland/wayland_window.h" + +namespace ui { + +ZWPTextInputWrapperV1::ZWPTextInputWrapperV1( + zwp_text_input_manager_v1* text_input_manager) + : client_(nullptr) { + static const zwp_text_input_v1_listener text_input_listener = { + &ZWPTextInputWrapperV1::OnEnter, // text_input_enter, + &ZWPTextInputWrapperV1::OnLeave, // text_input_leave, + &ZWPTextInputWrapperV1::OnModifiersMap, // text_input_modifiers_map, + &ZWPTextInputWrapperV1:: + OnInputPanelState, // text_input_input_panel_state, + &ZWPTextInputWrapperV1::OnPreeditString, // text_input_preedit_string, + &ZWPTextInputWrapperV1::OnPreeditStyling, // text_input_preedit_styling, + &ZWPTextInputWrapperV1::OnPreeditCursor, // text_input_preedit_cursor, + &ZWPTextInputWrapperV1::OnCommitString, // text_input_commit_string, + &ZWPTextInputWrapperV1::OnCursorPosition, // text_input_cursor_position, + &ZWPTextInputWrapperV1:: + OnDeleteSurroundingText, // text_input_delete_surrounding_text, + &ZWPTextInputWrapperV1::OnKeysym, // text_input_keysym, + &ZWPTextInputWrapperV1::OnLanguage, // text_input_language, + &ZWPTextInputWrapperV1::OnTextDirection, // text_input_text_direction + }; + ResetInputEventState(); + + zwp_text_input_v1* text_input = + zwp_text_input_manager_v1_create_text_input(text_input_manager); + obj_ = wl::Object<zwp_text_input_v1>(text_input); + + zwp_text_input_v1_add_listener(text_input, &text_input_listener, this); +} + +ZWPTextInputWrapperV1::~ZWPTextInputWrapperV1() {} + +void ZWPTextInputWrapperV1::Initialize(WaylandConnection* connection, + ZWPTextInputWrapperClient* client) { + connection_ = connection; + client_ = client; +} + +void ZWPTextInputWrapperV1::Reset() { + ResetInputEventState(); + zwp_text_input_v1_reset(obj_.get()); +} + +void ZWPTextInputWrapperV1::Activate(WaylandWindow* window) { + zwp_text_input_v1_activate(obj_.get(), connection_->seat(), + window->surface()); +} + +void ZWPTextInputWrapperV1::Deactivate() { + zwp_text_input_v1_deactivate(obj_.get(), connection_->seat()); +} + +void ZWPTextInputWrapperV1::ShowInputPanel() { + zwp_text_input_v1_show_input_panel(obj_.get()); +} + +void ZWPTextInputWrapperV1::HideInputPanel() { + zwp_text_input_v1_hide_input_panel(obj_.get()); +} + +void ZWPTextInputWrapperV1::SetCursorRect(const gfx::Rect& rect) { + zwp_text_input_v1_set_cursor_rectangle(obj_.get(), rect.x(), rect.y(), + rect.width(), rect.height()); +} + +void ZWPTextInputWrapperV1::SetSurroundingText( + const base::string16& text, + const gfx::Range& selection_range) { + const std::string text_utf8 = base::UTF16ToUTF8(text); + zwp_text_input_v1_set_surrounding_text(obj_.get(), text_utf8.c_str(), + selection_range.start(), + selection_range.end()); +} + +void ZWPTextInputWrapperV1::ResetInputEventState() { + preedit_cursor_ = -1; +} + +void ZWPTextInputWrapperV1::OnEnter(void* data, + struct zwp_text_input_v1* text_input, + struct wl_surface* surface) { + NOTIMPLEMENTED_LOG_ONCE(); +} + +void ZWPTextInputWrapperV1::OnLeave(void* data, + struct zwp_text_input_v1* text_input) { + NOTIMPLEMENTED_LOG_ONCE(); +} + +void ZWPTextInputWrapperV1::OnModifiersMap(void* data, + struct zwp_text_input_v1* text_input, + struct wl_array* map) { + NOTIMPLEMENTED_LOG_ONCE(); +} + +void ZWPTextInputWrapperV1::OnInputPanelState( + void* data, + struct zwp_text_input_v1* text_input, + uint32_t state) { + NOTIMPLEMENTED_LOG_ONCE(); +} + +void ZWPTextInputWrapperV1::OnPreeditString( + void* data, + struct zwp_text_input_v1* text_input, + uint32_t serial, + const char* text, + const char* commit) { + ZWPTextInputWrapperV1* wti = static_cast<ZWPTextInputWrapperV1*>(data); + wti->ResetInputEventState(); + wti->client_->OnPreeditString(std::string(text), wti->preedit_cursor_); +} + +void ZWPTextInputWrapperV1::OnPreeditStyling( + void* data, + struct zwp_text_input_v1* text_input, + uint32_t index, + uint32_t length, + uint32_t style) { + NOTIMPLEMENTED_LOG_ONCE(); +} + +void ZWPTextInputWrapperV1::OnPreeditCursor( + void* data, + struct zwp_text_input_v1* text_input, + int32_t index) { + ZWPTextInputWrapperV1* wti = static_cast<ZWPTextInputWrapperV1*>(data); + wti->preedit_cursor_ = index; +} + +void ZWPTextInputWrapperV1::OnCommitString(void* data, + struct zwp_text_input_v1* text_input, + uint32_t serial, + const char* text) { + ZWPTextInputWrapperV1* wti = static_cast<ZWPTextInputWrapperV1*>(data); + wti->ResetInputEventState(); + wti->client_->OnCommitString(std::string(text)); +} + +void ZWPTextInputWrapperV1::OnCursorPosition( + void* data, + struct zwp_text_input_v1* text_input, + int32_t index, + int32_t anchor) { + NOTIMPLEMENTED_LOG_ONCE(); +} + +void ZWPTextInputWrapperV1::OnDeleteSurroundingText( + void* data, + struct zwp_text_input_v1* text_input, + int32_t index, + uint32_t length) { + ZWPTextInputWrapperV1* wti = static_cast<ZWPTextInputWrapperV1*>(data); + wti->client_->OnDeleteSurroundingText(index, length); +} + +void ZWPTextInputWrapperV1::OnKeysym(void* data, + struct zwp_text_input_v1* text_input, + uint32_t serial, + uint32_t time, + uint32_t key, + uint32_t state, + uint32_t modifiers) { + ZWPTextInputWrapperV1* wti = static_cast<ZWPTextInputWrapperV1*>(data); + wti->client_->OnKeysym(key, state, modifiers); +} + +void ZWPTextInputWrapperV1::OnLanguage(void* data, + struct zwp_text_input_v1* text_input, + uint32_t serial, + const char* language) { + NOTIMPLEMENTED_LOG_ONCE(); +} + +void ZWPTextInputWrapperV1::OnTextDirection( + void* data, + struct zwp_text_input_v1* text_input, + uint32_t serial, + uint32_t direction) { + NOTIMPLEMENTED_LOG_ONCE(); +} + +} // namespace ui diff --git a/chromium/ui/ozone/platform/wayland/zwp_text_input_wrapper_v1.h b/chromium/ui/ozone/platform/wayland/zwp_text_input_wrapper_v1.h new file mode 100644 index 00000000000..62cfaa629e9 --- /dev/null +++ b/chromium/ui/ozone/platform/wayland/zwp_text_input_wrapper_v1.h @@ -0,0 +1,106 @@ +// 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_OZONE_PLATFORM_WAYLAND_ZWP_TEXT_INPUT_WRAPPER_V1_H_ +#define UI_OZONE_PLATFORM_WAYLAND_ZWP_TEXT_INPUT_WRAPPER_V1_H_ + +#include <text-input-unstable-v1-client-protocol.h> +#include <string> + +#include "ui/ozone/platform/wayland/zwp_text_input_wrapper.h" + +namespace gfx { +class Rect; +} + +namespace ui { + +class WaylandConnection; +class WaylandWindow; + +class ZWPTextInputWrapperV1 : public ZWPTextInputWrapper { + public: + explicit ZWPTextInputWrapperV1(zwp_text_input_manager_v1* text_input_manager); + ~ZWPTextInputWrapperV1() override; + + void Initialize(WaylandConnection* connection, + ZWPTextInputWrapperClient* client) override; + + void Reset() override; + + void Activate(WaylandWindow* window) override; + void Deactivate() override; + + void ShowInputPanel() override; + void HideInputPanel() override; + + void SetCursorRect(const gfx::Rect& rect) override; + void SetSurroundingText(const base::string16& text, + const gfx::Range& selection_range) override; + + private: + void ResetInputEventState(); + + // zwp_text_input_v1_listener + static void OnEnter(void* data, + struct zwp_text_input_v1* text_input, + struct wl_surface* surface); + static void OnLeave(void* data, struct zwp_text_input_v1* text_input); + static void OnModifiersMap(void* data, + struct zwp_text_input_v1* text_input, + struct wl_array* map); + static void OnInputPanelState(void* data, + struct zwp_text_input_v1* text_input, + uint32_t state); + static void OnPreeditString(void* data, + struct zwp_text_input_v1* text_input, + uint32_t serial, + const char* text, + const char* commit); + static void OnPreeditStyling(void* data, + struct zwp_text_input_v1* text_input, + uint32_t index, + uint32_t length, + uint32_t style); + static void OnPreeditCursor(void* data, + struct zwp_text_input_v1* text_input, + int32_t index); + static void OnCommitString(void* data, + struct zwp_text_input_v1* text_input, + uint32_t serial, + const char* text); + static void OnCursorPosition(void* data, + struct zwp_text_input_v1* text_input, + int32_t index, + int32_t anchor); + static void OnDeleteSurroundingText(void* data, + struct zwp_text_input_v1* text_input, + int32_t index, + uint32_t length); + static void OnKeysym(void* data, + struct zwp_text_input_v1* text_input, + uint32_t serial, + uint32_t time, + uint32_t key, + uint32_t state, + uint32_t modifiers); + static void OnLanguage(void* data, + struct zwp_text_input_v1* text_input, + uint32_t serial, + const char* language); + static void OnTextDirection(void* data, + struct zwp_text_input_v1* text_input, + uint32_t serial, + uint32_t direction); + + WaylandConnection* connection_ = nullptr; + wl::Object<zwp_text_input_v1> obj_; + ZWPTextInputWrapperClient* client_; + + int32_t preedit_cursor_; +}; + +} // namespace ui + +#endif // UI_OZONE_PLATFORM_WAYLAND_ZWP_TEXT_INPUT_WRAPPER_V1_H_ diff --git a/chromium/ui/ozone/public/input_controller.cc b/chromium/ui/ozone/public/input_controller.cc index 7ee1a3712b6..4ba1521c530 100644 --- a/chromium/ui/ozone/public/input_controller.cc +++ b/chromium/ui/ozone/public/input_controller.cc @@ -5,10 +5,8 @@ #include "ui/ozone/public/input_controller.h" #include "base/callback.h" -#include "base/compiler_specific.h" #include "base/logging.h" #include "base/macros.h" -#include "base/memory/ptr_util.h" namespace ui { @@ -16,146 +14,55 @@ namespace { class StubInputController : public InputController { public: - StubInputController(); - ~StubInputController() override; + StubInputController() = default; + ~StubInputController() override = default; // InputController: - bool HasMouse() override; - bool HasTouchpad() override; - bool IsCapsLockEnabled() override; - void SetCapsLockEnabled(bool enabled) override; - void SetNumLockEnabled(bool enabled) override; - bool IsAutoRepeatEnabled() override; - void SetAutoRepeatEnabled(bool enabled) override; + bool HasMouse() override { return false; } + bool HasTouchpad() override { return false; } + bool IsCapsLockEnabled() override { return false; } + void SetCapsLockEnabled(bool enabled) override {} + void SetNumLockEnabled(bool enabled) override {} + bool IsAutoRepeatEnabled() override { return true; } + void SetAutoRepeatEnabled(bool enabled) override {} void SetAutoRepeatRate(const base::TimeDelta& delay, - const base::TimeDelta& interval) override; + const base::TimeDelta& interval) override {} void GetAutoRepeatRate(base::TimeDelta* delay, - base::TimeDelta* interval) override; - void SetCurrentLayoutByName(const std::string& layout_name) override; - void SetTouchEventLoggingEnabled(bool enabled) override; - void SetTouchpadSensitivity(int value) override; - void SetTapToClick(bool enabled) override; - void SetThreeFingerClick(bool enabled) override; - void SetTapDragging(bool enabled) override; - void SetNaturalScroll(bool enabled) override; - void SetMouseSensitivity(int value) override; - void SetPrimaryButtonRight(bool right) override; - void SetMouseReverseScroll(bool enabled) override; - void SetTapToClickPaused(bool state) override; - void GetTouchDeviceStatus(GetTouchDeviceStatusReply reply) override; + base::TimeDelta* interval) override {} + void SetCurrentLayoutByName(const std::string& layout_name) override {} + void SetTouchEventLoggingEnabled(bool enabled) override { + NOTIMPLEMENTED_LOG_ONCE(); + } + void SetTouchpadSensitivity(int value) override {} + void SetTapToClick(bool enabled) override {} + void SetThreeFingerClick(bool enabled) override {} + void SetTapDragging(bool enabled) override {} + void SetNaturalScroll(bool enabled) override {} + void SetMouseSensitivity(int value) override {} + void SetPrimaryButtonRight(bool right) override {} + void SetMouseReverseScroll(bool enabled) override {} + void SetTapToClickPaused(bool state) override {} + void GetTouchDeviceStatus(GetTouchDeviceStatusReply reply) override { + std::move(reply).Run(std::string()); + } void GetTouchEventLog(const base::FilePath& out_dir, - GetTouchEventLogReply reply) override; - void SetInternalTouchpadEnabled(bool enabled) override; - bool IsInternalTouchpadEnabled() const override; - void SetTouchscreensEnabled(bool enabled) override; + GetTouchEventLogReply reply) override { + std::move(reply).Run(std::vector<base::FilePath>()); + } + void SetInternalTouchpadEnabled(bool enabled) override {} + bool IsInternalTouchpadEnabled() const override { return false; } + void SetTouchscreensEnabled(bool enabled) override {} void SetInternalKeyboardFilter(bool enable_filter, - std::vector<DomCode> allowed_keys) override; + std::vector<DomCode> allowed_keys) override {} private: DISALLOW_COPY_AND_ASSIGN(StubInputController); }; -StubInputController::StubInputController() { -} - -StubInputController::~StubInputController() { -} - -bool StubInputController::HasMouse() { - return false; -} - -bool StubInputController::HasTouchpad() { - return false; -} - -bool StubInputController::IsCapsLockEnabled() { - return false; -} - -void StubInputController::SetCapsLockEnabled(bool enabled) { -} - -void StubInputController::SetNumLockEnabled(bool enabled) { -} - -bool StubInputController::IsAutoRepeatEnabled() { - return true; -} - -void StubInputController::SetAutoRepeatEnabled(bool enabled) { -} - -void StubInputController::SetAutoRepeatRate(const base::TimeDelta& delay, - const base::TimeDelta& interval) { -} - -void StubInputController::GetAutoRepeatRate(base::TimeDelta* delay, - base::TimeDelta* interval) { -} - -void StubInputController::SetCurrentLayoutByName( - const std::string& layout_name) {} - -void StubInputController::SetTouchpadSensitivity(int value) { -} - -void StubInputController::SetTouchEventLoggingEnabled(bool enabled) { - NOTIMPLEMENTED(); -} - -void StubInputController::SetTapToClick(bool enabled) { -} - -void StubInputController::SetThreeFingerClick(bool enabled) { -} - -void StubInputController::SetTapDragging(bool enabled) { -} - -void StubInputController::SetNaturalScroll(bool enabled) { -} - -void StubInputController::SetMouseSensitivity(int value) { -} - -void StubInputController::SetPrimaryButtonRight(bool right) { -} - -void StubInputController::SetMouseReverseScroll(bool enabled) { -} - -void StubInputController::SetTapToClickPaused(bool state) { -} - -void StubInputController::GetTouchDeviceStatus( - GetTouchDeviceStatusReply reply) { - std::move(reply).Run(std::string()); -} - -void StubInputController::GetTouchEventLog(const base::FilePath& out_dir, - GetTouchEventLogReply reply) { - std::move(reply).Run(std::vector<base::FilePath>()); -} - -void StubInputController::SetInternalTouchpadEnabled(bool enabled) { -} - -bool StubInputController::IsInternalTouchpadEnabled() const { - return false; -} - -void StubInputController::SetTouchscreensEnabled(bool enabled) {} - -void StubInputController::SetInternalKeyboardFilter( - bool enable_filter, - std::vector<DomCode> allowed_keys) { -} - } // namespace std::unique_ptr<InputController> CreateStubInputController() { - return base::WrapUnique(new StubInputController); + return std::make_unique<StubInputController>(); } } // namespace ui diff --git a/chromium/ui/ozone/public/interfaces/wayland/BUILD.gn b/chromium/ui/ozone/public/interfaces/wayland/BUILD.gn index c6f2360d026..9ee493e1731 100644 --- a/chromium/ui/ozone/public/interfaces/wayland/BUILD.gn +++ b/chromium/ui/ozone/public/interfaces/wayland/BUILD.gn @@ -11,6 +11,7 @@ mojom("wayland_interfaces") { public_deps = [ "//mojo/public/mojom/base", + "//ui/gfx/geometry/mojo", "//ui/gfx/mojo", ] } diff --git a/chromium/ui/ozone/public/interfaces/wayland/wayland_connection.mojom b/chromium/ui/ozone/public/interfaces/wayland/wayland_connection.mojom index fde5b12974e..a3993ccb15c 100644 --- a/chromium/ui/ozone/public/interfaces/wayland/wayland_connection.mojom +++ b/chromium/ui/ozone/public/interfaces/wayland/wayland_connection.mojom @@ -6,7 +6,10 @@ module ui.ozone.mojom; import "mojo/public/mojom/base/file.mojom"; import "mojo/public/mojom/base/file_path.mojom"; +import "ui/gfx/geometry/mojo/geometry.mojom"; import "ui/gfx/mojo/accelerated_widget.mojom"; +import "ui/gfx/mojo/presentation_feedback.mojom"; +import "ui/gfx/mojo/swap_result.mojom"; // Used by the GPU for communication with a WaylandConnection on the browser // process. @@ -26,7 +29,10 @@ interface WaylandConnection { DestroyZwpLinuxDmabuf(uint32 buffer_id); // Swaps wl_buffers for a WaylandWindow with the following |widget|. - ScheduleBufferSwap(gfx.mojom.AcceleratedWidget widget, uint32 buffer_id); + ScheduleBufferSwap(gfx.mojom.AcceleratedWidget widget, uint32 buffer_id, + gfx.mojom.Rect damage_region) + => (gfx.mojom.SwapResult swap_result, + gfx.mojom.PresentationFeedback feedback); }; // Used by the browser process to provide the GPU process with a mojo ptr to a diff --git a/chromium/ui/ozone/public/ozone_platform.cc b/chromium/ui/ozone/public/ozone_platform.cc index aae41ff19ef..ec543bc9c10 100644 --- a/chromium/ui/ozone/public/ozone_platform.cc +++ b/chromium/ui/ozone/public/ozone_platform.cc @@ -41,10 +41,12 @@ OzonePlatform::PlatformProperties::PlatformProperties( bool needs_request, bool custom_frame_default, bool can_use_system_title_bar, + bool requires_mojo_for_ipc, std::vector<gfx::BufferFormat> buffer_formats) : needs_view_owner_request(needs_request), custom_frame_pref_default(custom_frame_default), use_system_title_bar(can_use_system_title_bar), + requires_mojo(requires_mojo_for_ipc), supported_buffer_formats(buffer_formats) {} OzonePlatform::PlatformProperties::~PlatformProperties() = default; diff --git a/chromium/ui/ozone/public/ozone_platform.h b/chromium/ui/ozone/public/ozone_platform.h index da9f46aa764..f1058530501 100644 --- a/chromium/ui/ozone/public/ozone_platform.h +++ b/chromium/ui/ozone/public/ozone_platform.h @@ -91,6 +91,7 @@ class OZONE_EXPORT OzonePlatform { PlatformProperties(bool needs_request, bool custom_frame_default, bool can_use_system_title_bar, + bool requires_mojo_for_ipc, std::vector<gfx::BufferFormat> buffer_formats); ~PlatformProperties(); PlatformProperties(const PlatformProperties& other); @@ -108,6 +109,10 @@ class OZONE_EXPORT OzonePlatform { // supported. bool use_system_title_bar = false; + // Determines if the platform requires mojo communication for the IPC. + // Currently used only by the Ozone/Wayland platform. + bool requires_mojo = false; + // Wayland only: carries buffer formats supported by a Wayland server. std::vector<gfx::BufferFormat> supported_buffer_formats; }; diff --git a/chromium/ui/ozone/public/ozone_switches.cc b/chromium/ui/ozone/public/ozone_switches.cc index 0a9b3ab32a0..7bec0b7ef22 100644 --- a/chromium/ui/ozone/public/ozone_switches.cc +++ b/chromium/ui/ozone/public/ozone_switches.cc @@ -12,4 +12,7 @@ const char kOzonePlatform[] = "ozone-platform"; // Specify location for image dumps. const char kOzoneDumpFile[] = "ozone-dump-file"; +// Try to enable wayland input method editor. +const char kEnableWaylandIme[] = "enable-wayland-ime"; + } // namespace switches diff --git a/chromium/ui/ozone/public/ozone_switches.h b/chromium/ui/ozone/public/ozone_switches.h index b062e67fcfb..d7e7d8a9256 100644 --- a/chromium/ui/ozone/public/ozone_switches.h +++ b/chromium/ui/ozone/public/ozone_switches.h @@ -14,6 +14,8 @@ OZONE_BASE_EXPORT extern const char kOzonePlatform[]; OZONE_BASE_EXPORT extern const char kOzoneDumpFile[]; +OZONE_BASE_EXPORT extern const char kEnableWaylandIme[]; + } // namespace switches #endif // UI_OZONE_PUBLIC_OZONE_SWITCHES_H_ diff --git a/chromium/ui/platform_window/BUILD.gn b/chromium/ui/platform_window/BUILD.gn index dbfac36279a..af5ef67e875 100644 --- a/chromium/ui/platform_window/BUILD.gn +++ b/chromium/ui/platform_window/BUILD.gn @@ -24,7 +24,7 @@ source_set("platform_window") { if (is_fuchsia) { public_deps = [ - "//third_party/fuchsia-sdk:viewsv1token", + "//third_party/fuchsia-sdk/sdk:viewsv1token", ] } } diff --git a/chromium/ui/platform_window/DEPS b/chromium/ui/platform_window/DEPS index 0e42d64275f..b51da65b022 100644 --- a/chromium/ui/platform_window/DEPS +++ b/chromium/ui/platform_window/DEPS @@ -1,5 +1,4 @@ include_rules = [ - "+ui/base/cursor", - "+ui/base/ime", + "+ui/base", "+ui/gfx", ] diff --git a/chromium/ui/platform_window/android/java/src/org/chromium/ui/PlatformImeControllerAndroid.java b/chromium/ui/platform_window/android/java/src/org/chromium/ui/PlatformImeControllerAndroid.java new file mode 100644 index 00000000000..e81c1e7ef86 --- /dev/null +++ b/chromium/ui/platform_window/android/java/src/org/chromium/ui/PlatformImeControllerAndroid.java @@ -0,0 +1,107 @@ +// Copyright 2015 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. + +package org.chromium.ui; + +import android.content.Context; +import android.view.inputmethod.BaseInputConnection; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; +import android.view.inputmethod.InputMethodManager; + +import org.chromium.base.annotations.CalledByNative; +import org.chromium.base.annotations.JNINamespace; + +/** + * Exposes IME related code to native code. + */ +@JNINamespace("ui") +class PlatformImeControllerAndroid { + private int mInputType; + private int mInputFlags; + private String mText = ""; + private int mSelectionStart; + private int mSelectionEnd; + private int mCompositionStart; + private int mCompositionEnd; + + private final PlatformWindowAndroid mWindow; + private final long mNativeHandle; + private final InputMethodManager mInputMethodManager; + private InputConnection mInputConnection; + + PlatformImeControllerAndroid(PlatformWindowAndroid window, long nativeHandle) { + mWindow = window; + mNativeHandle = nativeHandle; + mInputMethodManager = (InputMethodManager) mWindow.getContext().getSystemService( + Context.INPUT_METHOD_SERVICE); + assert mNativeHandle != 0; + nativeInit(mNativeHandle); + } + + boolean isTextEditorType() { + return mInputType != 0; + } + + InputConnection onCreateInputConnection(EditorInfo outAttrs) { + if (mInputType == 0) { + // Although onCheckIsTextEditor will return false in this case, the EditorInfo + // is still used by the InputMethodService. Need to make sure the IME doesn't + // enter fullscreen mode. + outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN; + } + + // TODO(penghuang): Support full editor. + final boolean fullEditor = false; + mInputConnection = new BaseInputConnection(mWindow, fullEditor); + outAttrs.actionLabel = null; + // TODO(penghuang): Pass blink text input type to Android framework. + outAttrs.inputType = + EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT; + outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI | EditorInfo.IME_FLAG_NO_FULLSCREEN + | EditorInfo.IME_ACTION_GO; + return mInputConnection; + } + + @CalledByNative + private void updateTextInputState(int textInputType, int textInputFlags, String text, + int selectionStart, int selectionEnd, int compositionStart, int compositionEnd) { + mInputType = textInputType; + mInputFlags = textInputFlags; + mText = text; + mSelectionStart = selectionStart; + mSelectionEnd = selectionEnd; + mCompositionStart = compositionStart; + mCompositionEnd = compositionEnd; + // Update keyboard visibility + if (mInputType == 0) { + dismissInput(); + } + } + + @CalledByNative + private void setImeVisibility(boolean visible) { + // The IME is visible only if |mInputType| isn't 0, so we don't need + // change the visibility if |mInputType| is 0. + if (mInputType != 0) { + if (visible) { + showKeyboard(); + } else { + dismissInput(); + } + } + } + + private void showKeyboard() { + mInputMethodManager.showSoftInput(mWindow, 0); + } + + private void dismissInput() { + mInputMethodManager.hideSoftInputFromWindow(mWindow.getWindowToken(), 0); + } + + // The generated native method implementation will call + // PlatformImeControllerAndroid::Init(JNIEnv* env, jobject self) + private native void nativeInit(long nativePlatformImeControllerAndroid); +} diff --git a/chromium/ui/platform_window/android/java/src/org/chromium/ui/PlatformWindowAndroid.java b/chromium/ui/platform_window/android/java/src/org/chromium/ui/PlatformWindowAndroid.java new file mode 100644 index 00000000000..26810fff1d7 --- /dev/null +++ b/chromium/ui/platform_window/android/java/src/org/chromium/ui/PlatformWindowAndroid.java @@ -0,0 +1,207 @@ +// Copyright 2013 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. + +package org.chromium.ui; + +import android.app.Activity; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputConnection; + +import org.chromium.base.ContextUtils; +import org.chromium.base.annotations.CalledByNative; +import org.chromium.base.annotations.JNINamespace; + +/** + * Exposes SurfaceView to native code. + */ +@JNINamespace("ui") +public class PlatformWindowAndroid extends SurfaceView { + + private long mNativeMojoViewport; + private final SurfaceHolder.Callback mSurfaceCallback; + private final PlatformImeControllerAndroid mImeController; + + @CalledByNative + public static PlatformWindowAndroid createForActivity( + long nativeViewport, long nativeImeController) { + PlatformWindowAndroid rv = new PlatformWindowAndroid(nativeViewport, nativeImeController); + ((Activity) ContextUtils.getApplicationContext()).setContentView(rv); + return rv; + } + + private PlatformWindowAndroid(long nativeViewport, long nativeImeController) { + super(ContextUtils.getApplicationContext()); + + setFocusable(true); + setFocusableInTouchMode(true); + + mNativeMojoViewport = nativeViewport; + assert mNativeMojoViewport != 0; + + final float density = + ContextUtils.getApplicationContext().getResources().getDisplayMetrics().density; + + mSurfaceCallback = new SurfaceHolder.Callback() { + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + assert mNativeMojoViewport != 0; + nativeSurfaceSetSize(mNativeMojoViewport, width, height, density); + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + assert mNativeMojoViewport != 0; + nativeSurfaceCreated(mNativeMojoViewport, holder.getSurface(), density); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + assert mNativeMojoViewport != 0; + nativeSurfaceDestroyed(mNativeMojoViewport); + } + }; + getHolder().addCallback(mSurfaceCallback); + + mImeController = new PlatformImeControllerAndroid(this, nativeImeController); + } + + @CalledByNative + public void detach() { + getHolder().removeCallback(mSurfaceCallback); + mNativeMojoViewport = 0; + } + + @Override + protected void onWindowVisibilityChanged(int visibility) { + super.onWindowVisibilityChanged(visibility); + if (visibility == View.VISIBLE) { + requestFocusFromTouch(); + requestFocus(); + } + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + final int actionMasked = event.getActionMasked(); + if (actionMasked == MotionEvent.ACTION_POINTER_DOWN + || actionMasked == MotionEvent.ACTION_POINTER_UP) { + // Up/down events identify a single point. + return notifyTouchEventAtIndex(event, event.getActionIndex()); + } + assert event.getPointerCount() != 0; + // All other types can have more than one point. + boolean result = false; + for (int i = 0, count = event.getPointerCount(); i < count; i++) { + final boolean sub_result = notifyTouchEventAtIndex(event, i); + result |= sub_result; + } + return result; + } + + @Override + public boolean onCheckIsTextEditor() { + return mImeController.isTextEditorType(); + } + + @Override + public InputConnection onCreateInputConnection(EditorInfo outAttrs) { + return mImeController.onCreateInputConnection(outAttrs); + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + if (privateDispatchKeyEvent(event)) { + return true; + } + return super.dispatchKeyEvent(event); + } + + @Override + public boolean dispatchKeyEventPreIme(KeyEvent event) { + if (privateDispatchKeyEvent(event)) { + return true; + } + return super.dispatchKeyEventPreIme(event); + } + + @Override + public boolean dispatchKeyShortcutEvent(KeyEvent event) { + if (privateDispatchKeyEvent(event)) { + return true; + } + return super.dispatchKeyShortcutEvent(event); + } + + private boolean notifyTouchEventAtIndex(MotionEvent event, int index) { + float touchMajor = event.getTouchMajor(index); + float touchMinor = event.getTouchMinor(index); + if (touchMajor < touchMinor) { + float tmp = touchMajor; + touchMajor = touchMinor; + touchMinor = tmp; + } + + return nativeTouchEvent(mNativeMojoViewport, event.getEventTime(), event.getActionMasked(), + event.getPointerId(index), event.getX(index), event.getY(index), + event.getPressure(index), touchMajor, touchMinor, + event.getOrientation(index), event.getAxisValue(MotionEvent.AXIS_HSCROLL, index), + event.getAxisValue(MotionEvent.AXIS_VSCROLL, index)); + } + + private boolean privateDispatchKeyEvent(KeyEvent event) { + if (event.getAction() == KeyEvent.ACTION_MULTIPLE) { + boolean result = false; + if (event.getKeyCode() == KeyEvent.KEYCODE_UNKNOWN && event.getCharacters() != null) { + String characters = event.getCharacters(); + for (int i = 0; i < characters.length(); ++i) { + char c = characters.charAt(i); + int codepoint = c; + if (codepoint >= Character.MIN_SURROGATE + && codepoint < (Character.MAX_SURROGATE + 1)) { + i++; + char c2 = characters.charAt(i); + codepoint = Character.toCodePoint(c, c2); + } + result |= nativeKeyEvent(mNativeMojoViewport, true, 0, codepoint); + result |= nativeKeyEvent(mNativeMojoViewport, false, 0, codepoint); + } + } else { + for (int i = 0; i < event.getRepeatCount(); ++i) { + result |= nativeKeyEvent( + mNativeMojoViewport, true, event.getKeyCode(), event.getUnicodeChar()); + result |= nativeKeyEvent( + mNativeMojoViewport, false, event.getKeyCode(), event.getUnicodeChar()); + } + } + return result; + } else { + return nativeKeyEvent(mNativeMojoViewport, event.getAction() == KeyEvent.ACTION_DOWN, + event.getKeyCode(), event.getUnicodeChar()); + } + } + + private static native void nativeDestroy(long nativePlatformWindowAndroid); + + private static native void nativeSurfaceCreated( + long nativePlatformWindowAndroid, Surface surface, float devicePixelRatio); + + private static native void nativeSurfaceDestroyed( + long nativePlatformWindowAndroid); + + private static native void nativeSurfaceSetSize( + long nativePlatformWindowAndroid, int width, int height, float density); + + private static native boolean nativeTouchEvent(long nativePlatformWindowAndroid, long timeMs, + int maskedAction, int pointerId, float x, float y, float pressure, float touchMajor, + float touchMinor, float orientation, float hWheel, float vWheel); + + private static native boolean nativeKeyEvent( + long nativePlatformWindowAndroid, boolean pressed, int keyCode, int unicodeCharacter); +} diff --git a/chromium/ui/platform_window/android/platform_window_android.cc b/chromium/ui/platform_window/android/platform_window_android.cc index 7267e3cbe4c..25f80458eee 100644 --- a/chromium/ui/platform_window/android/platform_window_android.cc +++ b/chromium/ui/platform_window/android/platform_window_android.cc @@ -120,8 +120,8 @@ bool PlatformWindowAndroid::TouchEvent(JNIEnv* env, event_type, gfx::Point(), base::TimeTicks() + base::TimeDelta::FromMilliseconds(time_ms), ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, pointer_id, - touch_major, touch_minor, pressure), - ui::EF_NONE, orientation); + touch_major, touch_minor, pressure, orientation), + ui::EF_NONE); touch.set_location_f(gfx::PointF(x, y)); touch.set_root_location_f(gfx::PointF(x, y)); delegate()->DispatchEvent(&touch); diff --git a/chromium/ui/platform_window/platform_window.h b/chromium/ui/platform_window/platform_window.h index 181ad85fd70..c7611eee8af 100644 --- a/chromium/ui/platform_window/platform_window.h +++ b/chromium/ui/platform_window/platform_window.h @@ -8,6 +8,7 @@ #include <memory> #include "base/strings/string16.h" +#include "ui/base/class_property.h" #include "ui/base/cursor/cursor.h" #include "ui/platform_window/platform_window_delegate.h" @@ -24,7 +25,7 @@ class PlatformImeController; // // Each instance of PlatformWindow represents a single window in the // underlying platform windowing system (i.e. X11/Win/OSX). -class PlatformWindow { +class PlatformWindow : public PropertyHandler { public: virtual ~PlatformWindow() {} diff --git a/chromium/ui/platform_window/platform_window_handler/BUILD.gn b/chromium/ui/platform_window/platform_window_handler/BUILD.gn new file mode 100644 index 00000000000..be75a75c702 --- /dev/null +++ b/chromium/ui/platform_window/platform_window_handler/BUILD.gn @@ -0,0 +1,22 @@ +# 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. + +import("//build/config/jumbo.gni") + +jumbo_component("platform_window_handler") { + output_name = "platform_window_handler_libs" + + sources = [ + "wm_move_resize_handler.cc", + "wm_move_resize_handler.h", + "wm_platform_export.h", + ] + + defines = [ "PLATFORM_WINDOW_HANDLER_IMPLEMENTATION" ] + + deps = [ + "//ui/base", + "//ui/platform_window", + ] +} diff --git a/chromium/ui/platform_window/platform_window_handler/DEPS b/chromium/ui/platform_window/platform_window_handler/DEPS new file mode 100644 index 00000000000..381a2e478f2 --- /dev/null +++ b/chromium/ui/platform_window/platform_window_handler/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+ui/base/class_property.h", + "+ui/platform_window/platform_window.h", +] diff --git a/chromium/ui/platform_window/platform_window_handler/wm_move_resize_handler.cc b/chromium/ui/platform_window/platform_window_handler/wm_move_resize_handler.cc new file mode 100644 index 00000000000..5a295a06e1b --- /dev/null +++ b/chromium/ui/platform_window/platform_window_handler/wm_move_resize_handler.cc @@ -0,0 +1,28 @@ +// 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/platform_window/platform_window_handler/wm_move_resize_handler.h" + +#include "ui/base/class_property.h" +#include "ui/platform_window/platform_window.h" + +DEFINE_UI_CLASS_PROPERTY_TYPE(WmMoveResizeHandler*); + +namespace ui { + +DEFINE_UI_CLASS_PROPERTY_KEY(WmMoveResizeHandler*, + kWmMoveResizeHandlerKey, + nullptr); + +void SetWmMoveResizeHandler(PlatformWindow* platform_window, + WmMoveResizeHandler* move_resize_handler) { + platform_window->SetProperty(kWmMoveResizeHandlerKey, move_resize_handler); +} + +WmMoveResizeHandler* GetWmMoveResizeHandler( + const PlatformWindow& platform_window) { + return platform_window.GetProperty(kWmMoveResizeHandlerKey); +} + +} // namespace ui diff --git a/chromium/ui/platform_window/platform_window_handler/wm_move_resize_handler.h b/chromium/ui/platform_window/platform_window_handler/wm_move_resize_handler.h new file mode 100644 index 00000000000..cd5a696a06b --- /dev/null +++ b/chromium/ui/platform_window/platform_window_handler/wm_move_resize_handler.h @@ -0,0 +1,62 @@ +// 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_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_MOVE_RESIZE_HANDLER_H_ +#define UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_MOVE_RESIZE_HANDLER_H_ + +#include "ui/platform_window/platform_window_handler/wm_platform_export.h" + +namespace gfx { +class Point; +} // namespace gfx + +namespace ui { + +class PlatformWindow; + +class WmMoveResizeHandler { + public: + // A system window manager starts interactive drag or resize of a window based + // on the |hittest| value. The |hittest| value identifies in which direction + // the window should be resized or whether it should be moved. See + // ui/base/hit_test.h for a concrete example with chromium symbolic names + // defined. The |pointer_location| indicates the position of the button press + // with respect to the platform window, which is needed when sending a + // move/resize request in such backends as X11. See _NET_WM_MOVERESIZE section + // in https://specifications.freedesktop.org/wm-spec/1.4/ar01s04.html. + // + // There is no need to implement this by all the platforms except Ozone/X11 + // and Ozone/Wayland, compositors of which support interactive move/resize. + // + // This API must be used on mouse or touch events, which are targeted for + // non-client components (check ui/base/hit_test.h again) except the ones + // targeted for components like HTMAXBUTTON. In that case, the mouse events + // are used to identify clicks on maximize/minimize/restore buttons located in + // the top non-client area of the chromium window. See + // WindowEventFilter::OnMouseEvent for a concrete example of how mouse events + // are identified as client or non-client. + // + // When the API is called, there is no way to know that the call was + // successful or not. The browser continues performing as usual except that a + // system compositor does not send any mouse/keyboard/etc events until user + // releases a mouse button. Instead, the compositor sends new bounds, which a + // client uses to recreate gpu buffers and redraw visual represantation of the + // browser. + virtual void DispatchHostWindowDragMovement( + int hittest, + const gfx::Point& pointer_location) = 0; + + protected: + virtual ~WmMoveResizeHandler() {} +}; + +WM_PLATFORM_EXPORT void SetWmMoveResizeHandler( + PlatformWindow* platform_window, + WmMoveResizeHandler* move_resize_handler); +WM_PLATFORM_EXPORT WmMoveResizeHandler* GetWmMoveResizeHandler( + const PlatformWindow& platform_window); + +} // namespace ui + +#endif // UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_MOVE_RESIZE_HANDLER_H_ diff --git a/chromium/ui/platform_window/platform_window_handler/wm_platform_export.h b/chromium/ui/platform_window/platform_window_handler/wm_platform_export.h new file mode 100644 index 00000000000..37165fb71ef --- /dev/null +++ b/chromium/ui/platform_window/platform_window_handler/wm_platform_export.h @@ -0,0 +1,32 @@ +// 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_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_PLATFORM_EXPORT_H_ +#define UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_PLATFORM_EXPORT_H_ + +// Defines WM_PLATFORM_EXPORT so that functionality implemented by the +// wm_platform module can be exported to consumers. + +#if defined(COMPONENT_BUILD) +#if defined(WIN32) + +#if defined(PLATFORM_WINDOW_HANDLER_IMPLEMENTATION) +#define WM_PLATFORM_EXPORT __declspec(dllexport) +#else +#define WM_PLATFORM_EXPORT __declspec(dllimport) +#endif // defined(WM_PLATFORM_IMPLEMENTATION) + +#else // defined(WIN32) +#if defined(PLATFORM_WINDOW_HANDLER_IMPLEMENTATION) +#define WM_PLATFORM_EXPORT __attribute__((visibility("default"))) +#else +#define WM_PLATFORM_EXPORT +#endif +#endif + +#else // defined(COMPONENT_BUILD) +#define WM_PLATFORM_EXPORT +#endif + +#endif // UI_PLATFORM_WINDOW_PLATFORM_WINDOW_HANDLER_WM_PLATFORM_EXPORT_H_ diff --git a/chromium/ui/shell_dialogs/base_shell_dialog_win.cc b/chromium/ui/shell_dialogs/base_shell_dialog_win.cc index ab67062109f..a61a9e7f953 100644 --- a/chromium/ui/shell_dialogs/base_shell_dialog_win.cc +++ b/chromium/ui/shell_dialogs/base_shell_dialog_win.cc @@ -6,11 +6,32 @@ #include <algorithm> -#include "base/threading/thread.h" +#include "base/task/post_task.h" #include "base/win/scoped_com_initializer.h" namespace ui { +namespace { + +// Creates a SingleThreadTaskRunner to run a shell dialog on. Each dialog +// requires its own dedicated single-threaded sequence otherwise in some +// situations where a singleton owns a single instance of this object we can +// have a situation where a modal dialog in one window blocks the appearance +// of a modal dialog in another. +scoped_refptr<base::SingleThreadTaskRunner> CreateDialogTaskRunner() { + return CreateCOMSTATaskRunnerWithTraits( + {base::TaskPriority::USER_BLOCKING, + base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN, base::MayBlock()}, + base::SingleThreadTaskRunnerThreadMode::DEDICATED); +} + +} // namespace + +BaseShellDialogImpl::RunState::RunState() = default; +BaseShellDialogImpl::RunState::~RunState() = default; + +BaseShellDialogImpl::RunState::RunState(const RunState& run_state) = default; + // static BaseShellDialogImpl::Owners BaseShellDialogImpl::owners_; int BaseShellDialogImpl::instance_count_ = 0; @@ -32,7 +53,7 @@ BaseShellDialogImpl::RunState BaseShellDialogImpl::BeginRun(HWND owner) { // entries in our map for the same top level window. DCHECK(!owner || owner == GetAncestor(owner, GA_ROOT)); RunState run_state; - run_state.dialog_thread = CreateDialogThread(); + run_state.dialog_task_runner = CreateDialogTaskRunner(); run_state.owner = owner; if (owner) { owners_.insert(owner); @@ -48,8 +69,6 @@ void BaseShellDialogImpl::EndRun(RunState run_state) { DCHECK(owners_.find(run_state.owner) != owners_.end()); owners_.erase(run_state.owner); } - DCHECK(run_state.dialog_thread); - delete run_state.dialog_thread; } bool BaseShellDialogImpl::IsRunningDialogForOwner(HWND owner) const { @@ -61,16 +80,6 @@ void BaseShellDialogImpl::DisableOwner(HWND owner) { EnableWindow(owner, FALSE); } -// static -base::Thread* BaseShellDialogImpl::CreateDialogThread() { - base::Thread* thread = new base::Thread("Chrome_ShellDialogThread"); - // Many shell dialogs require a COM Single-Threaded Apartment (STA) to run. - thread->init_com_with_mta(false); - bool started = thread->Start(); - DCHECK(started); - return thread; -} - void BaseShellDialogImpl::EnableOwner(HWND owner) { if (IsWindow(owner)) EnableWindow(owner, TRUE); diff --git a/chromium/ui/shell_dialogs/base_shell_dialog_win.h b/chromium/ui/shell_dialogs/base_shell_dialog_win.h index ca5ce0bd567..37959ba6c8a 100644 --- a/chromium/ui/shell_dialogs/base_shell_dialog_win.h +++ b/chromium/ui/shell_dialogs/base_shell_dialog_win.h @@ -9,11 +9,12 @@ #include <set> #include "base/macros.h" +#include "base/memory/scoped_refptr.h" #include "ui/shell_dialogs/base_shell_dialog.h" #include "ui/shell_dialogs/shell_dialogs_export.h" namespace base { -class Thread; +class SingleThreadTaskRunner; } namespace ui { @@ -28,17 +29,22 @@ class SHELL_DIALOGS_EXPORT BaseShellDialogImpl { protected: // Represents a run of a dialog. - struct RunState { + struct SHELL_DIALOGS_EXPORT RunState { + RunState(); + ~RunState(); + + RunState(const RunState& run_state); + // Owning HWND, may be null. HWND owner; - // Thread dialog is run on. - base::Thread* dialog_thread; + // Dedicated sequence on which the dialog runs. + scoped_refptr<base::SingleThreadTaskRunner> dialog_task_runner; }; // Called at the beginning of a modal dialog run. Disables the owner window - // and tracks it. Returns the message loop of the thread that the dialog will - // be run on. + // and tracks it. Returns the dedicated single-threaded sequence that the + // dialog will be run on. RunState BeginRun(HWND owner); // Cleans up after a dialog run. If the run_state has a valid HWND this makes @@ -54,24 +60,17 @@ class SHELL_DIALOGS_EXPORT BaseShellDialogImpl { bool IsRunningDialogForOwner(HWND owner) const; // Disables the window |owner|. Can be run from either the ui or the dialog - // thread. Can be called on either the UI or the dialog thread. This function - // is called on the dialog thread after the modal Windows Common dialog - // functions return because Windows automatically re-enables the owning - // window when those functions return, but we don't actually want them to be - // re-enabled until the response of the dialog propagates back to the UI - // thread, so we disable the owner manually after the Common dialog function - // returns. + // thread. This function is called on the dialog thread after the modal + // Windows Common dialog functions return because Windows automatically + // re-enables the owning window when those functions return, but we don't + // actually want them to be re-enabled until the response of the dialog + // propagates back to the UI thread, so we disable the owner manually after + // the Common dialog function returns. void DisableOwner(HWND owner); private: typedef std::set<HWND> Owners; - // Creates a thread to run a shell dialog on. Each dialog requires its own - // thread otherwise in some situations where a singleton owns a single - // instance of this object we can have a situation where a modal dialog in - // one window blocks the appearance of a modal dialog in another. - static base::Thread* CreateDialogThread(); - // Enables the window |owner_|. Can only be run from the ui thread. void EnableOwner(HWND owner); diff --git a/chromium/ui/shell_dialogs/select_file_dialog_android.cc b/chromium/ui/shell_dialogs/select_file_dialog_android.cc index 5a4fa530ce4..9b4346ca909 100644 --- a/chromium/ui/shell_dialogs/select_file_dialog_android.cc +++ b/chromium/ui/shell_dialogs/select_file_dialog_android.cc @@ -92,6 +92,15 @@ void SelectFileDialogImpl::OnFileNotSelected( listener_->FileSelectionCanceled(NULL); } +void SelectFileDialogImpl::OnContactsSelected( + JNIEnv* env, + const JavaParamRef<jobject>& java_object, + const JavaParamRef<jstring>& java_contacts) { + std::string data = ConvertJavaStringToUTF8(env, java_contacts.obj()); + listener_->FileSelectedWithExtraInfo(ui::SelectedFileInfo(), 0, + (void*)data.c_str()); +} + bool SelectFileDialogImpl::IsRunning(gfx::NativeWindow) const { return listener_; } diff --git a/chromium/ui/shell_dialogs/select_file_dialog_android.h b/chromium/ui/shell_dialogs/select_file_dialog_android.h index 5ac283abf45..719ea966689 100644 --- a/chromium/ui/shell_dialogs/select_file_dialog_android.h +++ b/chromium/ui/shell_dialogs/select_file_dialog_android.h @@ -34,6 +34,11 @@ class SelectFileDialogImpl : public SelectFileDialog { JNIEnv* env, const base::android::JavaParamRef<jobject>& java_object); + void OnContactsSelected( + JNIEnv* env, + const base::android::JavaParamRef<jobject>& java_object, + const base::android::JavaParamRef<jstring>& contacts); + // From SelectFileDialog bool IsRunning(gfx::NativeWindow) const override; void ListenerDestroyed() override; diff --git a/chromium/ui/shell_dialogs/select_file_dialog_win.cc b/chromium/ui/shell_dialogs/select_file_dialog_win.cc index c2208efd854..5f30fefc62f 100644 --- a/chromium/ui/shell_dialogs/select_file_dialog_win.cc +++ b/chromium/ui/shell_dialogs/select_file_dialog_win.cc @@ -23,7 +23,6 @@ #include "base/macros.h" #include "base/message_loop/message_loop.h" #include "base/single_thread_task_runner.h" -#include "base/threading/thread.h" #include "base/threading/thread_task_runner_handle.h" #include "base/win/registry.h" #include "base/win/shortcut.h" @@ -274,7 +273,7 @@ class SelectFileDialogImpl : public ui::SelectFileDialog, // Runs a Folder selection dialog box, passes back the selected folder in // |path| and returns true if the user clicks OK. If the user cancels the // dialog box the value in |path| is not modified and returns false. Run - // on the dialog thread. + // on the dedicated dialog sequence. bool RunSelectFolderDialog(const ExecuteSelectParams& params, base::FilePath* path); @@ -344,7 +343,7 @@ void SelectFileDialogImpl::SelectFileImpl( default_path, file_types, file_type_index, default_extension, BeginRun(owner), owner, params); - execute_params.run_state.dialog_thread->task_runner()->PostTask( + execute_params.run_state.dialog_task_runner->PostTask( FROM_HERE, base::BindOnce(&SelectFileDialogImpl::ExecuteSelectFile, this, execute_params)); } diff --git a/chromium/ui/snapshot/snapshot_aura_unittest.cc b/chromium/ui/snapshot/snapshot_aura_unittest.cc index 232b732e9f8..84854e5ec6f 100644 --- a/chromium/ui/snapshot/snapshot_aura_unittest.cc +++ b/chromium/ui/snapshot/snapshot_aura_unittest.cc @@ -10,6 +10,7 @@ #include "base/bind.h" #include "base/macros.h" #include "base/run_loop.h" +#include "base/test/scoped_task_environment.h" #include "base/test/test_simple_task_runner.h" #include "base/win/windows_version.h" #include "build/build_config.h" @@ -98,6 +99,10 @@ class SnapshotAuraTest : public testing::Test { void SetUp() override { testing::Test::SetUp(); + scoped_task_environment_ = + std::make_unique<base::test::ScopedTaskEnvironment>( + base::test::ScopedTaskEnvironment::MainThreadType::UI); + // The ContextFactory must exist before any Compositors are created. // Snapshot test tests real drawing and readback, so needs pixel output. bool enable_pixel_output = true; @@ -118,6 +123,7 @@ class SnapshotAuraTest : public testing::Test { helper_->RunAllPendingInMessageLoop(); helper_->TearDown(); ui::TerminateContextFactoryForTests(); + scoped_task_environment_.reset(); testing::Test::TearDown(); } @@ -178,6 +184,7 @@ class SnapshotAuraTest : public testing::Test { bool completed_; }; + std::unique_ptr<base::test::ScopedTaskEnvironment> scoped_task_environment_; std::unique_ptr<aura::test::AuraTestHelper> helper_; std::unique_ptr<aura::Window> test_window_; std::unique_ptr<TestPaintingWindowDelegate> delegate_; diff --git a/chromium/ui/strings/translations/ui_strings_am.xtb b/chromium/ui/strings/translations/ui_strings_am.xtb index 654af29168e..83623cebe78 100644 --- a/chromium/ui/strings/translations/ui_strings_am.xtb +++ b/chromium/ui/strings/translations/ui_strings_am.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{ከ1 ደቂቃ በፊት}one{ከ# ደቂቃዎች በፊት}other{ከ# ደቂቃዎች በፊት}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1ደ}one{#ደ}other{#ደ}}</translation> <translation id="1243314992276662751">ስቀል</translation> +<translation id="1269641567813814718">ማሸነፍ</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">የሚመከሩ መተግበሪያዎች</translation> <translation id="1368832886055348810">ከግራ ወደ ቀኝ</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">አስገባ</translation> <translation id="8210608804940886430">ወደታች አንቀሳቅስ</translation> <translation id="8245914219290430011">ትር</translation> +<translation id="8259556432390118667">የአስራስድስትዮሽ ቀለም እሴት</translation> <translation id="8328145009876646418">የግራ ጠርዝ</translation> <translation id="8331626408530291785">ወደ ላይ ሸብልል</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 አመት}one{# ዓመቶች}other{# ዓመቶች}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">እንደወረደ</translation> <translation id="8806053966018712535">አቃፊ <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">ምስልን አብራራ</translation> +<translation id="8841375032071747811">የተመለስ አዝራር</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> ኪባ</translation> <translation id="9002566407876343676">ክፈት</translation> <translation id="9038489124413477075">ያልተሰየመ አቃፊ</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ar.xtb b/chromium/ui/strings/translations/ui_strings_ar.xtb index b212cd8a850..5e810ada0ea 100644 --- a/chromium/ui/strings/translations/ui_strings_ar.xtb +++ b/chromium/ui/strings/translations/ui_strings_ar.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{قبل دقيقة واحدة}zero{قبل # دقيقة}two{قبل دقيقتين (#)}few{قبل # دقائق}many{قبل # دقيقة}other{قبل # دقيقة}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{دقيقة واحدة}zero{# دقيقة}two{دقيقتان (#)}few{# دقائق}many{# دقيقة}other{# دقيقة}}</translation> <translation id="1243314992276662751">تحميل</translation> +<translation id="1269641567813814718">الفوز</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">تطبيقات مقترحة</translation> <translation id="1368832886055348810">من اليسار لليمين</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">صفحة إلى أسفل</translation> <translation id="8245914219290430011">علامة تبويب</translation> +<translation id="8259556432390118667">القيمة السداسية للّون</translation> <translation id="8328145009876646418">الحافة اليمنى</translation> <translation id="8331626408530291785">التمرير إلى أعلى</translation> <translation id="8352146631962686268">{YEARS,plural, =1{سنة واحدة}zero{# سنة}two{سنتان (#)}few{# سنوات}many{# سنةً}other{# سنة}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">التلقائي</translation> <translation id="8806053966018712535">مجلد <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">إضافة تعليق توضيحي على الصورة</translation> +<translation id="8841375032071747811">زر الرجوع</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> كيلوبايت</translation> <translation id="9002566407876343676">الفتح</translation> <translation id="9038489124413477075">مجلد بدون اسم</translation> diff --git a/chromium/ui/strings/translations/ui_strings_bg.xtb b/chromium/ui/strings/translations/ui_strings_bg.xtb index da271229490..e1a2be58747 100644 --- a/chromium/ui/strings/translations/ui_strings_bg.xtb +++ b/chromium/ui/strings/translations/ui_strings_bg.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{Преди 1 минута}other{Преди # минути}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 мин}other{# мин}}</translation> <translation id="1243314992276662751">Качване</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">ПРЕПОРЪЧАНИ ПРИЛОЖЕНИЯ</translation> <translation id="1368832886055348810">Отляво надясно</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">Страница надолу</translation> <translation id="8245914219290430011">Табулатор</translation> +<translation id="8259556432390118667">Шестнадесетична стойност за цвета</translation> <translation id="8328145009876646418">Ляв край</translation> <translation id="8331626408530291785">Превъртане нагоре</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 година}other{# години}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">По подразбиране</translation> <translation id="8806053966018712535">Папка „<ph name="FOLDER_NAME" />“</translation> <translation id="883911313571074303">Добавяне на пояснение към изображението</translation> +<translation id="8841375032071747811">Бутон за връщане назад</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KБ</translation> <translation id="9002566407876343676">отварям</translation> <translation id="9038489124413477075">Папка без име</translation> diff --git a/chromium/ui/strings/translations/ui_strings_bn.xtb b/chromium/ui/strings/translations/ui_strings_bn.xtb index 97d034f74d3..3ed795a58e5 100644 --- a/chromium/ui/strings/translations/ui_strings_bn.xtb +++ b/chromium/ui/strings/translations/ui_strings_bn.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{১ মিনিট আগে}one{# মিনিট আগে}other{# মিনিট আগে}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{১ মিনিট}one{# মিনিট}other{# মিনিট}}</translation> <translation id="1243314992276662751">আপলোড</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">প্রস্তাবিত অ্যাপ</translation> <translation id="1368832886055348810">বাঁ থেকে ডান</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">পৃষ্ঠা উপরে</translation> <translation id="8245914219290430011">ট্যাব</translation> +<translation id="8259556432390118667">হেক্স রঙের মান</translation> <translation id="8328145009876646418">বাঁ প্রান্ত</translation> <translation id="8331626408530291785">উপরে স্ক্রোল করুন</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 বছর}one{# বছর}other{# বছর}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">ডিফল্ট</translation> <translation id="8806053966018712535"><ph name="FOLDER_NAME" /> ফোল্ডার</translation> <translation id="883911313571074303">চিত্রের জন্য টিকা লিখুন</translation> +<translation id="8841375032071747811">ফিরে যাওয়ার বোতাম</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">খুলুন</translation> <translation id="9038489124413477075">নামবিহীন ফোল্ডার</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ca.xtb b/chromium/ui/strings/translations/ui_strings_ca.xtb index dec5fd41189..9020f20262a 100644 --- a/chromium/ui/strings/translations/ui_strings_ca.xtb +++ b/chromium/ui/strings/translations/ui_strings_ca.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{Fa 1 minut}other{Fa # minuts}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 m}other{# m}}</translation> <translation id="1243314992276662751">Penja</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">APLICACIONS RECOMANADES</translation> <translation id="1368832886055348810">D'esquerra a dreta</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Retorn</translation> <translation id="8210608804940886430">Av Pàg</translation> <translation id="8245914219290430011">Tabulador</translation> +<translation id="8259556432390118667">Valor del color hexadecimal</translation> <translation id="8328145009876646418">Extrem esquerre</translation> <translation id="8331626408530291785">Desplaçament amunt</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 any}other{# anys}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Predeterminat</translation> <translation id="8806053966018712535">Carpeta <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Afegeix una anotació a la imatge</translation> +<translation id="8841375032071747811">Botó Enrere</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">obrir</translation> <translation id="9038489124413477075">Carpeta sense nom</translation> diff --git a/chromium/ui/strings/translations/ui_strings_cs.xtb b/chromium/ui/strings/translations/ui_strings_cs.xtb index 39a8bc4c5b6..fe6c504aa2d 100644 --- a/chromium/ui/strings/translations/ui_strings_cs.xtb +++ b/chromium/ui/strings/translations/ui_strings_cs.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{před minutou}few{před # minutami}many{před # minuty}other{před # minutami}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 min}few{# min}many{# min}other{# min}}</translation> <translation id="1243314992276662751">Nahrát</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Klávesa Esc</translation> <translation id="1306549533752902673">DOPORUČENÉ APLIKACE</translation> <translation id="1368832886055348810">Zleva doprava</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">Klávesa PageDown</translation> <translation id="8245914219290430011">Tab</translation> +<translation id="8259556432390118667">Hexadecimální kód barvy</translation> <translation id="8328145009876646418">Levý okraj</translation> <translation id="8331626408530291785">Posuv nahoru</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 rok}few{# roky}many{# roku}other{# let}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Výchozí</translation> <translation id="8806053966018712535">Složka <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Přidat k obrázku poznámku</translation> +<translation id="8841375032071747811">Tlačítko Zpět</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> kB</translation> <translation id="9002566407876343676">otevřít</translation> <translation id="9038489124413477075">Nepojmenovaná složka</translation> diff --git a/chromium/ui/strings/translations/ui_strings_da.xtb b/chromium/ui/strings/translations/ui_strings_da.xtb index b8a2327aa1c..847fd83e9f7 100644 --- a/chromium/ui/strings/translations/ui_strings_da.xtb +++ b/chromium/ui/strings/translations/ui_strings_da.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{For 1 minut siden}one{For # minut siden}other{For # minutter siden}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 min.}one{# min.}other{# min.}}</translation> <translation id="1243314992276662751">Upload</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">ANBEFALEDE APPS</translation> <translation id="1368832886055348810">Venstre til højre</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">Side ned</translation> <translation id="8245914219290430011">Tabulatortast</translation> +<translation id="8259556432390118667">Hex-farveværdi</translation> <translation id="8328145009876646418">Venstre kant</translation> <translation id="8331626408530291785">Scroll Up</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 år}one{# år}other{# år}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Standard</translation> <translation id="8806053966018712535">Mappen <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Annoter billede</translation> +<translation id="8841375032071747811">Knappen Tilbage</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> kB</translation> <translation id="9002566407876343676">åbn</translation> <translation id="9038489124413477075">Unavngiven mappe</translation> diff --git a/chromium/ui/strings/translations/ui_strings_de.xtb b/chromium/ui/strings/translations/ui_strings_de.xtb index 08739f8213b..47df84f9805 100644 --- a/chromium/ui/strings/translations/ui_strings_de.xtb +++ b/chromium/ui/strings/translations/ui_strings_de.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{Vor 1 Minute}other{Vor # Minuten}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 min}other{# min}}</translation> <translation id="1243314992276662751">Hochladen</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">EMPFOHLENE APPS</translation> <translation id="1368832886055348810">Rechtsläufig</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">Nach unten</translation> <translation id="8245914219290430011">Tabulatortaste</translation> +<translation id="8259556432390118667">Hex-Farbwert</translation> <translation id="8328145009876646418">Linker Rand</translation> <translation id="8331626408530291785">Nach oben blättern</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 Jahr}other{# Jahre}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Standardeinstellung</translation> <translation id="8806053966018712535">Ordner <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Bild mit Anmerkung versehen</translation> +<translation id="8841375032071747811">Schaltfläche "Zurück"</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">Öffnen</translation> <translation id="9038489124413477075">Unbenannter Ordner</translation> diff --git a/chromium/ui/strings/translations/ui_strings_el.xtb b/chromium/ui/strings/translations/ui_strings_el.xtb index dfba157889d..f1cca4202c7 100644 --- a/chromium/ui/strings/translations/ui_strings_el.xtb +++ b/chromium/ui/strings/translations/ui_strings_el.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{Πριν από 1 λεπτό}other{Πριν από # λεπτά}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1λ.}other{#λ.}}</translation> <translation id="1243314992276662751">Μεταφόρτωση</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">ΠΡΟΤΕΙΝΟΜΕΝΕΣ ΕΦΑΡΜΟΓΕΣ</translation> <translation id="1368832886055348810">Από αριστερά προς τα δεξιά</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">Επόμενη σελίδα</translation> <translation id="8245914219290430011">Πλήκτρο tab</translation> +<translation id="8259556432390118667">Δεκαεξαδική τιμή χρώματος</translation> <translation id="8328145009876646418">Αριστερή άκρη</translation> <translation id="8331626408530291785">Κύλιση επάνω</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 χρόνος}other{# χρόνια}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Προεπιλογή</translation> <translation id="8806053966018712535">Φάκελος <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Σχολιασμός εικόνας</translation> +<translation id="8841375032071747811">Κουμπί "Πίσω"</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">ανοίγω</translation> <translation id="9038489124413477075">Φάκελος χωρίς όνομα</translation> diff --git a/chromium/ui/strings/translations/ui_strings_en-GB.xtb b/chromium/ui/strings/translations/ui_strings_en-GB.xtb index 01dfc8c5cf9..cda9340e79c 100644 --- a/chromium/ui/strings/translations/ui_strings_en-GB.xtb +++ b/chromium/ui/strings/translations/ui_strings_en-GB.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{1 minute ago}other{# minutes ago}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 m}other{# m}}</translation> <translation id="1243314992276662751">Upload</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">RECOMMENDED APPS</translation> <translation id="1368832886055348810">Left to Right</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">Page Down</translation> <translation id="8245914219290430011">Tab</translation> +<translation id="8259556432390118667">Hex colour value</translation> <translation id="8328145009876646418">Left Edge</translation> <translation id="8331626408530291785">Scroll Up</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 year}other{# years}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Default</translation> <translation id="8806053966018712535">Folder <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Annotate image</translation> +<translation id="8841375032071747811">Back button</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">open</translation> <translation id="9038489124413477075">Unnamed Folder</translation> diff --git a/chromium/ui/strings/translations/ui_strings_es-419.xtb b/chromium/ui/strings/translations/ui_strings_es-419.xtb index fef2f136abf..2ceb6b6d7b3 100644 --- a/chromium/ui/strings/translations/ui_strings_es-419.xtb +++ b/chromium/ui/strings/translations/ui_strings_es-419.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{Hace 1 minuto}other{Hace # minutos}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 min}other{# min}}</translation> <translation id="1243314992276662751">Cargar</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">APPS RECOMENDADAS</translation> <translation id="1368832886055348810">De izquierda a derecha</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Intro</translation> <translation id="8210608804940886430">Avanzar página</translation> <translation id="8245914219290430011">Tabulador</translation> +<translation id="8259556432390118667">Valor de color hexadecimal</translation> <translation id="8328145009876646418">Borde izquierdo</translation> <translation id="8331626408530291785">Desplazar hacia arriba</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 año}other{# años}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Predeterminado</translation> <translation id="8806053966018712535">Carpeta <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Escribir en la imagen</translation> +<translation id="8841375032071747811">Botón Atrás</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">abrir</translation> <translation id="9038489124413477075">Carpeta sin nombre</translation> diff --git a/chromium/ui/strings/translations/ui_strings_es.xtb b/chromium/ui/strings/translations/ui_strings_es.xtb index 6eb857a643d..ad68eca086e 100644 --- a/chromium/ui/strings/translations/ui_strings_es.xtb +++ b/chromium/ui/strings/translations/ui_strings_es.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{Hace 1 minuto}other{Hace # minutos}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 min}other{# min}}</translation> <translation id="1243314992276662751">Subir</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">APLICACIONES RECOMENDADAS</translation> <translation id="1368832886055348810">De izquierda a derecha</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Intro</translation> <translation id="8210608804940886430">Avanzar página</translation> <translation id="8245914219290430011">Tabulador</translation> +<translation id="8259556432390118667">Valor de color hexadecimal</translation> <translation id="8328145009876646418">Borde izquierdo</translation> <translation id="8331626408530291785">Desplazar hacia arriba</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 año}other{# años}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Predeterminado</translation> <translation id="8806053966018712535">Carpeta <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Anotar imagen</translation> +<translation id="8841375032071747811">Botón Atrás</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">abrir</translation> <translation id="9038489124413477075">Carpeta sin nombre</translation> diff --git a/chromium/ui/strings/translations/ui_strings_et.xtb b/chromium/ui/strings/translations/ui_strings_et.xtb index 7eac371d49a..154405d6628 100644 --- a/chromium/ui/strings/translations/ui_strings_et.xtb +++ b/chromium/ui/strings/translations/ui_strings_et.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{1 minut tagasi}other{# minutit tagasi}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1m}other{#m}}</translation> <translation id="1243314992276662751">Laadi üles</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">SOOVITATUD RAKENDUSED</translation> <translation id="1368832886055348810">Left to Right (Vasakult paremale)</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Sisestusklahv</translation> <translation id="8210608804940886430">Lehekülje lõppu</translation> <translation id="8245914219290430011">Vahekaart</translation> +<translation id="8259556432390118667">Värvi kuueteistkümnendväärtus</translation> <translation id="8328145009876646418">Vasak serv</translation> <translation id="8331626408530291785">Keri üles</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 aasta}other{# aastat}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Vaikimisi</translation> <translation id="8806053966018712535">Kaust <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Lisa kujutisele märkused</translation> +<translation id="8841375032071747811">Nupp Tagasi</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> kB</translation> <translation id="9002566407876343676">avage</translation> <translation id="9038489124413477075">Nimeta kaust</translation> diff --git a/chromium/ui/strings/translations/ui_strings_fa.xtb b/chromium/ui/strings/translations/ui_strings_fa.xtb index 513e1464e6d..38102551c88 100644 --- a/chromium/ui/strings/translations/ui_strings_fa.xtb +++ b/chromium/ui/strings/translations/ui_strings_fa.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{۱ دقیقه قبل}one{# دقیقه قبل}other{# دقیقه قبل}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{۱ دقیقه}one{# دقیقه}other{# دقیقه}}</translation> <translation id="1243314992276662751">بارگذاری</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">برنامههای توصیهشده</translation> <translation id="1368832886055348810">چپ به راست</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">ورود</translation> <translation id="8210608804940886430">صفحه پایین</translation> <translation id="8245914219290430011">Tab</translation> +<translation id="8259556432390118667">مقدار رنگ در مبنای شانزده</translation> <translation id="8328145009876646418">حاشیه چپ</translation> <translation id="8331626408530291785">پیمایش به بالا</translation> <translation id="8352146631962686268">{YEARS,plural, =1{۱ سال}one{# سال}other{# سال}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">پیشفرض</translation> <translation id="8806053966018712535">پوشه <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">حاشیهنویسی تصویر</translation> +<translation id="8841375032071747811">دکمه برگشت</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> کیلوبایت</translation> <translation id="9002566407876343676">باز کنید</translation> <translation id="9038489124413477075">پوشه بدون نام</translation> diff --git a/chromium/ui/strings/translations/ui_strings_fi.xtb b/chromium/ui/strings/translations/ui_strings_fi.xtb index 42f06a67df3..3deafdc6e5b 100644 --- a/chromium/ui/strings/translations/ui_strings_fi.xtb +++ b/chromium/ui/strings/translations/ui_strings_fi.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{1 minuutti sitten}other{# minuuttia sitten}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 min}other{# min}}</translation> <translation id="1243314992276662751">Lähetä</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">SOVELLUSSUOSITUKSET</translation> <translation id="1368832886055348810">Vasemmalta oikealle</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">Sivu alas</translation> <translation id="8245914219290430011">Sarkain</translation> +<translation id="8259556432390118667">Värin heksadesimaaliarvo</translation> <translation id="8328145009876646418">Vasen reuna</translation> <translation id="8331626408530291785">Vieritä ylös</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 vuosi}other{# vuotta}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Oletus</translation> <translation id="8806053966018712535">Kansio <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Lisää kuvaan muistiinpano</translation> +<translation id="8841375032071747811">Takaisin-painike</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> kt</translation> <translation id="9002566407876343676">avata</translation> <translation id="9038489124413477075">Nimetön kansio</translation> diff --git a/chromium/ui/strings/translations/ui_strings_fil.xtb b/chromium/ui/strings/translations/ui_strings_fil.xtb index 33f399ad03b..14edf08b529 100644 --- a/chromium/ui/strings/translations/ui_strings_fil.xtb +++ b/chromium/ui/strings/translations/ui_strings_fil.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{1 minuto ang nakalipas}one{# minuto ang nakalipas}other{# na minuto ang nakalipas}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 minuto}one{# minuto}other{# na minuto}}</translation> <translation id="1243314992276662751">I-upload</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">MGA INIREREKOMENDANG APP</translation> <translation id="1368832886055348810">Kaliwa papuntang Kanan</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">Page Down</translation> <translation id="8245914219290430011">Tab</translation> +<translation id="8259556432390118667">Value ng kulay ng hex</translation> <translation id="8328145009876646418">Kalwang Edge</translation> <translation id="8331626408530291785">Mag-scroll Pataas</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 taon}one{# taon}other{# na taon}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Default</translation> <translation id="8806053966018712535">Folder <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">I-annotate ang larawan</translation> +<translation id="8841375032071747811">Button na Bumalik</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">buksan</translation> <translation id="9038489124413477075">Walang Pangalan na Folder</translation> diff --git a/chromium/ui/strings/translations/ui_strings_fr.xtb b/chromium/ui/strings/translations/ui_strings_fr.xtb index 3721d9b2ce5..b5dfcea2e3e 100644 --- a/chromium/ui/strings/translations/ui_strings_fr.xtb +++ b/chromium/ui/strings/translations/ui_strings_fr.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{Il y a une minute}one{Il y a # minute}other{Il y a # minutes}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 m}one{# m}other{# m}}</translation> <translation id="1243314992276662751">Importer</translation> +<translation id="1269641567813814718">Windows</translation> <translation id="1293699935367580298">Échap</translation> <translation id="1306549533752902673">APPLICATIONS RECOMMANDÉES</translation> <translation id="1368832886055348810">De gauche à droite</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Entrée</translation> <translation id="8210608804940886430">Page suivante</translation> <translation id="8245914219290430011">Tabulation</translation> +<translation id="8259556432390118667">Valeur de couleur hexadécimale</translation> <translation id="8328145009876646418">Côté gauche</translation> <translation id="8331626408530291785">Défilement vers le haut</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 an}one{# an}other{# ans}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Par défaut</translation> <translation id="8806053966018712535">Dossier <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Annoter l'image</translation> +<translation id="8841375032071747811">Bouton Retour</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> Ko</translation> <translation id="9002566407876343676">ouvrir</translation> <translation id="9038489124413477075">Dossier sans nom</translation> diff --git a/chromium/ui/strings/translations/ui_strings_gu.xtb b/chromium/ui/strings/translations/ui_strings_gu.xtb index 61f663f5f16..d1acf345a72 100644 --- a/chromium/ui/strings/translations/ui_strings_gu.xtb +++ b/chromium/ui/strings/translations/ui_strings_gu.xtb @@ -6,12 +6,13 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{1 મિનિટ પહેલાં}one{# મિનિટ પહેલાં}other{# મિનિટ પહેલાં}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 મિ}one{# મિ}other{# મિ}}</translation> <translation id="1243314992276662751">અપલોડ કરો</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">ભલામણ કરેલ ઍપ</translation> <translation id="1368832886055348810">ડાબેથી જમણે</translation> <translation id="1383876407941801731">શોધો</translation> <translation id="1398853756734560583">મોટું કરો</translation> -<translation id="1413622004203049571"><ph name="NOTIFIER_NAME" /> તરફથી સૂચનાઓ અક્ષમ કરો</translation> +<translation id="1413622004203049571"><ph name="NOTIFIER_NAME" /> તરફથી સૂચનાઓ બંધ કરો</translation> <translation id="1591184457164800433">{MINUTES,plural, =1{1 મિનિટ અને }one{# મિનિટ અને }other{# મિનિટ અને }}</translation> <translation id="1643823602425662293">સૂચના</translation> <translation id="169515659049020177">Shift</translation> @@ -43,7 +44,7 @@ <translation id="2497284189126895209">બધી ફાઇલો</translation> <translation id="2515586267016047495">Alt</translation> <translation id="2522350507219695259">કૅલિબ્રેશન પૂર્ણ થયું</translation> -<translation id="252373100621549798">અજ્ઞાત પ્રદર્શન</translation> +<translation id="252373100621549798">અજાણ્યું ડિસ્પ્લે</translation> <translation id="2583543531130364912">તમારી ટચસ્ક્રીનને કેલિબ્રેટ કરો</translation> <translation id="2666092431469916601">ઉપર</translation> <translation id="2743387203779672305">ક્લિપબોર્ડ પર કૉપિ કરો</translation> @@ -68,7 +69,7 @@ <translation id="3842239759367498783">તમારા મોબાઇલ ઉપકરણ <ph name="TITLE" />માંથી વાંચવાનું ચાલુ રાખો</translation> <translation id="385051799172605136">પાછળ</translation> <translation id="3889424535448813030">જમણો એરો</translation> -<translation id="3892641579809465218">આંતરિક પ્રદર્શન</translation> +<translation id="3892641579809465218">આંતરિક ડિસ્પ્લે</translation> <translation id="3897092660631435901">મેનૂ</translation> <translation id="3909791450649380159">કા&પો</translation> <translation id="3990502903496589789">જમણી કિનારી</translation> @@ -83,7 +84,7 @@ <translation id="4788285488841504513">{MONTHS,plural, =1{1 મહિનો બાકી}one{# મહિનો બાકી}other{# મહિના બાકી}}</translation> <translation id="4841881773802181781">ઉમેરો</translation> <translation id="4968171027979920686">{SECONDS,plural, =1{1 સેકન્ડ}one{# સેકન્ડ}other{# સેકન્ડ}}</translation> -<translation id="4971687151119236543">મીડિયા પહેલાંનું ટ્રૅક</translation> +<translation id="4971687151119236543">મીડિયા પાછલું ગીત</translation> <translation id="5046499563572181734">અહીં ટૅપ કરો</translation> <translation id="5076340679995252485">પેસ્ટ કરો</translation> <translation id="5137751499640340777">Google આસિસ્ટંટ શરૂ કરો</translation> @@ -128,7 +129,7 @@ <translation id="6845383723252244143">ફોલ્ડર પસંદ કરો</translation> <translation id="6845533974506654842">દબાવો</translation> <translation id="6863590663815976734">{HOURS,plural, =1{1 કલાક બાકી}one{# કલાક બાકી}other{# કલાક બાકી}}</translation> -<translation id="688711909580084195">શીર્ષક વિનાનું વેબપૃષ્ઠ </translation> +<translation id="688711909580084195">શીર્ષક વિનાનું વેબપેજ</translation> <translation id="6907759265145635167"><ph name="QUANTITY" /> PB/s</translation> <translation id="6917971086528278418">{YEARS,plural, =1{1 વર્ષ બાકી}one{# વર્ષ બાકી}other{# વર્ષ બાકી}}</translation> <translation id="6945221475159498467">પસંદ કરો</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">પૃષ્ઠ નીચે</translation> <translation id="8245914219290430011">ટૅબ</translation> +<translation id="8259556432390118667">રંગનું હેક્સ મૂલ્ય</translation> <translation id="8328145009876646418">ડાબી કિનારી</translation> <translation id="8331626408530291785">ઉપર સ્ક્રોલ કરો</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 વર્ષ}one{# વર્ષ}other{# વર્ષ}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">ડિફૉલ્ટ</translation> <translation id="8806053966018712535"><ph name="FOLDER_NAME" /> ફોલ્ડર</translation> <translation id="883911313571074303">છબીમાં ટીકા કરો</translation> +<translation id="8841375032071747811">પાછળ બટન</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">ખોલો</translation> <translation id="9038489124413477075">અનામાંકિત ફોલ્ડર</translation> diff --git a/chromium/ui/strings/translations/ui_strings_hi.xtb b/chromium/ui/strings/translations/ui_strings_hi.xtb index 79f3983c19c..a8d86000ed5 100644 --- a/chromium/ui/strings/translations/ui_strings_hi.xtb +++ b/chromium/ui/strings/translations/ui_strings_hi.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{1 मिनट पहले}one{# मिनट पहले}other{# मिनट पहले}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 मिनट}one{# मिनट}other{# मिनट}}</translation> <translation id="1243314992276662751">अपलोड करें</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">सुझाए गए ऐप्लिकेशन</translation> <translation id="1368832886055348810">बाएं से दाएं</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">Page Down</translation> <translation id="8245914219290430011">टैब</translation> +<translation id="8259556432390118667">हेक्स रंग मान</translation> <translation id="8328145009876646418">बायां सिरा</translation> <translation id="8331626408530291785">ऊपर स्क्रोल करें</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 वर्ष}one{# वर्ष}other{# वर्ष}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">सामान्य</translation> <translation id="8806053966018712535"><ph name="FOLDER_NAME" /> फ़ोल्डर</translation> <translation id="883911313571074303">व्याख्या चित्र</translation> +<translation id="8841375032071747811">वापस जाएं बटन</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> केबी</translation> <translation id="9002566407876343676">खोलें</translation> <translation id="9038489124413477075">अनाम फ़ोल्डर</translation> diff --git a/chromium/ui/strings/translations/ui_strings_hr.xtb b/chromium/ui/strings/translations/ui_strings_hr.xtb index 53be996b138..5447710ad23 100644 --- a/chromium/ui/strings/translations/ui_strings_hr.xtb +++ b/chromium/ui/strings/translations/ui_strings_hr.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{Prije 1 minute}one{prije # minute}few{prije # minute}other{prije # minuta}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 min}one{# min}few{# min}other{# min}}</translation> <translation id="1243314992276662751">Prenesi</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">PREPORUČENE APLIKACIJE</translation> <translation id="1368832886055348810">Slijeva udesno</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">Stranica prema dolje</translation> <translation id="8245914219290430011">Kartica</translation> +<translation id="8259556432390118667">Heksadecimalna vrijednost boje</translation> <translation id="8328145009876646418">Lijevi rub</translation> <translation id="8331626408530291785">Pomakni se gore</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 godina}one{# godina}few{# godine}other{# godina}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Zadano</translation> <translation id="8806053966018712535">Mapa <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Dodaj napomenu slici</translation> +<translation id="8841375032071747811">Gumb Natrag</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">otvaranje</translation> <translation id="9038489124413477075">Neimenovana mapa</translation> diff --git a/chromium/ui/strings/translations/ui_strings_hu.xtb b/chromium/ui/strings/translations/ui_strings_hu.xtb index 37d3461083a..7e0643cf914 100644 --- a/chromium/ui/strings/translations/ui_strings_hu.xtb +++ b/chromium/ui/strings/translations/ui_strings_hu.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{1 perce}other{# perce}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 p}other{# p}}</translation> <translation id="1243314992276662751">Feltöltés</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">AJÁNLOTT ALKALMAZÁSOK</translation> <translation id="1368832886055348810">Balról jobbra</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">Page Down</translation> <translation id="8245914219290430011">Lap</translation> +<translation id="8259556432390118667">Hexadecimális színérték</translation> <translation id="8328145009876646418">Bal sarok</translation> <translation id="8331626408530291785">Görgetés felfelé</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 év}other{# év}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Alapértelmezett</translation> <translation id="8806053966018712535"><ph name="FOLDER_NAME" /> mappa</translation> <translation id="883911313571074303">Megjegyzés fűzése a képhez</translation> +<translation id="8841375032071747811">Vissza gomb</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> kB</translation> <translation id="9002566407876343676">megnyitás</translation> <translation id="9038489124413477075">Név nélküli mappa</translation> diff --git a/chromium/ui/strings/translations/ui_strings_id.xtb b/chromium/ui/strings/translations/ui_strings_id.xtb index 5adc85f6491..c5349e33811 100644 --- a/chromium/ui/strings/translations/ui_strings_id.xtb +++ b/chromium/ui/strings/translations/ui_strings_id.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{1 menit yang lalu}other{# menit yang lalu}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 m}other{# m}}</translation> <translation id="1243314992276662751">Upload</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">APLIKASI YANG DIREKOMENDASIKAN</translation> <translation id="1368832886055348810">Kiri ke Kanan</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">Page Down</translation> <translation id="8245914219290430011">Tab</translation> +<translation id="8259556432390118667">Nilai warna hex</translation> <translation id="8328145009876646418">Tepi Kiri</translation> <translation id="8331626408530291785">Gulir ke Atas</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 tahun}other{# tahun}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Default</translation> <translation id="8806053966018712535">Folder <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Gambar anotasi</translation> +<translation id="8841375032071747811">Tombol kembali</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">buka</translation> <translation id="9038489124413477075">Folder Tanpa Nama</translation> diff --git a/chromium/ui/strings/translations/ui_strings_it.xtb b/chromium/ui/strings/translations/ui_strings_it.xtb index dfdeddd620e..e6b55ba6964 100644 --- a/chromium/ui/strings/translations/ui_strings_it.xtb +++ b/chromium/ui/strings/translations/ui_strings_it.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{1 minuto fa}other{# minuti fa}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 m}other{# m}}</translation> <translation id="1243314992276662751">Carica</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">APP CONSIGLIATE</translation> <translation id="1368832886055348810">Da sinistra a destra</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Invio</translation> <translation id="8210608804940886430">Pagina giù</translation> <translation id="8245914219290430011">Tabulazione</translation> +<translation id="8259556432390118667">Valore del colore esadecimale</translation> <translation id="8328145009876646418">Margine sinistro</translation> <translation id="8331626408530291785">Scorri verso l'alto</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 anno}other{# anni}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Predefinito</translation> <translation id="8806053966018712535">Cartella <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Annota immagine</translation> +<translation id="8841375032071747811">Pulsante Indietro</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> kB</translation> <translation id="9002566407876343676">apri</translation> <translation id="9038489124413477075">Cartella senza nome</translation> diff --git a/chromium/ui/strings/translations/ui_strings_iw.xtb b/chromium/ui/strings/translations/ui_strings_iw.xtb index 844ab24bd34..8178dc769ea 100644 --- a/chromium/ui/strings/translations/ui_strings_iw.xtb +++ b/chromium/ui/strings/translations/ui_strings_iw.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{לפני דקה אחת}two{לפני # דקות}many{לפני # דקות}other{לפני # דקות}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{דקה}two{# דק}many{# דק}other{# דק}}</translation> <translation id="1243314992276662751">העלה</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">אפליקציות מומלצות</translation> <translation id="1368832886055348810">משמאל לימין</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">דף למטה</translation> <translation id="8245914219290430011">כרטיסייה</translation> +<translation id="8259556432390118667">ערך צבע הקסדצימלי</translation> <translation id="8328145009876646418">קצה שמאלי</translation> <translation id="8331626408530291785">גלול למעלה</translation> <translation id="8352146631962686268">{YEARS,plural, =1{שנה אחת}two{שנתיים}many{# שנים}other{# שנים}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">ברירת מחדל</translation> <translation id="8806053966018712535">תיקייה <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">רשום הערה לתמונה</translation> +<translation id="8841375032071747811">לחצן 'הקודם'</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">פתח</translation> <translation id="9038489124413477075">תיקייה ללא שם</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ja.xtb b/chromium/ui/strings/translations/ui_strings_ja.xtb index c459dac1d8a..55d4dee55a6 100644 --- a/chromium/ui/strings/translations/ui_strings_ja.xtb +++ b/chromium/ui/strings/translations/ui_strings_ja.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{1 分前}other{# 分前}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1分}other{#分}}</translation> <translation id="1243314992276662751">アップロード</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">おすすめのアプリ</translation> <translation id="1368832886055348810">左から右</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">次のページへ</translation> <translation id="8245914219290430011">タブ</translation> +<translation id="8259556432390118667">16 進数色コード</translation> <translation id="8328145009876646418">左端</translation> <translation id="8331626408530291785">上にスクロール</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 年}other{# 年}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">既定</translation> <translation id="8806053966018712535">フォルダ <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">画像に注釈を付ける</translation> +<translation id="8841375032071747811">戻るボタン</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">開く</translation> <translation id="9038489124413477075">名前のないフォルダ</translation> diff --git a/chromium/ui/strings/translations/ui_strings_kn.xtb b/chromium/ui/strings/translations/ui_strings_kn.xtb index d43c9918a6f..3875572b073 100644 --- a/chromium/ui/strings/translations/ui_strings_kn.xtb +++ b/chromium/ui/strings/translations/ui_strings_kn.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{1 ನಿಮಿಷದ ಹಿಂದೆ}one{# ನಿಮಿಷಗಳ ಹಿಂದೆ}other{# ನಿಮಿಷಗಳ ಹಿಂದೆ}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1ಮೀ}one{#ಮೀ}other{#ಮೀ}}</translation> <translation id="1243314992276662751">ಅಪ್ಲೋಡ್</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">ಶಿಫಾರಸು ಮಾಡಲಾದ ಅಪ್ಲಿಕೇಶನ್ಗಳು</translation> <translation id="1368832886055348810">ಎಡದಿಂದ ಬಲಕ್ಕೆ</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">ಪುಟ ಕೆಳಗೆ</translation> <translation id="8245914219290430011">ಟ್ಯಾಬ್</translation> +<translation id="8259556432390118667">ಹೆಕ್ಸ್ ಬಣ್ಣ ಮೌಲ್ಯ</translation> <translation id="8328145009876646418">ಎಡ ಬದಿ</translation> <translation id="8331626408530291785">ಮೇಲೆ ಸ್ಕ್ರೋಲ್ ಮಾಡು</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 ವರ್ಷ}one{# ವರ್ಷಗಳು}other{# ವರ್ಷಗಳು}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">ಡಿಫಾಲ್ಟ್</translation> <translation id="8806053966018712535">ಫೋಲ್ಡರ್ <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">ಚಿತ್ರವನ್ನು ಟಿಪ್ಪಣಿ ಮಾಡಿ</translation> +<translation id="8841375032071747811">ಹಿಂದೆ ಬಟನ್</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">ತೆರೆ</translation> <translation id="9038489124413477075">ಹೆಸರಿಸದ ಫೋಲ್ಡರ್</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ko.xtb b/chromium/ui/strings/translations/ui_strings_ko.xtb index ea76ee5da19..7d7fa80827e 100644 --- a/chromium/ui/strings/translations/ui_strings_ko.xtb +++ b/chromium/ui/strings/translations/ui_strings_ko.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{1분 전}other{#분 전}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1분}other{#분}}</translation> <translation id="1243314992276662751">업로드</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">추천 앱</translation> <translation id="1368832886055348810">왼쪽에서 오른쪽으로</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">페이지 아래로</translation> <translation id="8245914219290430011">탭</translation> +<translation id="8259556432390118667">16진수 색상 값</translation> <translation id="8328145009876646418">왼쪽 모서리</translation> <translation id="8331626408530291785">위로 스크롤</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1년}other{#년}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">기본값</translation> <translation id="8806053966018712535"><ph name="FOLDER_NAME" /> 폴더</translation> <translation id="883911313571074303">이미지에 주석 달기</translation> +<translation id="8841375032071747811">뒤로 버튼</translation> <translation id="8901569739625249689"><ph name="QUANTITY" />KB</translation> <translation id="9002566407876343676">열기</translation> <translation id="9038489124413477075">이름이 없는 폴더</translation> diff --git a/chromium/ui/strings/translations/ui_strings_lt.xtb b/chromium/ui/strings/translations/ui_strings_lt.xtb index 5d1dc6431bb..ec6bc01b05a 100644 --- a/chromium/ui/strings/translations/ui_strings_lt.xtb +++ b/chromium/ui/strings/translations/ui_strings_lt.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{Prieš 1 minutę}one{Prieš # minutę}few{Prieš # minutes}many{Prieš # minutės}other{Prieš # minučių}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 min.}one{# min.}few{# min.}many{# min.}other{# min.}}</translation> <translation id="1243314992276662751">Įkelti</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">REKOMENDUOJAMOS PROGRAMOS</translation> <translation id="1368832886055348810">Iš kairės į dešinę</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Įvesti</translation> <translation id="8210608804940886430">Puslapį žemyn</translation> <translation id="8245914219290430011">Skirtukas</translation> +<translation id="8259556432390118667">Šešioliktainės spalvos vertė</translation> <translation id="8328145009876646418">Kairysis kraštas</translation> <translation id="8331626408530291785">Slinkti į viršų</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 metai}one{# metai}few{# metai}many{# metų}other{# metų}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Numatytasis</translation> <translation id="8806053966018712535">Aplankas „<ph name="FOLDER_NAME" />“</translation> <translation id="883911313571074303">Komentuoti vaizdą</translation> +<translation id="8841375032071747811">Mygtukas „Atgal“</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">atidaryti</translation> <translation id="9038489124413477075">Aplankas be pavadinimo</translation> diff --git a/chromium/ui/strings/translations/ui_strings_lv.xtb b/chromium/ui/strings/translations/ui_strings_lv.xtb index ef7ea461f7f..621829fab9a 100644 --- a/chromium/ui/strings/translations/ui_strings_lv.xtb +++ b/chromium/ui/strings/translations/ui_strings_lv.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{Pirms 1 minūtes}zero{Pirms # minūtēm}one{Pirms # minūtes}other{Pirms # minūtēm}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 min}zero{# min}one{# min}other{# min}}</translation> <translation id="1243314992276662751">Augšupielādēt</translation> +<translation id="1269641567813814718">Windows</translation> <translation id="1293699935367580298">Atsolis</translation> <translation id="1306549533752902673">IETEIKTĀS LIETOTNES</translation> <translation id="1368832886055348810">No kreisās uz labo pusi</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Ievadīt</translation> <translation id="8210608804940886430">Lejup</translation> <translation id="8245914219290430011">Tabulēšanas taustiņš</translation> +<translation id="8259556432390118667">Heksadecimāla krāsas vērtība</translation> <translation id="8328145009876646418">Kreisā mala</translation> <translation id="8331626408530291785">Ritināt augšup</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 gads}zero{# gadu}one{# gads}other{# gadi}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Noklusējums</translation> <translation id="8806053966018712535">Mape <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Anotēt attēlu</translation> +<translation id="8841375032071747811">Poga Atpakaļ</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">atvērt</translation> <translation id="9038489124413477075">Mape bez nosaukuma</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ml.xtb b/chromium/ui/strings/translations/ui_strings_ml.xtb index d44fc29a0e9..30a79e79ec1 100644 --- a/chromium/ui/strings/translations/ui_strings_ml.xtb +++ b/chromium/ui/strings/translations/ui_strings_ml.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{ഒരു മിനിറ്റിന് മുമ്പ്}other{# മിനിറ്റ് മുമ്പ്}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{ 1 മീറ്റര്}other{# മീറ്റർ}}</translation> <translation id="1243314992276662751">അപ്ലോഡുചെയ്യുക</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">ശുപാർശിത ആപ്പുകൾ</translation> <translation id="1368832886055348810">ഇടതുനിന്ന് വലത്തേക്ക്</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">താഴെയുള്ള പേജുകള്</translation> <translation id="8245914219290430011">ടാബ്</translation> +<translation id="8259556432390118667">ഹെക്സ് വർണ മൂല്യം</translation> <translation id="8328145009876646418">ഇടത് അഗ്രം</translation> <translation id="8331626408530291785">മുകളിലേക്ക് സ്ക്രോള് ചെയ്യൂ</translation> <translation id="8352146631962686268">{YEARS,plural, =1{ഒരു വര്ഷം}other{# വർഷം}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">സ്ഥിരസ്ഥിതി</translation> <translation id="8806053966018712535">ഫോൾഡർ <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">ചിത്രം വ്യാഖ്യാനിക്കുക</translation> +<translation id="8841375032071747811">ബാക്ക് ബട്ടൺ</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">തുറക്കുക</translation> <translation id="9038489124413477075">പേരിടാത്ത ഫോൾഡർ</translation> diff --git a/chromium/ui/strings/translations/ui_strings_mr.xtb b/chromium/ui/strings/translations/ui_strings_mr.xtb index dfd03abac5e..3c0483238e5 100644 --- a/chromium/ui/strings/translations/ui_strings_mr.xtb +++ b/chromium/ui/strings/translations/ui_strings_mr.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{1 मिनिटापूर्वी}one{# मिनिटापूर्वी}other{# मिनिटांपूर्वी}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1मि}one{#मि}other{#मि}}</translation> <translation id="1243314992276662751">अपलोड करा</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">शिफारस केलेले अॅप्स</translation> <translation id="1368832886055348810">डावीकडून उजवीकडे</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">पृष्ठ खाली</translation> <translation id="8245914219290430011">टॅब</translation> +<translation id="8259556432390118667">हेक्स रंग मूल्य</translation> <translation id="8328145009876646418">डावे काठ</translation> <translation id="8331626408530291785">वर स्क्रोल करा</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 वर्ष}one{# वर्ष}other{# वर्षे}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">डीफॉल्ट</translation> <translation id="8806053966018712535"><ph name="FOLDER_NAME" /> फोल्डर</translation> <translation id="883911313571074303">इमेजवर भाष्य करा</translation> +<translation id="8841375032071747811">मागे जा बटण</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">उघडा</translation> <translation id="9038489124413477075">अनामित फोल्डर</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ms.xtb b/chromium/ui/strings/translations/ui_strings_ms.xtb index ddf56c5d6ec..51591dd3ffb 100644 --- a/chromium/ui/strings/translations/ui_strings_ms.xtb +++ b/chromium/ui/strings/translations/ui_strings_ms.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{Seminit yang lalu}other{# minit yang lalu}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1m}other{#m}}</translation> <translation id="1243314992276662751">Muat naik</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">APL YANG DISYORKAN</translation> <translation id="1368832886055348810">Kiri ke Kanan</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">Ke Bawah Halaman</translation> <translation id="8245914219290430011">Tab</translation> +<translation id="8259556432390118667">Nilai warna perenambelasan</translation> <translation id="8328145009876646418">Tepi Kiri</translation> <translation id="8331626408530291785">Tatal Ke Atas</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 tahun}other{# tahun}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Lalai</translation> <translation id="8806053966018712535">Folder <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Anotasikan imej</translation> +<translation id="8841375032071747811">Butang kembali</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">buka</translation> <translation id="9038489124413477075">Folder Tanpa Nama</translation> diff --git a/chromium/ui/strings/translations/ui_strings_nl.xtb b/chromium/ui/strings/translations/ui_strings_nl.xtb index 65ebdb0427b..c8c3bb38225 100644 --- a/chromium/ui/strings/translations/ui_strings_nl.xtb +++ b/chromium/ui/strings/translations/ui_strings_nl.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{1 minuut geleden}other{# minuten geleden}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 m}other{# m}}</translation> <translation id="1243314992276662751">Uploaden</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">AANBEVOLEN APPS</translation> <translation id="1368832886055348810">Links naar rechts</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">Pagina omlaag</translation> <translation id="8245914219290430011">Tab</translation> +<translation id="8259556432390118667">Hex-kleurwaarde</translation> <translation id="8328145009876646418">Linkerzijde</translation> <translation id="8331626408530291785">Omhoog bladeren</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 jaar}other{# jaar}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Standaard</translation> <translation id="8806053966018712535">Map <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Afbeelding annoteren</translation> +<translation id="8841375032071747811">Knop Terug</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">openen</translation> <translation id="9038489124413477075">Naamloze map</translation> diff --git a/chromium/ui/strings/translations/ui_strings_no.xtb b/chromium/ui/strings/translations/ui_strings_no.xtb index bec5f749721..551c58bafa1 100644 --- a/chromium/ui/strings/translations/ui_strings_no.xtb +++ b/chromium/ui/strings/translations/ui_strings_no.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{for 1 minutt siden}other{for # minutter siden}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 m}other{# m}}</translation> <translation id="1243314992276662751">Last opp</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">ANBEFALTE APPER</translation> <translation id="1368832886055348810">Venstre til høyre</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">Ned 1 s.</translation> <translation id="8245914219290430011">Fane</translation> +<translation id="8259556432390118667">Heksadesimal fargekode</translation> <translation id="8328145009876646418">Venstre kant</translation> <translation id="8331626408530291785">Rull opp</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 år}other{# år}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Standard</translation> <translation id="8806053966018712535">Mappen <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Kommenter bildet</translation> +<translation id="8841375032071747811">Tilbakeknapp</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> kB</translation> <translation id="9002566407876343676">åpne</translation> <translation id="9038489124413477075">Mappe uten navn</translation> diff --git a/chromium/ui/strings/translations/ui_strings_pl.xtb b/chromium/ui/strings/translations/ui_strings_pl.xtb index d4f9cb40769..39c52df45dc 100644 --- a/chromium/ui/strings/translations/ui_strings_pl.xtb +++ b/chromium/ui/strings/translations/ui_strings_pl.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{minutę temu}few{# minuty temu}many{# minut temu}other{# minuty temu}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 min}few{# min}many{# min}other{# min}}</translation> <translation id="1243314992276662751">Prześlij</translation> +<translation id="1269641567813814718">Windows</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">POLECANE APLIKACJE</translation> <translation id="1368832886055348810">Od lewej do prawej</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">Strona w dół</translation> <translation id="8245914219290430011">Tab</translation> +<translation id="8259556432390118667">Wartość szesnastkowego kodu koloru</translation> <translation id="8328145009876646418">Krawędź po lewej</translation> <translation id="8331626408530291785">Przewiń w górę</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 rok}few{# lata}many{# lat}other{# roku}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Domyślny</translation> <translation id="8806053966018712535">Folder <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Dodaj notatkę na grafice</translation> +<translation id="8841375032071747811">Przycisk Wstecz</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> kB</translation> <translation id="9002566407876343676">otwórz</translation> <translation id="9038489124413477075">Folder bez nazwy</translation> diff --git a/chromium/ui/strings/translations/ui_strings_pt-BR.xtb b/chromium/ui/strings/translations/ui_strings_pt-BR.xtb index d1fd8f0e48b..84cc1d9f60a 100644 --- a/chromium/ui/strings/translations/ui_strings_pt-BR.xtb +++ b/chromium/ui/strings/translations/ui_strings_pt-BR.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{1 minuto atrás}one{# minuto atrás}other{# minutos atrás}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 min}one{# min}other{# min}}</translation> <translation id="1243314992276662751">Fazer upload</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">APPS RECOMENDADOS</translation> <translation id="1368832886055348810">Da esquerda para a direita</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Entrar</translation> <translation id="8210608804940886430">Página para baixo</translation> <translation id="8245914219290430011">Guia</translation> +<translation id="8259556432390118667">Valor de cor hexadecimal</translation> <translation id="8328145009876646418">Borda esquerda</translation> <translation id="8331626408530291785">Percorrer para cima</translation> <translation id="8352146631962686268">{YEARS,plural, =1{ ano}one{# ano}other{# anos}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Padrão</translation> <translation id="8806053966018712535">Pasta <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Fazer anotações na imagem</translation> +<translation id="8841375032071747811">Botão "Voltar"</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">abrir</translation> <translation id="9038489124413477075">Pasta sem nome</translation> diff --git a/chromium/ui/strings/translations/ui_strings_pt-PT.xtb b/chromium/ui/strings/translations/ui_strings_pt-PT.xtb index 620b5191fcf..f6590736244 100644 --- a/chromium/ui/strings/translations/ui_strings_pt-PT.xtb +++ b/chromium/ui/strings/translations/ui_strings_pt-PT.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{Há 1 minuto}other{Há # minutos}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 min}other{# min}}</translation> <translation id="1243314992276662751">Carregar</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">APLICAÇÕES RECOMENDADAS</translation> <translation id="1368832886055348810">Da esquerda para a direita</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">Página para baixo</translation> <translation id="8245914219290430011">Tabulação</translation> +<translation id="8259556432390118667">Valor de cor hexadecimal</translation> <translation id="8328145009876646418">Margem esquerda</translation> <translation id="8331626408530291785">Deslocar-se para cima</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 ano}other{# anos}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Predefinição</translation> <translation id="8806053966018712535">Pasta <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Anotar imagem</translation> +<translation id="8841375032071747811">Botão Anterior</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">abrir</translation> <translation id="9038489124413477075">Pasta sem nome</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ro.xtb b/chromium/ui/strings/translations/ui_strings_ro.xtb index 9ce3d6fee25..b3a5f77cbcc 100644 --- a/chromium/ui/strings/translations/ui_strings_ro.xtb +++ b/chromium/ui/strings/translations/ui_strings_ro.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{Acum 1 minut}few{Acum # minute}other{Acum # de minute}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 min.}few{# min.}other{# min.}}</translation> <translation id="1243314992276662751">Încărcați</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">APLICAȚII RECOMANDATE</translation> <translation id="1368832886055348810">De la stânga la dreapta</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">O pagină mai jos</translation> <translation id="8245914219290430011">Tab</translation> +<translation id="8259556432390118667">Valoarea culorii hexazecimale</translation> <translation id="8328145009876646418">Marginea stângă</translation> <translation id="8331626408530291785">Derulează în sus</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 an}few{# ani}other{# de ani}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Prestabilit</translation> <translation id="8806053966018712535">Dosarul <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Adnotează imaginea</translation> +<translation id="8841375032071747811">Butonul Înapoi</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KO</translation> <translation id="9002566407876343676">deschide</translation> <translation id="9038489124413477075">Dosar fără nume</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ru.xtb b/chromium/ui/strings/translations/ui_strings_ru.xtb index 638879b683a..b4752a9a49c 100644 --- a/chromium/ui/strings/translations/ui_strings_ru.xtb +++ b/chromium/ui/strings/translations/ui_strings_ru.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{минуту назад}one{# минуту назад}few{# минуты назад}many{# минут назад}other{# минуты назад}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 мин.}one{# мин.}few{# мин.}many{# мин.}other{# мин.}}</translation> <translation id="1243314992276662751">Загрузить</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">РЕКОМЕНДУЕМЫЕ ПРИЛОЖЕНИЯ</translation> <translation id="1368832886055348810">Слева направо</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">ВВОД</translation> <translation id="8210608804940886430">Прокрутка вниз</translation> <translation id="8245914219290430011">Tab</translation> +<translation id="8259556432390118667">Шестнадцатеричный код цвета</translation> <translation id="8328145009876646418">Левый край</translation> <translation id="8331626408530291785">Прокрутка вверх</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 год}one{# год}few{# года}many{# лет}other{# года}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">По умолчанию</translation> <translation id="8806053966018712535">Папка <ph name="FOLDER_NAME" />.</translation> <translation id="883911313571074303">Добавить примечание к изображению</translation> +<translation id="8841375032071747811">Назад</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> КБ</translation> <translation id="9002566407876343676">открыть</translation> <translation id="9038489124413477075">Без названия</translation> diff --git a/chromium/ui/strings/translations/ui_strings_sk.xtb b/chromium/ui/strings/translations/ui_strings_sk.xtb index 3688bce2913..6b8c84d28c2 100644 --- a/chromium/ui/strings/translations/ui_strings_sk.xtb +++ b/chromium/ui/strings/translations/ui_strings_sk.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{pred minútou}few{pred # minútami}many{pred # minútami}other{pred # minútami}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 min}few{# min}many{# min}other{# min}}</translation> <translation id="1243314992276662751">Nahrať</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">ODPORÚČANÉ APLIKÁCIE</translation> <translation id="1368832886055348810">Zľava doprava</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">Stránkovať nadol</translation> <translation id="8245914219290430011">Tab</translation> +<translation id="8259556432390118667">Hodnota farby v šestnástkovej sústave</translation> <translation id="8328145009876646418">Ľavý okraj</translation> <translation id="8331626408530291785">Rolovať nahor</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 rok}few{# roky}many{# roka}other{# rokov}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Predvolené</translation> <translation id="8806053966018712535">Priečinok <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Pridať k obrázku poznámku</translation> +<translation id="8841375032071747811">Tlačidlo Späť</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> kB</translation> <translation id="9002566407876343676">otvorenie</translation> <translation id="9038489124413477075">Priečinok bez názvu</translation> diff --git a/chromium/ui/strings/translations/ui_strings_sl.xtb b/chromium/ui/strings/translations/ui_strings_sl.xtb index a56bf499085..d43f759535c 100644 --- a/chromium/ui/strings/translations/ui_strings_sl.xtb +++ b/chromium/ui/strings/translations/ui_strings_sl.xtb @@ -5,6 +5,7 @@ <translation id="1127811143501539442">{DAYS,plural, =1{Pred 1 dnevom}one{Pred # dnevom}two{Pred # dnevoma}few{Pred # dnevi}other{Pred # dnevi}}</translation> <translation id="1156623771253174079">{SECONDS,plural, =1{Pred 1 minuto}one{Pred # minuto}two{Pred # minutama}few{Pred # minutami}other{Pred # minutami}}</translation> <translation id="1243314992276662751">Prenesi</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">PRIPOROČENE APLIKACIJE</translation> <translation id="1368832886055348810">Od leve proti desni</translation> @@ -150,6 +151,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">Stran dol</translation> <translation id="8245914219290430011">Zavihek</translation> +<translation id="8259556432390118667">Vrednost barve v šestnajstiškem zapisu</translation> <translation id="8328145009876646418">Levi rob</translation> <translation id="8331626408530291785">Pomik gor</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 leto}one{# leto}two{# leti}few{# leta}other{# let}}</translation> @@ -162,6 +164,7 @@ <translation id="8798099450830957504">Privzeto</translation> <translation id="8806053966018712535">Mapa <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Dodaj pripis sliki</translation> +<translation id="8841375032071747811">Gumb za nazaj</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">odpreti</translation> <translation id="9038489124413477075">Neimenovana mapa</translation> diff --git a/chromium/ui/strings/translations/ui_strings_sr.xtb b/chromium/ui/strings/translations/ui_strings_sr.xtb index 77293bd1e87..72192440d37 100644 --- a/chromium/ui/strings/translations/ui_strings_sr.xtb +++ b/chromium/ui/strings/translations/ui_strings_sr.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{Пре 1 минут}one{Пре # минут}few{Пре # минута}other{Пре # минута}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 м}one{# м}few{# м}other{# м}}</translation> <translation id="1243314992276662751">Отпреми</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">ПРЕПОРУЧЕНЕ АПЛИКАЦИЈЕ</translation> <translation id="1368832886055348810">Слева надесно</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">Страница надоле</translation> <translation id="8245914219290430011">Табулатор</translation> +<translation id="8259556432390118667">Хексадецимална вредност боје</translation> <translation id="8328145009876646418">Лева ивица</translation> <translation id="8331626408530291785">Помери нагоре</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 година}one{# година}few{# године}other{# година}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Подразумевано</translation> <translation id="8806053966018712535">Директоријум <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Додај напомену у слику</translation> +<translation id="8841375032071747811">Дугме Назад</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">отварање</translation> <translation id="9038489124413477075">Неименовани директоријум</translation> diff --git a/chromium/ui/strings/translations/ui_strings_sv.xtb b/chromium/ui/strings/translations/ui_strings_sv.xtb index ddad007d50c..54a2a2e2dd2 100644 --- a/chromium/ui/strings/translations/ui_strings_sv.xtb +++ b/chromium/ui/strings/translations/ui_strings_sv.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{1 minut sedan}other{# minuter sedan}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 min}other{# min}}</translation> <translation id="1243314992276662751">Ladda upp</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">REKOMMENDERADE APPAR</translation> <translation id="1368832886055348810">Vänster till höger</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">Page Down</translation> <translation id="8245914219290430011">Flik</translation> +<translation id="8259556432390118667">Hexadecimalt färgvärde</translation> <translation id="8328145009876646418">Vänsterkant</translation> <translation id="8331626408530291785">Rulla uppåt</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 år}other{# år}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Standard</translation> <translation id="8806053966018712535">Mappen <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Kommentera bild</translation> +<translation id="8841375032071747811">Bakåtknapp</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> kB</translation> <translation id="9002566407876343676">öppna</translation> <translation id="9038489124413477075">Namnlös mapp</translation> diff --git a/chromium/ui/strings/translations/ui_strings_sw.xtb b/chromium/ui/strings/translations/ui_strings_sw.xtb index d840c0ccf19..7826e6cc78b 100644 --- a/chromium/ui/strings/translations/ui_strings_sw.xtb +++ b/chromium/ui/strings/translations/ui_strings_sw.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{Dakika 1 iliyopita}other{Dakika # zilizopita}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{Dak 1}other{Dak #}}</translation> <translation id="1243314992276662751">Pakia</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">PROGRAMU ZINAZOPENDEKEZWA</translation> <translation id="1368832886055348810">Kushoto hadi Kulia</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">Ukurasa mmoja chini</translation> <translation id="8245914219290430011">Kichupo</translation> +<translation id="8259556432390118667">Thamani ya rangi ya hex</translation> <translation id="8328145009876646418">Ncha ya Kushoto</translation> <translation id="8331626408530291785">Sogeza Juu</translation> <translation id="8352146631962686268">{YEARS,plural, =1{Mwaka 1}other{Miaka #}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Chaguomsingi</translation> <translation id="8806053966018712535">Folda ya <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Eleza kuhusu picha</translation> +<translation id="8841375032071747811">Kitufe cha Nyuma</translation> <translation id="8901569739625249689">KB <ph name="QUANTITY" /></translation> <translation id="9002566407876343676">fungua</translation> <translation id="9038489124413477075">Folda isiyo na jina</translation> diff --git a/chromium/ui/strings/translations/ui_strings_ta.xtb b/chromium/ui/strings/translations/ui_strings_ta.xtb index 7431697282f..012be74b666 100644 --- a/chromium/ui/strings/translations/ui_strings_ta.xtb +++ b/chromium/ui/strings/translations/ui_strings_ta.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{1 நிமிடத்திற்கு முன்பு}other{# நிமிடங்களுக்கு முன்பு}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1நி}other{#நி}}</translation> <translation id="1243314992276662751">பதிவேற்று</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">பரிந்துரைக்கப்படும் பயன்பாடுகள்</translation> <translation id="1368832886055348810">இடமிருந்து வலம்</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">பக்கத்தின் கீழே</translation> <translation id="8245914219290430011">தாவல்</translation> +<translation id="8259556432390118667">ஹெக்ஸ் வண்ண மதிப்பு</translation> <translation id="8328145009876646418">இடதுபுற முனை</translation> <translation id="8331626408530291785">மேலே உருட்டு</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 ஆண்டு}other{# ஆண்டுகள்}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">இயல்புநிலை</translation> <translation id="8806053966018712535"><ph name="FOLDER_NAME" /> கோப்புறை</translation> <translation id="883911313571074303">படத்தைக் குறிப்பிடு</translation> +<translation id="8841375032071747811">முந்தையது பொத்தான்</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> கி.பை.</translation> <translation id="9002566407876343676">திற</translation> <translation id="9038489124413477075">பெயரிடப்படாதக் கோப்புறை</translation> diff --git a/chromium/ui/strings/translations/ui_strings_te.xtb b/chromium/ui/strings/translations/ui_strings_te.xtb index d435e2ad85a..b94fc4fc376 100644 --- a/chromium/ui/strings/translations/ui_strings_te.xtb +++ b/chromium/ui/strings/translations/ui_strings_te.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{1 నిమిషం క్రితం}other{# నిమిషాల క్రితం}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1ని}other{#ని}}</translation> <translation id="1243314992276662751">అప్లోడ్ చేయి</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">సిఫార్సు చేసిన యాప్లు</translation> <translation id="1368832886055348810">ఎడమ నుండి కుడికి</translation> @@ -38,7 +39,7 @@ <translation id="2289052229480071835">మీ స్క్రీన్పై ఉన్న స్పర్శ లక్ష్యాలను నొక్కండి.</translation> <translation id="2295140143284145483">సర్వే</translation> <translation id="2297836609126180313"><ph name="QUANTITY" /> TB/s</translation> -<translation id="24452542372838207">నోటిఫికేషన్ని విస్తరించు</translation> +<translation id="24452542372838207">నోటిఫికేషన్ను విస్తరించు</translation> <translation id="2482878487686419369">ప్రకటనలు</translation> <translation id="2497284189126895209">మొత్తం ఫైళ్లు</translation> <translation id="2515586267016047495">Alt</translation> @@ -92,7 +93,7 @@ <translation id="528468243742722775">ముగింపు</translation> <translation id="5329858601952122676">&తొలగించు</translation> <translation id="5463830097259460683">ఎమోజి && చిహ్నాలు</translation> -<translation id="5476505524087279545">ఎంపిక చెయ్యబడలేదు</translation> +<translation id="5476505524087279545">ఎంపిక చేయబడలేదు</translation> <translation id="5574202486608032840"><ph name="IDS_SHORT_PRODUCT_OS_NAME" /> సిస్టమ్</translation> <translation id="5583640892426849032">Backspace</translation> <translation id="5613020302032141669">ఎడమ బాణం</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">పేజీ క్రిందికి</translation> <translation id="8245914219290430011">ట్యాబ్</translation> +<translation id="8259556432390118667">హెక్స్ రంగు విలువ</translation> <translation id="8328145009876646418">ఎడమ హద్దు</translation> <translation id="8331626408530291785">పైకి స్క్రోల్ చెయ్యి</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 సంవత్సరం}other{# సంవత్సరాలు}}</translation> @@ -172,11 +174,12 @@ <translation id="8798099450830957504">డిఫాల్ట్</translation> <translation id="8806053966018712535">ఫోల్డర్ <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">చిత్రాన్ని అనులేఖించు</translation> +<translation id="8841375032071747811">వెనుకకు బటన్</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">తెరువు</translation> <translation id="9038489124413477075">పేరులేని ఫోల్డర్</translation> <translation id="9044832324875206639">{SECONDS,plural, =1{1 సెక.}other{# సెక.}}</translation> -<translation id="9059834730836941392">నోటిఫికేషన్ని కుదించు</translation> +<translation id="9059834730836941392">నోటిఫికేషన్ను కుదించు</translation> <translation id="9150735707954472829">ట్యాబ్</translation> <translation id="9161053988251441839">సూచించబడిన అనువర్తనాలు</translation> <translation id="9170848237812810038">&అన్డు</translation> diff --git a/chromium/ui/strings/translations/ui_strings_th.xtb b/chromium/ui/strings/translations/ui_strings_th.xtb index a1e943da437..632f518ccd6 100644 --- a/chromium/ui/strings/translations/ui_strings_th.xtb +++ b/chromium/ui/strings/translations/ui_strings_th.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{1 นาทีที่ผ่านมา}other{# นาทีที่ผ่านมา}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 นาที}other{# นาที}}</translation> <translation id="1243314992276662751">อัปโหลด</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">แอปแนะนำ</translation> <translation id="1368832886055348810">ซ้ายไปขวา</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">เลื่อนหน้าลง</translation> <translation id="8245914219290430011">แท็บ</translation> +<translation id="8259556432390118667">ค่าสีแบบเลขฐาน 16</translation> <translation id="8328145009876646418">ขอบซ้าย</translation> <translation id="8331626408530291785">เลื่อนขึ้น</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 ปี}other{# ปี}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">ค่าเริ่มต้น</translation> <translation id="8806053966018712535">โฟลเดอร์ <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">ใส่หมายเหตุในรูปภาพ</translation> +<translation id="8841375032071747811">ปุ่มกลับ</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">เปิด</translation> <translation id="9038489124413477075">โฟลเดอร์ที่ไม่มีชื่อ</translation> diff --git a/chromium/ui/strings/translations/ui_strings_tr.xtb b/chromium/ui/strings/translations/ui_strings_tr.xtb index abfe2111d7f..0d1d871005b 100644 --- a/chromium/ui/strings/translations/ui_strings_tr.xtb +++ b/chromium/ui/strings/translations/ui_strings_tr.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{1 dakika önce}other{# dakika önce}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 dk.}other{# dk.}}</translation> <translation id="1243314992276662751">Yükle</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">ÖNERİLEN UYGULAMALAR</translation> <translation id="1368832886055348810">Soldan Sağa</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">Page Down</translation> <translation id="8245914219290430011">Sekme</translation> +<translation id="8259556432390118667">Onaltılık renk değeri</translation> <translation id="8328145009876646418">Sol Kenar</translation> <translation id="8331626408530291785">Yukarı Kaydır</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 yıl}other{# yıl}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Varsayılan</translation> <translation id="8806053966018712535"><ph name="FOLDER_NAME" /> klasörü</translation> <translation id="883911313571074303">Resme açıklama ekle</translation> +<translation id="8841375032071747811">Geri düğmesi</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">açma</translation> <translation id="9038489124413477075">Adsız Klasör</translation> diff --git a/chromium/ui/strings/translations/ui_strings_uk.xtb b/chromium/ui/strings/translations/ui_strings_uk.xtb index 11c9c184233..bc3ffde91dc 100644 --- a/chromium/ui/strings/translations/ui_strings_uk.xtb +++ b/chromium/ui/strings/translations/ui_strings_uk.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{1 хвилину тому}one{# хвилину тому}few{# хвилини тому}many{# хвилин тому}other{# хвилини тому}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 хв}one{# хв}few{# хв}many{# хв}other{# хв}}</translation> <translation id="1243314992276662751">Завантажити</translation> +<translation id="1269641567813814718">Win</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">РЕКОМЕНДОВАНІ ПРОГРАМИ</translation> <translation id="1368832886055348810">Зліва направо</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">Сторінка вниз</translation> <translation id="8245914219290430011">Tab</translation> +<translation id="8259556432390118667">Шістнадцятковий код кольору</translation> <translation id="8328145009876646418">Лівий край</translation> <translation id="8331626408530291785">Прокрутка вгору</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 рік}one{# рік}few{# роки}many{# років}other{# року}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">За умовчанням</translation> <translation id="8806053966018712535">Папка <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Додати нотатку до зображення</translation> +<translation id="8841375032071747811">Кнопка "Назад"</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> КБ</translation> <translation id="9002566407876343676">відкрити</translation> <translation id="9038489124413477075">Папка без назви</translation> diff --git a/chromium/ui/strings/translations/ui_strings_vi.xtb b/chromium/ui/strings/translations/ui_strings_vi.xtb index 4ab508cc38e..ea8e3c7e21b 100644 --- a/chromium/ui/strings/translations/ui_strings_vi.xtb +++ b/chromium/ui/strings/translations/ui_strings_vi.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{1 phút trước}other{# phút trước}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 phút}other{# phút}}</translation> <translation id="1243314992276662751">Tải lên</translation> +<translation id="1269641567813814718">Phím Win</translation> <translation id="1293699935367580298">Thoát</translation> <translation id="1306549533752902673">ỨNG DỤNG ĐỀ XUẤT</translation> <translation id="1368832886055348810">Trái sang Phải</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">Trang Dưới</translation> <translation id="8245914219290430011">Tab</translation> +<translation id="8259556432390118667">Giá trị màu hex</translation> <translation id="8328145009876646418">Cạnh Bên trái</translation> <translation id="8331626408530291785">Cuộn Lên</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 năm}other{# năm}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">Mặc định</translation> <translation id="8806053966018712535">Thư mục <ph name="FOLDER_NAME" /></translation> <translation id="883911313571074303">Chú thích hình ảnh</translation> +<translation id="8841375032071747811">Nút quay lại</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">mở</translation> <translation id="9038489124413477075">Thư mục không có tên</translation> diff --git a/chromium/ui/strings/translations/ui_strings_zh-CN.xtb b/chromium/ui/strings/translations/ui_strings_zh-CN.xtb index 6bf7da30423..2d61a32c8e8 100644 --- a/chromium/ui/strings/translations/ui_strings_zh-CN.xtb +++ b/chromium/ui/strings/translations/ui_strings_zh-CN.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{1 分钟前}other{# 分钟前}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 分钟}other{# 分钟}}</translation> <translation id="1243314992276662751">上传</translation> +<translation id="1269641567813814718">Win 键</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">推荐的应用</translation> <translation id="1368832886055348810">从左向右</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">向下翻页</translation> <translation id="8245914219290430011">Tab</translation> +<translation id="8259556432390118667">十六进制颜色值</translation> <translation id="8328145009876646418">左边缘</translation> <translation id="8331626408530291785">向上滚动</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 年}other{# 年}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">默认</translation> <translation id="8806053966018712535">文件夹“<ph name="FOLDER_NAME" />”</translation> <translation id="883911313571074303">为图片添加注释</translation> +<translation id="8841375032071747811">“返回”按钮</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">打开</translation> <translation id="9038489124413477075">未命名的文件夹</translation> diff --git a/chromium/ui/strings/translations/ui_strings_zh-TW.xtb b/chromium/ui/strings/translations/ui_strings_zh-TW.xtb index efc43f929f4..4ef0487a835 100644 --- a/chromium/ui/strings/translations/ui_strings_zh-TW.xtb +++ b/chromium/ui/strings/translations/ui_strings_zh-TW.xtb @@ -6,6 +6,7 @@ <translation id="1156623771253174079">{SECONDS,plural, =1{1 分鐘前}other{# 分鐘前}}</translation> <translation id="1169783199079129864">{MINUTES,plural, =1{1 分鐘}other{# 分鐘}}</translation> <translation id="1243314992276662751">上傳</translation> +<translation id="1269641567813814718">Windows 鍵</translation> <translation id="1293699935367580298">Esc</translation> <translation id="1306549533752902673">推薦應用程式</translation> <translation id="1368832886055348810">由左至右</translation> @@ -157,6 +158,7 @@ <translation id="8179976553408161302">Enter</translation> <translation id="8210608804940886430">向下翻頁</translation> <translation id="8245914219290430011">Tab 鍵</translation> +<translation id="8259556432390118667">16 進位色彩值</translation> <translation id="8328145009876646418">左邊緣</translation> <translation id="8331626408530291785">向上捲動</translation> <translation id="8352146631962686268">{YEARS,plural, =1{1 年}other{# 年}}</translation> @@ -172,6 +174,7 @@ <translation id="8798099450830957504">預設</translation> <translation id="8806053966018712535"><ph name="FOLDER_NAME" />資料夾</translation> <translation id="883911313571074303">為圖片加註</translation> +<translation id="8841375032071747811">返回按鈕</translation> <translation id="8901569739625249689"><ph name="QUANTITY" /> KB</translation> <translation id="9002566407876343676">開啟</translation> <translation id="9038489124413477075">未命名的資料夾</translation> diff --git a/chromium/ui/strings/ui_strings.grd b/chromium/ui/strings/ui_strings.grd index fcf59a3d0c9..d82bf2ff6ce 100644 --- a/chromium/ui/strings/ui_strings.grd +++ b/chromium/ui/strings/ui_strings.grd @@ -429,6 +429,9 @@ need to be translated for each locale.--> Disclosure triangle </message> </if> + <message name="IDS_APP_ACCNAME_BACK" desc="The accessible name for the back button on the window frame."> + Back button + </message> <message name="IDS_APP_ACCNAME_CLOSE" desc="The accessible name for the Close button."> Close </message> @@ -444,6 +447,9 @@ need to be translated for each locale.--> <message name="IDS_APP_ACCNAME_MENU" desc="The accessible name for the Menu button."> Menu </message> + <message name="IDS_APP_ACCNAME_COLOR_CHOOSER_HEX_INPUT" desc="The accessible name for the color chooser hexadecimal input field"> + Hex color value + </message> <!-- Scroll Bar Context Menu Labels --> <message name="IDS_APP_SCROLLBAR_CXMENU_SCROLLHERE" desc="The label for the 'Scroll Here' item"> @@ -613,6 +619,12 @@ need to be translated for each locale.--> <message name="IDS_APP_SHIFT_KEY" desc="Shift key"> Shift </message> + <if expr="is_win"> + <message name="IDS_APP_WINDOWS_KEY" desc="This refers to the 'Windows' key on certain + keyboards (often between Ctrl and Alt). This may not need to be translated."> + Win + </message> + </if> <!-- Accelerator format --> <message name="IDS_APP_ACCELERATOR_WITH_MODIFIER" desc="Accelerator with a modifier key"> diff --git a/chromium/ui/strings/ui_strings_grd/IDS_APP_ACCNAME_COLOR_CHOOSER_HEX_INPUT.png.sha1 b/chromium/ui/strings/ui_strings_grd/IDS_APP_ACCNAME_COLOR_CHOOSER_HEX_INPUT.png.sha1 new file mode 100644 index 00000000000..6b9993fc250 --- /dev/null +++ b/chromium/ui/strings/ui_strings_grd/IDS_APP_ACCNAME_COLOR_CHOOSER_HEX_INPUT.png.sha1 @@ -0,0 +1 @@ +6f818215e03008c17e7fe7f3ac6b88d0ca52e958
\ No newline at end of file diff --git a/chromium/ui/strings/ui_strings_grd/OWNERS b/chromium/ui/strings/ui_strings_grd/OWNERS new file mode 100644 index 00000000000..72e8ffc0db8 --- /dev/null +++ b/chromium/ui/strings/ui_strings_grd/OWNERS @@ -0,0 +1 @@ +* diff --git a/chromium/ui/views/BUILD.gn b/chromium/ui/views/BUILD.gn index 3265121c103..b7fa6d3073c 100644 --- a/chromium/ui/views/BUILD.gn +++ b/chromium/ui/views/BUILD.gn @@ -54,7 +54,13 @@ jumbo_component("views") { all_dependent_configs = [ ":flags" ] public = [ + # TODO(ccameron): Move these sources to the views_bridge_mac component + "../views_bridge_mac/bridge_factory_impl.h", + "../views_bridge_mac/bridged_native_widget_impl.h", + "../views_bridge_mac/native_widget_mac_nswindow.h", + "../views_bridge_mac/window_touch_bar_delegate.h", "accessibility/view_accessibility.h", + "accessibility/view_accessibility_utils.h", "accessible_pane_view.h", "animation/bounds_animator.h", "animation/bounds_animator_observer.h", @@ -84,13 +90,12 @@ jumbo_component("views") { "bubble/info_bubble.h", "bubble/tooltip_icon.h", "button_drag_utils.h", - "cocoa/bridged_native_widget.h", - "cocoa/native_widget_mac_nswindow.h", - "cocoa/window_touch_bar_delegate.h", + "cocoa/bridge_factory_host.h", "color_chooser/color_chooser_listener.h", "color_chooser/color_chooser_view.h", "context_menu_controller.h", "controls/animated_icon_view.h", + "controls/animated_image_view.h", "controls/button/blue_button.h", "controls/button/button.h", "controls/button/checkbox.h", @@ -108,6 +113,7 @@ jumbo_component("views") { "controls/focus_ring.h", "controls/focusable_border.h", "controls/image_view.h", + "controls/image_view_base.h", "controls/label.h", "controls/link.h", "controls/link_listener.h", @@ -176,6 +182,7 @@ jumbo_component("views") { "drag_utils.h", "event_monitor.h", "event_monitor_mac.h", + "event_utils.h", "focus/external_focus_tracker.h", "focus/focus_manager.h", "focus/focus_manager_delegate.h", @@ -256,7 +263,11 @@ jumbo_component("views") { ] sources = [ + # TODO(ccameron): Move these sources to the views_bridge_mac component + "../views_bridge_mac/bridged_native_widget_impl.mm", + "../views_bridge_mac/native_widget_mac_nswindow.mm", "accessibility/view_accessibility.cc", + "accessibility/view_accessibility_utils.cc", "accessible_pane_view.cc", "animation/bounds_animator.cc", "animation/flood_fill_ink_drop_ripple.cc", @@ -281,10 +292,9 @@ jumbo_component("views") { "bubble/info_bubble.cc", "bubble/tooltip_icon.cc", "button_drag_utils.cc", - "cocoa/bridged_native_widget.mm", - "cocoa/native_widget_mac_nswindow.mm", "color_chooser/color_chooser_view.cc", "controls/animated_icon_view.cc", + "controls/animated_image_view.cc", "controls/button/blue_button.cc", "controls/button/button.cc", "controls/button/checkbox.cc", @@ -300,6 +310,7 @@ jumbo_component("views") { "controls/focus_ring.cc", "controls/focusable_border.cc", "controls/image_view.cc", + "controls/image_view_base.cc", "controls/label.cc", "controls/link.cc", "controls/menu/display_change_listener_mac.cc", @@ -362,6 +373,7 @@ jumbo_component("views") { "drag_utils.cc", "drag_utils_mac.mm", "event_monitor_mac.mm", + "event_utils.cc", "focus/external_focus_tracker.cc", "focus/focus_manager.cc", "focus/focus_manager_factory.cc", @@ -434,28 +446,24 @@ jumbo_component("views") { # Internal sources. TODO(https://crbug.com/871123): Move more headers from # public into this list, along with the implementation file. sources += [ - "cocoa/bridged_content_view.h", - "cocoa/bridged_content_view.mm", - "cocoa/bridged_content_view_touch_bar.mm", - "cocoa/bridged_native_widget_host.h", + # TODO(ccameron): Move these sources to the views_bridge_mac component + "../views_bridge_mac/bridge_factory_impl.mm", + "../views_bridge_mac/bridged_content_view.h", + "../views_bridge_mac/bridged_content_view.mm", + "../views_bridge_mac/bridged_content_view_touch_bar.mm", + "../views_bridge_mac/cocoa_window_move_loop.h", + "../views_bridge_mac/cocoa_window_move_loop.mm", + "../views_bridge_mac/views_nswindow_delegate.h", + "../views_bridge_mac/views_nswindow_delegate.mm", + "../views_bridge_mac/views_scrollbar_bridge.h", + "../views_bridge_mac/views_scrollbar_bridge.mm", + "cocoa/bridge_factory_host.cc", "cocoa/bridged_native_widget_host_impl.h", "cocoa/bridged_native_widget_host_impl.mm", - "cocoa/bridged_native_widget_owner.h", - "cocoa/cocoa_mouse_capture.h", - "cocoa/cocoa_mouse_capture.mm", - "cocoa/cocoa_mouse_capture_delegate.h", - "cocoa/cocoa_window_move_loop.h", - "cocoa/cocoa_window_move_loop.mm", "cocoa/drag_drop_client_mac.h", "cocoa/drag_drop_client_mac.mm", "cocoa/tooltip_manager_mac.h", "cocoa/tooltip_manager_mac.mm", - "cocoa/views_nswindow_delegate.h", - "cocoa/views_nswindow_delegate.mm", - "cocoa/views_scrollbar_bridge.h", - "cocoa/views_scrollbar_bridge.mm", - "cocoa/widget_owner_nswindow_adapter.h", - "cocoa/widget_owner_nswindow_adapter.mm", "controls/button/label_button_label.cc", "controls/button/label_button_label.h", "controls/menu/menu_pre_target_handler.h", @@ -477,6 +485,7 @@ jumbo_component("views") { "//base:i18n", "//base/third_party/dynamic_annotations", "//cc/paint", + "//mojo/public/cpp/bindings", "//services/ws/public/mojom", "//skia", "//third_party/icu", @@ -506,7 +515,6 @@ jumbo_component("views") { "//ui/gfx/animation", "//ui/gfx/geometry", "//ui/views/resources", - "//ui/views_bridge_mac:mojo", ] if (use_x11) { @@ -588,11 +596,11 @@ jumbo_component("views") { public += [ "accessibility/ax_aura_obj_cache.h", "accessibility/ax_aura_obj_wrapper.h", + "accessibility/ax_root_obj_wrapper.h", "accessibility/ax_tree_source_views.h", "accessibility/ax_view_obj_wrapper.h", "accessibility/ax_widget_obj_wrapper.h", "accessibility/ax_window_obj_wrapper.h", - "bubble/tray_bubble_view.h", "controls/native/native_view_host_aura.h", "corewm/cursor_height_provider_win.h", "corewm/tooltip.h", @@ -620,11 +628,11 @@ jumbo_component("views") { sources += [ "accessibility/ax_aura_obj_cache.cc", "accessibility/ax_aura_obj_wrapper.cc", + "accessibility/ax_root_obj_wrapper.cc", "accessibility/ax_tree_source_views.cc", "accessibility/ax_view_obj_wrapper.cc", "accessibility/ax_widget_obj_wrapper.cc", "accessibility/ax_window_obj_wrapper.cc", - "bubble/tray_bubble_view.cc", "controls/menu/display_change_listener_aura.cc", "controls/menu/menu_pre_target_handler_aura.cc", "controls/menu/menu_pre_target_handler_aura.h", @@ -658,6 +666,7 @@ jumbo_component("views") { "//services/ws/public/mojom", "//ui/aura", "//ui/platform_window", + "//ui/platform_window/platform_window_handler", "//ui/touch_selection", "//ui/wm", "//ui/wm/public", @@ -734,7 +743,9 @@ jumbo_component("views") { "//components/crash/core/common", "//ui/accelerated_widget_mac", "//ui/events:dom_keycode_converter", + "//ui/views_bridge_mac", ] + public_deps += [ "//ui/views_bridge_mac:mojo" ] libs = [ "AppKit.framework", "CoreGraphics.framework", @@ -1111,7 +1122,10 @@ source_set("views_unittests_sources") { if (is_mac) { # views_unittests not yet compiling on Mac. http://crbug.com/378134 sources -= [ "controls/native/native_view_host_unittest.cc" ] - public_deps += [ "//ui/accelerated_widget_mac" ] + public_deps += [ + "//ui/accelerated_widget_mac", + "//ui/views_bridge_mac:views_bridge_mac", + ] } } @@ -1211,12 +1225,23 @@ source_set("views_interactive_ui_tests") { "corewm/desktop_capture_controller_unittest.cc", "widget/native_widget_aura_interactive_uitest.cc", ] + deps += [ "//ui/aura", "//ui/aura:test_support", "//ui/wm", "//ui/wm/public", ] + + if (!is_chromeos && ((is_linux && !use_x11) || is_fuchsia)) { + sources += [ "widget/desktop_aura/desktop_window_tree_host_platform_interactive_uitest.cc" ] + } + + deps += [ + "//ui/events/platform", + "//ui/platform_window", + "//ui/platform_window/platform_window_handler", + ] } if (use_x11) { diff --git a/chromium/ui/views/DEPS b/chromium/ui/views/DEPS index f591ea6f71c..a2d0ab1f98c 100644 --- a/chromium/ui/views/DEPS +++ b/chromium/ui/views/DEPS @@ -2,6 +2,7 @@ include_rules = [ "+cc/paint", "+components/crash/core/common/crash_key.h", "+components/vector_icons", + "+mojo/public/cpp/bindings", "+services/ws/public/mojom", "+skia/ext", "+third_party/iaccessible2", @@ -20,6 +21,7 @@ include_rules = [ "+ui/resources/grit/ui_resources.h", "+ui/strings/grit/ui_strings.h", "+ui/touch_selection", + "+ui/views_bridge_mac", "+ui/wm/core", "+ui/wm/public", diff --git a/chromium/ui/views/OWNERS b/chromium/ui/views/OWNERS index 4aca57c69ca..57c9ed66f82 100644 --- a/chromium/ui/views/OWNERS +++ b/chromium/ui/views/OWNERS @@ -10,4 +10,7 @@ per-file *_mac.*=ellyjones@chromium.org per-file *_cocoa.*=ellyjones@chromium.org per-file *.mm=ellyjones@chromium.org +# If you're doing structural changes get a review from one of the OWNERS. +per-file BUILD.gn=* + # COMPONENT: Internals>Views diff --git a/chromium/ui/views/accessibility/ax_aura_obj_cache.cc b/chromium/ui/views/accessibility/ax_aura_obj_cache.cc index 519d09c2959..5811bf26790 100644 --- a/chromium/ui/views/accessibility/ax_aura_obj_cache.cc +++ b/chromium/ui/views/accessibility/ax_aura_obj_cache.cc @@ -31,6 +31,9 @@ AXAuraObjCache* AXAuraObjCache::GetInstance() { } AXAuraObjWrapper* AXAuraObjCache::GetOrCreate(View* view) { + // Avoid problems with transient focus events. https://crbug.com/729449 + if (!view->GetWidget()) + return nullptr; return CreateInternal<AXViewObjWrapper>(view, view_to_id_map_); } @@ -122,24 +125,28 @@ AXAuraObjCache::~AXAuraObjCache() { } View* AXAuraObjCache::GetFocusedView() { - if (root_windows_.empty()) - return nullptr; - aura::client::FocusClient* focus_client = - GetFocusClient(*root_windows_.begin()); - if (!focus_client) - return nullptr; - - aura::Window* focused_window = focus_client->GetFocusedWindow(); - if (!focused_window) - return nullptr; - - Widget* focused_widget = Widget::GetWidgetForNativeView(focused_window); - while (!focused_widget) { - focused_window = focused_window->parent(); + Widget* focused_widget = focused_widget_for_testing_; + aura::Window* focused_window = nullptr; + if (!focused_widget) { + if (root_windows_.empty()) + return nullptr; + aura::client::FocusClient* focus_client = + GetFocusClient(*root_windows_.begin()); + if (!focus_client) + return nullptr; + + focused_window = focus_client->GetFocusedWindow(); if (!focused_window) - break; + return nullptr; focused_widget = Widget::GetWidgetForNativeView(focused_window); + while (!focused_widget) { + focused_window = focused_window->parent(); + if (!focused_window) + break; + + focused_widget = Widget::GetWidgetForNativeView(focused_window); + } } if (!focused_widget) @@ -153,7 +160,8 @@ View* AXAuraObjCache::GetFocusedView() { if (focused_view) return focused_view; - if (focused_window->GetProperty( + if (focused_window && + focused_window->GetProperty( aura::client::kAccessibilityFocusFallsbackToWidgetKey)) { // If focused widget has non client view, falls back to first child view of // its client view. We don't expect that non client view gets keyboard diff --git a/chromium/ui/views/accessibility/ax_aura_obj_cache.h b/chromium/ui/views/accessibility/ax_aura_obj_cache.h index f798bdb059e..3a4a448e0ed 100644 --- a/chromium/ui/views/accessibility/ax_aura_obj_cache.h +++ b/chromium/ui/views/accessibility/ax_aura_obj_cache.h @@ -45,8 +45,11 @@ class VIEWS_EXPORT AXAuraObjCache : public aura::client::FocusChangeObserver { ax::mojom::Event event_type) = 0; }; - // Get or create an entry in the cache based on an Aura view. + // Get or create an entry in the cache. May return null if the View is not + // associated with a Widget. AXAuraObjWrapper* GetOrCreate(View* view); + + // Get or create an entry in the cache. AXAuraObjWrapper* GetOrCreate(Widget* widget); AXAuraObjWrapper* GetOrCreate(aura::Window* window); @@ -92,6 +95,13 @@ class VIEWS_EXPORT AXAuraObjCache : public aura::client::FocusChangeObserver { void SetDelegate(Delegate* delegate) { delegate_ = delegate; } + // Changes the behavior of GetFocusedView() so that it only considers + // views within the given Widget, this enables making tests + // involving focus reliable. + void set_focused_widget_for_testing(views::Widget* widget) { + focused_widget_for_testing_ = widget; + } + private: friend struct base::DefaultSingletonTraits<AXAuraObjCache>; @@ -131,6 +141,8 @@ class VIEWS_EXPORT AXAuraObjCache : public aura::client::FocusChangeObserver { std::set<aura::Window*> root_windows_; + views::Widget* focused_widget_for_testing_ = nullptr; + DISALLOW_COPY_AND_ASSIGN(AXAuraObjCache); }; diff --git a/chromium/ui/views/accessibility/ax_root_obj_wrapper.cc b/chromium/ui/views/accessibility/ax_root_obj_wrapper.cc new file mode 100644 index 00000000000..8f16302c7cb --- /dev/null +++ b/chromium/ui/views/accessibility/ax_root_obj_wrapper.cc @@ -0,0 +1,115 @@ +// 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/views/accessibility/ax_root_obj_wrapper.h" + +#include <utility> + +#include "base/stl_util.h" +#include "base/strings/utf_string_conversions.h" +#include "ui/accessibility/ax_enums.mojom.h" +#include "ui/accessibility/ax_node_data.h" +#include "ui/aura/env.h" +#include "ui/aura/window.h" +#include "ui/display/display.h" +#include "ui/display/screen.h" +#include "ui/views/accessibility/ax_aura_obj_cache.h" +#include "ui/views/accessibility/ax_window_obj_wrapper.h" + +AXRootObjWrapper::AXRootObjWrapper(views::AXAuraObjCache::Delegate* delegate) + : alert_window_(std::make_unique<aura::Window>(nullptr)), + delegate_(delegate) { + alert_window_->Init(ui::LAYER_NOT_DRAWN); +#if !defined(IS_CHROMECAST) + aura::Env::GetInstance()->AddObserver(this); + + if (display::Screen::GetScreen()) + display::Screen::GetScreen()->AddObserver(this); +#endif +} + +AXRootObjWrapper::~AXRootObjWrapper() { +#if !defined(IS_CHROMECAST) + if (display::Screen::GetScreen()) + display::Screen::GetScreen()->RemoveObserver(this); + + // If alert_window_ is nullptr already, that means OnWillDestroyEnv + // was already called, so we shouldn't call RemoveObserver(this) again. + if (!alert_window_) + return; + + aura::Env::GetInstance()->RemoveObserver(this); +#endif + alert_window_.reset(); +} + +views::AXAuraObjWrapper* AXRootObjWrapper::GetAlertForText( + const std::string& text) { + alert_window_->SetTitle(base::UTF8ToUTF16((text))); + views::AXWindowObjWrapper* window_obj = + static_cast<views::AXWindowObjWrapper*>( + views::AXAuraObjCache::GetInstance()->GetOrCreate( + alert_window_.get())); + window_obj->set_is_alert(true); + return window_obj; +} + +bool AXRootObjWrapper::HasChild(views::AXAuraObjWrapper* child) { + std::vector<views::AXAuraObjWrapper*> children; + GetChildren(&children); + return base::ContainsValue(children, child); +} + +bool AXRootObjWrapper::IsIgnored() { + return false; +} + +views::AXAuraObjWrapper* AXRootObjWrapper::GetParent() { + return NULL; +} + +void AXRootObjWrapper::GetChildren( + std::vector<views::AXAuraObjWrapper*>* out_children) { + views::AXAuraObjCache::GetInstance()->GetTopLevelWindows(out_children); + out_children->push_back( + views::AXAuraObjCache::GetInstance()->GetOrCreate(alert_window_.get())); +} + +void AXRootObjWrapper::Serialize(ui::AXNodeData* out_node_data) { + out_node_data->id = unique_id_.Get(); + out_node_data->role = ax::mojom::Role::kDesktop; + +#if !defined(IS_CHROMECAST) + display::Screen* screen = display::Screen::GetScreen(); + if (!screen) + return; + + const display::Display& display = screen->GetPrimaryDisplay(); + + // Utilize the display bounds to figure out if this screen is in landscape or + // portrait. We use this rather than |rotation| because some devices default + // to landscape, some in portrait. Encode landscape as horizontal state, + // portrait as vertical state. + if (display.bounds().width() > display.bounds().height()) + out_node_data->AddState(ax::mojom::State::kHorizontal); + else + out_node_data->AddState(ax::mojom::State::kVertical); +#endif +} + +const ui::AXUniqueId& AXRootObjWrapper::GetUniqueId() const { + return unique_id_; +} + +void AXRootObjWrapper::OnDisplayMetricsChanged(const display::Display& display, + uint32_t changed_metrics) { + delegate_->OnEvent(this, ax::mojom::Event::kLocationChanged); +} + +void AXRootObjWrapper::OnWindowInitialized(aura::Window* window) {} + +void AXRootObjWrapper::OnWillDestroyEnv() { + alert_window_.reset(); + aura::Env::GetInstance()->RemoveObserver(this); +} diff --git a/chromium/ui/views/accessibility/ax_root_obj_wrapper.h b/chromium/ui/views/accessibility/ax_root_obj_wrapper.h new file mode 100644 index 00000000000..f90f9853dab --- /dev/null +++ b/chromium/ui/views/accessibility/ax_root_obj_wrapper.h @@ -0,0 +1,60 @@ +// 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_VIEWS_ACCESSIBILITY_AX_ROOT_OBJ_WRAPPER_H_ +#define UI_VIEWS_ACCESSIBILITY_AX_ROOT_OBJ_WRAPPER_H_ + +#include <stdint.h> + +#include <memory> +#include <string> +#include <vector> + +#include "base/macros.h" +#include "ui/accessibility/platform/ax_unique_id.h" +#include "ui/aura/env_observer.h" +#include "ui/display/display_observer.h" +#include "ui/views/accessibility/ax_aura_obj_cache.h" +#include "ui/views/accessibility/ax_aura_obj_wrapper.h" + +class VIEWS_EXPORT AXRootObjWrapper : public views::AXAuraObjWrapper, + display::DisplayObserver, + aura::EnvObserver { + public: + explicit AXRootObjWrapper(views::AXAuraObjCache::Delegate* delegate); + ~AXRootObjWrapper() override; + + // Returns an AXAuraObjWrapper for an alert window with title set to |text|. + views::AXAuraObjWrapper* GetAlertForText(const std::string& text); + + // Convenience method to check for existence of a child. + bool HasChild(views::AXAuraObjWrapper* child); + + // views::AXAuraObjWrapper overrides. + bool IsIgnored() override; + views::AXAuraObjWrapper* GetParent() override; + void GetChildren( + std::vector<views::AXAuraObjWrapper*>* out_children) override; + void Serialize(ui::AXNodeData* out_node_data) override; + const ui::AXUniqueId& GetUniqueId() const override; + + private: + // display::DisplayObserver: + void OnDisplayMetricsChanged(const display::Display& display, + uint32_t changed_metrics) override; + + // aura::EnvObserver: + void OnWindowInitialized(aura::Window* window) override; + void OnWillDestroyEnv() override; + + ui::AXUniqueId unique_id_; + + std::unique_ptr<aura::Window> alert_window_; + + views::AXAuraObjCache::Delegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(AXRootObjWrapper); +}; + +#endif // UI_VIEWS_ACCESSIBILITY_AX_ROOT_OBJ_WRAPPER_H_ diff --git a/chromium/ui/views/accessibility/ax_system_caret_win_interactive_uitest.cc b/chromium/ui/views/accessibility/ax_system_caret_win_interactive_uitest.cc index 84b893fca7f..aa9e497e6b1 100644 --- a/chromium/ui/views/accessibility/ax_system_caret_win_interactive_uitest.cc +++ b/chromium/ui/views/accessibility/ax_system_caret_win_interactive_uitest.cc @@ -21,6 +21,7 @@ #include "ui/gfx/geometry/rect.h" #include "ui/gfx/native_widget_types.h" #include "ui/gl/test/gl_surface_test_support.h" +#include "ui/views/controls/button/label_button.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/controls/textfield/textfield_test_api.h" #include "ui/views/test/widget_test.h" @@ -74,6 +75,132 @@ class AXSystemCaretWinTest : public test::WidgetTest { DISALLOW_COPY_AND_ASSIGN(AXSystemCaretWinTest); }; +class WinAccessibilityCaretEventMonitor { + public: + WinAccessibilityCaretEventMonitor(UINT event_min, UINT event_max); + ~WinAccessibilityCaretEventMonitor(); + + // Blocks until the next event is received. When it's received, it + // queries accessibility information about the object that fired the + // event and populates the event, hwnd, role, state, and name in the + // passed arguments. + void WaitForNextEvent(DWORD* out_event, UINT* out_role, UINT* out_state); + + private: + void OnWinEventHook(HWINEVENTHOOK handle, + DWORD event, + HWND hwnd, + LONG obj_id, + LONG child_id, + DWORD event_thread, + DWORD event_time); + + static void CALLBACK WinEventHookThunk(HWINEVENTHOOK handle, + DWORD event, + HWND hwnd, + LONG obj_id, + LONG child_id, + DWORD event_thread, + DWORD event_time); + + struct EventInfo { + DWORD event; + HWND hwnd; + LONG obj_id; + LONG child_id; + }; + + base::circular_deque<EventInfo> event_queue_; + base::RunLoop loop_runner_; + HWINEVENTHOOK win_event_hook_handle_; + static WinAccessibilityCaretEventMonitor* instance_; + + DISALLOW_COPY_AND_ASSIGN(WinAccessibilityCaretEventMonitor); +}; + +// static +WinAccessibilityCaretEventMonitor* + WinAccessibilityCaretEventMonitor::instance_ = NULL; + +WinAccessibilityCaretEventMonitor::WinAccessibilityCaretEventMonitor( + UINT event_min, + UINT event_max) { + CHECK(!instance_) << "There can be only one instance of" + << " WinAccessibilityCaretEventMonitor at a time."; + instance_ = this; + win_event_hook_handle_ = + SetWinEventHook(event_min, event_max, NULL, + &WinAccessibilityCaretEventMonitor::WinEventHookThunk, + GetCurrentProcessId(), + 0, // Hook all threads + WINEVENT_OUTOFCONTEXT); +} + +WinAccessibilityCaretEventMonitor::~WinAccessibilityCaretEventMonitor() { + UnhookWinEvent(win_event_hook_handle_); + instance_ = NULL; +} + +void WinAccessibilityCaretEventMonitor::WaitForNextEvent(DWORD* out_event, + UINT* out_role, + UINT* out_state) { + if (event_queue_.empty()) + loop_runner_.Run(); + + EventInfo event_info = event_queue_.front(); + event_queue_.pop_front(); + + *out_event = event_info.event; + + Microsoft::WRL::ComPtr<IAccessible> acc_obj; + base::win::ScopedVariant child_variant; + CHECK(S_OK == AccessibleObjectFromEvent( + event_info.hwnd, event_info.obj_id, event_info.child_id, + acc_obj.GetAddressOf(), child_variant.Receive())); + + base::win::ScopedVariant role_variant; + if (S_OK == acc_obj->get_accRole(child_variant, role_variant.Receive())) + *out_role = V_I4(role_variant.ptr()); + else + *out_role = 0; + + base::win::ScopedVariant state_variant; + if (S_OK == acc_obj->get_accState(child_variant, state_variant.Receive())) + *out_state = V_I4(state_variant.ptr()); + else + *out_state = 0; +} + +void WinAccessibilityCaretEventMonitor::OnWinEventHook(HWINEVENTHOOK handle, + DWORD event, + HWND hwnd, + LONG obj_id, + LONG child_id, + DWORD event_thread, + DWORD event_time) { + EventInfo event_info; + event_info.event = event; + event_info.hwnd = hwnd; + event_info.obj_id = obj_id; + event_info.child_id = child_id; + event_queue_.push_back(event_info); + loop_runner_.Quit(); +} + +// static +void CALLBACK +WinAccessibilityCaretEventMonitor::WinEventHookThunk(HWINEVENTHOOK handle, + DWORD event, + HWND hwnd, + LONG obj_id, + LONG child_id, + DWORD event_thread, + DWORD event_time) { + if (instance_ && obj_id == OBJID_CARET) { + instance_->OnWinEventHook(handle, event, hwnd, obj_id, child_id, + event_thread, event_time); + } +} } // namespace TEST_F(AXSystemCaretWinTest, DISABLED_TestOnCaretBoundsChangeInTextField) { @@ -183,4 +310,81 @@ TEST_F(AXSystemCaretWinTest, DISABLED_TestMovingWindow) { EXPECT_EQ(height, height3); } +TEST_F(AXSystemCaretWinTest, DISABLED_TestCaretMSAAEvents) { + TextfieldTestApi textfield_test_api(textfield_); + Microsoft::WRL::ComPtr<IAccessible> caret_accessible; + gfx::NativeWindow native_window = widget_->GetNativeWindow(); + ASSERT_NE(nullptr, native_window); + HWND hwnd = native_window->GetHost()->GetAcceleratedWidget(); + EXPECT_HRESULT_SUCCEEDED(AccessibleObjectFromWindow( + hwnd, static_cast<DWORD>(OBJID_CARET), IID_IAccessible, + reinterpret_cast<void**>(caret_accessible.GetAddressOf()))); + + DWORD event; + UINT role; + UINT state; + + { + // Set caret to start of textfield. + WinAccessibilityCaretEventMonitor monitor(EVENT_OBJECT_SHOW, + EVENT_OBJECT_LOCATIONCHANGE); + textfield_test_api.ExecuteTextEditCommand( + ui::TextEditCommand::MOVE_TO_BEGINNING_OF_DOCUMENT); + monitor.WaitForNextEvent(&event, &role, &state); + ASSERT_EQ(event, static_cast<DWORD>(EVENT_OBJECT_LOCATIONCHANGE)) + << "Event should be EVENT_OBJECT_LOCATIONCHANGE"; + ASSERT_EQ(role, static_cast<UINT>(ROLE_SYSTEM_CARET)) + << "Role should be ROLE_SYSTEM_CARET"; + ASSERT_EQ(state, static_cast<UINT>(0)) << "State should be 0"; + } + + { + // Set caret to end of textfield. + WinAccessibilityCaretEventMonitor monitor(EVENT_OBJECT_SHOW, + EVENT_OBJECT_LOCATIONCHANGE); + textfield_test_api.ExecuteTextEditCommand( + ui::TextEditCommand::MOVE_TO_END_OF_DOCUMENT); + monitor.WaitForNextEvent(&event, &role, &state); + ASSERT_EQ(event, static_cast<DWORD>(EVENT_OBJECT_LOCATIONCHANGE)) + << "Event should be EVENT_OBJECT_LOCATIONCHANGE"; + ASSERT_EQ(role, static_cast<UINT>(ROLE_SYSTEM_CARET)) + << "Role should be ROLE_SYSTEM_CARET"; + ASSERT_EQ(state, static_cast<UINT>(0)) << "State should be 0"; + } + + { + // Move focus to a button. + LabelButton button(nullptr, base::string16()); + button.SetBounds(500, 0, 200, 20); + widget_->GetRootView()->AddChildView(&button); + test::WidgetActivationWaiter waiter(widget_, true); + WinAccessibilityCaretEventMonitor monitor(EVENT_OBJECT_SHOW, + EVENT_OBJECT_LOCATIONCHANGE); + widget_->Show(); + waiter.Wait(); + button.SetFocusBehavior(View::FocusBehavior::ALWAYS); + button.RequestFocus(); + monitor.WaitForNextEvent(&event, &role, &state); + ASSERT_EQ(event, static_cast<DWORD>(EVENT_OBJECT_HIDE)) + << "Event should be EVENT_OBJECT_HIDE"; + ASSERT_EQ(role, static_cast<UINT>(ROLE_SYSTEM_CARET)) + << "Role should be ROLE_SYSTEM_CARET"; + ASSERT_EQ(state, static_cast<UINT>(STATE_SYSTEM_INVISIBLE)) + << "State should be STATE_SYSTEM_INVISIBLE"; + } + + { + // Move focus back to the text field. + WinAccessibilityCaretEventMonitor monitor(EVENT_OBJECT_SHOW, + EVENT_OBJECT_LOCATIONCHANGE); + textfield_->RequestFocus(); + monitor.WaitForNextEvent(&event, &role, &state); + ASSERT_EQ(event, static_cast<DWORD>(EVENT_OBJECT_SHOW)) + << "Event should be EVENT_OBJECT_SHOW"; + ASSERT_EQ(role, static_cast<UINT>(ROLE_SYSTEM_CARET)) + << "Role should be ROLE_SYSTEM_CARET"; + ASSERT_EQ(state, static_cast<UINT>(0)) << "State should be 0"; + } +} + } // namespace views diff --git a/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc b/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc index b8ea4c3c754..cd77dabfd1b 100644 --- a/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc +++ b/chromium/ui/views/accessibility/ax_window_obj_wrapper.cc @@ -9,6 +9,7 @@ #include "base/strings/utf_string_conversions.h" #include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_node_data.h" +#include "ui/accessibility/ax_tree_id.h" #include "ui/accessibility/platform/aura_window_properties.h" #include "ui/accessibility/platform/ax_unique_id.h" #include "ui/aura/client/focus_client.h" @@ -96,9 +97,9 @@ void AXWindowObjWrapper::Serialize(ui::AXNodeData* out_node_data) { if (!window_->IsVisible()) out_node_data->AddState(ax::mojom::State::kInvisible); out_node_data->location = gfx::RectF(window_->GetBoundsInScreen()); - ui::AXTreeIDRegistry::AXTreeID child_ax_tree_id = - window_->GetProperty(ui::kChildAXTreeID); - if (child_ax_tree_id != ui::AXTreeIDRegistry::kNoAXTreeID) { + std::string* child_ax_tree_id_ptr = window_->GetProperty(ui::kChildAXTreeID); + if (child_ax_tree_id_ptr && ui::AXTreeID::FromString(*child_ax_tree_id_ptr) != + ui::AXTreeIDUnknown()) { // Most often, child AX trees are parented to Views. We need to handle // the case where they're not here, but we don't want the same AX tree // to be a child of two different parents. @@ -110,8 +111,8 @@ void AXWindowObjWrapper::Serialize(ui::AXNodeData* out_node_data) { return; } - out_node_data->AddIntAttribute(ax::mojom::IntAttribute::kChildTreeId, - child_ax_tree_id); + out_node_data->AddStringAttribute(ax::mojom::StringAttribute::kChildTreeId, + *child_ax_tree_id_ptr); } } diff --git a/chromium/ui/views/accessibility/view_accessibility_utils.cc b/chromium/ui/views/accessibility/view_accessibility_utils.cc new file mode 100644 index 00000000000..9663921aec7 --- /dev/null +++ b/chromium/ui/views/accessibility/view_accessibility_utils.cc @@ -0,0 +1,44 @@ +// 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/views/accessibility/view_accessibility_utils.h" + +#include "ui/views/view.h" +#include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_delegate.h" + +namespace views { + +// static +Widget* ViewAccessibilityUtils::GetFocusedChildWidgetForAccessibility( + const View* view) { + const FocusManager* focus_manager = view->GetFocusManager(); + if (!focus_manager) + return nullptr; + const View* focused_view = view->GetFocusManager()->GetFocusedView(); + if (!focused_view) + return nullptr; + + std::set<Widget*> child_widgets; + Widget::GetAllOwnedWidgets(view->GetWidget()->GetNativeView(), + &child_widgets); + for (auto iter = child_widgets.begin(); iter != child_widgets.end(); ++iter) { + Widget* child_widget = *iter; + DCHECK_NE(view->GetWidget(), child_widget); + + if (IsFocusedChildWidget(child_widget, focused_view)) + return child_widget; + } + + return nullptr; +} + +// static +bool ViewAccessibilityUtils::IsFocusedChildWidget(Widget* widget, + const View* focused_view) { + return widget->IsVisible() && + widget->GetContentsView()->Contains(focused_view); +}; + +} // namespace views diff --git a/chromium/ui/views/accessibility/view_accessibility_utils.h b/chromium/ui/views/accessibility/view_accessibility_utils.h new file mode 100644 index 00000000000..24c1a324b14 --- /dev/null +++ b/chromium/ui/views/accessibility/view_accessibility_utils.h @@ -0,0 +1,29 @@ +// 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_VIEWS_ACCESSIBILITY_VIEW_ACCESSIBILITY_UTILS_H_ +#define UI_VIEWS_ACCESSIBILITY_VIEW_ACCESSIBILITY_UTILS_H_ + +#include "ui/views/view.h" +#include "ui/views/widget/widget.h" + +namespace views { + +class VIEWS_EXPORT ViewAccessibilityUtils { + public: + // Returns a focused child widget if the view has a child that should be + // treated as a special case. For example, if a tab modal dialog is visible + // and focused, this will return the dialog when called on the BrowserView. + // This helper function is used to treat such widgets as separate windows for + // accessibility. Returns nullptr if no such widget is present. + static Widget* GetFocusedChildWidgetForAccessibility(const View* view); + + // Used by GetFocusedChildWidgetForAccessibility to determine if a Widget + // should be handled separately. + static bool IsFocusedChildWidget(Widget* widget, const View* focused_view); +}; + +} // namespace views + +#endif // UI_VIEWS_ACCESSIBILITY_VIEW_ACCESSIBILITY_UTILS_H_ diff --git a/chromium/ui/views/accessibility/view_ax_platform_node_delegate.cc b/chromium/ui/views/accessibility/view_ax_platform_node_delegate.cc index 10cc9408045..adc4575fd6e 100644 --- a/chromium/ui/views/accessibility/view_ax_platform_node_delegate.cc +++ b/chromium/ui/views/accessibility/view_ax_platform_node_delegate.cc @@ -9,12 +9,15 @@ #include "base/lazy_instance.h" #include "base/threading/thread_task_runner_handle.h" +#include "ui/accessibility/ax_role_properties.h" #include "ui/accessibility/platform/ax_platform_node.h" #include "ui/events/event_utils.h" #include "ui/gfx/native_widget_types.h" +#include "ui/views/accessibility/view_accessibility_utils.h" #include "ui/views/controls/native/native_view_host.h" #include "ui/views/view.h" #include "ui/views/widget/widget.h" +#include "ui/views/widget/widget_delegate.h" namespace views { @@ -146,7 +149,7 @@ void ViewAXPlatformNodeDelegate::NotifyAccessibilityEvent( OnMenuEnd(); break; case ax::mojom::Event::kSelection: - if (menu_depth_ && GetData().role == ax::mojom::Role::kMenuItem) + if (menu_depth_ && ui::IsMenuItem(GetData().role)) OnMenuItemActive(); break; case ax::mojom::Event::kFocusContext: { @@ -227,7 +230,12 @@ int ViewAXPlatformNodeDelegate::GetChildCount() { int child_count = view()->child_count(); std::vector<Widget*> child_widgets; - PopulateChildWidgetVector(&child_widgets); + bool is_tab_modal_showing; + PopulateChildWidgetVector(&child_widgets, &is_tab_modal_showing); + if (is_tab_modal_showing) { + DCHECK_EQ(child_widgets.size(), 1ULL); + return 1; + } child_count += child_widgets.size(); return child_count; @@ -239,7 +247,16 @@ gfx::NativeViewAccessible ViewAXPlatformNodeDelegate::ChildAtIndex(int index) { // If this is a root view, our widget might have child widgets. Include std::vector<Widget*> child_widgets; - PopulateChildWidgetVector(&child_widgets); + bool is_tab_modal_showing; + PopulateChildWidgetVector(&child_widgets, &is_tab_modal_showing); + + // If a visible tab modal dialog is present, ignore |index| and return the + // dialog. + if (is_tab_modal_showing) { + DCHECK_EQ(child_widgets.size(), 1ULL); + return child_widgets[0]->GetRootView()->GetNativeViewAccessible(); + } + int child_widget_count = static_cast<int>(child_widgets.size()); if (index < view()->child_count()) { @@ -290,7 +307,8 @@ gfx::NativeViewAccessible ViewAXPlatformNodeDelegate::HitTestSync(int x, // Search child widgets first, since they're on top in the z-order. std::vector<Widget*> child_widgets; - PopulateChildWidgetVector(&child_widgets); + bool is_tab_modal_showing; + PopulateChildWidgetVector(&child_widgets, &is_tab_modal_showing); for (Widget* child_widget : child_widgets) { View* child_root_view = child_widget->GetRootView(); gfx::Point point(x, y); @@ -358,13 +376,20 @@ const ui::AXUniqueId& ViewAXPlatformNodeDelegate::GetUniqueId() const { } void ViewAXPlatformNodeDelegate::PopulateChildWidgetVector( - std::vector<Widget*>* result_child_widgets) { + std::vector<Widget*>* result_child_widgets, + bool* is_tab_modal_showing) { // Only attach child widgets to the root view. Widget* widget = view()->GetWidget(); // Note that during window close, a Widget may exist in a state where it has // no NativeView, but hasn't yet torn down its view hierarchy. - if (!widget || !widget->GetNativeView() || widget->GetRootView() != view()) + if (!widget || !widget->GetNativeView() || widget->GetRootView() != view()) { + *is_tab_modal_showing = false; return; + } + + const views::FocusManager* focus_manager = view()->GetFocusManager(); + const views::View* focused_view = + focus_manager ? focus_manager->GetFocusedView() : nullptr; std::set<Widget*> child_widgets; Widget::GetAllOwnedWidgets(widget->GetNativeView(), &child_widgets); @@ -378,8 +403,19 @@ void ViewAXPlatformNodeDelegate::PopulateChildWidgetVector( if (widget->GetNativeWindowProperty(kWidgetNativeViewHostKey)) continue; + // Focused child widgets should take the place of the web page they cover in + // the accessibility tree. + if (ViewAccessibilityUtils::IsFocusedChildWidget(child_widget, + focused_view)) { + result_child_widgets->clear(); + result_child_widgets->push_back(child_widget); + *is_tab_modal_showing = true; + return; + } + result_child_widgets->push_back(child_widget); } + *is_tab_modal_showing = false; } } // namespace views diff --git a/chromium/ui/views/accessibility/view_ax_platform_node_delegate.h b/chromium/ui/views/accessibility/view_ax_platform_node_delegate.h index f3829054acc..8dea736b61f 100644 --- a/chromium/ui/views/accessibility/view_ax_platform_node_delegate.h +++ b/chromium/ui/views/accessibility/view_ax_platform_node_delegate.h @@ -63,7 +63,11 @@ class VIEWS_EXPORT ViewAXPlatformNodeDelegate explicit ViewAXPlatformNodeDelegate(View* view); private: - void PopulateChildWidgetVector(std::vector<Widget*>* result_child_widgets); + // |is_tab_modal_showing| is set to true if, instead of populating + // |result_child_widgets| normally, a single child widget was returned (e.g. a + // dialog that should be read instead of the rest of the page contents). + void PopulateChildWidgetVector(std::vector<Widget*>* result_child_widgets, + bool* is_tab_modal_showing); void OnMenuItemActive(); void OnMenuStart(); diff --git a/chromium/ui/views/accessible_pane_view.cc b/chromium/ui/views/accessible_pane_view.cc index cfd86c97ac1..a0b033b19fd 100644 --- a/chromium/ui/views/accessible_pane_view.cc +++ b/chromium/ui/views/accessible_pane_view.cc @@ -174,6 +174,9 @@ bool AccessiblePaneView::AcceleratorPressed( case ui::VKEY_ESCAPE: { RemovePaneFocus(); View* last_focused_view = last_focused_view_tracker_->view(); + // Ignore |last_focused_view| if it's no longer in the same widget. + if (last_focused_view && GetWidget() != last_focused_view->GetWidget()) + last_focused_view = nullptr; if (last_focused_view) { focus_manager_->SetFocusedViewWithReason( last_focused_view, FocusManager::kReasonFocusRestore); diff --git a/chromium/ui/views/accessible_pane_view_unittest.cc b/chromium/ui/views/accessible_pane_view_unittest.cc index 7aef74a3bbe..f8e88ebfb46 100644 --- a/chromium/ui/views/accessible_pane_view_unittest.cc +++ b/chromium/ui/views/accessible_pane_view_unittest.cc @@ -206,7 +206,7 @@ TEST_F(AccessiblePaneViewTest, PaneFocusTraversal) { EXPECT_TRUE(original_test_view->SetPaneFocus( original_test_view->third_child_button())); - // Test travesal in second view. + // Test traversal in second view. // Set pane focus on second child. EXPECT_TRUE(test_view->SetPaneFocus(test_view->second_child_button())); // home @@ -234,4 +234,35 @@ TEST_F(AccessiblePaneViewTest, PaneFocusTraversal) { widget->CloseNow(); widget.reset(); } + +TEST_F(AccessiblePaneViewTest, DoesntCrashOnEscapeWithRemovedView) { + TestBarView* test_view1 = new TestBarView(); + TestBarView* test_view2 = new TestBarView(); + Widget widget; + Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); + params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + params.bounds = gfx::Rect(50, 50, 650, 650); + widget.Init(params); + View* root = widget.GetRootView(); + root->AddChildView(test_view1); + root->AddChildView(test_view2); + widget.Show(); + widget.Activate(); + + View* v1 = test_view1->child_button(); + View* v2 = test_view2->child_button(); + // Do the following: + // 1. focus |v1|. + // 2. focus |v2|. This makes |test_view2| remember |v1| as having focus. + // 3. Removes |v1| from it's parent. + // 4. Presses escape on |test_view2|. Escape attempts to revert focus to |v1| + // (because of step 2). Because |v1| is not in a widget this should not + // attempt to focus anything. + EXPECT_TRUE(test_view1->SetPaneFocus(v1)); + EXPECT_TRUE(test_view2->SetPaneFocus(v2)); + v1->parent()->RemoveChildView(v1); + // This shouldn't hit a CHECK in the FocusManager. + EXPECT_TRUE(test_view2->AcceleratorPressed(test_view2->escape_key())); +} + } // namespace views diff --git a/chromium/ui/views/animation/ink_drop_host_view.cc b/chromium/ui/views/animation/ink_drop_host_view.cc index f21c750bd1f..b98691b786e 100644 --- a/chromium/ui/views/animation/ink_drop_host_view.cc +++ b/chromium/ui/views/animation/ink_drop_host_view.cc @@ -16,6 +16,7 @@ #include "ui/views/animation/ink_drop_stub.h" #include "ui/views/animation/square_ink_drop_ripple.h" #include "ui/views/style/platform_style.h" +#include "ui/views/view_properties.h" namespace views { namespace { @@ -170,6 +171,9 @@ std::unique_ptr<InkDropHighlight> InkDropHostView::CreateInkDropHighlight() } std::unique_ptr<views::InkDropMask> InkDropHostView::CreateInkDropMask() const { + if (gfx::Path* highlight_path = GetProperty(kHighlightPathKey)) + return std::make_unique<views::PathInkDropMask>(size(), *highlight_path); + return nullptr; } diff --git a/chromium/ui/views/animation/ink_drop_mask.cc b/chromium/ui/views/animation/ink_drop_mask.cc index a2d2adb366e..63c93c61b4a 100644 --- a/chromium/ui/views/animation/ink_drop_mask.cc +++ b/chromium/ui/views/animation/ink_drop_mask.cc @@ -75,4 +75,20 @@ void CircleInkDropMask::OnPaintLayer(const ui::PaintContext& context) { recorder.canvas()->DrawCircle(mask_center_, mask_radius_, flags); } +// PathInkDropMask + +PathInkDropMask::PathInkDropMask(const gfx::Size& layer_size, + const gfx::Path& path) + : InkDropMask(layer_size), path_(path) {} + +void PathInkDropMask::OnPaintLayer(const ui::PaintContext& context) { + cc::PaintFlags flags; + flags.setAlpha(255); + flags.setStyle(cc::PaintFlags::kFill_Style); + flags.setAntiAlias(true); + + ui::PaintRecorder recorder(context, layer()->size()); + recorder.canvas()->DrawPath(path_, flags); +} + } // namespace views diff --git a/chromium/ui/views/animation/ink_drop_mask.h b/chromium/ui/views/animation/ink_drop_mask.h index debfb1604e2..1a3fa87e066 100644 --- a/chromium/ui/views/animation/ink_drop_mask.h +++ b/chromium/ui/views/animation/ink_drop_mask.h @@ -11,6 +11,7 @@ #include "ui/gfx/geometry/insets_f.h" #include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" +#include "ui/gfx/path.h" #include "ui/views/views_export.h" namespace views { @@ -33,7 +34,7 @@ class VIEWS_EXPORT InkDropMask : public ui::LayerDelegate { explicit InkDropMask(const gfx::Size& layer_size); private: - // Overriden from ui::LayerDelegate: + // ui::LayerDelegate: void OnDeviceScaleFactorChanged(float old_device_scale_factor, float new_device_scale_factor) override; @@ -50,7 +51,7 @@ class VIEWS_EXPORT RoundRectInkDropMask : public InkDropMask { float corner_radius); private: - // Overriden from InkDropMask: + // InkDropMask: void OnPaintLayer(const ui::PaintContext& context) override; gfx::InsetsF mask_insets_; @@ -67,7 +68,7 @@ class VIEWS_EXPORT CircleInkDropMask : public InkDropMask { int mask_radius); private: - // Overriden from InkDropMask: + // InkDropMask: void OnPaintLayer(const ui::PaintContext& context) override; gfx::Point mask_center_; @@ -76,6 +77,20 @@ class VIEWS_EXPORT CircleInkDropMask : public InkDropMask { DISALLOW_COPY_AND_ASSIGN(CircleInkDropMask); }; +// An ink-drop mask that paints a specified path. +class VIEWS_EXPORT PathInkDropMask : public InkDropMask { + public: + PathInkDropMask(const gfx::Size& layer_size, const gfx::Path& path); + + private: + // InkDropMask: + void OnPaintLayer(const ui::PaintContext& context) override; + + gfx::Path path_; + + DISALLOW_COPY_AND_ASSIGN(PathInkDropMask); +}; + } // namespace views #endif // UI_VIEWS_ANIMATION_INK_DROP_MASK_H_ diff --git a/chromium/ui/views/bubble/OWNERS b/chromium/ui/views/bubble/OWNERS index f04407c0829..d17d1b2257c 100644 --- a/chromium/ui/views/bubble/OWNERS +++ b/chromium/ui/views/bubble/OWNERS @@ -1,4 +1,3 @@ msw@chromium.org -per-file tray_bubble_view.*=stevenjb@chromium.org # COMPONENT: UI>Browser>Bubbles diff --git a/chromium/ui/views/bubble/bubble_border.cc b/chromium/ui/views/bubble/bubble_border.cc index 9fc042cd02f..88e4653b928 100644 --- a/chromium/ui/views/bubble/bubble_border.cc +++ b/chromium/ui/views/bubble/bubble_border.cc @@ -14,6 +14,7 @@ #include "third_party/skia/include/core/SkPath.h" #include "ui/base/material_design/material_design_controller.h" #include "ui/base/resource/resource_bundle.h" +#include "ui/gfx/color_palette.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/path.h" #include "ui/gfx/scoped_canvas.h" @@ -127,7 +128,8 @@ gfx::Rect BubbleBorder::GetBounds(const gfx::Rect& anchor_rect, } // With NO_ASSETS, there should be further insets, but the same logic is // used to position the bubble origin according to |anchor_rect|. - DCHECK(shadow_ != NO_ASSETS || shadow_insets.IsEmpty()); + DCHECK((shadow_ != NO_ASSETS && shadow_ != NO_SHADOW) || + shadow_insets.IsEmpty()); contents_bounds.Inset(-shadow_insets); // |arrow_offset_| is used to adjust bubbles that would normally be // partially offscreen. @@ -177,6 +179,9 @@ void BubbleBorder::Paint(const views::View& view, gfx::Canvas* canvas) { if (shadow_ == NO_ASSETS) return PaintNoAssets(view, canvas); + if (shadow_ == NO_SHADOW) + return PaintNoShadow(view, canvas); + gfx::ScopedCanvas scoped(canvas); SkRRect r_rect = GetClientRect(view); @@ -188,9 +193,11 @@ void BubbleBorder::Paint(const views::View& view, gfx::Canvas* canvas) { } gfx::Insets BubbleBorder::GetInsets() const { - return (shadow_ == NO_ASSETS) - ? gfx::Insets() - : GetBorderAndShadowInsets(md_shadow_elevation_); + if (shadow_ == NO_ASSETS) + return gfx::Insets(); + if (shadow_ == NO_SHADOW) + return gfx::Insets(kBorderThicknessDip); + return GetBorderAndShadowInsets(md_shadow_elevation_); } gfx::Size BubbleBorder::GetMinimumSize() const { @@ -247,8 +254,8 @@ const cc::PaintFlags& BubbleBorder::GetBorderAndShadowFlags( return flag_map->find(key)->second; cc::PaintFlags flags; - constexpr SkColor kBorderColor = SkColorSetA(SK_ColorBLACK, 0x26); - flags.setColor(kBorderColor); + constexpr SkColor kBlurredBorderColor = SkColorSetA(SK_ColorBLACK, 0x26); + flags.setColor(kBlurredBorderColor); flags.setAntiAlias(true); flags.setLooper( gfx::CreateShadowDrawLooper(GetShadowValues(elevation, color))); @@ -280,6 +287,18 @@ void BubbleBorder::PaintNoAssets(const View& view, gfx::Canvas* canvas) { canvas->sk_canvas()->drawColor(SK_ColorTRANSPARENT, SkBlendMode::kSrc); } +void BubbleBorder::PaintNoShadow(const View& view, gfx::Canvas* canvas) { + gfx::RectF bounds(view.GetLocalBounds()); + bounds.Inset(gfx::InsetsF(kBorderThicknessDip / 2.0f)); + cc::PaintFlags flags; + flags.setAntiAlias(true); + flags.setStyle(cc::PaintFlags::kStroke_Style); + flags.setStrokeWidth(kBorderThicknessDip); + constexpr SkColor kBorderColor = gfx::kGoogleGrey600; + flags.setColor(kBorderColor); + canvas->DrawRoundRect(bounds, GetBorderCornerRadius(), flags); +} + void BubbleBackground::Paint(gfx::Canvas* canvas, views::View* view) const { if (border_->shadow() == BubbleBorder::NO_SHADOW_OPAQUE_BORDER) canvas->DrawColor(border_->background_color()); diff --git a/chromium/ui/views/bubble/bubble_border.h b/chromium/ui/views/bubble/bubble_border.h index d4a6e2a28a7..79c182cffd1 100644 --- a/chromium/ui/views/bubble/bubble_border.h +++ b/chromium/ui/views/bubble/bubble_border.h @@ -229,6 +229,9 @@ class VIEWS_EXPORT BubbleBorder : public Border { // to make the window shape based on insets and GetBorderCornerRadius(). void PaintNoAssets(const View& view, gfx::Canvas* canvas); + // Paint for the NO_SHADOW shadow type. This paints a simple line border. + void PaintNoShadow(const View& view, gfx::Canvas* canvas); + Arrow arrow_; int arrow_offset_; // Corner radius for the bubble border. If supplied the border will use diff --git a/chromium/ui/views/bubble/bubble_border_unittest.cc b/chromium/ui/views/bubble/bubble_border_unittest.cc index e1420089ab5..54476123fc1 100644 --- a/chromium/ui/views/bubble/bubble_border_unittest.cc +++ b/chromium/ui/views/bubble/bubble_border_unittest.cc @@ -314,6 +314,9 @@ TEST_F(BubbleBorderTest, GetBoundsOriginTest) { const int kStrokeWidth = shadow == BubbleBorder::NO_ASSETS ? 0 : BubbleBorder::kStroke; + const int kBorderedContentHeight = + kContentSize.height() + (2 * kStrokeWidth); + const int kTopHorizArrowY = kAnchor.bottom() + kStrokeWidth - kInsets.top(); const int kBottomHorizArrowY = kAnchor.y() - kTotalSize.height(); const int kLeftVertArrowX = kAnchor.x() + kAnchor.width(); @@ -338,9 +341,9 @@ TEST_F(BubbleBorderTest, GetBoundsOriginTest) { // Vertical arrow tests. {BubbleBorder::LEFT_TOP, kLeftVertArrowX, kAnchor.y() + kStrokeWidth}, {BubbleBorder::LEFT_CENTER, - kLeftVertArrowX - (kInsets.right() - kStrokeWidth), - kAnchor.CenterPoint().y() - (kTotalSize.height() / 2) + - (2 * kStrokeWidth)}, + kLeftVertArrowX - (kInsets.left() - kStrokeWidth), + kAnchor.CenterPoint().y() - (kBorderedContentHeight / 2) - + (kInsets.top() - kStrokeWidth)}, {BubbleBorder::RIGHT_BOTTOM, kRightVertArrowX, kAnchor.y() + kAnchor.height() - kTotalSize.height() - kStrokeWidth}, diff --git a/chromium/ui/views/bubble/bubble_dialog_delegate_view.cc b/chromium/ui/views/bubble/bubble_dialog_delegate_view.cc index 84d9ade9513..434ae1ea7db 100644 --- a/chromium/ui/views/bubble/bubble_dialog_delegate_view.cc +++ b/chromium/ui/views/bubble/bubble_dialog_delegate_view.cc @@ -17,7 +17,6 @@ #include "ui/views/layout/layout_manager.h" #include "ui/views/layout/layout_provider.h" #include "ui/views/view_properties.h" -#include "ui/views/view_tracker.h" #include "ui/views/views_delegate.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_observer.h" @@ -51,17 +50,30 @@ class BubbleDialogFrameView : public BubbleFrameView { DISALLOW_COPY_AND_ASSIGN(BubbleDialogFrameView); }; +bool CustomShadowsSupported() { +#if defined(OS_WIN) + return ui::win::IsAeroGlassEnabled(); +#else + return true; +#endif +} + // Create a widget to host the bubble. Widget* CreateBubbleWidget(BubbleDialogDelegateView* bubble) { Widget* bubble_widget = new Widget(); Widget::InitParams bubble_params(Widget::InitParams::TYPE_BUBBLE); bubble_params.delegate = bubble; - bubble_params.opacity = Widget::InitParams::TRANSLUCENT_WINDOW; + bubble_params.opacity = CustomShadowsSupported() + ? Widget::InitParams::TRANSLUCENT_WINDOW + : Widget::InitParams::OPAQUE_WINDOW; bubble_params.accept_events = bubble->accept_events(); // Use a window default shadow if the bubble doesn't provides its own. - bubble_params.shadow_type = bubble->shadow() == BubbleBorder::NO_ASSETS - ? Widget::InitParams::SHADOW_TYPE_DEFAULT - : Widget::InitParams::SHADOW_TYPE_NONE; + if (bubble->GetShadow() == BubbleBorder::NO_ASSETS) + bubble_params.shadow_type = Widget::InitParams::SHADOW_TYPE_DEFAULT; + else if (CustomShadowsSupported()) + bubble_params.shadow_type = Widget::InitParams::SHADOW_TYPE_NONE; + else + bubble_params.shadow_type = Widget::InitParams::SHADOW_TYPE_DROP; if (bubble->parent_window()) bubble_params.parent = bubble->parent_window(); else if (bubble->anchor_widget()) @@ -102,12 +114,7 @@ Widget* BubbleDialogDelegateView::CreateBubble( bubble_delegate->SetAnchorView(bubble_delegate->GetAnchorView()); Widget* bubble_widget = CreateBubbleWidget(bubble_delegate); -#if defined(OS_WIN) - // If glass is enabled, the bubble is allowed to extend outside the bounds of - // the parent frame and let DWM handle compositing. If not, then we don't - // want to allow the bubble to extend the frame because it will be clipped. - bubble_delegate->set_adjust_if_offscreen(ui::win::IsAeroGlassEnabled()); -#elif (defined(OS_LINUX) && !defined(OS_CHROMEOS)) || defined(OS_MACOSX) +#if (defined(OS_LINUX) && !defined(OS_CHROMEOS)) || defined(OS_MACOSX) // Linux clips bubble windows that extend outside their parent window bounds. // Mac never adjusts. bubble_delegate->set_adjust_if_offscreen(false); @@ -145,7 +152,11 @@ NonClientFrameView* BubbleDialogDelegateView::CreateNonClientFrameView( if (base::i18n::IsRTL() && mirror_arrow_in_rtl_) adjusted_arrow = BubbleBorder::horizontal_mirror(adjusted_arrow); std::unique_ptr<BubbleBorder> border = - std::make_unique<BubbleBorder>(adjusted_arrow, shadow(), color()); + std::make_unique<BubbleBorder>(adjusted_arrow, GetShadow(), color()); + // If custom shadows aren't supported we fall back to an OS provided square + // shadow. + if (!CustomShadowsSupported()) + border->SetCornerRadius(0); frame->SetBubbleBorder(std::move(border)); return frame; } @@ -198,16 +209,34 @@ void BubbleDialogDelegateView::OnWidgetBoundsChanged( SizeToContents(); } +BubbleBorder::Shadow BubbleDialogDelegateView::GetShadow() const { + if (CustomShadowsSupported() || shadow_ == BubbleBorder::NO_ASSETS) + return shadow_; + return BubbleBorder::NO_SHADOW; +} + View* BubbleDialogDelegateView::GetAnchorView() const { return anchor_view_tracker_->view(); } -void BubbleDialogDelegateView::set_arrow(BubbleBorder::Arrow arrow) { +void BubbleDialogDelegateView::SetHighlightedButton( + Button* highlighted_button) { + bool visible = GetWidget() && GetWidget()->IsVisible(); + // If the Widget is visible, ensure the old highlight (if any) is removed + // when the highlighted view changes. + if (visible) + UpdateHighlightedButton(false); + highlighted_button_tracker_.SetView(highlighted_button); + if (visible) + UpdateHighlightedButton(true); +} + +void BubbleDialogDelegateView::SetArrow(BubbleBorder::Arrow arrow) { if (arrow_ == arrow) return; arrow_ = arrow; - // If set_arrow() is called before CreateWidget(), there's no need to update + // If SetArrow() is called before CreateWidget(), there's no need to update // the BubbleFrameView. if (GetBubbleFrameView()) { GetBubbleFrameView()->bubble_border()->set_arrow(arrow); @@ -321,8 +350,10 @@ void BubbleDialogDelegateView::SetAnchorView(View* anchor_view) { // change as well. if (!anchor_view || anchor_widget() != anchor_view->GetWidget()) { if (anchor_widget()) { - if (GetWidget() && GetWidget()->IsVisible()) + if (GetWidget() && GetWidget()->IsVisible()) { UpdateAnchorWidgetRenderState(false); + UpdateHighlightedButton(false); + } anchor_widget_->RemoveObserver(this); anchor_widget_ = NULL; } @@ -330,7 +361,9 @@ void BubbleDialogDelegateView::SetAnchorView(View* anchor_view) { anchor_widget_ = anchor_view->GetWidget(); if (anchor_widget_) { anchor_widget_->AddObserver(this); - UpdateAnchorWidgetRenderState(GetWidget() && GetWidget()->IsVisible()); + const bool visible = GetWidget() && GetWidget()->IsVisible(); + UpdateAnchorWidgetRenderState(visible); + UpdateHighlightedButton(visible); } } } @@ -391,8 +424,10 @@ void BubbleDialogDelegateView::UpdateColorsFromTheme( void BubbleDialogDelegateView::HandleVisibilityChanged(Widget* widget, bool visible) { - if (widget == GetWidget()) + if (widget == GetWidget()) { UpdateAnchorWidgetRenderState(visible); + UpdateHighlightedButton(visible); + } // Fire ax::mojom::Event::kAlert for bubbles marked as // ax::mojom::Role::kAlertDialog; this instructs accessibility tools to read @@ -419,4 +454,11 @@ void BubbleDialogDelegateView::UpdateAnchorWidgetRenderState(bool visible) { anchor_widget()->GetTopLevelWidget()->SetAlwaysRenderAsActive(visible); } +void BubbleDialogDelegateView::UpdateHighlightedButton(bool highlighted) { + Button* button = Button::AsButton(highlighted_button_tracker_.view()); + button = button ? button : Button::AsButton(anchor_view_tracker_->view()); + if (button) + button->SetHighlighted(highlighted); +} + } // namespace views diff --git a/chromium/ui/views/bubble/bubble_dialog_delegate_view.h b/chromium/ui/views/bubble/bubble_dialog_delegate_view.h index 51a95a0ab17..8d6561420c6 100644 --- a/chromium/ui/views/bubble/bubble_dialog_delegate_view.h +++ b/chromium/ui/views/bubble/bubble_dialog_delegate_view.h @@ -12,6 +12,7 @@ #include "build/build_config.h" #include "ui/accessibility/ax_enums.mojom.h" #include "ui/views/bubble/bubble_border.h" +#include "ui/views/view_tracker.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_observer.h" #include "ui/views/window/dialog_delegate.h" @@ -27,7 +28,7 @@ class Rect; namespace views { class BubbleFrameView; -class ViewTracker; +class Button; // BubbleDialogDelegateView is a special DialogDelegateView for bubbles. class VIEWS_EXPORT BubbleDialogDelegateView : public DialogDelegateView, @@ -68,15 +69,17 @@ class VIEWS_EXPORT BubbleDialogDelegateView : public DialogDelegateView, View* GetAnchorView() const; Widget* anchor_widget() const { return anchor_widget_; } + void SetHighlightedButton(Button* highlighted_button); + // The anchor rect is used in the absence of an assigned anchor view. const gfx::Rect& anchor_rect() const { return anchor_rect_; } BubbleBorder::Arrow arrow() const { return arrow_; } - void set_arrow(BubbleBorder::Arrow arrow); + void SetArrow(BubbleBorder::Arrow arrow); void set_mirror_arrow_in_rtl(bool mirror) { mirror_arrow_in_rtl_ = mirror; } - BubbleBorder::Shadow shadow() const { return shadow_; } + BubbleBorder::Shadow GetShadow() const; void set_shadow(BubbleBorder::Shadow shadow) { shadow_ = shadow; } SkColor color() const { return color_; } @@ -182,6 +185,11 @@ class VIEWS_EXPORT BubbleDialogDelegateView : public DialogDelegateView, // When a bubble is visible, the anchor widget should always render as active. void UpdateAnchorWidgetRenderState(bool visible); + // Update the button highlight, which may be the anchor view or an explicit + // view set in |highlighted_button_tracker_|. This can be overridden to + // provide different highlight effects. + virtual void UpdateHighlightedButton(bool highlighted); + // A flag controlling bubble closure on deactivation. bool close_on_deactivate_; @@ -191,6 +199,11 @@ class VIEWS_EXPORT BubbleDialogDelegateView : public DialogDelegateView, std::unique_ptr<ViewTracker> anchor_view_tracker_; Widget* anchor_widget_; + // If provided, this button should be highlighted while the bubble is visible. + // If not provided, the anchor_view will attempt to be highlighted. A + // ViewTracker is used because the view can be deleted. + ViewTracker highlighted_button_tracker_; + // The anchor rect used in the absence of an anchor view. mutable gfx::Rect anchor_rect_; diff --git a/chromium/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc b/chromium/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc index 5de626a2bd4..cf205c20d3f 100644 --- a/chromium/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc +++ b/chromium/ui/views/bubble/bubble_dialog_delegate_view_unittest.cc @@ -11,7 +11,10 @@ #include "base/strings/utf_string_conversions.h" #include "ui/base/hit_test.h" #include "ui/events/event_utils.h" +#include "ui/views/animation/test/ink_drop_host_view_test_api.h" +#include "ui/views/animation/test/test_ink_drop.h" #include "ui/views/bubble/bubble_frame_view.h" +#include "ui/views/controls/button/button.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/styled_label.h" #include "ui/views/test/test_views.h" @@ -22,6 +25,8 @@ namespace views { +using test::TestInkDrop; + namespace { constexpr int kContentHeight = 200; @@ -362,6 +367,7 @@ TEST_F(BubbleDialogDelegateViewTest, CloseMethods) { BubbleDialogDelegateView::CreateBubble(bubble_delegate); bubble_widget->Show(); BubbleFrameView* frame_view = bubble_delegate->GetBubbleFrameView(); + frame_view->ResetViewShownTimeStampForTesting(); Button* close_button = frame_view->close_; ASSERT_TRUE(close_button); frame_view->ButtonPressed( @@ -477,6 +483,62 @@ TEST_F(BubbleDialogDelegateViewTest, StyledLabelTitle) { bubble_widget->GetWindowBoundsInScreen().height()); } +// Ensure associated buttons are highlighted or unhighlighted when the bubble +// widget is shown or hidden respectively. +TEST_F(BubbleDialogDelegateViewTest, AttachedWidgetShowsInkDropWhenVisible) { + std::unique_ptr<Widget> anchor_widget(CreateTestWidget()); + LabelButton* button = new LabelButton(nullptr, base::string16()); + anchor_widget->GetContentsView()->AddChildView(button); + TestInkDrop* ink_drop = new TestInkDrop(); + test::InkDropHostViewTestApi(button).SetInkDrop(base::WrapUnique(ink_drop)); + TestBubbleDialogDelegateView* bubble_delegate = + new TestBubbleDialogDelegateView(nullptr); + bubble_delegate->set_parent_window(anchor_widget->GetNativeView()); + + Widget* bubble_widget = + BubbleDialogDelegateView::CreateBubble(bubble_delegate); + bubble_delegate->SetHighlightedButton(button); + bubble_widget->Show(); + // Explicitly calling OnWidgetVisibilityChanging to test functionality for + // OS_WIN. Outside of the test environment this happens automatically by way + // of HWNDMessageHandler. + bubble_delegate->OnWidgetVisibilityChanging(bubble_widget, true); + EXPECT_EQ(InkDropState::ACTIVATED, ink_drop->GetTargetInkDropState()); + + bubble_widget->Close(); + bubble_delegate->OnWidgetVisibilityChanging(bubble_widget, false); + EXPECT_EQ(InkDropState::DEACTIVATED, ink_drop->GetTargetInkDropState()); +} + +// Ensure associated buttons are highlighted or unhighlighted when the bubble +// widget is shown or hidden respectively when highlighted button is set after +// widget is shown. +TEST_F(BubbleDialogDelegateViewTest, VisibleWidgetShowsInkDropOnAttaching) { + std::unique_ptr<Widget> anchor_widget(CreateTestWidget()); + LabelButton* button = new LabelButton(nullptr, base::string16()); + anchor_widget->GetContentsView()->AddChildView(button); + TestInkDrop* ink_drop = new TestInkDrop(); + test::InkDropHostViewTestApi(button).SetInkDrop(base::WrapUnique(ink_drop)); + TestBubbleDialogDelegateView* bubble_delegate = + new TestBubbleDialogDelegateView(nullptr); + bubble_delegate->set_parent_window(anchor_widget->GetNativeView()); + + Widget* bubble_widget = + BubbleDialogDelegateView::CreateBubble(bubble_delegate); + bubble_widget->Show(); + // Explicitly calling OnWidgetVisibilityChanging to test functionality for + // OS_WIN. Outside of the test environment this happens automatically by way + // of HWNDMessageHandler. + bubble_delegate->OnWidgetVisibilityChanging(bubble_widget, true); + EXPECT_EQ(InkDropState::HIDDEN, ink_drop->GetTargetInkDropState()); + bubble_delegate->SetHighlightedButton(button); + EXPECT_EQ(InkDropState::ACTIVATED, ink_drop->GetTargetInkDropState()); + + bubble_widget->Close(); + bubble_delegate->OnWidgetVisibilityChanging(bubble_widget, false); + EXPECT_EQ(InkDropState::DEACTIVATED, ink_drop->GetTargetInkDropState()); +} + TEST_F(BubbleDialogDelegateViewTest, VisibleAnchorChanges) { std::unique_ptr<Widget> anchor_widget(CreateTestWidget()); TestBubbleDialogDelegateView* bubble_delegate = diff --git a/chromium/ui/views/bubble/bubble_frame_view.cc b/chromium/ui/views/bubble/bubble_frame_view.cc index f43e505afb4..02aa4a05ff8 100644 --- a/chromium/ui/views/bubble/bubble_frame_view.cc +++ b/chromium/ui/views/bubble/bubble_frame_view.cc @@ -28,10 +28,12 @@ #include "ui/views/controls/button/image_button.h" #include "ui/views/controls/button/image_button_factory.h" #include "ui/views/controls/image_view.h" +#include "ui/views/event_utils.h" #include "ui/views/layout/box_layout.h" #include "ui/views/layout/layout_provider.h" #include "ui/views/paint_info.h" #include "ui/views/resources/grit/views_resources.h" +#include "ui/views/view_properties.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" #include "ui/views/window/client_view.h" @@ -131,6 +133,11 @@ Button* BubbleFrameView::CreateCloseButton(ButtonListener* listener) { close_button->SetTooltipText(l10n_util::GetStringUTF16(IDS_APP_CLOSE)); close_button->SizeToPreferredSize(); + // Let the close button use a circular inkdrop shape. + auto highlight_path = std::make_unique<gfx::Path>(); + highlight_path->addOval(gfx::RectToSkRect(gfx::Rect(close_button->size()))); + close_button->SetProperty(kHighlightPathKey, highlight_path.release()); + // Remove the close button from tab traversal on all platforms. Note this does // not affect screen readers' ability to focus the close button. Keyboard // access to the close button when not using a screen reader is done via the @@ -407,6 +414,13 @@ void BubbleFrameView::ViewHierarchyChanged( } } +void BubbleFrameView::VisibilityChanged(View* starting_from, bool is_visible) { + NonClientFrameView::VisibilityChanged(starting_from, is_visible); + + if (is_visible) + view_shown_time_stamp_ = base::TimeTicks::Now(); +} + void BubbleFrameView::OnPaint(gfx::Canvas* canvas) { OnPaintBackground(canvas); // Border comes after children. @@ -424,6 +438,9 @@ void BubbleFrameView::PaintChildren(const PaintInfo& paint_info) { } void BubbleFrameView::ButtonPressed(Button* sender, const ui::Event& event) { + if (IsPossiblyUnintendedInteraction(view_shown_time_stamp_, event)) + return; + if (sender == close_) { close_button_clicked_ = true; GetWidget()->Close(); @@ -477,6 +494,10 @@ gfx::Rect BubbleFrameView::GetUpdatedWindowBounds(const gfx::Rect& anchor_rect, return bubble_border_->GetBounds(anchor_rect, size); } +void BubbleFrameView::ResetViewShownTimeStampForTesting() { + view_shown_time_stamp_ = base::TimeTicks(); +} + gfx::Rect BubbleFrameView::GetAvailableScreenBounds( const gfx::Rect& rect) const { // The bubble attempts to fit within the current screen bounds. diff --git a/chromium/ui/views/bubble/bubble_frame_view.h b/chromium/ui/views/bubble/bubble_frame_view.h index eef3fda7a3e..bfb407a231f 100644 --- a/chromium/ui/views/bubble/bubble_frame_view.h +++ b/chromium/ui/views/bubble/bubble_frame_view.h @@ -8,6 +8,7 @@ #include "base/compiler_specific.h" #include "base/gtest_prod_util.h" #include "base/macros.h" +#include "base/time/time.h" #include "ui/gfx/font_list.h" #include "ui/gfx/geometry/insets.h" #include "ui/views/controls/button/button.h" @@ -65,6 +66,7 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView, void OnNativeThemeChanged(const ui::NativeTheme* theme) override; void ViewHierarchyChanged( const ViewHierarchyChangedDetails& details) override; + void VisibilityChanged(View* starting_from, bool is_visible) override; // ButtonListener: void ButtonPressed(Button* sender, const ui::Event& event) override; @@ -99,6 +101,11 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView, Button* GetCloseButtonForTest() { return close_; } + // Resets the time when view has been shown. Tests may need to call this + // method if they use events that could be otherwise treated as unintended. + // See IsPossiblyUnintendedInteraction(). + void ResetViewShownTimeStampForTesting(); + protected: // Returns the available screen bounds if the frame were to show in |rect|. virtual gfx::Rect GetAvailableScreenBounds(const gfx::Rect& rect) const; @@ -115,6 +122,7 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView, FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewTest, GetBoundsForClientView); FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewTest, RemoveFootnoteView); FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewTest, LayoutWithIcon); + FRIEND_TEST_ALL_PREFIXES(BubbleFrameViewTest, IgnorePossiblyUnintendedClicks); FRIEND_TEST_ALL_PREFIXES(BubbleDelegateTest, CloseReasons); FRIEND_TEST_ALL_PREFIXES(BubbleDialogDelegateViewTest, CloseMethods); @@ -180,6 +188,9 @@ class VIEWS_EXPORT BubbleFrameView : public NonClientFrameView, // Whether the close button was clicked. bool close_button_clicked_; + // Time when view has been shown. + base::TimeTicks view_shown_time_stamp_; + DISALLOW_COPY_AND_ASSIGN(BubbleFrameView); }; diff --git a/chromium/ui/views/bubble/bubble_frame_view_unittest.cc b/chromium/ui/views/bubble/bubble_frame_view_unittest.cc index 0a060d2fbf5..38930c39de0 100644 --- a/chromium/ui/views/bubble/bubble_frame_view_unittest.cc +++ b/chromium/ui/views/bubble/bubble_frame_view_unittest.cc @@ -8,14 +8,19 @@ #include "base/macros.h" #include "base/strings/utf_string_conversions.h" +#include "base/time/time.h" #include "build/build_config.h" +#include "ui/events/base_event_utils.h" +#include "ui/events/event.h" #include "ui/gfx/geometry/insets.h" +#include "ui/gfx/geometry/point.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" #include "ui/gfx/text_utils.h" #include "ui/views/bubble/bubble_border.h" #include "ui/views/bubble/bubble_dialog_delegate_view.h" #include "ui/views/controls/button/label_button.h" +#include "ui/views/metrics.h" #include "ui/views/test/test_layout_provider.h" #include "ui/views/test/test_views.h" #include "ui/views/test/views_test_base.h" @@ -84,7 +89,7 @@ class TestBubbleFrameView : public BubbleFrameView { : BubbleFrameView(gfx::Insets(), gfx::Insets(kMargin)), available_bounds_(gfx::Rect(0, 0, 1000, 1000)) { SetBubbleBorder(std::make_unique<BubbleBorder>( - kArrow, BubbleBorder::NO_SHADOW, kColor)); + kArrow, BubbleBorder::BIG_SHADOW, kColor)); widget_ = std::make_unique<Widget>(); widget_delegate_ = std::make_unique<TestBubbleFrameViewWidgetDelegate>(widget_.get()); @@ -751,4 +756,28 @@ TEST_F(BubbleFrameViewTest, NoElideTitle) { EXPECT_EQ(title, title_label->GetDisplayTextForTesting()); } +// Ensures that clicks are ignored for short time after view has been shown. +TEST_F(BubbleFrameViewTest, IgnorePossiblyUnintendedClicks) { + TestBubbleDialogDelegateView delegate; + TestAnchor anchor(CreateParams(Widget::InitParams::TYPE_WINDOW)); + delegate.SetAnchorView(anchor.widget().GetContentsView()); + Widget* bubble = BubbleDialogDelegateView::CreateBubble(&delegate); + bubble->Show(); + + BubbleFrameView* frame = delegate.GetBubbleFrameView(); + frame->ButtonPressed( + frame->close_, + ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), + ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE)); + EXPECT_FALSE(bubble->IsClosed()); + + frame->ButtonPressed( + frame->close_, + ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), + ui::EventTimeForNow() + base::TimeDelta::FromMilliseconds( + GetDoubleClickInterval()), + ui::EF_NONE, ui::EF_NONE)); + EXPECT_TRUE(bubble->IsClosed()); +} + } // namespace views diff --git a/chromium/ui/views/bubble/info_bubble.cc b/chromium/ui/views/bubble/info_bubble.cc index 65fc0411ece..442287d5d22 100644 --- a/chromium/ui/views/bubble/info_bubble.cc +++ b/chromium/ui/views/bubble/info_bubble.cc @@ -79,7 +79,7 @@ NonClientFrameView* InfoBubble::CreateNonClientFrameView(Widget* widget) { frame_ = new InfoBubbleFrame(margins()); frame_->set_available_bounds(anchor_widget()->GetWindowBoundsInScreen()); frame_->SetBubbleBorder(std::unique_ptr<BubbleBorder>( - new BubbleBorder(arrow(), shadow(), color()))); + new BubbleBorder(arrow(), GetShadow(), color()))); return frame_; } diff --git a/chromium/ui/views/bubble/tooltip_icon.cc b/chromium/ui/views/bubble/tooltip_icon.cc index 8e1969cd449..09be527d930 100644 --- a/chromium/ui/views/bubble/tooltip_icon.cc +++ b/chromium/ui/views/bubble/tooltip_icon.cc @@ -84,7 +84,7 @@ void TooltipIcon::ShowBubble() { bubble_ = new InfoBubble(this, tooltip_); bubble_->set_preferred_width(preferred_width_); - bubble_->set_arrow(anchor_point_arrow_); + bubble_->SetArrow(anchor_point_arrow_); // When shown due to a gesture event, close on deactivate (i.e. don't use // "focusless"). bubble_->set_can_activate(!mouse_inside_); diff --git a/chromium/ui/views/bubble/tray_bubble_view.cc b/chromium/ui/views/bubble/tray_bubble_view.cc deleted file mode 100644 index 9e2994cfefd..00000000000 --- a/chromium/ui/views/bubble/tray_bubble_view.cc +++ /dev/null @@ -1,496 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/views/bubble/tray_bubble_view.h" - -#include <algorithm> - -#include "base/macros.h" -#include "cc/paint/paint_flags.h" -#include "third_party/skia/include/core/SkCanvas.h" -#include "third_party/skia/include/core/SkColor.h" -#include "third_party/skia/include/core/SkPath.h" -#include "third_party/skia/include/effects/SkBlurImageFilter.h" -#include "ui/accessibility/ax_node_data.h" -#include "ui/aura/env.h" -#include "ui/aura/window.h" -#include "ui/compositor/layer.h" -#include "ui/compositor/layer_owner.h" -#include "ui/events/event.h" -#include "ui/gfx/canvas.h" -#include "ui/gfx/color_palette.h" -#include "ui/gfx/geometry/insets.h" -#include "ui/gfx/geometry/rect.h" -#include "ui/gfx/path.h" -#include "ui/gfx/skia_util.h" -#include "ui/views/bubble/bubble_frame_view.h" -#include "ui/views/layout/box_layout.h" -#include "ui/views/painter.h" -#include "ui/views/views_delegate.h" -#include "ui/views/widget/widget.h" -#include "ui/wm/core/shadow_types.h" -#include "ui/wm/core/window_util.h" - -namespace views { - -namespace { - -// The sampling time for mouse position changes in ms - which is roughly a frame -// time. -const int kFrameTimeInMS = 30; - -BubbleBorder::Arrow GetArrowAlignment( - TrayBubbleView::AnchorAlignment alignment) { - if (alignment == TrayBubbleView::ANCHOR_ALIGNMENT_BOTTOM) { - return base::i18n::IsRTL() ? BubbleBorder::BOTTOM_LEFT - : BubbleBorder::BOTTOM_RIGHT; - } - if (alignment == TrayBubbleView::ANCHOR_ALIGNMENT_LEFT) - return BubbleBorder::LEFT_BOTTOM; - return BubbleBorder::RIGHT_BOTTOM; -} - -// Only one TrayBubbleView is visible at a time, but there are cases where the -// lifetimes of two different bubbles can overlap briefly. -int g_current_tray_bubble_showing_count_ = 0; - -} // namespace - -namespace internal { - -// Detects any mouse movement. This is needed to detect mouse movements by the -// user over the bubble if the bubble got created underneath the cursor. -class MouseMoveDetectorHost : public MouseWatcherHost { - public: - MouseMoveDetectorHost(); - ~MouseMoveDetectorHost() override; - - bool Contains(const gfx::Point& screen_point, MouseEventType type) override; - - private: - DISALLOW_COPY_AND_ASSIGN(MouseMoveDetectorHost); -}; - -MouseMoveDetectorHost::MouseMoveDetectorHost() { -} - -MouseMoveDetectorHost::~MouseMoveDetectorHost() { -} - -bool MouseMoveDetectorHost::Contains(const gfx::Point& screen_point, - MouseEventType type) { - return false; -} - -// Custom layout for the bubble-view. Does the default box-layout if there is -// enough height. Otherwise, makes sure the bottom rows are visible. -class BottomAlignedBoxLayout : public BoxLayout { - public: - explicit BottomAlignedBoxLayout(TrayBubbleView* bubble_view) - : BoxLayout(BoxLayout::kVertical), bubble_view_(bubble_view) {} - - ~BottomAlignedBoxLayout() override {} - - private: - void Layout(View* host) override { - if (host->height() >= host->GetPreferredSize().height() || - !bubble_view_->is_gesture_dragging()) { - BoxLayout::Layout(host); - return; - } - - int consumed_height = 0; - for (int i = host->child_count() - 1; - i >= 0 && consumed_height < host->height(); --i) { - View* child = host->child_at(i); - if (!child->visible()) - continue; - gfx::Size size = child->GetPreferredSize(); - child->SetBounds(0, host->height() - consumed_height - size.height(), - host->width(), size.height()); - consumed_height += size.height(); - } - } - - TrayBubbleView* bubble_view_; - - DISALLOW_COPY_AND_ASSIGN(BottomAlignedBoxLayout); -}; - -} // namespace internal - -using internal::BottomAlignedBoxLayout; - -TrayBubbleView::Delegate::~Delegate() {} - -void TrayBubbleView::Delegate::BubbleViewDestroyed() {} - -void TrayBubbleView::Delegate::OnMouseEnteredView() {} - -void TrayBubbleView::Delegate::OnMouseExitedView() {} - -base::string16 TrayBubbleView::Delegate::GetAccessibleNameForBubble() { - return base::string16(); -} - -bool TrayBubbleView::Delegate::ShouldEnableExtraKeyboardAccessibility() { - return false; -} - -void TrayBubbleView::Delegate::HideBubble(const TrayBubbleView* bubble_view) {} - -void TrayBubbleView::Delegate::ProcessGestureEventForBubble( - ui::GestureEvent* event) {} - -TrayBubbleView::InitParams::InitParams() = default; - -TrayBubbleView::InitParams::InitParams(const InitParams& other) = default; - -TrayBubbleView::RerouteEventHandler::RerouteEventHandler( - TrayBubbleView* tray_bubble_view) - : tray_bubble_view_(tray_bubble_view) { - aura::Env::GetInstance()->AddPreTargetHandler( - this, ui::EventTarget::Priority::kSystem); -} - -TrayBubbleView::RerouteEventHandler::~RerouteEventHandler() { - aura::Env::GetInstance()->RemovePreTargetHandler(this); -} - -void TrayBubbleView::RerouteEventHandler::OnKeyEvent(ui::KeyEvent* event) { - // Do not handle a key event if it is targeted to the tray or its descendants, - // or if the target has the tray as a transient ancestor. RerouteEventHandler - // is for rerouting events which are not targetted to the tray. Those events - // should be handled by the target. - aura::Window* target = static_cast<aura::Window*>(event->target()); - aura::Window* tray_window = tray_bubble_view_->GetWidget()->GetNativeView(); - if (target && (tray_window->Contains(target) || - wm::HasTransientAncestor(target, tray_window))) { - return; - } - - // Only passes Tab, Shift+Tab, Esc to the widget as it can consume more key - // events. e.g. Alt+Tab can be consumed as focus traversal by FocusManager. - ui::KeyboardCode key_code = event->key_code(); - int flags = event->flags(); - if ((key_code == ui::VKEY_TAB && flags == ui::EF_NONE) || - (key_code == ui::VKEY_TAB && flags == ui::EF_SHIFT_DOWN) || - (key_code == ui::VKEY_ESCAPE && flags == ui::EF_NONE)) { - // Make TrayBubbleView activatable as the following Widget::OnKeyEvent might - // try to activate it. - tray_bubble_view_->set_can_activate(true); - - tray_bubble_view_->GetWidget()->OnKeyEvent(event); - - if (event->handled()) - return; - } - - // Always consumes key event not to pass it to other widgets. Calling - // StopPropagation here to make this consistent with - // MenuController::OnWillDispatchKeyEvent. - event->StopPropagation(); - - // To provide consistent behavior with a menu, process accelerator as a menu - // is open if the event is not handled by the widget. - ui::Accelerator accelerator(*event); - ViewsDelegate::ProcessMenuAcceleratorResult result = - ViewsDelegate::GetInstance()->ProcessAcceleratorWhileMenuShowing( - accelerator); - if (result == ViewsDelegate::ProcessMenuAcceleratorResult::CLOSE_MENU) - tray_bubble_view_->CloseBubbleView(); -} - -TrayBubbleView::TrayBubbleView(const InitParams& init_params) - : BubbleDialogDelegateView(init_params.anchor_view, - GetArrowAlignment(init_params.anchor_alignment)), - params_(init_params), - layout_(nullptr), - delegate_(init_params.delegate), - preferred_width_(init_params.min_width), - bubble_border_(new BubbleBorder( - arrow(), - init_params.has_shadow ? BubbleBorder::NO_ASSETS - : BubbleBorder::NO_SHADOW, - init_params.bg_color.value_or(gfx::kPlaceholderColor))), - owned_bubble_border_(bubble_border_), - is_gesture_dragging_(false), - mouse_actively_entered_(false) { - DCHECK(delegate_); - DCHECK(params_.parent_window); - DCHECK(anchor_widget()); // Computed by BubbleDialogDelegateView(). - bubble_border_->set_use_theme_background_color(!init_params.bg_color); - if (init_params.corner_radius) - bubble_border_->SetCornerRadius(init_params.corner_radius.value()); - set_parent_window(params_.parent_window); - set_can_activate(false); - set_notify_enter_exit_on_child(true); - set_close_on_deactivate(init_params.close_on_deactivate); - set_margins(gfx::Insets()); - SetPaintToLayer(); - - bubble_content_mask_ = views::Painter::CreatePaintedLayer( - views::Painter::CreateSolidRoundRectPainter( - SK_ColorBLACK, bubble_border_->GetBorderCornerRadius())); - - auto layout = std::make_unique<BottomAlignedBoxLayout>(this); - layout->SetDefaultFlex(1); - layout_ = SetLayoutManager(std::move(layout)); -} - -TrayBubbleView::~TrayBubbleView() { - mouse_watcher_.reset(); - - if (delegate_) { - // Inform host items (models) that their views are being destroyed. - delegate_->BubbleViewDestroyed(); - } -} - -// static -bool TrayBubbleView::IsATrayBubbleOpen() { - return g_current_tray_bubble_showing_count_ > 0; -} - -void TrayBubbleView::InitializeAndShowBubble() { - layer()->parent()->SetMaskLayer(bubble_content_mask_->layer()); - - GetWidget()->Show(); - UpdateBubble(); - - ++g_current_tray_bubble_showing_count_; - - // If TrayBubbleView cannot be activated and is shown by clicking on the - // corresponding tray view, register pre target event handler to reroute key - // events to the widget for activating the view or closing it. - if (!CanActivate() && params_.show_by_click) { - reroute_event_handler_ = std::make_unique<RerouteEventHandler>(this); - } -} - -void TrayBubbleView::UpdateBubble() { - if (GetWidget()) { - SizeToContents(); - GetWidget()->GetRootView()->SchedulePaint(); - - // When extra keyboard accessibility is enabled, focus the default item if - // no item is focused. - if (delegate_ && delegate_->ShouldEnableExtraKeyboardAccessibility()) - FocusDefaultIfNeeded(); - } -} - -void TrayBubbleView::SetMaxHeight(int height) { - params_.max_height = height; - if (GetWidget()) - SizeToContents(); -} - -void TrayBubbleView::SetBottomPadding(int padding) { - layout_->set_inside_border_insets(gfx::Insets(0, 0, padding, 0)); -} - -void TrayBubbleView::SetWidth(int width) { - width = std::max(std::min(width, params_.max_width), params_.min_width); - if (preferred_width_ == width) - return; - preferred_width_ = width; - if (GetWidget()) - SizeToContents(); -} - -gfx::Insets TrayBubbleView::GetBorderInsets() const { - return bubble_border_->GetInsets(); -} - -void TrayBubbleView::ResetDelegate() { - reroute_event_handler_.reset(); - - delegate_ = nullptr; -} - -void TrayBubbleView::ChangeAnchorView(views::View* anchor_view) { - BubbleDialogDelegateView::SetAnchorView(anchor_view); -} - -int TrayBubbleView::GetDialogButtons() const { - return ui::DIALOG_BUTTON_NONE; -} - -ax::mojom::Role TrayBubbleView::GetAccessibleWindowRole() const { - // We override the role because the base class sets it to alert dialog. - // This would make screen readers announce the whole of the system tray - // which is undesirable. - return ax::mojom::Role::kDialog; -} - -void TrayBubbleView::SizeToContents() { - BubbleDialogDelegateView::SizeToContents(); - bubble_content_mask_->layer()->SetBounds(layer()->parent()->bounds()); -} - -void TrayBubbleView::OnBeforeBubbleWidgetInit(Widget::InitParams* params, - Widget* bubble_widget) const { - if (bubble_border_->shadow() == BubbleBorder::NO_ASSETS) { - // Apply a WM-provided shadow (see ui/wm/core/). - params->shadow_type = Widget::InitParams::SHADOW_TYPE_DROP; - params->shadow_elevation = wm::kShadowElevationActiveWindow; - } -} - -void TrayBubbleView::OnWidgetClosing(Widget* widget) { - // We no longer need to watch key events for activation if the widget is - // closing. - reroute_event_handler_.reset(); - - BubbleDialogDelegateView::OnWidgetClosing(widget); - --g_current_tray_bubble_showing_count_; - DCHECK_GE(g_current_tray_bubble_showing_count_, 0) - << "Closing " << widget->GetName(); -} - -void TrayBubbleView::OnWidgetActivationChanged(Widget* widget, bool active) { - // We no longer need to watch key events for activation if the widget is - // activated. - reroute_event_handler_.reset(); - - BubbleDialogDelegateView::OnWidgetActivationChanged(widget, active); -} - -NonClientFrameView* TrayBubbleView::CreateNonClientFrameView(Widget* widget) { - BubbleFrameView* frame = static_cast<BubbleFrameView*>( - BubbleDialogDelegateView::CreateNonClientFrameView(widget)); - frame->SetBubbleBorder(std::move(owned_bubble_border_)); - return frame; -} - -bool TrayBubbleView::WidgetHasHitTestMask() const { - return true; -} - -void TrayBubbleView::GetWidgetHitTestMask(gfx::Path* mask) const { - DCHECK(mask); - mask->addRect(gfx::RectToSkRect(GetBubbleFrameView()->GetContentsBounds())); -} - -base::string16 TrayBubbleView::GetAccessibleWindowTitle() const { - if (delegate_) - return delegate_->GetAccessibleNameForBubble(); - else - return base::string16(); -} - -gfx::Size TrayBubbleView::CalculatePreferredSize() const { - DCHECK_LE(preferred_width_, params_.max_width); - return gfx::Size(preferred_width_, GetHeightForWidth(preferred_width_)); -} - -int TrayBubbleView::GetHeightForWidth(int width) const { - int height = GetInsets().height(); - width = std::max(width - GetInsets().width(), 0); - for (int i = 0; i < child_count(); ++i) { - const View* child = child_at(i); - if (child->visible()) - height += child->GetHeightForWidth(width); - } - - return (params_.max_height != 0) ? - std::min(height, params_.max_height) : height; -} - -void TrayBubbleView::OnMouseEntered(const ui::MouseEvent& event) { - mouse_watcher_.reset(); - if (delegate_ && !(event.flags() & ui::EF_IS_SYNTHESIZED)) { - // Coming here the user was actively moving the mouse over the bubble and - // we inform the delegate that we entered. This will prevent the bubble - // to auto close. - delegate_->OnMouseEnteredView(); - mouse_actively_entered_ = true; - } else { - // Coming here the bubble got shown and the mouse was 'accidentally' over it - // which is not a reason to prevent the bubble to auto close. As such we - // do not call the delegate, but wait for the first mouse move within the - // bubble. The used MouseWatcher will notify use of a movement and call - // |MouseMovedOutOfHost|. - mouse_watcher_ = std::make_unique<MouseWatcher>( - std::make_unique<views::internal::MouseMoveDetectorHost>(), this); - // Set the mouse sampling frequency to roughly a frame time so that the user - // cannot see a lag. - mouse_watcher_->set_notify_on_exit_time( - base::TimeDelta::FromMilliseconds(kFrameTimeInMS)); - mouse_watcher_->Start(GetWidget()->GetNativeWindow()); - } -} - -void TrayBubbleView::OnMouseExited(const ui::MouseEvent& event) { - // If there was a mouse watcher waiting for mouse movements we disable it - // immediately since we now leave the bubble. - mouse_watcher_.reset(); - // Do not notify the delegate of an exit if we never told it that we entered. - if (delegate_ && mouse_actively_entered_) - delegate_->OnMouseExitedView(); -} - -void TrayBubbleView::GetAccessibleNodeData(ui::AXNodeData* node_data) { - if (delegate_ && CanActivate()) { - node_data->role = ax::mojom::Role::kWindow; - node_data->SetName(delegate_->GetAccessibleNameForBubble()); - } -} - -void TrayBubbleView::OnGestureEvent(ui::GestureEvent* event) { - if (delegate_) - delegate_->ProcessGestureEventForBubble(event); - - if (!event->handled()) - BubbleDialogDelegateView::OnGestureEvent(event); -} - -void TrayBubbleView::MouseMovedOutOfHost() { - // The mouse was accidentally over the bubble when it opened and the AutoClose - // logic was not activated. Now that the user did move the mouse we tell the - // delegate to disable AutoClose. - if (delegate_) - delegate_->OnMouseEnteredView(); - mouse_actively_entered_ = true; - mouse_watcher_->Stop(); -} - -void TrayBubbleView::ChildPreferredSizeChanged(View* child) { - SizeToContents(); -} - -void TrayBubbleView::ViewHierarchyChanged( - const ViewHierarchyChangedDetails& details) { - if (details.is_add && details.child == this) { - details.parent->SetPaintToLayer(); - details.parent->layer()->SetMasksToBounds(true); - } -} - -void TrayBubbleView::CloseBubbleView() { - if (!delegate_) - return; - - delegate_->HideBubble(this); -} - -void TrayBubbleView::FocusDefaultIfNeeded() { - views::FocusManager* manager = GetFocusManager(); - if (!manager || manager->GetFocusedView()) - return; - - views::View* view = - manager->GetNextFocusableView(nullptr, nullptr, false, false); - if (!view) - return; - - // No need to explicitly activate the widget. View::RequestFocus will activate - // it if necessary. - set_can_activate(true); - - view->RequestFocus(); -} - -} // namespace views diff --git a/chromium/ui/views/bubble/tray_bubble_view.h b/chromium/ui/views/bubble/tray_bubble_view.h deleted file mode 100644 index 5ef449781bc..00000000000 --- a/chromium/ui/views/bubble/tray_bubble_view.h +++ /dev/null @@ -1,230 +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_VIEWS_BUBBLE_TRAY_BUBBLE_VIEW_H_ -#define UI_VIEWS_BUBBLE_TRAY_BUBBLE_VIEW_H_ - -#include <memory> - -#include "base/macros.h" -#include "base/optional.h" -#include "ui/accessibility/ax_enums.mojom.h" -#include "ui/events/event.h" -#include "ui/gfx/native_widget_types.h" -#include "ui/views/bubble/bubble_dialog_delegate_view.h" -#include "ui/views/mouse_watcher.h" -#include "ui/views/views_export.h" - -namespace ui { -class LayerOwner; -} - -namespace views { -class BoxLayout; -class View; -class Widget; -} - -namespace views { - -// Specialized bubble view for bubbles associated with a tray icon (e.g. the -// Ash status area). Mostly this handles custom anchor location and arrow and -// border rendering. This also has its own delegate for handling mouse events -// and other implementation specific details. -class VIEWS_EXPORT TrayBubbleView : public BubbleDialogDelegateView, - public MouseWatcherListener { - public: - // AnchorAlignment determines to which side of the anchor the bubble will - // align itself. - enum AnchorAlignment { - ANCHOR_ALIGNMENT_BOTTOM, - ANCHOR_ALIGNMENT_LEFT, - ANCHOR_ALIGNMENT_RIGHT, - }; - - class VIEWS_EXPORT Delegate { - public: - typedef TrayBubbleView::AnchorAlignment AnchorAlignment; - - Delegate() {} - virtual ~Delegate(); - - // Called when the view is destroyed. Any pointers to the view should be - // cleared when this gets called. - virtual void BubbleViewDestroyed(); - - // Called when the mouse enters/exits the view. - // Note: This event will only be called if the mouse gets actively moved by - // the user to enter the view. - virtual void OnMouseEnteredView(); - virtual void OnMouseExitedView(); - - // Called from GetAccessibleNodeData(); should return the appropriate - // accessible name for the bubble. - virtual base::string16 GetAccessibleNameForBubble(); - - // Should return true if extra keyboard accessibility is enabled. - // TrayBubbleView will put focus on the default item if extra keyboard - // accessibility is enabled. - virtual bool ShouldEnableExtraKeyboardAccessibility(); - - // Called when a bubble wants to hide/destroy itself (e.g. last visible - // child view was closed). - virtual void HideBubble(const TrayBubbleView* bubble_view); - - // Called to process the gesture events that happened on the TrayBubbleView. - // Swiping down on the opened TrayBubbleView to close the bubble. - virtual void ProcessGestureEventForBubble(ui::GestureEvent* event); - - private: - DISALLOW_COPY_AND_ASSIGN(Delegate); - }; - - struct VIEWS_EXPORT InitParams { - InitParams(); - InitParams(const InitParams& other); - Delegate* delegate = nullptr; - gfx::NativeWindow parent_window = nullptr; - View* anchor_view = nullptr; - AnchorAlignment anchor_alignment = ANCHOR_ALIGNMENT_BOTTOM; - int min_width = 0; - int max_width = 0; - int max_height = 0; - bool close_on_deactivate = true; - // Indicates whether tray bubble view is shown by click on the tray view. - bool show_by_click = false; - // If not provided, the bg color will be derived from the NativeTheme. - base::Optional<SkColor> bg_color; - base::Optional<int> corner_radius; - bool has_shadow = true; - }; - - explicit TrayBubbleView(const InitParams& init_params); - ~TrayBubbleView() override; - - // Returns whether a tray bubble is active. - static bool IsATrayBubbleOpen(); - - // Sets up animations, and show the bubble. Must occur after CreateBubble() - // is called. - void InitializeAndShowBubble(); - - // Called whenever the bubble size or location may have changed. - void UpdateBubble(); - - // Sets the maximum bubble height and resizes the bubble. - void SetMaxHeight(int height); - - // Sets the bottom padding that child views will be laid out within. - void SetBottomPadding(int padding); - - // Sets the bubble width. - void SetWidth(int width); - - // Returns the border insets. Called by TrayEventFilter. - gfx::Insets GetBorderInsets() const; - - // Called when the delegate is destroyed. This must be called before the - // delegate is actually destroyed. TrayBubbleView will do clean up in - // ResetDelegate. - void ResetDelegate(); - - // Anchors the bubble to |anchor_view|. - void ChangeAnchorView(views::View* anchor_view); - - Delegate* delegate() { return delegate_; } - - void set_gesture_dragging(bool dragging) { is_gesture_dragging_ = dragging; } - bool is_gesture_dragging() const { return is_gesture_dragging_; } - - // Overridden from views::WidgetDelegate. - views::NonClientFrameView* CreateNonClientFrameView( - views::Widget* widget) override; - bool WidgetHasHitTestMask() const override; - void GetWidgetHitTestMask(gfx::Path* mask) const override; - base::string16 GetAccessibleWindowTitle() const override; - - // Overridden from views::BubbleDialogDelegateView. - void OnBeforeBubbleWidgetInit(Widget::InitParams* params, - Widget* bubble_widget) const override; - void OnWidgetClosing(Widget* widget) override; - void OnWidgetActivationChanged(Widget* widget, bool active) override; - - // Overridden from views::View. - gfx::Size CalculatePreferredSize() const override; - int GetHeightForWidth(int width) const override; - void OnMouseEntered(const ui::MouseEvent& event) override; - void OnMouseExited(const ui::MouseEvent& event) override; - void GetAccessibleNodeData(ui::AXNodeData* node_data) override; - void OnGestureEvent(ui::GestureEvent* event) override; - - // Overridden from MouseWatcherListener - void MouseMovedOutOfHost() override; - - protected: - // Overridden from views::BubbleDialogDelegateView. - int GetDialogButtons() const override; - ax::mojom::Role GetAccessibleWindowRole() const override; - void SizeToContents() override; - - // Overridden from views::View. - void ChildPreferredSizeChanged(View* child) override; - void ViewHierarchyChanged( - const ViewHierarchyChangedDetails& details) override; - - private: - // This reroutes receiving key events to the TrayBubbleView passed in the - // constructor. TrayBubbleView is not activated by default. But we want to - // activate it if user tries to interact it with keyboard. To capture those - // key events in early stage, RerouteEventHandler installs this handler to - // aura::Env. RerouteEventHandler also sends key events to ViewsDelegate to - // process accelerator as menu is currently open. - class RerouteEventHandler : public ui::EventHandler { - public: - explicit RerouteEventHandler(TrayBubbleView* tray_bubble_view); - ~RerouteEventHandler() override; - - // Overridden from ui::EventHandler - void OnKeyEvent(ui::KeyEvent* event) override; - - private: - // TrayBubbleView to which key events are going to be rerouted. Not owned. - TrayBubbleView* tray_bubble_view_; - - DISALLOW_COPY_AND_ASSIGN(RerouteEventHandler); - }; - - void CloseBubbleView(); - - // Focus the default item if no item is focused. - void FocusDefaultIfNeeded(); - - InitParams params_; - BoxLayout* layout_; - Delegate* delegate_; - int preferred_width_; - // |bubble_border_| and |owned_bubble_border_| point to the same thing, but - // the latter ensures we don't leak it before passing off ownership. - BubbleBorder* bubble_border_; - std::unique_ptr<views::BubbleBorder> owned_bubble_border_; - std::unique_ptr<ui::LayerOwner> bubble_content_mask_; - bool is_gesture_dragging_; - - // True once the mouse cursor was actively moved by the user over the bubble. - // Only then the OnMouseExitedView() event will get passed on to listeners. - bool mouse_actively_entered_; - - // Used to find any mouse movements. - std::unique_ptr<MouseWatcher> mouse_watcher_; - - // Used to activate tray bubble view if user tries to interact the tray with - // keyboard. - std::unique_ptr<EventHandler> reroute_event_handler_; - - DISALLOW_COPY_AND_ASSIGN(TrayBubbleView); -}; - -} // namespace views - -#endif // UI_VIEWS_BUBBLE_TRAY_BUBBLE_VIEW_H_ diff --git a/chromium/ui/views/cocoa/DEPS b/chromium/ui/views/cocoa/DEPS index fcaeee7d596..440d45f5357 100644 --- a/chromium/ui/views/cocoa/DEPS +++ b/chromium/ui/views/cocoa/DEPS @@ -1,5 +1,4 @@ include_rules = [ - "+components/viz/common", "+ui/accelerated_widget_mac", "+ui/views_bridge_mac", ] diff --git a/chromium/ui/views/cocoa/bridge_factory_host.cc b/chromium/ui/views/cocoa/bridge_factory_host.cc new file mode 100644 index 00000000000..4350aae5c1a --- /dev/null +++ b/chromium/ui/views/cocoa/bridge_factory_host.cc @@ -0,0 +1,35 @@ +// 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/views/cocoa/bridge_factory_host.h" + +#include "mojo/public/cpp/bindings/interface_request.h" + +namespace views { + +BridgeFactoryHost::BridgeFactoryHost( + uint64_t host_id, + views_bridge_mac::mojom::BridgeFactoryAssociatedRequest* request) + : host_id_(host_id) { + *request = mojo::MakeRequest(&bridge_factory_ptr_); +} + +BridgeFactoryHost::~BridgeFactoryHost() { + for (Observer& obs : observers_) + obs.OnBridgeFactoryHostDestroying(this); +} + +views_bridge_mac::mojom::BridgeFactory* BridgeFactoryHost::GetFactory() { + return bridge_factory_ptr_.get(); +} + +void BridgeFactoryHost::AddObserver(Observer* observer) { + observers_.AddObserver(observer); +} + +void BridgeFactoryHost::RemoveObserver(const Observer* observer) { + observers_.RemoveObserver(observer); +} + +} // namespace views diff --git a/chromium/ui/views/cocoa/bridge_factory_host.h b/chromium/ui/views/cocoa/bridge_factory_host.h new file mode 100644 index 00000000000..14a197dce5e --- /dev/null +++ b/chromium/ui/views/cocoa/bridge_factory_host.h @@ -0,0 +1,47 @@ +// 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_VIEWS_COCOA_BRIDGE_FACTORY_HOST_H_ +#define UI_VIEWS_COCOA_BRIDGE_FACTORY_HOST_H_ + +#include "base/observer_list.h" +#include "base/observer_list_types.h" +#include "ui/views/views_export.h" +#include "ui/views_bridge_mac/mojo/bridge_factory.mojom.h" + +namespace views { + +class VIEWS_EXPORT BridgeFactoryHost { + public: + class Observer : public base::CheckedObserver { + public: + virtual void OnBridgeFactoryHostDestroying(BridgeFactoryHost* host) = 0; + + protected: + ~Observer() override {} + }; + + BridgeFactoryHost( + uint64_t host_id, + views_bridge_mac::mojom::BridgeFactoryAssociatedRequest* request); + ~BridgeFactoryHost(); + + // Return an id for the host process. This can be used to look up other + // factories to create NSViews (e.g in content). + uint64_t GetHostId() const { return host_id_; } + + views_bridge_mac::mojom::BridgeFactory* GetFactory(); + + void AddObserver(Observer* observer); + void RemoveObserver(const Observer* observer); + + private: + const uint64_t host_id_; + views_bridge_mac::mojom::BridgeFactoryAssociatedPtr bridge_factory_ptr_; + base::ObserverList<Observer> observers_; +}; + +} // namespace views + +#endif // UI_VIEWS_COCOA_BRIDGE_FACTORY_HOST_H_ diff --git a/chromium/ui/views/cocoa/bridged_native_widget_host.h b/chromium/ui/views/cocoa/bridged_native_widget_host.h deleted file mode 100644 index 9fa2c68091c..00000000000 --- a/chromium/ui/views/cocoa/bridged_native_widget_host.h +++ /dev/null @@ -1,152 +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_VIEWS_COCOA_BRIDGED_NATIVE_WIDGET_HOST_H_ -#define UI_VIEWS_COCOA_BRIDGED_NATIVE_WIDGET_HOST_H_ - -#include "ui/base/ui_base_types.h" -#include "ui/events/event_utils.h" -#include "ui/gfx/decorated_text.h" -#include "ui/gfx/geometry/point.h" -#include "ui/gfx/geometry/size.h" -#include "ui/views/views_export.h" - -@class NSView; - -namespace views { - -// The interface through which the app shim (BridgedNativeWidgetImpl) -// communicates with the browser process (BridgedNativeWidgetHostImpl). -class VIEWS_EXPORT BridgedNativeWidgetHost { - public: - virtual ~BridgedNativeWidgetHost() = default; - - // Retrieve the NSView for accessibility for this widget. - // TODO(ccameron): This interface cannot be implemented over IPC. A scheme - // for implementing accessibility across processes needs to be designed and - // implemented. - virtual NSView* GetNativeViewAccessible() = 0; - - // Update the views::Widget, ui::Compositor and ui::Layer's visibility. - virtual void OnVisibilityChanged(bool visible) = 0; - - // Resize the underlying views::View to |new_size|. Note that this will not - // necessarily match the content bounds from OnWindowGeometryChanged. - virtual void SetViewSize(const gfx::Size& new_size) = 0; - - // Indicate if full keyboard accessibility is needed and updates focus if - // needed. - virtual void SetKeyboardAccessible(bool enabled) = 0; - - // Indicate if the NSView is the first responder. - virtual void SetIsFirstResponder(bool is_first_responder) = 0; - - // Indicate if mouse capture is active. - virtual void OnMouseCaptureActiveChanged(bool capture_is_active) = 0; - - // Handle events. Note that whether or not the event is actually handled is - // not returned. - virtual void OnScrollEvent(const ui::ScrollEvent& const_event) = 0; - virtual void OnMouseEvent(const ui::MouseEvent& const_event) = 0; - virtual void OnGestureEvent(const ui::GestureEvent& const_event) = 0; - - // Synchronously dispatch a key event and return in |event_handled| whether - // or not the event was handled. - virtual void DispatchKeyEvent(const ui::KeyEvent& const_event, - bool* event_handled) = 0; - - // Synchronously dispatch a key event to the current menu controller (if any) - // exists. Return in |event_swallowed| whether or not the event was swallowed - // (that is, if the menu's dispatch returned POST_DISPATCH_NONE). Return in - // in |event_handled| whether or not the event was handled (that is, if the - // event in the caller's frame should be marked as handled). - virtual void DispatchKeyEventToMenuController(const ui::KeyEvent& const_event, - bool* event_swallowed, - bool* event_handled) = 0; - - // Synchronously return in |has_menu_controller| whether or not a menu - // controller exists for this widget. - virtual void GetHasMenuController(bool* has_menu_controller) = 0; - - // Synchronously query if |location_in_content| is a draggable background. - virtual void GetIsDraggableBackgroundAt(const gfx::Point& location_in_content, - bool* is_draggable_background) = 0; - - // Synchronously query the tooltip text for |location_in_content|. - virtual void GetTooltipTextAt(const gfx::Point& location_in_content, - base::string16* new_tooltip_text) = 0; - - // Synchronously query the quicklook text at |location_in_content|. Return in - // |found_word| whether or not a word was found. - virtual void GetWordAt(const gfx::Point& location_in_content, - bool* found_word, - gfx::DecoratedText* decorated_word, - gfx::Point* baseline_point) = 0; - - // Synchronously query the value of IsModal for this widget and store it in - // |*widget_is_modal|. - virtual void GetWidgetIsModal(bool* widget_is_modal) = 0; - - // Synchronously return in |is_textual| whether or not the focused view - // contains text that can be selected and copied. - virtual void GetIsFocusedViewTextual(bool* is_textual) = 0; - - // Called whenever the NSWindow's size or position changes. - virtual void OnWindowGeometryChanged( - const gfx::Rect& window_bounds_in_screen_dips, - const gfx::Rect& content_bounds_in_screen_dips) = 0; - - // Called when the window begins transitioning to or from being fullscreen. - virtual void OnWindowFullscreenTransitionStart( - bool target_fullscreen_state) = 0; - - // Called when the window has completed its transition to or from being - // fullscreen. Note that if there are multiple consecutive transitions - // (because a new transition was initiated before the previous one completed) - // then this will only be called when all transitions have competed. - virtual void OnWindowFullscreenTransitionComplete(bool is_fullscreen) = 0; - - // Called when the window is miniaturized or deminiaturized. - virtual void OnWindowMiniaturizedChanged(bool miniaturized) = 0; - - // Called when the current display or the properties of the current display - // change. - virtual void OnWindowDisplayChanged(const display::Display& display) = 0; - - // Called before the NSWindow is closed and destroyed. - virtual void OnWindowWillClose() = 0; - - // Called after the NSWindow has been closed and destroyed. - virtual void OnWindowHasClosed() = 0; - - // Called when the NSWindow becomes key or resigns from being key. Additional - // state required for the transition include whether or not the content NSView - // is the first responder for the NSWindow in |is_content_first_responder| and - // whether or not the NSApp's full keyboard access is enabled in - // |full_keyboard_access_enabled|. - virtual void OnWindowKeyStatusChanged(bool is_key, - bool is_content_first_responder, - bool full_keyboard_access_enabled) = 0; - - // Accept or cancel the current dialog window (depending on the value of - // |button|), if a current dialog exists. - virtual void DoDialogButtonAction(ui::DialogButton button) = 0; - - // Synchronously determine if the specified button exists in the current - // dialog (if any), along with its label, whether or not it is enabled, and - // whether or not it is the default button.. - virtual void GetDialogButtonInfo(ui::DialogButton button, - bool* button_exists, - base::string16* title, - bool* is_button_enabled, - bool* is_button_default) = 0; - - // Synchronously return in |buttons_exist| whether or not any buttons exist - // for the current dialog. - virtual void GetDoDialogButtonsExist(bool* buttons_exist) = 0; -}; - -} // namespace views - -#endif // UI_VIEWS_COCOA_BRIDGED_NATIVE_WIDGET_HOST_H_ diff --git a/chromium/ui/views/cocoa/bridged_native_widget_host_impl.h b/chromium/ui/views/cocoa/bridged_native_widget_host_impl.h index 561f3553ca6..5451da8d177 100644 --- a/chromium/ui/views/cocoa/bridged_native_widget_host_impl.h +++ b/chromium/ui/views/cocoa/bridged_native_widget_host_impl.h @@ -6,23 +6,27 @@ #define UI_VIEWS_COCOA_BRIDGED_NATIVE_WIDGET_HOST_IMPL_H_ #include <memory> +#include <vector> +#include "base/mac/scoped_nsobject.h" #include "base/macros.h" +#include "mojo/public/cpp/bindings/associated_binding.h" #include "ui/accelerated_widget_mac/accelerated_widget_mac.h" #include "ui/accelerated_widget_mac/display_link_mac.h" #include "ui/base/ime/input_method_delegate.h" #include "ui/compositor/layer_owner.h" -#include "ui/views/cocoa/bridged_native_widget_host.h" +#include "ui/views/cocoa/bridge_factory_host.h" +#include "ui/views/cocoa/drag_drop_client_mac.h" #include "ui/views/focus/focus_manager.h" #include "ui/views/views_export.h" #include "ui/views/widget/widget.h" #include "ui/views/window/dialog_observer.h" +#include "ui/views_bridge_mac/bridged_native_widget_host_helper.h" +#include "ui/views_bridge_mac/mojo/bridged_native_widget.mojom.h" +#include "ui/views_bridge_mac/mojo/bridged_native_widget_host.mojom.h" -namespace views_bridge_mac { -namespace mojom { -class BridgedNativeWidget; -} // namespace mojom -} // namespace views_bridge_mac +@class NativeWidgetMacNSWindow; +@class NSView; namespace ui { class RecyclableCompositorMac; @@ -30,14 +34,16 @@ class RecyclableCompositorMac; namespace views { -class BridgedNativeWidget; +class BridgedNativeWidgetImpl; class NativeWidgetMac; // The portion of NativeWidgetMac that lives in the browser process. This -// communicates to the BridgedNativeWidget, which interacts with the Cocoa +// communicates to the BridgedNativeWidgetImpl, which interacts with the Cocoa // APIs, and which may live in an app shim process. class VIEWS_EXPORT BridgedNativeWidgetHostImpl - : public BridgedNativeWidgetHost, + : public views_bridge_mac::BridgedNativeWidgetHostHelper, + public BridgeFactoryHost::Observer, + public views_bridge_mac::mojom::BridgedNativeWidgetHost, public DialogObserver, public FocusChangeListener, public ui::internal::InputMethodDelegate, @@ -45,15 +51,64 @@ class VIEWS_EXPORT BridgedNativeWidgetHostImpl public ui::LayerOwner, public ui::AcceleratedWidgetMacNSView { public: - // Creates one side of the bridge. |parent| must not be NULL. - explicit BridgedNativeWidgetHostImpl(NativeWidgetMac* parent); + // Retrieves the bridge host associated with the given NSWindow. Returns null + // if the supplied handle has no associated Widget. + static BridgedNativeWidgetHostImpl* GetFromNativeWindow( + gfx::NativeWindow window); + + // Unique integer id handles are used to bridge between the + // BridgedNativeWidgetHostImpl in one process and the BridgedNativeWidgetHost + // potentially in another. + static BridgedNativeWidgetHostImpl* GetFromId( + uint64_t bridged_native_widget_id); + uint64_t bridged_native_widget_id() const { return widget_id_; } + + // Creates one side of the bridge. |owner| must not be NULL. + explicit BridgedNativeWidgetHostImpl(NativeWidgetMac* owner); ~BridgedNativeWidgetHostImpl() override; - // Provide direct access to the BridgedNativeWidget that this is hosting. + // The NativeWidgetMac that owns |this|. + views::NativeWidgetMac* native_widget_mac() const { + return native_widget_mac_; + } + BridgedNativeWidgetHostImpl* parent() const { return parent_; } + std::vector<BridgedNativeWidgetHostImpl*> children() const { + return children_; + } + + // The bridge factory that was used to create the true NSWindow for this + // widget. This is nullptr for in-process windows. + BridgeFactoryHost* bridge_factory_host() const { + return bridge_factory_host_; + } + + // A NSWindow that is guaranteed to exist in this process. If the bridge + // object for this host is in this process, then this points to the bridge's + // NSWindow. Otherwise, it mirrors the id and bounds of the child window. + NativeWidgetMacNSWindow* GetLocalNSWindow() const; + + // The mojo interface through which to communicate with the underlying + // NSWindow and NSView. + views_bridge_mac::mojom::BridgedNativeWidget* bridge() const; + + // Direct access to the BridgedNativeWidgetImpl that this is hosting. // TODO(ccameron): Remove all accesses to this member, and replace them // with methods that may be sent across processes. - BridgedNativeWidget* bridge_impl() const { return bridge_impl_.get(); } - views_bridge_mac::mojom::BridgedNativeWidget* bridge() const; + BridgedNativeWidgetImpl* bridge_impl() const { return bridge_impl_.get(); } + + TooltipManager* tooltip_manager() { return tooltip_manager_.get(); } + + DragDropClientMac* drag_drop_client() const { + return drag_drop_client_.get(); + } + + // Create and set the bridge object to be in this process. + void CreateLocalBridge(base::scoped_nsobject<NativeWidgetMacNSWindow> window); + + // Create and set the bridge object to be potentially in another process. + void CreateRemoteBridge( + BridgeFactoryHost* bridge_factory_host, + views_bridge_mac::mojom::CreateWindowParamsPtr window_create_params); void InitWindow(const Widget::InitParams& params); @@ -75,6 +130,9 @@ class VIEWS_EXPORT BridgedNativeWidgetHostImpl // Set the root view (set during initialization and un-set during teardown). void SetRootView(views::View* root_view); + // Return the id through which the NSView for |root_view_| may be looked up. + uint64_t GetRootViewNSViewId() const { return root_view_id_; } + // Initialize the ui::Compositor and ui::Layer. void CreateCompositor(const Widget::InitParams& params); @@ -93,14 +151,12 @@ class VIEWS_EXPORT BridgedNativeWidgetHostImpl // Geometry of the window, in DIPs. const gfx::Rect& GetWindowBoundsInScreen() const { - DCHECK(has_received_window_geometry_); return window_bounds_in_screen_; } // Geometry of the content area of the window, in DIPs. Note that this is not // necessarily the same as the views::View's size. const gfx::Rect& GetContentBoundsInScreen() const { - DCHECK(has_received_window_geometry_); return content_bounds_in_screen_; } @@ -111,42 +167,70 @@ class VIEWS_EXPORT BridgedNativeWidgetHostImpl // fullscreen or transitioning between fullscreen states. gfx::Rect GetRestoredBounds() const; + // Set |parent_| and update the old and new parents' |children_|. It is valid + // to set |new_parent| to nullptr. Propagate this to the BridgedNativeWidget. + void SetParent(BridgedNativeWidgetHostImpl* new_parent); + + // Properties set and queried by views. Not actually native. + void SetNativeWindowProperty(const char* name, void* value); + void* GetNativeWindowProperty(const char* name) const; + + // Updates |associated_views_| on NativeViewHost::Attach()/Detach(). + void SetAssociationForView(const views::View* view, NSView* native_view); + void ClearAssociationForView(const views::View* view); + + // Sorts child NSViews according to NativeViewHosts order in views hierarchy. + void ReorderChildViews(); + bool IsVisible() const { return is_visible_; } bool IsMiniaturized() const { return is_miniaturized_; } bool IsWindowKey() const { return is_window_key_; } bool IsMouseCaptureActive() const { return is_mouse_capture_active_; } + // Used by NativeWidgetPrivate::GetGlobalCapture. + static NSView* GetGlobalCaptureView(); + private: - gfx::Vector2d GetBoundsOffsetForParent() const; void UpdateCompositorProperties(); void DestroyCompositor(); + void RankNSViewsRecursive(View* view, std::map<NSView*, int>* rank) const; - // views::BridgedNativeWidgetHost: + // BridgedNativeWidgetHostHelper: NSView* GetNativeViewAccessible() override; + void DispatchKeyEvent(ui::KeyEvent* event) override; + bool DispatchKeyEventToMenuController(ui::KeyEvent* event) override; + void GetWordAt(const gfx::Point& location_in_content, + bool* found_word, + gfx::DecoratedText* decorated_word, + gfx::Point* baseline_point) override; + double SheetPositionY() override; + views_bridge_mac::DragDropClient* GetDragDropClient() override; + + // BridgeFactoryHost::Observer: + void OnBridgeFactoryHostDestroying(BridgeFactoryHost* host) override; + + // views_bridge_mac::mojom::BridgedNativeWidgetHost: void OnVisibilityChanged(bool visible) override; + void OnWindowNativeThemeChanged() override; void SetViewSize(const gfx::Size& new_size) override; void SetKeyboardAccessible(bool enabled) override; void SetIsFirstResponder(bool is_first_responder) override; void OnMouseCaptureActiveChanged(bool capture_is_active) override; - void OnScrollEvent(const ui::ScrollEvent& const_event) override; - void OnMouseEvent(const ui::MouseEvent& const_event) override; - void OnGestureEvent(const ui::GestureEvent& const_event) override; - void DispatchKeyEvent(const ui::KeyEvent& const_event, - bool* event_handled) override; - void DispatchKeyEventToMenuController(const ui::KeyEvent& const_event, - bool* event_swallowed, - bool* event_handled) override; - void GetHasMenuController(bool* has_menu_controller) override; - void GetIsDraggableBackgroundAt(const gfx::Point& location_in_content, + void OnScrollEvent(std::unique_ptr<ui::Event> event) override; + void OnMouseEvent(std::unique_ptr<ui::Event> event) override; + void OnGestureEvent(std::unique_ptr<ui::Event> event) override; + bool DispatchKeyEventRemote(std::unique_ptr<ui::Event> event, + bool* event_handled) override; + bool DispatchKeyEventToMenuControllerRemote(std::unique_ptr<ui::Event> event, + bool* event_swallowed, + bool* event_handled) override; + bool GetHasMenuController(bool* has_menu_controller) override; + bool GetIsDraggableBackgroundAt(const gfx::Point& location_in_content, bool* is_draggable_background) override; - void GetTooltipTextAt(const gfx::Point& location_in_content, + bool GetTooltipTextAt(const gfx::Point& location_in_content, base::string16* new_tooltip_text) override; - void GetWordAt(const gfx::Point& location_in_content, - bool* found_word, - gfx::DecoratedText* decorated_word, - gfx::Point* baseline_point) override; - void GetWidgetIsModal(bool* widget_is_modal) override; - void GetIsFocusedViewTextual(bool* is_textual) override; + bool GetWidgetIsModal(bool* widget_is_modal) override; + bool GetIsFocusedViewTextual(bool* is_textual) override; void OnWindowGeometryChanged( const gfx::Rect& window_bounds_in_screen_dips, const gfx::Rect& content_bounds_in_screen_dips) override; @@ -161,12 +245,42 @@ class VIEWS_EXPORT BridgedNativeWidgetHostImpl bool is_content_first_responder, bool full_keyboard_access_enabled) override; void DoDialogButtonAction(ui::DialogButton button) override; - void GetDialogButtonInfo(ui::DialogButton type, + bool GetDialogButtonInfo(ui::DialogButton type, bool* button_exists, base::string16* button_label, bool* is_button_enabled, bool* is_button_default) override; - void GetDoDialogButtonsExist(bool* buttons_exist) override; + bool GetDoDialogButtonsExist(bool* buttons_exist) override; + bool GetShouldShowWindowTitle(bool* should_show_window_title) override; + bool GetCanWindowBecomeKey(bool* can_window_become_key) override; + bool GetAlwaysRenderWindowAsKey(bool* always_render_as_key) override; + bool GetCanWindowClose(bool* can_window_close) override; + + // views_bridge_mac::mojom::BridgedNativeWidgetHost, synchronous callbacks: + void DispatchKeyEventRemote(std::unique_ptr<ui::Event> event, + DispatchKeyEventRemoteCallback callback) override; + void DispatchKeyEventToMenuControllerRemote( + std::unique_ptr<ui::Event> event, + DispatchKeyEventToMenuControllerRemoteCallback callback) override; + void GetHasMenuController(GetHasMenuControllerCallback callback) override; + void GetIsDraggableBackgroundAt( + const gfx::Point& location_in_content, + GetIsDraggableBackgroundAtCallback callback) override; + void GetTooltipTextAt(const gfx::Point& location_in_content, + GetTooltipTextAtCallback callback) override; + void GetWidgetIsModal(GetWidgetIsModalCallback callback) override; + void GetIsFocusedViewTextual( + GetIsFocusedViewTextualCallback callback) override; + void GetDialogButtonInfo(ui::DialogButton button, + GetDialogButtonInfoCallback callback) override; + void GetDoDialogButtonsExist( + GetDoDialogButtonsExistCallback callback) override; + void GetShouldShowWindowTitle( + GetShouldShowWindowTitleCallback callback) override; + void GetCanWindowBecomeKey(GetCanWindowBecomeKeyCallback callback) override; + void GetAlwaysRenderWindowAsKey( + GetAlwaysRenderWindowAsKeyCallback callback) override; + void GetCanWindowClose(GetCanWindowCloseCallback callback) override; // DialogObserver: void OnDialogModelChanged() override; @@ -176,7 +290,9 @@ class VIEWS_EXPORT BridgedNativeWidgetHostImpl void OnDidChangeFocus(View* focused_before, View* focused_now) override; // ui::internal::InputMethodDelegate: - ui::EventDispatchDetails DispatchKeyEventPostIME(ui::KeyEvent* key) override; + ui::EventDispatchDetails DispatchKeyEventPostIME( + ui::KeyEvent* key, + base::OnceCallback<void(bool)> ack_callback) override; // ui::LayerDelegate: void OnPaintLayer(const ui::PaintContext& context) override; @@ -186,17 +302,38 @@ class VIEWS_EXPORT BridgedNativeWidgetHostImpl // ui::AcceleratedWidgetMacNSView: void AcceleratedWidgetCALayerParamsUpdated() override; + // The id that this bridge may be looked up from. + const uint64_t widget_id_; views::NativeWidgetMac* const native_widget_mac_; // Weak. Owns |this_|. + // Parent and child widgets. + BridgedNativeWidgetHostImpl* parent_ = nullptr; + std::vector<BridgedNativeWidgetHostImpl*> children_; + + // The factory that was used to create |bridge_ptr_|. This must be the same + // as |parent_->bridge_factory_host_|. + BridgeFactoryHost* bridge_factory_host_ = nullptr; + Widget::InitParams::Type widget_type_ = Widget::InitParams::TYPE_WINDOW; + // The id that may be used to look up the NSView for |root_view_|. + const uint64_t root_view_id_; views::View* root_view_ = nullptr; // Weak. Owned by |native_widget_mac_|. + std::unique_ptr<DragDropClientMac> drag_drop_client_; - // TODO(ccameron): Rather than instantiate a BridgedNativeWidget here, - // we will instantiate a mojo BridgedNativeWidget interface to a Cocoa + // The mojo pointer to a BridgedNativeWidget, which may exist in another + // process. + views_bridge_mac::mojom::BridgedNativeWidgetAssociatedPtr bridge_ptr_; + + // TODO(ccameron): Rather than instantiate a BridgedNativeWidgetImpl here, + // we will instantiate a mojo BridgedNativeWidgetImpl interface to a Cocoa // instance that may be in another process. - std::unique_ptr<BridgedNativeWidget> bridge_impl_; + std::unique_ptr<BridgedNativeWidgetImpl> bridge_impl_; + + // Window that is guaranteed to exist in this process (see GetLocalNSWindow). + base::scoped_nsobject<NativeWidgetMacNSWindow> local_window_; + std::unique_ptr<TooltipManager> tooltip_manager_; std::unique_ptr<ui::InputMethod> input_method_; FocusManager* focus_manager_ = nullptr; // Weak. Owned by our Widget. @@ -209,7 +346,6 @@ class VIEWS_EXPORT BridgedNativeWidgetHostImpl scoped_refptr<ui::DisplayLinkMac> display_link_; // The geometry of the window and its contents view, in screen coordinates. - bool has_received_window_geometry_ = false; gfx::Rect window_bounds_in_screen_; gfx::Rect content_bounds_in_screen_; bool is_visible_ = false; @@ -222,6 +358,14 @@ class VIEWS_EXPORT BridgedNativeWidgetHostImpl std::unique_ptr<ui::RecyclableCompositorMac> compositor_; + // Properties used by Set/GetNativeWindowProperty. + std::map<std::string, void*> native_window_properties_; + + // Contains NativeViewHost->gfx::NativeView associations. + std::map<const views::View*, NSView*> associated_views_; + + mojo::AssociatedBinding<views_bridge_mac::mojom::BridgedNativeWidgetHost> + host_mojo_binding_; DISALLOW_COPY_AND_ASSIGN(BridgedNativeWidgetHostImpl); }; diff --git a/chromium/ui/views/cocoa/bridged_native_widget_host_impl.mm b/chromium/ui/views/cocoa/bridged_native_widget_host_impl.mm index 53e6a582b6c..63f9be6db98 100644 --- a/chromium/ui/views/cocoa/bridged_native_widget_host_impl.mm +++ b/chromium/ui/views/cocoa/bridged_native_widget_host_impl.mm @@ -4,6 +4,8 @@ #include "ui/views/cocoa/bridged_native_widget_host_impl.h" +#include "base/mac/foundation_util.h" +#include "ui/accelerated_widget_mac/window_resize_helper_mac.h" #include "ui/base/hit_test.h" #include "ui/base/ime/input_method.h" #include "ui/base/ime/input_method_factory.h" @@ -11,7 +13,9 @@ #include "ui/compositor/recyclable_compositor_mac.h" #include "ui/display/screen.h" #include "ui/gfx/geometry/dip_util.h" -#include "ui/views/cocoa/bridged_native_widget.h" +#include "ui/gfx/mac/coordinate_conversion.h" +#include "ui/native_theme/native_theme_mac.h" +#include "ui/views/cocoa/tooltip_manager_mac.h" #include "ui/views/controls/menu/menu_config.h" #include "ui/views/controls/menu/menu_controller.h" #include "ui/views/views_delegate.h" @@ -21,6 +25,9 @@ #include "ui/views/window/dialog_client_view.h" #include "ui/views/window/dialog_delegate.h" #include "ui/views/word_lookup_client.h" +#include "ui/views_bridge_mac/bridged_native_widget_impl.h" +#include "ui/views_bridge_mac/cocoa_mouse_capture.h" +#include "ui/views_bridge_mac/native_widget_mac_nswindow.h" using views_bridge_mac::mojom::BridgedNativeWidgetInitParams; using views_bridge_mac::mojom::WindowVisibilityState; @@ -40,14 +47,61 @@ bool PositionWindowInScreenCoordinates(Widget* widget, return widget && widget->is_top_level(); } +std::map<uint64_t, BridgedNativeWidgetHostImpl*>& GetIdToWidgetHostImplMap() { + static base::NoDestructor<std::map<uint64_t, BridgedNativeWidgetHostImpl*>> + id_map; + return *id_map; +} + +uint64_t g_last_bridged_native_widget_id = 0; + } // namespace -BridgedNativeWidgetHostImpl::BridgedNativeWidgetHostImpl( - NativeWidgetMac* parent) - : native_widget_mac_(parent), - bridge_impl_(new BridgedNativeWidget(this, parent)) {} +// static +BridgedNativeWidgetHostImpl* BridgedNativeWidgetHostImpl::GetFromNativeWindow( + gfx::NativeWindow window) { + if (NativeWidgetMacNSWindow* widget_window = + base::mac::ObjCCast<NativeWidgetMacNSWindow>(window)) { + return GetFromId([widget_window bridgedNativeWidgetId]); + } + return nullptr; // Not created by NativeWidgetMac. +} + +// static +BridgedNativeWidgetHostImpl* BridgedNativeWidgetHostImpl::GetFromId( + uint64_t bridged_native_widget_id) { + auto found = GetIdToWidgetHostImplMap().find(bridged_native_widget_id); + if (found == GetIdToWidgetHostImplMap().end()) + return nullptr; + return found->second; +} + +BridgedNativeWidgetHostImpl::BridgedNativeWidgetHostImpl(NativeWidgetMac* owner) + : widget_id_(++g_last_bridged_native_widget_id), + native_widget_mac_(owner), + root_view_id_(ui::NSViewIds::GetNewId()), + host_mojo_binding_(this) { + DCHECK(GetIdToWidgetHostImplMap().find(widget_id_) == + GetIdToWidgetHostImplMap().end()); + GetIdToWidgetHostImplMap().insert(std::make_pair(widget_id_, this)); + DCHECK(owner); +} BridgedNativeWidgetHostImpl::~BridgedNativeWidgetHostImpl() { + DCHECK(children_.empty()); + if (bridge_factory_host_) { + bridge_ptr_.reset(); + host_mojo_binding_.Unbind(); + bridge_factory_host_->RemoveObserver(this); + bridge_factory_host_ = nullptr; + } + + // Ensure that |this| cannot be reached by its id while it is being destroyed. + auto found = GetIdToWidgetHostImplMap().find(widget_id_); + DCHECK(found != GetIdToWidgetHostImplMap().end()); + DCHECK_EQ(found->second, this); + GetIdToWidgetHostImplMap().erase(found); + // Destroy the bridge first to prevent any calls back into this during // destruction. // TODO(ccameron): When all communication from |bridge_| to this goes through @@ -57,26 +111,67 @@ BridgedNativeWidgetHostImpl::~BridgedNativeWidgetHostImpl() { DestroyCompositor(); } +NativeWidgetMacNSWindow* BridgedNativeWidgetHostImpl::GetLocalNSWindow() const { + return local_window_.get(); +} + views_bridge_mac::mojom::BridgedNativeWidget* BridgedNativeWidgetHostImpl::bridge() const { - return bridge_impl_.get(); + if (bridge_ptr_) + return bridge_ptr_.get(); + if (bridge_impl_) + return bridge_impl_.get(); + return nullptr; +} + +void BridgedNativeWidgetHostImpl::CreateLocalBridge( + base::scoped_nsobject<NativeWidgetMacNSWindow> window) { + local_window_ = window; + bridge_impl_ = + std::make_unique<BridgedNativeWidgetImpl>(widget_id_, this, this); + bridge_impl_->SetWindow(window); +} + +void BridgedNativeWidgetHostImpl::CreateRemoteBridge( + BridgeFactoryHost* bridge_factory_host, + views_bridge_mac::mojom::CreateWindowParamsPtr window_create_params) { + bridge_factory_host_ = bridge_factory_host; + bridge_factory_host_->AddObserver(this); + + // Create the local window with the same parameters as will be used in the + // other process. + local_window_ = + BridgedNativeWidgetImpl::CreateNSWindow(window_create_params.get()); + [local_window_ setBridgedNativeWidgetId:widget_id_]; + + // Initialize |bridge_ptr_| to point to a bridge created by |factory|. + views_bridge_mac::mojom::BridgedNativeWidgetHostAssociatedPtr host_ptr; + host_mojo_binding_.Bind(mojo::MakeRequest(&host_ptr), + ui::WindowResizeHelperMac::Get()->task_runner()); + bridge_factory_host_->GetFactory()->CreateBridgedNativeWidget( + widget_id_, mojo::MakeRequest(&bridge_ptr_), host_ptr.PassInterface()); + + // Create the window in its process, and attach it to its parent window. + bridge()->CreateWindow(std::move(window_create_params)); } void BridgedNativeWidgetHostImpl::InitWindow(const Widget::InitParams& params) { + Widget* widget = native_widget_mac_->GetWidget(); // Tooltip Widgets shouldn't have their own tooltip manager, but tooltips are // native on Mac, so nothing should ever want one in Widget form. DCHECK_NE(params.type, Widget::InitParams::TYPE_TOOLTIP); widget_type_ = params.type; - - bridge_impl_->SetParent(params.parent); + tooltip_manager_.reset(new TooltipManagerMac(bridge())); // Initialize the window. { auto bridge_params = BridgedNativeWidgetInitParams::New(); - bridge_params->modal_type = - native_widget_mac_->GetWidget()->widget_delegate()->GetModalType(); + bridge_params->modal_type = widget->widget_delegate()->GetModalType(); bridge_params->is_translucent = params.opacity == Widget::InitParams::TRANSLUCENT_WINDOW; + bridge_params->widget_is_top_level = widget->is_top_level(); + bridge_params->position_window_in_screen_coords = + PositionWindowInScreenCoordinates(widget, widget_type_); // OSX likes to put shadows on most things. However, frameless windows (with // styleMask = NSBorderlessWindowMask) default to no shadow. So change that. @@ -111,9 +206,11 @@ void BridgedNativeWidgetHostImpl::InitWindow(const Widget::InitParams& params) { // set at all, the creator of the Widget is expected to call SetBounds() // before calling Widget::Show() to avoid a kWindowSizeDeterminedLater-sized // (i.e. 1x1) window appearing. - bridge()->SetInitialBounds(params.bounds, - native_widget_mac_->GetWidget()->GetMinimumSize(), - GetBoundsOffsetForParent()); + bridge()->SetInitialBounds(params.bounds, widget->GetMinimumSize()); + + // TODO(ccameron): Correctly set these based |local_window_|. + window_bounds_in_screen_ = params.bounds; + content_bounds_in_screen_ = params.bounds; // Widgets for UI controls (usually layered above web contents) start visible. if (widget_type_ == Widget::InitParams::TYPE_CONTROL) @@ -121,21 +218,10 @@ void BridgedNativeWidgetHostImpl::InitWindow(const Widget::InitParams& params) { } void BridgedNativeWidgetHostImpl::SetBounds(const gfx::Rect& bounds) { - gfx::Rect adjusted_bounds = bounds; - adjusted_bounds.Offset(GetBoundsOffsetForParent()); - bridge()->SetBounds(adjusted_bounds, + bridge()->SetBounds(bounds, native_widget_mac_->GetWidget()->GetMinimumSize()); } -gfx::Vector2d BridgedNativeWidgetHostImpl::GetBoundsOffsetForParent() const { - gfx::Vector2d offset; - Widget* widget = native_widget_mac_->GetWidget(); - BridgedNativeWidgetOwner* parent = bridge_impl_->parent(); - if (parent && !PositionWindowInScreenCoordinates(widget, widget_type_)) - offset = parent->GetChildWindowOffset(); - return offset; -} - void BridgedNativeWidgetHostImpl::SetFullscreen(bool fullscreen) { // Note that when the NSWindow begins a fullscreen transition, the value of // |target_fullscreen_state_| updates via OnWindowFullscreenTransitionStart. @@ -148,6 +234,15 @@ void BridgedNativeWidgetHostImpl::SetFullscreen(bool fullscreen) { void BridgedNativeWidgetHostImpl::SetRootView(views::View* root_view) { root_view_ = root_view; + if (root_view_) { + // TODO(ccameron): Drag-drop functionality does not yet run over mojo. + if (bridge_impl_) { + drag_drop_client_.reset( + new DragDropClientMac(bridge_impl_.get(), root_view_)); + } + } else { + drag_drop_client_.reset(); + } } void BridgedNativeWidgetHostImpl::CreateCompositor( @@ -202,7 +297,7 @@ void BridgedNativeWidgetHostImpl::DestroyCompositor() { if (layer()) { // LayerOwner supports a change in ownership, e.g., to animate a closing // window, but that won't work as expected for the root layer in - // BridgedNativeWidget. + // BridgedNativeWidgetImpl. DCHECK_EQ(this, layer()->owner()); layer()->CompleteAllAnimations(); layer()->SuppressPaint(); @@ -267,13 +362,129 @@ gfx::Rect BridgedNativeWidgetHostImpl::GetRestoredBounds() const { return window_bounds_in_screen_; } +void BridgedNativeWidgetHostImpl::SetNativeWindowProperty(const char* name, + void* value) { + if (value) + native_window_properties_[name] = value; + else + native_window_properties_.erase(name); +} + +void* BridgedNativeWidgetHostImpl::GetNativeWindowProperty( + const char* name) const { + auto found = native_window_properties_.find(name); + if (found == native_window_properties_.end()) + return nullptr; + return found->second; +} + +void BridgedNativeWidgetHostImpl::SetParent( + BridgedNativeWidgetHostImpl* new_parent) { + if (new_parent == parent_) + return; + + if (parent_) { + auto found = + std::find(parent_->children_.begin(), parent_->children_.end(), this); + DCHECK(found != parent_->children_.end()); + parent_->children_.erase(found); + } + + parent_ = new_parent; + if (parent_) { + // We can only re-parent to another Widget if that Widget is hosted in the + // same process that we were already hosted by. + CHECK_EQ(bridge_factory_host_, parent_->bridge_factory_host()); + parent_->children_.push_back(this); + bridge()->SetParent(parent_->bridged_native_widget_id()); + } else { + bridge()->SetParent(0); + } +} + +void BridgedNativeWidgetHostImpl::SetAssociationForView(const View* view, + NSView* native_view) { + DCHECK_EQ(0u, associated_views_.count(view)); + associated_views_[view] = native_view; + native_widget_mac_->GetWidget()->ReorderNativeViews(); +} + +void BridgedNativeWidgetHostImpl::ClearAssociationForView(const View* view) { + auto it = associated_views_.find(view); + DCHECK(it != associated_views_.end()); + associated_views_.erase(it); +} + +void BridgedNativeWidgetHostImpl::ReorderChildViews() { + std::map<NSView*, int> rank; + Widget* widget = native_widget_mac_->GetWidget(); + RankNSViewsRecursive(widget->GetRootView(), &rank); + if (bridge_impl_) + bridge_impl_->SortSubviews(std::move(rank)); +} + +void BridgedNativeWidgetHostImpl::RankNSViewsRecursive( + View* view, + std::map<NSView*, int>* rank) const { + auto it = associated_views_.find(view); + if (it != associated_views_.end()) + rank->emplace(it->second, rank->size()); + for (int i = 0; i < view->child_count(); ++i) + RankNSViewsRecursive(view->child_at(i), rank); +} + +// static +NSView* BridgedNativeWidgetHostImpl::GetGlobalCaptureView() { + // TODO(ccameron): This will not work across process boundaries. + return [CocoaMouseCapture::GetGlobalCaptureWindow() contentView]; +} + //////////////////////////////////////////////////////////////////////////////// -// BridgedNativeWidgetHostImpl, views::BridgedNativeWidgetHost: +// BridgedNativeWidgetHostImpl, views_bridge_mac::BridgedNativeWidgetHostHelper: NSView* BridgedNativeWidgetHostImpl::GetNativeViewAccessible() { return root_view_ ? root_view_->GetNativeViewAccessible() : nil; } +void BridgedNativeWidgetHostImpl::DispatchKeyEvent(ui::KeyEvent* event) { + ignore_result( + root_view_->GetWidget()->GetInputMethod()->DispatchKeyEvent(event)); +} + +bool BridgedNativeWidgetHostImpl::DispatchKeyEventToMenuController( + ui::KeyEvent* event) { + MenuController* menu_controller = MenuController::GetActiveInstance(); + if (menu_controller && root_view_ && + menu_controller->owner() == root_view_->GetWidget()) { + return menu_controller->OnWillDispatchKeyEvent(event) == + ui::POST_DISPATCH_NONE; + } + return false; +} + +double BridgedNativeWidgetHostImpl::SheetPositionY() { + return native_widget_mac_->SheetPositionY(); +} + +views_bridge_mac::DragDropClient* +BridgedNativeWidgetHostImpl::GetDragDropClient() { + return drag_drop_client_.get(); +} + +//////////////////////////////////////////////////////////////////////////////// +// BridgedNativeWidgetHostImpl, BridgeFactoryHost::Observer: +void BridgedNativeWidgetHostImpl::OnBridgeFactoryHostDestroying( + BridgeFactoryHost* host) { + DCHECK_EQ(host, bridge_factory_host_); + bridge_factory_host_->RemoveObserver(this); + bridge_factory_host_ = nullptr; + // TODO(ccameron): This should be treated as the window closing. +} + +//////////////////////////////////////////////////////////////////////////////// +// BridgedNativeWidgetHostImpl, +// views_bridge_mac::mojom::BridgedNativeWidgetHost: + void BridgedNativeWidgetHostImpl::OnVisibilityChanged(bool window_visible) { is_visible_ = window_visible; if (compositor_) { @@ -289,54 +500,48 @@ void BridgedNativeWidgetHostImpl::OnVisibilityChanged(bool window_visible) { window_visible); } +void BridgedNativeWidgetHostImpl::OnWindowNativeThemeChanged() { + ui::NativeTheme::GetInstanceForNativeUi()->NotifyObservers(); +} + void BridgedNativeWidgetHostImpl::OnScrollEvent( - const ui::ScrollEvent& const_event) { - ui::ScrollEvent event = const_event; - root_view_->GetWidget()->OnScrollEvent(&event); + std::unique_ptr<ui::Event> event) { + root_view_->GetWidget()->OnScrollEvent(event->AsScrollEvent()); } void BridgedNativeWidgetHostImpl::OnMouseEvent( - const ui::MouseEvent& const_event) { - ui::MouseEvent event = const_event; - root_view_->GetWidget()->OnMouseEvent(&event); + std::unique_ptr<ui::Event> event) { + root_view_->GetWidget()->OnMouseEvent(event->AsMouseEvent()); } void BridgedNativeWidgetHostImpl::OnGestureEvent( - const ui::GestureEvent& const_event) { - ui::GestureEvent event = const_event; - root_view_->GetWidget()->OnGestureEvent(&event); + std::unique_ptr<ui::Event> event) { + root_view_->GetWidget()->OnGestureEvent(event->AsGestureEvent()); } -void BridgedNativeWidgetHostImpl::DispatchKeyEvent( - const ui::KeyEvent& const_event, +bool BridgedNativeWidgetHostImpl::DispatchKeyEventRemote( + std::unique_ptr<ui::Event> event, bool* event_handled) { - ui::KeyEvent event = const_event; - ignore_result( - root_view_->GetWidget()->GetInputMethod()->DispatchKeyEvent(&event)); - *event_handled = event.handled(); + DispatchKeyEvent(event->AsKeyEvent()); + *event_handled = event->handled(); + return true; } -void BridgedNativeWidgetHostImpl::DispatchKeyEventToMenuController( - const ui::KeyEvent& const_event, +bool BridgedNativeWidgetHostImpl::DispatchKeyEventToMenuControllerRemote( + std::unique_ptr<ui::Event> event, bool* event_swallowed, bool* event_handled) { - ui::KeyEvent event = const_event; - MenuController* menu_controller = MenuController::GetActiveInstance(); - if (menu_controller && root_view_ && - menu_controller->owner() == root_view_->GetWidget()) { - *event_swallowed = menu_controller->OnWillDispatchKeyEvent(&event) == - ui::POST_DISPATCH_NONE; - } else { - *event_swallowed = false; - } - *event_handled = event.handled(); + *event_swallowed = DispatchKeyEventToMenuController(event->AsKeyEvent()); + *event_handled = event->handled(); + return true; } -void BridgedNativeWidgetHostImpl::GetHasMenuController( +bool BridgedNativeWidgetHostImpl::GetHasMenuController( bool* has_menu_controller) { MenuController* menu_controller = MenuController::GetActiveInstance(); *has_menu_controller = menu_controller && root_view_ && menu_controller->owner() == root_view_->GetWidget(); + return true; } void BridgedNativeWidgetHostImpl::SetViewSize(const gfx::Size& new_size) { @@ -364,15 +569,16 @@ void BridgedNativeWidgetHostImpl::OnMouseCaptureActiveChanged(bool is_active) { native_widget_mac_->GetWidget()->OnMouseCaptureLost(); } -void BridgedNativeWidgetHostImpl::GetIsDraggableBackgroundAt( +bool BridgedNativeWidgetHostImpl::GetIsDraggableBackgroundAt( const gfx::Point& location_in_content, bool* is_draggable_background) { int component = root_view_->GetWidget()->GetNonClientComponent(location_in_content); *is_draggable_background = component == HTCAPTION; + return true; } -void BridgedNativeWidgetHostImpl::GetTooltipTextAt( +bool BridgedNativeWidgetHostImpl::GetTooltipTextAt( const gfx::Point& location_in_content, base::string16* new_tooltip_text) { views::View* view = @@ -384,6 +590,7 @@ void BridgedNativeWidgetHostImpl::GetTooltipTextAt( if (!view->GetTooltipText(view_point, new_tooltip_text)) DCHECK(new_tooltip_text->empty()); } + return true; } void BridgedNativeWidgetHostImpl::GetWordAt( @@ -414,22 +621,32 @@ void BridgedNativeWidgetHostImpl::GetWordAt( *found_word = true; } -void BridgedNativeWidgetHostImpl::GetWidgetIsModal(bool* widget_is_modal) { +bool BridgedNativeWidgetHostImpl::GetWidgetIsModal(bool* widget_is_modal) { *widget_is_modal = native_widget_mac_->GetWidget()->IsModal(); + return true; } -void BridgedNativeWidgetHostImpl::GetIsFocusedViewTextual(bool* is_textual) { +bool BridgedNativeWidgetHostImpl::GetIsFocusedViewTextual(bool* is_textual) { views::FocusManager* focus_manager = root_view_ ? root_view_->GetWidget()->GetFocusManager() : nullptr; *is_textual = focus_manager && focus_manager->GetFocusedView() && focus_manager->GetFocusedView()->GetClassName() == views::Label::kViewClassName; + return true; } void BridgedNativeWidgetHostImpl::OnWindowGeometryChanged( const gfx::Rect& new_window_bounds_in_screen, const gfx::Rect& new_content_bounds_in_screen) { - has_received_window_geometry_ = true; + // If we are accessing the BridgedNativeWidget through mojo, then + // |local_window_| is not the true window that was just resized. Update + // the frame of |local_window_| to keep it in sync for any native calls + // that may use it (e.g, for context menu positioning). + if (bridge_ptr_) { + [local_window_ setFrame:gfx::ScreenRectToNSRect(new_window_bounds_in_screen) + display:NO + animate:NO]; + } bool window_has_moved = new_window_bounds_in_screen.origin() != window_bounds_in_screen_.origin(); @@ -478,6 +695,8 @@ void BridgedNativeWidgetHostImpl::OnWindowFullscreenTransitionComplete( void BridgedNativeWidgetHostImpl::OnWindowMiniaturizedChanged( bool miniaturized) { is_miniaturized_ = miniaturized; + if (native_widget_mac_) + native_widget_mac_->GetWidget()->OnNativeWidgetWindowShowStateChanged(); } void BridgedNativeWidgetHostImpl::OnWindowDisplayChanged( @@ -486,7 +705,7 @@ void BridgedNativeWidgetHostImpl::OnWindowDisplayChanged( display_.device_scale_factor() != new_display.device_scale_factor(); bool display_id_changed = display_.id() != new_display.id(); display_ = new_display; - if (scale_factor_changed && compositor_ && has_received_window_geometry_) { + if (scale_factor_changed && compositor_) { compositor_->UpdateSurface( ConvertSizeToPixel(display_.device_scale_factor(), content_bounds_in_screen_.size()), @@ -507,9 +726,14 @@ void BridgedNativeWidgetHostImpl::OnWindowWillClose() { if (DialogDelegate* dialog = widget->widget_delegate()->AsDialogDelegate()) dialog->RemoveObserver(this); native_widget_mac_->WindowDestroying(); + // Remove |this| from the parent's list of children. + SetParent(nullptr); } void BridgedNativeWidgetHostImpl::OnWindowHasClosed() { + // OnWindowHasClosed will be called only after all child windows have had + // OnWindowWillClose called on them. + DCHECK(children_.empty()); native_widget_mac_->WindowDestroyed(); } @@ -551,7 +775,7 @@ void BridgedNativeWidgetHostImpl::DoDialogButtonAction( } } -void BridgedNativeWidgetHostImpl::GetDialogButtonInfo( +bool BridgedNativeWidgetHostImpl::GetDialogButtonInfo( ui::DialogButton button, bool* button_exists, base::string16* button_label, @@ -561,17 +785,154 @@ void BridgedNativeWidgetHostImpl::GetDialogButtonInfo( ui::DialogModel* model = root_view_->GetWidget()->widget_delegate()->AsDialogDelegate(); if (!model || !(model->GetDialogButtons() & button)) - return; + return true; *button_exists = true; *button_label = model->GetDialogButtonLabel(button); *is_button_enabled = model->IsDialogButtonEnabled(button); *is_button_default = button == model->GetDefaultDialogButton(); + return true; } -void BridgedNativeWidgetHostImpl::GetDoDialogButtonsExist(bool* buttons_exist) { +bool BridgedNativeWidgetHostImpl::GetDoDialogButtonsExist(bool* buttons_exist) { ui::DialogModel* model = root_view_->GetWidget()->widget_delegate()->AsDialogDelegate(); *buttons_exist = model && model->GetDialogButtons(); + return true; +} + +bool BridgedNativeWidgetHostImpl::GetShouldShowWindowTitle( + bool* should_show_window_title) { + *should_show_window_title = + root_view_ + ? root_view_->GetWidget()->widget_delegate()->ShouldShowWindowTitle() + : true; + return true; +} + +bool BridgedNativeWidgetHostImpl::GetCanWindowBecomeKey( + bool* can_window_become_key) { + *can_window_become_key = + root_view_ ? root_view_->GetWidget()->CanActivate() : false; + return true; +} + +bool BridgedNativeWidgetHostImpl::GetAlwaysRenderWindowAsKey( + bool* always_render_as_key) { + *always_render_as_key = + root_view_ ? root_view_->GetWidget()->IsAlwaysRenderAsActive() : false; + return true; +} + +bool BridgedNativeWidgetHostImpl::GetCanWindowClose(bool* can_window_close) { + *can_window_close = true; + views::NonClientView* non_client_view = + root_view_ ? root_view_->GetWidget()->non_client_view() : nullptr; + if (non_client_view) + *can_window_close = non_client_view->CanClose(); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// BridgedNativeWidgetHostImpl, +// views_bridge_mac::mojom::BridgedNativeWidgetHost synchronous callbacks: + +void BridgedNativeWidgetHostImpl::DispatchKeyEventRemote( + std::unique_ptr<ui::Event> event, + DispatchKeyEventRemoteCallback callback) { + bool event_handled = false; + DispatchKeyEventRemote(std::move(event), &event_handled); + std::move(callback).Run(event_handled); +} + +void BridgedNativeWidgetHostImpl::DispatchKeyEventToMenuControllerRemote( + std::unique_ptr<ui::Event> event, + DispatchKeyEventToMenuControllerRemoteCallback callback) { + ui::KeyEvent* key_event = event->AsKeyEvent(); + bool event_swallowed = DispatchKeyEventToMenuController(key_event); + std::move(callback).Run(event_swallowed, key_event->handled()); +} + +void BridgedNativeWidgetHostImpl::GetHasMenuController( + GetHasMenuControllerCallback callback) { + bool has_menu_controller = false; + GetHasMenuController(&has_menu_controller); + std::move(callback).Run(has_menu_controller); +} + +void BridgedNativeWidgetHostImpl::GetIsDraggableBackgroundAt( + const gfx::Point& location_in_content, + GetIsDraggableBackgroundAtCallback callback) { + bool is_draggable_background = false; + GetIsDraggableBackgroundAt(location_in_content, &is_draggable_background); + std::move(callback).Run(is_draggable_background); +} + +void BridgedNativeWidgetHostImpl::GetTooltipTextAt( + const gfx::Point& location_in_content, + GetTooltipTextAtCallback callback) { + base::string16 new_tooltip_text; + GetTooltipTextAt(location_in_content, &new_tooltip_text); + std::move(callback).Run(new_tooltip_text); +} + +void BridgedNativeWidgetHostImpl::GetIsFocusedViewTextual( + GetIsFocusedViewTextualCallback callback) { + bool is_textual = false; + GetIsFocusedViewTextual(&is_textual); + std::move(callback).Run(is_textual); +} + +void BridgedNativeWidgetHostImpl::GetWidgetIsModal( + GetWidgetIsModalCallback callback) { + bool widget_is_modal = false; + GetWidgetIsModal(&widget_is_modal); + std::move(callback).Run(widget_is_modal); +} + +void BridgedNativeWidgetHostImpl::GetDialogButtonInfo( + ui::DialogButton button, + GetDialogButtonInfoCallback callback) { + bool exists = false; + base::string16 label; + bool is_enabled = false; + bool is_default = false; + GetDialogButtonInfo(button, &exists, &label, &is_enabled, &is_default); + std::move(callback).Run(exists, label, is_enabled, is_default); +} + +void BridgedNativeWidgetHostImpl::GetDoDialogButtonsExist( + GetDoDialogButtonsExistCallback callback) { + bool buttons_exist = false; + GetDoDialogButtonsExist(&buttons_exist); + std::move(callback).Run(buttons_exist); +} + +void BridgedNativeWidgetHostImpl::GetShouldShowWindowTitle( + GetShouldShowWindowTitleCallback callback) { + bool should_show_window_title = false; + GetShouldShowWindowTitle(&should_show_window_title); + std::move(callback).Run(should_show_window_title); +} + +void BridgedNativeWidgetHostImpl::GetCanWindowBecomeKey( + GetCanWindowBecomeKeyCallback callback) { + bool can_window_become_key = false; + GetCanWindowBecomeKey(&can_window_become_key); + std::move(callback).Run(can_window_become_key); +} + +void BridgedNativeWidgetHostImpl::GetAlwaysRenderWindowAsKey( + GetAlwaysRenderWindowAsKeyCallback callback) { + bool always_render_as_key = false; + GetAlwaysRenderWindowAsKey(&always_render_as_key); + std::move(callback).Run(always_render_as_key); +} + +void BridgedNativeWidgetHostImpl::GetCanWindowClose( + GetCanWindowCloseCallback callback) { + bool can_window_close = false; + GetCanWindowClose(&can_window_close); + std::move(callback).Run(can_window_close); } //////////////////////////////////////////////////////////////////////////////// @@ -598,20 +959,25 @@ void BridgedNativeWidgetHostImpl::OnDidChangeFocus(View* focused_before, // Sanity check: When focus moves away from the widget (i.e. |focused_now| // is nil), then the textInputClient will be cleared. DCHECK(!!focused_now || !input_client); - bridge_impl_->SetTextInputClient(input_client); + // TODO(ccameron): TextInputClient is not handled across process borders + // yet. + if (bridge_impl_) + bridge_impl_->SetTextInputClient(input_client); } } //////////////////////////////////////////////////////////////////////////////// -// BridgedNativeWidget, internal::InputMethodDelegate: +// BridgedNativeWidgetImpl, internal::InputMethodDelegate: ui::EventDispatchDetails BridgedNativeWidgetHostImpl::DispatchKeyEventPostIME( - ui::KeyEvent* key) { + ui::KeyEvent* key, + base::OnceCallback<void(bool)> ack_callback) { DCHECK(focus_manager_); if (!focus_manager_->OnKeyEvent(*key)) key->StopPropagation(); else native_widget_mac_->GetWidget()->OnKeyEvent(key); + CallDispatchKeyEventPostIMEAck(key, std::move(ack_callback)); return ui::EventDispatchDetails(); } diff --git a/chromium/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm b/chromium/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm index 53c32814537..38f71672f2e 100644 --- a/chromium/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm +++ b/chromium/ui/views/cocoa/bridged_native_widget_interactive_uitest.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "ui/views/cocoa/bridged_native_widget.h" +#import "ui/views_bridge_mac/bridged_native_widget_impl.h" #import <Cocoa/Cocoa.h> @@ -251,7 +251,7 @@ void WaitForEvent(NSUInteger mask) { } // namespace // This is used to inject test versions of NativeFrameView and -// BridgedNativeWidget. +// BridgedNativeWidgetImpl. class HitTestNativeWidgetMac : public NativeWidgetMac { public: HitTestNativeWidgetMac(internal::NativeWidgetDelegate* delegate, diff --git a/chromium/ui/views/cocoa/bridged_native_widget_owner.h b/chromium/ui/views/cocoa/bridged_native_widget_owner.h deleted file mode 100644 index 9869ed5a938..00000000000 --- a/chromium/ui/views/cocoa/bridged_native_widget_owner.h +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2015 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_VIEWS_COCOA_BRIDGED_NATIVE_WIDGET_OWNER_H_ -#define UI_VIEWS_COCOA_BRIDGED_NATIVE_WIDGET_OWNER_H_ - -namespace gfx { -class Vector2d; -} - -@class NSWindow; - -namespace views { - -class BridgedNativeWidget; - -// An abstract interface wrapping an NSWindow that ties the lifetime of one or -// more child BridgedNativeWidgets to the lifetime of that NSWindow. This is not -// simply an NSWindow, because the child window API provided by NSWindow -// requires child windows to always be visible. -class BridgedNativeWidgetOwner { - public: - // The NSWindow parent. - virtual NSWindow* GetNSWindow() = 0; - - // The offset in screen pixels for positioning child windows owned by |this|. - virtual gfx::Vector2d GetChildWindowOffset() const = 0; - - // Return false if |this| is hidden, or has a hidden ancestor. - virtual bool IsVisibleParent() const = 0; - - // Removes a child window. Note |this| may be deleted after calling, so the - // caller should immediately null out the pointer used to make the call. - virtual void RemoveChildWindow(BridgedNativeWidget* child) = 0; - - protected: - // Instances of this class may be self-deleting. - virtual ~BridgedNativeWidgetOwner() {} -}; - -} // namespace views - -#endif // UI_VIEWS_COCOA_BRIDGED_NATIVE_WIDGET_OWNER_H_ diff --git a/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm b/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm index 8f3abc65650..3a2248849fd 100644 --- a/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm +++ b/chromium/ui/views/cocoa/bridged_native_widget_unittest.mm @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "ui/views/cocoa/bridged_native_widget.h" +#import "ui/views_bridge_mac/bridged_native_widget_impl.h" #import <Cocoa/Cocoa.h> +#include <objc/runtime.h> #include <memory> @@ -25,10 +26,7 @@ #include "ui/base/test/material_design_controller_test_api.h" #include "ui/events/test/cocoa_test_event_utils.h" #import "ui/gfx/mac/coordinate_conversion.h" -#import "ui/views/cocoa/bridged_content_view.h" #import "ui/views/cocoa/bridged_native_widget_host_impl.h" -#import "ui/views/cocoa/native_widget_mac_nswindow.h" -#import "ui/views/cocoa/views_nswindow_delegate.h" #include "ui/views/controls/textfield/textfield.h" #include "ui/views/controls/textfield/textfield_controller.h" #include "ui/views/controls/textfield/textfield_model.h" @@ -38,6 +36,9 @@ #include "ui/views/widget/root_view.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_observer.h" +#import "ui/views_bridge_mac/bridged_content_view.h" +#import "ui/views_bridge_mac/native_widget_mac_nswindow.h" +#import "ui/views_bridge_mac/views_nswindow_delegate.h" using base::ASCIIToUTF16; using base::SysNSStringToUTF8; @@ -121,8 +122,8 @@ NSArray* const kDeleteActions = @[ @"deleteToBeginningOfParagraph:", @"deleteToEndOfParagraph:" ]; -NSArray* const kMiscActions = - @[ @"insertText:", @"cancelOperation:", @"transpose:", @"yank:" ]; +// This omits @"insertText:":. See BridgedNativeWidgetTest.NilTextInputClient. +NSArray* const kMiscActions = @[ @"cancelOperation:", @"transpose:", @"yank:" ]; // Empty range shortcut for readibility. NSRange EmptyRange() { @@ -293,12 +294,12 @@ NSTextInputContext* g_fake_current_input_context = nullptr; namespace views { namespace test { -// Provides the |parent| argument to construct a BridgedNativeWidget. +// Provides the |parent| argument to construct a BridgedNativeWidgetImpl. class MockNativeWidgetMac : public NativeWidgetMac { public: explicit MockNativeWidgetMac(internal::NativeWidgetDelegate* delegate) : NativeWidgetMac(delegate) {} - using NativeWidgetMac::bridge; + using NativeWidgetMac::bridge_impl; using NativeWidgetMac::bridge_host_for_testing; // internal::NativeWidgetPrivate: @@ -311,7 +312,12 @@ class MockNativeWidgetMac : public NativeWidgetMac { styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]); - bridge()->SetWindow(window); + bridge_host_for_testing()->CreateLocalBridge(window); + if (BridgedNativeWidgetHostImpl* parent = + BridgedNativeWidgetHostImpl::GetFromNativeWindow( + [params.parent window])) { + bridge_host_for_testing()->SetParent(parent); + } bridge_host_for_testing()->InitWindow(params); // Usually the bridge gets initialized here. It is skipped to run extra @@ -330,7 +336,7 @@ class MockNativeWidgetMac : public NativeWidgetMac { DISALLOW_COPY_AND_ASSIGN(MockNativeWidgetMac); }; -// Helper test base to construct a BridgedNativeWidget with a valid parent. +// Helper test base to construct a BridgedNativeWidgetImpl with a valid parent. class BridgedNativeWidgetTestBase : public ui::CocoaTest { public: struct SkipInitialization {}; @@ -342,11 +348,27 @@ class BridgedNativeWidgetTestBase : public ui::CocoaTest { explicit BridgedNativeWidgetTestBase(SkipInitialization tag) : native_widget_mac_(nullptr) {} - BridgedNativeWidget* bridge() { return native_widget_mac_->bridge(); } + BridgedNativeWidgetImpl* bridge() { + return native_widget_mac_->bridge_impl(); + } BridgedNativeWidgetHostImpl* bridge_host() { return native_widget_mac_->bridge_host_for_testing(); } + // Generate an autoreleased KeyDown NSEvent* in |widget_| for pressing the + // corresponding |key_code|. + NSEvent* VkeyKeyDown(ui::KeyboardCode key_code) { + return cocoa_test_event_utils::SynthesizeKeyEvent( + widget_->GetNativeWindow(), true /* keyDown */, key_code, 0); + } + + // Generate an autoreleased KeyDown NSEvent* using the given keycode, and + // representing the first unicode character of |chars|. + NSEvent* UnicodeKeyDown(int key_code, NSString* chars) { + return cocoa_test_event_utils::KeyEventWithKeyCode( + key_code, [chars characterAtIndex:0], NSKeyDown, 0); + } + // Overridden from testing::Test: void SetUp() override { ui::CocoaTest::SetUp(); @@ -385,7 +407,9 @@ class BridgedNativeWidgetTestBase : public ui::CocoaTest { } NSWindow* bridge_window() const { - return native_widget_mac_->bridge()->ns_window(); + if (native_widget_mac_->bridge_impl()) + return native_widget_mac_->bridge_impl()->ns_window(); + return nil; } protected: @@ -614,7 +638,8 @@ void BridgedNativeWidgetTest::SetUp() { // The delegate should exist before setting the root view. EXPECT_TRUE([window delegate]); bridge_host()->SetRootView(view_.get()); - bridge()->CreateContentView(view_->bounds()); + bridge()->CreateContentView(bridge_host()->GetRootViewNSViewId(), + view_->bounds()); ns_view_ = bridge()->ns_view(); // Pretend it has been shown via NativeWidgetMac::Show(). @@ -853,7 +878,7 @@ class BridgedNativeWidgetInitTest : public BridgedNativeWidgetTestBase { DISALLOW_COPY_AND_ASSIGN(BridgedNativeWidgetInitTest); }; -// Test that BridgedNativeWidget remains sane if Init() is never called. +// Test that BridgedNativeWidgetImpl remains sane if Init() is never called. TEST_F(BridgedNativeWidgetInitTest, InitNotCalled) { // Don't use a Widget* as the delegate. ~Widget() checks for Widget:: // |native_widget_destroyed_| being set to true. That can only happen with a @@ -862,8 +887,8 @@ TEST_F(BridgedNativeWidgetInitTest, InitNotCalled) { std::unique_ptr<MockNativeWidgetMac> native_widget( new MockNativeWidgetMac(nullptr)); native_widget_mac_ = native_widget.get(); - EXPECT_FALSE(bridge()->ns_view()); - EXPECT_FALSE(bridge()->ns_window()); + EXPECT_FALSE(bridge()); + EXPECT_FALSE(bridge_host()->GetLocalNSWindow()); } // Tests the shadow type given in InitParams. @@ -1323,10 +1348,16 @@ TEST_F(BridgedNativeWidgetTest, NilTextInputClient) { [selectors addObjectsFromArray:kMoveActions]; [selectors addObjectsFromArray:kSelectActions]; [selectors addObjectsFromArray:kDeleteActions]; + + // -insertText: is omitted from this list to avoid a DCHECK in + // doCommandBySelector:. AppKit never passes -insertText: to + // doCommandBySelector: (it calls -insertText: directly instead). [selectors addObjectsFromArray:kMiscActions]; for (NSString* selector in selectors) [ns_view_ doCommandBySelector:NSSelectorFromString(selector)]; + + [ns_view_ insertText:@""]; } // Test transpose command against expectations set by |dummy_text_view_|. @@ -1434,27 +1465,22 @@ TEST_F(BridgedNativeWidgetTest, TextInput_SimulatePhoneticIme) { // Sequence of calls (and corresponding keyDown events) obtained via tracing // with 2-Set Korean IME and pressing q, o, then Enter on the keyboard. - NSEvent* q_in_ime = cocoa_test_event_utils::KeyEventWithKeyCode( - 12, [@"ㅂ" characterAtIndex:0], NSKeyDown, 0); + NSEvent* q_in_ime = UnicodeKeyDown(12, @"ㅂ"); InterpretKeyEventsCallback handle_q_in_ime = base::BindRepeating([](id view) { [view insertText:@"ㅂ" replacementRange:NSMakeRange(NSNotFound, 0)]; }); - NSEvent* o_in_ime = cocoa_test_event_utils::KeyEventWithKeyCode( - 31, [@"ㅐ" characterAtIndex:0], NSKeyDown, 0); + NSEvent* o_in_ime = UnicodeKeyDown(31, @"ㅐ"); InterpretKeyEventsCallback handle_o_in_ime = base::BindRepeating([](id view) { [view insertText:@"배" replacementRange:NSMakeRange(0, 1)]; }); - NSEvent* return_in_ime = cocoa_test_event_utils::SynthesizeKeyEvent( - widget_->GetNativeWindow(), true, ui::VKEY_RETURN, 0); InterpretKeyEventsCallback handle_return_in_ime = base::BindRepeating([](id view) { // When confirming the composition, AppKit repeats itself. [view insertText:@"배" replacementRange:NSMakeRange(0, 1)]; [view insertText:@"배" replacementRange:NSMakeRange(0, 1)]; - // Note: there is no insertText:@"\r", which we would normally see when - // not in an IME context for VKEY_RETURN. + [view doCommandBySelector:@selector(insertNewLine:)]; }); // Add a hook for the KeyEvent being received by the TextfieldController. E.g. @@ -1486,7 +1512,7 @@ TEST_F(BridgedNativeWidgetTest, TextInput_SimulatePhoneticIme) { // Note the "Enter" should not replace the replacement range, even though a // replacement range was set. g_fake_interpret_key_events = &handle_return_in_ime; - [ns_view_ keyDown:return_in_ime]; + [ns_view_ keyDown:VkeyKeyDown(ui::VKEY_RETURN)]; EXPECT_EQ(base::SysNSStringToUTF16(@"배"), textfield->text()); // VKEY_RETURN should be seen by via the unhandled key event handler (but not @@ -1496,6 +1522,76 @@ TEST_F(BridgedNativeWidgetTest, TextInput_SimulatePhoneticIme) { g_fake_interpret_key_events = nullptr; } +// Test simulated codepaths for typing 'm', 'o', 'o', Enter in the Telex IME. +// This IME does not mark text, but, unlike 2-set Korean, it re-inserts the +// entire word on each keypress, even though only the last character in the word +// can be modified. This prevents the keypress being treated as a "character" +// event (which is unavoidably unfortunate for the Undo buffer), but also led to +// a codepath that suppressed a VKEY_RETURN when it should not, since there is +// no candidate IME window to dismiss for this IME. +TEST_F(BridgedNativeWidgetTest, TextInput_SimulateTelexMoo) { + Textfield* textfield = InstallTextField(""); + EXPECT_TRUE([ns_view_ textInputClient]); + + EnterAcceleratorView* enter_view = new EnterAcceleratorView(); + textfield->parent()->AddChildView(enter_view); + + // Sequence of calls (and corresponding keyDown events) obtained via tracing + // with Telex IME and pressing 'm', 'o', 'o', then Enter on the keyboard. + // Note that without the leading 'm', only one character changes, which could + // allow the keypress to be treated as a character event, which would not + // produce the bug. + NSEvent* m_in_ime = UnicodeKeyDown(46, @"m"); + InterpretKeyEventsCallback handle_m_in_ime = base::BindRepeating([](id view) { + [view insertText:@"m" replacementRange:NSMakeRange(NSNotFound, 0)]; + }); + + // Note that (unlike Korean IME), Telex generates a latin "o" for both events: + // it doesn't associate a unicode character on the second NSEvent. + NSEvent* o_in_ime = UnicodeKeyDown(31, @"o"); + InterpretKeyEventsCallback handle_first_o_in_ime = + base::BindRepeating([](id view) { + // Note the whole word is replaced, not just the last character. + [view insertText:@"mo" replacementRange:NSMakeRange(0, 1)]; + }); + InterpretKeyEventsCallback handle_second_o_in_ime = + base::BindRepeating([](id view) { + [view insertText:@"mô" replacementRange:NSMakeRange(0, 2)]; + }); + + InterpretKeyEventsCallback handle_return_in_ime = + base::BindRepeating([](id view) { + // Note the previous -insertText: repeats, even though it is unchanged. + // But the IME also follows with an -insertNewLine:. + [view insertText:@"mô" replacementRange:NSMakeRange(0, 2)]; + [view doCommandBySelector:@selector(insertNewLine:)]; + }); + + EXPECT_EQ(base::UTF8ToUTF16(""), textfield->text()); + EXPECT_EQ(0, enter_view->count()); + + object_setClass(ns_view_, [InterpretKeyEventMockedBridgedContentView class]); + g_fake_interpret_key_events = &handle_m_in_ime; + [ns_view_ keyDown:m_in_ime]; + EXPECT_EQ(base::SysNSStringToUTF16(@"m"), textfield->text()); + EXPECT_EQ(0, enter_view->count()); + + g_fake_interpret_key_events = &handle_first_o_in_ime; + [ns_view_ keyDown:o_in_ime]; + EXPECT_EQ(base::SysNSStringToUTF16(@"mo"), textfield->text()); + EXPECT_EQ(0, enter_view->count()); + + g_fake_interpret_key_events = &handle_second_o_in_ime; + [ns_view_ keyDown:o_in_ime]; + EXPECT_EQ(base::SysNSStringToUTF16(@"mô"), textfield->text()); + EXPECT_EQ(0, enter_view->count()); + + g_fake_interpret_key_events = &handle_return_in_ime; + [ns_view_ keyDown:VkeyKeyDown(ui::VKEY_RETURN)]; + EXPECT_EQ(base::SysNSStringToUTF16(@"mô"), textfield->text()); // No change. + EXPECT_EQ(1, enter_view->count()); // Now we see the accelerator. +} + // Simulate 'a', Enter in Hiragana. This should just insert "あ", suppressing // accelerators. TEST_F(BridgedNativeWidgetTest, TextInput_NoAcceleratorEnterComposition) { @@ -1507,8 +1603,8 @@ TEST_F(BridgedNativeWidgetTest, TextInput_NoAcceleratorEnterComposition) { // Sequence of calls (and corresponding keyDown events) obtained via tracing // with Hiragana IME and pressing 'a', then Enter on the keyboard. - NSEvent* a_in_ime = cocoa_test_event_utils::KeyEventWithKeyCode( - 0, [@"a" characterAtIndex:0], NSKeyDown, 0); + // Note 0 is the actual keyCode for 'a', not a placeholder. + NSEvent* a_in_ime = UnicodeKeyDown(0, @"a"); InterpretKeyEventsCallback handle_a_in_ime = base::BindRepeating([](id view) { // TODO(crbug/612675): |text| should be an NSAttributedString. [view setMarkedText:@"あ" @@ -1516,12 +1612,13 @@ TEST_F(BridgedNativeWidgetTest, TextInput_NoAcceleratorEnterComposition) { replacementRange:NSMakeRange(NSNotFound, 0)]; }); - NSEvent* return_event = cocoa_test_event_utils::SynthesizeKeyEvent( - widget_->GetNativeWindow(), true, ui::VKEY_RETURN, 0); - InterpretKeyEventsCallback handle_return_in_ime = + InterpretKeyEventsCallback handle_first_return_in_ime = base::BindRepeating([](id view) { [view insertText:@"あ" replacementRange:NSMakeRange(NSNotFound, 0)]; + // Note there is no call to -insertNewLine: here. }); + InterpretKeyEventsCallback handle_second_return_in_ime = base::BindRepeating( + [](id view) { [view doCommandBySelector:@selector(insertNewLine:)]; }); EXPECT_EQ(base::UTF8ToUTF16(""), textfield->text()); EXPECT_EQ(0, enter_view->count()); @@ -1532,16 +1629,14 @@ TEST_F(BridgedNativeWidgetTest, TextInput_NoAcceleratorEnterComposition) { EXPECT_EQ(base::SysNSStringToUTF16(@"あ"), textfield->text()); EXPECT_EQ(0, enter_view->count()); - g_fake_interpret_key_events = &handle_return_in_ime; - [ns_view_ keyDown:return_event]; + g_fake_interpret_key_events = &handle_first_return_in_ime; + [ns_view_ keyDown:VkeyKeyDown(ui::VKEY_RETURN)]; EXPECT_EQ(base::SysNSStringToUTF16(@"あ"), textfield->text()); EXPECT_EQ(0, enter_view->count()); // Not seen as an accelerator. - // IME Window is dismissed here and there is no marked text, so remove the - // swizzler. - object_setClass(ns_view_, [BridgedContentView class]); - - [ns_view_ keyDown:return_event]; // Sanity check: send Enter again. + g_fake_interpret_key_events = &handle_second_return_in_ime; + [ns_view_ + keyDown:VkeyKeyDown(ui::VKEY_RETURN)]; // Sanity check: send Enter again. EXPECT_EQ(base::SysNSStringToUTF16(@"あ"), textfield->text()); // No change. EXPECT_EQ(1, enter_view->count()); // Now we see the accelerator. } @@ -1557,8 +1652,7 @@ TEST_F(BridgedNativeWidgetTest, TextInput_NoAcceleratorTabEnterComposition) { // Sequence of calls (and corresponding keyDown events) obtained via tracing // with Hiragana IME and pressing 'a', Tab, then Enter on the keyboard. - NSEvent* a_in_ime = cocoa_test_event_utils::KeyEventWithKeyCode( - 0, [@"a" characterAtIndex:0], NSKeyDown, 0); + NSEvent* a_in_ime = UnicodeKeyDown(0, @"a"); InterpretKeyEventsCallback handle_a_in_ime = base::BindRepeating([](id view) { // TODO(crbug/612675): |text| should have an underline. [view setMarkedText:@"あ" @@ -1566,8 +1660,6 @@ TEST_F(BridgedNativeWidgetTest, TextInput_NoAcceleratorTabEnterComposition) { replacementRange:NSMakeRange(NSNotFound, 0)]; }); - NSEvent* tab_in_ime = cocoa_test_event_utils::SynthesizeKeyEvent( - widget_->GetNativeWindow(), true, ui::VKEY_TAB, 0); InterpretKeyEventsCallback handle_tab_in_ime = base::BindRepeating([](id view) { // TODO(crbug/612675): |text| should be an NSAttributedString (now with @@ -1575,10 +1667,9 @@ TEST_F(BridgedNativeWidgetTest, TextInput_NoAcceleratorTabEnterComposition) { [view setMarkedText:@"a" selectedRange:NSMakeRange(0, 1) replacementRange:NSMakeRange(NSNotFound, 0)]; + // Note there is no -insertTab: generated. }); - NSEvent* return_event = cocoa_test_event_utils::SynthesizeKeyEvent( - widget_->GetNativeWindow(), true, ui::VKEY_RETURN, 0); InterpretKeyEventsCallback handle_first_return_in_ime = base::BindRepeating([](id view) { // Do *nothing*. Enter does not confirm nor change the composition, it @@ -1589,6 +1680,11 @@ TEST_F(BridgedNativeWidgetTest, TextInput_NoAcceleratorTabEnterComposition) { // The second return will confirm the composition. [view insertText:@"a" replacementRange:NSMakeRange(NSNotFound, 0)]; }); + InterpretKeyEventsCallback handle_third_return_in_ime = + base::BindRepeating([](id view) { + // Only the third return will generate -insertNewLine:. + [view doCommandBySelector:@selector(insertNewLine:)]; + }); EXPECT_EQ(base::UTF8ToUTF16(""), textfield->text()); EXPECT_EQ(0, enter_view->count()); @@ -1600,28 +1696,26 @@ TEST_F(BridgedNativeWidgetTest, TextInput_NoAcceleratorTabEnterComposition) { EXPECT_EQ(0, enter_view->count()); g_fake_interpret_key_events = &handle_tab_in_ime; - [ns_view_ keyDown:tab_in_ime]; + [ns_view_ keyDown:VkeyKeyDown(ui::VKEY_TAB)]; // Tab will switch to a Romanji (Latin) character. EXPECT_EQ(base::SysNSStringToUTF16(@"a"), textfield->text()); EXPECT_EQ(0, enter_view->count()); g_fake_interpret_key_events = &handle_first_return_in_ime; - [ns_view_ keyDown:return_event]; + [ns_view_ keyDown:VkeyKeyDown(ui::VKEY_RETURN)]; // Enter just dismisses the IME window. The composition is still active. EXPECT_EQ(base::SysNSStringToUTF16(@"a"), textfield->text()); EXPECT_EQ(0, enter_view->count()); // Not seen as an accelerator. g_fake_interpret_key_events = &handle_second_return_in_ime; - [ns_view_ keyDown:return_event]; + [ns_view_ keyDown:VkeyKeyDown(ui::VKEY_RETURN)]; // Enter now confirms the composition (unmarks text). Note there is still no // IME window visible but, since there is marked text, IME is still active. EXPECT_EQ(base::SysNSStringToUTF16(@"a"), textfield->text()); EXPECT_EQ(0, enter_view->count()); // Not seen as an accelerator. - // No marked text, no IME window. We could remove the swizzler here, but - // that is equivalent to the "do nothing" case, so set than handler again. - g_fake_interpret_key_events = &handle_first_return_in_ime; - [ns_view_ keyDown:return_event]; // Send Enter a _third_ time. + g_fake_interpret_key_events = &handle_third_return_in_ime; + [ns_view_ keyDown:VkeyKeyDown(ui::VKEY_RETURN)]; // Send Enter a third time. EXPECT_EQ(base::SysNSStringToUTF16(@"a"), textfield->text()); // No change. EXPECT_EQ(1, enter_view->count()); // Now we see the accelerator. } @@ -1754,8 +1848,8 @@ TEST_F(BridgedNativeWidgetSimulateFullscreenTest, FailToEnterAndExit) { object:window]; // On a failure, Cocoa starts by sending an unexpected *exit* fullscreen, and - // BridgedNativeWidget will think it's just a delayed transition and try to go - // back into fullscreen but get ignored by Cocoa. + // BridgedNativeWidgetImpl will think it's just a delayed transition and try + // to go back into fullscreen but get ignored by Cocoa. EXPECT_EQ(0, [window ignoredToggleFullScreenCount]); EXPECT_TRUE(bridge()->target_fullscreen_state()); [center postNotificationName:NSWindowDidExitFullScreenNotification diff --git a/chromium/ui/views/cocoa/cocoa_mouse_capture_unittest.mm b/chromium/ui/views/cocoa/cocoa_mouse_capture_unittest.mm index 47235a7fcff..295e0617cd8 100644 --- a/chromium/ui/views/cocoa/cocoa_mouse_capture_unittest.mm +++ b/chromium/ui/views/cocoa/cocoa_mouse_capture_unittest.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "ui/views/cocoa/cocoa_mouse_capture.h" +#import "ui/views_bridge_mac/cocoa_mouse_capture.h" #import <Cocoa/Cocoa.h> @@ -10,7 +10,7 @@ #include "base/macros.h" #import "ui/base/test/cocoa_helper.h" #import "ui/events/test/cocoa_test_event_utils.h" -#import "ui/views/cocoa/cocoa_mouse_capture_delegate.h" +#import "ui/views_bridge_mac/cocoa_mouse_capture_delegate.h" // Simple test view that counts calls to -[NSView mouseDown:]. @interface CocoaMouseCaptureTestView : NSView { @@ -30,7 +30,7 @@ @end -namespace views { +namespace views_bridge_mac { namespace { // Simple capture delegate that just counts events forwarded. @@ -128,4 +128,4 @@ TEST_F(CocoaMouseCaptureTest, CaptureEvents) { EXPECT_EQ(2, [view mouseDownCount]); } -} // namespace views +} // namespace views_bridge_mac diff --git a/chromium/ui/views/cocoa/drag_drop_client_mac.h b/chromium/ui/views/cocoa/drag_drop_client_mac.h index 2e1129957b4..e93d8a09441 100644 --- a/chromium/ui/views/cocoa/drag_drop_client_mac.h +++ b/chromium/ui/views/cocoa/drag_drop_client_mac.h @@ -14,6 +14,7 @@ #include "ui/base/dragdrop/os_exchange_data.h" #include "ui/views/views_export.h" #include "ui/views/widget/drop_helper.h" +#include "ui/views_bridge_mac/drag_drop_client.h" // This class acts as a bridge between NSPasteboardItem and OSExchangeData by // implementing NSPasteboardItemDataProvider and writing data from @@ -32,16 +33,16 @@ namespace test { class DragDropClientMacTest; } -class BridgedNativeWidget; +class BridgedNativeWidgetImpl; class View; // Implements drag and drop on MacViews. This class acts as a bridge between // the Views and native system's drag and drop. This class mimics // DesktopDragDropClientAuraX11. -class VIEWS_EXPORT DragDropClientMac { +class VIEWS_EXPORT DragDropClientMac : public views_bridge_mac::DragDropClient { public: - explicit DragDropClientMac(BridgedNativeWidget* bridge, View* root_view); - ~DragDropClientMac(); + DragDropClientMac(BridgedNativeWidgetImpl* bridge, View* root_view); + ~DragDropClientMac() override; // Initiates a drag and drop session. Returns the drag operation that was // applied at the end of the drag drop session. @@ -50,20 +51,14 @@ class VIEWS_EXPORT DragDropClientMac { int operation, ui::DragDropTypes::DragEventSource source); - // Called when mouse is dragged during a drag and drop. - NSDragOperation DragUpdate(id<NSDraggingInfo>); - - // Called when mouse is released during a drag and drop. - NSDragOperation Drop(id<NSDraggingInfo> sender); - - // Called when the drag and drop session has ended. - void EndDrag(); - - // Called when mouse leaves the drop area. - void DragExit(); - DropHelper* drop_helper() { return &drop_helper_; } + // views_bridge_mac::DragDropClient: + NSDragOperation DragUpdate(id<NSDraggingInfo>) override; + NSDragOperation Drop(id<NSDraggingInfo> sender) override; + void EndDrag() override; + void DragExit() override; + private: friend class test::DragDropClientMacTest; @@ -80,7 +75,7 @@ class VIEWS_EXPORT DragDropClientMac { int operation_; // The bridge between the content view and the drag drop client. - BridgedNativeWidget* bridge_; // Weak. Owns |this|. + BridgedNativeWidgetImpl* bridge_; // Weak. Owns |this|. // The closure for the drag and drop's run loop. base::Closure quit_closure_; diff --git a/chromium/ui/views/cocoa/drag_drop_client_mac.mm b/chromium/ui/views/cocoa/drag_drop_client_mac.mm index 78e7b0b3226..2df33355f9e 100644 --- a/chromium/ui/views/cocoa/drag_drop_client_mac.mm +++ b/chromium/ui/views/cocoa/drag_drop_client_mac.mm @@ -10,9 +10,9 @@ #import "ui/base/dragdrop/os_exchange_data_provider_mac.h" #include "ui/gfx/image/image_skia_util_mac.h" #include "ui/views/drag_utils.h" -#import "ui/views/cocoa/bridged_content_view.h" -#import "ui/views/cocoa/bridged_native_widget.h" #include "ui/views/widget/native_widget_mac.h" +#import "ui/views_bridge_mac/bridged_content_view.h" +#import "ui/views_bridge_mac/bridged_native_widget_impl.h" @interface CocoaDragDropDataProvider () - (id)initWithData:(const ui::OSExchangeData&)data; @@ -57,7 +57,7 @@ namespace views { -DragDropClientMac::DragDropClientMac(BridgedNativeWidget* bridge, +DragDropClientMac::DragDropClientMac(BridgedNativeWidgetImpl* bridge, View* root_view) : drop_helper_(root_view), operation_(0), diff --git a/chromium/ui/views/cocoa/drag_drop_client_mac_unittest.mm b/chromium/ui/views/cocoa/drag_drop_client_mac_unittest.mm index 9f3559de2ef..4202d53a348 100644 --- a/chromium/ui/views/cocoa/drag_drop_client_mac_unittest.mm +++ b/chromium/ui/views/cocoa/drag_drop_client_mac_unittest.mm @@ -13,12 +13,12 @@ #include "base/threading/thread_task_runner_handle.h" #import "ui/base/clipboard/clipboard_util_mac.h" #include "ui/gfx/image/image_unittest_util.h" -#import "ui/views/cocoa/bridged_native_widget.h" #import "ui/views/cocoa/bridged_native_widget_host_impl.h" #include "ui/views/test/widget_test.h" #include "ui/views/view.h" #include "ui/views/widget/native_widget_mac.h" #include "ui/views/widget/widget.h" +#import "ui/views_bridge_mac/bridged_native_widget_impl.h" using base::ASCIIToUTF16; @@ -156,7 +156,9 @@ class DragDropClientMacTest : public WidgetTest { public: DragDropClientMacTest() : widget_(new Widget) {} - DragDropClientMac* drag_drop_client() { return bridge_->drag_drop_client(); } + DragDropClientMac* drag_drop_client() { + return bridge_host_->drag_drop_client(); + } NSDragOperation DragUpdate(NSPasteboard* pasteboard) { DragDropClientMac* client = drag_drop_client(); @@ -186,9 +188,9 @@ class DragDropClientMacTest : public WidgetTest { gfx::Rect bounds(0, 0, 100, 100); widget_->SetBounds(bounds); - bridge_ = - NativeWidgetMac::GetBridgeForNativeWindow(widget_->GetNativeWindow()); - bridge_host_ = NativeWidgetMac::GetBridgeHostImplForNativeWindow( + bridge_ = BridgedNativeWidgetImpl::GetFromNativeWindow( + widget_->GetNativeWindow()); + bridge_host_ = BridgedNativeWidgetHostImpl::GetFromNativeWindow( widget_->GetNativeWindow()); widget_->Show(); @@ -207,7 +209,7 @@ class DragDropClientMacTest : public WidgetTest { protected: Widget* widget_ = nullptr; - BridgedNativeWidget* bridge_ = nullptr; + BridgedNativeWidgetImpl* bridge_ = nullptr; BridgedNativeWidgetHostImpl* bridge_host_ = nullptr; DragDropView* target_ = nullptr; base::scoped_nsobject<MockDraggingInfo> dragging_info_; diff --git a/chromium/ui/views/cocoa/tooltip_manager_mac.h b/chromium/ui/views/cocoa/tooltip_manager_mac.h index 208fb4e948a..7fa612ca843 100644 --- a/chromium/ui/views/cocoa/tooltip_manager_mac.h +++ b/chromium/ui/views/cocoa/tooltip_manager_mac.h @@ -8,13 +8,19 @@ #include "base/macros.h" #include "ui/views/widget/tooltip_manager.h" -namespace views { +namespace views_bridge_mac { +namespace mojom { class BridgedNativeWidget; +} // namespace mojom +} // namespace views_bridge_mac + +namespace views { -// Manages native Cocoa tooltips for the given BridgedNativeWidget. +// Manages native Cocoa tooltips for the given BridgedNativeWidgetHostImpl. class TooltipManagerMac : public TooltipManager { public: - explicit TooltipManagerMac(BridgedNativeWidget* widget); + explicit TooltipManagerMac( + views_bridge_mac::mojom::BridgedNativeWidget* bridge); ~TooltipManagerMac() override; // TooltipManager: @@ -24,7 +30,8 @@ class TooltipManagerMac : public TooltipManager { void TooltipTextChanged(View* view) override; private: - BridgedNativeWidget* widget_; // Weak. Owns this. + views_bridge_mac::mojom::BridgedNativeWidget* + bridge_; // Weak. Owned by the owner of this. DISALLOW_COPY_AND_ASSIGN(TooltipManagerMac); }; diff --git a/chromium/ui/views/cocoa/tooltip_manager_mac.mm b/chromium/ui/views/cocoa/tooltip_manager_mac.mm index 99a1fb5d906..cbf12b14fda 100644 --- a/chromium/ui/views/cocoa/tooltip_manager_mac.mm +++ b/chromium/ui/views/cocoa/tooltip_manager_mac.mm @@ -4,11 +4,12 @@ #include "ui/views/cocoa/tooltip_manager_mac.h" +#include "base/no_destructor.h" #include "ui/base/cocoa/cocoa_base_utils.h" #include "ui/gfx/font_list.h" #import "ui/gfx/mac/coordinate_conversion.h" -#import "ui/views/cocoa/bridged_content_view.h" -#import "ui/views/cocoa/bridged_native_widget.h" +#import "ui/views_bridge_mac/bridged_content_view.h" +#import "ui/views_bridge_mac/bridged_native_widget_impl.h" namespace { @@ -19,9 +20,9 @@ const int kTooltipMaxWidthPixels = 250; namespace views { -TooltipManagerMac::TooltipManagerMac(BridgedNativeWidget* widget) - : widget_(widget) { -} +TooltipManagerMac::TooltipManagerMac( + views_bridge_mac::mojom::BridgedNativeWidget* bridge) + : bridge_(bridge) {} TooltipManagerMac::~TooltipManagerMac() { } @@ -31,20 +32,13 @@ int TooltipManagerMac::GetMaxWidth(const gfx::Point& location) const { } const gfx::FontList& TooltipManagerMac::GetFontList() const { - CR_DEFINE_STATIC_LOCAL(gfx::FontList, font_list, - (gfx::Font([NSFont toolTipsFontOfSize:0]))); - return font_list; + static base::NoDestructor<gfx::FontList> font_list( + []() { return gfx::Font([NSFont toolTipsFontOfSize:0]); }()); + return *font_list; } void TooltipManagerMac::UpdateTooltip() { - NSWindow* window = widget_->ns_window(); - BridgedContentView* view = widget_->ns_view(); - - NSPoint nspoint = - ui::ConvertPointFromScreenToWindow(window, [NSEvent mouseLocation]); - // Note: flip in the view's frame, which matches the window's contentRect. - gfx::Point point(nspoint.x, NSHeight([view frame]) - nspoint.y); - [view updateTooltipIfRequiredAt:point]; + bridge_->UpdateTooltip(); } void TooltipManagerMac::TooltipTextChanged(View* view) { diff --git a/chromium/ui/views/cocoa/widget_owner_nswindow_adapter.h b/chromium/ui/views/cocoa/widget_owner_nswindow_adapter.h deleted file mode 100644 index 8240bead793..00000000000 --- a/chromium/ui/views/cocoa/widget_owner_nswindow_adapter.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2015 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_VIEWS_COCOA_WIDGET_OWNER_NSWINDOW_ADAPTER_H_ -#define UI_VIEWS_COCOA_WIDGET_OWNER_NSWINDOW_ADAPTER_H_ - -#import "base/mac/scoped_nsobject.h" -#include "base/macros.h" -#import "ui/views/cocoa/bridged_native_widget_owner.h" - -@class NSView; -@class NSWindow; -@class WidgetOwnerNSWindowAdapterBridge; - -namespace views { - -// An adapter that allows a views::Widget to be owned by an NSWindow that is not -// backed by another BridgedNativeWidget. -class WidgetOwnerNSWindowAdapter : public BridgedNativeWidgetOwner { - public: - // Create an adapter that will own |child|, tying its lifetime with the - // NSWindow containing |anchor_view|. The object is self-deleting, via a call - // to RemoveChildWindow() made in child->OnWindowWillClose(). - WidgetOwnerNSWindowAdapter(BridgedNativeWidget* child, NSView* anchor_view); - - // Called when the owning window is closing. - void OnWindowWillClose(); - - // Called when the owning window is hidden or shown. - void OnWindowDidChangeOcclusionState(); - - // Overridden from BridgedNativeWidgetOwner: - NSWindow* GetNSWindow() override; - gfx::Vector2d GetChildWindowOffset() const override; - bool IsVisibleParent() const override; - void RemoveChildWindow(BridgedNativeWidget* child) override; - - private: - // Self-deleting. - ~WidgetOwnerNSWindowAdapter() override; - - BridgedNativeWidget* child_; // Weak. Owned by its NativeWidgetMac. - base::scoped_nsobject<NSView> anchor_view_; - base::scoped_nsobject<NSWindow> anchor_window_; - base::scoped_nsobject<WidgetOwnerNSWindowAdapterBridge> observer_bridge_; - - DISALLOW_COPY_AND_ASSIGN(WidgetOwnerNSWindowAdapter); -}; - -} // namespace views - -#endif // UI_VIEWS_COCOA_WIDGET_OWNER_NSWINDOW_ADAPTER_H_ diff --git a/chromium/ui/views/cocoa/widget_owner_nswindow_adapter.mm b/chromium/ui/views/cocoa/widget_owner_nswindow_adapter.mm deleted file mode 100644 index 3585b78344e..00000000000 --- a/chromium/ui/views/cocoa/widget_owner_nswindow_adapter.mm +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2015 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/views/cocoa/widget_owner_nswindow_adapter.h" - -#import <Cocoa/Cocoa.h> - -#include "base/logging.h" -#include "base/mac/sdk_forward_declarations.h" -#include "ui/gfx/geometry/rect.h" -#include "ui/gfx/geometry/vector2d.h" -#import "ui/gfx/mac/coordinate_conversion.h" -#import "ui/views/cocoa/bridged_native_widget.h" - -// Bridges an AppKit observer to observe when the (non-views) NSWindow owning a -// views::Widget will close or change occlusion state. -@interface WidgetOwnerNSWindowAdapterBridge : NSObject { - @private - views::WidgetOwnerNSWindowAdapter* adapter_; // Weak. Owns us. -} -- (instancetype)initWithAdapter:(views::WidgetOwnerNSWindowAdapter*)adapter; -- (void)windowWillClose:(NSNotification*)notification; -- (void)windowDidChangeOcclusionState:(NSNotification*)notification; -@end - -@implementation WidgetOwnerNSWindowAdapterBridge - -- (instancetype)initWithAdapter:(views::WidgetOwnerNSWindowAdapter*)adapter { - if ((self = [super init])) - adapter_ = adapter; - return self; -} - -- (void)windowWillClose:(NSNotification*)notification { - adapter_->OnWindowWillClose(); -} - -- (void)windowDidChangeOcclusionState:(NSNotification*)notification { - adapter_->OnWindowDidChangeOcclusionState(); -} - -@end - -namespace views { - -WidgetOwnerNSWindowAdapter::WidgetOwnerNSWindowAdapter( - BridgedNativeWidget* child, - NSView* anchor_view) - : child_(child), - anchor_view_([anchor_view retain]), - observer_bridge_( - [[WidgetOwnerNSWindowAdapterBridge alloc] initWithAdapter:this]) { - - // Although the |anchor_view| must be in an NSWindow when the child dialog is - // created, it's permitted for the |anchor_view| to be removed from its view - // hierarchy before the child dialog window is fully removed from screen. When - // this happens, [anchor_view_ window] will become nil, so retain both. - anchor_window_.reset([[anchor_view_ window] retain]); - DCHECK(anchor_window_); - - [[NSNotificationCenter defaultCenter] - addObserver:observer_bridge_ - selector:@selector(windowWillClose:) - name:NSWindowWillCloseNotification - object:anchor_window_]; - - // BridgedNativeWidget removes NSWindow parent/child relationships for hidden - // windows. Observe when the parent's visibility changes so they can be - // reconnected. - [[NSNotificationCenter defaultCenter] - addObserver:observer_bridge_ - selector:@selector(windowDidChangeOcclusionState:) - name:NSWindowDidChangeOcclusionStateNotification - object:anchor_window_]; - - // On a deminiaturize, the occlusion state change may occur before -[NSWindow - // isVisible] returns YES. So observe NSWindowDidDeminiaturizeNotification. - // This allows the adapter to check again at the end of the deminiaturize. - [[NSNotificationCenter defaultCenter] - addObserver:observer_bridge_ - selector:@selector(windowDidChangeOcclusionState:) - name:NSWindowDidDeminiaturizeNotification - object:anchor_window_]; -} - -void WidgetOwnerNSWindowAdapter::OnWindowWillClose() { - // Retain the child window before closing it. If the last reference to the - // NSWindow goes away inside -[NSWindow close], then bad stuff can happen. - // See e.g. http://crbug.com/616701. - base::scoped_nsobject<NSWindow> child_window(child_->ns_window(), - base::scoped_policy::RETAIN); - - // AppKit child window relationships break when the windows are not visible, - // so if the child is not visible, it won't currently be a child. - if (![child_window isVisible]) - DCHECK(![child_window parentWindow] && ![child_window sheetParent]); - DCHECK([child_window delegate]); - - [child_window close]; - // Note: |this| will be deleted here. - - DCHECK(![child_window parentWindow]); - DCHECK(![child_window delegate]); -} - -void WidgetOwnerNSWindowAdapter::OnWindowDidChangeOcclusionState() { - // The adapter only needs to handle a parent "show", since the only way it - // should be hidden is via -[NSApp hide], and all BridgedNativeWidgets - // subscribe to NSApplicationDidHideNotification already. - if (![anchor_window_ isVisible]) - return; - - if (child_->window_visible()) { - // A sheet should never have a parentWindow, otherwise dismissing the sheet - // causes graphical glitches (http://crbug.com/605098). Non-sheets should - // always have a parentWindow. - DCHECK([child_->ns_window() isSheet] != - !![child_->ns_window() parentWindow]); - DCHECK(child_->wants_to_be_visible()); - return; - } - - // The parent relationship should have been removed when the child was hidden. - DCHECK(![child_->ns_window() parentWindow]); - if (!child_->wants_to_be_visible()) - return; - - [child_->ns_window() orderWindow:NSWindowAbove - relativeTo:[anchor_window_ windowNumber]]; - - // Ordering the window should add back the relationship (unless it's a sheet). - DCHECK([child_->ns_window() isSheet] != !![child_->ns_window() parentWindow]); - DCHECK(child_->window_visible()); -} - -NSWindow* WidgetOwnerNSWindowAdapter::GetNSWindow() { - return anchor_window_; -} - -gfx::Vector2d WidgetOwnerNSWindowAdapter::GetChildWindowOffset() const { - NSRect rect_in_window = - [anchor_view_ convertRect:[anchor_view_ bounds] toView:nil]; - NSRect rect_in_screen = [anchor_window_ convertRectToScreen:rect_in_window]; - // Ensure we anchor off the top-left of |anchor_view_| (rect_in_screen.origin - // is the bottom-left of the view). - NSPoint anchor_in_screen = - NSMakePoint(NSMinX(rect_in_screen), NSMaxY(rect_in_screen)); - return gfx::ScreenPointFromNSPoint(anchor_in_screen).OffsetFromOrigin(); -} - -bool WidgetOwnerNSWindowAdapter::IsVisibleParent() const { - return [anchor_window_ isVisible]; -} - -void WidgetOwnerNSWindowAdapter::RemoveChildWindow(BridgedNativeWidget* child) { - DCHECK_EQ(child, child_); - [GetNSWindow() removeChildWindow:child->ns_window()]; - delete this; -} - -WidgetOwnerNSWindowAdapter::~WidgetOwnerNSWindowAdapter() { - [[NSNotificationCenter defaultCenter] removeObserver:observer_bridge_]; -} - -} // namespace views diff --git a/chromium/ui/views/color_chooser/color_chooser_view.cc b/chromium/ui/views/color_chooser/color_chooser_view.cc index d844b6a58f2..19eab2ec3d5 100644 --- a/chromium/ui/views/color_chooser/color_chooser_view.cc +++ b/chromium/ui/views/color_chooser/color_chooser_view.cc @@ -15,10 +15,12 @@ #include "cc/paint/paint_shader.h" #include "third_party/skia/include/core/SkPath.h" #include "third_party/skia/include/effects/SkGradientShader.h" +#include "ui/base/l10n/l10n_util.h" #include "ui/events/event.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/canvas.h" #include "ui/gfx/geometry/insets.h" +#include "ui/strings/grit/ui_strings.h" #include "ui/views/background.h" #include "ui/views/border.h" #include "ui/views/color_chooser/color_chooser_listener.h" @@ -389,6 +391,8 @@ ColorChooserView::ColorChooserView(ColorChooserListener* listener, textfield_ = new Textfield(); textfield_->set_controller(this); textfield_->SetDefaultWidthInChars(kTextfieldLengthInChars); + textfield_->SetAccessibleName( + l10n_util::GetStringUTF16(IDS_APP_ACCNAME_COLOR_CHOOSER_HEX_INPUT)); layout->AddView(textfield_); selected_color_patch_ = new SelectedColorPatchView(); layout->AddView(selected_color_patch_); diff --git a/chromium/ui/views/controls/animated_image_view.cc b/chromium/ui/views/controls/animated_image_view.cc new file mode 100644 index 00000000000..2d56a88b440 --- /dev/null +++ b/chromium/ui/views/controls/animated_image_view.cc @@ -0,0 +1,139 @@ +// 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/views/controls/animated_image_view.h" + +#include <utility> + +#include "base/logging.h" +#include "ui/gfx/canvas.h" +#include "ui/gfx/skottie_wrapper.h" +#include "ui/views/widget/widget.h" + +namespace views { +namespace { + +bool AreAnimatedImagesEqual(const gfx::SkiaVectorAnimation& animation_1, + const gfx::SkiaVectorAnimation& animation_2) { + // In rare cases this may return false, even if the animated images are backed + // by the same resource file. + return animation_1.skottie() == animation_2.skottie(); +} + +} // namespace + +AnimatedImageView::AnimatedImageView() = default; + +AnimatedImageView::~AnimatedImageView() = default; + +void AnimatedImageView::SetAnimatedImage( + std::unique_ptr<gfx::SkiaVectorAnimation> animated_image) { + if (animated_image_ && + AreAnimatedImagesEqual(*animated_image, *animated_image_)) { + Stop(); + return; + } + + gfx::Size preferred_size(GetPreferredSize()); + animated_image_ = std::move(animated_image); + + // Stop the animation to reset it. + Stop(); + + if (preferred_size != GetPreferredSize()) + PreferredSizeChanged(); + SchedulePaint(); +} + +void AnimatedImageView::Play() { + DCHECK(animated_image_); + DCHECK_EQ(state_, State::kStopped); + + state_ = State::kPlaying; + + // We cannot play the animation unless we have a valid compositor. + if (!compositor_) + return; + + // Ensure the class is added as an observer to receive clock ticks. + if (!compositor_->HasAnimationObserver(this)) + compositor_->AddAnimationObserver(this); + + animated_image_->Start(); +} + +void AnimatedImageView::Stop() { + DCHECK(animated_image_); + if (compositor_) + compositor_->RemoveAnimationObserver(this); + + animated_image_->Stop(); + state_ = State::kStopped; +} + +gfx::Size AnimatedImageView::GetImageSize() const { + return image_size_.value_or( + animated_image_ ? animated_image_->GetOriginalSize() : gfx::Size()); +} + +void AnimatedImageView::OnPaint(gfx::Canvas* canvas) { + View::OnPaint(canvas); + if (!animated_image_) + return; + canvas->Save(); + canvas->Translate(GetImageBounds().origin().OffsetFromOrigin()); + + // OnPaint may be called before clock tick was received; in that case just + // paint the first frame. + if (!previous_timestamp_.is_null() && state_ != State::kStopped) + animated_image_->Paint(canvas, previous_timestamp_, GetImageSize()); + else + animated_image_->PaintFrame(canvas, 0, GetImageSize()); + + canvas->Restore(); +} + +const char* AnimatedImageView::GetClassName() const { + return "AnimatedImageView"; +} + +void AnimatedImageView::NativeViewHierarchyChanged() { + // When switching a window from one display to another, the compositor + // associated with the widget changes. + AddedToWidget(); +} + +void AnimatedImageView::AddedToWidget() { + ui::Compositor* compositor = GetWidget()->GetCompositor(); + DCHECK(compositor); + if (compositor_ != compositor) { + if (compositor_ && compositor_->HasAnimationObserver(this)) + compositor_->RemoveAnimationObserver(this); + compositor_ = compositor; + } +} + +void AnimatedImageView::RemovedFromWidget() { + if (compositor_) { + Stop(); + if (compositor_->HasAnimationObserver(this)) + compositor_->RemoveAnimationObserver(this); + compositor_ = nullptr; + } +} + +void AnimatedImageView::OnAnimationStep(base::TimeTicks timestamp) { + previous_timestamp_ = timestamp; + SchedulePaint(); +} + +void AnimatedImageView::OnCompositingShuttingDown(ui::Compositor* compositor) { + if (compositor_ == compositor) { + Stop(); + compositor_->RemoveAnimationObserver(this); + compositor_ = nullptr; + } +} + +} // namespace views diff --git a/chromium/ui/views/controls/animated_image_view.h b/chromium/ui/views/controls/animated_image_view.h new file mode 100644 index 00000000000..c62fe5c3723 --- /dev/null +++ b/chromium/ui/views/controls/animated_image_view.h @@ -0,0 +1,91 @@ +// 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_VIEWS_CONTROLS_ANIMATED_IMAGE_VIEW_H_ +#define UI_VIEWS_CONTROLS_ANIMATED_IMAGE_VIEW_H_ + +#include <memory> + +#include "base/macros.h" +#include "ui/gfx/skia_vector_animation.h" +#include "ui/views/controls/image_view_base.h" + +namespace gfx { +class SkiaVectorAnimation; +class Canvas; +} // namespace gfx + +namespace ui { +class Compositor; +} + +namespace views { + +///////////////////////////////////////////////////////////////////////////// +// +// AnimatedImageView class. +// +// An AnimatedImageView can display a skia vector animation. The animation paint +// size can be set via SetImageSize. The animation is stopped by default. +// Use this over AnimatedIconView if you want to play a skottie animation file. +// +///////////////////////////////////////////////////////////////////////////// +class VIEWS_EXPORT AnimatedImageView : public ImageViewBase, + public ui::CompositorAnimationObserver { + public: + enum class State { + kPlaying, // The animation is currently playing. + kStopped // The animation is stopped and paint will raster the first + // frame. + }; + + AnimatedImageView(); + ~AnimatedImageView() override; + + // Set the animated image that should be displayed. Setting an animated image + // will result in stopping the current animation. + void SetAnimatedImage( + std::unique_ptr<gfx::SkiaVectorAnimation> animated_image); + + // Plays the animation in loop. + void Play(); + + // Stops any animation and resets it to the start frame. + void Stop(); + + private: + friend class AnimatedImageViewTest; + + // Overridden from View: + void OnPaint(gfx::Canvas* canvas) override; + const char* GetClassName() const override; + void NativeViewHierarchyChanged() override; + void AddedToWidget() override; + void RemovedFromWidget() override; + + // Overridden from ui::CompositorAnimationObserver: + void OnAnimationStep(base::TimeTicks timestamp) override; + void OnCompositingShuttingDown(ui::Compositor* compositor) override; + + // Overridden from ImageViewBase: + gfx::Size GetImageSize() const override; + + // The current state of the animation. + State state_ = State::kStopped; + + // The compositor associated with the widget of this view. + ui::Compositor* compositor_ = nullptr; + + // The most recent timestamp at which a paint was scheduled for this view. + base::TimeTicks previous_timestamp_; + + // The underlying skia vector animation. + std::unique_ptr<gfx::SkiaVectorAnimation> animated_image_; + + DISALLOW_COPY_AND_ASSIGN(AnimatedImageView); +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_ANIMATED_IMAGE_VIEW_H_ diff --git a/chromium/ui/views/controls/button/button.cc b/chromium/ui/views/controls/button/button.cc index 9656e5dc4a3..ebf086404e3 100644 --- a/chromium/ui/views/controls/button/button.cc +++ b/chromium/ui/views/controls/button/button.cc @@ -170,6 +170,12 @@ void Button::SetFocusPainter(std::unique_ptr<Painter> focus_painter) { focus_painter_ = std::move(focus_painter); } +void Button::SetHighlighted(bool bubble_visible) { + AnimateInkDrop(bubble_visible ? views::InkDropState::ACTIVATED + : views::InkDropState::DEACTIVATED, + nullptr); +} + //////////////////////////////////////////////////////////////////////////////// // Button, View overrides: diff --git a/chromium/ui/views/controls/button/button.h b/chromium/ui/views/controls/button/button.h index b5347ff0e00..050512ffa34 100644 --- a/chromium/ui/views/controls/button/button.h +++ b/chromium/ui/views/controls/button/button.h @@ -151,6 +151,9 @@ class VIEWS_EXPORT Button : public InkDropHostView, void SetFocusPainter(std::unique_ptr<Painter> focus_painter); + // Highlights the ink drop for the button. + void SetHighlighted(bool bubble_visible); + // Overridden from View: void OnEnabledChanged() override; const char* GetClassName() const override; diff --git a/chromium/ui/views/controls/button/checkbox.cc b/chromium/ui/views/controls/button/checkbox.cc index 91e398ebfe3..1a84d97ab26 100644 --- a/chromium/ui/views/controls/button/checkbox.cc +++ b/chromium/ui/views/controls/button/checkbox.cc @@ -55,7 +55,10 @@ Checkbox::~Checkbox() { } void Checkbox::SetChecked(bool checked) { - checked_ = checked; + if (checked_ != checked) { + checked_ = checked; + NotifyAccessibilityEvent(ax::mojom::Event::kCheckedStateChanged, true); + } UpdateImage(); } diff --git a/chromium/ui/views/controls/button/image_button_factory.cc b/chromium/ui/views/controls/button/image_button_factory.cc index 649002d85ad..baf33d4ef8d 100644 --- a/chromium/ui/views/controls/button/image_button_factory.cc +++ b/chromium/ui/views/controls/button/image_button_factory.cc @@ -31,7 +31,8 @@ void SetImageFromVectorIcon(ImageButton* button, SkColor related_text_color) { const SkColor icon_color = color_utils::DeriveDefaultIconColor(related_text_color); - const SkColor disabled_color = SkColorSetA(icon_color, 0xff / 2); + const SkColor disabled_color = + SkColorSetA(icon_color, gfx::kDisabledControlAlpha); button->SetImage(Button::STATE_NORMAL, gfx::CreateVectorIcon(icon, icon_color)); button->SetImage(Button::STATE_DISABLED, diff --git a/chromium/ui/views/controls/button/label_button_unittest.cc b/chromium/ui/views/controls/button/label_button_unittest.cc index 4a1ac7683d6..966ab6fec75 100644 --- a/chromium/ui/views/controls/button/label_button_unittest.cc +++ b/chromium/ui/views/controls/button/label_button_unittest.cc @@ -543,8 +543,6 @@ class InkDropLabelButtonTest : public ViewsTestBase { // ViewsTestBase: void SetUp() override { - base::CommandLine::ForCurrentProcess()->AppendSwitchASCII( - switches::kTopChromeMD, switches::kTopChromeMDMaterial); ViewsTestBase::SetUp(); // Create a widget so that the Button can query the hover state @@ -568,7 +566,6 @@ class InkDropLabelButtonTest : public ViewsTestBase { void TearDown() override { widget_.reset(); ViewsTestBase::TearDown(); - ui::test::MaterialDesignControllerTestAPI::Uninitialize(); } protected: diff --git a/chromium/ui/views/controls/button/md_text_button.cc b/chromium/ui/views/controls/button/md_text_button.cc index 4e6011dc24e..0d9f69cab39 100644 --- a/chromium/ui/views/controls/button/md_text_button.cc +++ b/chromium/ui/views/controls/button/md_text_button.cc @@ -7,7 +7,6 @@ #include "base/i18n/case_conversion.h" #include "base/memory/ptr_util.h" #include "build/build_config.h" -#include "ui/base/material_design/material_design_controller.h" #include "ui/gfx/canvas.h" #include "ui/gfx/color_palette.h" #include "ui/gfx/color_utils.h" @@ -197,12 +196,9 @@ void MdTextButton::UpdatePadding() { style::GetFont(style::CONTEXT_BUTTON_MD, style::STYLE_PRIMARY) .GetFontSize(); // TODO(tapted): This should get |target_height| using LayoutProvider:: - // GetControlHeightForFont(). It can't because that only returns a correct - // result with --secondary-ui-md, and MdTextButtons appear in top chrome - // without that. - const int base_height = - ui::MaterialDesignController::IsNewerMaterialUi() ? 32 : 28; - int target_height = std::max(base_height + size_delta * 2, + // GetControlHeightForFont(). + constexpr int kBaseHeight = 32; + int target_height = std::max(kBaseHeight + size_delta * 2, label()->font_list().GetFontSize() * 2); int label_height = label()->GetPreferredSize().height(); diff --git a/chromium/ui/views/controls/button/radio_button.cc b/chromium/ui/views/controls/button/radio_button.cc index 9adf4949221..6113742ab7f 100644 --- a/chromium/ui/views/controls/button/radio_button.cc +++ b/chromium/ui/views/controls/button/radio_button.cc @@ -99,7 +99,7 @@ void RadioButton::SetChecked(bool checked) { if (container) { Views other; container->GetViewsInGroup(GetGroup(), &other); - for (Views::iterator i(other.begin()); i != other.end(); ++i) { + for (auto i(other.begin()); i != other.end(); ++i) { if (*i != this) { if (strcmp((*i)->GetClassName(), kViewClassName)) { NOTREACHED() << "radio-button-nt has same group as other non " diff --git a/chromium/ui/views/controls/focus_ring.cc b/chromium/ui/views/controls/focus_ring.cc index 148c22ae909..bc9597596ce 100644 --- a/chromium/ui/views/controls/focus_ring.cc +++ b/chromium/ui/views/controls/focus_ring.cc @@ -7,6 +7,7 @@ #include "ui/gfx/canvas.h" #include "ui/views/controls/focusable_border.h" #include "ui/views/style/platform_style.h" +#include "ui/views/view_properties.h" namespace { @@ -79,6 +80,11 @@ void FocusRing::OnPaint(gfx::Canvas* canvas) { paint.setStrokeWidth(PlatformStyle::kFocusHaloThickness); SkPath path = path_; + if (path.isEmpty()) { + gfx::Path* highlight_path = parent()->GetProperty(kHighlightPathKey); + if (highlight_path) + path = *highlight_path; + } if (path.isEmpty()) path.addRect(RectToSkRect(parent()->GetLocalBounds())); diff --git a/chromium/ui/views/controls/focus_ring.h b/chromium/ui/views/controls/focus_ring.h index a608042b24f..1627eda9347 100644 --- a/chromium/ui/views/controls/focus_ring.h +++ b/chromium/ui/views/controls/focus_ring.h @@ -58,6 +58,9 @@ class VIEWS_EXPORT FocusRing : public View, public ViewObserver { // view's coordinate system, *not* in the FocusRing's coordinate system. Note // that this path will not be mirrored in RTL, so your View's computation of // it should take RTL into account. + // Note: This method should only be used if the focus ring needs to differ + // from the highlight shape used for inkdrops. Otherwise set kHighlightPathKey + // on the parent and FocusRing will use it as well. void SetPath(const SkPath& path); // Sets whether the FocusRing should show an invalid state for the View it diff --git a/chromium/ui/views/controls/image_view.cc b/chromium/ui/views/controls/image_view.cc index b1abacf1e0d..2e9c4062526 100644 --- a/chromium/ui/views/controls/image_view.cc +++ b/chromium/ui/views/controls/image_view.cc @@ -7,12 +7,9 @@ #include <utility> #include "base/logging.h" -#include "base/strings/utf_string_conversions.h" #include "cc/paint/paint_flags.h" #include "skia/ext/image_operations.h" -#include "ui/accessibility/ax_node_data.h" #include "ui/gfx/canvas.h" -#include "ui/gfx/geometry/insets.h" namespace views { @@ -21,7 +18,7 @@ namespace { // Returns the pixels for the bitmap in |image| at scale |image_scale|. void* GetBitmapPixels(const gfx::ImageSkia& img, float image_scale) { DCHECK_NE(0.0f, image_scale); - return img.GetRepresentation(image_scale).sk_bitmap().getPixels(); + return img.GetRepresentation(image_scale).GetBitmap().getPixels(); } } // namespace @@ -29,13 +26,9 @@ void* GetBitmapPixels(const gfx::ImageSkia& img, float image_scale) { // static const char ImageView::kViewClassName[] = "ImageView"; -ImageView::ImageView() - : horizontal_alignment_(CENTER), - vertical_alignment_(CENTER), - last_paint_scale_(0.f), - last_painted_bitmap_pixels_(nullptr) {} +ImageView::ImageView() = default; -ImageView::~ImageView() {} +ImageView::~ImageView() = default; void ImageView::SetImage(const gfx::ImageSkia& img) { if (IsImageEqual(img)) @@ -63,20 +56,6 @@ const gfx::ImageSkia& ImageView::GetImage() const { return image_; } -void ImageView::SetImageSize(const gfx::Size& image_size) { - image_size_ = image_size; - PreferredSizeChanged(); -} - -gfx::Rect ImageView::GetImageBounds() const { - gfx::Size image_size = GetImageSize(); - return gfx::Rect(ComputeImageOrigin(image_size), image_size); -} - -void ImageView::ResetImageSize() { - image_size_.reset(); -} - bool ImageView::IsImageEqual(const gfx::ImageSkia& img) const { // Even though we copy ImageSkia in SetImage() the backing store // (ImageSkiaStorage) is not copied and may have changed since the last call @@ -92,119 +71,15 @@ gfx::Size ImageView::GetImageSize() const { return image_size_.value_or(image_.size()); } -gfx::Point ImageView::ComputeImageOrigin(const gfx::Size& image_size) const { - gfx::Insets insets = GetInsets(); - - int x = 0; - // In order to properly handle alignment of images in RTL locales, we need - // to flip the meaning of trailing and leading. For example, if the - // horizontal alignment is set to trailing, then we'll use left alignment for - // the image instead of right alignment if the UI layout is RTL. - Alignment actual_horizontal_alignment = horizontal_alignment_; - if (base::i18n::IsRTL() && (horizontal_alignment_ != CENTER)) { - actual_horizontal_alignment = - (horizontal_alignment_ == LEADING) ? TRAILING : LEADING; - } - switch (actual_horizontal_alignment) { - case LEADING: - x = insets.left(); - break; - case TRAILING: - x = width() - insets.right() - image_size.width(); - break; - case CENTER: - x = (width() - insets.width() - image_size.width()) / 2 + insets.left(); - break; - } - - int y = 0; - switch (vertical_alignment_) { - case LEADING: - y = insets.top(); - break; - case TRAILING: - y = height() - insets.bottom() - image_size.height(); - break; - case CENTER: - y = (height() - insets.height() - image_size.height()) / 2 + insets.top(); - break; - } - - return gfx::Point(x, y); -} - void ImageView::OnPaint(gfx::Canvas* canvas) { View::OnPaint(canvas); OnPaintImage(canvas); } -void ImageView::GetAccessibleNodeData(ui::AXNodeData* node_data) { - node_data->role = ax::mojom::Role::kImage; - node_data->SetName(tooltip_text_); -} - const char* ImageView::GetClassName() const { return kViewClassName; } -void ImageView::SetHorizontalAlignment(Alignment alignment) { - if (alignment != horizontal_alignment_) { - horizontal_alignment_ = alignment; - SchedulePaint(); - } -} - -ImageView::Alignment ImageView::GetHorizontalAlignment() const { - return horizontal_alignment_; -} - -void ImageView::SetVerticalAlignment(Alignment alignment) { - if (alignment != vertical_alignment_) { - vertical_alignment_ = alignment; - SchedulePaint(); - } -} - -ImageView::Alignment ImageView::GetVerticalAlignment() const { - return vertical_alignment_; -} - -void ImageView::SetTooltipText(const base::string16& tooltip) { - tooltip_text_ = tooltip; -} - -base::string16 ImageView::GetTooltipText() const { - return tooltip_text_; -} - -bool ImageView::GetTooltipText(const gfx::Point& p, - base::string16* tooltip) const { - if (tooltip_text_.empty()) - return false; - - *tooltip = GetTooltipText(); - return true; -} - -gfx::Size ImageView::CalculatePreferredSize() const { - gfx::Size size = GetImageSize(); - size.Enlarge(GetInsets().width(), GetInsets().height()); - return size; -} - -views::PaintInfo::ScaleType ImageView::GetPaintScaleType() const { - // ImageView contains an image which is rastered at the device scale factor. - // By default, the paint commands are recorded at a scale factor slightly - // different from the device scale factor. Re-rastering the image at this - // paint recording scale will result in a distorted image. Paint recording - // scale might also not be uniform along the x & y axis, thus resulting in - // further distortion in the aspect ratio of the final image. - // |kUniformScaling| ensures that the paint recording scale is uniform along - // the x & y axis and keeps the scale equal to the device scale factor. - // See http://crbug.com/754010 for more details. - return views::PaintInfo::ScaleType::kUniformScaling; -} - void ImageView::OnPaintImage(gfx::Canvas* canvas) { last_paint_scale_ = canvas->image_scale(); last_painted_bitmap_pixels_ = nullptr; @@ -248,7 +123,7 @@ gfx::ImageSkia ImageView::GetPaintImage(float scale) { gfx::Size scaled_size = gfx::ScaleToCeiledSize(rep.pixel_size(), scale / rep.scale()); scaled_image_.AddRepresentation(gfx::ImageSkiaRep( - skia::ImageOperations::Resize(rep.sk_bitmap(), + skia::ImageOperations::Resize(rep.GetBitmap(), skia::ImageOperations::RESIZE_BEST, scaled_size.width(), scaled_size.height()), scale)); diff --git a/chromium/ui/views/controls/image_view.h b/chromium/ui/views/controls/image_view.h index c3adf654e36..e80a97a34a1 100644 --- a/chromium/ui/views/controls/image_view.h +++ b/chromium/ui/views/controls/image_view.h @@ -6,9 +6,8 @@ #define UI_VIEWS_CONTROLS_IMAGE_VIEW_H_ #include "base/macros.h" -#include "base/optional.h" #include "ui/gfx/image/image_skia.h" -#include "ui/views/view.h" +#include "ui/views/controls/image_view_base.h" namespace gfx { class Canvas; @@ -26,17 +25,11 @@ namespace views { // provided image size. // ///////////////////////////////////////////////////////////////////////////// -class VIEWS_EXPORT ImageView : public View { +class VIEWS_EXPORT ImageView : public ImageViewBase { public: // Internal class name. static const char kViewClassName[]; - enum Alignment { - LEADING = 0, - CENTER, - TRAILING - }; - ImageView(); ~ImageView() override; @@ -52,35 +45,13 @@ class VIEWS_EXPORT ImageView : public View { // The returned image is still owned by the ImageView. const gfx::ImageSkia& GetImage() const; - // Set the desired image size for the receiving ImageView. - void SetImageSize(const gfx::Size& image_size); - - // Returns the actual bounds of the visible image inside the view. - gfx::Rect GetImageBounds() const; - - // Reset the image size to the current image dimensions. - void ResetImageSize(); - - // Set / Get the horizontal alignment. - void SetHorizontalAlignment(Alignment ha); - Alignment GetHorizontalAlignment() const; - - // Set / Get the vertical alignment. - void SetVerticalAlignment(Alignment va); - Alignment GetVerticalAlignment() const; - - // Set / Get the tooltip text. - void SetTooltipText(const base::string16& tooltip); - base::string16 GetTooltipText() const; - - // Overriden from View: + // Overridden from View: void OnPaint(gfx::Canvas* canvas) override; - void GetAccessibleNodeData(ui::AXNodeData* node_data) override; const char* GetClassName() const override; - bool GetTooltipText(const gfx::Point& p, - base::string16* tooltip) const override; - gfx::Size CalculatePreferredSize() const override; - views::PaintInfo::ScaleType GetPaintScaleType() const override; + + protected: + // Overridden from ImageViewBase: + gfx::Size GetImageSize() const override; private: friend class ImageViewTest; @@ -95,37 +66,18 @@ class VIEWS_EXPORT ImageView : public View { // for this to return false even though the images are in fact equal. bool IsImageEqual(const gfx::ImageSkia& img) const; - // Returns the size the image will be painted. - gfx::Size GetImageSize() const; - - // Compute the image origin given the desired size and the receiver alignment - // properties. - gfx::Point ComputeImageOrigin(const gfx::Size& image_size) const; - - // The actual image size. - base::Optional<gfx::Size> image_size_; - // The underlying image. gfx::ImageSkia image_; // Caches the scaled image reps. gfx::ImageSkia scaled_image_; - // Horizontal alignment. - Alignment horizontal_alignment_; - - // Vertical alignment. - Alignment vertical_alignment_; - - // The current tooltip text. - base::string16 tooltip_text_; - // Scale last painted at. - float last_paint_scale_; + float last_paint_scale_ = 0.f; // Address of bytes we last painted. This is used only for comparison, so its // safe to cache. - void* last_painted_bitmap_pixels_; + void* last_painted_bitmap_pixels_ = nullptr; DISALLOW_COPY_AND_ASSIGN(ImageView); }; diff --git a/chromium/ui/views/controls/image_view_base.cc b/chromium/ui/views/controls/image_view_base.cc new file mode 100644 index 00000000000..d898798239f --- /dev/null +++ b/chromium/ui/views/controls/image_view_base.cc @@ -0,0 +1,160 @@ +// 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/views/controls/image_view_base.h" + +#include <utility> + +#include "base/logging.h" +#include "ui/accessibility/ax_node_data.h" +#include "ui/gfx/geometry/insets.h" + +namespace views { + +ImageViewBase::ImageViewBase() = default; + +ImageViewBase::~ImageViewBase() = default; + +void ImageViewBase::SetImageSize(const gfx::Size& image_size) { + image_size_ = image_size; + PreferredSizeChanged(); +} + +gfx::Rect ImageViewBase::GetImageBounds() const { + return gfx::Rect(image_origin_, GetImageSize()); +} + +void ImageViewBase::ResetImageSize() { + image_size_.reset(); + PreferredSizeChanged(); +} + +void ImageViewBase::GetAccessibleNodeData(ui::AXNodeData* node_data) { + node_data->role = ax::mojom::Role::kImage; + node_data->SetName(accessible_name_); +} + +void ImageViewBase::SetHorizontalAlignment(Alignment alignment) { + if (alignment != horizontal_alignment_) { + horizontal_alignment_ = alignment; + UpdateImageOrigin(); + SchedulePaint(); + } +} + +ImageViewBase::Alignment ImageViewBase::GetHorizontalAlignment() const { + return horizontal_alignment_; +} + +void ImageViewBase::SetVerticalAlignment(Alignment alignment) { + if (alignment != vertical_alignment_) { + vertical_alignment_ = alignment; + UpdateImageOrigin(); + SchedulePaint(); + } +} + +ImageViewBase::Alignment ImageViewBase::GetVerticalAlignment() const { + return vertical_alignment_; +} + +void ImageViewBase::SetAccessibleName(const base::string16& accessible_name) { + accessible_name_ = accessible_name; +} + +base::string16 ImageViewBase::GetAccessibleName() const { + return accessible_name_; +} + +// TODO(crbug.com/890465): Update the duplicate code here and in views::Button. +void ImageViewBase::SetTooltipText(const base::string16& tooltip) { + tooltip_text_ = tooltip; + if (accessible_name_.empty()) + accessible_name_ = tooltip_text_; +} + +base::string16 ImageViewBase::GetTooltipText() const { + return tooltip_text_; +} + +bool ImageViewBase::GetTooltipText(const gfx::Point& p, + base::string16* tooltip) const { + if (tooltip_text_.empty()) + return false; + + *tooltip = GetTooltipText(); + return true; +} + +gfx::Size ImageViewBase::CalculatePreferredSize() const { + gfx::Size size = GetImageSize(); + size.Enlarge(GetInsets().width(), GetInsets().height()); + return size; +} + +views::PaintInfo::ScaleType ImageViewBase::GetPaintScaleType() const { + // ImageViewBase contains an image which is rastered at the device scale + // factor. By default, the paint commands are recorded at a scale factor + // slightly different from the device scale factor. Re-rastering the image at + // this paint recording scale will result in a distorted image. Paint + // recording scale might also not be uniform along the x & y axis, thus + // resulting in further distortion in the aspect ratio of the final image. + // |kUniformScaling| ensures that the paint recording scale is uniform along + // the x & y axis and keeps the scale equal to the device scale factor. + // See http://crbug.com/754010 for more details. + return views::PaintInfo::ScaleType::kUniformScaling; +} + +void ImageViewBase::OnBoundsChanged(const gfx::Rect& previous_bounds) { + UpdateImageOrigin(); +} + +void ImageViewBase::UpdateImageOrigin() { + gfx::Size image_size = GetImageSize(); + gfx::Insets insets = GetInsets(); + + int x = 0; + // In order to properly handle alignment of images in RTL locales, we need + // to flip the meaning of trailing and leading. For example, if the + // horizontal alignment is set to trailing, then we'll use left alignment for + // the image instead of right alignment if the UI layout is RTL. + Alignment actual_horizontal_alignment = horizontal_alignment_; + if (base::i18n::IsRTL() && (horizontal_alignment_ != CENTER)) { + actual_horizontal_alignment = + (horizontal_alignment_ == LEADING) ? TRAILING : LEADING; + } + switch (actual_horizontal_alignment) { + case LEADING: + x = insets.left(); + break; + case TRAILING: + x = width() - insets.right() - image_size.width(); + break; + case CENTER: + x = (width() - insets.width() - image_size.width()) / 2 + insets.left(); + break; + } + + int y = 0; + switch (vertical_alignment_) { + case LEADING: + y = insets.top(); + break; + case TRAILING: + y = height() - insets.bottom() - image_size.height(); + break; + case CENTER: + y = (height() - insets.height() - image_size.height()) / 2 + insets.top(); + break; + } + + image_origin_ = gfx::Point(x, y); +} + +void ImageViewBase::PreferredSizeChanged() { + View::PreferredSizeChanged(); + UpdateImageOrigin(); +} + +} // namespace views diff --git a/chromium/ui/views/controls/image_view_base.h b/chromium/ui/views/controls/image_view_base.h new file mode 100644 index 00000000000..4764f2f4c47 --- /dev/null +++ b/chromium/ui/views/controls/image_view_base.h @@ -0,0 +1,94 @@ +// 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_VIEWS_CONTROLS_IMAGE_VIEW_BASE_H_ +#define UI_VIEWS_CONTROLS_IMAGE_VIEW_BASE_H_ + +#include "base/macros.h" +#include "base/optional.h" +#include "ui/views/view.h" + +namespace gfx { +class Canvas; +} + +namespace views { + +class VIEWS_EXPORT ImageViewBase : public View { + public: + enum Alignment { LEADING, CENTER, TRAILING }; + + ImageViewBase(); + ~ImageViewBase() override; + + // Set the desired image size for the receiving ImageView. + void SetImageSize(const gfx::Size& image_size); + + // Returns the actual bounds of the visible image inside the view. + gfx::Rect GetImageBounds() const; + + // Reset the image size to the current image dimensions. + void ResetImageSize(); + + // Set / Get the horizontal alignment. + void SetHorizontalAlignment(Alignment ha); + Alignment GetHorizontalAlignment() const; + + // Set / Get the vertical alignment. + void SetVerticalAlignment(Alignment va); + Alignment GetVerticalAlignment() const; + + // Set / Get the tooltip text. + void SetTooltipText(const base::string16& tooltip); + base::string16 GetTooltipText() const; + + // Set / Get the accessible name text. + void SetAccessibleName(const base::string16& name); + base::string16 GetAccessibleName() const; + + // Overridden from View: + void OnPaint(gfx::Canvas* canvas) override = 0; + void GetAccessibleNodeData(ui::AXNodeData* node_data) override; + const char* GetClassName() const override = 0; + bool GetTooltipText(const gfx::Point& p, + base::string16* tooltip) const override; + gfx::Size CalculatePreferredSize() const override; + views::PaintInfo::ScaleType GetPaintScaleType() const override; + void OnBoundsChanged(const gfx::Rect& previous_bounds) override; + void PreferredSizeChanged() override; + + protected: + // Returns the size the image will be painted. + virtual gfx::Size GetImageSize() const = 0; + + // The requested image size. + base::Optional<gfx::Size> image_size_; + + private: + friend class ImageViewTest; + + // Recomputes and updates the |image_origin_|. + void UpdateImageOrigin(); + + // The origin of the image. + gfx::Point image_origin_; + + // Horizontal alignment. + Alignment horizontal_alignment_ = Alignment::CENTER; + + // Vertical alignment. + Alignment vertical_alignment_ = Alignment::CENTER; + + // The current tooltip text. + base::string16 tooltip_text_; + + // The current accessible name text. + base::string16 accessible_name_; + + DISALLOW_COPY_AND_ASSIGN(ImageViewBase); +}; + +} // namespace views + +#endif // UI_VIEWS_CONTROLS_IMAGE_VIEW_BASE_H_ diff --git a/chromium/ui/views/controls/image_view_unittest.cc b/chromium/ui/views/controls/image_view_unittest.cc index 28cbb21460a..5e4d4a5576a 100644 --- a/chromium/ui/views/controls/image_view_unittest.cc +++ b/chromium/ui/views/controls/image_view_unittest.cc @@ -69,8 +69,8 @@ class ImageViewTest : public ViewsTestBase, } int CurrentImageOriginForParam() { - gfx::Point origin = - image_view()->ComputeImageOrigin(image_view()->GetImageSize()); + image_view()->UpdateImageOrigin(); + gfx::Point origin = image_view()->GetImageBounds().origin(); return GetParam() == Axis::kHorizontal ? origin.x() : origin.y(); } @@ -126,6 +126,21 @@ TEST_P(ImageViewTest, CenterAlignment) { EXPECT_EQ(kLeadingInset, CurrentImageOriginForParam()); } +TEST_P(ImageViewTest, ImageOriginForCustomViewBounds) { + gfx::Rect image_view_bounds(10, 10, 80, 80); + image_view()->SetHorizontalAlignment(ImageView::CENTER); + image_view()->SetBoundsRect(image_view_bounds); + + SkBitmap bitmap; + constexpr int kImageSkiaSize = 20; + bitmap.allocN32Pixels(kImageSkiaSize, kImageSkiaSize); + gfx::ImageSkia image_skia = gfx::ImageSkia::CreateFrom1xBitmap(bitmap); + image_view()->SetImage(image_skia); + + EXPECT_EQ(gfx::Point(30, 30), image_view()->GetImageBounds().origin()); + EXPECT_EQ(image_view_bounds, image_view()->bounds()); +} + INSTANTIATE_TEST_CASE_P(, ImageViewTest, ::testing::Values(Axis::kHorizontal, Axis::kVertical)); diff --git a/chromium/ui/views/controls/label_unittest.cc b/chromium/ui/views/controls/label_unittest.cc index d87acf7c42d..1ee2b444c42 100644 --- a/chromium/ui/views/controls/label_unittest.cc +++ b/chromium/ui/views/controls/label_unittest.cc @@ -20,7 +20,6 @@ #include "ui/events/test/event_generator.h" #include "ui/gfx/canvas.h" #include "ui/gfx/render_text.h" -#include "ui/gfx/switches.h" #include "ui/gfx/text_elider.h" #include "ui/strings/grit/ui_strings.h" #include "ui/views/border.h" @@ -163,14 +162,6 @@ class LabelSelectionTest : public LabelTest { // LabelTest overrides: void SetUp() override { -#if defined(OS_MACOSX) - // On Mac, by default RenderTextMac is used for labels which does not - // support text selection. Instead use RenderTextHarfBuzz for selection - // related tests. TODO(crbug.com/661394): Remove this once Mac also uses - // RenderTextHarfBuzz for Labels. - base::CommandLine::ForCurrentProcess()->AppendSwitch( - switches::kEnableHarfBuzzRenderText); -#endif LabelTest::SetUp(); event_generator_ = std::make_unique<ui::test::EventGenerator>(widget()->GetNativeWindow()); diff --git a/chromium/ui/views/controls/menu/menu_closure_animation_mac.h b/chromium/ui/views/controls/menu/menu_closure_animation_mac.h index 5c3b6d69aff..e966edd9888 100644 --- a/chromium/ui/views/controls/menu/menu_closure_animation_mac.h +++ b/chromium/ui/views/controls/menu/menu_closure_animation_mac.h @@ -14,6 +14,7 @@ namespace views { class MenuItemView; +class SubmenuView; // This class implements the Mac menu closure animation: // 1) For 100ms, the selected item is drawn as unselected @@ -24,11 +25,19 @@ class MenuItemView; // will stop the timer or animation (if they are running), so the callback will // *not* be run - which is good, since the MenuController that would have // received it is being deleted. +// +// This class also supports animating a menu away without animating the +// selection effect, which is achieved by passing nullptr for the item to +// animate. In this case, the animation skips straight to step 3 above. class VIEWS_EXPORT MenuClosureAnimationMac : public gfx::AnimationDelegate { public: - // After this closure animation is done, |callback| is run to finally accept - // |item|. - MenuClosureAnimationMac(MenuItemView* item, base::OnceClosure callback); + // After this closure animation is done, |callback| is run to finish + // dismissing the menu. If |item| is given, this will animate the item being + // accepted before animating the menu closing; if |item| is nullptr, only the + // menu closure will be animated. + MenuClosureAnimationMac(MenuItemView* item, + SubmenuView* menu, + base::OnceClosure callback); ~MenuClosureAnimationMac() override; // Start the animation. @@ -37,6 +46,9 @@ class VIEWS_EXPORT MenuClosureAnimationMac : public gfx::AnimationDelegate { // Returns the MenuItemView this animation targets. MenuItemView* item() { return item_; } + // Returns the SubmenuView this animation targets. + SubmenuView* menu() { return menu_; } + // Causes animations to take no time for testing purposes. Note that this // still causes the completion callback to be run asynchronously, so test // situations have the same control flow as non-test situations. @@ -51,7 +63,7 @@ class VIEWS_EXPORT MenuClosureAnimationMac : public gfx::AnimationDelegate { kFinish, }; - static constexpr AnimationStep NextStepFor(AnimationStep step); + AnimationStep NextStepFor(AnimationStep step) const; void AdvanceAnimation(); @@ -64,6 +76,7 @@ class VIEWS_EXPORT MenuClosureAnimationMac : public gfx::AnimationDelegate { base::OneShotTimer timer_; std::unique_ptr<gfx::Animation> fade_animation_; MenuItemView* item_; + SubmenuView* menu_; AnimationStep step_; DISALLOW_COPY_AND_ASSIGN(MenuClosureAnimationMac); diff --git a/chromium/ui/views/controls/menu/menu_closure_animation_mac.mm b/chromium/ui/views/controls/menu/menu_closure_animation_mac.mm index 2bec6d31087..32127db42d9 100644 --- a/chromium/ui/views/controls/menu/menu_closure_animation_mac.mm +++ b/chromium/ui/views/controls/menu/menu_closure_animation_mac.mm @@ -10,6 +10,7 @@ #include "base/threading/thread_task_runner_handle.h" #include "ui/gfx/animation/linear_animation.h" #include "ui/views/controls/menu/menu_item_view.h" +#include "ui/views/controls/menu/submenu_view.h" #include "ui/views/widget/widget.h" namespace { @@ -19,9 +20,11 @@ static bool g_disable_animations_for_testing = false; namespace views { MenuClosureAnimationMac::MenuClosureAnimationMac(MenuItemView* item, + SubmenuView* menu, base::OnceClosure callback) : callback_(std::move(callback)), item_(item), + menu_(menu), step_(AnimationStep::kStart) {} MenuClosureAnimationMac::~MenuClosureAnimationMac() {} @@ -42,12 +45,11 @@ void MenuClosureAnimationMac::Start() { } // static -constexpr MenuClosureAnimationMac::AnimationStep -MenuClosureAnimationMac::NextStepFor( - MenuClosureAnimationMac::AnimationStep step) { +MenuClosureAnimationMac::AnimationStep MenuClosureAnimationMac::NextStepFor( + MenuClosureAnimationMac::AnimationStep step) const { switch (step) { case AnimationStep::kStart: - return AnimationStep::kUnselected; + return item_ ? AnimationStep::kUnselected : AnimationStep::kFading; case AnimationStep::kUnselected: return AnimationStep::kSelected; case AnimationStep::kSelected: @@ -84,7 +86,7 @@ void MenuClosureAnimationMac::DisableAnimationsForTesting() { void MenuClosureAnimationMac::AnimationProgressed( const gfx::Animation* animation) { - NSWindow* window = item_->GetWidget()->GetNativeWindow(); + NSWindow* window = menu_->GetWidget()->GetNativeWindow(); [window setAlphaValue:animation->CurrentValueBetween(1.0, 0.0)]; } diff --git a/chromium/ui/views/controls/menu/menu_config.cc b/chromium/ui/views/controls/menu/menu_config.cc index 7ea5c0104bc..3dc6eef0d4f 100644 --- a/chromium/ui/views/controls/menu/menu_config.cc +++ b/chromium/ui/views/controls/menu/menu_config.cc @@ -5,6 +5,7 @@ #include "ui/views/controls/menu/menu_config.h" #include "base/macros.h" +#include "base/no_destructor.h" #include "ui/views/controls/menu/menu_controller.h" #include "ui/views/controls/menu/menu_image_util.h" #include "ui/views/controls/menu/menu_item_view.h" @@ -100,8 +101,8 @@ bool MenuConfig::ShouldShowAcceleratorText(const MenuItemView* item, // static const MenuConfig& MenuConfig::instance() { - CR_DEFINE_STATIC_LOCAL(MenuConfig, instance, ()); - return instance; + static base::NoDestructor<MenuConfig> instance; + return *instance; } } // namespace views diff --git a/chromium/ui/views/controls/menu/menu_config_win.cc b/chromium/ui/views/controls/menu/menu_config_win.cc index 6048ee062fd..72665510ca0 100644 --- a/chromium/ui/views/controls/menu/menu_config_win.cc +++ b/chromium/ui/views/controls/menu/menu_config_win.cc @@ -10,9 +10,8 @@ #include "base/logging.h" #include "base/win/scoped_gdi_object.h" -#include "base/win/win_client_metrics.h" -#include "ui/base/l10n/l10n_util_win.h" #include "ui/gfx/color_utils.h" +#include "ui/gfx/platform_font_win.h" #include "ui/native_theme/native_theme_win.h" using ui::NativeTheme; @@ -21,15 +20,9 @@ namespace views { void MenuConfig::Init() { arrow_color = color_utils::GetSysSkColor(COLOR_MENUTEXT); + font_list = gfx::FontList(gfx::PlatformFontWin::GetSystemFont( + gfx::PlatformFontWin::SystemFont::kMenu)); - NONCLIENTMETRICS_XP metrics; - base::win::GetNonClientMetrics(&metrics); - l10n_util::AdjustUIFont(&(metrics.lfMenuFont)); - { - base::win::ScopedHFONT new_font(CreateFontIndirect(&metrics.lfMenuFont)); - DLOG_ASSERT(new_font.is_valid()); - font_list = gfx::FontList(gfx::Font(new_font.get())); - } NativeTheme::ExtraParams extra; gfx::Size arrow_size = NativeTheme::GetInstanceForNativeUi()->GetPartSize( NativeTheme::kMenuPopupArrow, NativeTheme::kNormal, extra); diff --git a/chromium/ui/views/controls/menu/menu_controller.cc b/chromium/ui/views/controls/menu/menu_controller.cc index d6806061652..9e56ca54813 100644 --- a/chromium/ui/views/controls/menu/menu_controller.cc +++ b/chromium/ui/views/controls/menu/menu_controller.cc @@ -499,6 +499,10 @@ void MenuController::Run(Widget* parent, } void MenuController::Cancel(ExitType type) { +#if defined(OS_MACOSX) + menu_closure_animation_.reset(); +#endif + // If the menu has already been destroyed, no further cancellation is // needed. We especially don't want to set the |exit_type_| to a lesser // value. @@ -1529,7 +1533,7 @@ void MenuController::UpdateInitialLocation(const gfx::Rect& bounds, void MenuController::Accept(MenuItemView* item, int event_flags) { #if defined(OS_MACOSX) menu_closure_animation_ = std::make_unique<MenuClosureAnimationMac>( - item, + item, item->GetParentMenuItem()->GetSubmenu(), base::BindOnce(&MenuController::ReallyAccept, base::Unretained(this), base::Unretained(item), event_flags)); menu_closure_animation_->Start(); @@ -1845,8 +1849,7 @@ void MenuController::CommitPendingSelection() { // Open all the submenus preceeding the last menu item (last menu item is // handled next). if (new_path.size() > 1) { - for (std::vector<MenuItemView*>::iterator i = new_path.begin(); - i != new_path.end() - 1; ++i) { + for (auto i = new_path.begin(); i != new_path.end() - 1; ++i) { OpenMenu(*i); } } @@ -2070,10 +2073,9 @@ gfx::Rect MenuController::CalculateMenuBounds(MenuItemView* item, const int right_of_parent = item_loc.x() + item->width() - submenu_horizontal_inset; - int border_size = menu_config.CornerRadiusForMenu(this); - if (!border_size) - border_size = menu_config.menu_vertical_border_size; - menu_bounds.set_y(item_loc.y() - border_size); + MenuScrollViewContainer* container = + item->GetParentMenuItem()->GetSubmenu()->GetScrollViewContainer(); + menu_bounds.set_y(item_loc.y() - container->border()->GetInsets().top()); // Assume the menu can be placed in the preferred location. menu_bounds.set_x(create_on_right ? right_of_parent : left_of_parent); @@ -2609,7 +2611,18 @@ void MenuController::RepostEventAndCancel(SubmenuView* source, if (last_part.type != MenuPart::NONE) exit_type = EXIT_OUTERMOST; } +#if defined(OS_MACOSX) + SubmenuView* target = exit_type == EXIT_ALL + ? source + : state_.item->GetRootMenuItem()->GetSubmenu(); + menu_closure_animation_ = std::make_unique<MenuClosureAnimationMac>( + nullptr, target, + base::BindOnce(&MenuController::Cancel, base::Unretained(this), + exit_type)); + menu_closure_animation_->Start(); +#else Cancel(exit_type); +#endif } void MenuController::SetDropMenuItem(MenuItemView* new_target, diff --git a/chromium/ui/views/controls/menu/menu_controller_unittest.cc b/chromium/ui/views/controls/menu/menu_controller_unittest.cc index 57c22228ae9..ad8f6732a80 100644 --- a/chromium/ui/views/controls/menu/menu_controller_unittest.cc +++ b/chromium/ui/views/controls/menu/menu_controller_unittest.cc @@ -1232,6 +1232,7 @@ TEST_F(MenuControllerTest, PreserveGestureForOwner) { // Tests that touch outside menu does not closes the menu when forwarding // gesture events to owner. TEST_F(MenuControllerTest, NoTouchCloseWhenSendingGesturesToOwner) { + views::test::DisableMenuClosureAnimations(); MenuController* controller = menu_controller(); // Owner wants the gesture events. @@ -1259,6 +1260,7 @@ TEST_F(MenuControllerTest, NoTouchCloseWhenSendingGesturesToOwner) { // Touch outside again and menu should be closed. controller->OnTouchEvent(sub_menu, &touch_event); + views::test::WaitForMenuClosureAnimation(); EXPECT_FALSE(IsShowing()); EXPECT_EQ(MenuController::EXIT_ALL, controller->exit_type()); } @@ -1267,6 +1269,7 @@ TEST_F(MenuControllerTest, NoTouchCloseWhenSendingGesturesToOwner) { // occur outside of the bounds of the menu. Instead a proper shutdown should // occur. TEST_F(MenuControllerTest, AsynchronousRepostEvent) { + views::test::DisableMenuClosureAnimations(); MenuController* controller = menu_controller(); TestMenuControllerDelegate* delegate = menu_controller_delegate(); std::unique_ptr<TestMenuControllerDelegate> nested_delegate( @@ -1291,6 +1294,7 @@ TEST_F(MenuControllerTest, AsynchronousRepostEvent) { // When attempting to select outside of all menus this should lead to a // shutdown. This should not crash while attempting to repost the event. SetSelectionOnPointerDown(sub_menu, &event); + views::test::WaitForMenuClosureAnimation(); EXPECT_EQ(delegate, GetCurrentDelegate()); EXPECT_EQ(1, delegate->on_menu_closed_called()); @@ -1305,6 +1309,7 @@ TEST_F(MenuControllerTest, AsynchronousRepostEvent) { // Tests that an asynchronous menu reposts touch events that occur outside of // the bounds of the menu, and that the menu closes. TEST_F(MenuControllerTest, AsynchronousTouchEventRepostEvent) { + views::test::DisableMenuClosureAnimations(); MenuController* controller = menu_controller(); TestMenuControllerDelegate* delegate = menu_controller_delegate(); @@ -1319,6 +1324,7 @@ TEST_F(MenuControllerTest, AsynchronousTouchEventRepostEvent) { ui::ET_TOUCH_PRESSED, location, ui::EventTimeForNow(), ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, 0)); controller->OnTouchEvent(sub_menu, &event); + views::test::WaitForMenuClosureAnimation(); EXPECT_FALSE(IsShowing()); EXPECT_EQ(1, delegate->on_menu_closed_called()); @@ -1332,6 +1338,7 @@ TEST_F(MenuControllerTest, AsynchronousTouchEventRepostEvent) { // Tests that having the MenuController deleted during RepostEvent does not // cause a crash. ASAN bots should not detect use-after-free in MenuController. TEST_F(MenuControllerTest, AsynchronousRepostEventDeletesController) { + views::test::DisableMenuClosureAnimations(); MenuController* controller = menu_controller(); std::unique_ptr<TestMenuControllerDelegate> nested_delegate( new TestMenuControllerDelegate()); @@ -1358,6 +1365,7 @@ TEST_F(MenuControllerTest, AsynchronousRepostEventDeletesController) { // When attempting to select outside of all menus this should lead to a // shutdown. This should not crash while attempting to repost the event. SetSelectionOnPointerDown(sub_menu, &event); + views::test::WaitForMenuClosureAnimation(); // Close to remove observers before test TearDown sub_menu->Close(); diff --git a/chromium/ui/views/controls/menu/menu_host.cc b/chromium/ui/views/controls/menu/menu_host.cc index 104ae4c6da5..780a0f9c27b 100644 --- a/chromium/ui/views/controls/menu/menu_host.cc +++ b/chromium/ui/views/controls/menu/menu_host.cc @@ -86,7 +86,7 @@ void TransferGesture(Widget* source, Widget* target) { #else // !defined(OS_MACOSX) source->GetGestureRecognizer()->TransferEventsTo( source->GetNativeView(), target->GetNativeView(), - ui::GestureRecognizer::ShouldCancelTouches::DontCancel); + ui::TransferTouchesBehavior::kDontCancel); #endif // defined(OS_MACOSX) } diff --git a/chromium/ui/views/controls/menu/menu_item_view.cc b/chromium/ui/views/controls/menu/menu_item_view.cc index 6c007a1030c..7b17d2662ec 100644 --- a/chromium/ui/views/controls/menu/menu_item_view.cc +++ b/chromium/ui/views/controls/menu/menu_item_view.cc @@ -155,7 +155,18 @@ bool MenuItemView::GetTooltipText(const gfx::Point& p, } void MenuItemView::GetAccessibleNodeData(ui::AXNodeData* node_data) { - node_data->role = ax::mojom::Role::kMenuItem; + // Set the role based on the type of menu item. + switch (GetType()) { + case CHECKBOX: + node_data->role = ax::mojom::Role::kMenuItemCheckBox; + break; + case RADIO: + node_data->role = ax::mojom::Role::kMenuItemRadio; + break; + default: + node_data->role = ax::mojom::Role::kMenuItem; + break; + } base::string16 item_text; if (IsContainer()) { @@ -867,7 +878,7 @@ void MenuItemView::GetLabelStyle(MenuDelegate::LabelStyle* style) const { void MenuItemView::AddEmptyMenus() { DCHECK(HasSubmenu()); - if (!submenu_->HasVisibleChildren()) { + if (!submenu_->HasVisibleChildren() && !submenu_->HasEmptyMenuItemView()) { submenu_->AddChildViewAt(new EmptyMenuMenuItem(this), 0); } else { for (int i = 0, item_count = submenu_->GetMenuItemCount(); i < item_count; diff --git a/chromium/ui/views/controls/menu/menu_item_view_unittest.cc b/chromium/ui/views/controls/menu/menu_item_view_unittest.cc index 07273f5de1a..e4f42f008b4 100644 --- a/chromium/ui/views/controls/menu/menu_item_view_unittest.cc +++ b/chromium/ui/views/controls/menu/menu_item_view_unittest.cc @@ -136,13 +136,18 @@ TEST(MenuItemViewUnitTest, TestEmptySubmenuWhenAllChildItemsAreHidden) { EXPECT_EQ(2, submenu->child_count()); // Adds any empty menu items to the menu, if needed. + EXPECT_FALSE(submenu->HasEmptyMenuItemView()); root_menu.AddEmptyMenus(); - + EXPECT_TRUE(submenu->HasEmptyMenuItemView()); // Because all of the submenu's children are hidden, an empty menu item should // have been added. ASSERT_EQ(3, submenu->child_count()); MenuItemView* empty_item = static_cast<MenuItemView*>(submenu->child_at(0)); ASSERT_TRUE(empty_item); + // Not allowed to add an duplicated empty menu item + // if it already has an empty menu item. + root_menu.AddEmptyMenus(); + ASSERT_EQ(3, submenu->child_count()); ASSERT_EQ(MenuItemView::kEmptyMenuItemViewID, empty_item->id()); EXPECT_EQ(l10n_util::GetStringUTF16(IDS_APP_MENU_EMPTY_SUBMENU), empty_item->title()); diff --git a/chromium/ui/views/controls/menu/menu_pre_target_handler_aura.cc b/chromium/ui/views/controls/menu/menu_pre_target_handler_aura.cc index 45b071661bb..b38b0d49d53 100644 --- a/chromium/ui/views/controls/menu/menu_pre_target_handler_aura.cc +++ b/chromium/ui/views/controls/menu/menu_pre_target_handler_aura.cc @@ -6,6 +6,7 @@ #include "ui/aura/env.h" #include "ui/aura/window.h" +#include "ui/base/ui_base_features.h" #include "ui/views/controls/menu/menu_controller.h" #include "ui/views/widget/widget.h" #include "ui/wm/public/activation_client.h" @@ -23,16 +24,27 @@ aura::Window* GetOwnerRootWindow(views::Widget* owner) { MenuPreTargetHandlerAura::MenuPreTargetHandlerAura(MenuController* controller, Widget* owner) : controller_(controller), root_(GetOwnerRootWindow(owner)) { - aura::Env::GetInstance()->AddPreTargetHandler( - this, ui::EventTarget::Priority::kSystem); if (root_) { + root_->env()->AddPreTargetHandler(this, ui::EventTarget::Priority::kSystem); wm::GetActivationClient(root_)->AddObserver(this); root_->AddObserver(this); + } else { + // TODO(mukai): check if this code path can run in ChromeOS and find the + // solution for SingleProcessMash. + if (features::IsUsingWindowService()) { + LOG(WARNING) << "MenuPreTargetHandlerAura is created without owner " + << "widget. This may not work well in SingleProcessMash."; + } + aura::Env::GetInstance()->AddPreTargetHandler( + this, ui::EventTarget::Priority::kSystem); } } MenuPreTargetHandlerAura::~MenuPreTargetHandlerAura() { - aura::Env::GetInstance()->RemovePreTargetHandler(this); + if (root_) + root_->env()->RemovePreTargetHandler(this); + else + aura::Env::GetInstance()->RemovePreTargetHandler(this); Cleanup(); } diff --git a/chromium/ui/views/controls/menu/menu_runner_cocoa_unittest.mm b/chromium/ui/views/controls/menu/menu_runner_cocoa_unittest.mm index 2cc6a8c1a7c..7acfc99f907 100644 --- a/chromium/ui/views/controls/menu/menu_runner_cocoa_unittest.mm +++ b/chromium/ui/views/controls/menu/menu_runner_cocoa_unittest.mm @@ -106,6 +106,8 @@ class MenuRunnerCocoaTest : public ViewsTestBase, gfx::Rect(kWindowOffset, kWindowOffset, kWindowWidth, kWindowHeight)); parent_->Show(); + native_view_subview_count_ = [[parent_->GetNativeView() subviews] count]; + base::Closure on_close = base::Bind(&MenuRunnerCocoaTest::MenuCloseCallback, base::Unretained(this)); if (GetParam() == MenuType::NATIVE) @@ -116,6 +118,9 @@ class MenuRunnerCocoaTest : public ViewsTestBase, } void TearDown() override { + EXPECT_EQ(native_view_subview_count_, + [[parent_->GetNativeView() subviews] count]); + if (runner_) { runner_->Release(); runner_ = NULL; @@ -151,10 +156,6 @@ class MenuRunnerCocoaTest : public ViewsTestBase, void RunMenuAt(const gfx::Rect& anchor) { last_anchor_frame_ = NSZeroRect; - // Should be one child (the compositor layer) before showing, and it should - // go up by one (the anchor view) while the menu is shown. - EXPECT_EQ(1u, [[parent_->GetNativeView() subviews] count]); - base::OnceClosure callback = base::BindOnce(&MenuRunnerCocoaTest::ComboboxRunMenuAtCallback, base::Unretained(this)); @@ -168,9 +169,6 @@ class MenuRunnerCocoaTest : public ViewsTestBase, runner_->RunMenuAt(parent_, nullptr, anchor, MENU_ANCHOR_TOPLEFT, MenuRunner::COMBOBOX); MaybeRunAsync(); - - // Ensure the anchor view is removed. - EXPECT_EQ(1u, [[parent_->GetNativeView() subviews] count]); } void MenuCancelCallback() { @@ -244,6 +242,7 @@ class MenuRunnerCocoaTest : public ViewsTestBase, internal::MenuRunnerImplInterface* runner_ = nullptr; views::Widget* parent_ = nullptr; NSRect last_anchor_frame_ = NSZeroRect; + NSUInteger native_view_subview_count_ = 0; int menu_close_count_ = 0; private: @@ -256,10 +255,11 @@ class MenuRunnerCocoaTest : public ViewsTestBase, NSArray* subviews = [parent_->GetNativeView() subviews]; // An anchor view should only be added for Native menus. if (GetParam() == MenuType::NATIVE) { - ASSERT_EQ(2u, [subviews count]); - last_anchor_frame_ = [[subviews objectAtIndex:1] frame]; + ASSERT_EQ(native_view_subview_count_ + 1, [subviews count]); + last_anchor_frame_ = + [[subviews objectAtIndex:native_view_subview_count_] frame]; } else { - EXPECT_EQ(1u, [subviews count]); + EXPECT_EQ(native_view_subview_count_, [subviews count]); } runner_->Cancel(); } diff --git a/chromium/ui/views/controls/menu/menu_runner_impl.cc b/chromium/ui/views/controls/menu/menu_runner_impl.cc index 1c7b5c4c72a..3f1fb8af38b 100644 --- a/chromium/ui/views/controls/menu/menu_runner_impl.cc +++ b/chromium/ui/views/controls/menu/menu_runner_impl.cc @@ -192,8 +192,7 @@ void MenuRunnerImpl::SiblingMenuCreated(MenuItemView* menu) { MenuRunnerImpl::~MenuRunnerImpl() { delete menu_; - for (std::set<MenuItemView*>::iterator i = sibling_menus_.begin(); - i != sibling_menus_.end(); ++i) + for (auto i = sibling_menus_.begin(); i != sibling_menus_.end(); ++i) delete *i; } diff --git a/chromium/ui/views/controls/menu/submenu_view.cc b/chromium/ui/views/controls/menu/submenu_view.cc index c9ad4e4f5b9..36865a59316 100644 --- a/chromium/ui/views/controls/menu/submenu_view.cc +++ b/chromium/ui/views/controls/menu/submenu_view.cc @@ -61,6 +61,14 @@ SubmenuView::~SubmenuView() { delete scroll_view_container_; } +bool SubmenuView::HasEmptyMenuItemView() { + for (int i = 0; i < child_count(); i++) { + if (child_at(i)->id() == MenuItemView::kEmptyMenuItemViewID) + return true; + } + return false; +} + bool SubmenuView::HasVisibleChildren() { for (int i = 0, item_count = GetMenuItemCount(); i < item_count; i++) { if (GetMenuItemAt(i)->visible()) diff --git a/chromium/ui/views/controls/menu/submenu_view.h b/chromium/ui/views/controls/menu/submenu_view.h index 9ba69331264..4374790035e 100644 --- a/chromium/ui/views/controls/menu/submenu_view.h +++ b/chromium/ui/views/controls/menu/submenu_view.h @@ -51,6 +51,9 @@ class VIEWS_EXPORT SubmenuView : public View, explicit SubmenuView(MenuItemView* parent); ~SubmenuView() override; + // Returns true if the submenu has at least one empty menu item. + bool HasEmptyMenuItemView(); + // Returns true if the submenu has at least one visible child item. bool HasVisibleChildren(); diff --git a/chromium/ui/views/controls/native/native_view_host.cc b/chromium/ui/views/controls/native/native_view_host.cc index 32019ba3fdb..5240d2bf72d 100644 --- a/chromium/ui/views/controls/native/native_view_host.cc +++ b/chromium/ui/views/controls/native/native_view_host.cc @@ -67,6 +67,10 @@ void NativeViewHost::SetNativeViewSize(const gfx::Size& size) { InvalidateLayout(); } +gfx::NativeView NativeViewHost::GetNativeViewContainer() const { + return native_view_ ? native_wrapper_->GetNativeViewContainer() : nullptr; +} + void NativeViewHost::NativeViewDestroyed() { // Detach so we can clear our state and notify the native_wrapper_ to release // ref on the native view. @@ -225,7 +229,7 @@ void NativeViewHost::ClearFocus() { Widget::Widgets widgets; Widget::GetAllChildWidgets(native_view(), &widgets); - for (Widget::Widgets::iterator i = widgets.begin(); i != widgets.end(); ++i) { + for (auto i = widgets.begin(); i != widgets.end(); ++i) { focus_manager->ViewRemoved((*i)->GetRootView()); if (!focus_manager->GetFocusedView()) return; diff --git a/chromium/ui/views/controls/native/native_view_host.h b/chromium/ui/views/controls/native/native_view_host.h index 5bd897cf3ed..ec0b8e9f18a 100644 --- a/chromium/ui/views/controls/native/native_view_host.h +++ b/chromium/ui/views/controls/native/native_view_host.h @@ -63,6 +63,10 @@ class VIEWS_EXPORT NativeViewHost : public View { // NatieView's size always equals this View's size. void SetNativeViewSize(const gfx::Size& size); + // Returns the container that contains this host's native view. Returns null + // if there's no attached native view or it has no container. + gfx::NativeView GetNativeViewContainer() const; + // Fast resizing will move the native view and clip its visible region, this // will result in white areas and will not resize the content (so scrollbars // will be all wrong and content will flow offscreen). Only use this diff --git a/chromium/ui/views/controls/native/native_view_host_aura.cc b/chromium/ui/views/controls/native/native_view_host_aura.cc index 4bd67c7a87d..3be7f536eb1 100644 --- a/chromium/ui/views/controls/native/native_view_host_aura.cc +++ b/chromium/ui/views/controls/native/native_view_host_aura.cc @@ -5,6 +5,7 @@ #include "ui/views/controls/native/native_view_host_aura.h" #include "base/logging.h" +#include "base/optional.h" #include "build/build_config.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/client/focus_client.h" @@ -98,10 +99,12 @@ void NativeViewHostAura::AttachNativeView() { } void NativeViewHostAura::NativeViewDetaching(bool destroyed) { - // This method causes a succession of window tree changes. - // ScopedPauseOcclusionTracking ensures that occlusion is recomputed at the - // end of the method instead of after each change. - aura::WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion; + // This method causes a succession of window tree changes. ScopedPause ensures + // that occlusion is recomputed at the end of the method instead of after each + // change. + base::Optional<aura::WindowOcclusionTracker::ScopedPause> pause_occlusion; + if (clipping_window_) + pause_occlusion.emplace(clipping_window_->env()); clipping_window_delegate_->set_native_view(NULL); RemoveClippingWindow(); @@ -212,6 +215,10 @@ void NativeViewHostAura::SetFocus() { client->FocusWindow(window); } +gfx::NativeView NativeViewHostAura::GetNativeViewContainer() const { + return clipping_window_.get(); +} + gfx::NativeViewAccessible NativeViewHostAura::GetNativeViewAccessible() { return NULL; } diff --git a/chromium/ui/views/controls/native/native_view_host_aura.h b/chromium/ui/views/controls/native/native_view_host_aura.h index a6072997977..0adf8fb3aec 100644 --- a/chromium/ui/views/controls/native/native_view_host_aura.h +++ b/chromium/ui/views/controls/native/native_view_host_aura.h @@ -41,6 +41,7 @@ class NativeViewHostAura : public NativeViewHostWrapper, override; void HideWidget() override; void SetFocus() override; + gfx::NativeView GetNativeViewContainer() const override; gfx::NativeViewAccessible GetNativeViewAccessible() override; gfx::NativeCursor GetCursor(int x, int y) override; diff --git a/chromium/ui/views/controls/native/native_view_host_mac.h b/chromium/ui/views/controls/native/native_view_host_mac.h index 69659c9aafd..f46e575e442 100644 --- a/chromium/ui/views/controls/native/native_view_host_mac.h +++ b/chromium/ui/views/controls/native/native_view_host_mac.h @@ -7,24 +7,35 @@ #include "base/mac/scoped_nsobject.h" #include "base/macros.h" +#include "ui/base/cocoa/views_hostable.h" #include "ui/views/controls/native/native_view_host_wrapper.h" #include "ui/views/views_export.h" namespace ui { class LayerOwner; -} +class ViewsHostableView; +} // namespace ui namespace views { +class BridgedNativeWidgetHostImpl; class NativeViewHost; // Mac implementation of NativeViewHostWrapper. -class NativeViewHostMac : public NativeViewHostWrapper { +class NativeViewHostMac : public NativeViewHostWrapper, + public ui::ViewsHostableView::Host { public: explicit NativeViewHostMac(NativeViewHost* host); ~NativeViewHostMac() override; - // Overridden from NativeViewHostWrapper: + // ViewsHostableView::Host: + ui::Layer* GetUiLayer() const override; + uint64_t GetViewsFactoryHostId() const override; + uint64_t GetNSViewId() const override; + id GetAccessibilityElement() const override; + void OnHostableViewDestroying() override; + + // NativeViewHostWrapper: void AttachNativeView() override; void NativeViewDetaching(bool destroyed) override; void AddedToWidget() override; @@ -37,16 +48,25 @@ class NativeViewHostMac : public NativeViewHostWrapper { override; void HideWidget() override; void SetFocus() override; + gfx::NativeView GetNativeViewContainer() const override; gfx::NativeViewAccessible GetNativeViewAccessible() override; gfx::NativeCursor GetCursor(int x, int y) override; private: + // Return the BridgedNativeWidgetHostImpl for this hosted view. + BridgedNativeWidgetHostImpl* GetBridgedNativeWidgetHost() const; + // Our associated NativeViewHost. Owns this. NativeViewHost* host_; // Retain the native view as it may be destroyed at an unpredictable time. base::scoped_nsobject<NSView> native_view_; + // If |native_view| supports the ViewsHostable protocol, then this is the + // the corresponding ViewsHostableView interface (which is implemeted only + // by WebContents and tests). + ui::ViewsHostableView* native_view_hostable_ = nullptr; + DISALLOW_COPY_AND_ASSIGN(NativeViewHostMac); }; diff --git a/chromium/ui/views/controls/native/native_view_host_mac.mm b/chromium/ui/views/controls/native/native_view_host_mac.mm index debbe51f102..d98e6e1456c 100644 --- a/chromium/ui/views/controls/native/native_view_host_mac.mm +++ b/chromium/ui/views/controls/native/native_view_host_mac.mm @@ -8,19 +8,11 @@ #include "base/mac/foundation_util.h" #import "ui/accessibility/platform/ax_platform_node_mac.h" -#import "ui/base/cocoa/accessibility_hostable.h" -#import "ui/views/cocoa/bridged_native_widget.h" +#import "ui/views/cocoa/bridged_native_widget_host_impl.h" #include "ui/views/controls/native/native_view_host.h" #include "ui/views/widget/native_widget_mac.h" #include "ui/views/widget/widget.h" -// NSViews that can be drawn as a ui::Layer directly will implement this -// interface. Calling cr_setParentLayer will embed the ui::Layer of the NSView -// under |parentUiLayer|. -@interface NSView (UICompositor) -- (void)cr_setParentUiLayer:(ui::Layer*)parentUiLayer; -@end - namespace views { namespace { @@ -59,6 +51,60 @@ NativeViewHostMac::NativeViewHostMac(NativeViewHost* host) : host_(host) { NativeViewHostMac::~NativeViewHostMac() { } +BridgedNativeWidgetHostImpl* NativeViewHostMac::GetBridgedNativeWidgetHost() + const { + return BridgedNativeWidgetHostImpl::GetFromNativeWindow( + host_->GetWidget()->GetNativeWindow()); +} + +//////////////////////////////////////////////////////////////////////////////// +// NativeViewHostMac, ViewsHostableView::Host implementation: + +ui::Layer* NativeViewHostMac::GetUiLayer() const { + return host_->layer(); +} + +uint64_t NativeViewHostMac::GetViewsFactoryHostId() const { + auto* bridge_host = GetBridgedNativeWidgetHost(); + if (bridge_host && bridge_host->bridge_factory_host()) + return bridge_host->bridge_factory_host()->GetHostId(); + return 0; +} + +uint64_t NativeViewHostMac::GetNSViewId() const { + auto* bridge_host = GetBridgedNativeWidgetHost(); + if (bridge_host) + return bridge_host->GetRootViewNSViewId(); + return 0; +} + +id NativeViewHostMac::GetAccessibilityElement() const { + // Find the closest ancestor view that participates in the views toolkit + // accessibility hierarchy and set its element as the native view's parent. + // This is necessary because a closer ancestor might already be attaching + // to the NSView/content hierarchy. + // For example, web content is currently embedded into the views hierarchy + // roughly like this: + // BrowserView (views) + // |_ WebView (views) + // |_ NativeViewHost (views) + // |_ WebContentView (Cocoa, is |native_view_| in this scenario, + // | accessibility ignored). + // |_ RenderWidgetHostView (Cocoa) + // WebView specifies either the RenderWidgetHostView or the native view as + // its accessibility element. That means that if we were to set it as + // |native_view_|'s parent, the RenderWidgetHostView would be its own + // accessibility parent! Instead, we want to find the browser view and + // attach to its node. + return ClosestPlatformAncestorNode(host_->parent()); +} + +void NativeViewHostMac::OnHostableViewDestroying() { + DCHECK(native_view_hostable_); + host_->NativeViewDestroyed(); + DCHECK(!native_view_hostable_); +} + //////////////////////////////////////////////////////////////////////////////// // NativeViewHostMac, NativeViewHostWrapper implementation: @@ -66,46 +112,25 @@ void NativeViewHostMac::AttachNativeView() { DCHECK(host_->native_view()); DCHECK(!native_view_); native_view_.reset([host_->native_view() retain]); + EnsureNativeViewHasNoChildWidgets(native_view_); - if ([native_view_ respondsToSelector:@selector(cr_setParentUiLayer:)]) - [native_view_ cr_setParentUiLayer:host_->layer()]; - if ([native_view_ conformsToProtocol:@protocol(AccessibilityHostable)]) { - // Find the closest ancestor view that participates in the views toolkit - // accessibility hierarchy and set its element as the native view's parent. - // This is necessary because a closer ancestor might already be attaching - // to the NSView/content hierarchy. - // For example, web content is currently embedded into the views hierarchy - // roughly like this: - // BrowserView (views) - // |_ WebView (views) - // |_ NativeViewHost (views) - // |_ WebContentView (Cocoa, is |native_view_| in this scenario, - // | accessibility ignored). - // |_ RenderWidgetHostView (Cocoa) - // WebView specifies either the RenderWidgetHostView or the native view as - // its accessibility element. That means that if we were to set it as - // |native_view_|'s parent, the RenderWidgetHostView would be its own - // accessibility parent! Instead, we want to find the browser view and - // attach to its node. + auto* bridge_host = GetBridgedNativeWidgetHost(); + DCHECK(bridge_host); + [bridge_host->native_widget_mac()->GetNativeView() addSubview:native_view_]; + bridge_host->SetAssociationForView(host_, native_view_); + + if ([native_view_ conformsToProtocol:@protocol(ViewsHostable)]) { id hostable = native_view_; - [hostable setAccessibilityParentElement:ClosestPlatformAncestorNode( - host_->parent())]; + native_view_hostable_ = [hostable viewsHostableView]; + if (native_view_hostable_) + native_view_hostable_->OnViewsHostableAttached(this); } - - EnsureNativeViewHasNoChildWidgets(native_view_); - BridgedNativeWidget* bridge = NativeWidgetMac::GetBridgeForNativeWindow( - host_->GetWidget()->GetNativeWindow()); - DCHECK(bridge); - [bridge->ns_view() addSubview:native_view_]; - bridge->SetAssociationForView(host_, native_view_); } void NativeViewHostMac::NativeViewDetaching(bool destroyed) { - // |destroyed| is only true if this class calls host_->NativeViewDestroyed(). - // Aura does this after observing an aura OnWindowDestroying, but NSViews - // are reference counted so there isn't a reliable signal. Instead, a - // reference is retained until the NativeViewHost is detached. - DCHECK(!destroyed); + // |destroyed| is only true if this class calls host_->NativeViewDestroyed(), + // which is called if a hosted WebContentsView about to be destroyed (note + // that its corresponding NSView may still exist). // |native_view_| can be nil here if RemovedFromWidget() is called before // NativeViewHost::Detach(). @@ -118,19 +143,16 @@ void NativeViewHostMac::NativeViewDetaching(bool destroyed) { [host_->native_view() setHidden:YES]; [host_->native_view() removeFromSuperview]; - if ([native_view_ respondsToSelector:@selector(cr_setParentUiLayer:)]) - [native_view_ cr_setParentUiLayer:nullptr]; - if ([native_view_ conformsToProtocol:@protocol(AccessibilityHostable)]) { - id hostable = native_view_; - [hostable setAccessibilityParentElement:nil]; + if (native_view_hostable_) { + native_view_hostable_->OnViewsHostableDetached(); + native_view_hostable_ = nullptr; } EnsureNativeViewHasNoChildWidgets(host_->native_view()); - BridgedNativeWidget* bridge = NativeWidgetMac::GetBridgeForNativeWindow( - host_->GetWidget()->GetNativeWindow()); - // BridgedNativeWidget can be null when Widget is closing. - if (bridge) - bridge->ClearAssociationForView(host_); + auto* bridge_host = GetBridgedNativeWidgetHost(); + // BridgedNativeWidgetImpl can be null when Widget is closing. + if (bridge_host) + bridge_host->ClearAssociationForView(host_); native_view_.reset(); } @@ -191,15 +213,29 @@ void NativeViewHostMac::ShowWidget(int x, [[host_->native_view() superview] convertRect:window_rect fromView:nil]; [host_->native_view() setFrame:container_rect]; [host_->native_view() setHidden:NO]; + + if (native_view_hostable_) + native_view_hostable_->OnViewsHostableShow(gfx::Rect(x, y, w, h)); } void NativeViewHostMac::HideWidget() { [host_->native_view() setHidden:YES]; + + if (native_view_hostable_) + native_view_hostable_->OnViewsHostableHide(); } void NativeViewHostMac::SetFocus() { if ([host_->native_view() acceptsFirstResponder]) [[host_->native_view() window] makeFirstResponder:host_->native_view()]; + + if (native_view_hostable_) + native_view_hostable_->OnViewsHostableMakeFirstResponder(); +} + +gfx::NativeView NativeViewHostMac::GetNativeViewContainer() const { + NOTIMPLEMENTED(); + return nullptr; } gfx::NativeViewAccessible NativeViewHostMac::GetNativeViewAccessible() { diff --git a/chromium/ui/views/controls/native/native_view_host_mac_unittest.mm b/chromium/ui/views/controls/native/native_view_host_mac_unittest.mm index 4a4fdcad1b1..85373e5b977 100644 --- a/chromium/ui/views/controls/native/native_view_host_mac_unittest.mm +++ b/chromium/ui/views/controls/native/native_view_host_mac_unittest.mm @@ -12,17 +12,38 @@ #import "base/mac/scoped_nsobject.h" #include "base/macros.h" #import "testing/gtest_mac.h" -#import "ui/base/cocoa/accessibility_hostable.h" +#import "ui/base/cocoa/views_hostable.h" #include "ui/views/controls/native/native_view_host.h" #include "ui/views/controls/native/native_view_host_test_base.h" #include "ui/views/view.h" #include "ui/views/widget/widget.h" -@interface TestAccessibilityHostableView : NSView<AccessibilityHostable> -@property(nonatomic, assign) id accessibilityParentElement; +class TestViewsHostable : public ui::ViewsHostableView { + public: + id parent_accessibility_element() const { + return parent_accessibility_element_; + } + + private: + // ui::ViewsHostableView: + void OnViewsHostableAttached(ui::ViewsHostableView::Host* host) override { + parent_accessibility_element_ = host->GetAccessibilityElement(); + } + void OnViewsHostableDetached() override { + parent_accessibility_element_ = nil; + } + void OnViewsHostableShow(const gfx::Rect& bounds_in_window) override {} + void OnViewsHostableHide() override {} + void OnViewsHostableMakeFirstResponder() override {} + + id parent_accessibility_element_ = nil; +}; + +@interface TestViewsHostableView : NSView<ViewsHostable> +@property(nonatomic, assign) ui::ViewsHostableView* viewsHostableView; @end -@implementation TestAccessibilityHostableView -@synthesize accessibilityParentElement = accessibilityParentElement_; +@implementation TestViewsHostableView +@synthesize viewsHostableView = viewsHostableView_; @end namespace views { @@ -115,15 +136,18 @@ TEST_F(NativeViewHostMacTest, AccessibilityParent) { CreateHost(); host()->Detach(); - base::scoped_nsobject<TestAccessibilityHostableView> view( - [[TestAccessibilityHostableView alloc] init]); + base::scoped_nsobject<TestViewsHostableView> view( + [[TestViewsHostableView alloc] init]); + TestViewsHostable views_hostable; + [view setViewsHostableView:&views_hostable]; + host()->Attach(view); - EXPECT_NSEQ([view accessibilityParentElement], + EXPECT_NSEQ(views_hostable.parent_accessibility_element(), toplevel()->GetRootView()->GetNativeViewAccessible()); host()->Detach(); DestroyHost(); - EXPECT_FALSE([view accessibilityParentElement]); + EXPECT_FALSE(views_hostable.parent_accessibility_element()); } // Test that the content windows' bounds are set to the correct values while the diff --git a/chromium/ui/views/controls/native/native_view_host_wrapper.h b/chromium/ui/views/controls/native/native_view_host_wrapper.h index 513c35f1a71..07cf9f1fea0 100644 --- a/chromium/ui/views/controls/native/native_view_host_wrapper.h +++ b/chromium/ui/views/controls/native/native_view_host_wrapper.h @@ -78,6 +78,10 @@ class NativeViewHostWrapper { // Sets focus to the gfx::NativeView. virtual void SetFocus() = 0; + // Returns the container that contains the NativeViewHost's native view if + // any. + virtual gfx::NativeView GetNativeViewContainer() const = 0; + // Return the native view accessible corresponding to the wrapped native // view. virtual gfx::NativeViewAccessible GetNativeViewAccessible() = 0; diff --git a/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.h b/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.h index a0c88726dab..c69087a9382 100644 --- a/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.h +++ b/chromium/ui/views/controls/scrollbar/cocoa_scroll_bar.h @@ -5,13 +5,13 @@ #ifndef UI_VIEWS_CONTROLS_SCROLLBAR_COCOA_SCROLL_BAR_H_ #define UI_VIEWS_CONTROLS_SCROLLBAR_COCOA_SCROLL_BAR_H_ -#include "base/macros.h" #import "base/mac/scoped_nsobject.h" +#include "base/macros.h" #include "ui/compositor/layer_animation_observer.h" #include "ui/gfx/animation/slide_animation.h" -#import "ui/views/cocoa/views_scrollbar_bridge.h" #include "ui/views/controls/scrollbar/base_scroll_bar.h" #include "ui/views/views_export.h" +#import "ui/views_bridge_mac/views_scrollbar_bridge.h" namespace views { diff --git a/chromium/ui/views/controls/styled_label.cc b/chromium/ui/views/controls/styled_label.cc index 2c12e6e77ef..9e4477f7636 100644 --- a/chromium/ui/views/controls/styled_label.cc +++ b/chromium/ui/views/controls/styled_label.cc @@ -28,13 +28,6 @@ namespace views { namespace { -gfx::Insets FocusBorderInsets(const Label& label) { - // StyledLabel never adds a border, so the only Insets added are for the - // possible focus ring. - DCHECK(label.View::GetInsets().IsEmpty()); - return label.GetInsets(); -} - std::unique_ptr<Label> CreateLabelRange( const base::string16& text, int text_context, @@ -216,24 +209,6 @@ const char* StyledLabel::GetClassName() const { return kViewClassName; } -gfx::Insets StyledLabel::GetInsets() const { - gfx::Insets insets = View::GetInsets(); - if (Link::GetDefaultFocusStyle() != Link::FocusStyle::RING) - return insets; - - // We need a focus border iff we contain a link that will have a focus border. - // That in turn will be true only if the link is non-empty. - for (StyleRanges::const_iterator i(style_ranges_.begin()); - i != style_ranges_.end(); ++i) { - if (i->style_info.IsLink() && !i->range.is_empty()) { - insets += gfx::Insets(Link::kFocusBorderPadding); - break; - } - } - - return insets; -} - void StyledLabel::GetAccessibleNodeData(ui::AXNodeData* node_data) { if (text_context_ == style::CONTEXT_DIALOG_TITLE) node_data->role = ax::mojom::Role::kTitleBar; @@ -488,26 +463,18 @@ gfx::Size StyledLabel::CalculateAndDoLayout(int width, bool dry_run) { gfx::Size view_size = child_view->GetPreferredSize(); // |offset.y()| already contains |insets.top()|. gfx::Point view_origin(insets.left() + offset.x(), offset.y()); - gfx::Insets focus_border_insets; - if (Link::GetDefaultFocusStyle() == Link::FocusStyle::RING && label) { - // Calculate the size of the optional focus border, and overlap by that - // amount. Otherwise, "<a>link</a>," will render as "link ,". - focus_border_insets = FocusBorderInsets(*label); - } - view_origin.Offset(-focus_border_insets.left(), -focus_border_insets.top()); // The custom view could be wider than the available width; clamp as needed. - if (custom_view) { - view_size.set_width(std::min( - view_size.width(), width - offset.x() + focus_border_insets.width())); - } + if (custom_view) + view_size.set_width(std::min(view_size.width(), width - offset.x())); + child_view->SetBoundsRect(gfx::Rect(view_origin, view_size)); - offset.set_x(offset.x() + view_size.width() - focus_border_insets.width()); + offset.set_x(offset.x() + view_size.width()); total_height = - std::max(total_height, child_view->bounds().bottom() + insets.bottom() - - focus_border_insets.bottom()); + std::max(total_height, std::max(child_view->bounds().bottom(), + offset.y() + default_line_height) + + insets.bottom()); used_width = std::max(used_width, offset.x()); - max_line_height = std::max( - max_line_height, view_size.height() - focus_border_insets.height()); + max_line_height = std::max(max_line_height, view_size.height()); if (!dry_run) { views_in_a_line.push_back(child_view); diff --git a/chromium/ui/views/controls/styled_label.h b/chromium/ui/views/controls/styled_label.h index 43544ef691b..110007b1838 100644 --- a/chromium/ui/views/controls/styled_label.h +++ b/chromium/ui/views/controls/styled_label.h @@ -140,7 +140,6 @@ class VIEWS_EXPORT StyledLabel : public View, public LinkListener { // View: const char* GetClassName() const override; - gfx::Insets GetInsets() const override; void GetAccessibleNodeData(ui::AXNodeData* node_data) override; gfx::Size CalculatePreferredSize() const override; int GetHeightForWidth(int w) const override; diff --git a/chromium/ui/views/controls/styled_label_unittest.cc b/chromium/ui/views/controls/styled_label_unittest.cc index e79fadddb32..1d218215e66 100644 --- a/chromium/ui/views/controls/styled_label_unittest.cc +++ b/chromium/ui/views/controls/styled_label_unittest.cc @@ -513,13 +513,32 @@ TEST_F(StyledLabelTest, SetTextContextAndDefaultStyle) { } TEST_F(StyledLabelTest, LineHeight) { - const std::string text("one"); + const std::string text("one\ntwo\nthree"); + InitStyledLabel(text); + styled()->SetLineHeight(18); + EXPECT_EQ(18 * 3, styled()->GetHeightForWidth(100)); +} + +TEST_F(StyledLabelTest, LineHeightWithBorder) { + const std::string text("one\ntwo\nthree"); + InitStyledLabel(text); + styled()->SetLineHeight(18); + styled()->SetBorder(views::CreateSolidBorder(1, SK_ColorGRAY)); + EXPECT_EQ(18 * 3 + 2, styled()->GetHeightForWidth(100)); +} + +TEST_F(StyledLabelTest, LineHeightWithLink) { + const std::string text("one\ntwo\nthree"); InitStyledLabel(text); - int default_height = styled()->GetHeightForWidth(100); - const std::string newline_text("one\ntwo\nthree"); - InitStyledLabel(newline_text); styled()->SetLineHeight(18); - EXPECT_EQ(18 * 2 + default_height, styled()->GetHeightForWidth(100)); + + styled()->AddStyleRange(gfx::Range(0, 3), + StyledLabel::RangeStyleInfo::CreateForLink()); + styled()->AddStyleRange(gfx::Range(4, 7), + StyledLabel::RangeStyleInfo::CreateForLink()); + styled()->AddStyleRange(gfx::Range(8, 13), + StyledLabel::RangeStyleInfo::CreateForLink()); + EXPECT_EQ(18 * 3, styled()->GetHeightForWidth(100)); } TEST_F(StyledLabelTest, HandleEmptyLayout) { diff --git a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc index 04e572c25d0..96309bc1709 100644 --- a/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc +++ b/chromium/ui/views/controls/tabbed_pane/tabbed_pane.cc @@ -407,7 +407,7 @@ TabStrip::TabStrip(TabbedPane::Orientation orientation, layout->set_cross_axis_alignment(BoxLayout::CROSS_AXIS_ALIGNMENT_END); } else { const int kTabStripEdgePadding = 8; - const int kTabSpacing = 16; + const int kTabSpacing = 8; layout = std::make_unique<BoxLayout>( BoxLayout::kVertical, gfx::Insets(kTabStripEdgePadding, 0, 0, 0), kTabSpacing); diff --git a/chromium/ui/views/controls/textfield/textfield.cc b/chromium/ui/views/controls/textfield/textfield.cc index 752c642e2da..2ff60335add 100644 --- a/chromium/ui/views/controls/textfield/textfield.cc +++ b/chromium/ui/views/controls/textfield/textfield.cc @@ -19,6 +19,7 @@ #include "ui/base/cursor/cursor.h" #include "ui/base/default_style.h" #include "ui/base/dragdrop/drag_drop_types.h" +#include "ui/base/ime/constants.h" #include "ui/base/ime/input_method.h" #include "ui/base/ime/text_edit_commands.h" #include "ui/base/resource/resource_bundle.h" @@ -73,6 +74,7 @@ #endif #if defined(OS_MACOSX) +#include "ui/base/cocoa/defaults_utils.h" #include "ui/base/cocoa/secure_password_input.h" #endif @@ -149,6 +151,14 @@ ui::TextEditCommand GetCommandForKeyEvent(const ui::KeyEvent& event) { return shift ? ui::TextEditCommand::MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION : ui::TextEditCommand::MOVE_TO_END_OF_LINE; + case ui::VKEY_UP: + return shift ? ui::TextEditCommand:: + MOVE_TO_BEGINNING_OF_LINE_AND_MODIFY_SELECTION + : ui::TextEditCommand::INVALID_COMMAND; + case ui::VKEY_DOWN: + return shift + ? ui::TextEditCommand::MOVE_TO_END_OF_LINE_AND_MODIFY_SELECTION + : ui::TextEditCommand::INVALID_COMMAND; case ui::VKEY_BACK: if (!control) return ui::TextEditCommand::DELETE_BACKWARD; @@ -203,11 +213,14 @@ ui::TextEditCommand GetTextEditCommandFromMenuCommand(int command_id, return ui::TextEditCommand::INVALID_COMMAND; } -base::TimeDelta GetPasswordRevealDuration() { - return ViewsDelegate::GetInstance() - ? ViewsDelegate::GetInstance() - ->GetTextfieldPasswordRevealDuration() - : base::TimeDelta(); +base::TimeDelta GetPasswordRevealDuration(const ui::KeyEvent& event) { + // The key event may carries the property that indicates it was from the + // virtual keyboard. + // In that case, reveals the password characters for 1 second. + auto* properties = event.properties(); + bool from_vk = + properties && properties->find(ui::kPropertyFromVK) != properties->end(); + return from_vk ? base::TimeDelta::FromSeconds(1) : base::TimeDelta(); } bool IsControlKeyModifier(int flags) { @@ -229,8 +242,6 @@ const char Textfield::kViewClassName[] = "Textfield"; // static base::TimeDelta Textfield::GetCaretBlinkInterval() { - static constexpr base::TimeDelta default_value = - base::TimeDelta::FromMilliseconds(500); #if defined(OS_WIN) static const size_t system_value = ::GetCaretBlinkTime(); if (system_value != 0) { @@ -238,8 +249,12 @@ base::TimeDelta Textfield::GetCaretBlinkInterval() { ? base::TimeDelta() : base::TimeDelta::FromMilliseconds(system_value); } +#elif defined(OS_MACOSX) + base::TimeDelta system_value; + if (ui::TextInsertionCaretBlinkPeriod(&system_value)) + return system_value; #endif - return default_value; + return base::TimeDelta::FromMilliseconds(500); } // static @@ -1398,17 +1413,9 @@ bool Textfield::GetAcceleratorForCommandId(int command_id, *accelerator = ui::Accelerator(ui::VKEY_A, ui::EF_PLATFORM_ACCELERATOR); return true; - case IDS_CONTENT_CONTEXT_EMOJI: -#if defined(OS_MACOSX) - *accelerator = ui::Accelerator(ui::VKEY_SPACE, - ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN); - return true; -#else - return false; -#endif - default: - return false; + return text_services_context_menu_->GetAcceleratorForCommandId( + command_id, accelerator); } } @@ -1495,11 +1502,14 @@ void Textfield::InsertChar(const ui::KeyEvent& event) { DoInsertChar(ch); - if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD && - !GetPasswordRevealDuration().is_zero()) { - const size_t change_offset = model_->GetCursorPosition(); - DCHECK_GT(change_offset, 0u); - RevealPasswordChar(change_offset - 1); + if (text_input_type_ == ui::TEXT_INPUT_TYPE_PASSWORD) { + password_char_reveal_index_ = -1; + base::TimeDelta duration = GetPasswordRevealDuration(event); + if (!duration.is_zero()) { + const size_t change_offset = model_->GetCursorPosition(); + DCHECK_GT(change_offset, 0u); + RevealPasswordChar(change_offset - 1, duration); + } } } @@ -2287,15 +2297,16 @@ bool Textfield::ImeEditingAllowed() const { return (t != ui::TEXT_INPUT_TYPE_NONE && t != ui::TEXT_INPUT_TYPE_PASSWORD); } -void Textfield::RevealPasswordChar(int index) { +void Textfield::RevealPasswordChar(int index, base::TimeDelta duration) { GetRenderText()->SetObscuredRevealIndex(index); SchedulePaint(); + password_char_reveal_index_ = index; if (index != -1) { password_reveal_timer_.Start( - FROM_HERE, GetPasswordRevealDuration(), + FROM_HERE, duration, base::Bind(&Textfield::RevealPasswordChar, - weak_ptr_factory_.GetWeakPtr(), -1)); + weak_ptr_factory_.GetWeakPtr(), -1, duration)); } } diff --git a/chromium/ui/views/controls/textfield/textfield.h b/chromium/ui/views/controls/textfield/textfield.h index 314bde9feff..4d20d983432 100644 --- a/chromium/ui/views/controls/textfield/textfield.h +++ b/chromium/ui/views/controls/textfield/textfield.h @@ -244,6 +244,8 @@ class VIEWS_EXPORT Textfield : public View, // Set extra spacing placed between glyphs; used for obscured text styling. void SetGlyphSpacing(int spacing); + int GetPasswordCharRevealIndex() const { return password_char_reveal_index_; } + // View overrides: int GetBaseline() const override; gfx::Size CalculatePreferredSize() const override; @@ -470,7 +472,8 @@ class VIEWS_EXPORT Textfield : public View, // Reveals the password character at |index| for a set duration. // If |index| is -1, the existing revealed character will be reset. - void RevealPasswordChar(int index); + // |duration| is the time to remain the password char to be visible. + void RevealPasswordChar(int index, base::TimeDelta duration); void CreateTouchSelectionControllerAndNotifyIt(); @@ -628,6 +631,9 @@ class VIEWS_EXPORT Textfield : public View, // The focus ring for this TextField. std::unique_ptr<FocusRing> focus_ring_; + // The password char reveal index, for testing only. + int password_char_reveal_index_ = -1; + // Used to bind callback functions to this object. base::WeakPtrFactory<Textfield> weak_ptr_factory_; diff --git a/chromium/ui/views/controls/textfield/textfield_model.cc b/chromium/ui/views/controls/textfield/textfield_model.cc index 4d77de34ffc..1bfea07108c 100644 --- a/chromium/ui/views/controls/textfield/textfield_model.cc +++ b/chromium/ui/views/controls/textfield/textfield_model.cc @@ -9,6 +9,7 @@ #include "base/logging.h" #include "base/macros.h" #include "base/message_loop/message_loop.h" +#include "base/no_destructor.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" #include "ui/base/clipboard/clipboard.h" @@ -268,9 +269,9 @@ gfx::Range GetFirstEmphasizedRange(const ui::CompositionText& composition) { // NSTextKillRingSize, a text system default. However to keep things simple, // the default kill ring size of 1 (i.e. a single buffer) is assumed. base::string16* GetKillBuffer() { - CR_DEFINE_STATIC_LOCAL(base::string16, kill_buffer, ()); + static base::NoDestructor<base::string16> kill_buffer; DCHECK(base::MessageLoopForUI::IsCurrent()); - return &kill_buffer; + return kill_buffer.get(); } // Helper method to set the kill buffer. @@ -486,7 +487,7 @@ bool TextfieldModel::CanRedo() { if (edit_history_.empty()) return false; // There is no redo iff the current edit is the last element in the history. - EditHistory::iterator iter = current_edit_; + auto iter = current_edit_; return iter == edit_history_.end() || // at the top. ++iter != edit_history_.end(); } @@ -760,7 +761,7 @@ void TextfieldModel::ClearRedoHistory() { ClearEditHistory(); return; } - EditHistory::iterator delete_start = current_edit_; + auto delete_start = current_edit_; ++delete_start; edit_history_.erase(delete_start, edit_history_.end()); } diff --git a/chromium/ui/views/controls/textfield/textfield_unittest.cc b/chromium/ui/views/controls/textfield/textfield_unittest.cc index f8a8292c501..be57d658a24 100644 --- a/chromium/ui/views/controls/textfield/textfield_unittest.cc +++ b/chromium/ui/views/controls/textfield/textfield_unittest.cc @@ -11,6 +11,7 @@ #include <string> #include <vector> +#include "base/bind_helpers.h" #include "base/command_line.h" #include "base/format_macros.h" #include "base/i18n/rtl.h" @@ -27,6 +28,7 @@ #include "ui/base/clipboard/scoped_clipboard_writer.h" #include "ui/base/dragdrop/drag_drop_types.h" #include "ui/base/emoji/emoji_panel_helper.h" +#include "ui/base/ime/constants.h" #include "ui/base/ime/input_method_base.h" #include "ui/base/ime/input_method_delegate.h" #include "ui/base/ime/input_method_factory.h" @@ -157,7 +159,7 @@ ui::EventDispatchDetails MockInputMethod::DispatchKeyEvent(ui::KeyEvent* key) { // which trigger the appropriate NSResponder action messages for composition. #if defined(OS_MACOSX) if (key->is_char()) - return DispatchKeyEventPostIME(key); + return DispatchKeyEventPostIME(key, base::NullCallback()); #endif // Checks whether the key event is from EventGenerator on Windows which will @@ -177,9 +179,9 @@ ui::EventDispatchDetails MockInputMethod::DispatchKeyEvent(ui::KeyEvent* key) { ui::KeyEvent mock_key(ui::ET_KEY_PRESSED, ui::VKEY_PROCESSKEY, key->flags()); - dispatch_details = DispatchKeyEventPostIME(&mock_key); + dispatch_details = DispatchKeyEventPostIME(&mock_key, base::NullCallback()); } else { - dispatch_details = DispatchKeyEventPostIME(key); + dispatch_details = DispatchKeyEventPostIME(key, base::NullCallback()); } if (key->handled() || dispatch_details.dispatcher_destroyed) @@ -553,9 +555,13 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController { SendKeyEvent(key_code, false, false); } - void SendKeyEvent(base::char16 ch) { SendKeyEvent(ch, ui::EF_NONE); } + void SendKeyEvent(base::char16 ch) { SendKeyEvent(ch, ui::EF_NONE, false); } void SendKeyEvent(base::char16 ch, int flags) { + SendKeyEvent(ch, flags, false); + } + + void SendKeyEvent(base::char16 ch, int flags, bool from_vk) { if (ch < 0x80) { ui::KeyboardCode code = ch == ' ' ? ui::VKEY_SPACE : @@ -567,6 +573,11 @@ class TextfieldTest : public ViewsTestBase, public TextfieldController { // Mac, key events don't pass through InputMethod. Hence they are // dispatched regularly. ui::KeyEvent event(ch, ui::VKEY_UNKNOWN, ui::DomCode::NONE, flags); + if (from_vk) { + ui::Event::Properties properties; + properties[ui::kPropertyFromVK] = std::vector<uint8_t>(); + event.SetProperties(properties); + } #if defined(OS_MACOSX) event_generator_->Dispatch(&event); #else @@ -1056,27 +1067,18 @@ TEST_F(TextfieldTest, MoveUpDownAndModifySelection) { textfield_->SetSelectionRange(gfx::Range(6)); - // Shift+[Up/Down] on Mac should execute the command - // MOVE_[UP/DOWN]_AND_MODIFY_SELECTION. On other platforms, textfield won't - // handle these events. + // Shift+[Up/Down] should select the text to the beginning and end of the + // line, respectively. SendKeyEvent(ui::VKEY_UP, true /* shift */, false /* command */); EXPECT_TRUE(textfield_->key_received()); -#if defined(OS_MACOSX) EXPECT_TRUE(textfield_->key_handled()); EXPECT_EQ(gfx::Range(6, 0), textfield_->GetSelectedRange()); -#else - EXPECT_FALSE(textfield_->key_handled()); -#endif textfield_->clear(); SendKeyEvent(ui::VKEY_DOWN, true /* shift */, false /* command */); EXPECT_TRUE(textfield_->key_received()); -#if defined(OS_MACOSX) EXPECT_TRUE(textfield_->key_handled()); EXPECT_EQ(gfx::Range(6, 11), textfield_->GetSelectedRange()); -#else - EXPECT_FALSE(textfield_->key_handled()); -#endif textfield_->clear(); } @@ -1292,8 +1294,15 @@ TEST_F(TextfieldTest, TextInputType_InsertionTest) { EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, textfield_->GetTextInputType()); SendKeyEvent(ui::VKEY_A); - SendKeyEvent(kHebrewLetterSamekh); + EXPECT_EQ(-1, textfield_->GetPasswordCharRevealIndex()); + SendKeyEvent(kHebrewLetterSamekh, ui::EF_NONE, true /* from_vk */); +#if !defined(OS_MACOSX) + // Don't verifies the password character reveal on MacOS, because on MacOS, + // the text insertion is not done through TextInputClient::InsertChar(). + EXPECT_EQ(1, textfield_->GetPasswordCharRevealIndex()); +#endif SendKeyEvent(ui::VKEY_B); + EXPECT_EQ(-1, textfield_->GetPasswordCharRevealIndex()); EXPECT_EQ(WideToUTF16(L"a\x05E1" L"b"), diff --git a/chromium/ui/views/controls/views_text_services_context_menu.h b/chromium/ui/views/controls/views_text_services_context_menu.h index bf411354261..726f6e37146 100644 --- a/chromium/ui/views/controls/views_text_services_context_menu.h +++ b/chromium/ui/views/controls/views_text_services_context_menu.h @@ -8,6 +8,7 @@ #include <memory> #include "base/i18n/rtl.h" +#include "ui/base/accelerators/accelerator.h" #include "ui/views/views_export.h" namespace ui { @@ -20,10 +21,8 @@ class Textfield; // This class is used to add and handle text service items in the text context // menu. -class ViewsTextServicesContextMenu { +class ViewsTextServicesContextMenu : public ui::AcceleratorProvider { public: - virtual ~ViewsTextServicesContextMenu() {} - // Creates a platform-specific ViewsTextServicesContextMenu object. static std::unique_ptr<ViewsTextServicesContextMenu> Create( ui::SimpleMenuModel* menu, diff --git a/chromium/ui/views/controls/views_text_services_context_menu_base.cc b/chromium/ui/views/controls/views_text_services_context_menu_base.cc index eae69906422..89858fbb099 100644 --- a/chromium/ui/views/controls/views_text_services_context_menu_base.cc +++ b/chromium/ui/views/controls/views_text_services_context_menu_base.cc @@ -5,17 +5,28 @@ #include "ui/views/controls/views_text_services_context_menu_base.h" #include "base/metrics/histogram_macros.h" +#include "build/build_config.h" +#include "ui/base/accelerators/accelerator.h" #include "ui/base/emoji/emoji_panel_helper.h" #include "ui/base/models/simple_menu_model.h" +#include "ui/events/event.h" +#include "ui/events/event_constants.h" #include "ui/resources/grit/ui_resources.h" #include "ui/strings/grit/ui_strings.h" #include "ui/views/controls/textfield/textfield.h" namespace views { +namespace { + const char kViewsTextServicesContextMenuHistogram[] = "ViewsTextServicesContextMenu.Used"; +// Do not change the values in this enum as they are used by UMA. +enum class Command { kEmoji = 0, kMaxValue = kEmoji }; + +} // namespace + ViewsTextServicesContextMenuBase::ViewsTextServicesContextMenuBase( ui::SimpleMenuModel* menu, Textfield* client) @@ -25,7 +36,7 @@ ViewsTextServicesContextMenuBase::ViewsTextServicesContextMenuBase( // Not inserted on read-only fields or if the OS/version doesn't support it. if (!client_->read_only() && ui::IsEmojiPanelSupported()) { menu->InsertSeparatorAt(0, ui::NORMAL_SEPARATOR); - menu->InsertItemWithStringIdAt(0, static_cast<int>(Command::kEmoji), + menu->InsertItemWithStringIdAt(0, IDS_CONTENT_CONTEXT_EMOJI, IDS_CONTENT_CONTEXT_EMOJI); } } @@ -33,7 +44,27 @@ ViewsTextServicesContextMenuBase::ViewsTextServicesContextMenuBase( ViewsTextServicesContextMenuBase::~ViewsTextServicesContextMenuBase() {} bool ViewsTextServicesContextMenuBase::SupportsCommand(int command_id) const { - return command_id == static_cast<int>(Command::kEmoji); + return command_id == IDS_CONTENT_CONTEXT_EMOJI; +} + +bool ViewsTextServicesContextMenuBase::GetAcceleratorForCommandId( + int command_id, + ui::Accelerator* accelerator) const { + if (command_id == IDS_CONTENT_CONTEXT_EMOJI) { +#if defined(OS_WIN) + *accelerator = ui::Accelerator(ui::VKEY_OEM_PERIOD, ui::EF_COMMAND_DOWN); + return true; +#elif defined(OS_MACOSX) + *accelerator = ui::Accelerator(ui::VKEY_SPACE, + ui::EF_COMMAND_DOWN | ui::EF_CONTROL_DOWN); + return true; +#else + // TODO(crbug.com/887660): Add accelerator key for Chrome OS. + return false; +#endif + } + + return false; } bool ViewsTextServicesContextMenuBase::IsCommandIdChecked( @@ -43,14 +74,14 @@ bool ViewsTextServicesContextMenuBase::IsCommandIdChecked( bool ViewsTextServicesContextMenuBase::IsCommandIdEnabled( int command_id) const { - if (command_id == static_cast<int>(Command::kEmoji)) + if (command_id == IDS_CONTENT_CONTEXT_EMOJI) return true; return false; } void ViewsTextServicesContextMenuBase::ExecuteCommand(int command_id) { - if (command_id == static_cast<int>(Command::kEmoji)) { + if (command_id == IDS_CONTENT_CONTEXT_EMOJI) { ui::ShowEmojiPanel(); UMA_HISTOGRAM_ENUMERATION(kViewsTextServicesContextMenuHistogram, Command::kEmoji); diff --git a/chromium/ui/views/controls/views_text_services_context_menu_base.h b/chromium/ui/views/controls/views_text_services_context_menu_base.h index 08e5a7ad0c0..bbc3e19d206 100644 --- a/chromium/ui/views/controls/views_text_services_context_menu_base.h +++ b/chromium/ui/views/controls/views_text_services_context_menu_base.h @@ -21,6 +21,10 @@ class ViewsTextServicesContextMenuBase : public ViewsTextServicesContextMenu { // Returns true if the given |command_id| is handled by the menu. bool SupportsCommand(int command_id) const override; + // ui::AcceleratorProvider: + bool GetAcceleratorForCommandId(int command_id, + ui::Accelerator* accelerator) const override; + // Methods associated with SimpleMenuModel::Delegate. bool IsCommandIdChecked(int command_id) const override; bool IsCommandIdEnabled(int command_id) const override; @@ -30,9 +34,6 @@ class ViewsTextServicesContextMenuBase : public ViewsTextServicesContextMenu { Textfield* client() const { return client_; } private: - // Do not change the values in this enum as they are used by UMA. - enum class Command { kEmoji = 0, kMaxValue = kEmoji }; - // The view associated with the menu. Weak. Owns |this|. Textfield* client_ = nullptr; diff --git a/chromium/ui/views/controls/webview/unhandled_keyboard_event_handler_mac.mm b/chromium/ui/views/controls/webview/unhandled_keyboard_event_handler_mac.mm index ece41dd5f53..e87f913ff61 100644 --- a/chromium/ui/views/controls/webview/unhandled_keyboard_event_handler_mac.mm +++ b/chromium/ui/views/controls/webview/unhandled_keyboard_event_handler_mac.mm @@ -5,7 +5,7 @@ #include "ui/views/controls/webview/unhandled_keyboard_event_handler.h" #import "base/mac/foundation_util.h" -#import "ui/views/cocoa/native_widget_mac_nswindow.h" +#import "ui/views_bridge_mac/native_widget_mac_nswindow.h" namespace views { diff --git a/chromium/ui/views/controls/webview/web_dialog_view.cc b/chromium/ui/views/controls/webview/web_dialog_view.cc index 90a9612cf61..56fc98b2840 100644 --- a/chromium/ui/views/controls/webview/web_dialog_view.cc +++ b/chromium/ui/views/controls/webview/web_dialog_view.cc @@ -116,7 +116,7 @@ bool WebDialogView::CanClose() { if (!is_attempting_close_dialog_) { // Fire beforeunload event when user attempts to close the dialog. is_attempting_close_dialog_ = true; - web_view_->web_contents()->DispatchBeforeUnload(); + web_view_->web_contents()->DispatchBeforeUnload(false /* auto_cancel */); } return false; } @@ -140,6 +140,12 @@ base::string16 WebDialogView::GetWindowTitle() const { return base::string16(); } +base::string16 WebDialogView::GetAccessibleWindowTitle() const { + if (delegate_) + return delegate_->GetAccessibleDialogTitle(); + return GetWindowTitle(); +} + std::string WebDialogView::GetWindowName() const { if (delegate_) return delegate_->GetDialogName(); diff --git a/chromium/ui/views/controls/webview/web_dialog_view.h b/chromium/ui/views/controls/webview/web_dialog_view.h index 0fd7bc03288..29d18cb2e1f 100644 --- a/chromium/ui/views/controls/webview/web_dialog_view.h +++ b/chromium/ui/views/controls/webview/web_dialog_view.h @@ -66,6 +66,7 @@ class WEBVIEW_EXPORT WebDialogView : public views::ClientView, bool CanResize() const override; ui::ModalType GetModalType() const override; base::string16 GetWindowTitle() const override; + base::string16 GetAccessibleWindowTitle() const override; std::string GetWindowName() const override; void WindowClosing() override; views::View* GetContentsView() override; diff --git a/chromium/ui/views/controls/webview/webview.cc b/chromium/ui/views/controls/webview/webview.cc index 74ddbc9b3a4..2a4cc7e8938 100644 --- a/chromium/ui/views/controls/webview/webview.cc +++ b/chromium/ui/views/controls/webview/webview.cc @@ -149,18 +149,6 @@ const char* WebView::GetClassName() const { return kViewClassName; } -std::unique_ptr<content::WebContents> WebView::SwapWebContents( - std::unique_ptr<content::WebContents> new_web_contents) { - if (wc_owner_) - wc_owner_->SetDelegate(NULL); - std::unique_ptr<content::WebContents> old_web_contents(std::move(wc_owner_)); - wc_owner_ = std::move(new_web_contents); - if (wc_owner_) - wc_owner_->SetDelegate(this); - SetWebContents(wc_owner_.get()); - return old_web_contents; -} - void WebView::OnBoundsChanged(const gfx::Rect& previous_bounds) { if (crashed_overlay_view_) crashed_overlay_view_->SetBoundsRect(gfx::Rect(size())); @@ -258,6 +246,10 @@ void WebView::GetAccessibleNodeData(ui::AXNodeData* node_data) { // provided via other means. Providing it here would be redundant. // Mark the name as explicitly empty so that accessibility_checks pass. node_data->SetNameExplicitlyEmpty(); + if (child_ax_tree_id_ != ui::AXTreeIDUnknown()) { + node_data->AddStringAttribute(ax::mojom::StringAttribute::kChildTreeId, + child_ax_tree_id_); + } } gfx::NativeViewAccessible WebView::GetNativeViewAccessible() { @@ -415,8 +407,13 @@ void WebView::UpdateCrashedOverlayView() { } void WebView::NotifyAccessibilityWebContentsChanged() { - if (web_contents()) - NotifyAccessibilityEvent(ax::mojom::Event::kChildrenChanged, false); + content::RenderFrameHost* rfh = + web_contents() ? web_contents()->GetMainFrame() : nullptr; + if (rfh) + child_ax_tree_id_ = rfh->GetAXTreeID(); + else + child_ax_tree_id_ = ui::AXTreeIDUnknown(); + NotifyAccessibilityEvent(ax::mojom::Event::kChildrenChanged, false); } std::unique_ptr<content::WebContents> WebView::CreateWebContents( diff --git a/chromium/ui/views/controls/webview/webview.h b/chromium/ui/views/controls/webview/webview.h index 994520b83b1..2a4b4937c16 100644 --- a/chromium/ui/views/controls/webview/webview.h +++ b/chromium/ui/views/controls/webview/webview.h @@ -120,11 +120,6 @@ class WEBVIEW_EXPORT WebView : public View, }; protected: - // Swaps the owned WebContents |wc_owner_| with |new_web_contents|. Returns - // the previously owned WebContents. - std::unique_ptr<content::WebContents> SwapWebContents( - std::unique_ptr<content::WebContents> new_web_contents); - // Called when the web contents is successfully attached. virtual void OnWebContentsAttached() {} // Called when letterboxing (scaling the native view to preserve aspect @@ -211,6 +206,10 @@ class WEBVIEW_EXPORT WebView : public View, gfx::Size min_size_; gfx::Size max_size_; + // Tracks the child accessibility tree id which is associated with the + // WebContents's main RenderFrameHost. + ui::AXTreeID child_ax_tree_id_; + DISALLOW_COPY_AND_ASSIGN(WebView); }; diff --git a/chromium/ui/views/corewm/tooltip_controller.cc b/chromium/ui/views/corewm/tooltip_controller.cc index 02cd71ac0ad..77d84caccc6 100644 --- a/chromium/ui/views/corewm/tooltip_controller.cc +++ b/chromium/ui/views/corewm/tooltip_controller.cc @@ -95,16 +95,14 @@ aura::Window* GetTooltipTarget(const ui::MouseEvent& event, // If |target| has capture all events go to it, even if the mouse is // really over another window. Find the real window the mouse is over. - gfx::Point screen_loc(event.location()); - aura::client::GetScreenPositionClient(event_target->GetRootWindow())-> - ConvertPointToScreen(event_target, &screen_loc); + const gfx::Point screen_loc = event.target()->GetScreenLocation(event); display::Screen* screen = display::Screen::GetScreen(); aura::Window* target = screen->GetWindowAtScreenPoint(screen_loc); if (!target) return NULL; gfx::Point target_loc(screen_loc); - aura::client::GetScreenPositionClient(target->GetRootWindow())-> - ConvertPointFromScreen(target, &target_loc); + aura::client::GetScreenPositionClient(target->GetRootWindow()) + ->ConvertPointFromScreen(target, &target_loc); aura::Window* screen_target = target->GetEventHandlerForPoint(target_loc); if (!IsValidTarget(event_target, screen_target)) return NULL; diff --git a/chromium/ui/views/corewm/tooltip_controller_unittest.cc b/chromium/ui/views/corewm/tooltip_controller_unittest.cc index 1212a6f38b1..83519930cee 100644 --- a/chromium/ui/views/corewm/tooltip_controller_unittest.cc +++ b/chromium/ui/views/corewm/tooltip_controller_unittest.cc @@ -463,10 +463,8 @@ namespace { // Returns the index of |window| in its parent's children. int IndexInParent(const aura::Window* window) { - aura::Window::Windows::const_iterator i = - std::find(window->parent()->children().begin(), - window->parent()->children().end(), - window); + auto i = std::find(window->parent()->children().begin(), + window->parent()->children().end(), window); return i == window->parent()->children().end() ? -1 : static_cast<int>(i - window->parent()->children().begin()); } diff --git a/chromium/ui/views/corewm/tooltip_win.cc b/chromium/ui/views/corewm/tooltip_win.cc index 462365750fb..153d4c47484 100644 --- a/chromium/ui/views/corewm/tooltip_win.cc +++ b/chromium/ui/views/corewm/tooltip_win.cc @@ -4,8 +4,6 @@ #include "ui/views/corewm/tooltip_win.h" -#include <winuser.h> - #include "base/debug/stack_trace.h" #include "base/i18n/rtl.h" #include "base/logging.h" @@ -14,11 +12,28 @@ #include "ui/display/screen.h" #include "ui/display/win/screen_win.h" #include "ui/gfx/geometry/rect.h" +#include "ui/gfx/platform_font_win.h" #include "ui/views/corewm/cursor_height_provider_win.h" namespace views { namespace corewm { +namespace { + +// Substitute GetWindowFont() from windowsx.h. +// Do not include windowsx.h as its macros break the views jumbo build. +HFONT GetWindowFont(HWND hwnd) { + return reinterpret_cast<HFONT>(::SendMessage(hwnd, WM_GETFONT, 0, 0)); +} + +// Substitute SetWindowFont() from windowsx.h. +// Do not include windowsx.h as its macros break the views jumbo build. +void SetWindowFont(HWND hwnd, HFONT hfont, BOOL fRedraw) { + ::SendMessage(hwnd, WM_SETFONT, reinterpret_cast<WPARAM>(hfont), fRedraw); +} + +} // namespace + TooltipWin::TooltipWin(HWND parent) : parent_hwnd_(parent), tooltip_hwnd_(NULL), @@ -70,7 +85,7 @@ bool TooltipWin::EnsureTooltipWindow() { return false; } - l10n_util::AdjustUIFontForWindow(tooltip_hwnd_); + MaybeOverrideFont(); SendMessage(tooltip_hwnd_, TTM_ADDTOOL, 0, reinterpret_cast<LPARAM>(&toolinfo_)); @@ -96,6 +111,33 @@ void TooltipWin::PositionTooltip() { display.work_area())); SetWindowPos(tooltip_hwnd_, NULL, tooltip_bounds.x(), tooltip_bounds.y(), 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + + MaybeOverrideFont(); +} + +void TooltipWin::MaybeOverrideFont() { + gfx::PlatformFontWin::FontAdjustment font_adjustment; + const HFONT old_font = GetWindowFont(tooltip_hwnd_); + + // Determine if we need to override the font. + if ((!override_font_ || override_font_->GetNativeFont() != old_font) && + l10n_util::NeedOverrideDefaultUIFont( + &font_adjustment.font_family_override, &font_adjustment.font_scale)) { + // Determine if we need to regenerate the font. + // There are a number of situations under which Windows can replace the + // font in a tooltip, but we don't actually need to regenerate our override + // font unless the underlying text/DPI scale of the window has changed. + const float current_scale = + display::win::ScreenWin::GetScaleFactorForHWND(tooltip_hwnd_); + if (!override_font_ || current_scale != override_scale_) { + override_font_ = + gfx::PlatformFontWin::AdjustExistingFont(old_font, font_adjustment); + override_scale_ = current_scale; + } + + // Override the font in the tooltip. + SetWindowFont(tooltip_hwnd_, override_font_->GetNativeFont(), FALSE); + } } int TooltipWin::GetMaxWidth(const gfx::Point& location) const { @@ -116,12 +158,6 @@ void TooltipWin::SetText(aura::Window* window, // See comment in header for details on why |location_| is needed. location_ = location; - // Without this we get a flicker of the tooltip appearing at 0x0. Not sure - // why. - SetWindowPos(tooltip_hwnd_, NULL, 0, 0, 0, 0, - SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | - SWP_NOREPOSITION | SWP_NOSIZE | SWP_NOZORDER); - base::string16 adjusted_text(tooltip_text); base::i18n::AdjustStringForLocaleDirection(&adjusted_text); toolinfo_.lpszText = const_cast<WCHAR*>(adjusted_text.c_str()); @@ -138,8 +174,6 @@ void TooltipWin::Show() { SendMessage(tooltip_hwnd_, TTM_TRACKACTIVATE, TRUE, reinterpret_cast<LPARAM>(&toolinfo_)); - SetWindowPos(tooltip_hwnd_, HWND_TOPMOST, 0, 0, 0, 0, - SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOSIZE); } void TooltipWin::Hide() { diff --git a/chromium/ui/views/corewm/tooltip_win.h b/chromium/ui/views/corewm/tooltip_win.h index eb9e5209de3..e656c87d3ce 100644 --- a/chromium/ui/views/corewm/tooltip_win.h +++ b/chromium/ui/views/corewm/tooltip_win.h @@ -5,15 +5,17 @@ #ifndef UI_VIEWS_COREWM_TOOLTIP_WIN_H_ #define UI_VIEWS_COREWM_TOOLTIP_WIN_H_ +#include <windows.h> +#include <commctrl.h> + #include "base/compiler_specific.h" #include "base/macros.h" +#include "base/optional.h" #include "base/strings/string16.h" +#include "ui/gfx/font.h" #include "ui/gfx/geometry/point.h" #include "ui/views/corewm/tooltip.h" -#include <windows.h> -#include <commctrl.h> - namespace views { namespace corewm { @@ -36,6 +38,9 @@ class VIEWS_EXPORT TooltipWin : public Tooltip { // Sets the position of the tooltip. void PositionTooltip(); + // Might override the font size for localization (e.g. Hindi). + void MaybeOverrideFont(); + // Tooltip: int GetMaxWidth(const gfx::Point& location) const override; void SetText(aura::Window* window, @@ -45,6 +50,11 @@ class VIEWS_EXPORT TooltipWin : public Tooltip { void Hide() override; bool IsVisible() override; + // Font we're currently overriding our UI font with. + // (Lets us keep a handle around so we don't leak.) + // Should outlast |tooltip_hwnd_|. + base::Optional<gfx::Font> override_font_; + // The window |tooltip_hwnd_| is parented to. HWND parent_hwnd_; @@ -62,6 +72,10 @@ class VIEWS_EXPORT TooltipWin : public Tooltip { // cache it. gfx::Point location_; + // What the scale was the last time we overrode the font, to see if we can + // re-use our previous override. + float override_scale_ = 0.0f; + DISALLOW_COPY_AND_ASSIGN(TooltipWin); }; diff --git a/chromium/ui/views/event_utils.cc b/chromium/ui/views/event_utils.cc new file mode 100644 index 00000000000..ecec6e0220c --- /dev/null +++ b/chromium/ui/views/event_utils.cc @@ -0,0 +1,22 @@ +// Copyright (c) 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/views/event_utils.h" + +#include "base/time/time.h" +#include "ui/events/event.h" +#include "ui/views/metrics.h" + +namespace views { + +bool IsPossiblyUnintendedInteraction(const base::TimeTicks& initial_timestamp, + const ui::Event& event) { + return (event.IsMouseEvent() || event.IsPointerEvent() || + event.IsTouchEvent()) && + event.time_stamp() < + initial_timestamp + + base::TimeDelta::FromMilliseconds(GetDoubleClickInterval()); +} + +} // namespace views diff --git a/chromium/ui/views/event_utils.h b/chromium/ui/views/event_utils.h new file mode 100644 index 00000000000..cfc553ff659 --- /dev/null +++ b/chromium/ui/views/event_utils.h @@ -0,0 +1,28 @@ +// Copyright (c) 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_VIEWS_EVENT_UTILS_H_ +#define UI_VIEWS_EVENT_UTILS_H_ + +#include "ui/views/views_export.h" + +namespace base { +class TimeTicks; +} + +namespace ui { +class Event; +} + +namespace views { + +// Returns true if the event is a mouse, touch, or pointer event that took place +// within the double-click time interval after the |initial_timestamp|. +VIEWS_EXPORT bool IsPossiblyUnintendedInteraction( + const base::TimeTicks& initial_timestamp, + const ui::Event& event); + +} // namespace views + +#endif // UI_VIEWS_EVENT_UTILS_H_ diff --git a/chromium/ui/views/examples/BUILD.gn b/chromium/ui/views/examples/BUILD.gn index c757e20b7ff..0069d8c9c8d 100644 --- a/chromium/ui/views/examples/BUILD.gn +++ b/chromium/ui/views/examples/BUILD.gn @@ -9,6 +9,8 @@ jumbo_component("views_examples_lib") { testonly = true sources = [ + "animated_image_view_example.cc", + "animated_image_view_example.h", "box_layout_example.cc", "box_layout_example.h", "bubble_example.cc", diff --git a/chromium/ui/views/examples/animated_image_view_example.cc b/chromium/ui/views/examples/animated_image_view_example.cc new file mode 100644 index 00000000000..4e52a95aeab --- /dev/null +++ b/chromium/ui/views/examples/animated_image_view_example.cc @@ -0,0 +1,143 @@ +// 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/views/examples/animated_image_view_example.h" + +#include <memory> + +#include "base/files/file_path.h" +#include "base/files/file_util.h" +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "base/threading/thread_restrictions.h" +#include "build/build_config.h" +#include "ui/gfx/geometry/insets.h" +#include "ui/gfx/skottie_wrapper.h" +#include "ui/views/border.h" +#include "ui/views/controls/animated_image_view.h" +#include "ui/views/controls/button/button.h" +#include "ui/views/controls/button/md_text_button.h" +#include "ui/views/controls/textfield/textfield.h" +#include "ui/views/controls/textfield/textfield_controller.h" +#include "ui/views/layout/box_layout.h" +#include "ui/views/layout/fill_layout.h" +#include "ui/views/view.h" + +namespace views { +namespace examples { + +namespace { + +// This class can load a skottie(and lottie) animation file from disk and play +// it in a view as AnimatedImageView. +// See https://skia.org/user/modules/skottie for more info on skottie. +class AnimationGallery : public View, + public TextfieldController, + public ButtonListener { + public: + AnimationGallery() + : animated_image_view_(new AnimatedImageView()), + image_view_container_(new views::View()), + size_input_(new Textfield()), + file_chooser_(new Textfield()), + file_go_button_( + MdTextButton::Create(this, base::ASCIIToUTF16("Render"))) { + AddChildView(size_input_); + + image_view_container_->AddChildView(animated_image_view_); + image_view_container_->SetLayoutManager(std::make_unique<FillLayout>()); + animated_image_view_->SetBorder( + CreateSolidSidedBorder(1, 1, 1, 1, SK_ColorBLACK)); + AddChildView(image_view_container_); + + BoxLayout* box = SetLayoutManager( + std::make_unique<BoxLayout>(BoxLayout::kVertical, gfx::Insets(10), 10)); + box->SetFlexForView(image_view_container_, 1); + + file_chooser_->set_placeholder_text( + base::ASCIIToUTF16("Enter path to lottie JSON file")); + View* file_container = new View(); + BoxLayout* file_box = + file_container->SetLayoutManager(std::make_unique<BoxLayout>( + BoxLayout::kHorizontal, gfx::Insets(10), 10)); + file_container->AddChildView(file_chooser_); + file_container->AddChildView(file_go_button_); + file_box->SetFlexForView(file_chooser_, 1); + AddChildView(file_container); + + size_input_->set_placeholder_text( + base::ASCIIToUTF16("Size in dip (Empty for default)")); + size_input_->set_controller(this); + } + + ~AnimationGallery() override = default; + + // TextfieldController: + void ContentsChanged(Textfield* sender, + const base::string16& new_contents) override { + if (sender == size_input_) { + if (!base::StringToInt(new_contents, &size_) && (size_ > 0)) { + size_ = 0; + size_input_->SetText(base::string16()); + } + Update(); + } + } + + // ButtonListener: + void ButtonPressed(Button* sender, const ui::Event& event) override { + DCHECK_EQ(file_go_button_, sender); + std::string json; + base::ScopedAllowBlockingForTesting allow_blocking; +#if defined(OS_POSIX) + base::FilePath path(base::UTF16ToUTF8(file_chooser_->text())); +#else + base::FilePath path(file_chooser_->text()); +#endif // defined(OS_POSIX) + base::ReadFileToString(path, &json); + + auto skottie = base::MakeRefCounted<gfx::SkottieWrapper>( + base::RefCountedString::TakeString(&json)); + animated_image_view_->SetAnimatedImage( + std::make_unique<gfx::SkiaVectorAnimation>(skottie)); + animated_image_view_->Play(); + Update(); + } + + private: + void Update() { + if (size_ > 24) + animated_image_view_->SetImageSize(gfx::Size(size_, size_)); + else + animated_image_view_->ResetImageSize(); + Layout(); + } + + AnimatedImageView* animated_image_view_; + View* image_view_container_; + Textfield* size_input_; + Textfield* file_chooser_; + Button* file_go_button_; + + int size_ = 0; + + DISALLOW_COPY_AND_ASSIGN(AnimationGallery); +}; + +} // namespace + +AnimatedImageViewExample::AnimatedImageViewExample() + : ExampleBase("Animated Image View") {} + +AnimatedImageViewExample::~AnimatedImageViewExample() {} + +void AnimatedImageViewExample::CreateExampleView(View* container) { + container->SetLayoutManager(std::make_unique<FillLayout>()); + container->AddChildView(new AnimationGallery()); +} + +} // namespace examples +} // namespace views diff --git a/chromium/ui/views/examples/animated_image_view_example.h b/chromium/ui/views/examples/animated_image_view_example.h new file mode 100644 index 00000000000..8bf30b56a87 --- /dev/null +++ b/chromium/ui/views/examples/animated_image_view_example.h @@ -0,0 +1,29 @@ +// 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_VIEWS_EXAMPLES_ANIMATED_IMAGE_VIEW_EXAMPLE_H_ +#define UI_VIEWS_EXAMPLES_ANIMATED_IMAGE_VIEW_EXAMPLE_H_ + +#include "base/macros.h" +#include "ui/views/examples/example_base.h" + +namespace views { +namespace examples { + +class VIEWS_EXAMPLES_EXPORT AnimatedImageViewExample : public ExampleBase { + public: + AnimatedImageViewExample(); + ~AnimatedImageViewExample() override; + + // ExampleBase: + void CreateExampleView(View* container) override; + + private: + DISALLOW_COPY_AND_ASSIGN(AnimatedImageViewExample); +}; + +} // namespace examples +} // namespace views + +#endif // UI_VIEWS_EXAMPLES_ANIMATED_IMAGE_VIEW_EXAMPLE_H_ diff --git a/chromium/ui/views/examples/examples_main.cc b/chromium/ui/views/examples/examples_main.cc index 4dd22a71197..f2f3a2a205b 100644 --- a/chromium/ui/views/examples/examples_main.cc +++ b/chromium/ui/views/examples/examples_main.cc @@ -118,9 +118,10 @@ int main(int argc, char** argv) { display::Screen::SetScreenInstance(desktop_screen.get()); #endif - views::examples::ShowExamplesWindow(views::examples::QUIT_ON_CLOSE); + base::RunLoop run_loop; + views::examples::ShowExamplesWindow(run_loop.QuitClosure()); - base::RunLoop().Run(); + run_loop.Run(); ui::ResourceBundle::CleanupSharedInstance(); } diff --git a/chromium/ui/views/examples/examples_window.cc b/chromium/ui/views/examples/examples_window.cc index b903e478740..683d5d59711 100644 --- a/chromium/ui/views/examples/examples_window.cc +++ b/chromium/ui/views/examples/examples_window.cc @@ -18,6 +18,7 @@ #include "ui/views/background.h" #include "ui/views/controls/combobox/combobox.h" #include "ui/views/controls/label.h" +#include "ui/views/examples/animated_image_view_example.h" #include "ui/views/examples/box_layout_example.h" #include "ui/views/examples/bubble_example.h" #include "ui/views/examples/button_example.h" @@ -58,6 +59,7 @@ namespace { // Creates the default set of examples. ExampleVector CreateExamples() { ExampleVector examples; + examples.push_back(std::make_unique<AnimatedImageViewExample>()); examples.push_back(std::make_unique<BoxLayoutExample>()); examples.push_back(std::make_unique<BubbleExample>()); examples.push_back(std::make_unique<ButtonExample>()); @@ -131,11 +133,11 @@ class ComboboxModelExampleList : public ui::ComboboxModel { class ExamplesWindowContents : public WidgetDelegateView, public ComboboxListener { public: - ExamplesWindowContents(Operation operation, ExampleVector examples) + ExamplesWindowContents(base::OnceClosure on_close, ExampleVector examples) : combobox_(new Combobox(&combobox_model_)), example_shown_(new View), status_label_(new Label), - operation_(operation) { + on_close_(std::move(on_close)) { instance_ = this; combobox_->set_listener(this); combobox_model_.SetExamples(std::move(examples)); @@ -188,8 +190,8 @@ class ExamplesWindowContents : public WidgetDelegateView, } void WindowClosing() override { instance_ = NULL; - if (operation_ == QUIT_ON_CLOSE) - base::RunLoop::QuitCurrentWhenIdleDeprecated(); + if (on_close_) + std::move(on_close_).Run(); } gfx::Size CalculatePreferredSize() const override { return gfx::Size(800, 300); @@ -212,7 +214,7 @@ class ExamplesWindowContents : public WidgetDelegateView, Combobox* combobox_; View* example_shown_; Label* status_label_; - const Operation operation_; + base::OnceClosure on_close_; DISALLOW_COPY_AND_ASSIGN(ExamplesWindowContents); }; @@ -220,7 +222,7 @@ class ExamplesWindowContents : public WidgetDelegateView, // static ExamplesWindowContents* ExamplesWindowContents::instance_ = NULL; -void ShowExamplesWindow(Operation operation, +void ShowExamplesWindow(base::OnceClosure on_close, gfx::NativeWindow window_context, ExampleVector extra_examples) { if (ExamplesWindowContents::instance()) { @@ -230,7 +232,7 @@ void ShowExamplesWindow(Operation operation, Widget* widget = new Widget; Widget::InitParams params; params.delegate = - new ExamplesWindowContents(operation, std::move(examples)); + new ExamplesWindowContents(std::move(on_close), std::move(examples)); params.context = window_context; widget->Init(params); widget->Show(); diff --git a/chromium/ui/views/examples/examples_window.h b/chromium/ui/views/examples/examples_window.h index 804252fcc9b..d543c01c3f6 100644 --- a/chromium/ui/views/examples/examples_window.h +++ b/chromium/ui/views/examples/examples_window.h @@ -15,16 +15,11 @@ namespace views { namespace examples { -enum Operation { - DO_NOTHING_ON_CLOSE = 0, - QUIT_ON_CLOSE, -}; - // Shows a window with the views examples in it. |extra_examples| contains any // additional examples to add. |window_context| is used to determine where the // window should be created (see |Widget::InitParams::context| for details). VIEWS_EXAMPLES_EXPORT void ShowExamplesWindow( - Operation operation, + base::OnceClosure on_close, gfx::NativeWindow window_context = nullptr, std::vector<std::unique_ptr<ExampleBase>> extra_examples = std::vector<std::unique_ptr<ExampleBase>>()); diff --git a/chromium/ui/views/examples/examples_window_with_content.cc b/chromium/ui/views/examples/examples_window_with_content.cc index 1133e88fa9a..c9391fa8bd8 100644 --- a/chromium/ui/views/examples/examples_window_with_content.cc +++ b/chromium/ui/views/examples/examples_window_with_content.cc @@ -14,12 +14,13 @@ namespace views { namespace examples { -void ShowExamplesWindowWithContent(Operation operation, +void ShowExamplesWindowWithContent(base::OnceClosure on_close, content::BrowserContext* browser_context, gfx::NativeWindow window_context) { std::vector<std::unique_ptr<ExampleBase>> extra_examples; extra_examples.push_back(std::make_unique<WebViewExample>(browser_context)); - ShowExamplesWindow(operation, window_context, std::move(extra_examples)); + ShowExamplesWindow(std::move(on_close), window_context, + std::move(extra_examples)); } } // namespace examples diff --git a/chromium/ui/views/examples/examples_window_with_content.h b/chromium/ui/views/examples/examples_window_with_content.h index 945201bdf7c..dbcb1fbd806 100644 --- a/chromium/ui/views/examples/examples_window_with_content.h +++ b/chromium/ui/views/examples/examples_window_with_content.h @@ -18,7 +18,7 @@ namespace examples { // Shows a window with the views examples in it. VIEWS_EXAMPLES_WITH_CONTENT_EXPORT void ShowExamplesWindowWithContent( - Operation operation, + base::OnceClosure on_close, content::BrowserContext* browser_context, gfx::NativeWindow window_context); diff --git a/chromium/ui/views/examples/examples_with_content_main_exe.cc b/chromium/ui/views/examples/examples_with_content_main_exe.cc index ffbad7ff3e7..3506d740035 100644 --- a/chromium/ui/views/examples/examples_with_content_main_exe.cc +++ b/chromium/ui/views/examples/examples_with_content_main_exe.cc @@ -15,11 +15,12 @@ namespace { -void ShowContentExampleWindow(content::BrowserContext* browser_context, +void ShowContentExampleWindow(ui::ViewsContentClient* views_content_client, + content::BrowserContext* browser_context, gfx::NativeWindow window_context) { - views::examples::ShowExamplesWindowWithContent(views::examples::QUIT_ON_CLOSE, - browser_context, - window_context); + views::examples::ShowExamplesWindowWithContent( + std::move(views_content_client->quit_closure()), browser_context, + window_context); // These lines serve no purpose other than to introduce an explicit content // dependency. If the main executable doesn't have this dependency, the linker @@ -45,6 +46,7 @@ int main(int argc, const char** argv) { ui::ViewsContentClient views_content_client(argc, argv); #endif - views_content_client.set_task(base::Bind(&ShowContentExampleWindow)); + views_content_client.set_task(base::Bind( + &ShowContentExampleWindow, base::Unretained(&views_content_client))); return views_content_client.RunMain(); } diff --git a/chromium/ui/views/examples/menu_example.cc b/chromium/ui/views/examples/menu_example.cc index 2e9081c9893..3c2713a432c 100644 --- a/chromium/ui/views/examples/menu_example.cc +++ b/chromium/ui/views/examples/menu_example.cc @@ -155,7 +155,7 @@ void ExampleMenuModel::ExecuteCommand(int command_id, int event_flags) { checked_fruit = "Kiwi"; // Update the check status. - std::set<int>::iterator iter = checked_fruits_.find(command_id); + auto iter = checked_fruits_.find(command_id); if (iter == checked_fruits_.end()) { DVLOG(1) << "Checked " << checked_fruit; checked_fruits_.insert(command_id); diff --git a/chromium/ui/views/focus/focus_manager_unittest.cc b/chromium/ui/views/focus/focus_manager_unittest.cc index ed29e91704b..10da1cd7fb3 100644 --- a/chromium/ui/views/focus/focus_manager_unittest.cc +++ b/chromium/ui/views/focus/focus_manager_unittest.cc @@ -130,12 +130,6 @@ TEST_F(FocusManagerTest, FocusChangeListener) { } TEST_F(FocusManagerTest, WidgetFocusChangeListener) { - // TODO: this test ends up calling focus on the aura::Window associated with - // the Widget and expecting that to change activation. This should work for - // aura-mus-client as well. http://crbug.com/664261. - if (IsMus()) - return; - // First, ensure the simulator is aware of the Widget created in SetUp() being // currently active. test::WidgetTest::SimulateNativeActivate(GetWidget()); diff --git a/chromium/ui/views/focus/widget_focus_manager.cc b/chromium/ui/views/focus/widget_focus_manager.cc index 099b286e94c..79517fbd4bd 100644 --- a/chromium/ui/views/focus/widget_focus_manager.cc +++ b/chromium/ui/views/focus/widget_focus_manager.cc @@ -4,17 +4,63 @@ #include "ui/views/focus/widget_focus_manager.h" -#include "base/memory/singleton.h" +#include "base/supports_user_data.h" + +#if defined(USE_AURA) +#include "ui/aura/env.h" +#include "ui/aura/window.h" +#endif namespace views { +#if defined(USE_AURA) +namespace { + +const char kWidgetFocusManagerKey[] = "WidgetFocusManager"; + +} // namespace + +class WidgetFocusManager::Owner : public base::SupportsUserData::Data { + public: + explicit Owner(std::unique_ptr<WidgetFocusManager> focus_manager) + : focus_manager_(std::move(focus_manager)) {} + ~Owner() override = default; + + WidgetFocusManager* focus_manager() { return focus_manager_.get(); } + + private: + std::unique_ptr<WidgetFocusManager> focus_manager_; + + DISALLOW_COPY_AND_ASSIGN(Owner); +}; + +#endif + // WidgetFocusManager ---------------------------------------------------------- // static -WidgetFocusManager* WidgetFocusManager::GetInstance() { - return base::Singleton<WidgetFocusManager>::get(); +WidgetFocusManager* WidgetFocusManager::GetInstance(gfx::NativeWindow context) { +#if defined(USE_AURA) + // With aura there may be multiple Envs, in such a situation the + // WidgetFocusManager needs to be per Env. + aura::Env* env = context ? context->env() : aura::Env::GetInstance(); + DCHECK(env); + Owner* owner = static_cast<Owner*>(env->GetUserData(kWidgetFocusManagerKey)); + if (!owner) { + std::unique_ptr<Owner> owner_ptr = + std::make_unique<Owner>(base::WrapUnique(new WidgetFocusManager())); + owner = owner_ptr.get(); + env->SetUserData(kWidgetFocusManagerKey, std::move(owner_ptr)); + } + return owner->focus_manager(); +#else + static base::NoDestructor<WidgetFocusManager> instance; + return instance.get(); +#endif } +WidgetFocusManager::~WidgetFocusManager() = default; + void WidgetFocusManager::AddFocusChangeListener( WidgetFocusChangeListener* listener) { focus_change_listeners_.AddObserver(listener); @@ -34,8 +80,6 @@ void WidgetFocusManager::OnNativeFocusChanged(gfx::NativeView focused_now) { WidgetFocusManager::WidgetFocusManager() : enabled_(true) {} -WidgetFocusManager::~WidgetFocusManager() {} - // AutoNativeNotificationDisabler ---------------------------------------------- AutoNativeNotificationDisabler::AutoNativeNotificationDisabler() { diff --git a/chromium/ui/views/focus/widget_focus_manager.h b/chromium/ui/views/focus/widget_focus_manager.h index 068a4825b46..eb29250c7e6 100644 --- a/chromium/ui/views/focus/widget_focus_manager.h +++ b/chromium/ui/views/focus/widget_focus_manager.h @@ -6,14 +6,11 @@ #define UI_VIEWS_FOCUS_WIDGET_FOCUS_MANAGER_H_ #include "base/macros.h" +#include "base/no_destructor.h" #include "base/observer_list.h" #include "ui/gfx/native_widget_types.h" #include "ui/views/views_export.h" -namespace base { -template <typename T> struct DefaultSingletonTraits; -} - namespace views { // This interface should be implemented by classes that want to be notified when @@ -32,7 +29,9 @@ class WidgetFocusChangeListener { class VIEWS_EXPORT WidgetFocusManager { public: // Returns the singleton instance. - static WidgetFocusManager* GetInstance(); + static WidgetFocusManager* GetInstance(gfx::NativeWindow context = nullptr); + + ~WidgetFocusManager(); // Adds/removes a WidgetFocusChangeListener |listener| to the set of // active listeners. @@ -50,10 +49,10 @@ class VIEWS_EXPORT WidgetFocusManager { void DisableNotifications() { enabled_ = false; } private: - friend struct base::DefaultSingletonTraits<WidgetFocusManager>; + class Owner; + friend class base::NoDestructor<WidgetFocusManager>; WidgetFocusManager(); - ~WidgetFocusManager(); base::ObserverList<WidgetFocusChangeListener>::Unchecked focus_change_listeners_; diff --git a/chromium/ui/views/layout/box_layout.cc b/chromium/ui/views/layout/box_layout.cc index f9c1b6dc485..cdc9750f08b 100644 --- a/chromium/ui/views/layout/box_layout.cc +++ b/chromium/ui/views/layout/box_layout.cc @@ -363,7 +363,7 @@ void BoxLayout::ViewRemoved(View* host, View* view) { } int BoxLayout::GetFlexForView(const View* view) const { - FlexMap::const_iterator it = flex_map_.find(view); + auto it = flex_map_.find(view); if (it == flex_map_.end()) return default_flex_; @@ -371,7 +371,7 @@ int BoxLayout::GetFlexForView(const View* view) const { } int BoxLayout::GetMinimumSizeForView(const View* view) const { - FlexMap::const_iterator it = flex_map_.find(view); + auto it = flex_map_.find(view); if (it == flex_map_.end() || !it->second.use_min_size) return 0; diff --git a/chromium/ui/views/linux_ui/linux_ui.h b/chromium/ui/views/linux_ui/linux_ui.h index 86a3c0a78cb..759d4ab03f0 100644 --- a/chromium/ui/views/linux_ui/linux_ui.h +++ b/chromium/ui/views/linux_ui/linux_ui.h @@ -101,12 +101,10 @@ class VIEWS_EXPORT LinuxUI : public ui::LinuxInputMethodContextFactory, virtual bool GetColor(int id, SkColor* color, PrefService* pref_service) const = 0; + virtual bool GetDisplayProperty(int id, int* result) const = 0; // Returns the preferences that we pass to WebKit. virtual SkColor GetFocusRingColor() const = 0; - virtual SkColor GetThumbActiveColor() const = 0; - virtual SkColor GetThumbInactiveColor() const = 0; - virtual SkColor GetTrackColor() const = 0; virtual SkColor GetActiveSelectionBgColor() const = 0; virtual SkColor GetActiveSelectionFgColor() const = 0; virtual SkColor GetInactiveSelectionBgColor() const = 0; diff --git a/chromium/ui/views/mus/ax_remote_host.cc b/chromium/ui/views/mus/ax_remote_host.cc index 0affab635ac..01511da2296 100644 --- a/chromium/ui/views/mus/ax_remote_host.cc +++ b/chromium/ui/views/mus/ax_remote_host.cc @@ -6,10 +6,12 @@ #include <stddef.h> +#include "base/no_destructor.h" #include "services/service_manager/public/cpp/connector.h" #include "ui/accessibility/ax_action_data.h" #include "ui/accessibility/ax_enums.mojom.h" #include "ui/accessibility/ax_event.h" +#include "ui/accessibility/platform/aura_window_properties.h" #include "ui/accessibility/platform/ax_unique_id.h" #include "ui/aura/mus/window_tree_client.h" #include "ui/aura/window.h" @@ -27,9 +29,6 @@ using display::Screen; namespace views { -// For external linkage. -constexpr int AXRemoteHost::kRemoteAXTreeID; - AXRemoteHost::AXRemoteHost() { AXAuraObjCache::GetInstance()->SetDelegate(this); } @@ -42,12 +41,12 @@ AXRemoteHost::~AXRemoteHost() { void AXRemoteHost::Init(service_manager::Connector* connector) { connector->BindInterface(ax::mojom::kAXHostServiceName, &ax_host_ptr_); - BindAndSetRemote(); + BindAndRegisterRemote(); } void AXRemoteHost::InitForTesting(ax::mojom::AXHostPtr host_ptr) { ax_host_ptr_ = std::move(host_ptr); - BindAndSetRemote(); + BindAndRegisterRemote(); } void AXRemoteHost::StartMonitoringWidget(Widget* widget) { @@ -62,6 +61,10 @@ void AXRemoteHost::StartMonitoringWidget(Widget* widget) { widget_ = widget; widget_->AddObserver(this); + DCHECK_NE(tree_id_, ui::AXTreeIDUnknown()); + widget_->GetNativeWindow()->SetProperty(ui::kChildAXTreeID, + new std::string(tree_id_.ToString())); + // The cache needs to track the root window to follow focus changes. AXAuraObjCache* cache = AXAuraObjCache::GetInstance(); cache->OnRootWindowObjCreated(widget_->GetNativeWindow()); @@ -71,7 +74,7 @@ void AXRemoteHost::StartMonitoringWidget(Widget* widget) { View* contents_view = widget_->widget_delegate()->GetContentsView(); AXAuraObjWrapper* contents_wrapper = cache->GetOrCreate(contents_view); - tree_source_ = std::make_unique<AXTreeSourceMus>(contents_wrapper); + tree_source_ = std::make_unique<AXTreeSourceMus>(contents_wrapper, tree_id_); tree_serializer_ = std::make_unique<AuraAXTreeSerializer>(tree_source_.get()); // Inform the serializer of the display device scale factor. @@ -99,9 +102,15 @@ void AXRemoteHost::HandleEvent(View* view, ax::mojom::Event event_type) { if (!enabled_) return; - AXAuraObjWrapper* aura_obj = - view ? AXAuraObjCache::GetInstance()->GetOrCreate(view) - : tree_source_->GetRoot(); + if (!view) { + SendEvent(tree_source_->GetRoot(), event_type); + return; + } + + // Can return null for views without a widget. + AXAuraObjWrapper* aura_obj = AXAuraObjCache::GetInstance()->GetOrCreate(view); + if (!aura_obj) + return; SendEvent(aura_obj, event_type); } @@ -168,15 +177,26 @@ void AXRemoteHost::FlushForTesting() { ax_host_ptr_.FlushForTesting(); } -void AXRemoteHost::BindAndSetRemote() { +void AXRemoteHost::BindAndRegisterRemote() { ax::mojom::AXRemoteHostPtr remote; binding_.Bind(mojo::MakeRequest(&remote)); - ax_host_ptr_->SetRemoteHost(std::move(remote)); + ax_host_ptr_->RegisterRemoteHost( + std::move(remote), + base::BindOnce(&AXRemoteHost::RegisterRemoteHostCallback, + base::Unretained(this))); +} + +void AXRemoteHost::RegisterRemoteHostCallback(const ui::AXTreeID& tree_id, + bool enabled) { + tree_id_ = tree_id; + + // Set the initial enabled state and send the AX tree if necessary. + OnAutomationEnabled(enabled); } void AXRemoteHost::Enable() { // Don't early-exit if already enabled. AXRemoteHost can start up in the - // "enabled" state even if ChromeVox is on at the moment the app launches. + // "enabled" state even if ChromeVox is off at the moment the app launches. // Turning on ChromeVox later will generate another OnAutomationEnabled() // call and we need to serialize the node tree again. This is similar to // AutomationManagerAura's behavior. https://crbug.com/876407 @@ -234,7 +254,7 @@ void AXRemoteHost::SendEvent(AXAuraObjWrapper* aura_obj, event.event_type = event_type; // Other fields are not used. - ax_host_ptr_->HandleAccessibilityEvent(kRemoteAXTreeID, updates, event); + ax_host_ptr_->HandleAccessibilityEvent(tree_id_, updates, event); } void AXRemoteHost::PerformHitTest(const ui::AXActionData& action) { diff --git a/chromium/ui/views/mus/ax_remote_host.h b/chromium/ui/views/mus/ax_remote_host.h index 7e506159487..bb432ba5073 100644 --- a/chromium/ui/views/mus/ax_remote_host.h +++ b/chromium/ui/views/mus/ax_remote_host.h @@ -10,6 +10,7 @@ #include "base/macros.h" #include "mojo/public/cpp/bindings/binding.h" +#include "ui/accessibility/ax_tree_id.h" #include "ui/accessibility/ax_tree_serializer.h" #include "ui/accessibility/mojom/ax_host.mojom.h" #include "ui/display/display_observer.h" @@ -41,10 +42,6 @@ class VIEWS_MUS_EXPORT AXRemoteHost : public ax::mojom::AXRemoteHost, public display::DisplayObserver, public AXAuraObjCache::Delegate { public: - // Well-known tree ID for the remote client. - // TODO(jamescook): Support different IDs for different clients. - static constexpr int kRemoteAXTreeID = -2; - AXRemoteHost(); ~AXRemoteHost() override; @@ -84,7 +81,10 @@ class VIEWS_MUS_EXPORT AXRemoteHost : public ax::mojom::AXRemoteHost, private: // Registers this object as a remote host for the parent AXHost. - void BindAndSetRemote(); + void BindAndRegisterRemote(); + + // Callback for initial state from AXHost. + void RegisterRemoteHostCallback(const ui::AXTreeID& tree_id, bool enabled); void Enable(); void Disable(); @@ -102,6 +102,9 @@ class VIEWS_MUS_EXPORT AXRemoteHost : public ax::mojom::AXRemoteHost, mojo::Binding<ax::mojom::AXRemoteHost> binding_{this}; + // ID to use for the AX tree. + ui::AXTreeID tree_id_; + // Whether accessibility automation support is enabled. bool enabled_ = false; diff --git a/chromium/ui/views/mus/ax_remote_host_unittest.cc b/chromium/ui/views/mus/ax_remote_host_unittest.cc index fd187350140..d6620cf9b2a 100644 --- a/chromium/ui/views/mus/ax_remote_host_unittest.cc +++ b/chromium/ui/views/mus/ax_remote_host_unittest.cc @@ -5,9 +5,11 @@ #include "ui/views/mus/ax_remote_host.h" #include "base/macros.h" +#include "base/no_destructor.h" #include "base/run_loop.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/accessibility/mojom/ax_host.mojom.h" +#include "ui/accessibility/platform/aura_window_properties.h" #include "ui/display/display.h" #include "ui/gfx/geometry/vector2d_f.h" #include "ui/gfx/transform.h" @@ -21,6 +23,13 @@ namespace views { namespace { +// Returns a well-known tree ID for the test widget. +const ui::AXTreeID& TestAXTreeID() { + static const base::NoDestructor<ui::AXTreeID> test_ax_tree_id( + ui::AXTreeID::FromString("123")); + return *test_ax_tree_id; +} + // Simulates the AXHostService in the browser. class TestAXHostService : public ax::mojom::AXHost { public: @@ -35,33 +44,34 @@ class TestAXHostService : public ax::mojom::AXHost { } void ResetCounts() { - add_client_count_ = 0; + remote_host_count_ = 0; event_count_ = 0; - last_tree_id_ = 0; + last_tree_id_ = ui::AXTreeIDUnknown(); last_updates_.clear(); last_event_ = ui::AXEvent(); } // ax::mojom::AXHost: - void SetRemoteHost(ax::mojom::AXRemoteHostPtr client) override { - ++add_client_count_; - client->OnAutomationEnabled(automation_enabled_); + void RegisterRemoteHost(ax::mojom::AXRemoteHostPtr client, + RegisterRemoteHostCallback cb) override { + ++remote_host_count_; + std::move(cb).Run(TestAXTreeID(), automation_enabled_); client.FlushForTesting(); } - void HandleAccessibilityEvent(int32_t tree_id, + void HandleAccessibilityEvent(const ui::AXTreeID& tree_id, const std::vector<ui::AXTreeUpdate>& updates, const ui::AXEvent& event) override { ++event_count_; - last_tree_id_ = tree_id; + last_tree_id_ = ui::AXTreeID::FromString(tree_id); last_updates_ = updates; last_event_ = event; } mojo::Binding<ax::mojom::AXHost> binding_{this}; bool automation_enabled_ = false; - int add_client_count_ = 0; + int remote_host_count_ = 0; int event_count_ = 0; - int last_tree_id_ = 0; + ui::AXTreeID last_tree_id_ = ui::AXTreeIDUnknown(); std::vector<ui::AXTreeUpdate> last_updates_; ui::AXEvent last_event_; @@ -127,7 +137,7 @@ TEST_F(AXRemoteHostTest, CreateRemote) { CreateRemote(&service); // Client registered itself with service. - EXPECT_EQ(1, service.add_client_count_); + EXPECT_EQ(1, service.remote_host_count_); } TEST_F(AXRemoteHostTest, AutomationEnabled) { @@ -136,7 +146,15 @@ TEST_F(AXRemoteHostTest, AutomationEnabled) { std::unique_ptr<Widget> widget = CreateTestWidget(); remote->FlushForTesting(); + // Tree ID is assigned. + std::string* tree_id_ptr = + widget->GetNativeWindow()->GetProperty(ui::kChildAXTreeID); + ASSERT_TRUE(tree_id_ptr); + ui::AXTreeID tree_id = ui::AXTreeID::FromString(*tree_id_ptr); + EXPECT_EQ(TestAXTreeID(), tree_id); + // Event was sent with initial hierarchy. + EXPECT_EQ(TestAXTreeID(), service.last_tree_id_); EXPECT_EQ(ax::mojom::Event::kLoadComplete, service.last_event_.event_type); EXPECT_EQ(AXAuraObjCache::GetInstance()->GetID( widget->widget_delegate()->GetContentsView()), @@ -165,13 +183,15 @@ TEST_F(AXRemoteHostTest, AutomationEnabledTwice) { EXPECT_EQ(ax::mojom::Event::kLoadComplete, service.last_event_.event_type); } -// Views can trigger accessibility events during Widget construction before the -// AXRemoteHost starts monitoring the widget. This happens with the material -// design focus ring on text fields. Verify we don't crash in this case. -// https://crbug.com/862759 -TEST_F(AXRemoteHostTest, SendEventBeforeWidgetCreated) { +// Verifies that a remote app doesn't crash if a View triggers an accessibility +// event before it is attached to a Widget. https://crbug.com/889121 +TEST_F(AXRemoteHostTest, SendEventOnViewWithNoWidget) { TestAXHostService service(true /*automation_enabled*/); AXRemoteHost* remote = CreateRemote(&service); + std::unique_ptr<Widget> widget = CreateTestWidget(); + remote->FlushForTesting(); + + // Create a view that is not yet associated with the widget. views::View view; remote->HandleEvent(&view, ax::mojom::Event::kLocationChanged); // No crash. @@ -230,6 +250,9 @@ TEST_F(AXRemoteHostTest, PerformAction) { // Create a view to sense the action. TestView view; + view.SetBounds(0, 0, 100, 100); + std::unique_ptr<Widget> widget = CreateTestWidget(); + widget->GetRootView()->AddChildView(&view); AXAuraObjCache::GetInstance()->GetOrCreate(&view); // Request an action on the view. diff --git a/chromium/ui/views/mus/ax_tree_source_mus.cc b/chromium/ui/views/mus/ax_tree_source_mus.cc index 7f767c443f6..6199fb3cabf 100644 --- a/chromium/ui/views/mus/ax_tree_source_mus.cc +++ b/chromium/ui/views/mus/ax_tree_source_mus.cc @@ -11,14 +11,17 @@ namespace views { -AXTreeSourceMus::AXTreeSourceMus(AXAuraObjWrapper* root) : root_(root) { +AXTreeSourceMus::AXTreeSourceMus(AXAuraObjWrapper* root, + const ui::AXTreeID& tree_id) + : root_(root), tree_id_(tree_id) { DCHECK(root_); + DCHECK_NE(tree_id_, ui::AXTreeIDUnknown()); } AXTreeSourceMus::~AXTreeSourceMus() = default; bool AXTreeSourceMus::GetTreeData(ui::AXTreeData* tree_data) const { - tree_data->tree_id = AXRemoteHost::kRemoteAXTreeID; + tree_data->tree_id = tree_id_; return AXTreeSourceViews::GetTreeData(tree_data); } diff --git a/chromium/ui/views/mus/ax_tree_source_mus.h b/chromium/ui/views/mus/ax_tree_source_mus.h index 6e3681e0773..6c9170747ed 100644 --- a/chromium/ui/views/mus/ax_tree_source_mus.h +++ b/chromium/ui/views/mus/ax_tree_source_mus.h @@ -6,6 +6,7 @@ #define UI_VIEWS_MUS_AX_TREE_SOURCE_MUS_H_ #include "base/macros.h" +#include "ui/accessibility/ax_tree_id.h" #include "ui/views/accessibility/ax_tree_source_views.h" #include "ui/views/mus/mus_export.h" @@ -20,7 +21,7 @@ class AXAuraObjWrapper; class VIEWS_MUS_EXPORT AXTreeSourceMus : public AXTreeSourceViews { public: // |root| must outlive this object. - explicit AXTreeSourceMus(AXAuraObjWrapper* root); + AXTreeSourceMus(AXAuraObjWrapper* root, const ui::AXTreeID& tree_id); ~AXTreeSourceMus() override; void set_device_scale_factor(float scale) { device_scale_factor_ = scale; } @@ -35,6 +36,9 @@ class VIEWS_MUS_EXPORT AXTreeSourceMus : public AXTreeSourceViews { // The top-level object to use for the AX tree. AXAuraObjWrapper* root_; + // ID to use for the AX tree. + const ui::AXTreeID tree_id_; + // The display device scale factor to use while serializing this update. float device_scale_factor_ = 1.f; diff --git a/chromium/ui/views/mus/ax_tree_source_mus_unittest.cc b/chromium/ui/views/mus/ax_tree_source_mus_unittest.cc index 2f7bc9a6299..b4fbdd54ac4 100644 --- a/chromium/ui/views/mus/ax_tree_source_mus_unittest.cc +++ b/chromium/ui/views/mus/ax_tree_source_mus_unittest.cc @@ -51,6 +51,7 @@ class AXTreeSourceMusTest : public ViewsTestBase { std::unique_ptr<Widget> widget_; Label* label_ = nullptr; // Owned by views hierarchy. + const ui::AXTreeID ax_tree_id_ = ui::AXTreeID::FromString("123"); private: DISALLOW_COPY_AND_ASSIGN(AXTreeSourceMusTest); @@ -59,17 +60,17 @@ class AXTreeSourceMusTest : public ViewsTestBase { TEST_F(AXTreeSourceMusTest, GetTreeData) { AXAuraObjWrapper* root = AXAuraObjCache::GetInstance()->GetOrCreate(widget_->GetContentsView()); - AXTreeSourceMus tree(root); + AXTreeSourceMus tree(root, ax_tree_id_); ui::AXTreeData tree_data; tree.GetTreeData(&tree_data); - EXPECT_EQ(AXRemoteHost::kRemoteAXTreeID, tree_data.tree_id); + EXPECT_EQ(ax_tree_id_, tree_data.tree_id); } TEST_F(AXTreeSourceMusTest, Serialize) { AXAuraObjCache* cache = AXAuraObjCache::GetInstance(); AXAuraObjWrapper* root = cache->GetOrCreate(widget_->GetContentsView()); - AXTreeSourceMus tree(root); + AXTreeSourceMus tree(root, ax_tree_id_); EXPECT_EQ(root, tree.GetRoot()); // Serialize the root. @@ -93,7 +94,7 @@ TEST_F(AXTreeSourceMusTest, ScaleFactor) { AXAuraObjWrapper* root = cache->GetOrCreate(widget_->GetContentsView()); // Simulate serializing a widget on a high-dpi display. - AXTreeSourceMus tree(root); + AXTreeSourceMus tree(root, ax_tree_id_); tree.set_device_scale_factor(2.f); // Serialize the root. diff --git a/chromium/ui/views/mus/desktop_window_tree_host_mus.cc b/chromium/ui/views/mus/desktop_window_tree_host_mus.cc index 93d30122d2a..61fd282fbb4 100644 --- a/chromium/ui/views/mus/desktop_window_tree_host_mus.cc +++ b/chromium/ui/views/mus/desktop_window_tree_host_mus.cc @@ -43,7 +43,7 @@ namespace views { namespace { // As the window manager renderers the non-client decorations this class does -// very little but honor the client area insets from the window manager. +// very little but honor kTopViewInset. class ClientSideNonClientFrameView : public NonClientFrameView, public aura::WindowObserver { public: @@ -53,16 +53,22 @@ class ClientSideNonClientFrameView : public NonClientFrameView, // provided by the window manager. GetViewAccessibility().set_is_ignored(true); - observed_.Add(widget_->GetNativeWindow()->GetRootWindow()); + // Initialize kTopViewInset to a default value. Further updates will come + // from Ash. This is necessary so that during app window creation, + // GetWindowBoundsForClientBounds() can calculate correctly. + const auto& values = views::WindowManagerFrameValues::instance(); + widget->GetNativeWindow()->SetProperty(aura::client::kTopViewInset, + widget->IsMaximized() + ? values.maximized_insets.top() + : values.normal_insets.top()); + observed_.Add(window()); } ~ClientSideNonClientFrameView() override {} private: - // Returns the default values of client area insets from the window manager. - static gfx::Insets GetDefaultWindowManagerInsets(bool is_maximized) { - const WindowManagerFrameValues& values = - WindowManagerFrameValues::instance(); - return is_maximized ? values.maximized_insets : values.normal_insets; + gfx::Insets GetClientInsets() const { + const int top_inset = window()->GetProperty(aura::client::kTopViewInset); + return gfx::Insets(top_inset, 0, 0, 0); } // View: @@ -75,7 +81,7 @@ class ClientSideNonClientFrameView : public NonClientFrameView, gfx::Rect result(GetLocalBounds()); if (widget_->IsFullscreen()) return result; - result.Inset(GetDefaultWindowManagerInsets(widget_->IsMaximized())); + result.Inset(GetClientInsets()); return result; } gfx::Rect GetWindowBoundsForClientBounds( @@ -83,12 +89,9 @@ class ClientSideNonClientFrameView : public NonClientFrameView, if (widget_->IsFullscreen()) return client_bounds; - const gfx::Insets insets( - GetDefaultWindowManagerInsets(widget_->IsMaximized())); - return gfx::Rect(client_bounds.x() - insets.left(), - client_bounds.y() - insets.top(), - client_bounds.width() + insets.width(), - client_bounds.height() + insets.height()); + gfx::Rect outset_bounds = client_bounds; + outset_bounds.Inset(-GetClientInsets()); + return outset_bounds; } int NonClientHitTest(const gfx::Point& point) override { return HTNOWHERE; } void GetWindowMask(const gfx::Size& size, gfx::Path* window_mask) override { @@ -136,20 +139,16 @@ class ClientSideNonClientFrameView : public NonClientFrameView, void OnWindowPropertyChanged(aura::Window* window, const void* key, intptr_t old) override { - // Do a re-layout on state changes which affect GetBoundsForClientView(). - // The associated bounds change would also cause a re-layout, but there may - // not be a bounds change or it may come from the server before the state is - // updated. - if (key == aura::client::kShowStateKey) { - if (GetBoundsForClientView() != widget_->client_view()->bounds() && - window->GetProperty(aura::client::kShowStateKey) != - ui::SHOW_STATE_MINIMIZED) { - InvalidateLayout(); - widget_->GetRootView()->Layout(); - } + if (key == aura::client::kTopViewInset) { + InvalidateLayout(); + widget_->GetRootView()->Layout(); } } + aura::Window* window() const { + return widget_->GetNativeWindow()->GetRootWindow(); + } + views::Widget* widget_; ScopedObserver<aura::Window, aura::WindowObserver> observed_{this}; @@ -286,20 +285,6 @@ void DesktopWindowTreeHostMus::SendClientAreaToServer() { std::vector<gfx::Rect>()); } -void DesktopWindowTreeHostMus::SendHitTestMaskToServer() { - if (!native_widget_delegate_->HasHitTestMask()) { - aura::WindowPortMus::Get(window())->SetHitTestMask(base::nullopt); - return; - } - - gfx::Path mask_path; - native_widget_delegate_->GetHitTestMask(&mask_path); - // TODO(jamescook): Use the full path for the mask. - gfx::Rect mask_rect = - gfx::ToEnclosingRect(gfx::SkRectToRectF(mask_path.getBounds())); - aura::WindowPortMus::Get(window())->SetHitTestMask(mask_rect); -} - bool DesktopWindowTreeHostMus::IsFocusClientInstalledOnFocusSynchronizer() const { return MusClient::Get() @@ -340,8 +325,11 @@ void DesktopWindowTreeHostMus::Init(const Widget::InitParams& params) { window()->SetProperty(aura::client::kShowStateKey, params.show_state); - if (!params.bounds.IsEmpty()) + if (!params.bounds.IsEmpty()) { + // Init the scale now (before InitHost below), it is used by SetBoundsInDIP. + IntializeDeviceScaleFactor(GetDisplay().device_scale_factor()); SetBoundsInDIP(params.bounds); + } cursor_manager_ = std::make_unique<wm::CursorManager>( std::make_unique<NativeCursorManagerMus>(window())); @@ -428,7 +416,6 @@ void DesktopWindowTreeHostMus::OnWidgetInitDone() { // the NonClientView was created, which means we may not have sent the // client-area and hit-test-mask. SendClientAreaToServer(); - SendHitTestMaskToServer(); MusClient::Get()->OnCaptureClientSet( aura::client::GetCaptureClient(window())); @@ -871,7 +858,6 @@ void DesktopWindowTreeHostMus::OnWindowManagerFrameValuesChanged() { } SendClientAreaToServer(); - SendHitTestMaskToServer(); } void DesktopWindowTreeHostMus::OnActiveFocusClientChanged( @@ -946,7 +932,6 @@ void DesktopWindowTreeHostMus::OnViewBoundsChanged(views::View* observed_view) { native_widget_delegate_->AsWidget()->non_client_view()->frame_view()); SendClientAreaToServer(); - SendHitTestMaskToServer(); } void DesktopWindowTreeHostMus::OnViewIsDeleting(View* observed_view) { diff --git a/chromium/ui/views/mus/desktop_window_tree_host_mus.h b/chromium/ui/views/mus/desktop_window_tree_host_mus.h index a93e0c297ed..17c0655e987 100644 --- a/chromium/ui/views/mus/desktop_window_tree_host_mus.h +++ b/chromium/ui/views/mus/desktop_window_tree_host_mus.h @@ -49,7 +49,6 @@ class VIEWS_MUS_EXPORT DesktopWindowTreeHostMus private: void SendClientAreaToServer(); - void SendHitTestMaskToServer(); // Returns true if the FocusClient associated with our window is installed on // the FocusSynchronizer. diff --git a/chromium/ui/views/mus/desktop_window_tree_host_mus_unittest.cc b/chromium/ui/views/mus/desktop_window_tree_host_mus_unittest.cc index 85bfbad2488..159770b5a1c 100644 --- a/chromium/ui/views/mus/desktop_window_tree_host_mus_unittest.cc +++ b/chromium/ui/views/mus/desktop_window_tree_host_mus_unittest.cc @@ -24,7 +24,6 @@ #include "ui/views/mus/mus_client.h" #include "ui/views/mus/mus_client_test_api.h" #include "ui/views/mus/screen_mus.h" -#include "ui/views/mus/window_manager_frame_values.h" #include "ui/views/test/views_test_base.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" @@ -240,42 +239,24 @@ TEST_F(DesktopWindowTreeHostMusTest, ActivateBeforeShow) { ->active_focus_client()); } -// Tests that changes to a widget's show state will cause the client area to be -// updated. -TEST_F(DesktopWindowTreeHostMusTest, ServerShowStateChangeUpdatesClientArea) { - WindowManagerFrameValues test_frame_values; - test_frame_values.normal_insets = {3, 0, 0, 0}; - test_frame_values.maximized_insets = {7, 0, 0, 0}; - WindowManagerFrameValues::SetInstance(test_frame_values); - +// Tests that changes to kTopViewInset will cause the client area to be updated. +TEST_F(DesktopWindowTreeHostMusTest, ServerTopInsetChangeUpdatesClientArea) { std::unique_ptr<Widget> widget(CreateWidget()); widget->Show(); - // Simulate state changes from the server. - auto set_widget_state = - [&widget](ui::WindowShowState state) { - widget->GetNativeWindow()->GetRootWindow()->SetProperty( - aura::client::kShowStateKey, state); - }; - - // A restored window respects normal_insets (the client area is inset from the - // root view). - gfx::Rect expected_restored_bounds = widget->GetRootView()->bounds(); - expected_restored_bounds.Inset(test_frame_values.normal_insets); - EXPECT_EQ(expected_restored_bounds, widget->client_view()->bounds()); - - // A fullscreen window has no insets. - EXPECT_FALSE(widget->IsFullscreen()); - set_widget_state(ui::SHOW_STATE_FULLSCREEN); - EXPECT_TRUE(widget->IsFullscreen()); + auto set_top_inset = [&widget](int value) { + widget->GetNativeWindow()->GetRootWindow()->SetProperty( + aura::client::kTopViewInset, value); + }; + EXPECT_EQ(widget->GetRootView()->bounds(), widget->client_view()->bounds()); - // A maximized window respects maximized_insets. - gfx::Rect expected_maximized_bounds = widget->GetRootView()->bounds(); - expected_maximized_bounds.Inset(test_frame_values.maximized_insets); - set_widget_state(ui::SHOW_STATE_MAXIMIZED); - EXPECT_FALSE(widget->IsFullscreen()); - EXPECT_EQ(expected_maximized_bounds, widget->client_view()->bounds()); + set_top_inset(3); + gfx::Rect root_bounds = widget->GetRootView()->bounds(); + root_bounds.Inset(gfx::Insets(3, 0, 0, 0)); + + set_top_inset(0); + EXPECT_EQ(widget->GetRootView()->bounds(), widget->client_view()->bounds()); } TEST_F(DesktopWindowTreeHostMusTest, CursorClientDuringTearDown) { diff --git a/chromium/ui/views/mus/drag_interactive_uitest.cc b/chromium/ui/views/mus/drag_interactive_uitest.cc index 999ec9e269a..92dc07d7a7d 100644 --- a/chromium/ui/views/mus/drag_interactive_uitest.cc +++ b/chromium/ui/views/mus/drag_interactive_uitest.cc @@ -86,24 +86,24 @@ class TargetView : public views::View { DISALLOW_COPY_AND_ASSIGN(TargetView); }; -std::unique_ptr<ui::PointerEvent> CreateMouseMoveEvent(int x, int y) { - return std::make_unique<ui::PointerEvent>(ui::MouseEvent( +std::unique_ptr<ui::MouseEvent> CreateMouseMoveEvent(int x, int y) { + return std::make_unique<ui::MouseEvent>( ui::ET_MOUSE_MOVED, gfx::Point(x, y), gfx::Point(x, y), - ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_NONE)); + ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, ui::EF_NONE); } -std::unique_ptr<ui::PointerEvent> CreateMouseDownEvent(int x, int y) { - return std::make_unique<ui::PointerEvent>( - ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(x, y), gfx::Point(x, y), - ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, - ui::EF_LEFT_MOUSE_BUTTON)); +std::unique_ptr<ui::MouseEvent> CreateMouseDownEvent(int x, int y) { + return std::make_unique<ui::MouseEvent>( + ui::ET_MOUSE_PRESSED, gfx::Point(x, y), gfx::Point(x, y), + ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, + ui::EF_LEFT_MOUSE_BUTTON); } -std::unique_ptr<ui::PointerEvent> CreateMouseUpEvent(int x, int y) { - return std::make_unique<ui::PointerEvent>( - ui::MouseEvent(ui::ET_MOUSE_RELEASED, gfx::Point(x, y), gfx::Point(x, y), - ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, - ui::EF_LEFT_MOUSE_BUTTON)); +std::unique_ptr<ui::MouseEvent> CreateMouseUpEvent(int x, int y) { + return std::make_unique<ui::MouseEvent>( + ui::ET_MOUSE_RELEASED, gfx::Point(x, y), gfx::Point(x, y), + ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, + ui::EF_LEFT_MOUSE_BUTTON); } } // namespace diff --git a/chromium/ui/views/mus/mus_client.cc b/chromium/ui/views/mus/mus_client.cc index 4678479162a..4bed24086c3 100644 --- a/chromium/ui/views/mus/mus_client.cc +++ b/chromium/ui/views/mus/mus_client.cc @@ -270,7 +270,7 @@ MusClient::ConfigurePropertiesFromParams( // TODO(crbug.com/667566): Support additional scales or gfx::Image[Skia]. gfx::ImageSkia app_icon = init_params.delegate->GetWindowAppIcon(); - SkBitmap app_bitmap = app_icon.GetRepresentation(1.f).sk_bitmap(); + SkBitmap app_bitmap = app_icon.GetRepresentation(1.f).GetBitmap(); if (!app_bitmap.isNull()) { properties[WindowManager::kAppIcon_Property] = mojo::ConvertTo<TransportType>(app_bitmap); @@ -278,7 +278,7 @@ MusClient::ConfigurePropertiesFromParams( // TODO(crbug.com/667566): Support additional scales or gfx::Image[Skia]. gfx::ImageSkia window_icon = init_params.delegate->GetWindowIcon(); - SkBitmap window_bitmap = window_icon.GetRepresentation(1.f).sk_bitmap(); + SkBitmap window_bitmap = window_icon.GetRepresentation(1.f).GetBitmap(); if (!window_bitmap.isNull()) { properties[WindowManager::kWindowIcon_Property] = mojo::ConvertTo<TransportType>(window_bitmap); diff --git a/chromium/ui/views/mus/mus_views_delegate.cc b/chromium/ui/views/mus/mus_views_delegate.cc index e383ac00d69..6456b0d31d7 100644 --- a/chromium/ui/views/mus/mus_views_delegate.cc +++ b/chromium/ui/views/mus/mus_views_delegate.cc @@ -6,6 +6,7 @@ #include "ui/views/mus/ax_remote_host.h" #include "ui/views/mus/mus_client.h" +#include "ui/views/mus/pointer_watcher_event_router.h" namespace views { @@ -19,4 +20,19 @@ void MusViewsDelegate::NotifyAccessibilityEvent(View* view, MusClient::Get()->ax_remote_host()->HandleEvent(view, event_type); } +void MusViewsDelegate::AddPointerWatcher(PointerWatcher* pointer_watcher, + bool wants_moves) { + MusClient::Get()->pointer_watcher_event_router()->AddPointerWatcher( + pointer_watcher, wants_moves); +} + +void MusViewsDelegate::RemovePointerWatcher(PointerWatcher* pointer_watcher) { + MusClient::Get()->pointer_watcher_event_router()->RemovePointerWatcher( + pointer_watcher); +} + +bool MusViewsDelegate::IsPointerWatcherSupported() const { + return true; +} + } // namespace views diff --git a/chromium/ui/views/mus/mus_views_delegate.h b/chromium/ui/views/mus/mus_views_delegate.h index b7d55503ac1..6346df2979b 100644 --- a/chromium/ui/views/mus/mus_views_delegate.h +++ b/chromium/ui/views/mus/mus_views_delegate.h @@ -20,6 +20,10 @@ class VIEWS_MUS_EXPORT MusViewsDelegate : public ViewsDelegate { // ViewsDelegate: void NotifyAccessibilityEvent(View* view, ax::mojom::Event event_type) override; + void AddPointerWatcher(PointerWatcher* pointer_watcher, + bool wants_moves) override; + void RemovePointerWatcher(PointerWatcher* pointer_watcher) override; + bool IsPointerWatcherSupported() const override; private: LayoutProvider layout_provider_; diff --git a/chromium/ui/views/mus/remote_view/remote_view_host.cc b/chromium/ui/views/mus/remote_view/remote_view_host.cc index a203d2b14fd..a0fc60681e3 100644 --- a/chromium/ui/views/mus/remote_view/remote_view_host.cc +++ b/chromium/ui/views/mus/remote_view/remote_view_host.cc @@ -15,7 +15,14 @@ namespace views { -RemoteViewHost::RemoteViewHost() = default; +RemoteViewHost::RemoteViewHost() + : embedding_root_(std::make_unique<aura::Window>(nullptr)) { + embedding_root_->set_owned_by_parent(false); + embedding_root_->SetName("RemoteViewHostWindow"); + embedding_root_->SetType(aura::client::WINDOW_TYPE_CONTROL); + embedding_root_->Init(ui::LAYER_NOT_DRAWN); +} + RemoteViewHost::~RemoteViewHost() = default; void RemoteViewHost::EmbedUsingToken(const base::UnguessableToken& embed_token, @@ -29,29 +36,11 @@ void RemoteViewHost::EmbedUsingToken(const base::UnguessableToken& embed_token, embed_callback_ = std::move(callback); if (GetWidget()) - CreateEmbeddingRoot(); + EmbedImpl(); } -void RemoteViewHost::CreateEmbeddingRoot() { - // Should not be attached to anything. - DCHECK(!native_view()); - - // There is a pending embed request. - DCHECK(!embed_token_.is_empty()); - - embedding_root_ = std::make_unique<aura::Window>(nullptr); - embedding_root_->set_owned_by_parent(false); - - embedding_root_->SetName("RemoteViewHostWindow"); - embedding_root_->SetProperty(aura::client::kEmbedType, - aura::client::WindowEmbedType::EMBED_IN_OWNER); - embedding_root_->SetType(aura::client::WINDOW_TYPE_CONTROL); - embedding_root_->Init(ui::LAYER_NOT_DRAWN); - - // Must happen before EmbedUsingToken call for window server to figure out - // the relevant display. - Attach(embedding_root_.get()); - +void RemoteViewHost::EmbedImpl() { + DCHECK(IsEmbedPending()); aura::WindowPortMus::Get(embedding_root_.get()) ->EmbedUsingToken(embed_token_, embed_flags_, base::BindOnce(&RemoteViewHost::OnEmbedResult, @@ -60,17 +49,17 @@ void RemoteViewHost::CreateEmbeddingRoot() { void RemoteViewHost::OnEmbedResult(bool success) { LOG_IF(ERROR, !success) << "Failed to embed, token=" << embed_token_; - - if (!success && embedding_root_) - embedding_root_.reset(); - + embed_token_ = {}; if (embed_callback_) std::move(embed_callback_).Run(success); } void RemoteViewHost::AddedToWidget() { - if (!native_view() && !embed_token_.is_empty()) - CreateEmbeddingRoot(); + if (native_view()) + return; + Attach(embedding_root_.get()); + if (IsEmbedPending()) + EmbedImpl(); } } // namespace views diff --git a/chromium/ui/views/mus/remote_view/remote_view_host.h b/chromium/ui/views/mus/remote_view/remote_view_host.h index 44b1552f325..2a3e4530124 100644 --- a/chromium/ui/views/mus/remote_view/remote_view_host.h +++ b/chromium/ui/views/mus/remote_view/remote_view_host.h @@ -36,8 +36,10 @@ class RemoteViewHost : public views::NativeViewHost { EmbedCallback callback); private: + bool IsEmbedPending() const { return !embed_token_.is_empty(); } + // Creates the embedding aura::Window and attach to it. - void CreateEmbeddingRoot(); + void EmbedImpl(); // Invoked after the embed operation. void OnEmbedResult(bool success); @@ -49,7 +51,7 @@ class RemoteViewHost : public views::NativeViewHost { int embed_flags_ = 0; EmbedCallback embed_callback_; - std::unique_ptr<aura::Window> embedding_root_; + const std::unique_ptr<aura::Window> embedding_root_; base::WeakPtrFactory<RemoteViewHost> weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(RemoteViewHost); diff --git a/chromium/ui/views/mus/remote_view/remote_view_host_unittest.cc b/chromium/ui/views/mus/remote_view/remote_view_host_unittest.cc index 611c00151cd..30db5c39f9a 100644 --- a/chromium/ui/views/mus/remote_view/remote_view_host_unittest.cc +++ b/chromium/ui/views/mus/remote_view/remote_view_host_unittest.cc @@ -80,21 +80,20 @@ class RemoteViewHostTest : public aura::test::AuraTestBase { DISALLOW_COPY_AND_ASSIGN(RemoteViewHostTest); }; -// Tests that the embed operation fails with an unknown token and RemoteViewHost -// will not be attached. +// Tests that the embed operation fails with an unknown token. TEST_F(RemoteViewHostTest, BadEmbed) { const base::UnguessableToken unknown_token = base::UnguessableToken::Create(); // Ownership will be passed to |widget| later. RemoteViewHost* host = new RemoteViewHost(); std::unique_ptr<views::Widget> widget = CreateTestWidget(host); - EXPECT_FALSE(host->native_view()); + EXPECT_TRUE(host->native_view()); // Embed fails with unknown token. EXPECT_FALSE(Embed(host, unknown_token)); - // |host| is not attached after adding to a widget. - EXPECT_FALSE(host->native_view()); + // |host| is still attached despite the Embed failure. + EXPECT_TRUE(host->native_view()); } // Tests when RemoveViewHost is added to a widget before embedding. @@ -105,15 +104,15 @@ TEST_F(RemoteViewHostTest, AddToWidgetBeforeEmbed) { // Ownership will be passed to |widget| later. RemoteViewHost* host = new RemoteViewHost(); - // |host| is not attached because embed operation is not performed. + // |host| is not attached until the widget is created. EXPECT_FALSE(host->native_view()); std::unique_ptr<views::Widget> widget = CreateTestWidget(host); - EXPECT_FALSE(host->native_view()); + EXPECT_TRUE(host->native_view()); // Embed succeeds. EXPECT_TRUE(Embed(host, token)); - // |host| is now attached to the embedding window. + // |host| is still attached to the embedding window. EXPECT_TRUE(host->native_view()); } diff --git a/chromium/ui/views/mus/views_mus_test_suite.cc b/chromium/ui/views/mus/views_mus_test_suite.cc index 40dafe4c9db..1beddbf9de8 100644 --- a/chromium/ui/views/mus/views_mus_test_suite.cc +++ b/chromium/ui/views/mus/views_mus_test_suite.cc @@ -14,7 +14,6 @@ #include "base/run_loop.h" #include "base/synchronization/waitable_event.h" #include "base/threading/simple_thread.h" -#include "base/threading/thread.h" #include "mojo/core/embedder/embedder.h" #include "mojo/core/embedder/scoped_ipc_support.h" #include "services/catalog/catalog.h" @@ -68,24 +67,18 @@ class DefaultService : public service_manager::Service { class ServiceManagerConnection { public: ServiceManagerConnection() - : thread_("Persistent service_manager connections"), - ipc_thread_("IPC thread") { + : thread_("Persistent service_manager connections") { catalog::Catalog::LoadDefaultCatalogManifest( base::FilePath(kCatalogFilename)); - mojo::core::Init(); - ipc_thread_.StartWithOptions( - base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); - ipc_support_ = std::make_unique<mojo::core::ScopedIPCSupport>( - ipc_thread_.task_runner(), - mojo::core::ScopedIPCSupport::ShutdownPolicy::CLEAN); - base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::AUTOMATIC, base::WaitableEvent::InitialState::NOT_SIGNALED); base::Thread::Options options; thread_.StartWithOptions(options); thread_.task_runner()->PostTask( - FROM_HERE, base::BindOnce(&ServiceManagerConnection::SetUpConnections, - base::Unretained(this), &wait)); + FROM_HERE, + base::BindOnce( + &ServiceManagerConnection::SetUpConnectionsOnBackgroundThread, + base::Unretained(this), &wait)); wait.Wait(); } @@ -94,8 +87,9 @@ class ServiceManagerConnection { base::WaitableEvent::InitialState::NOT_SIGNALED); thread_.task_runner()->PostTask( FROM_HERE, - base::BindOnce(&ServiceManagerConnection::TearDownConnections, - base::Unretained(this), &wait)); + base::BindOnce( + &ServiceManagerConnection::TearDownConnectionsOnBackgroundThread, + base::Unretained(this), &wait)); wait.Wait(); } @@ -124,7 +118,7 @@ class ServiceManagerConnection { wait->Signal(); } - void SetUpConnections(base::WaitableEvent* wait) { + void SetUpConnectionsOnBackgroundThread(base::WaitableEvent* wait) { background_service_manager_ = std::make_unique<service_manager::BackgroundServiceManager>(nullptr, nullptr); @@ -140,8 +134,9 @@ class ServiceManagerConnection { wait->Signal(); } - void TearDownConnections(base::WaitableEvent* wait) { + void TearDownConnectionsOnBackgroundThread(base::WaitableEvent* wait) { context_.reset(); + background_service_manager_.reset(); wait->Signal(); } @@ -155,8 +150,6 @@ class ServiceManagerConnection { } base::Thread thread_; - base::Thread ipc_thread_; - std::unique_ptr<mojo::core::ScopedIPCSupport> ipc_support_; std::unique_ptr<service_manager::BackgroundServiceManager> background_service_manager_; std::unique_ptr<service_manager::ServiceContext> context_; @@ -226,7 +219,7 @@ std::unique_ptr<PlatformTestHelper> CreatePlatformTestHelper() { } // namespace ViewsMusTestSuite::ViewsMusTestSuite(int argc, char** argv) - : ViewsTestSuite(argc, argv) {} + : ViewsTestSuite(argc, argv), ipc_thread_("IPC thread") {} ViewsMusTestSuite::~ViewsMusTestSuite() {} @@ -248,6 +241,13 @@ void ViewsMusTestSuite::Initialize() { switches::kEnableFeatures, features::kMash.name); PlatformTestHelper::set_factory(base::Bind(&CreatePlatformTestHelper)); + + mojo::core::Init(); + ipc_thread_.StartWithOptions( + base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); + ipc_support_ = std::make_unique<mojo::core::ScopedIPCSupport>( + ipc_thread_.task_runner(), + mojo::core::ScopedIPCSupport::ShutdownPolicy::CLEAN); } void ViewsMusTestSuite::InitializeEnv() { diff --git a/chromium/ui/views/mus/views_mus_test_suite.h b/chromium/ui/views/mus/views_mus_test_suite.h index 4dc9d09dc07..8490ab91912 100644 --- a/chromium/ui/views/mus/views_mus_test_suite.h +++ b/chromium/ui/views/mus/views_mus_test_suite.h @@ -9,8 +9,15 @@ #include "base/macros.h" #include "base/test/scoped_feature_list.h" +#include "base/threading/thread.h" #include "ui/views/views_test_suite.h" +namespace mojo { +namespace core { +class ScopedIPCSupport; +} +} // namespace mojo + namespace views { class ViewsMusTestSuite : public ViewsTestSuite { @@ -24,6 +31,9 @@ class ViewsMusTestSuite : public ViewsTestSuite { void InitializeEnv() override; void DestroyEnv() override; + base::Thread ipc_thread_; + std::unique_ptr<mojo::core::ScopedIPCSupport> ipc_support_; + base::test::ScopedFeatureList feature_list_; std::unique_ptr<aura::Env> env_; diff --git a/chromium/ui/views/painter.cc b/chromium/ui/views/painter.cc index 24bb9e2a566..da297c1a02b 100644 --- a/chromium/ui/views/painter.cc +++ b/chromium/ui/views/painter.cc @@ -31,7 +31,9 @@ class SolidRoundRectPainter : public Painter { SolidRoundRectPainter(SkColor bg_color, SkColor stroke_color, float radius, - const gfx::Insets& insets); + const gfx::Insets& insets, + SkBlendMode blend_mode, + bool antialias); ~SolidRoundRectPainter() override; // Painter: @@ -43,6 +45,8 @@ class SolidRoundRectPainter : public Painter { const SkColor stroke_color_; const float radius_; const gfx::Insets insets_; + const SkBlendMode blend_mode_; + const bool antialias_; DISALLOW_COPY_AND_ASSIGN(SolidRoundRectPainter); }; @@ -50,11 +54,15 @@ class SolidRoundRectPainter : public Painter { SolidRoundRectPainter::SolidRoundRectPainter(SkColor bg_color, SkColor stroke_color, float radius, - const gfx::Insets& insets) + const gfx::Insets& insets, + SkBlendMode blend_mode, + bool antialias) : bg_color_(bg_color), stroke_color_(stroke_color), radius_(radius), - insets_(insets) {} + insets_(insets), + blend_mode_(blend_mode), + antialias_(antialias) {} SolidRoundRectPainter::~SolidRoundRectPainter() {} @@ -68,20 +76,27 @@ void SolidRoundRectPainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) { gfx::Rect inset_rect(size); inset_rect.Inset(insets_); - gfx::RectF border_rect_f(gfx::ScaleToEnclosingRect(inset_rect, scale)); - const SkScalar scaled_corner_radius = SkFloatToScalar(radius_ * scale); + gfx::RectF fill_rect(gfx::ScaleToEnclosingRect(inset_rect, scale)); + gfx::RectF stroke_rect = fill_rect; + float scaled_radius = radius_ * scale; cc::PaintFlags flags; - flags.setAntiAlias(true); + flags.setBlendMode(blend_mode_); + if (antialias_) + flags.setAntiAlias(true); flags.setStyle(cc::PaintFlags::kFill_Style); flags.setColor(bg_color_); - canvas->DrawRoundRect(border_rect_f, scaled_corner_radius, flags); - - border_rect_f.Inset(gfx::InsetsF(0.5f)); - flags.setStyle(cc::PaintFlags::kStroke_Style); - flags.setStrokeWidth(1); - flags.setColor(stroke_color_); - canvas->DrawRoundRect(border_rect_f, scaled_corner_radius, flags); + canvas->DrawRoundRect(fill_rect, scaled_radius, flags); + + if (stroke_color_ != SK_ColorTRANSPARENT) { + constexpr float kStrokeWidth = 1.0f; + stroke_rect.Inset(gfx::InsetsF(kStrokeWidth / 2)); + scaled_radius -= kStrokeWidth / 2; + flags.setStyle(cc::PaintFlags::kStroke_Style); + flags.setStrokeWidth(kStrokeWidth); + flags.setColor(stroke_color_); + canvas->DrawRoundRect(stroke_rect, scaled_radius, flags); + } } // DashedFocusPainter ---------------------------------------------------------- @@ -266,18 +281,22 @@ void Painter::PaintFocusPainter(View* view, std::unique_ptr<Painter> Painter::CreateSolidRoundRectPainter( SkColor color, float radius, - const gfx::Insets& insets) { - return std::make_unique<SolidRoundRectPainter>(color, SK_ColorTRANSPARENT, - radius, insets); + const gfx::Insets& insets, + SkBlendMode blend_mode, + bool antialias) { + return std::make_unique<SolidRoundRectPainter>( + color, SK_ColorTRANSPARENT, radius, insets, blend_mode, antialias); } // static std::unique_ptr<Painter> Painter::CreateRoundRectWith1PxBorderPainter( SkColor bg_color, SkColor stroke_color, - float radius) { - return std::make_unique<SolidRoundRectPainter>(bg_color, stroke_color, radius, - gfx::Insets()); + float radius, + SkBlendMode blend_mode, + bool antialias) { + return std::make_unique<SolidRoundRectPainter>( + bg_color, stroke_color, radius, gfx::Insets(), blend_mode, antialias); } // static diff --git a/chromium/ui/views/painter.h b/chromium/ui/views/painter.h index cb3f495dc8d..984348ee13a 100644 --- a/chromium/ui/views/painter.h +++ b/chromium/ui/views/painter.h @@ -11,6 +11,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" +#include "third_party/skia/include/core/SkBlendMode.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/base/nine_image_painter_factory.h" #include "ui/gfx/geometry/insets.h" @@ -57,14 +58,18 @@ class VIEWS_EXPORT Painter { static std::unique_ptr<Painter> CreateSolidRoundRectPainter( SkColor color, float radius, - const gfx::Insets& insets = gfx::Insets()); + const gfx::Insets& insets = gfx::Insets(), + SkBlendMode blend_mode = SkBlendMode::kSrcOver, + bool antialias = true); // Creates a painter that draws a RoundRect with a solid color and a given // corner radius, and also adds a 1px border (inset) in the given color. static std::unique_ptr<Painter> CreateRoundRectWith1PxBorderPainter( SkColor bg_color, SkColor stroke_color, - float radius); + float radius, + SkBlendMode blend_mode = SkBlendMode::kSrcOver, + bool antialias = true); // Creates a painter that divides |image| into nine regions. The four corners // are rendered at the size specified in insets (eg. the upper-left corner is diff --git a/chromium/ui/views/touchui/touch_selection_controller_impl.cc b/chromium/ui/views/touchui/touch_selection_controller_impl.cc index ae97f1994e4..5264dfe5712 100644 --- a/chromium/ui/views/touchui/touch_selection_controller_impl.cc +++ b/chromium/ui/views/touchui/touch_selection_controller_impl.cc @@ -1,10 +1,9 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// 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/views/touchui/touch_selection_controller_impl.h" -#include "base/macros.h" #include "base/metrics/histogram_macros.h" #include "base/time/time.h" #include "ui/aura/client/cursor_client.h" @@ -19,6 +18,7 @@ #include "ui/gfx/image/image.h" #include "ui/gfx/path.h" #include "ui/resources/grit/ui_resources.h" +#include "ui/views/views_delegate.h" #include "ui/views/widget/widget.h" #include "ui/views/widget/widget_delegate.h" #include "ui/wm/core/coordinate_conversion.h" @@ -74,14 +74,14 @@ const int kSelectionHandleBarBottomAllowance = 3; // Creates a widget to host SelectionHandleView. views::Widget* CreateTouchSelectionPopupWidget( - gfx::NativeView context, + gfx::NativeView parent, views::WidgetDelegate* widget_delegate) { views::Widget* widget = new views::Widget; views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; params.shadow_type = views::Widget::InitParams::SHADOW_TYPE_NONE; params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; - params.parent = context; + params.parent = parent; params.delegate = widget_delegate; widget->Init(params); return widget; @@ -202,34 +202,6 @@ gfx::Rect BoundToRect(const gfx::SelectionBound& bound) { bound.edge_bottom_rounded()); } -// A WindowTargeter that insets the top of the touch handle's hit-test region. -// This ensures that the client receives touch events above the painted image. -// The widget extends its height to handle touch events below the painted image. -class TouchHandleWindowTargeter : public aura::WindowTargeter { - public: - explicit TouchHandleWindowTargeter(aura::Window* window) : window_(window) {} - ~TouchHandleWindowTargeter() override = default; - - void SetTopInset(int inset) { SetInsets(gfx::Insets(inset, 0, 0, 0)); } - - // aura::WindowTargeter: - void OnSetInsets(const gfx::Insets& last_mouse_extend, - const gfx::Insets& last_touch_extend) override { - // Send the targeter insets to the window service if this is a mus client. - // This helps the window service send events directly to the text window. - // OnSetInsets is generally only called when the insets actually change. - if (window_->env()->mode() == aura::Env::Mode::MUS) { - gfx::Rect mask(window_->bounds().size()); - mask.Inset(touch_extend()); - aura::WindowPortMus::Get(window_->GetRootWindow())->SetHitTestMask(mask); - } - } - - private: - aura::Window* window_; - DISALLOW_COPY_AND_ASSIGN(TouchHandleWindowTargeter); -}; - } // namespace namespace views { @@ -238,20 +210,24 @@ using EditingHandleView = TouchSelectionControllerImpl::EditingHandleView; // A View that displays the text selection handle. class TouchSelectionControllerImpl::EditingHandleView - : public views::WidgetDelegateView { + : public WidgetDelegateView { public: EditingHandleView(TouchSelectionControllerImpl* controller, - gfx::NativeView context, + gfx::NativeView parent, bool is_cursor_handle) : controller_(controller), image_(GetCenterHandleImage()), is_cursor_handle_(is_cursor_handle), draw_invisible_(false), weak_ptr_factory_(this) { - widget_.reset(CreateTouchSelectionPopupWidget(context, this)); + widget_.reset(CreateTouchSelectionPopupWidget(parent, this)); + targeter_ = new aura::WindowTargeter(); aura::Window* window = widget_->GetNativeWindow(); - targeter_ = new TouchHandleWindowTargeter(window); + // For Mus clients, adjust targeting of the handle's client root window, + // constructed by the window server for the handle's "content" window. + if (window->env()->mode() == aura::Env::Mode::MUS) + window = window->GetRootWindow(); window->SetEventTargeter(std::unique_ptr<aura::WindowTargeter>(targeter_)); // We are owned by the TouchSelectionControllerImpl. @@ -264,12 +240,12 @@ class TouchSelectionControllerImpl::EditingHandleView return selection_bound_.type(); } - // Overridden from views::WidgetDelegateView: + // WidgetDelegateView: void DeleteDelegate() override { // We are owned and deleted by TouchSelectionControllerImpl. } - // Overridden from views::View: + // View: void OnPaint(gfx::Canvas* canvas) override { if (draw_invisible_) return; @@ -379,8 +355,10 @@ class TouchSelectionControllerImpl::EditingHandleView selection_bound_.SetEdge(gfx::PointF(edge_top), gfx::PointF(edge_bottom)); } - targeter_->SetTopInset(selection_bound_.GetHeight() + - kSelectionHandleVerticalVisualOffset); + const gfx::Insets insets( + selection_bound_.GetHeight() + kSelectionHandleVerticalVisualOffset, 0, + 0, 0); + targeter_->SetInsets(insets, insets); } void SetDrawInvisible(bool draw_invisible) { @@ -394,11 +372,11 @@ class TouchSelectionControllerImpl::EditingHandleView std::unique_ptr<Widget> widget_; TouchSelectionControllerImpl* controller_; - // A custom targeter that shifts the hit-test target below the apparent bounds + // A WindowTargeter that shifts the hit-test target below the apparent bounds // to make dragging easier. The |widget_|'s NativeWindow takes ownership over // the |targeter_| but since the |widget_|'s lifetime is known to this class, // it can safely access the |targeter_|. - TouchHandleWindowTargeter* targeter_; + aura::WindowTargeter* targeter_; // In local coordinates gfx::SelectionBound selection_bound_; @@ -427,23 +405,20 @@ class TouchSelectionControllerImpl::EditingHandleView TouchSelectionControllerImpl::TouchSelectionControllerImpl( ui::TouchEditable* client_view) : client_view_(client_view), - client_widget_(nullptr), - selection_handle_1_(new EditingHandleView(this, - client_view->GetNativeView(), - false)), - selection_handle_2_(new EditingHandleView(this, - client_view->GetNativeView(), - false)), - cursor_handle_(new EditingHandleView(this, - client_view->GetNativeView(), - true)), - command_executed_(false), - dragging_handle_(nullptr) { + selection_handle_1_( + new EditingHandleView(this, client_view->GetNativeView(), false)), + selection_handle_2_( + new EditingHandleView(this, client_view->GetNativeView(), false)), + cursor_handle_( + new EditingHandleView(this, client_view->GetNativeView(), true)) { selection_start_time_ = base::TimeTicks::Now(); aura::Window* client_window = client_view_->GetNativeView(); client_widget_ = Widget::GetTopLevelWidgetForNativeView(client_window); + // Observe client widget moves and resizes to update the selection handles. if (client_widget_) client_widget_->AddObserver(this); + if (ViewsDelegate::GetInstance()->IsPointerWatcherSupported()) + ViewsDelegate::GetInstance()->AddPointerWatcher(this, true); aura::Env::GetInstance()->AddPreTargetHandler(this); } @@ -452,6 +427,8 @@ TouchSelectionControllerImpl::~TouchSelectionControllerImpl() { command_executed_); HideQuickMenu(); aura::Env::GetInstance()->RemovePreTargetHandler(this); + if (ViewsDelegate::GetInstance()->IsPointerWatcherSupported()) + ViewsDelegate::GetInstance()->RemovePointerWatcher(this); if (client_widget_) client_widget_->RemoveObserver(this); } @@ -649,6 +626,18 @@ void TouchSelectionControllerImpl::OnWidgetBoundsChanged( SelectionChanged(); } +void TouchSelectionControllerImpl::OnPointerEventObserved( + const ui::PointerEvent& event, + const gfx::Point& location_in_screen, + gfx::NativeView target) { + // Disregard CursorClient::IsMouseEventsEnabled, it is disabled for touch + // events in this client, but not re-enabled for mouse events sent elsewhere. + if (event.pointer_details().pointer_type == + ui::EventPointerType::POINTER_TYPE_MOUSE) { + client_view_->DestroyTouchSelection(); + } +} + void TouchSelectionControllerImpl::OnKeyEvent(ui::KeyEvent* event) { client_view_->DestroyTouchSelection(); } @@ -773,11 +762,11 @@ gfx::Rect TouchSelectionControllerImpl::GetExpectedHandleBounds( return GetSelectionWidgetBounds(bound); } -views::WidgetDelegateView* TouchSelectionControllerImpl::GetHandle1View() { +WidgetDelegateView* TouchSelectionControllerImpl::GetHandle1View() { return selection_handle_1_.get(); } -views::WidgetDelegateView* TouchSelectionControllerImpl::GetHandle2View() { +WidgetDelegateView* TouchSelectionControllerImpl::GetHandle2View() { return selection_handle_2_.get(); } diff --git a/chromium/ui/views/touchui/touch_selection_controller_impl.h b/chromium/ui/views/touchui/touch_selection_controller_impl.h index cf222421548..7e231e126e1 100644 --- a/chromium/ui/views/touchui/touch_selection_controller_impl.h +++ b/chromium/ui/views/touchui/touch_selection_controller_impl.h @@ -1,9 +1,9 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// 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_UI_VIEWS_TOUCHUI_TOUCH_SELECTION_CONTROLLER_IMPL_H_ -#define UI_UI_VIEWS_TOUCHUI_TOUCH_SELECTION_CONTROLLER_IMPL_H_ +#ifndef UI_VIEWS_TOUCHUI_TOUCH_SELECTION_CONTROLLER_IMPL_H_ +#define UI_VIEWS_TOUCHUI_TOUCH_SELECTION_CONTROLLER_IMPL_H_ #include "base/macros.h" #include "base/timer/timer.h" @@ -11,6 +11,7 @@ #include "ui/gfx/geometry/point.h" #include "ui/gfx/selection_bound.h" #include "ui/touch_selection/touch_selection_menu_runner.h" +#include "ui/views/pointer_watcher.h" #include "ui/views/view.h" #include "ui/views/views_export.h" #include "ui/views/widget/widget_observer.h" @@ -29,17 +30,16 @@ class VIEWS_EXPORT TouchSelectionControllerImpl : public ui::TouchEditingControllerDeprecated, public ui::TouchSelectionMenuClient, public WidgetObserver, + public PointerWatcher, public ui::EventHandler { public: class EditingHandleView; - // Use TextSelectionController::create(). - explicit TouchSelectionControllerImpl( - ui::TouchEditable* client_view); - + // Use ui::TouchEditingControllerFactory::Create() instead. + explicit TouchSelectionControllerImpl(ui::TouchEditable* client_view); ~TouchSelectionControllerImpl() override; - // TextSelectionController. + // ui::TouchEditingControllerDeprecated: void SelectionChanged() override; bool IsHandleDragInProgress() override; void HideHandles(bool quick) override; @@ -70,19 +70,22 @@ class VIEWS_EXPORT TouchSelectionControllerImpl // |bound| should be the clipped version of the selection bound. bool ShouldShowHandleFor(const gfx::SelectionBound& bound) const; - // Overridden from ui::TouchSelectionMenuClient. + // ui::TouchSelectionMenuClient: bool IsCommandIdEnabled(int command_id) const override; void ExecuteCommand(int command_id, int event_flags) override; void RunContextMenu() override; - // Overridden from WidgetObserver. We will observe the widget backing the - // |client_view_| so that when its moved/resized, we can update the selection - // handles appropriately. + // WidgetObserver: void OnWidgetClosing(Widget* widget) override; void OnWidgetBoundsChanged(Widget* widget, const gfx::Rect& new_bounds) override; - // Overriden from ui::EventHandler. + // PointerWatcher: + void OnPointerEventObserved(const ui::PointerEvent& event, + const gfx::Point& location_in_screen, + gfx::NativeView target) override; + + // ui::EventHandler: void OnKeyEvent(ui::KeyEvent* event) override; void OnMouseEvent(ui::MouseEvent* event) override; void OnScrollEvent(ui::ScrollEvent* event) override; @@ -112,15 +115,15 @@ class VIEWS_EXPORT TouchSelectionControllerImpl bool IsSelectionHandle2Visible(); bool IsCursorHandleVisible(); gfx::Rect GetExpectedHandleBounds(const gfx::SelectionBound& bound); - views::WidgetDelegateView* GetHandle1View(); - views::WidgetDelegateView* GetHandle2View(); + WidgetDelegateView* GetHandle1View(); + WidgetDelegateView* GetHandle2View(); ui::TouchEditable* client_view_; - Widget* client_widget_; + Widget* client_widget_ = nullptr; std::unique_ptr<EditingHandleView> selection_handle_1_; std::unique_ptr<EditingHandleView> selection_handle_2_; std::unique_ptr<EditingHandleView> cursor_handle_; - bool command_executed_; + bool command_executed_ = false; base::TimeTicks selection_start_time_; // Timer to trigger quick menu (Quick menu is not shown if the selection @@ -129,7 +132,7 @@ class VIEWS_EXPORT TouchSelectionControllerImpl base::OneShotTimer quick_menu_timer_; // Pointer to the SelectionHandleView being dragged during a drag session. - EditingHandleView* dragging_handle_; + EditingHandleView* dragging_handle_ = nullptr; // In cursor mode, the two selection bounds are the same and correspond to // |cursor_handle_|; otherwise, they correspond to |selection_handle_1_| and @@ -148,4 +151,4 @@ class VIEWS_EXPORT TouchSelectionControllerImpl } // namespace views -#endif // UI_UI_VIEWS_TOUCHUI_TOUCH_SELECTION_CONTROLLER_IMPL_H_ +#endif // UI_VIEWS_TOUCHUI_TOUCH_SELECTION_CONTROLLER_IMPL_H_ diff --git a/chromium/ui/views/view.cc b/chromium/ui/views/view.cc index d238fad21fd..d0aba92898f 100644 --- a/chromium/ui/views/view.cc +++ b/chromium/ui/views/view.cc @@ -311,7 +311,7 @@ bool View::Contains(const View* view) const { } int View::GetIndexOf(const View* view) const { - Views::const_iterator i(std::find(children_.begin(), children_.end(), view)); + auto i(std::find(children_.begin(), children_.end(), view)); return i != children_.end() ? static_cast<int>(i - children_.begin()) : -1; } @@ -1191,6 +1191,13 @@ void View::ConvertEventToTarget(ui::EventTarget* target, event->ConvertLocationToTarget(this, static_cast<View*>(target)); } +gfx::PointF View::GetScreenLocationF(const ui::LocatedEvent& event) const { + DCHECK_EQ(this, event.target()); + gfx::Point screen_location(event.location()); + ConvertPointToScreen(this, &screen_location); + return gfx::PointF(screen_location); +} + // Accelerators ---------------------------------------------------------------- void View::AddAccelerator(const ui::Accelerator& accelerator) { @@ -1209,8 +1216,7 @@ void View::RemoveAccelerator(const ui::Accelerator& accelerator) { return; } - std::vector<ui::Accelerator>::iterator i( - std::find(accelerators_->begin(), accelerators_->end(), accelerator)); + auto i(std::find(accelerators_->begin(), accelerators_->end(), accelerator)); if (i == accelerators_->end()) { NOTREACHED() << "Removing non-existing accelerator"; return; @@ -2204,7 +2210,7 @@ void View::BoundsChanged(const gfx::Rect& previous_bounds) { // Notify interested Views that visible bounds within the root view may have // changed. if (descendants_to_notify_.get()) { - for (Views::iterator i(descendants_to_notify_->begin()); + for (auto i(descendants_to_notify_->begin()); i != descendants_to_notify_->end(); ++i) { (*i)->OnVisibleBoundsChanged(); } @@ -2254,8 +2260,8 @@ void View::AddDescendantToNotify(View* view) { void View::RemoveDescendantToNotify(View* view) { DCHECK(view && descendants_to_notify_.get()); - Views::iterator i(std::find( - descendants_to_notify_->begin(), descendants_to_notify_->end(), view)); + auto i(std::find(descendants_to_notify_->begin(), + descendants_to_notify_->end(), view)); DCHECK(i != descendants_to_notify_->end()); descendants_to_notify_->erase(i); if (descendants_to_notify_->empty()) diff --git a/chromium/ui/views/view.h b/chromium/ui/views/view.h index 4248609dd3c..161e015c044 100644 --- a/chromium/ui/views/view.h +++ b/chromium/ui/views/view.h @@ -888,6 +888,7 @@ class VIEWS_EXPORT View : public ui::LayerDelegate, ui::EventTargeter* GetEventTargeter() override; void ConvertEventToTarget(ui::EventTarget* target, ui::LocatedEvent* event) override; + gfx::PointF GetScreenLocationF(const ui::LocatedEvent& event) const override; // Overridden from ui::EventHandler: void OnKeyEvent(ui::KeyEvent* event) override; diff --git a/chromium/ui/views/view_properties.cc b/chromium/ui/views/view_properties.cc index e55acc0ca94..2f4f54e35ff 100644 --- a/chromium/ui/views/view_properties.cc +++ b/chromium/ui/views/view_properties.cc @@ -20,6 +20,8 @@ DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, gfx::Insets*); DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, views::BubbleDialogDelegateView*); +DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, gfx::Path*); + namespace views { DEFINE_UI_CLASS_PROPERTY_KEY(int, kHitTestComponentKey, HTNOWHERE); @@ -27,5 +29,6 @@ DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(gfx::Insets, kMarginsKey, nullptr); DEFINE_UI_CLASS_PROPERTY_KEY(views::BubbleDialogDelegateView*, kAnchoredDialogKey, nullptr); +DEFINE_OWNED_UI_CLASS_PROPERTY_KEY(gfx::Path, kHighlightPathKey, nullptr); } // namespace views diff --git a/chromium/ui/views/view_properties.h b/chromium/ui/views/view_properties.h index 2896ee94512..5f63c1b902d 100644 --- a/chromium/ui/views/view_properties.h +++ b/chromium/ui/views/view_properties.h @@ -10,6 +10,7 @@ namespace gfx { class Insets; +class Path; } // namespace gfx namespace views { @@ -30,6 +31,12 @@ VIEWS_EXPORT extern const ui::ClassProperty<gfx::Insets*>* const kMarginsKey; VIEWS_EXPORT extern const ui::ClassProperty<BubbleDialogDelegateView*>* const kAnchoredDialogKey; +// A property to store a highlight path related to the view. This is nominally +// used by the default inkdrop and focus ring that are both used to highlight +// the view in different ways. +VIEWS_EXPORT extern const ui::ClassProperty<gfx::Path*>* const + kHighlightPathKey; + } // namespace views // Declaring the template specialization here to make sure that the @@ -40,5 +47,5 @@ VIEWS_EXPORT extern const ui::ClassProperty<BubbleDialogDelegateView*>* const DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, gfx::Insets*); DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, views::BubbleDialogDelegateView*); - +DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(VIEWS_EXPORT, gfx::Path*); #endif // UI_VIEWS_VIEW_PROPERTIES_H_ diff --git a/chromium/ui/views/views_delegate.cc b/chromium/ui/views/views_delegate.cc index 846e579237c..d580fde1f8b 100644 --- a/chromium/ui/views/views_delegate.cc +++ b/chromium/ui/views/views_delegate.cc @@ -48,8 +48,7 @@ ViewsDelegate* ViewsDelegate::GetInstance() { void ViewsDelegate::SaveWindowPlacement(const Widget* widget, const std::string& window_name, const gfx::Rect& bounds, - ui::WindowShowState show_state) { -} + ui::WindowShowState show_state) {} bool ViewsDelegate::GetSavedWindowPlacement( const Widget* widget, @@ -66,8 +65,7 @@ void ViewsDelegate::NotifyMenuItemFocused(const base::string16& menu_name, const base::string16& menu_item_name, int item_index, int item_count, - bool has_submenu) { -} + bool has_submenu) {} ViewsDelegate::ProcessMenuAcceleratorResult ViewsDelegate::ProcessAcceleratorWhileMenuShowing( @@ -98,20 +96,14 @@ NonClientFrameView* ViewsDelegate::CreateDefaultNonClientFrameView( return nullptr; } -void ViewsDelegate::AddRef() { -} +void ViewsDelegate::AddRef() {} -void ViewsDelegate::ReleaseRef() { -} +void ViewsDelegate::ReleaseRef() {} void ViewsDelegate::OnBeforeWidgetInit( Widget::InitParams* params, internal::NativeWidgetDelegate* delegate) {} -base::TimeDelta ViewsDelegate::GetTextfieldPasswordRevealDuration() { - return base::TimeDelta(); -} - bool ViewsDelegate::WindowManagerProvidesTitleBar(bool maximized) { return false; } @@ -140,4 +132,16 @@ bool ViewsDelegate::ShouldMirrorArrowsInRTL() const { return true; } +void ViewsDelegate::AddPointerWatcher(PointerWatcher*, bool) { + NOTREACHED(); +} + +void ViewsDelegate::RemovePointerWatcher(PointerWatcher*) { + NOTREACHED(); +} + +bool ViewsDelegate::IsPointerWatcherSupported() const { + return false; +} + } // namespace views diff --git a/chromium/ui/views/views_delegate.h b/chromium/ui/views/views_delegate.h index 3f16d177c21..b5237c3f20f 100644 --- a/chromium/ui/views/views_delegate.h +++ b/chromium/ui/views/views_delegate.h @@ -23,10 +23,6 @@ #include "ui/views/views_export.h" #include "ui/views/widget/widget.h" -namespace base { -class TimeDelta; -} - namespace gfx { class ImageSkia; class Rect; @@ -34,13 +30,14 @@ class Rect; namespace ui { class ContextFactory; +class TouchEditingControllerFactory; } namespace views { class NativeWidget; class NonClientFrameView; -class ViewsTouchEditingControllerFactory; +class PointerWatcher; class View; class Widget; @@ -173,9 +170,6 @@ class VIEWS_EXPORT ViewsDelegate { virtual void OnBeforeWidgetInit(Widget::InitParams* params, internal::NativeWidgetDelegate* delegate); - // Returns the password reveal duration for Textfield. - virtual base::TimeDelta GetTextfieldPasswordRevealDuration(); - // Returns true if the operating system's window manager will always provide a // title bar with caption buttons (ignoring the setting to // |remove_standard_frame| in InitParams). If |maximized|, this applies to @@ -206,11 +200,18 @@ class VIEWS_EXPORT ViewsDelegate { // opens in the opposite direction. virtual bool ShouldMirrorArrowsInRTL() const; + // Allows lower-level views components to use Mus-only PointerWatcher wiring. + // TODO(crbug.com/887725): Support PointerWatcher without mus, refactor. + virtual void AddPointerWatcher(PointerWatcher* pointer_watcher, + bool wants_moves); + virtual void RemovePointerWatcher(PointerWatcher* pointer_watcher); + virtual bool IsPointerWatcherSupported() const; + protected: ViewsDelegate(); private: - std::unique_ptr<ViewsTouchEditingControllerFactory> + std::unique_ptr<ui::TouchEditingControllerFactory> editing_controller_factory_; #if defined(USE_AURA) diff --git a/chromium/ui/views/widget/desktop_aura/desktop_capture_client.cc b/chromium/ui/views/widget/desktop_aura/desktop_capture_client.cc index 86578b995a2..38447dc9ae0 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_capture_client.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_capture_client.cc @@ -79,8 +79,7 @@ void DesktopCaptureClient::SetCapture(aura::Window* new_capture_window) { // Notify the other roots that we got capture. This is important so that // they reset state. CaptureClients capture_clients(*capture_clients_); - for (CaptureClients::iterator i = capture_clients.begin(); - i != capture_clients.end(); ++i) { + for (auto i = capture_clients.begin(); i != capture_clients.end(); ++i) { if (*i != this) { aura::client::CaptureDelegate* delegate = (*i)->root_->GetHost()->dispatcher(); diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc index 00073d3d3bf..f0da4f88142 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11.cc @@ -450,8 +450,7 @@ void DesktopDragDropClientAuraX11::X11DragContext::ReadActions() { int DesktopDragDropClientAuraX11::X11DragContext::GetDragOperation() const { int drag_operation = ui::DragDropTypes::DRAG_NONE; - for (std::vector<::Atom>::const_iterator it = actions_.begin(); - it != actions_.end(); ++it) { + for (auto it = actions_.begin(); it != actions_.end(); ++it) { MaskOperation(*it, &drag_operation); } @@ -711,7 +710,7 @@ void DesktopDragDropClientAuraX11::OnXdndDrop( } if (!IsDragDropInProgress()) { - UMA_HISTOGRAM_COUNTS("Event.DragDrop.ExternalOriginDrop", 1); + UMA_HISTOGRAM_COUNTS_1M("Event.DragDrop.ExternalOriginDrop", 1); } drag_operation = delegate->OnPerformDrop(event); diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc index a6fe148e3d2..f60cc49e7ab 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_drag_drop_client_aurax11_unittest.cc @@ -354,8 +354,7 @@ void TestDragDropClient::SetTopmostXWindowAndMoveMouse(::Window xid) { } void TestDragDropClient::SendXClientEvent(::Window xid, XEvent* event) { - std::map< ::Window, ClientMessageEventCollector*>::iterator it = - collectors_.find(xid); + auto it = collectors_.find(xid); if (it != collectors_.end()) it->second->RecordEvent(event->xclient); } diff --git a/chromium/ui/views/widget/desktop_aura/desktop_drop_target_win.cc b/chromium/ui/views/widget/desktop_aura/desktop_drop_target_win.cc index 99c525f3219..094a0b00bce 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_drop_target_win.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_drop_target_win.cc @@ -99,7 +99,7 @@ DWORD DesktopDropTargetWin::OnDrop(IDataObject* data_object, DragDropClient* client = aura::client::GetDragDropClient(root_window_); if (client && !client->IsDragDropInProgress() && drag_operation != ui::DragDropTypes::DRAG_NONE) { - UMA_HISTOGRAM_COUNTS("Event.DragDrop.ExternalOriginDrop", 1); + UMA_HISTOGRAM_COUNTS_1M("Event.DragDrop.ExternalOriginDrop", 1); } } if (target_window_) { diff --git a/chromium/ui/views/widget/desktop_aura/desktop_native_cursor_manager.cc b/chromium/ui/views/widget/desktop_aura/desktop_native_cursor_manager.cc index 5d4ae7a9805..d0731338684 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_native_cursor_manager.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_native_cursor_manager.cc @@ -51,7 +51,7 @@ void DesktopNativeCursorManager::SetCursor( delegate->CommitCursor(new_cursor); if (delegate->IsCursorVisible()) { - for (Hosts::const_iterator i = hosts_.begin(); i != hosts_.end(); ++i) + for (auto i = hosts_.begin(); i != hosts_.end(); ++i) (*i)->SetCursor(new_cursor); } } @@ -66,11 +66,11 @@ void DesktopNativeCursorManager::SetVisibility( } else { gfx::NativeCursor invisible_cursor(ui::CursorType::kNone); cursor_loader_->SetPlatformCursor(&invisible_cursor); - for (Hosts::const_iterator i = hosts_.begin(); i != hosts_.end(); ++i) + for (auto i = hosts_.begin(); i != hosts_.end(); ++i) (*i)->SetCursor(invisible_cursor); } - for (Hosts::const_iterator i = hosts_.begin(); i != hosts_.end(); ++i) + for (auto i = hosts_.begin(); i != hosts_.end(); ++i) (*i)->OnCursorVisibilityChanged(visible); } @@ -90,7 +90,7 @@ void DesktopNativeCursorManager::SetMouseEventsEnabled( SetVisibility(delegate->IsCursorVisible(), delegate); - for (Hosts::const_iterator i = hosts_.begin(); i != hosts_.end(); ++i) + for (auto i = hosts_.begin(); i != hosts_.end(); ++i) (*i)->dispatcher()->OnMouseEventsEnableStateChanged(enabled); } diff --git a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc index 01a429a67e3..264858b3a46 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura.cc @@ -595,11 +595,15 @@ const ui::Layer* DesktopNativeWidgetAura::GetLayer() const { } void DesktopNativeWidgetAura::ReorderNativeViews() { + if (!content_window_) + return; + // Reordering native views causes multiple changes to the window tree. - // Instantiate a ScopedPauseOcclusionTracking to recompute occlusion once at - // the end of this scope rather than after each individual change. + // Instantiate a ScopedPause to recompute occlusion once at the end of this + // scope rather than after each individual change. // https://crbug.com/829918 - aura::WindowOcclusionTracker::ScopedPauseOcclusionTracking pause_occlusion; + aura::WindowOcclusionTracker::ScopedPause pause_occlusion( + content_window_->env()); window_reorderer_->ReorderChildWindows(); } diff --git a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc index 9d4d564ee7a..2514071e8bd 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_native_widget_aura_unittest.cc @@ -17,7 +17,6 @@ #include "ui/aura/test/test_window_delegate.h" #include "ui/aura/test/window_occlusion_tracker_test_api.h" #include "ui/aura/window.h" -#include "ui/aura/window_occlusion_tracker.h" #include "ui/aura/window_tree_host.h" #include "ui/display/screen.h" #include "ui/events/event_processor.h" @@ -312,7 +311,7 @@ TEST_F(DesktopNativeWidgetAuraTest, ReorderDoesntRecomputeOcclusion) { parent.Show(); aura::Window* parent_window = parent.GetNativeWindow(); - aura::WindowOcclusionTracker::Track(parent_window); + parent_window->TrackOcclusionState(); View* contents_view = parent.GetContentsView(); @@ -335,7 +334,8 @@ TEST_F(DesktopNativeWidgetAuraTest, ReorderDoesntRecomputeOcclusion) { contents_view->AddChildView(host_view3); // Reorder child views. Expect occlusion to only be recomputed once. - aura::test::WindowOcclusionTrackerTestApi window_occlusion_tracker_test_api; + aura::test::WindowOcclusionTrackerTestApi window_occlusion_tracker_test_api( + parent_window->env()); const int num_times_occlusion_recomputed = window_occlusion_tracker_test_api.GetNumTimesOcclusionRecomputed(); contents_view->ReorderChildView(host_view3, 0); diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.cc index a5e871e8dae..6dd57cd7454 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_ozone.cc @@ -4,6 +4,7 @@ #include "ui/views/widget/desktop_aura/desktop_screen_ozone.h" +#include "ui/aura/screen_ozone.h" #include "ui/display/display.h" #include "ui/display/types/display_constants.h" #include "ui/display/types/display_snapshot.h" @@ -55,7 +56,16 @@ void DesktopScreenOzone::OnDisplaySnapshotsInvalidated() {} ////////////////////////////////////////////////////////////////////////////// display::Screen* CreateDesktopScreen() { - return new DesktopScreenOzone; + auto platform_screen = ui::OzonePlatform::GetInstance()->CreateScreen(); + if (!platform_screen) { + // TODO: At the moment, only the Ozone/Headless uses this patch. Fix it: + // https://crbug.com/891613 + LOG(ERROR) << "PlatformScreen is not implemented for this ozone platform. " + "Falling back to old DesktopScreenOzone implementation. See " + "https://crbug.com/872339 for details"; + return new DesktopScreenOzone; + } + return new aura::ScreenOzone(std::move(platform_screen)); } } // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc index e90710c2c39..0951d2fb5b9 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_screen_x11.cc @@ -232,8 +232,7 @@ display::Display DesktopScreenX11::GetDisplayNearestPoint( const gfx::Point& point) const { if (displays_.size() <= 1) return GetPrimaryDisplay(); - for (std::vector<display::Display>::const_iterator it = displays_.begin(); - it != displays_.end(); ++it) { + for (auto it = displays_.begin(); it != displays_.end(); ++it) { if (it->bounds().Contains(point)) return *it; } @@ -244,8 +243,7 @@ display::Display DesktopScreenX11::GetDisplayMatching( const gfx::Rect& match_rect) const { int max_area = 0; const display::Display* matching = NULL; - for (std::vector<display::Display>::const_iterator it = displays_.begin(); - it != displays_.end(); ++it) { + for (auto it = displays_.begin(); it != displays_.end(); ++it) { gfx::Rect intersect = gfx::IntersectRects(it->bounds(), match_rect); int area = intersect.width() * intersect.height(); if (area > max_area) { @@ -450,7 +448,7 @@ std::vector<display::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() { if (monitor_iter != output_to_monitor.end() && monitor_iter->second == 0) monitor_order_primary_display_index = displays.size(); - if (!display::Display::HasForceColorProfile()) { + if (!display::Display::HasForceDisplayColorProfile()) { gfx::ICCProfile icc_profile = GetICCProfileForMonitor( monitor_iter == output_to_monitor.end() ? 0 : monitor_iter->second); icc_profile.HistogramDisplay(display.id()); diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc index dcf1d7d40c2..fbfa8df1f7d 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.cc @@ -6,13 +6,16 @@ #include "ui/aura/client/drag_drop_client.h" #include "ui/aura/client/transient_window_client.h" +#include "ui/base/hit_test.h" #include "ui/display/display.h" #include "ui/display/screen.h" #include "ui/gfx/geometry/dip_util.h" #include "ui/platform_window/platform_window.h" +#include "ui/platform_window/platform_window_handler/wm_move_resize_handler.h" #include "ui/platform_window/platform_window_init_properties.h" #include "ui/views/corewm/tooltip_aura.h" #include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" +#include "ui/views/widget/desktop_aura/window_event_filter.h" #include "ui/views/widget/widget_aura_utils.h" #include "ui/views/window/native_frame_view.h" #include "ui/wm/core/window_util.h" @@ -91,6 +94,19 @@ void DesktopWindowTreeHostPlatform::Init(const Widget::InitParams& params) { void DesktopWindowTreeHostPlatform::OnNativeWidgetCreated( const Widget::InitParams& params) { native_widget_delegate_->OnNativeWidgetCreated(true); + + // Setup a non_client_window_event_filter, which handles resize/move, double + // click and other events. + DCHECK(!non_client_window_event_filter_); + std::unique_ptr<WindowEventFilter> window_event_filter = + std::make_unique<WindowEventFilter>(this); + auto* wm_move_resize_handler = GetWmMoveResizeHandler(*platform_window()); + if (wm_move_resize_handler) + window_event_filter->SetWmMoveResizeHandler( + GetWmMoveResizeHandler(*(platform_window()))); + + non_client_window_event_filter_ = std::move(window_event_filter); + window()->AddPreTargetHandler(non_client_window_event_filter_.get()); } void DesktopWindowTreeHostPlatform::OnWidgetInitDone() {} @@ -135,6 +151,8 @@ void DesktopWindowTreeHostPlatform::CloseNow() { if (!weak_ref || got_on_closed_) return; + RemoveNonClientEventFilter(); + native_widget_delegate_->OnNativeWidgetDestroying(); got_on_closed_ = true; @@ -449,7 +467,34 @@ bool DesktopWindowTreeHostPlatform::ShouldCreateVisibilityController() const { return true; } +void DesktopWindowTreeHostPlatform::DispatchEvent(ui::Event* event) { +#if defined(USE_OZONE) + // Make sure the |event| is marked as a non-client if it's a non-client + // mouse down event. This is needed to make sure the WindowEventDispatcher + // does not set a |mouse_pressed_handler_| for such events, because they are + // not always followed with non-client mouse up events in case of + // Ozone/Wayland or Ozone/X11. + // + // Also see the comment in WindowEventDispatcher::PreDispatchMouseEvent.. + aura::Window* content_window = desktop_native_widget_aura_->content_window(); + if (content_window && content_window->delegate()) { + if (event->IsMouseEvent()) { + ui::MouseEvent* mouse_event = event->AsMouseEvent(); + int flags = mouse_event->flags(); + int hit_test_code = content_window->delegate()->GetNonClientComponent( + mouse_event->location()); + if (hit_test_code != HTCLIENT && hit_test_code != HTNOWHERE) + flags |= ui::EF_IS_NON_CLIENT; + mouse_event->set_flags(flags); + } + } +#endif + + WindowTreeHostPlatform::DispatchEvent(event); +} + void DesktopWindowTreeHostPlatform::OnClosed() { + RemoveNonClientEventFilter(); got_on_closed_ = true; desktop_native_widget_aura_->OnHostClosed(); } @@ -492,6 +537,14 @@ void DesktopWindowTreeHostPlatform::Relayout() { widget->GetRootView()->Layout(); } +void DesktopWindowTreeHostPlatform::RemoveNonClientEventFilter() { + if (!non_client_window_event_filter_) + return; + + window()->RemovePreTargetHandler(non_client_window_event_filter_.get()); + non_client_window_event_filter_.reset(); +} + Widget* DesktopWindowTreeHostPlatform::GetWidget() { return native_widget_delegate_->AsWidget(); } diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h index b6219dd00a7..e0b98abdaf0 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform.h @@ -12,6 +12,8 @@ namespace views { +class WindowEventFilter; + class VIEWS_EXPORT DesktopWindowTreeHostPlatform : public aura::WindowTreeHostPlatform, public DesktopWindowTreeHost { @@ -90,14 +92,19 @@ class VIEWS_EXPORT DesktopWindowTreeHostPlatform bool ShouldCreateVisibilityController() const override; // WindowTreeHostPlatform: + void DispatchEvent(ui::Event* event) override; void OnClosed() override; void OnWindowStateChanged(ui::PlatformWindowState new_state) override; void OnCloseRequest() override; void OnActivationChanged(bool active) override; private: + FRIEND_TEST_ALL_PREFIXES(DesktopWindowTreeHostPlatformTest, HitTest); + void Relayout(); + void RemoveNonClientEventFilter(); + Widget* GetWidget(); gfx::Rect ToDIPRect(const gfx::Rect& rect_in_pixels) const; @@ -113,6 +120,9 @@ class VIEWS_EXPORT DesktopWindowTreeHostPlatform bool is_active_ = false; + // A handler for events intended for non client area. + std::unique_ptr<WindowEventFilter> non_client_window_event_filter_; + base::WeakPtrFactory<DesktopWindowTreeHostPlatform> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostPlatform); diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_interactive_uitest.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_interactive_uitest.cc new file mode 100644 index 00000000000..f959917fcfe --- /dev/null +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_platform_interactive_uitest.cc @@ -0,0 +1,272 @@ +// 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/views/widget/desktop_aura/desktop_window_tree_host_platform.h" + +#include "ui/aura/window_tree_host_platform.h" +#include "ui/base/hit_test.h" +#include "ui/platform_window/platform_window.h" +#include "ui/platform_window/platform_window_handler/wm_move_resize_handler.h" +#include "ui/views/test/views_interactive_ui_test_base.h" +#include "ui/views/widget/desktop_aura/desktop_native_widget_aura.h" +#include "ui/views/widget/desktop_aura/window_event_filter.h" +#include "ui/views/widget/widget_delegate.h" +#include "ui/views/window/native_frame_view.h" + +namespace views { + +namespace { + +bool IsNonClientComponent(int hittest) { + switch (hittest) { + case HTBOTTOM: + case HTBOTTOMLEFT: + case HTBOTTOMRIGHT: + case HTCAPTION: + case HTLEFT: + case HTRIGHT: + case HTTOP: + case HTTOPLEFT: + case HTTOPRIGHT: + return true; + default: + return false; + } + return true; +} + +// A fake handler, which just stores the hittest and pointer location values. +class FakeWmMoveResizeHandler : public ui::WmMoveResizeHandler { + public: + using SetBoundsCallback = base::RepeatingCallback<void(gfx::Rect)>; + explicit FakeWmMoveResizeHandler(ui::PlatformWindow* window) + : platform_window_(window), hittest_(-1) {} + ~FakeWmMoveResizeHandler() override = default; + + void Reset() { + hittest_ = -1; + pointer_location_ = gfx::Point(); + } + + int hittest() const { return hittest_; } + gfx::Point pointer_location() const { return pointer_location_; } + + void set_bounds(const gfx::Rect& bounds) { bounds_ = bounds; } + + // ui::WmMoveResizeHandler + void DispatchHostWindowDragMovement( + int hittest, + const gfx::Point& pointer_location) override { + hittest_ = hittest; + pointer_location_ = pointer_location; + + platform_window_->SetBounds(bounds_); + } + + private: + ui::PlatformWindow* platform_window_; + gfx::Rect bounds_; + + int hittest_ = -1; + gfx::Point pointer_location_; + + DISALLOW_COPY_AND_ASSIGN(FakeWmMoveResizeHandler); +}; + +void SetExpectationBasedOnHittestValue(int hittest, + const FakeWmMoveResizeHandler& handler, + const gfx::Point& pointer_location) { + if (IsNonClientComponent(hittest)) { + // Ensure both the pointer location and the hit test value are passed to the + // fake move/resize handler. + EXPECT_EQ(handler.pointer_location().ToString(), + pointer_location.ToString()); + EXPECT_EQ(handler.hittest(), hittest); + return; + } + + // Ensure the handler does not receive the hittest value or the pointer + // location. + EXPECT_TRUE(handler.pointer_location().IsOrigin()); + EXPECT_NE(handler.hittest(), hittest); +} + +// This is used to return a customized result to NonClientHitTest. +class HitTestNonClientFrameView : public NativeFrameView { + public: + explicit HitTestNonClientFrameView(Widget* widget) + : NativeFrameView(widget), hit_test_result_(HTNOWHERE) {} + ~HitTestNonClientFrameView() override {} + + void set_hit_test_result(int component) { hit_test_result_ = component; } + + // NonClientFrameView overrides: + int NonClientHitTest(const gfx::Point& point) override { + return hit_test_result_; + } + + private: + int hit_test_result_; + + DISALLOW_COPY_AND_ASSIGN(HitTestNonClientFrameView); +}; + +// This is used to return HitTestNonClientFrameView on create call. +class HitTestWidgetDelegate : public views::WidgetDelegate { + public: + HitTestWidgetDelegate(views::Widget* widget, + HitTestNonClientFrameView* frame_view) + : widget_(widget), frame_view_(frame_view) {} + ~HitTestWidgetDelegate() override {} + + void set_can_resize(bool can_resize) { + can_resize_ = can_resize; + widget_->OnSizeConstraintsChanged(); + } + + // views::WidgetDelegate: + bool CanResize() const override { return can_resize_; } + views::Widget* GetWidget() override { return widget_; } + views::Widget* GetWidget() const override { return widget_; } + views::NonClientFrameView* CreateNonClientFrameView(Widget* widget) override { + return frame_view_; + } + + private: + views::Widget* widget_; + HitTestNonClientFrameView* frame_view_; + bool can_resize_ = false; + + DISALLOW_COPY_AND_ASSIGN(HitTestWidgetDelegate); +}; + +} // namespace + +class DesktopWindowTreeHostPlatformTest : public ViewsInteractiveUITestBase { + public: + DesktopWindowTreeHostPlatformTest() = default; + ~DesktopWindowTreeHostPlatformTest() override = default; + + protected: + Widget* BuildTopLevelDesktopWidget(const gfx::Rect& bounds) { + Widget* toplevel = new Widget; + frame_view_ = new HitTestNonClientFrameView(toplevel); + delegate_ = new HitTestWidgetDelegate(toplevel, frame_view_); + Widget::InitParams toplevel_params = + CreateParams(Widget::InitParams::TYPE_WINDOW); + toplevel_params.native_widget = + new views::DesktopNativeWidgetAura(toplevel); + toplevel_params.delegate = delegate_; + toplevel_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; + toplevel_params.bounds = bounds; + toplevel_params.remove_standard_frame = true; + toplevel->Init(toplevel_params); + return toplevel; + } + + std::unique_ptr<ui::MouseEvent> CreateMouseEvent( + const gfx::Point& pointer_location, + ui::EventType event_type, + int flags) { + std::unique_ptr<ui::MouseEvent> mouse_event = + std::make_unique<ui::MouseEvent>(event_type, pointer_location, + pointer_location, + base::TimeTicks::Now(), flags, flags); + return mouse_event; + } + + HitTestNonClientFrameView* frame_view_ = nullptr; + HitTestWidgetDelegate* delegate_ = nullptr; + + private: + DISALLOW_COPY_AND_ASSIGN(DesktopWindowTreeHostPlatformTest); +}; + +TEST_F(DesktopWindowTreeHostPlatformTest, HitTest) { + gfx::Rect bounds(0, 0, 100, 100); + std::unique_ptr<Widget> widget(BuildTopLevelDesktopWidget(bounds)); + widget->Show(); + + aura::Window* window = widget->GetNativeWindow(); + DesktopWindowTreeHostPlatform* host = + static_cast<DesktopWindowTreeHostPlatform*>(window->GetHost()); + + // Install a fake move/resize handler to intercept the move/resize call. + WindowEventFilter* non_client_filter = + host->non_client_window_event_filter_.get(); + std::unique_ptr<FakeWmMoveResizeHandler> handler = + std::make_unique<FakeWmMoveResizeHandler>(host->platform_window()); + non_client_filter->SetWmMoveResizeHandler(handler.get()); + + delegate_->set_can_resize(true); + + // It is not important to use pointer locations corresponding to the hittests + // values used in the browser itself, because we fake the hit test results, + // which non client frame view sends back. Thus, just make sure the content + // window is able to receive these events. + gfx::Point pointer_location(10, 10); + + constexpr int hittest_values[] = { + HTBOTTOM, HTBOTTOMLEFT, HTBOTTOMRIGHT, HTCAPTION, HTLEFT, + HTRIGHT, HTTOP, HTTOPLEFT, HTTOPRIGHT, HTNOWHERE, + HTBORDER, HTCLIENT, HTCLOSE, HTERROR, HTGROWBOX, + HTHELP, HTHSCROLL, HTMENU, HTMAXBUTTON, HTMINBUTTON, + HTREDUCE, HTSIZE, HTSYSMENU, HTTRANSPARENT, HTVSCROLL, + HTZOOM, + }; + + for (int hittest : hittest_values) { + handler->Reset(); + + // Set the desired hit test result value, which will be returned, when + // WindowEventFilter starts to perform hit testing. + frame_view_->set_hit_test_result(hittest); + + gfx::Rect bounds = window->GetBoundsInScreen(); + + // The wm move/resize handler receives pointer location in the global screen + // coordinate, whereas event dispatcher receives event locations on a local + // system coordinate. Thus, add an offset of a new possible origin value of + // a window to the expected pointer location. + gfx::Point expected_pointer_location(pointer_location); + expected_pointer_location.Offset(bounds.x(), bounds.y()); + + if (hittest == HTCAPTION) { + // Move the window on HTCAPTION hit test value. + bounds = + gfx::Rect(gfx::Point(bounds.x() + 2, bounds.y() + 4), bounds.size()); + handler->set_bounds(bounds); + } else if (IsNonClientComponent(hittest)) { + // Resize the window on other than HTCAPTION non client hit test values. + bounds = gfx::Rect( + gfx::Point(bounds.origin()), + gfx::Size(bounds.size().width() + 5, bounds.size().height() + 10)); + handler->set_bounds(bounds); + } + + // Send mouse down event and make sure the WindowEventFilter calls the + // move/resize handler to start interactive move/resize with the |hittest| + // value we specified. + auto mouse_down_event = CreateMouseEvent( + pointer_location, ui::ET_MOUSE_PRESSED, ui::EF_LEFT_MOUSE_BUTTON); + host->DispatchEvent(mouse_down_event.get()); + + // The test expectation is based on the hit test component. If it is a + // non-client component, which results in a call to move/resize, the handler + // must receive the hittest value and the pointer location in global screen + // coordinate system. In other cases, it must not. + SetExpectationBasedOnHittestValue(hittest, *handler.get(), + expected_pointer_location); + // Make sure the bounds of the content window are correct. + EXPECT_EQ(window->GetBoundsInScreen().ToString(), bounds.ToString()); + + // Dispatch mouse up event to release mouse pressed handler and be able to + // consume future events. + auto mouse_up_event = CreateMouseEvent( + pointer_location, ui::ET_MOUSE_RELEASED, ui::EF_LEFT_MOUSE_BUTTON); + host->DispatchEvent(mouse_up_event.get()); + } +} + +} // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc index 4e04197810d..0ab1728d69b 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.cc @@ -476,8 +476,7 @@ void DesktopWindowTreeHostX11::CloseNow() { // If we have children, close them. Use a copy for iteration because they'll // remove themselves. std::set<DesktopWindowTreeHostX11*> window_children_copy = window_children_; - for (std::set<DesktopWindowTreeHostX11*>::iterator it = - window_children_copy.begin(); it != window_children_copy.end(); + for (auto it = window_children_copy.begin(); it != window_children_copy.end(); ++it) { (*it)->CloseNow(); } @@ -1006,7 +1005,6 @@ void DesktopWindowTreeHostX11::SetFullscreen(bool fullscreen) { if (is_fullscreen_ == fullscreen) return; is_fullscreen_ = fullscreen; - OnFullscreenStateChanged(); if (is_fullscreen_) delayed_resize_task_.Cancel(); @@ -1372,10 +1370,6 @@ void DesktopWindowTreeHostX11::OnDisplayMetricsChanged( } } -void DesktopWindowTreeHostX11::OnMaximizedStateChanged() {} - -void DesktopWindowTreeHostX11::OnFullscreenStateChanged() {} - //////////////////////////////////////////////////////////////////////////////// // DesktopWindowTreeHostX11, private: @@ -1677,12 +1671,10 @@ void DesktopWindowTreeHostX11::OnWMStateUpdated() { void DesktopWindowTreeHostX11::UpdateWindowProperties( const base::flat_set<XAtom>& new_window_properties) { bool was_minimized = IsMinimized(); - bool was_maximized = IsMaximized(); window_properties_ = new_window_properties; bool is_minimized = IsMinimized(); - bool is_maximized = IsMaximized(); // Propagate the window minimization information to the content window, so // the render side can update its visibility properly. OnWMStateUpdated() is @@ -1729,9 +1721,6 @@ void DesktopWindowTreeHostX11::UpdateWindowProperties( is_always_on_top_ = ui::HasWMSpecProperty( window_properties_, gfx::GetAtom("_NET_WM_STATE_ABOVE")); - if (was_maximized != is_maximized) - OnMaximizedStateChanged(); - // Now that we have different window properties, we may need to relayout the // window. (The windows code doesn't need this because their window change is // synchronous.) @@ -1935,7 +1924,7 @@ void DesktopWindowTreeHostX11::SerializeImageRepresentation( int height = rep.GetHeight(); data->push_back(height); - const SkBitmap& bitmap = rep.sk_bitmap(); + const SkBitmap& bitmap = rep.GetBitmap(); for (int y = 0; y < height; ++y) for (int x = 0; x < width; ++x) @@ -2367,7 +2356,7 @@ gfx::Rect DesktopWindowTreeHostX11::ToPixelRect( return gfx::ToEnclosingRect(rect_in_pixels); } -std::unique_ptr<base::OnceClosure> +std::unique_ptr<base::Closure> DesktopWindowTreeHostX11::DisableEventListening() { // Allows to open multiple file-pickers. See https://crbug.com/678982 modal_dialog_counter_++; @@ -2378,9 +2367,9 @@ DesktopWindowTreeHostX11::DisableEventListening() { window(), std::make_unique<aura::NullWindowTargeter>()); } - return std::make_unique<base::OnceClosure>( - base::BindOnce(&DesktopWindowTreeHostX11::EnableEventListening, - weak_factory_.GetWeakPtr())); + return std::make_unique<base::Closure>( + base::Bind(&DesktopWindowTreeHostX11::EnableEventListening, + weak_factory_.GetWeakPtr())); } void DesktopWindowTreeHostX11::EnableEventListening() { diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h index cc9d5fb4823..2600a0621f3 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11.h @@ -86,7 +86,7 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 static void CleanUpWindowList(void (*func)(aura::Window* window)); // Disables event listening to make |dialog| modal. - std::unique_ptr<base::OnceClosure> DisableEventListening(); + std::unique_ptr<base::Closure> DisableEventListening(); // Returns a map of KeyboardEvent code to KeyboardEvent key values. base::flat_map<std::string, std::string> GetKeyboardLayoutMap() override; @@ -184,12 +184,6 @@ class VIEWS_EXPORT DesktopWindowTreeHostX11 void OnDisplayMetricsChanged(const display::Display& display, uint32_t changed_metrics) override; - // Called after the window is maximized or restored. - virtual void OnMaximizedStateChanged(); - - // Called after the window is fullscreened or unfullscreened. - virtual void OnFullscreenStateChanged(); - private: friend class DesktopWindowTreeHostX11HighDPITest; diff --git a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc index 73507805813..2f3f85b16b1 100644 --- a/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc +++ b/chromium/ui/views/widget/desktop_aura/desktop_window_tree_host_x11_unittest.cc @@ -9,6 +9,7 @@ #include "base/command_line.h" #include "base/macros.h" #include "base/run_loop.h" +#include "base/stl_util.h" #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" #include "ui/base/hit_test.h" @@ -50,11 +51,8 @@ class WMStateWaiter : public X11PropertyChangeWaiter { // X11PropertyChangeWaiter: bool ShouldKeepOnWaiting(const ui::PlatformEvent& event) override { std::vector<Atom> hints; - if (ui::GetAtomArrayProperty(xwindow(), "_NET_WM_STATE", &hints)) { - auto it = std::find(hints.cbegin(), hints.cend(), gfx::GetAtom(hint_)); - bool hint_set = (it != hints.cend()); - return hint_set != wait_till_set_; - } + if (ui::GetAtomArrayProperty(xwindow(), "_NET_WM_STATE", &hints)) + return base::ContainsValue(hints, gfx::GetAtom(hint_)) != wait_till_set_; return true; } diff --git a/chromium/ui/views/widget/desktop_aura/window_event_filter.cc b/chromium/ui/views/widget/desktop_aura/window_event_filter.cc index 530b079289b..3b01fb995b4 100644 --- a/chromium/ui/views/widget/desktop_aura/window_event_filter.cc +++ b/chromium/ui/views/widget/desktop_aura/window_event_filter.cc @@ -6,6 +6,7 @@ #include "services/ws/public/mojom/window_tree_constants.mojom.h" #include "ui/aura/client/aura_constants.h" +#include "ui/aura/env.h" #include "ui/aura/window.h" #include "ui/aura/window_delegate.h" #include "ui/aura/window_tree_host.h" @@ -14,6 +15,7 @@ #include "ui/display/screen.h" #include "ui/events/event.h" #include "ui/events/event_utils.h" +#include "ui/platform_window/platform_window_handler/wm_move_resize_handler.h" #include "ui/views/linux_ui/linux_ui.h" #include "ui/views/widget/desktop_aura/desktop_window_tree_host.h" #include "ui/views/widget/native_widget_aura.h" @@ -21,6 +23,28 @@ namespace views { +namespace { + +bool CanPerformDragOrResize(int hittest) { + switch (hittest) { + case HTBOTTOM: + case HTBOTTOMLEFT: + case HTBOTTOMRIGHT: + case HTCAPTION: + case HTLEFT: + case HTRIGHT: + case HTTOP: + case HTTOPLEFT: + case HTTOPRIGHT: + return true; + default: + return false; + } + return true; +} + +} // namespace + WindowEventFilter::WindowEventFilter(DesktopWindowTreeHost* window_tree_host) : window_tree_host_(window_tree_host), click_component_(HTNOWHERE) {} @@ -53,6 +77,12 @@ void WindowEventFilter::OnMouseEvent(ui::MouseEvent* event) { } } +void WindowEventFilter::SetWmMoveResizeHandler( + ui::WmMoveResizeHandler* handler) { + DCHECK(!handler_); + handler_ = handler; +} + void WindowEventFilter::OnClickedCaption(ui::MouseEvent* event, int previous_click_component) { aura::Window* target = static_cast<aura::Window*>(event->target()); @@ -149,6 +179,18 @@ void WindowEventFilter::LowerWindow() {} void WindowEventFilter::MaybeDispatchHostWindowDragMovement( int hittest, - ui::MouseEvent* event) {} + ui::MouseEvent* event) { + if (handler_ && event->IsLeftMouseButton() && + CanPerformDragOrResize(hittest)) { + // Some platforms (eg X11) may require last pointer location not in the + // local surface coordinates, but rather in the screen coordinates for + // interactive move/resize. + const gfx::Point last_pointer_location = + aura::Env::GetInstance()->last_mouse_location(); + handler_->DispatchHostWindowDragMovement(hittest, last_pointer_location); + event->StopPropagation(); + return; + } +} } // namespace views diff --git a/chromium/ui/views/widget/desktop_aura/window_event_filter.h b/chromium/ui/views/widget/desktop_aura/window_event_filter.h index a9bc0387c25..d3d7214f021 100644 --- a/chromium/ui/views/widget/desktop_aura/window_event_filter.h +++ b/chromium/ui/views/widget/desktop_aura/window_event_filter.h @@ -10,6 +10,10 @@ #include "ui/events/event_handler.h" #include "ui/views/views_export.h" +namespace ui { +class WmMoveResizeHandler; +} + namespace views { class DesktopWindowTreeHost; @@ -24,6 +28,11 @@ class VIEWS_EXPORT WindowEventFilter : public ui::EventHandler { // Overridden from ui::EventHandler: void OnMouseEvent(ui::MouseEvent* event) override; + // Sets a move resize handler. Currently initialized only by ozone platforms. + // See WaylandWindow::WaylandWindow in the wayland_window.cc file for an + // example. + void SetWmMoveResizeHandler(ui::WmMoveResizeHandler* handler); + private: // Called when the user clicked the caption area. void OnClickedCaption(ui::MouseEvent* event, int previous_click_component); @@ -52,6 +61,11 @@ class VIEWS_EXPORT WindowEventFilter : public ui::EventHandler { // components. int click_component_; + // A handler, which is used for interactive move/resize events if set and + // unless MaybeDispatchHostWindowDragMovement is overridden by a derived + // class. + ui::WmMoveResizeHandler* handler_ = nullptr; + DISALLOW_COPY_AND_ASSIGN(WindowEventFilter); }; diff --git a/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc b/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc index e0a11a00d46..a688da8c12b 100644 --- a/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc +++ b/chromium/ui/views/widget/desktop_aura/x11_topmost_window_finder_interactive_uitest.cc @@ -11,6 +11,7 @@ #include <vector> #include "base/macros.h" +#include "base/stl_util.h" #include "third_party/skia/include/core/SkRect.h" #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" @@ -42,9 +43,8 @@ class MinimizeWaiter : public X11PropertyChangeWaiter { bool ShouldKeepOnWaiting(const ui::PlatformEvent& event) override { std::vector<Atom> wm_states; if (ui::GetAtomArrayProperty(xwindow(), "_NET_WM_STATE", &wm_states)) { - auto it = std::find(wm_states.cbegin(), wm_states.cend(), - gfx::GetAtom("_NET_WM_STATE_HIDDEN")); - return it == wm_states.cend(); + return !base::ContainsValue(wm_states, + gfx::GetAtom("_NET_WM_STATE_HIDDEN")); } return true; } @@ -80,8 +80,7 @@ class StackingClientListWaiter : public X11PropertyChangeWaiter { std::vector<XID> stack; ui::GetXWindowStack(ui::GetX11RootWindow(), &stack); for (size_t i = 0; i < expected_windows_.size(); ++i) { - auto it = std::find(stack.cbegin(), stack.cend(), expected_windows_[i]); - if (it == stack.cend()) + if (!base::ContainsValue(stack, expected_windows_[i])) return true; } return false; diff --git a/chromium/ui/views/widget/native_widget_aura.cc b/chromium/ui/views/widget/native_widget_aura.cc index e6661017075..28c63b02b81 100644 --- a/chromium/ui/views/widget/native_widget_aura.cc +++ b/chromium/ui/views/widget/native_widget_aura.cc @@ -4,6 +4,9 @@ #include "ui/views/widget/native_widget_aura.h" +#include <memory> +#include <utility> + #include "base/bind.h" #include "base/location.h" #include "base/single_thread_task_runner.h" @@ -57,8 +60,7 @@ #if defined(OS_WIN) #include "base/win/scoped_gdi_object.h" -#include "base/win/win_client_metrics.h" -#include "ui/base/l10n/l10n_util_win.h" +#include "ui/gfx/platform_font_win.h" #include "ui/views/widget/desktop_aura/desktop_window_tree_host_win.h" #endif @@ -1155,8 +1157,7 @@ void NativeWidgetPrivate::GetAllChildWidgets(gfx::NativeView native_view, } const aura::Window::Windows& child_windows = native_view->children(); - for (aura::Window::Windows::const_iterator i = child_windows.begin(); - i != child_windows.end(); ++i) { + for (auto i = child_windows.begin(); i != child_windows.end(); ++i) { GetAllChildWidgets((*i), children); } } @@ -1192,8 +1193,7 @@ void NativeWidgetPrivate::ReparentNativeView(gfx::NativeView native_view, // First notify all the widgets that they are being disassociated // from their previous parent. - for (Widget::Widgets::iterator it = widgets.begin(); - it != widgets.end(); ++it) { + for (auto it = widgets.begin(); it != widgets.end(); ++it) { (*it)->NotifyNativeViewHierarchyWillChange(); } @@ -1217,8 +1217,7 @@ void NativeWidgetPrivate::ReparentNativeView(gfx::NativeView native_view, } // And now, notify them that they have a brand new parent. - for (Widget::Widgets::iterator it = widgets.begin(); - it != widgets.end(); ++it) { + for (auto it = widgets.begin(); it != widgets.end(); ++it) { (*it)->NotifyNativeViewHierarchyChanged(); } } @@ -1226,11 +1225,8 @@ void NativeWidgetPrivate::ReparentNativeView(gfx::NativeView native_view, // static gfx::FontList NativeWidgetPrivate::GetWindowTitleFontList() { #if defined(OS_WIN) - NONCLIENTMETRICS_XP ncm; - base::win::GetNonClientMetrics(&ncm); - l10n_util::AdjustUIFont(&(ncm.lfCaptionFont)); - base::win::ScopedHFONT caption_font(CreateFontIndirect(&(ncm.lfCaptionFont))); - return gfx::FontList(gfx::Font(caption_font.get())); + return gfx::FontList(gfx::PlatformFontWin::GetSystemFont( + gfx::PlatformFontWin::SystemFont::kCaption)); #else return gfx::FontList(); #endif diff --git a/chromium/ui/views/widget/native_widget_mac.h b/chromium/ui/views/widget/native_widget_mac.h index 5bb34bd75c3..e8e387ccd3c 100644 --- a/chromium/ui/views/widget/native_widget_mac.h +++ b/chromium/ui/views/widget/native_widget_mac.h @@ -15,6 +15,12 @@ class NativeWidgetMacNSWindow; #endif +namespace views_bridge_mac { +namespace mojom { +class BridgedNativeWidget; +} // namespace mojom +} // namespace views_bridge_mac + namespace views { namespace test { class HitTestNativeWidgetMac; @@ -22,7 +28,8 @@ class MockNativeWidgetMac; class WidgetTest; } -class BridgedNativeWidget; +class BridgeFactoryHost; +class BridgedNativeWidgetImpl; class BridgedNativeWidgetHostImpl; class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate { @@ -30,15 +37,8 @@ class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate { explicit NativeWidgetMac(internal::NativeWidgetDelegate* delegate); ~NativeWidgetMac() override; - // Retrieves the bridge associated with the given NSWindow. Returns null if - // the supplied handle has no associated Widget. - static BridgedNativeWidgetHostImpl* GetBridgeHostImplForNativeWindow( - gfx::NativeWindow window); - static BridgedNativeWidget* GetBridgeForNativeWindow( - gfx::NativeWindow window); - // Informs |delegate_| that the native widget is about to be destroyed. - // BridgedNativeWidget::OnWindowWillClose() invokes this early when the + // BridgedNativeWidgetImpl::OnWindowWillClose() invokes this early when the // NSWindowDelegate informs the bridge that the window is being closed (later, // invoking OnWindowDestroyed()). void WindowDestroying(); @@ -144,7 +144,7 @@ class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate { std::string GetName() const override; protected: - // Creates the NSWindow that will be passed to the BridgedNativeWidget. + // Creates the NSWindow that will be passed to the BridgedNativeWidgetImpl. // Called by InitNativeWidget. The return value will be autoreleased. // Note that some tests (in particular, views_unittests that interact // with ScopedFakeNSWindowFullscreen, on 10.10) assume that these windows @@ -153,11 +153,17 @@ class VIEWS_EXPORT NativeWidgetMac : public internal::NativeWidgetPrivate { virtual NativeWidgetMacNSWindow* CreateNSWindow( const Widget::InitParams& params); + // Return the BridgeFactoryHost that is to be used for creating this window + // and all of its child windows. This will return nullptr if the native + // windows are to be created in the current process. + virtual BridgeFactoryHost* GetBridgeFactoryHost(); + // Optional hook for subclasses invoked by WindowDestroying(). virtual void OnWindowDestroying(NSWindow* window) {} internal::NativeWidgetDelegate* delegate() { return delegate_; } - BridgedNativeWidget* bridge() const; + views_bridge_mac::mojom::BridgedNativeWidget* bridge() const; + BridgedNativeWidgetImpl* bridge_impl() const; BridgedNativeWidgetHostImpl* bridge_host_for_testing() const { return bridge_host_.get(); } diff --git a/chromium/ui/views/widget/native_widget_mac.mm b/chromium/ui/views/widget/native_widget_mac.mm index decbcdf69ac..4f75293c284 100644 --- a/chromium/ui/views/widget/native_widget_mac.mm +++ b/chromium/ui/views/widget/native_widget_mac.mm @@ -10,7 +10,6 @@ #include "base/bind.h" #include "base/lazy_instance.h" -#include "base/mac/foundation_util.h" #include "base/mac/scoped_nsobject.h" #include "base/strings/sys_string_conversions.h" #include "base/threading/thread_task_runner_handle.h" @@ -25,16 +24,15 @@ #import "ui/gfx/mac/nswindow_frame_controls.h" #include "ui/native_theme/native_theme.h" #include "ui/native_theme/native_theme_mac.h" -#import "ui/views/cocoa/bridged_content_view.h" -#import "ui/views/cocoa/bridged_native_widget.h" #import "ui/views/cocoa/bridged_native_widget_host_impl.h" -#include "ui/views/cocoa/cocoa_mouse_capture.h" #import "ui/views/cocoa/drag_drop_client_mac.h" -#import "ui/views/cocoa/native_widget_mac_nswindow.h" -#import "ui/views/cocoa/views_nswindow_delegate.h" #include "ui/views/widget/drop_helper.h" #include "ui/views/widget/widget_delegate.h" #include "ui/views/window/native_frame_view.h" +#import "ui/views_bridge_mac/bridged_content_view.h" +#import "ui/views_bridge_mac/bridged_native_widget_impl.h" +#import "ui/views_bridge_mac/native_widget_mac_nswindow.h" +#import "ui/views_bridge_mac/views_nswindow_delegate.h" using views_bridge_mac::mojom::WindowVisibilityState; @@ -44,17 +42,6 @@ namespace { base::LazyInstance<ui::GestureRecognizerImplMac>::Leaky g_gesture_recognizer_instance = LAZY_INSTANCE_INITIALIZER; -NativeWidgetMac* GetNativeWidgetMacForNativeWindow( - gfx::NativeWindow native_window) { - id<NSWindowDelegate> window_delegate = [native_window delegate]; - if ([window_delegate respondsToSelector:@selector(nativeWidgetMac)]) { - ViewsNSWindowDelegate* delegate = - base::mac::ObjCCastStrict<ViewsNSWindowDelegate>(window_delegate); - return [delegate nativeWidgetMac]; - } - return nullptr; // Not created by NativeWidgetMac. -} - NSInteger StyleMaskForParams(const Widget::InitParams& params) { // If the Widget is modal, it will be displayed as a sheet. This works best if // it has NSTitledWindowMask. For example, with NSBorderlessWindowMask, the @@ -92,22 +79,6 @@ NativeWidgetMac::~NativeWidgetMac() { CloseNow(); } -// static -BridgedNativeWidget* NativeWidgetMac::GetBridgeForNativeWindow( - gfx::NativeWindow window) { - if (NativeWidgetMac* widget = GetNativeWidgetMacForNativeWindow(window)) - return widget->bridge(); - return nullptr; // Not created by NativeWidgetMac. -} - -// static -BridgedNativeWidgetHostImpl* NativeWidgetMac::GetBridgeHostImplForNativeWindow( - gfx::NativeWindow window) { - if (NativeWidgetMac* widget = GetNativeWidgetMacForNativeWindow(window)) - return widget->bridge_host_.get(); - return nullptr; // Not created by NativeWidgetMac. -} - void NativeWidgetMac::WindowDestroying() { OnWindowDestroying(GetNativeWindow()); delegate_->OnNativeWidgetDestroying(); @@ -137,9 +108,29 @@ int NativeWidgetMac::SheetPositionY() { void NativeWidgetMac::InitNativeWidget(const Widget::InitParams& params) { ownership_ = params.ownership; name_ = params.name; - base::scoped_nsobject<NativeWidgetMacNSWindow> window( - [CreateNSWindow(params) retain]); - bridge()->SetWindow(window); + BridgedNativeWidgetHostImpl* parent_host = + BridgedNativeWidgetHostImpl::GetFromNativeWindow([params.parent window]); + + // Determine the factory through which to create the bridge + BridgeFactoryHost* bridge_factory_host = + parent_host ? parent_host->bridge_factory_host() : GetBridgeFactoryHost(); + if (bridge_factory_host) { + // Compute the parameters to describe the NSWindow. + // TODO(ccameron): This is not yet adequate to capture all NSWindow + // sub-classes that may be used. Make the parameter structure more + // expressive. + auto create_window_params = + views_bridge_mac::mojom::CreateWindowParams::New(); + create_window_params->style_mask = StyleMaskForParams(params); + + bridge_host_->CreateRemoteBridge(bridge_factory_host, + std::move(create_window_params)); + } else { + base::scoped_nsobject<NativeWidgetMacNSWindow> window( + [CreateNSWindow(params) retain]); + bridge_host_->CreateLocalBridge(std::move(window)); + } + bridge_host_->SetParent(parent_host); bridge_host_->InitWindow(params); // Only set always-on-top here if it is true since setting it may affect how @@ -151,8 +142,8 @@ void NativeWidgetMac::InitNativeWidget(const Widget::InitParams& params) { DCHECK(GetWidget()->GetRootView()); bridge_host_->SetRootView(GetWidget()->GetRootView()); - bridge()->CreateContentView(GetWidget()->GetRootView()->bounds()); - bridge()->CreateDragDropClient(GetWidget()->GetRootView()); + bridge()->CreateContentView(bridge_host_->GetRootViewNSViewId(), + GetWidget()->GetRootView()->bounds()); if (auto* focus_manager = GetWidget()->GetFocusManager()) { bridge()->MakeFirstResponder(); bridge_host_->SetFocusManager(focus_manager); @@ -201,7 +192,7 @@ gfx::NativeView NativeWidgetMac::GetNativeView() const { } gfx::NativeWindow NativeWidgetMac::GetNativeWindow() const { - return bridge() ? bridge()->ns_window() : nil; + return bridge_host_ ? bridge_host_->GetLocalNSWindow() : nil; } Widget* NativeWidgetMac::GetTopLevelWidget() { @@ -220,31 +211,32 @@ const ui::Layer* NativeWidgetMac::GetLayer() const { } void NativeWidgetMac::ReorderNativeViews() { - if (bridge()) - bridge()->ReorderChildViews(); + if (bridge_host_) + bridge_host_->ReorderChildViews(); } void NativeWidgetMac::ViewRemoved(View* view) { - DragDropClientMac* client = bridge() ? bridge()->drag_drop_client() : nullptr; + DragDropClientMac* client = + bridge_host_ ? bridge_host_->drag_drop_client() : nullptr; if (client) client->drop_helper()->ResetTargetViewIfEquals(view); } void NativeWidgetMac::SetNativeWindowProperty(const char* name, void* value) { - if (bridge()) - bridge()->SetNativeWindowProperty(name, value); + if (bridge_host_) + bridge_host_->SetNativeWindowProperty(name, value); } void* NativeWidgetMac::GetNativeWindowProperty(const char* name) const { - if (bridge()) - return bridge()->GetNativeWindowProperty(name); + if (bridge_host_) + return bridge_host_->GetNativeWindowProperty(name); return nullptr; } TooltipManager* NativeWidgetMac::GetTooltipManager() const { - if (bridge()) - return bridge()->tooltip_manager(); + if (bridge_host_) + return bridge_host_->tooltip_manager(); return nullptr; } @@ -268,12 +260,7 @@ ui::InputMethod* NativeWidgetMac::GetInputMethod() { } void NativeWidgetMac::CenterWindow(const gfx::Size& size) { - SetSize( - BridgedNativeWidget::GetWindowSizeForClientSize(GetNativeWindow(), size)); - // Note that this is not the precise center of screen, but it is the standard - // location for windows like dialogs to appear on screen for Mac. - // TODO(tapted): If there is a parent window, center in that instead. - [GetNativeWindow() center]; + bridge()->SetSizeAndCenter(size, GetWidget()->GetMinimumSize()); } void NativeWidgetMac::GetWindowPlacement( @@ -311,7 +298,7 @@ void NativeWidgetMac::InitModalType(ui::ModalType modal_type) { // A peculiarity of the constrained window framework is that it permits a // dialog of MODAL_TYPE_WINDOW to have a null parent window; falling back to // a non-modal window in this case. - DCHECK(bridge()->parent() || modal_type == ui::MODAL_TYPE_WINDOW); + DCHECK(bridge_host_->parent() || modal_type == ui::MODAL_TYPE_WINDOW); // Everything happens upon show. } @@ -338,19 +325,14 @@ void NativeWidgetMac::SetBounds(const gfx::Rect& bounds) { } void NativeWidgetMac::SetBoundsConstrained(const gfx::Rect& bounds) { - if (!bridge()) + if (!bridge_host_) return; - gfx::Rect new_bounds(bounds); - NativeWidgetPrivate* ancestor = - bridge() && bridge()->parent() - ? GetNativeWidgetForNativeWindow(bridge()->parent()->GetNSWindow()) - : nullptr; - if (!ancestor) { - new_bounds = ConstrainBoundsToDisplayWorkArea(new_bounds); - } else { + if (bridge_host_->parent()) { new_bounds.AdjustToFit( - gfx::Rect(ancestor->GetWindowBoundsInScreen().size())); + gfx::Rect(bridge_host_->parent()->GetWindowBoundsInScreen().size())); + } else { + new_bounds = ConstrainBoundsToDisplayWorkArea(new_bounds); } SetBounds(new_bounds); } @@ -395,8 +377,8 @@ void NativeWidgetMac::Show(ui::WindowShowState show_state, case ui::SHOW_STATE_DEFAULT: case ui::SHOW_STATE_NORMAL: case ui::SHOW_STATE_INACTIVE: - break; case ui::SHOW_STATE_MINIMIZED: + break; case ui::SHOW_STATE_MAXIMIZED: case ui::SHOW_STATE_FULLSCREEN: NOTIMPLEMENTED(); @@ -405,10 +387,12 @@ void NativeWidgetMac::Show(ui::WindowShowState show_state, NOTREACHED(); break; } - bridge()->SetVisibilityState( - show_state == ui::SHOW_STATE_INACTIVE - ? WindowVisibilityState::kShowInactive - : WindowVisibilityState::kShowAndActivateWindow); + auto window_state = WindowVisibilityState::kShowAndActivateWindow; + if (show_state == ui::SHOW_STATE_INACTIVE) + window_state = WindowVisibilityState::kShowInactive; + else if (show_state == ui::SHOW_STATE_MINIMIZED) + window_state = WindowVisibilityState::kHideWindow; + bridge()->SetVisibilityState(window_state); // Ignore the SetInitialFocus() result. BridgedContentView should get // firstResponder status regardless. @@ -517,7 +501,8 @@ void NativeWidgetMac::RunShellDrag(View* view, const gfx::Point& location, int operation, ui::DragDropTypes::DragEventSource source) { - bridge()->drag_drop_client()->StartDragAndDrop(view, data, operation, source); + bridge_host_->drag_drop_client()->StartDragAndDrop(view, data, operation, + source); } void NativeWidgetMac::SchedulePaintInRect(const gfx::Rect& rect) { @@ -535,8 +520,8 @@ void NativeWidgetMac::SchedulePaintInRect(const gfx::Rect& rect) { } void NativeWidgetMac::SetCursor(gfx::NativeCursor cursor) { - if (bridge()) - bridge()->SetCursor(cursor); + if (bridge_impl()) + bridge_impl()->SetCursor(cursor); } bool NativeWidgetMac::IsMouseEventsEnabled() const { @@ -568,20 +553,21 @@ Widget::MoveLoopResult NativeWidgetMac::RunMoveLoop( const gfx::Vector2d& drag_offset, Widget::MoveLoopSource source, Widget::MoveLoopEscapeBehavior escape_behavior) { - if (!bridge()) + if (!bridge_impl()) return Widget::MOVE_LOOP_CANCELED; - return bridge()->RunMoveLoop(drag_offset); + return bridge_impl()->RunMoveLoop(drag_offset) ? Widget::MOVE_LOOP_SUCCESSFUL + : Widget::MOVE_LOOP_CANCELED; } void NativeWidgetMac::EndMoveLoop() { - if (bridge()) - bridge()->EndMoveLoop(); + if (bridge_impl()) + bridge_impl()->EndMoveLoop(); } void NativeWidgetMac::SetVisibilityChangedAnimationsEnabled(bool value) { - if (bridge()) - bridge()->SetAnimationEnabled(value); + if (bridge_impl()) + bridge_impl()->SetAnimationEnabled(value); } void NativeWidgetMac::SetVisibilityAnimationDuration( @@ -590,9 +576,25 @@ void NativeWidgetMac::SetVisibilityAnimationDuration( } void NativeWidgetMac::SetVisibilityAnimationTransition( - Widget::VisibilityTransition transition) { + Widget::VisibilityTransition widget_transitions) { + views_bridge_mac::mojom::VisibilityTransition transitions = + views_bridge_mac::mojom::VisibilityTransition::kNone; + switch (widget_transitions) { + case Widget::ANIMATE_NONE: + transitions = views_bridge_mac::mojom::VisibilityTransition::kNone; + break; + case Widget::ANIMATE_SHOW: + transitions = views_bridge_mac::mojom::VisibilityTransition::kShow; + break; + case Widget::ANIMATE_HIDE: + transitions = views_bridge_mac::mojom::VisibilityTransition::kHide; + break; + case Widget::ANIMATE_BOTH: + transitions = views_bridge_mac::mojom::VisibilityTransition::kBoth; + break; + } if (bridge()) - bridge()->set_transitions_to_animate(transition); + bridge()->SetTransitionsToAnimate(transitions); } bool NativeWidgetMac::IsTranslucentWindowOpacitySupported() const { @@ -631,7 +633,15 @@ NativeWidgetMacNSWindow* NativeWidgetMac::CreateNSWindow( defer:NO] autorelease]; } -BridgedNativeWidget* NativeWidgetMac::bridge() const { +BridgeFactoryHost* NativeWidgetMac::GetBridgeFactoryHost() { + return nullptr; +} + +views_bridge_mac::mojom::BridgedNativeWidget* NativeWidgetMac::bridge() const { + return bridge_host_ ? bridge_host_->bridge() : nullptr; +} + +BridgedNativeWidgetImpl* NativeWidgetMac::bridge_impl() const { return bridge_host_ ? bridge_host_->bridge_impl() : nullptr; } @@ -690,32 +700,31 @@ NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeView( // static NativeWidgetPrivate* NativeWidgetPrivate::GetNativeWidgetForNativeWindow( gfx::NativeWindow window) { - if (NativeWidgetMac* widget = GetNativeWidgetMacForNativeWindow(window)) - return widget; + if (BridgedNativeWidgetHostImpl* bridge_host_impl = + BridgedNativeWidgetHostImpl::GetFromNativeWindow(window)) { + return bridge_host_impl->native_widget_mac(); + } return nullptr; // Not created by NativeWidgetMac. } // static NativeWidgetPrivate* NativeWidgetPrivate::GetTopLevelNativeWidget( gfx::NativeView native_view) { - BridgedNativeWidget* bridge = - NativeWidgetMac::GetBridgeForNativeWindow([native_view window]); - if (!bridge) + BridgedNativeWidgetHostImpl* bridge_host = + BridgedNativeWidgetHostImpl::GetFromNativeWindow([native_view window]); + if (!bridge_host) return nullptr; - - NativeWidgetPrivate* ancestor = - bridge->parent() ? GetTopLevelNativeWidget( - [bridge->parent()->GetNSWindow() contentView]) - : nullptr; - return ancestor ? ancestor : bridge->native_widget_mac(); + while (bridge_host->parent()) + bridge_host = bridge_host->parent(); + return bridge_host->native_widget_mac(); } // static void NativeWidgetPrivate::GetAllChildWidgets(gfx::NativeView native_view, Widget::Widgets* children) { - BridgedNativeWidget* bridge = - NativeWidgetMac::GetBridgeForNativeWindow([native_view window]); - if (!bridge) { + BridgedNativeWidgetHostImpl* bridge_host = + BridgedNativeWidgetHostImpl::GetFromNativeWindow([native_view window]); + if (!bridge_host) { // The NSWindow is not itself a views::Widget, but it may have children that // are. Support returning Widgets that are parented to the NSWindow, except: // - Ignore requests for children of an NSView that is not a contentView. @@ -738,67 +747,101 @@ void NativeWidgetPrivate::GetAllChildWidgets(gfx::NativeView native_view, // If |native_view| is a subview of the contentView, it will share an // NSWindow, but will itself be a native child of the Widget. That is, adding - // bridge->..->GetWidget() to |children| would be adding the _parent_ of + // bridge_host->..->GetWidget() to |children| would be adding the _parent_ of // |native_view|, not the Widget for |native_view|. |native_view| doesn't have // a corresponding Widget of its own in this case (and so can't have Widget // children of its own on Mac). - if (bridge->ns_view() != native_view) + if (bridge_host->native_widget_mac()->GetNativeView() != native_view) return; // Code expects widget for |native_view| to be added to |children|. - if (bridge->native_widget_mac()->GetWidget()) - children->insert(bridge->native_widget_mac()->GetWidget()); + if (bridge_host->native_widget_mac()->GetWidget()) + children->insert(bridge_host->native_widget_mac()->GetWidget()); - // When the NSWindow *is* a Widget, only consider child_windows(). I.e. do not - // look through -[NSWindow childWindows] as done for the (!bridge) case above. - // -childWindows does not support hidden windows, and anything in there which - // is not in child_windows() would have been added by AppKit. - for (BridgedNativeWidget* child : bridge->child_windows()) - GetAllChildWidgets(child->ns_view(), children); + // When the NSWindow *is* a Widget, only consider children(). I.e. do not + // look through -[NSWindow childWindows] as done for the (!bridge_host) case + // above. -childWindows does not support hidden windows, and anything in there + // which is not in children() would have been added by AppKit. + for (BridgedNativeWidgetHostImpl* child : bridge_host->children()) + GetAllChildWidgets(child->native_widget_mac()->GetNativeView(), children); } // static void NativeWidgetPrivate::GetAllOwnedWidgets(gfx::NativeView native_view, Widget::Widgets* owned) { - BridgedNativeWidget* bridge = - NativeWidgetMac::GetBridgeForNativeWindow([native_view window]); - if (!bridge) { + BridgedNativeWidgetHostImpl* bridge_host = + BridgedNativeWidgetHostImpl::GetFromNativeWindow([native_view window]); + if (!bridge_host) { GetAllChildWidgets(native_view, owned); return; } - if (bridge->ns_view() != native_view) + if (bridge_host->native_widget_mac()->GetNativeView() != native_view) return; - for (BridgedNativeWidget* child : bridge->child_windows()) - GetAllChildWidgets(child->ns_view(), owned); + for (BridgedNativeWidgetHostImpl* child : bridge_host->children()) + GetAllChildWidgets(child->native_widget_mac()->GetNativeView(), owned); } // static void NativeWidgetPrivate::ReparentNativeView(gfx::NativeView native_view, gfx::NativeView new_parent) { DCHECK_NE(native_view, new_parent); + DCHECK([new_parent window]); if (!new_parent || [native_view superview] == new_parent) { NOTREACHED(); return; } - BridgedNativeWidget* bridge = - NativeWidgetMac::GetBridgeForNativeWindow([native_view window]); - BridgedNativeWidget* parent_bridge = - NativeWidgetMac::GetBridgeForNativeWindow([new_parent window]); - DCHECK(bridge); - if (Widget::GetWidgetForNativeView(native_view)->is_top_level() && - bridge->parent() == parent_bridge) + BridgedNativeWidgetHostImpl* bridge_host = + BridgedNativeWidgetHostImpl::GetFromNativeWindow([native_view window]); + DCHECK(bridge_host); + NSView* bridge_view = bridge_host->native_widget_mac()->GetNativeView(); + NSWindow* bridge_window = bridge_host->native_widget_mac()->GetNativeWindow(); + bool bridge_is_top_level = + bridge_host->native_widget_mac()->GetWidget()->is_top_level(); + DCHECK([native_view isDescendantOf:bridge_view]); + DCHECK(bridge_window && ![bridge_window isSheet]); + + BridgedNativeWidgetHostImpl* parent_bridge_host = + BridgedNativeWidgetHostImpl::GetFromNativeWindow([new_parent window]); + + // Early out for no-op changes. + if (native_view == bridge_view && bridge_is_top_level && + bridge_host->parent() == parent_bridge_host) { return; + } + // First notify all the widgets that they are being disassociated from their + // previous parent. Widget::Widgets widgets; GetAllChildWidgets(native_view, &widgets); - - // First notify all the widgets that they are being disassociated - // from their previous parent. for (auto* child : widgets) child->NotifyNativeViewHierarchyWillChange(); - bridge->ReparentNativeView(native_view, new_parent); + // Update |brige_host|'s parent only if + // BridgedNativeWidgetImpl::ReparentNativeView will. + if (native_view == bridge_view) { + bridge_host->SetParent(parent_bridge_host); + if (!bridge_is_top_level) { + // Make |bridge_host|'s NSView be a child of |new_parent| by adding it as + // a subview. Note that this will have the effect of removing + // |bridge_host|'s NSView from its NSWindow. The |NSWindow| must remain + // visible because it controls the bounds and visibility of the ui::Layer, + // so just hide it by setting alpha value to zero. + // TODO(ccameron): This path likely violates assumptions. Verify that this + // path is unused and remove it. + LOG(ERROR) << "Reparenting a non-top-level BridgedNativeWidget. This is " + "likely unsupported."; + [new_parent addSubview:native_view]; + [bridge_window setAlphaValue:0]; + [bridge_window setIgnoresMouseEvents:YES]; + } + } else { + // TODO(ccameron): This path likely violates assumptions. Verify that this + // path is unused and remove it. + LOG(ERROR) << "Reparenting with a non-root BridgedNativeWidget NSView. " + "This is likely unsupported."; + [new_parent addSubview:native_view]; + } // And now, notify them that they have a brand new parent. for (auto* child : widgets) @@ -814,7 +857,7 @@ gfx::FontList NativeWidgetPrivate::GetWindowTitleFontList() { // static gfx::NativeView NativeWidgetPrivate::GetGlobalCapture( gfx::NativeView native_view) { - return [CocoaMouseCapture::GetGlobalCaptureWindow() contentView]; + return BridgedNativeWidgetHostImpl::GetGlobalCaptureView(); } } // namespace internal diff --git a/chromium/ui/views/widget/native_widget_mac_unittest.mm b/chromium/ui/views/widget/native_widget_mac_unittest.mm index ebb9ea6cd6d..4efaef021c4 100644 --- a/chromium/ui/views/widget/native_widget_mac_unittest.mm +++ b/chromium/ui/views/widget/native_widget_mac_unittest.mm @@ -28,9 +28,6 @@ #include "ui/events/test/event_generator.h" #import "ui/gfx/mac/coordinate_conversion.h" #include "ui/views/bubble/bubble_dialog_delegate_view.h" -#import "ui/views/cocoa/bridged_content_view.h" -#import "ui/views/cocoa/bridged_native_widget.h" -#import "ui/views/cocoa/native_widget_mac_nswindow.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/label.h" #include "ui/views/controls/native/native_view_host.h" @@ -42,6 +39,9 @@ #include "ui/views/widget/native_widget_private.h" #include "ui/views/window/dialog_client_view.h" #include "ui/views/window/dialog_delegate.h" +#import "ui/views_bridge_mac/bridged_content_view.h" +#import "ui/views_bridge_mac/bridged_native_widget_impl.h" +#import "ui/views_bridge_mac/native_widget_mac_nswindow.h" // Donates an implementation of -[NSAnimation stopAnimation] which calls the // original implementation, then quits a nested run loop. @@ -86,18 +86,14 @@ @interface FocusableTestNSView : NSView @end -@interface TestNativeParentWindow : NSWindow -@property(assign, nonatomic) bool* deallocFlag; -@end - namespace views { namespace test { -// BridgedNativeWidget friend to access private members. +// BridgedNativeWidgetImpl friend to access private members. class BridgedNativeWidgetTestApi { public: explicit BridgedNativeWidgetTestApi(NSWindow* window) { - bridge_ = NativeWidgetMac::GetBridgeForNativeWindow(window); + bridge_ = BridgedNativeWidgetImpl::GetFromNativeWindow(window); } // Simulate a frame swap from the compositor. @@ -116,7 +112,7 @@ class BridgedNativeWidgetTestApi { } private: - BridgedNativeWidget* bridge_; + BridgedNativeWidgetImpl* bridge_; DISALLOW_COPY_AND_ASSIGN(BridgedNativeWidgetTestApi); }; @@ -148,30 +144,33 @@ class TestWindowNativeWidgetMac : public NativeWidgetMac { DISALLOW_COPY_AND_ASSIGN(TestWindowNativeWidgetMac); }; -// Tests for parts of NativeWidgetMac not covered by BridgedNativeWidget, which -// need access to Cocoa APIs. +// Tests for parts of NativeWidgetMac not covered by BridgedNativeWidgetImpl, +// which need access to Cocoa APIs. class NativeWidgetMacTest : public WidgetTest { public: NativeWidgetMacTest() {} - // The content size of NSWindows made by MakeNativeParent(). - NSRect ParentRect() const { return NSMakeRect(100, 100, 300, 200); } - - // Make a native NSWindow with the given |style_mask| to use as a parent. - TestNativeParentWindow* MakeNativeParentWithStyle(int style_mask) { - native_parent_.reset([[TestNativeParentWindow alloc] - initWithContentRect:ParentRect() - styleMask:style_mask - backing:NSBackingStoreBuffered - defer:NO]); - [native_parent_ setReleasedWhenClosed:NO]; // Owned by scoped_nsobject. - [native_parent_ makeKeyAndOrderFront:nil]; - return native_parent_; + // Make an NSWindow with a close button and a title bar to use as a parent. + // This NSWindow is backed by a widget that is not exposed to the caller. + // To destroy the Widget, the native NSWindow must be closed. + NativeWidgetMacTestWindow* MakeClosableTitledNativeParent() { + NativeWidgetMacTestWindow* native_parent = nil; + Widget::InitParams parent_init_params = + CreateParams(Widget::InitParams::TYPE_WINDOW); + parent_init_params.bounds = gfx::Rect(100, 100, 200, 200); + CreateWidgetWithTestWindow(parent_init_params, &native_parent); + return native_parent; } - // Make a borderless, native NSWindow to use as a parent. - TestNativeParentWindow* MakeNativeParent() { - return MakeNativeParentWithStyle(NSBorderlessWindowMask); + // Same as the above, but creates a borderless NSWindow. + NativeWidgetMacTestWindow* MakeBorderlessNativeParent() { + NativeWidgetMacTestWindow* native_parent = nil; + Widget::InitParams parent_init_params = + CreateParams(Widget::InitParams::TYPE_WINDOW); + parent_init_params.remove_standard_frame = true; + parent_init_params.bounds = gfx::Rect(100, 100, 200, 200); + CreateWidgetWithTestWindow(parent_init_params, &native_parent); + return native_parent; } // Create a Widget backed by the NativeWidgetMacTestWindow NSWindow subclass. @@ -187,9 +186,6 @@ class NativeWidgetMacTest : public WidgetTest { return widget; } - protected: - base::scoped_nsobject<TestNativeParentWindow> native_parent_; - private: DISALLOW_COPY_AND_ASSIGN(NativeWidgetMacTest); }; @@ -740,26 +736,27 @@ Widget* AttachPopupToNativeParent(NSWindow* native_parent) { // Tests creating a views::Widget parented off a native NSWindow. TEST_F(NativeWidgetMacTest, NonWidgetParent) { - NSWindow* native_parent = MakeNativeParent(); + NSWindow* native_parent = MakeBorderlessNativeParent(); Widget::Widgets children; Widget::GetAllChildWidgets([native_parent contentView], &children); - EXPECT_TRUE(children.empty()); + EXPECT_EQ(1u, children.size()); Widget* child = AttachPopupToNativeParent(native_parent); TestWidgetObserver child_observer(child); - // GetTopLevelNativeWidget() only goes as far as there exists a Widget (i.e. - // must stop at |child|. + // GetTopLevelNativeWidget() will go up through |native_parent|'s Widget. internal::NativeWidgetPrivate* top_level_widget = internal::NativeWidgetPrivate::GetTopLevelNativeWidget( child->GetNativeView()); - EXPECT_EQ(child, top_level_widget->GetWidget()); + EXPECT_EQ(Widget::GetWidgetForNativeWindow(native_parent), + top_level_widget->GetWidget()); + EXPECT_NE(child, top_level_widget->GetWidget()); // To verify the parent, we need to use NativeWidgetMac APIs. - BridgedNativeWidget* bridged_native_widget = - NativeWidgetMac::GetBridgeForNativeWindow(child->GetNativeWindow()); - EXPECT_EQ(native_parent, bridged_native_widget->parent()->GetNSWindow()); + BridgedNativeWidgetImpl* bridged_native_widget = + BridgedNativeWidgetImpl::GetFromNativeWindow(child->GetNativeWindow()); + EXPECT_EQ(native_parent, bridged_native_widget->parent()->ns_window()); const gfx::Rect child_bounds(50, 50, 200, 100); child->SetBounds(child_bounds); @@ -774,8 +771,8 @@ TEST_F(NativeWidgetMacTest, NonWidgetParent) { EXPECT_EQ(native_parent, [child->GetNativeWindow() parentWindow]); Widget::GetAllChildWidgets([native_parent contentView], &children); - ASSERT_EQ(1u, children.size()); - EXPECT_EQ(child, *children.begin()); + ASSERT_EQ(2u, children.size()); + EXPECT_EQ(1u, children.count(child)); // Only non-toplevel Widgets are positioned relative to the parent, so the // bounds set above should be in screen coordinates. @@ -786,7 +783,7 @@ TEST_F(NativeWidgetMacTest, NonWidgetParent) { NSView* anchor_view = [[native_parent contentView] subviews][0]; EXPECT_TRUE(anchor_view); [anchor_view removeFromSuperview]; - EXPECT_EQ(native_parent, bridged_native_widget->parent()->GetNSWindow()); + EXPECT_EQ(native_parent, bridged_native_widget->parent()->ns_window()); // Closing the parent should close and destroy the child. EXPECT_FALSE(child_observer.widget_closed()); @@ -794,6 +791,7 @@ TEST_F(NativeWidgetMacTest, NonWidgetParent) { EXPECT_TRUE(child_observer.widget_closed()); EXPECT_EQ(0u, [[native_parent childWindows] count]); + [native_parent close]; } // Tests that CloseAllSecondaryWidgets behaves in various configurations. @@ -871,15 +869,16 @@ TEST_F(NativeWidgetMacTest, CloseAllSecondaryWidgetsValidState) { TEST_F(NativeWidgetMacTest, NonWidgetParentLastReference) { bool child_dealloced = false; bool native_parent_dealloced = false; + NativeWidgetMacTestWindow* native_parent = nil; { base::mac::ScopedNSAutoreleasePool pool; - TestNativeParentWindow* native_parent = MakeNativeParent(); + native_parent = MakeBorderlessNativeParent(); [native_parent setDeallocFlag:&native_parent_dealloced]; NativeWidgetMacTestWindow* window; Widget::InitParams init_params = CreateParams(Widget::InitParams::TYPE_POPUP); - init_params.parent = [native_parent_ contentView]; + init_params.parent = [native_parent contentView]; init_params.bounds = gfx::Rect(0, 0, 100, 200); CreateWidgetWithTestWindow(init_params, &window); [window setDeallocFlag:&child_dealloced]; @@ -891,7 +890,7 @@ TEST_F(NativeWidgetMacTest, NonWidgetParentLastReference) { // to the child window is released inside WidgetOwnerNSWindowAdapter:: // OnWindowWillClose(). base::mac::ScopedNSAutoreleasePool pool; - [native_parent_.autorelease() close]; + [native_parent close]; EXPECT_TRUE(child_dealloced); } EXPECT_TRUE(native_parent_dealloced); @@ -900,7 +899,7 @@ TEST_F(NativeWidgetMacTest, NonWidgetParentLastReference) { // Tests visibility for child of native NSWindow, reshowing after -[NSApp hide]. // Occasionally flaky (maybe due to [NSApp hide]). See https://crbug.com/777247. TEST_F(NativeWidgetMacTest, DISABLED_VisibleAfterNativeParentShow) { - NSWindow* native_parent = MakeNativeParent(); + NSWindow* native_parent = MakeBorderlessNativeParent(); Widget* child = AttachPopupToNativeParent(native_parent); child->Show(); EXPECT_TRUE(child->IsVisible()); @@ -925,7 +924,7 @@ TEST_F(NativeWidgetMacTest, VisibleAfterNativeParentDeminiaturize) { if (base::mac::IsOS10_10()) return; - NSWindow* native_parent = MakeNativeParent(); + NSWindow* native_parent = MakeBorderlessNativeParent(); [native_parent makeKeyAndOrderFront:nil]; [native_parent miniaturize:nil]; Widget* child = AttachPopupToNativeParent(native_parent); @@ -1173,9 +1172,9 @@ Widget* ShowWindowModalWidget(NSWindow* native_parent) { } // namespace // Tests object lifetime for the show/hide animations used for child-modal -// windows. Parents the dialog off a native parent window (not a views::Widget). +// windows. TEST_F(NativeWidgetMacTest, NativeWindowChildModalShowHide) { - NSWindow* native_parent = MakeNativeParent(); + NSWindow* native_parent = MakeBorderlessNativeParent(); { Widget* modal_dialog_widget = ShowChildModalWidgetAndWait(native_parent); TestWidgetObserver widget_observer(modal_dialog_widget); @@ -1228,7 +1227,7 @@ TEST_F(NativeWidgetMacTest, NativeWindowChildModalShowHide) { // Tests that calls to Hide() a Widget cancel any in-progress show animation, // and that clients can control the triggering of the animation. TEST_F(NativeWidgetMacTest, ShowAnimationControl) { - NSWindow* native_parent = MakeNativeParent(); + NSWindow* native_parent = MakeBorderlessNativeParent(); Widget* modal_dialog_widget = views::DialogDelegate::CreateDialogWidget( new ModalDialogDelegate(ui::MODAL_TYPE_CHILD), nullptr, [native_parent contentView]); @@ -1247,7 +1246,7 @@ TEST_F(NativeWidgetMacTest, ShowAnimationControl) { EXPECT_TRUE([retained_animation isAnimating]); // Hide without waiting for the animation to complete. Animation should cancel - // and clear references from BridgedNativeWidget. + // and clear references from BridgedNativeWidgetImpl. modal_dialog_widget->Hide(); EXPECT_FALSE([retained_animation isAnimating]); EXPECT_FALSE(test_api.show_animation()); @@ -1286,8 +1285,7 @@ TEST_F(NativeWidgetMacTest, ShowAnimationControl) { // Tests behavior of window-modal dialogs, displayed as sheets. TEST_F(NativeWidgetMacTest, WindowModalSheet) { - NSWindow* native_parent = - MakeNativeParentWithStyle(NSClosableWindowMask | NSTitledWindowMask); + NSWindow* native_parent = MakeClosableTitledNativeParent(); Widget* sheet_widget = views::DialogDelegate::CreateDialogWidget( new ModalDialogDelegate(ui::MODAL_TYPE_WINDOW), nullptr, @@ -1328,7 +1326,7 @@ TEST_F(NativeWidgetMacTest, WindowModalSheet) { Widget::Widgets children; Widget::GetAllChildWidgets([native_parent contentView], &children); - EXPECT_TRUE(children.empty()); + ASSERT_EQ(2u, children.size()); sheet_widget->Show(); // Should run the above block, then animate the sheet. EXPECT_TRUE(did_observe); @@ -1336,8 +1334,8 @@ TEST_F(NativeWidgetMacTest, WindowModalSheet) { // Ensure sheets are included as a child. Widget::GetAllChildWidgets([native_parent contentView], &children); - ASSERT_EQ(1u, children.size()); - EXPECT_EQ(sheet_widget, *children.begin()); + ASSERT_EQ(2u, children.size()); + EXPECT_TRUE(children.count(sheet_widget)); // Modal, so the close button in the parent window should get disabled. EXPECT_FALSE([parent_close_button isEnabled]); @@ -1348,9 +1346,9 @@ TEST_F(NativeWidgetMacTest, WindowModalSheet) { // TODO(tapted): Ideally [native_parent orderOut:nil] would also work here. // But it does not. AppKit's childWindow management breaks down after an - // -orderOut: (see BridgedNativeWidget::OnVisibilityChanged()). For regular - // child windows, BridgedNativeWidget fixes the behavior with its own - // management. However, it can't do that for sheets without encountering + // -orderOut: (see BridgedNativeWidgetImpl::OnVisibilityChanged()). For + // regular child windows, BridgedNativeWidgetImpl fixes the behavior with its + // own management. However, it can't do that for sheets without encountering // http://crbug.com/605098 and http://crbug.com/667602. -[NSApp hide:] makes // the NSWindow hidden in a different way, which does not break like // -orderOut: does. Which is good, because a user can always do -[NSApp @@ -1368,41 +1366,24 @@ TEST_F(NativeWidgetMacTest, WindowModalSheet) { sheet_widget->Close(); EXPECT_TRUE(sheet_widget->IsVisible()); - did_observe = false; - - // Experimentally (on 10.10), this notification is posted from within the - // -[NSWindow orderOut:] call that is triggered from -[ViewsNSWindowDelegate - // sheetDidEnd:]. |sheet_widget| will be destroyed next, so it's still safe to - // use in the block. However, since the orderOut just happened, it's not very - // interesting. - observer = [[NSNotificationCenter defaultCenter] - addObserverForName:NSWindowDidEndSheetNotification - object:native_parent - queue:nil - usingBlock:^(NSNotification* note) { - EXPECT_TRUE([sheet_window delegate]); - *did_observe_ptr = true; - }]; - // Pump in order to trigger -[NSWindow endSheet:..], which will block while // the animation runs, then delete |sheet_widget|. EXPECT_TRUE([sheet_window delegate]); base::RunLoop().RunUntilIdle(); EXPECT_FALSE([sheet_window delegate]); - - EXPECT_TRUE(did_observe); // Also ensures the Close() actually uses sheets. [[NSNotificationCenter defaultCenter] removeObserver:observer]; EXPECT_TRUE(widget_observer.widget_closed()); EXPECT_TRUE([parent_close_button isEnabled]); + + [native_parent close]; } // Tests behavior when closing a window that is a sheet, or that hosts a sheet, // and reshowing a sheet on a window after the sheet was closed with -[NSWindow // close]. TEST_F(NativeWidgetMacTest, CloseWithWindowModalSheet) { - NSWindow* native_parent = - MakeNativeParentWithStyle(NSClosableWindowMask | NSTitledWindowMask); + NSWindow* native_parent = MakeClosableTitledNativeParent(); { Widget* sheet_widget = ShowWindowModalWidget(native_parent); @@ -1496,8 +1477,7 @@ TEST_F(NativeWidgetMacTest, CloseWithWindowModalSheet) { // eventually complete on a destroyed NSWindowDelegate. Regression test for // https://crbug.com/851376. TEST_F(NativeWidgetMacTest, CloseWindowModalSheetWithoutSheetParent) { - NSWindow* native_parent = - MakeNativeParentWithStyle(NSClosableWindowMask | NSTitledWindowMask); + NSWindow* native_parent = MakeClosableTitledNativeParent(); { base::mac::ScopedNSAutoreleasePool pool; Widget* sheet_widget = ShowWindowModalWidget(native_parent); @@ -1541,17 +1521,16 @@ TEST_F(NativeWidgetMacTest, CloseWindowModalSheetWithoutSheetParent) { } // Test calls to Widget::ReparentNativeView() that result in a no-op on Mac. -// Tests with both native and non-native parents. TEST_F(NativeWidgetMacTest, NoopReparentNativeView) { - NSWindow* parent = MakeNativeParent(); + NSWindow* parent = MakeBorderlessNativeParent(); Widget* dialog = views::DialogDelegate::CreateDialogWidget( new DialogDelegateView, nullptr, [parent contentView]); - BridgedNativeWidget* bridge = - NativeWidgetMac::GetBridgeForNativeWindow(dialog->GetNativeWindow()); + BridgedNativeWidgetImpl* bridge = + BridgedNativeWidgetImpl::GetFromNativeWindow(dialog->GetNativeWindow()); - EXPECT_EQ(bridge->parent()->GetNSWindow(), parent); + EXPECT_EQ(bridge->parent()->ns_window(), parent); Widget::ReparentNativeView(dialog->GetNativeView(), [parent contentView]); - EXPECT_EQ(bridge->parent()->GetNSWindow(), parent); + EXPECT_EQ(bridge->parent()->ns_window(), parent); [parent close]; @@ -1559,11 +1538,12 @@ TEST_F(NativeWidgetMacTest, NoopReparentNativeView) { parent = parent_widget->GetNativeWindow(); dialog = views::DialogDelegate::CreateDialogWidget( new DialogDelegateView, nullptr, [parent contentView]); - bridge = NativeWidgetMac::GetBridgeForNativeWindow(dialog->GetNativeWindow()); + bridge = + BridgedNativeWidgetImpl::GetFromNativeWindow(dialog->GetNativeWindow()); - EXPECT_EQ(bridge->parent()->GetNSWindow(), parent); + EXPECT_EQ(bridge->parent()->ns_window(), parent); Widget::ReparentNativeView(dialog->GetNativeView(), [parent contentView]); - EXPECT_EQ(bridge->parent()->GetNSWindow(), parent); + EXPECT_EQ(bridge->parent()->ns_window(), parent); parent_widget->CloseNow(); } @@ -1778,7 +1758,7 @@ TEST_F(NativeWidgetMacTest, DISABLED_DoesHideTitle) { // The default window with a title should look different from the // window with an empty title. - EXPECT_FALSE([empty_title_data isEqualToData:this_title_data]); + EXPECT_NSNE(empty_title_data, this_title_data); delegate.set_should_show_title(false); delegate.set_title(base::ASCIIToUTF16("This is another title")); @@ -1788,7 +1768,7 @@ TEST_F(NativeWidgetMacTest, DISABLED_DoesHideTitle) { // With our magic setting, the window with a title should look the // same as the window with an empty title. EXPECT_TRUE([ns_window _isTitleHidden]); - EXPECT_TRUE([empty_title_data isEqualToData:hidden_title_data]); + EXPECT_NSEQ(empty_title_data, hidden_title_data); widget->CloseNow(); } @@ -2068,13 +2048,16 @@ TEST_F(NativeWidgetMacTest, ReparentNativeViewTypes) { CreateParams(Widget::InitParams::TYPE_POPUP); toplevel_params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET; toplevel1->Init(toplevel_params); + toplevel1->Show(); std::unique_ptr<Widget> toplevel2(new Widget); toplevel2->Init(toplevel_params); + toplevel2->Show(); Widget* child = new Widget; Widget::InitParams child_params(Widget::InitParams::TYPE_CONTROL); child->Init(child_params); + child->Show(); Widget::ReparentNativeView(child->GetNativeView(), toplevel1->GetNativeView()); @@ -2106,8 +2089,8 @@ class NativeWidgetMacFullKeyboardAccessTest : public NativeWidgetMacTest { NativeWidgetMacTest::SetUp(); widget_ = CreateTopLevelPlatformWidget(); - bridge_ = - NativeWidgetMac::GetBridgeForNativeWindow(widget_->GetNativeWindow()); + bridge_ = BridgedNativeWidgetImpl::GetFromNativeWindow( + widget_->GetNativeWindow()); fake_full_keyboard_access_ = ui::test::ScopedFakeFullKeyboardAccess::GetInstance(); DCHECK(fake_full_keyboard_access_); @@ -2120,7 +2103,7 @@ class NativeWidgetMacFullKeyboardAccessTest : public NativeWidgetMacTest { } Widget* widget_ = nullptr; - BridgedNativeWidget* bridge_ = nullptr; + BridgedNativeWidgetImpl* bridge_ = nullptr; ui::test::ScopedFakeFullKeyboardAccess* fake_full_keyboard_access_ = nullptr; }; @@ -2179,8 +2162,7 @@ class NativeWidgetMacViewsOrderTest : public WidgetTest { widget_ = CreateTopLevelPlatformWidget(); - ASSERT_EQ(1u, [[widget_->GetNativeView() subviews] count]); - compositor_view_ = [[widget_->GetNativeView() subviews] firstObject]; + starting_subviews_.reset([[widget_->GetNativeView() subviews] copy]); native_host_parent_ = new View(); widget_->GetContentsView()->AddChildView(native_host_parent_); @@ -2193,9 +2175,10 @@ class NativeWidgetMacViewsOrderTest : public WidgetTest { hosts_.push_back(std::move(holder)); } EXPECT_EQ(kNativeViewCount, native_host_parent_->child_count()); - EXPECT_TRUE(([[widget_->GetNativeView() subviews] isEqualToArray:@[ - compositor_view_, hosts_[0]->view(), hosts_[1]->view(), hosts_[2]->view() - ]])); + EXPECT_NSEQ([widget_->GetNativeView() subviews], + ([GetStartingSubviews() arrayByAddingObjectsFromArray:@[ + hosts_[0]->view(), hosts_[1]->view(), hosts_[2]->view() + ]])); } void TearDown() override { @@ -2205,10 +2188,12 @@ class NativeWidgetMacViewsOrderTest : public WidgetTest { NSView* GetContentNativeView() { return widget_->GetNativeView(); } + NSArray<NSView*>* GetStartingSubviews() { return starting_subviews_; } + Widget* widget_ = nullptr; View* native_host_parent_ = nullptr; - NSView* compositor_view_ = nil; std::vector<std::unique_ptr<NativeHostHolder>> hosts_; + base::scoped_nsobject<NSArray<NSView*>> starting_subviews_; private: DISALLOW_COPY_AND_ASSIGN(NativeWidgetMacViewsOrderTest); @@ -2218,61 +2203,65 @@ class NativeWidgetMacViewsOrderTest : public WidgetTest { // z-order. TEST_F(NativeWidgetMacViewsOrderTest, NativeViewAttached) { hosts_[1]->Detach(); - EXPECT_TRUE(([[GetContentNativeView() subviews] isEqualToArray:@[ - compositor_view_, hosts_[0]->view(), hosts_[2]->view() - ]])); + EXPECT_NSEQ([GetContentNativeView() subviews], + ([GetStartingSubviews() arrayByAddingObjectsFromArray:@[ + hosts_[0]->view(), hosts_[2]->view() + ]])); hosts_[1]->AttachNativeView(); - EXPECT_TRUE(([[GetContentNativeView() subviews] isEqualToArray:@[ - compositor_view_, hosts_[0]->view(), hosts_[1]->view(), - hosts_[2]->view() - ]])); + EXPECT_NSEQ([GetContentNativeView() subviews], + ([GetStartingSubviews() arrayByAddingObjectsFromArray:@[ + hosts_[0]->view(), hosts_[1]->view(), hosts_[2]->view() + ]])); } // Tests that NativeViews order changes according to views::View hierarchy. TEST_F(NativeWidgetMacViewsOrderTest, ReorderViews) { native_host_parent_->ReorderChildView(hosts_[2]->host(), 1); - EXPECT_TRUE(([[GetContentNativeView() subviews] isEqualToArray:@[ - compositor_view_, hosts_[0]->view(), hosts_[2]->view(), - hosts_[1]->view() - ]])); + EXPECT_NSEQ([GetContentNativeView() subviews], + ([GetStartingSubviews() arrayByAddingObjectsFromArray:@[ + hosts_[0]->view(), hosts_[2]->view(), hosts_[1]->view() + ]])); native_host_parent_->RemoveChildView(hosts_[2]->host()); - EXPECT_TRUE(([[GetContentNativeView() subviews] isEqualToArray:@[ - compositor_view_, hosts_[0]->view(), hosts_[1]->view() - ]])); + EXPECT_NSEQ([GetContentNativeView() subviews], + ([GetStartingSubviews() arrayByAddingObjectsFromArray:@[ + hosts_[0]->view(), hosts_[1]->view() + ]])); View* new_parent = new View(); native_host_parent_->RemoveChildView(hosts_[1]->host()); native_host_parent_->AddChildView(new_parent); new_parent->AddChildView(hosts_[1]->host()); new_parent->AddChildView(hosts_[2]->host()); - EXPECT_TRUE(([[GetContentNativeView() subviews] isEqualToArray:@[ - compositor_view_, hosts_[0]->view(), hosts_[1]->view(), - hosts_[2]->view() - ]])); + EXPECT_NSEQ([GetContentNativeView() subviews], + ([GetStartingSubviews() arrayByAddingObjectsFromArray:@[ + hosts_[0]->view(), hosts_[1]->view(), hosts_[2]->view() + ]])); native_host_parent_->ReorderChildView(new_parent, 0); - EXPECT_TRUE(([[GetContentNativeView() subviews] isEqualToArray:@[ - compositor_view_, hosts_[1]->view(), hosts_[2]->view(), - hosts_[0]->view() - ]])); + EXPECT_NSEQ([GetContentNativeView() subviews], + ([GetStartingSubviews() arrayByAddingObjectsFromArray:@[ + hosts_[1]->view(), hosts_[2]->view(), hosts_[0]->view() + ]])); } // Test that unassociated native views stay on top after reordering. TEST_F(NativeWidgetMacViewsOrderTest, UnassociatedViewsIsAbove) { base::scoped_nsobject<NSView> child_view([[NSView alloc] init]); [GetContentNativeView() addSubview:child_view]; - EXPECT_TRUE(([[GetContentNativeView() subviews] isEqualToArray:@[ - compositor_view_, hosts_[0]->view(), hosts_[1]->view(), - hosts_[2]->view(), child_view - ]])); + EXPECT_NSEQ( + [GetContentNativeView() subviews], + ([GetStartingSubviews() arrayByAddingObjectsFromArray:@[ + hosts_[0]->view(), hosts_[1]->view(), hosts_[2]->view(), child_view + ]])); native_host_parent_->ReorderChildView(hosts_[2]->host(), 1); - EXPECT_TRUE(([[GetContentNativeView() subviews] isEqualToArray:@[ - compositor_view_, hosts_[0]->view(), hosts_[2]->view(), - hosts_[1]->view(), child_view - ]])); + EXPECT_NSEQ( + [GetContentNativeView() subviews], + ([GetStartingSubviews() arrayByAddingObjectsFromArray:@[ + hosts_[0]->view(), hosts_[2]->view(), hosts_[1]->view(), child_view + ]])); } // Test -[NSWindowDelegate windowShouldClose:]. @@ -2421,18 +2410,3 @@ TEST_F(NativeWidgetMacTest, TouchBar) { } @end -@implementation TestNativeParentWindow { - bool* deallocFlag_; -} - -@synthesize deallocFlag = deallocFlag_; - -- (void)dealloc { - if (deallocFlag_) { - DCHECK(!*deallocFlag_); - *deallocFlag_ = true; - } - [super dealloc]; -} - -@end diff --git a/chromium/ui/views/widget/widget.cc b/chromium/ui/views/widget/widget.cc index 5ad48b616fa..0353201e793 100644 --- a/chromium/ui/views/widget/widget.cc +++ b/chromium/ui/views/widget/widget.cc @@ -1058,11 +1058,13 @@ bool Widget::OnNativeWidgetActivationChanged(bool active) { } void Widget::OnNativeFocus() { - WidgetFocusManager::GetInstance()->OnNativeFocusChanged(GetNativeView()); + WidgetFocusManager::GetInstance(GetNativeWindow()) + ->OnNativeFocusChanged(GetNativeView()); } void Widget::OnNativeBlur() { - WidgetFocusManager::GetInstance()->OnNativeFocusChanged(nullptr); + WidgetFocusManager::GetInstance(GetNativeWindow()) + ->OnNativeFocusChanged(nullptr); } void Widget::OnNativeWidgetVisibilityChanging(bool visible) { diff --git a/chromium/ui/views/widget/widget_unittest.cc b/chromium/ui/views/widget/widget_unittest.cc index 24c23336a45..352e4fbfe64 100644 --- a/chromium/ui/views/widget/widget_unittest.cc +++ b/chromium/ui/views/widget/widget_unittest.cc @@ -2034,11 +2034,6 @@ TEST_F(WidgetWindowTitleTest, SetWindowTitleChanged_DesktopNativeWidget) { #endif // !OS_CHROMEOS TEST_F(WidgetTest, WidgetDeleted_InOnMousePressed) { - // TODO: test uses GetContext(), which is not applicable to aura-mus. - // http://crbug.com/663809. - if (IsMus()) - return; - Widget* widget = new Widget; Widget::InitParams params = CreateParams(views::Widget::InitParams::TYPE_POPUP); @@ -2049,10 +2044,14 @@ TEST_F(WidgetTest, WidgetDeleted_InOnMousePressed) { widget->SetSize(gfx::Size(100, 100)); widget->Show(); - ui::test::EventGenerator generator(GetContext(), widget->GetNativeWindow()); + ui::test::EventGenerator generator( + IsMus() ? widget->GetNativeWindow() : GetContext(), + widget->GetNativeWindow()); WidgetDeletionObserver deletion_observer(widget); - generator.ClickLeftButton(); + generator.PressLeftButton(); + if (deletion_observer.IsWidgetAlive()) + generator.ReleaseLeftButton(); EXPECT_FALSE(deletion_observer.IsWidgetAlive()); // Yay we did not crash! @@ -4080,7 +4079,7 @@ class CompositingWidgetTest : public views::test::WidgetTest { const Widget::InitParams::WindowOpacity opacity) { for (const auto& widget_type : widget_types_) { #if defined(OS_MACOSX) - // Tooltips are native on Mac. See BridgedNativeWidget::Init. + // Tooltips are native on Mac. See BridgedNativeWidgetImpl::Init. if (widget_type == Widget::InitParams::TYPE_TOOLTIP) continue; #elif defined(OS_WIN) diff --git a/chromium/ui/views/widget/widget_utils_mac.mm b/chromium/ui/views/widget/widget_utils_mac.mm index 58d0a437fb0..990e17faf2f 100644 --- a/chromium/ui/views/widget/widget_utils_mac.mm +++ b/chromium/ui/views/widget/widget_utils_mac.mm @@ -4,13 +4,13 @@ #include "ui/views/widget/widget_utils_mac.h" -#import "ui/views/cocoa/bridged_native_widget.h" +#import "ui/views_bridge_mac/bridged_native_widget_impl.h" namespace views { gfx::Size GetWindowSizeForClientSize(Widget* widget, const gfx::Size& size) { DCHECK(widget); - return BridgedNativeWidget::GetWindowSizeForClientSize( + return BridgedNativeWidgetImpl::GetWindowSizeForClientSize( widget->GetNativeWindow(), size); } diff --git a/chromium/ui/views/widget/window_reorderer.cc b/chromium/ui/views/widget/window_reorderer.cc index 2f83b18fbd9..2f459a861d6 100644 --- a/chromium/ui/views/widget/window_reorderer.cc +++ b/chromium/ui/views/widget/window_reorderer.cc @@ -166,14 +166,13 @@ void WindowReorderer::ReorderChildWindows() { // |view_with_layer_order| backwards and stack windows at the bottom so that // windows not associated to a view are stacked above windows with an // associated view. - for (std::vector<View*>::reverse_iterator it = view_with_layer_order.rbegin(); + for (auto it = view_with_layer_order.rbegin(); it != view_with_layer_order.rend(); ++it) { View* view = *it; ui::Layer* layer = view->layer(); aura::Window* window = NULL; - std::map<View*, aura::Window*>::iterator hosted_window_it = - hosted_windows.find(view); + auto hosted_window_it = hosted_windows.find(view); if (hosted_window_it != hosted_windows.end()) { window = hosted_window_it->second; layer = window->layer(); diff --git a/chromium/ui/views/widget/window_reorderer_unittest.cc b/chromium/ui/views/widget/window_reorderer_unittest.cc index 579e622ee73..faecf6bf9ab 100644 --- a/chromium/ui/views/widget/window_reorderer_unittest.cc +++ b/chromium/ui/views/widget/window_reorderer_unittest.cc @@ -39,9 +39,8 @@ void SetWindowAndLayerName(aura::Window* window, const std::string& name) { // first) of |parent|. The format of the string is "name1 name2 name3 ...". std::string ChildWindowNamesAsString(const aura::Window& parent) { std::string names; - typedef std::vector<aura::Window*> Windows; - for (Windows::const_iterator it = parent.children().begin(); - it != parent.children().end(); ++it) { + for (auto it = parent.children().begin(); it != parent.children().end(); + ++it) { if (!names.empty()) names += " "; names += (*it)->GetName(); diff --git a/chromium/ui/views/win/hwnd_message_handler.cc b/chromium/ui/views/win/hwnd_message_handler.cc index 54eb0fcd0bf..9afbcf6ca30 100644 --- a/chromium/ui/views/win/hwnd_message_handler.cc +++ b/chromium/ui/views/win/hwnd_message_handler.cc @@ -1000,12 +1000,14 @@ void HWNDMessageHandler::OnBlur() {} void HWNDMessageHandler::OnCaretBoundsChanged( const ui::TextInputClient* client) { - if (!client || client->GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) - return; - if (!ax_system_caret_) ax_system_caret_ = std::make_unique<ui::AXSystemCaretWin>(hwnd()); + if (!client || client->GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) { + ax_system_caret_->Hide(); + return; + } + const gfx::Rect dip_caret_bounds(client->GetCaretBounds()); gfx::Rect caret_bounds = display::win::ScreenWin::DIPToScreenRect(hwnd(), dip_caret_bounds); @@ -1015,7 +1017,10 @@ void HWNDMessageHandler::OnCaretBoundsChanged( } void HWNDMessageHandler::OnTextInputStateChanged( - const ui::TextInputClient* client) {} + const ui::TextInputClient* client) { + if (!client || client->GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) + OnCaretBoundsChanged(client); +} void HWNDMessageHandler::OnInputMethodDestroyed( const ui::InputMethod* input_method) { @@ -1586,8 +1591,7 @@ LRESULT HWNDMessageHandler::OnCreate(CREATESTRUCT* create_struct) { base::Bind(&HWNDMessageHandler::OnSessionChange, base::Unretained(this)))); - float scale_factor = display::win::ScreenWin::GetScaleFactorForHWND(hwnd()); - dpi_ = display::win::GetDPIFromScalingFactor(scale_factor); + dpi_ = display::win::ScreenWin::GetDPIForHWND(hwnd()); // TODO(beng): move more of NWW::OnCreate here. return 0; @@ -1642,7 +1646,7 @@ LRESULT HWNDMessageHandler::OnDpiChanged(UINT msg, dpi = display::win::GetDPIFromScalingFactor(scaling_factor); } else { dpi = LOWORD(w_param); - scaling_factor = display::win::GetScalingFactorFromDPI(dpi); + scaling_factor = display::win::ScreenWin::GetScaleFactorForDPI(dpi); } // The first WM_DPICHANGED originates from EnableChildWindowDpiMessage during @@ -2967,8 +2971,8 @@ LRESULT HWNDMessageHandler::HandlePointerEventTypeTouch(UINT message, event_type, touch_point, event_time, ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_TOUCH, mapped_pointer_id, radius_x, radius_y, pressure, - pointer_touch_info.orientation, 0.0f, 0.0f, 0.0f), - ui::GetModifiersFromKeyState(), rotation_angle); + rotation_angle), + ui::GetModifiersFromKeyState()); event.latency()->AddLatencyNumberWithTimestamp( ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, event_time, 1); diff --git a/chromium/ui/views/win/pen_event_processor.cc b/chromium/ui/views/win/pen_event_processor.cc index b820c51be5e..e1429935534 100644 --- a/chromium/ui/views/win/pen_event_processor.cc +++ b/chromium/ui/views/win/pen_event_processor.cc @@ -56,12 +56,14 @@ std::unique_ptr<ui::Event> PenEventProcessor::GenerateEvent( // convert pressure into a float [0, 1]. The range of the pressure is // [0, 1024] as specified on MSDN. float pressure = static_cast<float>(pointer_pen_info.pressure) / 1024; - float rotation = pointer_pen_info.rotation; + int rotation_angle = static_cast<int>(pointer_pen_info.rotation) % 180; + if (rotation_angle < 0) + rotation_angle += 180; int tilt_x = pointer_pen_info.tiltX; int tilt_y = pointer_pen_info.tiltY; ui::PointerDetails pointer_details( input_type, mapped_pointer_id, /* radius_x */ 0.0f, /* radius_y */ 0.0f, - pressure, rotation, tilt_x, tilt_y, /* tangential_pressure */ 0.0f); + pressure, rotation_angle, tilt_x, tilt_y, /* tangential_pressure */ 0.0f); // If the flag is disabled, we send mouse events for all pen inputs. if (!direct_manipulation_enabled_) { @@ -186,12 +188,9 @@ std::unique_ptr<ui::Event> PenEventProcessor::GenerateTouchEvent( const base::TimeTicks event_time = ui::EventTimeForNow(); - int rotation_angle = static_cast<int>(pointer_details.twist) % 180; - if (rotation_angle < 0) - rotation_angle += 180; std::unique_ptr<ui::TouchEvent> event = std::make_unique<ui::TouchEvent>( event_type, point, event_time, pointer_details, - flags | ui::GetModifiersFromKeyState(), rotation_angle); + flags | ui::GetModifiersFromKeyState()); event->set_hovering(event_type == ui::ET_TOUCH_RELEASED); event->latency()->AddLatencyNumberWithTimestamp( ui::INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, event_time, 1); diff --git a/chromium/ui/views/window/custom_frame_view.cc b/chromium/ui/views/window/custom_frame_view.cc index 0ae8ebd9ad3..01a5c58eb39 100644 --- a/chromium/ui/views/window/custom_frame_view.cc +++ b/chromium/ui/views/window/custom_frame_view.cc @@ -522,8 +522,7 @@ void CustomFrameView::LayoutWindowControls() { button_order->trailing_buttons(); ImageButton* button = NULL; - for (std::vector<views::FrameButton>::const_iterator it = - leading_buttons.begin(); it != leading_buttons.end(); ++it) { + for (auto it = leading_buttons.begin(); it != leading_buttons.end(); ++it) { button = GetImageButton(*it); if (!button) continue; @@ -538,8 +537,8 @@ void CustomFrameView::LayoutWindowControls() { // Trailing buttions are laid out in a RTL fashion next_button_x = width() - FrameBorderThickness(); - for (std::vector<views::FrameButton>::const_reverse_iterator it = - trailing_buttons.rbegin(); it != trailing_buttons.rend(); ++it) { + for (auto it = trailing_buttons.rbegin(); it != trailing_buttons.rend(); + ++it) { button = GetImageButton(*it); if (!button) continue; diff --git a/chromium/ui/views/window/dialog_client_view.cc b/chromium/ui/views/window/dialog_client_view.cc index 40cc9f35cfa..7c5c56f4c27 100644 --- a/chromium/ui/views/window/dialog_client_view.cc +++ b/chromium/ui/views/window/dialog_client_view.cc @@ -17,6 +17,7 @@ #include "ui/views/controls/button/image_button.h" #include "ui/views/controls/button/label_button.h" #include "ui/views/controls/button/md_text_button.h" +#include "ui/views/event_utils.h" #include "ui/views/layout/grid_layout.h" #include "ui/views/layout/layout_provider.h" #include "ui/views/style/platform_style.h" @@ -164,6 +165,13 @@ gfx::Size DialogClientView::GetMaximumSize() const { return max_size; } +void DialogClientView::VisibilityChanged(View* starting_from, bool is_visible) { + ClientView::VisibilityChanged(starting_from, is_visible); + + if (is_visible) + view_shown_time_stamp_ = base::TimeTicks::Now(); +} + void DialogClientView::Layout() { button_row_container_->SetSize( gfx::Size(width(), button_row_container_->GetHeightForWidth(width()))); @@ -234,6 +242,9 @@ void DialogClientView::ButtonPressed(Button* sender, const ui::Event& event) { if (!GetDialogDelegate()) return; + if (IsPossiblyUnintendedInteraction(view_shown_time_stamp_, event)) + return; + if (sender == ok_button_) AcceptWindow(); else if (sender == cancel_button_) @@ -242,6 +253,10 @@ void DialogClientView::ButtonPressed(Button* sender, const ui::Event& event) { NOTREACHED(); } +void DialogClientView::ResetViewShownTimeStampForTesting() { + view_shown_time_stamp_ = base::TimeTicks(); +} + //////////////////////////////////////////////////////////////////////////////// // DialogClientView, private: diff --git a/chromium/ui/views/window/dialog_client_view.h b/chromium/ui/views/window/dialog_client_view.h index 108d9776d0b..3981e4f8b6c 100644 --- a/chromium/ui/views/window/dialog_client_view.h +++ b/chromium/ui/views/window/dialog_client_view.h @@ -7,6 +7,7 @@ #include "base/gtest_prod_util.h" #include "base/macros.h" +#include "base/time/time.h" #include "ui/base/ui_base_types.h" #include "ui/views/controls/button/button.h" #include "ui/views/window/client_view.h" @@ -54,6 +55,7 @@ class VIEWS_EXPORT DialogClientView : public ClientView, gfx::Size CalculatePreferredSize() const override; gfx::Size GetMinimumSize() const override; gfx::Size GetMaximumSize() const override; + void VisibilityChanged(View* starting_from, bool is_visible) override; void Layout() override; bool AcceleratorPressed(const ui::Accelerator& accelerator) override; @@ -66,6 +68,11 @@ class VIEWS_EXPORT DialogClientView : public ClientView, void set_minimum_size(const gfx::Size& size) { minimum_size_ = size; } + // Resets the time when view has been shown. Tests may need to call this + // method if they use events that could be otherwise treated as unintended. + // See IsPossiblyUnintendedInteraction(). + void ResetViewShownTimeStampForTesting(); + private: enum { // The number of buttons that DialogClientView can support. @@ -134,6 +141,9 @@ class VIEWS_EXPORT DialogClientView : public ClientView, // SetupLayout(). Everything will be manually updated afterwards. bool adding_or_removing_views_ = false; + // Time when view has been shown. + base::TimeTicks view_shown_time_stamp_; + DISALLOW_COPY_AND_ASSIGN(DialogClientView); }; diff --git a/chromium/ui/views/window/dialog_client_view_unittest.cc b/chromium/ui/views/window/dialog_client_view_unittest.cc index d3f0d56cf04..4c4e67e7dbc 100644 --- a/chromium/ui/views/window/dialog_client_view_unittest.cc +++ b/chromium/ui/views/window/dialog_client_view_unittest.cc @@ -8,11 +8,16 @@ #include "base/macros.h" #include "base/strings/utf_string_conversions.h" +#include "base/time/time.h" #include "build/build_config.h" #include "ui/base/ui_base_types.h" +#include "ui/events/base_event_utils.h" +#include "ui/events/event.h" +#include "ui/gfx/geometry/point.h" #include "ui/views/controls/button/checkbox.h" #include "ui/views/controls/button/image_button.h" #include "ui/views/controls/button/label_button.h" +#include "ui/views/metrics.h" #include "ui/views/style/platform_style.h" #include "ui/views/test/test_layout_provider.h" #include "ui/views/test/test_views.h" @@ -505,4 +510,24 @@ TEST_F(DialogClientViewTest, FocusChangingButtons) { EXPECT_EQ(nullptr, focus_manager->GetFocusedView()); } +// Ensures that clicks are ignored for short time after view has been shown. +TEST_F(DialogClientViewTest, IgnorePossiblyUnintendedClicks) { + widget()->Show(); + SetDialogButtons(ui::DIALOG_BUTTON_CANCEL | ui::DIALOG_BUTTON_OK); + + ui::MouseEvent mouse_event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), + ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE); + client_view()->ButtonPressed(client_view()->ok_button(), mouse_event); + client_view()->ButtonPressed(client_view()->cancel_button(), mouse_event); + EXPECT_FALSE(widget()->IsClosed()); + + client_view()->ButtonPressed( + client_view()->cancel_button(), + ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), + ui::EventTimeForNow() + base::TimeDelta::FromMilliseconds( + GetDoubleClickInterval()), + ui::EF_NONE, ui::EF_NONE)); + EXPECT_TRUE(widget()->IsClosed()); +} + } // namespace views diff --git a/chromium/ui/views_bridge_mac/BUILD.gn b/chromium/ui/views_bridge_mac/BUILD.gn index 81c1add7699..1043a8aa7bb 100644 --- a/chromium/ui/views_bridge_mac/BUILD.gn +++ b/chromium/ui/views_bridge_mac/BUILD.gn @@ -4,16 +4,41 @@ import("//mojo/public/tools/bindings/mojom.gni") +component("views_bridge_mac") { + assert(is_mac) + + sources = [ + "bridged_native_widget_host_helper.h", + "cocoa_mouse_capture.h", + "cocoa_mouse_capture.mm", + "cocoa_mouse_capture_delegate.h", + "drag_drop_client.h", + "views_bridge_mac_export.h", + ] + defines = [ "VIEWS_BRIDGE_MAC_IMPLEMENTATION" ] + deps = [ + "//base", + "//ui/base", + "//ui/events", + "//ui/gfx", + ] + libs = [ "Cocoa.framework" ] +} + mojom("mojo") { - cpp_only = true + assert(is_mac) sources = [ + "mojo/bridge_factory.mojom", "mojo/bridged_native_widget.mojom", + "mojo/bridged_native_widget_host.mojom", ] public_deps = [ "//mojo/public/mojom/base", "//ui/base/mojo", + "//ui/display/mojo:interfaces", + "//ui/events/mojo:interfaces", "//ui/gfx/geometry/mojo", "//ui/gfx/mojo", ] diff --git a/chromium/ui/views_bridge_mac/DEPS b/chromium/ui/views_bridge_mac/DEPS index b51da65b022..612029d43cb 100644 --- a/chromium/ui/views_bridge_mac/DEPS +++ b/chromium/ui/views_bridge_mac/DEPS @@ -1,4 +1,17 @@ include_rules = [ + "+components/crash/core/common/crash_key.h", + "+components/viz/common", + "+mojo/public/cpp/bindings", + "+skia/ext", + "+ui/accelerated_widget_mac", "+ui/base", + "+ui/compositor", + "+ui/display", + "+ui/events", "+ui/gfx", + # TODO(ccameron): This can be removed when sources are moved to the + # views_bridge_mac component. + "+ui/views/views_export.h", + # TODO(ccameron): This file should be moved to forward declarations. + "+ui/views/widget/util_mac.h", ] diff --git a/chromium/ui/views_bridge_mac/bridge_factory_impl.h b/chromium/ui/views_bridge_mac/bridge_factory_impl.h new file mode 100644 index 00000000000..5075e76bf52 --- /dev/null +++ b/chromium/ui/views_bridge_mac/bridge_factory_impl.h @@ -0,0 +1,41 @@ +// 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_VIEWS_BRIDGE_MAC_BRIDGE_FACTORY_IMPL_H_ +#define UI_VIEWS_BRIDGE_MAC_BRIDGE_FACTORY_IMPL_H_ + +#include "mojo/public/cpp/bindings/associated_binding.h" +#include "ui/views/views_export.h" +#include "ui/views_bridge_mac/mojo/bridge_factory.mojom.h" +#include "ui/views_bridge_mac/mojo/bridged_native_widget.mojom.h" +#include "ui/views_bridge_mac/mojo/bridged_native_widget_host.mojom.h" + +// TODO(ccameron): This file is to be moved to /ui/views_bridge_mac when +// possible. For now, put it in the namespace of that path. +namespace views_bridge_mac { + +// The factory that creates BridgedNativeWidget instances. This object is to +// be instantiated in app shim processes. +class VIEWS_EXPORT BridgeFactoryImpl : public mojom::BridgeFactory { + public: + static BridgeFactoryImpl* Get(); + void BindRequest(mojom::BridgeFactoryAssociatedRequest request); + + // mojom::BridgeFactory: + void CreateBridgedNativeWidget( + uint64_t bridge_id, + mojom::BridgedNativeWidgetAssociatedRequest bridge_request, + mojom::BridgedNativeWidgetHostAssociatedPtrInfo host) override; + + private: + friend class base::NoDestructor<BridgeFactoryImpl>; + BridgeFactoryImpl(); + ~BridgeFactoryImpl() override; + + mojo::AssociatedBinding<mojom::BridgeFactory> binding_; +}; + +} // namespace views_bridge_mac + +#endif // UI_VIEWS_BRIDGE_MAC_BRIDGE_FACTORY_IMPL_H_ diff --git a/chromium/ui/views_bridge_mac/bridge_factory_impl.mm b/chromium/ui/views_bridge_mac/bridge_factory_impl.mm new file mode 100644 index 00000000000..323abef93cc --- /dev/null +++ b/chromium/ui/views_bridge_mac/bridge_factory_impl.mm @@ -0,0 +1,86 @@ +// 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/views_bridge_mac/bridge_factory_impl.h" + +#include "base/no_destructor.h" +#include "ui/accelerated_widget_mac/window_resize_helper_mac.h" +#include "ui/views_bridge_mac/bridged_native_widget_host_helper.h" +#include "ui/views_bridge_mac/bridged_native_widget_impl.h" + +namespace views_bridge_mac { + +using views::BridgedNativeWidgetImpl; +using views::BridgedNativeWidgetHostHelper; + +namespace { + +class Bridge : public BridgedNativeWidgetHostHelper { + public: + Bridge(uint64_t bridge_id, + mojom::BridgedNativeWidgetHostAssociatedPtrInfo host_ptr, + mojom::BridgedNativeWidgetAssociatedRequest bridge_request) { + host_ptr_.Bind(std::move(host_ptr), + ui::WindowResizeHelperMac::Get()->task_runner()); + bridge_impl_ = std::make_unique<BridgedNativeWidgetImpl>( + bridge_id, host_ptr_.get(), this); + bridge_impl_->BindRequest( + std::move(bridge_request), + base::BindOnce(&Bridge::OnConnectionError, base::Unretained(this))); + } + + private: + ~Bridge() override {} + + void OnConnectionError() { delete this; } + + // BridgedNativeWidgetHostHelper: + NSView* GetNativeViewAccessible() override { return nil; } + void DispatchKeyEvent(ui::KeyEvent* event) override {} + bool DispatchKeyEventToMenuController(ui::KeyEvent* event) override { + return false; + } + void GetWordAt(const gfx::Point& location_in_content, + bool* found_word, + gfx::DecoratedText* decorated_word, + gfx::Point* baseline_point) override { + *found_word = false; + } + double SheetPositionY() override { return 0; } + views_bridge_mac::DragDropClient* GetDragDropClient() override { + // Drag-drop only doesn't work across mojo yet. + return nullptr; + } + + mojom::BridgedNativeWidgetHostAssociatedPtr host_ptr_; + std::unique_ptr<BridgedNativeWidgetImpl> bridge_impl_; +}; + +} // namespace + +// static +BridgeFactoryImpl* BridgeFactoryImpl::Get() { + static base::NoDestructor<BridgeFactoryImpl> factory; + return factory.get(); +} + +void BridgeFactoryImpl::BindRequest( + mojom::BridgeFactoryAssociatedRequest request) { + binding_.Bind(std::move(request)); +} + +void BridgeFactoryImpl::CreateBridgedNativeWidget( + uint64_t bridge_id, + mojom::BridgedNativeWidgetAssociatedRequest bridge_request, + mojom::BridgedNativeWidgetHostAssociatedPtrInfo host) { + // The resulting object will be destroyed when its message pipe is closed. + ignore_result( + new Bridge(bridge_id, std::move(host), std::move(bridge_request))); +} + +BridgeFactoryImpl::BridgeFactoryImpl() : binding_(this) {} + +BridgeFactoryImpl::~BridgeFactoryImpl() {} + +} // namespace views_bridge_mac diff --git a/chromium/ui/views/cocoa/bridged_content_view.h b/chromium/ui/views_bridge_mac/bridged_content_view.h index a6daf562123..eefdeafe1b3 100644 --- a/chromium/ui/views/cocoa/bridged_content_view.h +++ b/chromium/ui/views_bridge_mac/bridged_content_view.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_VIEWS_COCOA_BRIDGED_CONTENT_VIEW_H_ -#define UI_VIEWS_COCOA_BRIDGED_CONTENT_VIEW_H_ +#ifndef UI_VIEWS_BRIDGE_MAC_BRIDGED_CONTENT_VIEW_H_ +#define UI_VIEWS_BRIDGE_MAC_BRIDGED_CONTENT_VIEW_H_ #import <Cocoa/Cocoa.h> @@ -17,7 +17,7 @@ class TextInputClient; } namespace views { -class BridgedNativeWidget; +class BridgedNativeWidgetImpl; } // The NSView that sits as the root contentView of the NSWindow, whilst it has @@ -26,10 +26,11 @@ class BridgedNativeWidget; VIEWS_EXPORT @interface BridgedContentView : ToolTipBaseView<NSTextInputClient, NSUserInterfaceValidations, - NSDraggingSource> { + NSDraggingSource, + NSServicesMenuRequestor> { @private // Weak, reset by clearView. - views::BridgedNativeWidget* bridge_; + views::BridgedNativeWidgetImpl* bridge_; // Weak. If non-null the TextInputClient of the currently focused View in the // hierarchy rooted at |hostedView_|. Owned by the focused View. @@ -51,16 +52,22 @@ VIEWS_EXPORT // Whether there's an active key down event which is not handled yet. BOOL hasUnhandledKeyDownEvent_; + // Whether any -insertFoo: selector (e.g. -insertNewLine:) was passed to + // -doCommandBySelector: during the processing of this keyDown. These must + // always be dispatched as a ui::KeyEvent in -keyDown:. + BOOL wantsKeyHandledForInsert_; + // The last tooltip text, used to limit updates. base::string16 lastTooltipText_; } -@property(readonly, nonatomic) views::BridgedNativeWidget* bridge; +@property(readonly, nonatomic) views::BridgedNativeWidgetImpl* bridge; @property(assign, nonatomic) ui::TextInputClient* textInputClient; @property(assign, nonatomic) BOOL drawMenuBackgroundForBlur; // Initialize the NSView -> views::View bridge. |viewToHost| must be non-NULL. -- (id)initWithBridge:(views::BridgedNativeWidget*)bridge bounds:(gfx::Rect)rect; +- (id)initWithBridge:(views::BridgedNativeWidgetImpl*)bridge + bounds:(gfx::Rect)rect; // Clear the hosted view. For example, if it is about to be destroyed. - (void)clearView; @@ -80,4 +87,4 @@ VIEWS_EXPORT @end -#endif // UI_VIEWS_COCOA_BRIDGED_CONTENT_VIEW_H_ +#endif // UI_VIEWS_BRIDGE_MAC_BRIDGED_CONTENT_VIEW_H_ diff --git a/chromium/ui/views/cocoa/bridged_content_view.mm b/chromium/ui/views_bridge_mac/bridged_content_view.mm index 9ffa0c183b3..ad444b08bd3 100644 --- a/chromium/ui/views/cocoa/bridged_content_view.mm +++ b/chromium/ui/views_bridge_mac/bridged_content_view.mm @@ -2,9 +2,10 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "ui/views/cocoa/bridged_content_view.h" +#import "ui/views_bridge_mac/bridged_content_view.h" #include "base/logging.h" +#import "base/mac/foundation_util.h" #import "base/mac/mac_util.h" #import "base/mac/scoped_nsobject.h" #import "base/mac/sdk_forward_declarations.h" @@ -30,13 +31,10 @@ #include "ui/gfx/path.h" #import "ui/gfx/path_mac.h" #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" -#import "ui/views/cocoa/bridged_native_widget.h" -#import "ui/views/cocoa/bridged_native_widget_host.h" -#import "ui/views/cocoa/drag_drop_client_mac.h" -#include "ui/views/controls/label.h" -#include "ui/views/view.h" -#include "ui/views/widget/native_widget_mac.h" -#include "ui/views/widget/widget.h" +#include "ui/views_bridge_mac/bridged_native_widget_host_helper.h" +#import "ui/views_bridge_mac/bridged_native_widget_impl.h" +#import "ui/views_bridge_mac/drag_drop_client.h" +#include "ui/views_bridge_mac/mojo/bridged_native_widget_host.mojom.h" namespace { @@ -50,9 +48,9 @@ NSString* const kFullKeyboardAccessChangedNotification = gfx::Point MovePointToWindow(const NSPoint& point, NSWindow* source_window, NSWindow* target_window) { - NSPoint point_in_screen = source_window - ? ui::ConvertPointFromWindowToScreen(source_window, point) - : point; + NSPoint point_in_screen = + source_window ? ui::ConvertPointFromWindowToScreen(source_window, point) + : point; NSPoint point_in_window = ui::ConvertPointFromScreenToWindow(target_window, point_in_screen); @@ -197,6 +195,8 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { return ui::TextEditCommand::COPY; if (action == @selector(paste:)) return ui::TextEditCommand::PASTE; + if (action == @selector(pasteAndMatchStyle:)) + return ui::TextEditCommand::PASTE; if (action == @selector(selectAll:)) return ui::TextEditCommand::SELECT_ALL; return ui::TextEditCommand::INVALID_COMMAND; @@ -254,7 +254,7 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { - (void)insertTextInternal:(id)text; // Returns the native Widget's drag drop client. Possibly null. -- (views::DragDropClientMac*)dragDropClient; +- (views_bridge_mac::DragDropClient*)dragDropClient; // Menu action handlers. - (void)undo:(id)sender; @@ -262,6 +262,7 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { - (void)cut:(id)sender; - (void)copy:(id)sender; - (void)paste:(id)sender; +- (void)pasteAndMatchStyle:(id)sender; - (void)selectAll:(id)sender; @end @@ -272,7 +273,7 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { @synthesize textInputClient = textInputClient_; @synthesize drawMenuBackgroundForBlur = drawMenuBackgroundForBlur_; -- (id)initWithBridge:(views::BridgedNativeWidget*)bridge +- (id)initWithBridge:(views::BridgedNativeWidgetImpl*)bridge bounds:(gfx::Rect)bounds { // To keep things simple, assume the origin is (0, 0) until there exists a use // case for something other than that. @@ -424,13 +425,13 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { [self updateTooltipIfRequiredAt:event_location]; if (isScrollEvent) { - ui::ScrollEvent event(theEvent); - event.set_location(event_location); - bridge_->host()->OnScrollEvent(event); + auto event = std::make_unique<ui::ScrollEvent>(theEvent); + event->set_location(event_location); + bridge_->host()->OnScrollEvent(std::move(event)); } else { - ui::MouseEvent event(theEvent); - event.set_location(event_location); - bridge_->host()->OnMouseEvent(event); + auto event = std::make_unique<ui::MouseEvent>(theEvent); + event->set_location(event_location); + bridge_->host()->OnMouseEvent(std::move(event)); } } @@ -454,11 +455,8 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { // BridgedContentView private implementation. - (void)dispatchKeyEvent:(ui::KeyEvent*)event { - bool eventHandled = false; if (bridge_) - bridge_->host()->DispatchKeyEvent(*event, &eventHandled); - if (eventHandled) - event->SetHandled(); + bridge_->host_helper()->DispatchKeyEvent(event); } - (BOOL)hasActiveMenuController { @@ -469,15 +467,9 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { } - (BOOL)dispatchKeyEventToMenuController:(ui::KeyEvent*)event { - bool eventSwallowed = false; - bool eventHandled = false; - if (bridge_) { - bridge_->host()->DispatchKeyEventToMenuController(*event, &eventSwallowed, - &eventHandled); - } - if (eventHandled) - event->SetHandled(); - return eventSwallowed; + if (bridge_) + return bridge_->host_helper()->DispatchKeyEventToMenuController(event); + return false; } - (void)handleKeyEvent:(ui::KeyEvent*)event { @@ -617,22 +609,17 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { textInputClient_->InsertChar( ui::KeyEvent([text characterAtIndex:0], ui::VKEY_UNKNOWN, ui::DomCode::NONE, ui::EF_NONE)); - // Leave character events that may have triggered IME confirmation for - // inline IME (e.g. Korean) as "unhandled". There will be no more - // -insertText: messages, but we are unable to handle these via - // -handleKeyEvent: earlier in this method since toolkit-views client code - // assumes it can ignore characters associated with, e.g., VKEY_TAB. - DCHECK(keyDownEvent_); // Otherwise it is not a character event. - if ([self hasMarkedText] || !IsImeTriggerEvent(keyDownEvent_)) - hasUnhandledKeyDownEvent_ = NO; } else { textInputClient_->InsertText(base::SysNSStringToUTF16(text)); - hasUnhandledKeyDownEvent_ = NO; } + // Suppress accelerators that may be bound to this key, since it inserted + // text instead. But note that IME may follow with -insertNewLine:, which + // will resurrect the keyEvent for accelerator handling. + hasUnhandledKeyDownEvent_ = NO; } } -- (views::DragDropClientMac*)dragDropClient { +- (views_bridge_mac::DragDropClient*)dragDropClient { return bridge_ ? bridge_->drag_drop_client() : nullptr; } @@ -671,6 +658,13 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { eventFlags:ui::EF_CONTROL_DOWN]; } +- (void)pasteAndMatchStyle:(id)sender { + [self handleAction:ui::TextEditCommand::PASTE + keyCode:ui::VKEY_V + domCode:ui::DomCode::US_V + eventFlags:ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN]; +} + - (void)selectAll:(id)sender { [self handleAction:ui::TextEditCommand::SELECT_ALL keyCode:ui::VKEY_A @@ -692,13 +686,13 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { return; DCHECK([theEvent type] != NSScrollWheel); - ui::MouseEvent event(theEvent); - [self adjustUiEventLocation:&event fromNativeEvent:theEvent]; + auto event = std::make_unique<ui::MouseEvent>(theEvent); + [self adjustUiEventLocation:event.get() fromNativeEvent:theEvent]; // Aura updates tooltips with the help of aura::Window::AddPreTargetHandler(). // Mac hooks in here. - [self updateTooltipIfRequiredAt:event.location()]; - bridge_->host()->OnMouseEvent(event); + [self updateTooltipIfRequiredAt:event->location()]; + bridge_->host()->OnMouseEvent(std::move(event)); } - (void)forceTouchEvent:(NSEvent*)theEvent { @@ -780,18 +774,18 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { } - (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender { - views::DragDropClientMac* client = [self dragDropClient]; + views_bridge_mac::DragDropClient* client = [self dragDropClient]; return client ? client->DragUpdate(sender) : ui::DragDropTypes::DRAG_NONE; } - (void)draggingExited:(id<NSDraggingInfo>)sender { - views::DragDropClientMac* client = [self dragDropClient]; + views_bridge_mac::DragDropClient* client = [self dragDropClient]; if (client) client->DragExit(); } - (BOOL)performDragOperation:(id<NSDraggingInfo>)sender { - views::DragDropClientMac* client = [self dragDropClient]; + views_bridge_mac::DragDropClient* client = [self dragDropClient]; return client && client->Drop(sender) != NSDragOperationNone; } @@ -840,6 +834,7 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { // Convert the event into an action message, according to OSX key mappings. keyDownEvent_ = theEvent; hasUnhandledKeyDownEvent_ = YES; + wantsKeyHandledForInsert_ = NO; [self interpretKeyEvents:@[ theEvent ]]; // When there is marked text, -[NSView interpretKeyEvents:] may handle the @@ -853,8 +848,16 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { if (hadMarkedTextAtKeyDown && IsImeTriggerEvent(theEvent)) hasUnhandledKeyDownEvent_ = NO; - // If |keyDownEvent_| wasn't cleared during -interpretKeyEvents:, it wasn't - // handled. Give Widget accelerators a chance to handle it. + // Even with marked text, some IMEs may follow with -insertNewLine:; + // simultaneously confirming the composition. In this case, always generate + // the corresponding ui::KeyEvent. Note this is done even if there was no + // marked text, so it is orthogonal to the case above. + if (wantsKeyHandledForInsert_) + hasUnhandledKeyDownEvent_ = YES; + + // If |hasUnhandledKeyDownEvent_| wasn't set to NO during + // -interpretKeyEvents:, it wasn't handled. Give Widget accelerators a chance + // to handle it. [self handleUnhandledKeyDownAsKeyEvent]; DCHECK(!hasUnhandledKeyDownEvent_); keyDownEvent_ = nil; @@ -866,6 +869,13 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { } - (void)flagsChanged:(NSEvent*)theEvent { + if (theEvent.keyCode == 0) { + // An event like this gets sent when sending some key commands via + // AppleScript. Since 0 is VKEY_A, we end up interpreting this as Cmd+A + // which is incorrect. The correct event for command up/down (keyCode = 55) + // is also sent, so we should drop this one. See https://crbug.com/889618 + return; + } ui::KeyEvent event(theEvent); [self handleKeyEvent:&event]; } @@ -874,13 +884,13 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { if (!bridge_) return; - ui::ScrollEvent event(theEvent); - [self adjustUiEventLocation:&event fromNativeEvent:theEvent]; + auto event = std::make_unique<ui::ScrollEvent>(theEvent); + [self adjustUiEventLocation:event.get() fromNativeEvent:theEvent]; // Aura updates tooltips with the help of aura::Window::AddPreTargetHandler(). // Mac hooks in here. - [self updateTooltipIfRequiredAt:event.location()]; - bridge_->host()->OnScrollEvent(event); + [self updateTooltipIfRequiredAt:event->location()]; + bridge_->host()->OnScrollEvent(std::move(event)); } // Called when we get a three-finger swipe, and they're enabled in System @@ -904,10 +914,10 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { // Note this uses the default unique_touch_event_id of 0 (Swipe events do not // support -[NSEvent eventNumber]). This doesn't seem like a real omission // because the three-finger swipes are instant and can't be tracked anyway. - ui::GestureEvent gestureEvent(location.x(), location.y(), - ui::EventFlagsFromNative(event), - ui::EventTimeFromNative(event), swipeDetails); - bridge_->host()->OnGestureEvent(gestureEvent); + auto gestureEvent = std::make_unique<ui::GestureEvent>( + location.x(), location.y(), ui::EventFlagsFromNative(event), + ui::EventTimeFromNative(event), swipeDetails); + bridge_->host()->OnGestureEvent(std::move(gestureEvent)); } - (void)quickLookWithEvent:(NSEvent*)theEvent { @@ -920,8 +930,8 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { bool foundWord = false; gfx::DecoratedText decoratedWord; gfx::Point baselinePoint; - bridge_->host()->GetWordAt(locationInContent, &foundWord, &decoratedWord, - &baselinePoint); + bridge_->host_helper()->GetWordAt(locationInContent, &foundWord, + &decoratedWord, &baselinePoint); if (!foundWord) return; @@ -1341,21 +1351,27 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { // Currently we only support reading and writing plain strings. - (id)validRequestorForSendType:(NSString*)sendType returnType:(NSString*)returnType { - BOOL canWrite = [sendType isEqualToString:NSStringPboardType] && - [self selectedRange].length > 0; - BOOL canRead = [returnType isEqualToString:NSStringPboardType]; + NSString* const utf8Type = base::mac::CFToNSCast(kUTTypeUTF8PlainText); + BOOL canWrite = + [sendType isEqualToString:utf8Type] && [self selectedRange].length > 0; + BOOL canRead = [returnType isEqualToString:utf8Type]; // Valid if (sendType, returnType) is either (string, nil), (nil, string), // or (string, string). BOOL valid = textInputClient_ && ((canWrite && (canRead || !returnType)) || (canRead && (canWrite || !sendType))); - return valid ? self : [super validRequestorForSendType:sendType - returnType:returnType]; + return valid + ? self + : [super validRequestorForSendType:sendType returnType:returnType]; } -// NSServicesRequests informal protocol. +// NSServicesMenuRequestor protocol - (BOOL)writeSelectionToPasteboard:(NSPasteboard*)pboard types:(NSArray*)types { - DCHECK([types containsObject:NSStringPboardType]); + // NB: The NSServicesMenuRequestor protocol has not (as of 10.14) been + // upgraded to request UTIs rather than obsolete PboardType constants. Handle + // either for when it is upgraded. + DCHECK([types containsObject:NSStringPboardType] || + [types containsObject:base::mac::CFToNSCast(kUTTypeUTF8PlainText)]); if (!textInputClient_) return NO; @@ -1386,9 +1402,9 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { // |self|) that we're trying to invalidate in -setTextInputClient:. // See https://crbug.com/817097#c12 for further details on this atrocity. -- (NSAttributedString*) - attributedSubstringForProposedRange:(NSRange)range - actualRange:(NSRangePointer)actualRange { +- (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range + actualRange: + (NSRangePointer)actualRange { // On TouchBar Macs, the IME subsystem sometimes sends an invalid range with a // non-zero length. This will cause a DCHECK in gfx::Range, so repair it here. // See https://crbug.com/888782. @@ -1418,9 +1434,34 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { - (void)doCommandBySelector:(SEL)selector { // Like the renderer, handle insert action messages as a regular key dispatch. - // This ensures, e.g., insertTab correctly changes focus between fields. - if (keyDownEvent_ && [NSStringFromSelector(selector) hasPrefix:@"insert"]) + // This ensures, e.g., insertTab correctly changes focus between fields. This + // handles: + // -insertTab:(id)sender + // -insertBacktab: + // -insertNewline: + // -insertParagraphSeparator: + // -insertNewlineIgnoringFieldEditor: + // -insertTabIgnoringFieldEditor: + // -insertLineBreak: + // -insertContainerBreak: + // -insertSingleQuoteIgnoringSubstitution: + // -insertDoubleQuoteIgnoringSubstitution: + // It does not handle |-insertText:(id)insertString|, which is not a command. + // I.e. AppKit invokes _either_ -insertText: or -doCommandBySelector:. Also + // note -insertText: is only invoked if -inputContext: has returned nil. + DCHECK_NE(@selector(insertText:), selector); + if (keyDownEvent_ && [NSStringFromSelector(selector) hasPrefix:@"insert"]) { + // When return is pressed during IME composition, engines typically first + // confirm the composition with a series of -insertText:replacementRange: + // calls. Then, some also invoke -insertNewLine: (some do not). If an engine + // DOES invokes -insertNewLine:, we always want a corresponding VKEY_RETURN + // ui::KeyEvent generated. If it does NOT follow with -insertNewLine:, the + // VKEY_RETURN must be suppressed in keyDown:, since it typically will have + // merely dismissed the IME window: the composition is still ongoing. + // Setting this ensures keyDown: always generates a ui::KeyEvent. + wantsKeyHandledForInsert_ = YES; return; // Handle in -keyDown:. + } if ([self respondsToSelector:selector]) { [self performSelector:selector withObject:nil]; @@ -1547,7 +1588,7 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { - (void)draggingSession:(NSDraggingSession*)session endedAtPoint:(NSPoint)screenPoint operation:(NSDragOperation)operation { - views::DragDropClientMac* client = [self dragDropClient]; + views_bridge_mac::DragDropClient* client = [self dragDropClient]; if (client) client->EndDrag(); } @@ -1556,21 +1597,21 @@ ui::TextEditCommand GetTextEditCommandForMenuAction(SEL action) { - (id)accessibilityAttributeValue:(NSString*)attribute { if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { - return @[ bridge_->host()->GetNativeViewAccessible() ]; + return @[ bridge_->host_helper()->GetNativeViewAccessible() ]; } return [super accessibilityAttributeValue:attribute]; } - (id)accessibilityHitTest:(NSPoint)point { - return - [bridge_->host()->GetNativeViewAccessible() accessibilityHitTest:point]; + return [bridge_->host_helper()->GetNativeViewAccessible() + accessibilityHitTest:point]; } - (id)accessibilityFocusedUIElement { if (!bridge_) return nil; - return [bridge_->host()->GetNativeViewAccessible() + return [bridge_->host_helper()->GetNativeViewAccessible() accessibilityFocusedUIElement]; } diff --git a/chromium/ui/views/cocoa/bridged_content_view_touch_bar.mm b/chromium/ui/views_bridge_mac/bridged_content_view_touch_bar.mm index 6f1d18d11c4..008f7ff7886 100644 --- a/chromium/ui/views/cocoa/bridged_content_view_touch_bar.mm +++ b/chromium/ui/views_bridge_mac/bridged_content_view_touch_bar.mm @@ -7,9 +7,9 @@ #import "base/mac/sdk_forward_declarations.h" #include "base/strings/sys_string_conversions.h" #import "ui/base/cocoa/touch_bar_forward_declarations.h" -#import "ui/views/cocoa/bridged_content_view.h" -#import "ui/views/cocoa/bridged_native_widget.h" -#import "ui/views/cocoa/bridged_native_widget_host.h" +#import "ui/views_bridge_mac/bridged_content_view.h" +#import "ui/views_bridge_mac/bridged_native_widget_impl.h" +#include "ui/views_bridge_mac/mojo/bridged_native_widget_host.mojom.h" namespace { diff --git a/chromium/ui/views_bridge_mac/bridged_native_widget_host_helper.h b/chromium/ui/views_bridge_mac/bridged_native_widget_host_helper.h new file mode 100644 index 00000000000..4fe8218cc2a --- /dev/null +++ b/chromium/ui/views_bridge_mac/bridged_native_widget_host_helper.h @@ -0,0 +1,67 @@ +// 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_VIEWS_BRIDGE_MAC_BRIDGED_NATIVE_WIDGET_HOST_HELPER_H_ +#define UI_VIEWS_BRIDGE_MAC_BRIDGED_NATIVE_WIDGET_HOST_HELPER_H_ + +#include "ui/base/ui_base_types.h" +#include "ui/events/event_utils.h" +#include "ui/gfx/decorated_text.h" +#include "ui/gfx/geometry/point.h" +#include "ui/views_bridge_mac/views_bridge_mac_export.h" + +@class NSView; + +namespace views_bridge_mac { + +class DragDropClient; + +// This is a helper class for the mojo interface BridgedNativeWidgetHost. +// This provides an easier-to-use interface than the mojo for selected +// functions. It also is temporarily exposing functionality that is not yet +// implemented over mojo. +class VIEWS_BRIDGE_MAC_EXPORT BridgedNativeWidgetHostHelper { + public: + virtual ~BridgedNativeWidgetHostHelper() = default; + + // Retrieve the NSView for accessibility for this widget. + // TODO(ccameron): This interface cannot be implemented over IPC. A scheme + // for implementing accessibility across processes needs to be designed and + // implemented. + virtual NSView* GetNativeViewAccessible() = 0; + + // Synchronously dispatch a key event. Note that this function will modify + // |event| based on whether or not it was handled. + virtual void DispatchKeyEvent(ui::KeyEvent* event) = 0; + + // Synchronously dispatch a key event to the current menu controller (if one + // exists and it is owned by the widget for this). Return true if the event + // was swallowed (that is, if the menu's dispatch returned + // POST_DISPATCH_NONE). Note that this function will modify |event| based on + // whether or not it was handled. + virtual bool DispatchKeyEventToMenuController(ui::KeyEvent* event) = 0; + + // Synchronously query the quicklook text at |location_in_content|. Return in + // |found_word| whether or not a word was found. + // TODO(ccameron): This needs gfx::DecoratedText to be mojo-ified before it + // can be done over mojo. + virtual void GetWordAt(const gfx::Point& location_in_content, + bool* found_word, + gfx::DecoratedText* decorated_word, + gfx::Point* baseline_point) = 0; + + // Returns the vertical position that sheets should be anchored, in pixels + // from the bottom of the window. + // TODO(ccameron): This should be either moved to the mojo interface or + // separated out in such a way as to avoid needing to go through mojo. + virtual double SheetPositionY() = 0; + + // Return a pointer to host's DragDropClientMac. + // TODO(ccameron): Drag-drop behavior needs to be implemented over mojo. + virtual DragDropClient* GetDragDropClient() = 0; +}; + +} // namespace views_bridge_mac + +#endif // UI_VIEWS_BRIDGE_MAC_BRIDGED_NATIVE_WIDGET_HOST_HELPER_H_ diff --git a/chromium/ui/views/cocoa/bridged_native_widget.h b/chromium/ui/views_bridge_mac/bridged_native_widget_impl.h index 6d2d12e6455..acdb1997003 100644 --- a/chromium/ui/views/cocoa/bridged_native_widget.h +++ b/chromium/ui/views_bridge_mac/bridged_native_widget_impl.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_VIEWS_COCOA_BRIDGED_NATIVE_WIDGET_H_ -#define UI_VIEWS_COCOA_BRIDGED_NATIVE_WIDGET_H_ +#ifndef UI_VIEWS_BRIDGE_MAC_BRIDGED_NATIVE_WIDGET_IMPL_H_ +#define UI_VIEWS_BRIDGE_MAC_BRIDGED_NATIVE_WIDGET_IMPL_H_ #import <Cocoa/Cocoa.h> @@ -13,15 +13,14 @@ #import "base/mac/scoped_nsobject.h" #include "base/macros.h" #include "components/viz/common/surfaces/parent_local_surface_id_allocator.h" +#include "mojo/public/cpp/bindings/associated_binding.h" #include "ui/accelerated_widget_mac/ca_transaction_observer.h" #include "ui/accelerated_widget_mac/display_ca_layer_tree.h" +#include "ui/base/cocoa/ns_view_ids.h" #include "ui/base/ime/text_input_client.h" #include "ui/display/display_observer.h" -#import "ui/views/cocoa/bridged_native_widget_owner.h" -#import "ui/views/cocoa/cocoa_mouse_capture_delegate.h" -#import "ui/views/focus/focus_manager.h" #include "ui/views/views_export.h" -#include "ui/views/widget/widget.h" +#import "ui/views_bridge_mac/cocoa_mouse_capture_delegate.h" #include "ui/views_bridge_mac/mojo/bridged_native_widget.mojom.h" @class BridgedContentView; @@ -29,75 +28,80 @@ @class NativeWidgetMacNSWindow; @class ViewsNSWindowDelegate; +namespace views_bridge_mac { + +namespace mojom { +class BridgedNativeWidgetHost; +} // namespace mojom + +class BridgedNativeWidgetHostHelper; +class CocoaMouseCapture; +class DragDropClient; + +} // namespace views_bridge_mac + namespace views { namespace test { class BridgedNativeWidgetTestApi; } -class BridgedNativeWidgetHost; -class CocoaMouseCapture; class CocoaWindowMoveLoop; -class DragDropClientMac; -class NativeWidgetMac; -class View; + +using views_bridge_mac::mojom::BridgedNativeWidgetHost; +using views_bridge_mac::BridgedNativeWidgetHostHelper; +using views_bridge_mac::CocoaMouseCapture; +using views_bridge_mac::CocoaMouseCaptureDelegate; // A bridge to an NSWindow managed by an instance of NativeWidgetMac or // DesktopNativeWidgetMac. Serves as a helper class to bridge requests from the // NativeWidgetMac to the Cocoa window. Behaves a bit like an aura::Window. -class VIEWS_EXPORT BridgedNativeWidget +class VIEWS_EXPORT BridgedNativeWidgetImpl : public views_bridge_mac::mojom::BridgedNativeWidget, public display::DisplayObserver, public ui::CATransactionCoordinator::PreCommitObserver, - public CocoaMouseCaptureDelegate, - public BridgedNativeWidgetOwner { + public CocoaMouseCaptureDelegate { public: - // Contains NativeViewHost->gfx::NativeView associations. - using AssociatedViews = std::map<const views::View*, NSView*>; - // Return the size that |window| will take for the given client area |size|, // based on its current style mask. static gfx::Size GetWindowSizeForClientSize(NSWindow* window, const gfx::Size& size); + // Retrieves the bridge associated with the given NSWindow. Returns null if + // the supplied handle has no associated Widget. + static BridgedNativeWidgetImpl* GetFromNativeWindow(NSWindow* window); + + // Retrieve a BridgedNativeWidgetImpl* from its id. + static BridgedNativeWidgetImpl* GetFromId(uint64_t bridged_native_widget_id); + + // Create an NSWindow for the specified parameters. + static base::scoped_nsobject<NativeWidgetMacNSWindow> CreateNSWindow( + views_bridge_mac::mojom::CreateWindowParams* params); + // Creates one side of the bridge. |host| and |parent| must not be NULL. - BridgedNativeWidget(BridgedNativeWidgetHost* host, NativeWidgetMac* parent); - ~BridgedNativeWidget() override; + BridgedNativeWidgetImpl(uint64_t bridged_native_widget_id, + BridgedNativeWidgetHost* host, + BridgedNativeWidgetHostHelper* host_helper); + ~BridgedNativeWidgetImpl() override; + + // Bind |bridge_mojo_binding_| to |request|, and set the connection error + // callback for |bridge_mojo_binding_| to |connection_closed_callback| (which + // will delete |this| when the connection is closed). + void BindRequest( + views_bridge_mac::mojom::BridgedNativeWidgetAssociatedRequest request, + base::OnceClosure connection_closed_callback); // Initialize the NSWindow by taking ownership of the specified object. - // TODO(ccameron): When a BridgedNativeWidget is allocated across a process - // boundary, it will not be possible to explicitly set an NSWindow in this - // way. + // TODO(ccameron): When a BridgedNativeWidgetImpl is allocated across a + // process boundary, it will not be possible to explicitly set an NSWindow in + // this way. void SetWindow(base::scoped_nsobject<NativeWidgetMacNSWindow> window); - // Create the drag drop client for this widget. - // TODO(ccameron): This function takes a views::View (and is not rolled into - // CreateContentView) because drag-drop has not been routed through |host_| - // yet. - void CreateDragDropClient(views::View* view); - - // Set the parent NSView for the widget. - // TODO(ccameron): Like SetWindow, this will need to pass a handle instead of - // an NSView across processes. - void SetParent(NSView* parent); - - // Changes the bounds of the window and the hosted layer if present. The - // origin is a location in screen coordinates except for "child" windows, - // which are positioned relative to their parent(). SetBounds() considers a - // "child" window to be one initialized with InitParams specifying all of: - // a |parent| NSWindow, the |child| attribute, and a |type| that - // views::GetAuraWindowTypeForWidgetType does not consider a "popup" type. - void SetBounds(const gfx::Rect& new_bounds); - // Start moving the window, pinned to the mouse cursor, and monitor events. - // Return MOVE_LOOP_SUCCESSFUL on mouse up or MOVE_LOOP_CANCELED on premature - // termination via EndMoveLoop() or when window is destroyed during the drag. - Widget::MoveLoopResult RunMoveLoop(const gfx::Vector2d& drag_offset); + // Return true on mouse up or false on premature termination via EndMoveLoop() + // or when window is destroyed during the drag. + bool RunMoveLoop(const gfx::Vector2d& drag_offset); void EndMoveLoop(); - // See views::Widget. - void SetNativeWindowProperty(const char* key, void* value); - void* GetNativeWindowProperty(const char* key) const; - // Sets the cursor associated with the NSWindow. Retains |cursor|. void SetCursor(NSCursor* cursor); @@ -141,38 +145,25 @@ class VIEWS_EXPORT BridgedNativeWidget // Called by the window show animation when it completes and wants to destroy // itself. void OnShowAnimationComplete(); + // Sort child NSViews according to their ranking in |rank|. + void SortSubviews(std::map<NSView*, int> rank); - // Updates |associated_views_| on NativeViewHost::Attach()/Detach(). - void SetAssociationForView(const views::View* view, NSView* native_view); - void ClearAssociationForView(const views::View* view); - // Sorts child NSViews according to NativeViewHosts order in views hierarchy. - void ReorderChildViews(); - - NativeWidgetMac* native_widget_mac() { return native_widget_mac_; } BridgedContentView* ns_view() { return bridged_view_; } BridgedNativeWidgetHost* host() { return host_; } + BridgedNativeWidgetHostHelper* host_helper() { return host_helper_; } NSWindow* ns_window(); - TooltipManager* tooltip_manager() { return tooltip_manager_.get(); } - - DragDropClientMac* drag_drop_client() { return drag_drop_client_.get(); } + views_bridge_mac::DragDropClient* drag_drop_client(); bool is_translucent_window() const { return is_translucent_window_; } // The parent widget specified in Widget::InitParams::parent. If non-null, the // parent will close children before the parent closes, and children will be // raised above their parent when window z-order changes. - BridgedNativeWidgetOwner* parent() { return parent_; } - const std::vector<BridgedNativeWidget*>& child_windows() { + BridgedNativeWidgetImpl* parent() { return parent_; } + const std::vector<BridgedNativeWidgetImpl*>& child_windows() { return child_windows_; } - // Re-parent a |native_view| in this Widget to be a child of |new_parent|. - // |native_view| must either be |ns_view()| or a descendant of |ns_view()|. - // |native_view| is added as a subview of |new_parent| unless it is the - // contentView of a top-level Widget. If |native_view| is |ns_view()|, |this| - // also becomes a child window of |new_parent|'s NSWindow. - void ReparentNativeView(NSView* native_view, NSView* new_parent); - bool target_fullscreen_state() const { return target_fullscreen_state_; } bool window_visible() const { return window_visible_; } bool wants_to_be_visible() const { return wants_to_be_visible_; } @@ -181,16 +172,9 @@ class VIEWS_EXPORT BridgedNativeWidget // Enables or disables all window animations. void SetAnimationEnabled(bool animate); - // Sets which transitions will animate. Currently this only affects non-native - // animations. TODO(tapted): Use scoping to disable native animations at - // appropriate times as well. - void set_transitions_to_animate(int transitions) { - transitions_to_animate_ = transitions; - } - // Whether to run a custom animation for the provided |transition|. bool ShouldRunCustomAnimationFor( - Widget::VisibilityTransition transition) const; + views_bridge_mac::mojom::VisibilityTransition transition) const; // display::DisplayObserver: void OnDisplayMetricsChanged(const display::Display& display, @@ -201,20 +185,26 @@ class VIEWS_EXPORT BridgedNativeWidget base::TimeDelta PreCommitTimeout() override; // views_bridge_mac::mojom::BridgedNativeWidget: + void CreateWindow( + views_bridge_mac::mojom::CreateWindowParamsPtr params) override; + void SetParent(uint64_t parent_id) override; void InitWindow(views_bridge_mac::mojom::BridgedNativeWidgetInitParamsPtr params) override; void InitCompositorView() override; - void CreateContentView(const gfx::Rect& bounds) override; + void CreateContentView(uint64_t ns_view_id, const gfx::Rect& bounds) override; void DestroyContentView() override; void CloseWindow() override; void CloseWindowNow() override; void SetInitialBounds(const gfx::Rect& new_bounds, - const gfx::Size& minimum_content_size, - const gfx::Vector2d& parent_offset) override; + const gfx::Size& minimum_content_size) override; void SetBounds(const gfx::Rect& new_bounds, const gfx::Size& minimum_content_size) override; + void SetSizeAndCenter(const gfx::Size& content_size, + const gfx::Size& minimum_content_size) override; void SetVisibilityState( views_bridge_mac::mojom::WindowVisibilityState new_state) override; + void SetTransitionsToAnimate( + views_bridge_mac::mojom::VisibilityTransition transitions) override; void SetVisibleOnAllSpaces(bool always_visible) override; void SetFullscreen(bool fullscreen) override; void SetMiniaturized(bool miniaturized) override; @@ -228,6 +218,7 @@ class VIEWS_EXPORT BridgedNativeWidget void SetWindowTitle(const base::string16& title) override; void MakeFirstResponder() override; void ClearTouchBar() override; + void UpdateTooltip() override; void AcquireCapture() override; void ReleaseCapture() override; @@ -239,18 +230,22 @@ class VIEWS_EXPORT BridgedNativeWidget // update widget and compositor size. void UpdateWindowGeometry(); + // The offset in screen pixels for positioning child windows owned by |this|. + gfx::Vector2d GetChildWindowOffset() const; + private: friend class test::BridgedNativeWidgetTestApi; - // Closes all child windows. BridgedNativeWidget children will be destroyed. + // Closes all child windows. BridgedNativeWidgetImpl children will be + // destroyed. void RemoveOrDestroyChildren(); + // Remove the specified child window without closing it. + void RemoveChildWindow(BridgedNativeWidgetImpl* child); + // Notify descendants of a visibility change. void NotifyVisibilityChangeDown(); - // Installs the NSView for hosting the composited layer. - void AddCompositorSuperview(); - // Query the display properties of the monitor that |window_| is on, and // forward them to |host_|. void UpdateWindowDisplay(); @@ -270,31 +265,23 @@ class VIEWS_EXPORT BridgedNativeWidget void OnMouseCaptureLost() override; NSWindow* GetWindow() const override; - // Returns a properties dictionary associated with the NSWindow. - // Creates and attaches a new instance if not found. - NSMutableDictionary* GetWindowProperties() const; - - // BridgedNativeWidgetOwner: - NSWindow* GetNSWindow() override; - gfx::Vector2d GetChildWindowOffset() const override; - bool IsVisibleParent() const override; - void RemoveChildWindow(BridgedNativeWidget* child) override; - - BridgedNativeWidgetHost* const host_; // Weak. Owns this. - NativeWidgetMac* const native_widget_mac_; // Weak. Owns |host_|. + const uint64_t id_; + BridgedNativeWidgetHost* const host_; // Weak. Owns this. + BridgedNativeWidgetHostHelper* const host_helper_; // Weak, owned by |host_|. base::scoped_nsobject<NativeWidgetMacNSWindow> window_; base::scoped_nsobject<ViewsNSWindowDelegate> window_delegate_; base::scoped_nsobject<BridgedContentView> bridged_view_; + std::unique_ptr<ui::ScopedNSViewIdMapping> bridged_view_id_mapping_; base::scoped_nsobject<ModalShowAnimationWithLayer> show_animation_; std::unique_ptr<CocoaMouseCapture> mouse_capture_; std::unique_ptr<CocoaWindowMoveLoop> window_move_loop_; - std::unique_ptr<TooltipManager> tooltip_manager_; - std::unique_ptr<DragDropClientMac> drag_drop_client_; ui::ModalType modal_type_ = ui::MODAL_TYPE_NONE; bool is_translucent_window_ = false; + bool widget_is_top_level_ = false; + bool position_window_in_screen_coords_ = false; - BridgedNativeWidgetOwner* parent_ = nullptr; // Weak. If non-null, owns this. - std::vector<BridgedNativeWidget*> child_windows_; + BridgedNativeWidgetImpl* parent_ = nullptr; // Weak. If non-null, owns this. + std::vector<BridgedNativeWidgetImpl*> child_windows_; // The size of the content area of the window most recently sent to |host_| // (and its compositor). @@ -305,7 +292,6 @@ class VIEWS_EXPORT BridgedNativeWidget // |content_dip_size_|, which is the frame size most recently *sent to* the // compositor. gfx::Size compositor_frame_dip_size_; - base::scoped_nsobject<NSView> compositor_superview_; std::unique_ptr<ui::DisplayCALayerTree> display_ca_layer_tree_; // Tracks the bounds when the window last started entering fullscreen. Used to @@ -314,8 +300,9 @@ class VIEWS_EXPORT BridgedNativeWidget gfx::Rect bounds_before_fullscreen_; // The transition types to animate when not relying on native NSWindow - // animation behaviors. Bitmask of Widget::VisibilityTransition. - int transitions_to_animate_ = Widget::ANIMATE_BOTH; + // animation behaviors. + views_bridge_mac::mojom::VisibilityTransition transitions_to_animate_ = + views_bridge_mac::mojom::VisibilityTransition::kBoth; // Whether this window wants to be fullscreen. If a fullscreen animation is in // progress then it might not be actually fullscreen. @@ -341,11 +328,11 @@ class VIEWS_EXPORT BridgedNativeWidget // shadow needs to be invalidated when a frame is received for the new shape. bool invalidate_shadow_on_frame_swap_ = false; - AssociatedViews associated_views_; - - DISALLOW_COPY_AND_ASSIGN(BridgedNativeWidget); + mojo::AssociatedBinding<views_bridge_mac::mojom::BridgedNativeWidget> + bridge_mojo_binding_; + DISALLOW_COPY_AND_ASSIGN(BridgedNativeWidgetImpl); }; } // namespace views -#endif // UI_VIEWS_COCOA_BRIDGED_NATIVE_WIDGET_H_ +#endif // UI_VIEWS_BRIDGE_MAC_BRIDGED_NATIVE_WIDGET_IMPL_H_ diff --git a/chromium/ui/views/cocoa/bridged_native_widget.mm b/chromium/ui/views_bridge_mac/bridged_native_widget_impl.mm index abdbcc45fab..86b67f18fba 100644 --- a/chromium/ui/views/cocoa/bridged_native_widget.mm +++ b/chromium/ui/views_bridge_mac/bridged_native_widget_impl.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "ui/views/cocoa/bridged_native_widget.h" +#import "ui/views_bridge_mac/bridged_native_widget_impl.h" #import <objc/runtime.h> #include <stddef.h> @@ -13,9 +13,13 @@ #import "base/mac/foundation_util.h" #include "base/mac/mac_util.h" #import "base/mac/sdk_forward_declarations.h" +#include "base/no_destructor.h" #include "base/single_thread_task_runner.h" #include "base/strings/sys_string_conversions.h" +#include "ui/accelerated_widget_mac/window_resize_helper_mac.h" +#include "ui/base/cocoa/cocoa_base_utils.h" #import "ui/base/cocoa/constrained_window/constrained_window_animation.h" +#import "ui/base/cocoa/window_size_constants.h" #include "ui/base/hit_test.h" #include "ui/base/layout.h" #include "ui/base/ui_base_switches.h" @@ -23,25 +27,33 @@ #include "ui/gfx/geometry/dip_util.h" #import "ui/gfx/mac/coordinate_conversion.h" #import "ui/gfx/mac/nswindow_frame_controls.h" -#import "ui/native_theme/native_theme_mac.h" -#import "ui/views/cocoa/bridged_content_view.h" -#import "ui/views/cocoa/bridged_native_widget_host.h" -#import "ui/views/cocoa/cocoa_mouse_capture.h" -#import "ui/views/cocoa/cocoa_window_move_loop.h" -#import "ui/views/cocoa/drag_drop_client_mac.h" -#import "ui/views/cocoa/native_widget_mac_nswindow.h" -#include "ui/views/cocoa/tooltip_manager_mac.h" -#import "ui/views/cocoa/views_nswindow_delegate.h" -#import "ui/views/cocoa/widget_owner_nswindow_adapter.h" -#include "ui/views/widget/native_widget_mac.h" -#include "ui/views/widget/widget.h" - +#import "ui/views_bridge_mac/bridged_content_view.h" +#import "ui/views_bridge_mac/bridged_native_widget_host_helper.h" +#import "ui/views_bridge_mac/cocoa_mouse_capture.h" +#import "ui/views_bridge_mac/cocoa_window_move_loop.h" +#include "ui/views_bridge_mac/mojo/bridged_native_widget_host.mojom.h" +#import "ui/views_bridge_mac/native_widget_mac_nswindow.h" +#import "ui/views_bridge_mac/views_nswindow_delegate.h" + +using views_bridge_mac::mojom::VisibilityTransition; using views_bridge_mac::mojom::WindowVisibilityState; namespace { constexpr auto kUIPaintTimeout = base::TimeDelta::FromSeconds(5); } // namespace +// The NSView that hosts the composited CALayer drawing the UI. It fills the +// window but is not hittable so that accessibility hit tests always go to the +// BridgedContentView. +@interface ViewsCompositorSuperview : NSView +@end + +@implementation ViewsCompositorSuperview +- (NSView*)hitTest:(NSPoint)aPoint { + return nil; +} +@end + // Self-owning animation delegate that starts a hide animation, then calls // -[NSWindow close] when the animation ends, releasing itself. @interface ViewsNSWindowCloseAnimator : NSObject<NSAnimationDelegate> { @@ -77,18 +89,6 @@ constexpr auto kUIPaintTimeout = base::TimeDelta::FromSeconds(5); } @end -// The NSView that hosts the composited CALayer drawing the UI. It fills the -// window but is not hittable so that accessibility hit tests always go to the -// BridgedContentView. -@interface ViewsCompositorSuperview : NSView -@end - -@implementation ViewsCompositorSuperview -- (NSView*)hitTest:(NSPoint)aPoint { - return nil; -} -@end - // This class overrides NSAnimation methods to invalidate the shadow for each // frame. It is required because the show animation uses CGSSetWindowWarp() // which is touchy about the consistency of the points it is given. The show @@ -106,10 +106,10 @@ constexpr auto kUIPaintTimeout = base::TimeDelta::FromSeconds(5); @implementation ModalShowAnimationWithLayer { // This is the "real" delegate, but this class acts as the NSAnimationDelegate // to avoid a separate object. - views::BridgedNativeWidget* bridgedNativeWidget_; + views::BridgedNativeWidgetImpl* bridgedNativeWidget_; } - (instancetype)initWithBridgedNativeWidget: - (views::BridgedNativeWidget*)widget { + (views::BridgedNativeWidgetImpl*)widget { if ((self = [super initWithWindow:widget->ns_window()])) { bridgedNativeWidget_ = widget; [self setDelegate:self]; @@ -136,6 +136,8 @@ constexpr auto kUIPaintTimeout = base::TimeDelta::FromSeconds(5); } @end +namespace views { + namespace { using RankMap = std::map<NSView*, int>; @@ -150,8 +152,6 @@ using NSViewComparatorValue = id; using NSViewComparatorValue = __kindof NSView*; #endif -int kWindowPropertiesKey; - // Returns true if the content_view is reparented. bool PositionWindowInNativeViewParent(NSView* content_view) { return [[content_view window] contentView] != content_view; @@ -177,21 +177,14 @@ gfx::Size GetClientSizeForWindowSize(NSWindow* window, return gfx::Size([window contentRectForFrameRect:frame_rect].size); } -void RankNSViews(views::View* view, - const views::BridgedNativeWidget::AssociatedViews& hosts, - RankMap* rank) { - auto it = hosts.find(view); - if (it != hosts.end()) - rank->emplace(it->second, rank->size()); - for (int i = 0; i < view->child_count(); ++i) - RankNSViews(view->child_at(i), hosts, rank); -} - NSComparisonResult SubviewSorter(NSViewComparatorValue lhs, NSViewComparatorValue rhs, void* rank_as_void) { DCHECK_NE(lhs, rhs); + if ([lhs isKindOfClass:[ViewsCompositorSuperview class]]) + return NSOrderedAscending; + const RankMap* rank = static_cast<const RankMap*>(rank_as_void); auto left_rank = rank->find(lhs); auto right_rank = rank->find(rhs); @@ -211,7 +204,7 @@ NSComparisonResult SubviewSorter(NSViewComparatorValue lhs, return NSOrderedSame; } -// Counts windows managed by a BridgedNativeWidget instance in the +// Counts windows managed by a BridgedNativeWidgetImpl instance in the // |child_windows| array ignoring the windows added by AppKit. NSUInteger CountBridgedWindows(NSArray* child_windows) { NSUInteger count = 0; @@ -222,12 +215,16 @@ NSUInteger CountBridgedWindows(NSArray* child_windows) { return count; } -} // namespace +std::map<uint64_t, BridgedNativeWidgetImpl*>& GetIdToWidgetImplMap() { + static base::NoDestructor<std::map<uint64_t, BridgedNativeWidgetImpl*>> + id_map; + return *id_map; +} -namespace views { +} // namespace // static -gfx::Size BridgedNativeWidget::GetWindowSizeForClientSize( +gfx::Size BridgedNativeWidgetImpl::GetWindowSizeForClientSize( NSWindow* window, const gfx::Size& content_size) { NSRect content_rect = @@ -236,59 +233,101 @@ gfx::Size BridgedNativeWidget::GetWindowSizeForClientSize( return gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect)); } -BridgedNativeWidget::BridgedNativeWidget(BridgedNativeWidgetHost* host, - NativeWidgetMac* parent) - : host_(host), native_widget_mac_(parent) { - DCHECK(parent); - window_delegate_.reset( - [[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]); - ui::CATransactionCoordinator::Get().AddPreCommitObserver(this); +// static +BridgedNativeWidgetImpl* BridgedNativeWidgetImpl::GetFromNativeWindow( + gfx::NativeWindow window) { + if (NativeWidgetMacNSWindow* widget_window = + base::mac::ObjCCast<NativeWidgetMacNSWindow>(window)) { + return GetFromId([widget_window bridgedNativeWidgetId]); + } + return nullptr; // Not created by NativeWidgetMac. +} + +// static +BridgedNativeWidgetImpl* BridgedNativeWidgetImpl::GetFromId( + uint64_t bridged_native_widget_id) { + auto found = GetIdToWidgetImplMap().find(bridged_native_widget_id); + if (found == GetIdToWidgetImplMap().end()) + return nullptr; + return found->second; } -BridgedNativeWidget::~BridgedNativeWidget() { +// static +base::scoped_nsobject<NativeWidgetMacNSWindow> +BridgedNativeWidgetImpl::CreateNSWindow( + views_bridge_mac::mojom::CreateWindowParams* params) { + base::scoped_nsobject<NativeWidgetMacNSWindow> result( + [[NativeWidgetMacNSWindow alloc] + initWithContentRect:ui::kWindowSizeDeterminedLater + styleMask:params->style_mask + backing:NSBackingStoreBuffered + defer:NO]); + return result; +} + +BridgedNativeWidgetImpl::BridgedNativeWidgetImpl( + uint64_t bridged_native_widget_id, + BridgedNativeWidgetHost* host, + BridgedNativeWidgetHostHelper* host_helper) + : id_(bridged_native_widget_id), + host_(host), + host_helper_(host_helper), + bridge_mojo_binding_(this) { + DCHECK(GetIdToWidgetImplMap().find(id_) == GetIdToWidgetImplMap().end()); + GetIdToWidgetImplMap().insert(std::make_pair(id_, this)); +} + +BridgedNativeWidgetImpl::~BridgedNativeWidgetImpl() { // The delegate should be cleared already. Note this enforces the precondition // that -[NSWindow close] is invoked on the hosted window before the // destructor is called. DCHECK(![window_ delegate]); - - ui::CATransactionCoordinator::Get().RemovePreCommitObserver(this); - RemoveOrDestroyChildren(); DCHECK(child_windows_.empty()); DestroyContentView(); } -void BridgedNativeWidget::SetWindow( +void BridgedNativeWidgetImpl::BindRequest( + views_bridge_mac::mojom::BridgedNativeWidgetAssociatedRequest request, + base::OnceClosure connection_closed_callback) { + bridge_mojo_binding_.Bind(std::move(request), + ui::WindowResizeHelperMac::Get()->task_runner()); + bridge_mojo_binding_.set_connection_error_handler( + std::move(connection_closed_callback)); +} + +void BridgedNativeWidgetImpl::SetWindow( base::scoped_nsobject<NativeWidgetMacNSWindow> window) { DCHECK(!window_); + window_delegate_.reset( + [[ViewsNSWindowDelegate alloc] initWithBridgedNativeWidget:this]); window_ = std::move(window); + [window_ setBridgedNativeWidgetId:id_]; [window_ setReleasedWhenClosed:NO]; // Owned by scoped_nsobject. [window_ setDelegate:window_delegate_]; + ui::CATransactionCoordinator::Get().AddPreCommitObserver(this); } -void BridgedNativeWidget::SetParent(NSView* new_parent) { - BridgedNativeWidget* bridged_native_widget_parent = - NativeWidgetMac::GetBridgeForNativeWindow([new_parent window]); +void BridgedNativeWidgetImpl::SetParent(uint64_t new_parent_id) { // Remove from the old parent. if (parent_) { parent_->RemoveChildWindow(this); parent_ = nullptr; } - - if (!new_parent) + if (!new_parent_id) return; - // Disallow creating child windows of views not currently in an NSWindow. - CHECK([new_parent window]); + // It is only valid to have a NativeWidgetMac be the parent of another + // NativeWidgetMac. + BridgedNativeWidgetImpl* new_parent = + BridgedNativeWidgetImpl::GetFromId(new_parent_id); + DCHECK(new_parent); - // If the parent is another BridgedNativeWidget, just add to the collection - // of child windows it owns and manages. Otherwise, create an adapter to - // anchor the child widget and observe when the parent NSWindow is closed. - if (bridged_native_widget_parent) { - parent_ = bridged_native_widget_parent; - bridged_native_widget_parent->child_windows_.push_back(this); - } else { - parent_ = new WidgetOwnerNSWindowAdapter(this, new_parent); - } + // If the parent is another BridgedNativeWidgetImpl, just add to the + // collection of child windows it owns and manages. Otherwise, create an + // adapter to anchor the child widget and observe when the parent NSWindow is + // closed. + parent_ = new_parent; + parent_->child_windows_.push_back(this); // Widget::ShowInactive() could result in a Space switch when the widget has a // parent, and we're calling -orderWindow:relativeTo:. Use Transient @@ -296,12 +335,24 @@ void BridgedNativeWidget::SetParent(NSView* new_parent) { // https://crbug.com/697829 [window_ setCollectionBehavior:[window_ collectionBehavior] | NSWindowCollectionBehaviorTransient]; + + // If |window_| was already visible then add it as a child window immediately. + // As in OnVisibilityChanged, do not set a parent for sheets. + if (window_visible_ && ![window_ isSheet]) + [parent_->ns_window() addChildWindow:window_ ordered:NSWindowAbove]; +} + +void BridgedNativeWidgetImpl::CreateWindow( + views_bridge_mac::mojom::CreateWindowParamsPtr params) { + SetWindow(CreateNSWindow(params.get())); } -void BridgedNativeWidget::InitWindow( +void BridgedNativeWidgetImpl::InitWindow( views_bridge_mac::mojom::BridgedNativeWidgetInitParamsPtr params) { modal_type_ = params->modal_type; is_translucent_window_ = params->is_translucent; + widget_is_top_level_ = params->widget_is_top_level; + position_window_in_screen_coords_ = params->position_window_in_screen_coords; // Register for application hide notifications so that visibility can be // properly tracked. This is not done in the delegate so that the lifetime is @@ -335,13 +386,11 @@ void BridgedNativeWidget::InitWindow( } [window_ setHasShadow:params->has_window_server_shadow]; - tooltip_manager_.reset(new TooltipManagerMac(this)); } -void BridgedNativeWidget::SetInitialBounds( +void BridgedNativeWidgetImpl::SetInitialBounds( const gfx::Rect& new_bounds, - const gfx::Size& minimum_content_size, - const gfx::Vector2d& bounds_offset_for_parent) { + const gfx::Size& minimum_content_size) { gfx::Rect adjusted_bounds = new_bounds; if (new_bounds.IsEmpty()) { // If a position is set, but no size, complain. Otherwise, a 1x1 window @@ -358,12 +407,11 @@ void BridgedNativeWidget::SetInitialBounds( adjusted_bounds = gfx::Rect( gfx::Point(), gfx::Size(NSWidth(frame_rect), NSHeight(frame_rect))); } - adjusted_bounds.Offset(bounds_offset_for_parent); SetBounds(adjusted_bounds, minimum_content_size); } -void BridgedNativeWidget::SetBounds(const gfx::Rect& new_bounds, - const gfx::Size& minimum_content_size) { +void BridgedNativeWidgetImpl::SetBounds(const gfx::Rect& new_bounds, + const gfx::Size& minimum_content_size) { // -[NSWindow contentMinSize] is only checked by Cocoa for user-initiated // resizes. This is not what toolkit-views expects, so clamp. Note there is // no check for maximum size (consistent with aura::Window::SetBounds()). @@ -387,6 +435,9 @@ void BridgedNativeWidget::SetBounds(const gfx::Rect& new_bounds, new_bounds.origin(), GetWindowSizeForClientSize(window_, clamped_content_size)); + if (parent_ && !position_window_in_screen_coords_) + actual_new_bounds.Offset(parent_->GetChildWindowOffset()); + if (PositionWindowInNativeViewParent(bridged_view_)) actual_new_bounds.Offset(GetNativeViewParentOffset(bridged_view_)); @@ -395,39 +446,60 @@ void BridgedNativeWidget::SetBounds(const gfx::Rect& new_bounds, animate:NO]; } -void BridgedNativeWidget::DestroyContentView() { +void BridgedNativeWidgetImpl::SetSizeAndCenter( + const gfx::Size& content_size, + const gfx::Size& minimum_content_size) { + gfx::Rect new_window_bounds = gfx::ScreenRectFromNSRect([window_ frame]); + new_window_bounds.set_size(GetWindowSizeForClientSize(window_, content_size)); + SetBounds(new_window_bounds, minimum_content_size); + + // Note that this is not the precise center of screen, but it is the standard + // location for windows like dialogs to appear on screen for Mac. + // TODO(tapted): If there is a parent window, center in that instead. + [window_ center]; +} + +void BridgedNativeWidgetImpl::DestroyContentView() { if (!bridged_view_) return; - drag_drop_client_.reset(); [bridged_view_ clearView]; + bridged_view_id_mapping_.reset(); bridged_view_.reset(); [window_ setContentView:nil]; } -void BridgedNativeWidget::CreateContentView(const gfx::Rect& bounds) { +void BridgedNativeWidgetImpl::CreateContentView(uint64_t ns_view_id, + const gfx::Rect& bounds) { DCHECK(!bridged_view_); - // The compositor needs to point at the new content view created here. - DCHECK(!compositor_superview_); bridged_view_.reset( [[BridgedContentView alloc] initWithBridge:this bounds:bounds]); + bridged_view_id_mapping_ = std::make_unique<ui::ScopedNSViewIdMapping>( + ns_view_id, bridged_view_.get()); // Objective C initializers can return nil. However, if |view| is non-NULL // this should be treated as an error and caught early. CHECK(bridged_view_); - // Layer backing the content view improves resize performance, reduces memory - // use (no backing store), and clips sublayers to rounded window corners. - [bridged_view_ setWantsLayer:YES]; + // Beware: This view was briefly removed (in favor of a bare CALayer) in + // crrev/c/1236675. The ordering of unassociated layers relative to NSView + // layers is undefined on macOS 10.12 and earlier, so the compositor layer + // ended up covering up subviews (see crbug/899499). + base::scoped_nsobject<NSView> compositor_view( + [[ViewsCompositorSuperview alloc] initWithFrame:[bridged_view_ bounds]]); + [compositor_view + setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; + auto* background_layer = [CALayer layer]; + display_ca_layer_tree_ = + std::make_unique<ui::DisplayCALayerTree>(background_layer); + [compositor_view setLayer:background_layer]; + [compositor_view setWantsLayer:YES]; + [bridged_view_ addSubview:compositor_view]; [window_ setContentView:bridged_view_]; } -void BridgedNativeWidget::CreateDragDropClient(views::View* view) { - drag_drop_client_.reset(new DragDropClientMac(this, view)); -} - -void BridgedNativeWidget::CloseWindow() { +void BridgedNativeWidgetImpl::CloseWindow() { // Keep |window| on the stack so that the ObjectiveC block below can capture // it and properly increment the reference count bound to the posted task. NSWindow* window = ns_window(); @@ -447,7 +519,7 @@ void BridgedNativeWidget::CloseWindow() { } // For other modal types, animate the close. - if (ShouldRunCustomAnimationFor(Widget::ANIMATE_HIDE)) { + if (ShouldRunCustomAnimationFor(VisibilityTransition::kHide)) { [ViewsNSWindowCloseAnimator closeWindowWithAnimation:window]; return; } @@ -472,7 +544,7 @@ void BridgedNativeWidget::CloseWindow() { }))); } -void BridgedNativeWidget::CloseWindowNow() { +void BridgedNativeWidgetImpl::CloseWindowNow() { // NSWindows must be retained until -[NSWindow close] returns. auto window_retain = window_; @@ -482,7 +554,8 @@ void BridgedNativeWidget::CloseWindowNow() { // Note: |this| will be deleted here. } -void BridgedNativeWidget::SetVisibilityState(WindowVisibilityState new_state) { +void BridgedNativeWidgetImpl::SetVisibilityState( + WindowVisibilityState new_state) { // Ensure that: // - A window with an invisible parent is not made visible. // - A parent changing visibility updates child window visibility. @@ -519,8 +592,10 @@ void BridgedNativeWidget::SetVisibilityState(WindowVisibilityState new_state) { // If the parent (or an ancestor) is hidden, return and wait for it to become // visible. - if (parent() && !parent()->IsVisibleParent()) - return; + for (auto* ancestor = parent_; ancestor; ancestor = ancestor->parent_) { + if (!ancestor->window_visible_) + return; + } if (IsWindowModalSheet()) { ShowAsModalSheet(); @@ -549,17 +624,16 @@ void BridgedNativeWidget::SetVisibilityState(WindowVisibilityState new_state) { if (window_visible_) return; // Avoid a Spaces transition. - parent_window_number = [parent_->GetNSWindow() windowNumber]; + parent_window_number = [parent_->ns_window() windowNumber]; } - [window_ orderWindow:NSWindowAbove - relativeTo:parent_window_number]; + [window_ orderWindow:NSWindowAbove relativeTo:parent_window_number]; } DCHECK(window_visible_); // For non-sheet modal types, use the constrained window animations to make // the window appear. - if (ShouldRunCustomAnimationFor(Widget::ANIMATE_SHOW)) { + if (ShouldRunCustomAnimationFor(VisibilityTransition::kShow)) { show_animation_.reset( [[ModalShowAnimationWithLayer alloc] initWithBridgedNativeWidget:this]); @@ -575,7 +649,14 @@ void BridgedNativeWidget::SetVisibilityState(WindowVisibilityState new_state) { } } -void BridgedNativeWidget::AcquireCapture() { +void BridgedNativeWidgetImpl::SetTransitionsToAnimate( + VisibilityTransition transitions) { + // TODO(tapted): Use scoping to disable native animations at appropriate + // times as well. + transitions_to_animate_ = transitions; +} + +void BridgedNativeWidgetImpl::AcquireCapture() { if (HasCapture()) return; if (!window_visible_) @@ -593,18 +674,18 @@ void BridgedNativeWidget::AcquireCapture() { [window_ cursorUpdate:[NSApp currentEvent]]; } -void BridgedNativeWidget::ReleaseCapture() { +void BridgedNativeWidgetImpl::ReleaseCapture() { mouse_capture_.reset(); } -bool BridgedNativeWidget::HasCapture() { +bool BridgedNativeWidgetImpl::HasCapture() { return mouse_capture_ && mouse_capture_->IsActive(); } -Widget::MoveLoopResult BridgedNativeWidget::RunMoveLoop( - const gfx::Vector2d& drag_offset) { +bool BridgedNativeWidgetImpl::RunMoveLoop(const gfx::Vector2d& drag_offset) { DCHECK(!HasCapture()); - DCHECK(!window_move_loop_); + // https://crbug.com/876493 + CHECK(!window_move_loop_); // RunMoveLoop caller is responsible for updating the window to be under the // mouse, but it does this using possibly outdated coordinate from the mouse @@ -628,36 +709,21 @@ Widget::MoveLoopResult BridgedNativeWidget::RunMoveLoop( // function returns. But don't DCHECK since |this| might not be valid. } -void BridgedNativeWidget::EndMoveLoop() { +void BridgedNativeWidgetImpl::EndMoveLoop() { DCHECK(window_move_loop_); window_move_loop_->End(); window_move_loop_.reset(); } -void BridgedNativeWidget::SetNativeWindowProperty(const char* name, - void* value) { - NSString* key = [NSString stringWithUTF8String:name]; - if (value) { - [GetWindowProperties() setObject:[NSValue valueWithPointer:value] - forKey:key]; - } else { - [GetWindowProperties() removeObjectForKey:key]; - } -} - -void* BridgedNativeWidget::GetNativeWindowProperty(const char* name) const { - NSString* key = [NSString stringWithUTF8String:name]; - return [[GetWindowProperties() objectForKey:key] pointerValue]; -} - -void BridgedNativeWidget::SetCursor(NSCursor* cursor) { +void BridgedNativeWidgetImpl::SetCursor(NSCursor* cursor) { [window_delegate_ setCursor:cursor]; } -void BridgedNativeWidget::OnWindowWillClose() { +void BridgedNativeWidgetImpl::OnWindowWillClose() { + ui::CATransactionCoordinator::Get().RemovePreCommitObserver(this); host_->OnWindowWillClose(); - // Ensure BridgedNativeWidget does not have capture, otherwise + // Ensure BridgedNativeWidgetImpl does not have capture, otherwise // OnMouseCaptureLost() may reference a deleted |host_| when called via // ~CocoaMouseCapture() upon the destruction of |mouse_capture_|. See // https://crbug.com/622201. Also we do this before setting the delegate to @@ -675,11 +741,19 @@ void BridgedNativeWidget::OnWindowWillClose() { DCHECK(!show_animation_); [window_ setDelegate:nil]; + + // Ensure that |this| cannot be reached by its id while it is being destroyed. + size_t erased = GetIdToWidgetImplMap().erase(id_); + DCHECK_EQ(1u, erased); + + RemoveOrDestroyChildren(); + DCHECK(child_windows_.empty()); + host_->OnWindowHasClosed(); // Note: |this| and its host will be deleted here. } -void BridgedNativeWidget::OnFullscreenTransitionStart( +void BridgedNativeWidgetImpl::OnFullscreenTransitionStart( bool target_fullscreen_state) { // Note: This can fail for fullscreen changes started externally, but a user // shouldn't be able to do that if the window is invisible to begin with. @@ -692,7 +766,7 @@ void BridgedNativeWidget::OnFullscreenTransitionStart( host_->OnWindowFullscreenTransitionStart(target_fullscreen_state); } -void BridgedNativeWidget::OnFullscreenTransitionComplete( +void BridgedNativeWidgetImpl::OnFullscreenTransitionComplete( bool actual_fullscreen_state) { in_fullscreen_transition_ = false; @@ -709,7 +783,7 @@ void BridgedNativeWidget::OnFullscreenTransitionComplete( ToggleDesiredFullscreenState(true /* async */); } -void BridgedNativeWidget::ToggleDesiredFullscreenState(bool async) { +void BridgedNativeWidgetImpl::ToggleDesiredFullscreenState(bool async) { // If there is currently an animation into or out of fullscreen, then AppKit // emits the string "not in fullscreen state" to stdio and does nothing. For // this case, schedule a transition back into the desired state when the @@ -758,15 +832,15 @@ void BridgedNativeWidget::ToggleDesiredFullscreenState(bool async) { } } -void BridgedNativeWidget::OnSizeChanged() { +void BridgedNativeWidgetImpl::OnSizeChanged() { UpdateWindowGeometry(); } -void BridgedNativeWidget::OnPositionChanged() { +void BridgedNativeWidgetImpl::OnPositionChanged() { UpdateWindowGeometry(); } -void BridgedNativeWidget::OnVisibilityChanged() { +void BridgedNativeWidgetImpl::OnVisibilityChanged() { const bool window_visible = [window_ isVisible]; if (window_visible_ == window_visible) return; @@ -783,7 +857,7 @@ void BridgedNativeWidget::OnVisibilityChanged() { // Sheets don't need a parentWindow set, and setting one causes graphical // glitches (http://crbug.com/605098). if (parent_ && ![window_ isSheet]) - [parent_->GetNSWindow() addChildWindow:window_ ordered:NSWindowAbove]; + [parent_->ns_window() addChildWindow:window_ ordered:NSWindowAbove]; } else { ReleaseCapture(); // Capture on hidden windows is not permitted. @@ -791,7 +865,7 @@ void BridgedNativeWidget::OnVisibilityChanged() { // list. Cocoa's childWindow management breaks down when child windows are // hidden. if (parent_) - [parent_->GetNSWindow() removeChildWindow:window_]; + [parent_->ns_window() removeChildWindow:window_]; } // Showing a translucent window after hiding it should trigger shadow @@ -809,24 +883,24 @@ void BridgedNativeWidget::OnVisibilityChanged() { [window_ setAutodisplay:window_visible_]; } -void BridgedNativeWidget::OnSystemControlTintChanged() { - ui::NativeTheme::GetInstanceForNativeUi()->NotifyObservers(); +void BridgedNativeWidgetImpl::OnSystemControlTintChanged() { + host_->OnWindowNativeThemeChanged(); } -void BridgedNativeWidget::OnBackingPropertiesChanged() { +void BridgedNativeWidgetImpl::OnBackingPropertiesChanged() { UpdateWindowDisplay(); } -void BridgedNativeWidget::OnWindowKeyStatusChangedTo(bool is_key) { +void BridgedNativeWidgetImpl::OnWindowKeyStatusChangedTo(bool is_key) { host_->OnWindowKeyStatusChanged( is_key, [window_ contentView] == [window_ firstResponder], [NSApp isFullKeyboardAccessEnabled]); } -void BridgedNativeWidget::SetSizeConstraints(const gfx::Size& min_size, - const gfx::Size& max_size, - bool is_resizable, - bool is_maximizable) { +void BridgedNativeWidgetImpl::SetSizeConstraints(const gfx::Size& min_size, + const gfx::Size& max_size, + bool is_resizable, + bool is_maximizable) { // Don't modify the size constraints or fullscreen collection behavior while // in fullscreen or during a transition. OnFullscreenTransitionComplete will // reset these after leaving fullscreen. @@ -842,13 +916,11 @@ void BridgedNativeWidget::SetSizeConstraints(const gfx::Size& min_size, shows_fullscreen_controls); } -void BridgedNativeWidget::OnShowAnimationComplete() { +void BridgedNativeWidgetImpl::OnShowAnimationComplete() { show_animation_.reset(); } -void BridgedNativeWidget::InitCompositorView() { - AddCompositorSuperview(); - +void BridgedNativeWidgetImpl::InitCompositorView() { // Use the regular window background for window modal sheets. The layer will // still paint over most of it, but the native -[NSApp beginSheet:] animation // blocks the UI thread, so there's no way to invalidate the shadow to match @@ -875,84 +947,29 @@ void BridgedNativeWidget::InitCompositorView() { UpdateWindowGeometry(); } -void BridgedNativeWidget::SetAssociationForView(const views::View* view, - NSView* native_view) { - DCHECK_EQ(0u, associated_views_.count(view)); - associated_views_[view] = native_view; - native_widget_mac_->GetWidget()->ReorderNativeViews(); -} - -void BridgedNativeWidget::ClearAssociationForView(const views::View* view) { - auto it = associated_views_.find(view); - DCHECK(it != associated_views_.end()); - associated_views_.erase(it); -} - -void BridgedNativeWidget::ReorderChildViews() { +void BridgedNativeWidgetImpl::SortSubviews(RankMap rank) { // Ignore layer manipulation during a Close(). This can be reached during the // orderOut: in Close(), which notifies visibility changes to Views. if (!bridged_view_) return; - - RankMap rank; - Widget* widget = native_widget_mac_->GetWidget(); - RankNSViews(widget->GetRootView(), associated_views_, &rank); - // Unassociated NSViews should be ordered above associated ones. The exception - // is the UI compositor's superview, which should always be on the very - // bottom, so give it an explicit negative rank. - if (compositor_superview_) - rank[compositor_superview_] = -1; [bridged_view_ sortSubviewsUsingFunction:&SubviewSorter context:&rank]; } -void BridgedNativeWidget::ReparentNativeView(NSView* native_view, - NSView* new_parent) { - DCHECK([new_parent window]); - DCHECK([native_view isDescendantOf:bridged_view_]); - DCHECK(window_ && ![window_ isSheet]); - - BridgedNativeWidget* parent_bridge = - NativeWidgetMac::GetBridgeForNativeWindow([new_parent window]); - if (native_view == bridged_view_.get() && parent_bridge != parent_) { - SetParent(new_parent); - - // TODO(ccameron): This is likely not correct, as the window for |this| - // should only be added as a child window if it is visible. - if (!window_visible_) - NOTIMPLEMENTED(); - [[new_parent window] addChildWindow:window_ ordered:NSWindowAbove]; - } - - if (!native_widget_mac_->GetWidget()->is_top_level() || - native_view != bridged_view_.get()) { - // Make native_view be a child of new_parent by adding it as a subview. - // The window_ must remain visible because it controls the bounds and - // visibility of the ui::Layer. So just hide it by setting alpha value to - // zero. - [new_parent addSubview:native_view]; - if (native_view == bridged_view_.get()) { - [window_ setAlphaValue:0]; - [window_ setIgnoresMouseEvents:YES]; - } - } -} - -void BridgedNativeWidget::SetAnimationEnabled(bool animate) { +void BridgedNativeWidgetImpl::SetAnimationEnabled(bool animate) { [window_ setAnimationBehavior:(animate ? NSWindowAnimationBehaviorDocumentWindow : NSWindowAnimationBehaviorNone)]; } -bool BridgedNativeWidget::ShouldRunCustomAnimationFor( - Widget::VisibilityTransition transition) const { +bool BridgedNativeWidgetImpl::ShouldRunCustomAnimationFor( + VisibilityTransition transition) const { // The logic around this needs to change if new transition types are set. // E.g. it would be nice to distinguish "hide" from "close". Mac currently // treats "hide" only as "close". Hide (e.g. Cmd+h) should not animate on Mac. - constexpr int kSupported = - Widget::ANIMATE_SHOW | Widget::ANIMATE_HIDE | Widget::ANIMATE_NONE; - DCHECK_EQ(0, transitions_to_animate_ & ~kSupported); - if (!(transitions_to_animate_ & transition)) + if (transitions_to_animate_ != transition && + transitions_to_animate_ != VisibilityTransition::kBoth) { return false; + } // Custom animations are only used for tab-modals. bool widget_is_modal = false; @@ -974,66 +991,70 @@ bool BridgedNativeWidget::ShouldRunCustomAnimationFor( return true; } -NSWindow* BridgedNativeWidget::ns_window() { +NSWindow* BridgedNativeWidgetImpl::ns_window() { return window_.get(); } +views_bridge_mac::DragDropClient* BridgedNativeWidgetImpl::drag_drop_client() { + return host_helper_->GetDragDropClient(); +} + //////////////////////////////////////////////////////////////////////////////// -// BridgedNativeWidget, ui::CATransactionObserver +// BridgedNativeWidgetImpl, ui::CATransactionObserver -void BridgedNativeWidget::OnDisplayMetricsChanged( +void BridgedNativeWidgetImpl::OnDisplayMetricsChanged( const display::Display& display, uint32_t metrics) { UpdateWindowDisplay(); } //////////////////////////////////////////////////////////////////////////////// -// BridgedNativeWidget, ui::CATransactionObserver +// BridgedNativeWidgetImpl, ui::CATransactionObserver -bool BridgedNativeWidget::ShouldWaitInPreCommit() { +bool BridgedNativeWidgetImpl::ShouldWaitInPreCommit() { if (!window_visible_) return false; if (ca_transaction_sync_suppressed_) return false; - if (!compositor_superview_) + if (!bridged_view_) return false; return content_dip_size_ != compositor_frame_dip_size_; } -base::TimeDelta BridgedNativeWidget::PreCommitTimeout() { +base::TimeDelta BridgedNativeWidgetImpl::PreCommitTimeout() { return kUIPaintTimeout; } //////////////////////////////////////////////////////////////////////////////// -// BridgedNativeWidget, CocoaMouseCaptureDelegate: +// BridgedNativeWidgetImpl, CocoaMouseCaptureDelegate: -void BridgedNativeWidget::PostCapturedEvent(NSEvent* event) { +void BridgedNativeWidgetImpl::PostCapturedEvent(NSEvent* event) { [bridged_view_ processCapturedMouseEvent:event]; } -void BridgedNativeWidget::OnMouseCaptureLost() { +void BridgedNativeWidgetImpl::OnMouseCaptureLost() { host_->OnMouseCaptureActiveChanged(false); } -NSWindow* BridgedNativeWidget::GetWindow() const { +NSWindow* BridgedNativeWidgetImpl::GetWindow() const { return window_; } //////////////////////////////////////////////////////////////////////////////// // TODO(ccameron): Update class names to: -// BridgedNativeWidgetImpl, BridgedNativeWidget: +// BridgedNativeWidgetImpl, BridgedNativeWidgetImpl: -void BridgedNativeWidget::SetVisibleOnAllSpaces(bool always_visible) { +void BridgedNativeWidgetImpl::SetVisibleOnAllSpaces(bool always_visible) { gfx::SetNSWindowVisibleOnAllWorkspaces(window_, always_visible); } -void BridgedNativeWidget::SetFullscreen(bool fullscreen) { +void BridgedNativeWidgetImpl::SetFullscreen(bool fullscreen) { if (fullscreen == target_fullscreen_state_) return; ToggleDesiredFullscreenState(); } -void BridgedNativeWidget::SetMiniaturized(bool miniaturized) { +void BridgedNativeWidgetImpl::SetMiniaturized(bool miniaturized) { if (miniaturized) { // Calling performMiniaturize: will momentarily highlight the button, but // AppKit will reject it if there is no miniaturize button. @@ -1046,17 +1067,17 @@ void BridgedNativeWidget::SetMiniaturized(bool miniaturized) { } } -void BridgedNativeWidget::SetOpacity(float opacity) { +void BridgedNativeWidgetImpl::SetOpacity(float opacity) { [window_ setAlphaValue:opacity]; } -void BridgedNativeWidget::SetContentAspectRatio( +void BridgedNativeWidgetImpl::SetContentAspectRatio( const gfx::SizeF& aspect_ratio) { [window_ setContentAspectRatio:NSMakeSize(aspect_ratio.width(), aspect_ratio.height())]; } -void BridgedNativeWidget::SetCALayerParams( +void BridgedNativeWidgetImpl::SetCALayerParams( const gfx::CALayerParams& ca_layer_params) { // Ignore frames arriving "late" for an old size. A frame at the new size // should arrive soon. @@ -1079,46 +1100,46 @@ void BridgedNativeWidget::SetCALayerParams( } } -void BridgedNativeWidget::MakeFirstResponder() { +void BridgedNativeWidgetImpl::MakeFirstResponder() { [window_ makeFirstResponder:bridged_view_]; } -void BridgedNativeWidget::SetWindowTitle(const base::string16& title) { +void BridgedNativeWidgetImpl::SetWindowTitle(const base::string16& title) { NSString* new_title = base::SysUTF16ToNSString(title); [window_ setTitle:new_title]; } -void BridgedNativeWidget::ClearTouchBar() { +void BridgedNativeWidgetImpl::ClearTouchBar() { if (@available(macOS 10.12.2, *)) { if ([bridged_view_ respondsToSelector:@selector(setTouchBar:)]) [bridged_view_ setTouchBar:nil]; } } -void BridgedNativeWidget::SetTextInputClient( +void BridgedNativeWidgetImpl::UpdateTooltip() { + NSPoint nspoint = + ui::ConvertPointFromScreenToWindow(window_, [NSEvent mouseLocation]); + // Note: flip in the view's frame, which matches the window's contentRect. + gfx::Point point(nspoint.x, NSHeight([bridged_view_ frame]) - nspoint.y); + [bridged_view_ updateTooltipIfRequiredAt:point]; +} + +void BridgedNativeWidgetImpl::SetTextInputClient( ui::TextInputClient* text_input_client) { [bridged_view_ setTextInputClient:text_input_client]; } //////////////////////////////////////////////////////////////////////////////// -// BridgedNativeWidget, BridgedNativeWidgetOwner: - -NSWindow* BridgedNativeWidget::GetNSWindow() { - return window_; -} +// BridgedNativeWidgetImpl, former BridgedNativeWidgetOwner: -gfx::Vector2d BridgedNativeWidget::GetChildWindowOffset() const { +gfx::Vector2d BridgedNativeWidgetImpl::GetChildWindowOffset() const { return gfx::ScreenRectFromNSRect([window_ frame]).OffsetFromOrigin(); } -bool BridgedNativeWidget::IsVisibleParent() const { - return parent_ ? window_visible_ && parent_->IsVisibleParent() - : window_visible_; -} - -void BridgedNativeWidget::RemoveChildWindow(BridgedNativeWidget* child) { - auto location = std::find( - child_windows_.begin(), child_windows_.end(), child); +void BridgedNativeWidgetImpl::RemoveChildWindow( + BridgedNativeWidgetImpl* child) { + auto location = + std::find(child_windows_.begin(), child_windows_.end(), child); DCHECK(location != child_windows_.end()); child_windows_.erase(location); @@ -1129,9 +1150,9 @@ void BridgedNativeWidget::RemoveChildWindow(BridgedNativeWidget* child) { } //////////////////////////////////////////////////////////////////////////////// -// BridgedNativeWidget, private: +// BridgedNativeWidgetImpl, private: -void BridgedNativeWidget::RemoveOrDestroyChildren() { +void BridgedNativeWidgetImpl::RemoveOrDestroyChildren() { // TODO(tapted): Implement unowned child windows if required. while (!child_windows_.empty()) { // The NSWindow can only be destroyed after -[NSWindow close] is complete. @@ -1143,13 +1164,13 @@ void BridgedNativeWidget::RemoveOrDestroyChildren() { } } -void BridgedNativeWidget::NotifyVisibilityChangeDown() { +void BridgedNativeWidgetImpl::NotifyVisibilityChangeDown() { // Child windows sometimes like to close themselves in response to visibility // changes. That's supported, but only with the asynchronous Widget::Close(). // Perform a heuristic to detect child removal that would break these loops. const size_t child_count = child_windows_.size(); if (!window_visible_) { - for (BridgedNativeWidget* child : child_windows_) { + for (BridgedNativeWidgetImpl* child : child_windows_) { if (child->window_visible_) [child->ns_window() orderOut:nil]; @@ -1160,7 +1181,7 @@ void BridgedNativeWidget::NotifyVisibilityChangeDown() { // in each child. There, children will remove themselves from the NSWindow // childWindow list as well as propagate NotifyVisibilityChangeDown() calls // to any children of their own. However this is only true for windows - // managed by the BridgedNativeWidget i.e. windows which have + // managed by the BridgedNativeWidgetImpl i.e. windows which have // ViewsNSWindowDelegate as the delegate. DCHECK_EQ(0u, CountBridgedWindows([window_ childWindows])); return; @@ -1168,7 +1189,7 @@ void BridgedNativeWidget::NotifyVisibilityChangeDown() { NSUInteger visible_bridged_children = 0; // For a DCHECK below. NSInteger parent_window_number = [window_ windowNumber]; - for (BridgedNativeWidget* child: child_windows_) { + for (BridgedNativeWidgetImpl* child : child_windows_) { // Note: order the child windows on top, regardless of whether or not they // are currently visible. They probably aren't, since the parent was hidden // prior to this, but they could have been made visible in other ways. @@ -1192,36 +1213,7 @@ void BridgedNativeWidget::NotifyVisibilityChangeDown() { CountBridgedWindows([window_ childWindows])); } -void BridgedNativeWidget::AddCompositorSuperview() { - DCHECK(!compositor_superview_); - compositor_superview_.reset( - [[ViewsCompositorSuperview alloc] initWithFrame:[bridged_view_ bounds]]); - - // Size and resize automatically with |bridged_view_|. - [compositor_superview_ - setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; - - // Enable HiDPI backing when supported (only on 10.7+). - if ([compositor_superview_ respondsToSelector: - @selector(setWantsBestResolutionOpenGLSurface:)]) { - [compositor_superview_ setWantsBestResolutionOpenGLSurface:YES]; - } - - // Set the layer first to create a layer-hosting view (not layer-backed), and - // set the compositor output to go to that layer. - base::scoped_nsobject<CALayer> background_layer([[CALayer alloc] init]); - display_ca_layer_tree_ = - std::make_unique<ui::DisplayCALayerTree>(background_layer.get()); - [compositor_superview_ setLayer:background_layer]; - [compositor_superview_ setWantsLayer:YES]; - - // The UI compositor should always be the first subview, to ensure webviews - // are drawn on top of it. - DCHECK_EQ(0u, [[bridged_view_ subviews] count]); - [bridged_view_ addSubview:compositor_superview_]; -} - -void BridgedNativeWidget::UpdateWindowGeometry() { +void BridgedNativeWidgetImpl::UpdateWindowGeometry() { gfx::Rect window_in_screen = gfx::ScreenRectFromNSRect([window_ frame]); gfx::Rect content_in_screen = gfx::ScreenRectFromNSRect( [window_ contentRectForFrameRect:[window_ frame]]); @@ -1239,23 +1231,23 @@ void BridgedNativeWidget::UpdateWindowGeometry() { invalidate_shadow_on_frame_swap_ = true; } -void BridgedNativeWidget::UpdateWindowDisplay() { +void BridgedNativeWidgetImpl::UpdateWindowDisplay() { host_->OnWindowDisplayChanged( display::Screen::GetScreen()->GetDisplayNearestWindow(window_)); } -bool BridgedNativeWidget::IsWindowModalSheet() const { +bool BridgedNativeWidgetImpl::IsWindowModalSheet() const { return parent_ && modal_type_ == ui::MODAL_TYPE_WINDOW; } -void BridgedNativeWidget::ShowAsModalSheet() { +void BridgedNativeWidgetImpl::ShowAsModalSheet() { // -[NSApp beginSheet:] will block the UI thread while the animation runs. // So that it doesn't animate a fully transparent window, first wait for a // frame. The first step is to pretend that the window is already visible. window_visible_ = true; host_->OnVisibilityChanged(window_visible_); - NSWindow* parent_window = parent_->GetNSWindow(); + NSWindow* parent_window = parent_->ns_window(); DCHECK(parent_window); // -beginSheet: does not retain |modalDelegate| (and we would not want it to). @@ -1269,15 +1261,4 @@ void BridgedNativeWidget::ShowAsModalSheet() { contextInfo:nullptr]; } -NSMutableDictionary* BridgedNativeWidget::GetWindowProperties() const { - NSMutableDictionary* properties = objc_getAssociatedObject( - window_, &kWindowPropertiesKey); - if (!properties) { - properties = [NSMutableDictionary dictionary]; - objc_setAssociatedObject(window_, &kWindowPropertiesKey, - properties, OBJC_ASSOCIATION_RETAIN); - } - return properties; -} - } // namespace views diff --git a/chromium/ui/views/cocoa/cocoa_mouse_capture.h b/chromium/ui/views_bridge_mac/cocoa_mouse_capture.h index 9b170e4a7e9..b7dedad9445 100644 --- a/chromium/ui/views/cocoa/cocoa_mouse_capture.h +++ b/chromium/ui/views_bridge_mac/cocoa_mouse_capture.h @@ -2,17 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_VIEWS_COCOA_COCOA_MOUSE_CAPTURE_H_ -#define UI_VIEWS_COCOA_COCOA_MOUSE_CAPTURE_H_ +#ifndef UI_VIEWS_BRIDGE_MAC_COCOA_MOUSE_CAPTURE_H_ +#define UI_VIEWS_BRIDGE_MAC_COCOA_MOUSE_CAPTURE_H_ #include <memory> #include "base/macros.h" -#include "ui/views/views_export.h" +#include "ui/views_bridge_mac/views_bridge_mac_export.h" @class NSWindow; -namespace views { +namespace views_bridge_mac { class CocoaMouseCaptureDelegate; @@ -21,7 +21,7 @@ class CocoaMouseCaptureDelegate; // menu should dismiss the menu and "swallow" the mouse event. All events are // forwarded, but only events to the same application are "swallowed", which is // consistent with how native NSMenus behave. -class VIEWS_EXPORT CocoaMouseCapture { +class VIEWS_BRIDGE_MAC_EXPORT CocoaMouseCapture { public: explicit CocoaMouseCapture(CocoaMouseCaptureDelegate* delegate); ~CocoaMouseCapture(); @@ -48,6 +48,6 @@ class VIEWS_EXPORT CocoaMouseCapture { DISALLOW_COPY_AND_ASSIGN(CocoaMouseCapture); }; -} // namespace views +} // namespace views_bridge_mac -#endif // UI_VIEWS_COCOA_COCOA_MOUSE_CAPTURE_H_ +#endif // UI_VIEWS_BRIDGE_MAC_COCOA_MOUSE_CAPTURE_H_ diff --git a/chromium/ui/views/cocoa/cocoa_mouse_capture.mm b/chromium/ui/views_bridge_mac/cocoa_mouse_capture.mm index b1f3cb216de..21f538b122f 100644 --- a/chromium/ui/views/cocoa/cocoa_mouse_capture.mm +++ b/chromium/ui/views_bridge_mac/cocoa_mouse_capture.mm @@ -2,16 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "ui/views/cocoa/cocoa_mouse_capture.h" +#import "ui/views_bridge_mac/cocoa_mouse_capture.h" #import <Cocoa/Cocoa.h> #include "base/logging.h" #include "base/macros.h" #include "ui/base/cocoa/weak_ptr_nsobject.h" -#import "ui/views/cocoa/cocoa_mouse_capture_delegate.h" +#import "ui/views_bridge_mac/cocoa_mouse_capture_delegate.h" -namespace views { +namespace views_bridge_mac { // The ActiveEventTap is a RAII handle on the resources being used to capture // events. There is either 0 or 1 active instance of this class. If a second @@ -125,4 +125,4 @@ void CocoaMouseCapture::OnOtherClientGotCapture() { active_handle_.reset(); } -} // namespace views +} // namespace views_bridge_mac diff --git a/chromium/ui/views/cocoa/cocoa_mouse_capture_delegate.h b/chromium/ui/views_bridge_mac/cocoa_mouse_capture_delegate.h index 9e6d2a0e4ea..41093fae784 100644 --- a/chromium/ui/views/cocoa/cocoa_mouse_capture_delegate.h +++ b/chromium/ui/views_bridge_mac/cocoa_mouse_capture_delegate.h @@ -2,13 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_VIEWS_COCOA_COCOA_MOUSE_CAPTURE_DELEGATE_H_ -#define UI_VIEWS_COCOA_COCOA_MOUSE_CAPTURE_DELEGATE_H_ +#ifndef UI_VIEWS_BRIDGE_MAC_COCOA_MOUSE_CAPTURE_DELEGATE_H_ +#define UI_VIEWS_BRIDGE_MAC_COCOA_MOUSE_CAPTURE_DELEGATE_H_ @class NSEvent; @class NSWindow; -namespace views { +namespace views_bridge_mac { // Delegate for receiving captured events from a CocoaMouseCapture. class CocoaMouseCaptureDelegate { @@ -26,6 +26,6 @@ class CocoaMouseCaptureDelegate { virtual NSWindow* GetWindow() const = 0; }; -} // namespace views +} // namespace views_bridge_mac -#endif // UI_VIEWS_COCOA_COCOA_MOUSE_CAPTURE_DELEGATE_H_ +#endif // UI_VIEWS_BRIDGE_MAC_COCOA_MOUSE_CAPTURE_DELEGATE_H_ diff --git a/chromium/ui/views/cocoa/cocoa_window_move_loop.h b/chromium/ui/views_bridge_mac/cocoa_window_move_loop.h index e688884facb..ff074d8a8f8 100644 --- a/chromium/ui/views/cocoa/cocoa_window_move_loop.h +++ b/chromium/ui/views_bridge_mac/cocoa_window_move_loop.h @@ -2,27 +2,27 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_VIEWS_COCOA_COCOA_WINDOW_MOVE_LOOP_H_ -#define UI_VIEWS_COCOA_COCOA_WINDOW_MOVE_LOOP_H_ +#ifndef UI_VIEWS_BRIDGE_MAC_COCOA_WINDOW_MOVE_LOOP_H_ +#define UI_VIEWS_BRIDGE_MAC_COCOA_WINDOW_MOVE_LOOP_H_ #import <Cocoa/Cocoa.h> #include "base/callback.h" #include "base/memory/weak_ptr.h" -#include "ui/views/widget/widget.h" namespace views { -class BridgedNativeWidget; +class BridgedNativeWidgetImpl; -// Used by views::BridgedNativeWidget when dragging detached tabs. +// Used by views::BridgedNativeWidgetImpl when dragging detached tabs. class CocoaWindowMoveLoop { public: - CocoaWindowMoveLoop(BridgedNativeWidget* owner, + CocoaWindowMoveLoop(BridgedNativeWidgetImpl* owner, const NSPoint& initial_mouse_in_screen); ~CocoaWindowMoveLoop(); // Initiates the drag until a mouse up event is observed, or End() is called. - Widget::MoveLoopResult Run(); + // Returns true if a mouse up event ended the loop. + bool Run(); void End(); private: @@ -32,7 +32,7 @@ class CocoaWindowMoveLoop { WINDOW_DESTROYED, }; - BridgedNativeWidget* owner_; // Weak. Owns this. + BridgedNativeWidgetImpl* owner_; // Weak. Owns this. // Initial mouse location at the time before the CocoaWindowMoveLoop is // created. @@ -50,4 +50,4 @@ class CocoaWindowMoveLoop { } // namespace views -#endif // UI_VIEWS_COCOA_COCOA_WINDOW_MOVE_LOOP_H_ +#endif // UI_VIEWS_BRIDGE_MAC_COCOA_WINDOW_MOVE_LOOP_H_ diff --git a/chromium/ui/views/cocoa/cocoa_window_move_loop.mm b/chromium/ui/views_bridge_mac/cocoa_window_move_loop.mm index a306f9cfb5c..4b2c64f0941 100644 --- a/chromium/ui/views/cocoa/cocoa_window_move_loop.mm +++ b/chromium/ui/views_bridge_mac/cocoa_window_move_loop.mm @@ -2,12 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "ui/views/cocoa/cocoa_window_move_loop.h" +#include "ui/views_bridge_mac/cocoa_window_move_loop.h" +#include "base/debug/stack_trace.h" #include "base/run_loop.h" +#include "base/strings/stringprintf.h" +#include "components/crash/core/common/crash_key.h" #include "ui/display/screen.h" #import "ui/gfx/mac/coordinate_conversion.h" -#import "ui/views/cocoa/bridged_native_widget.h" +#import "ui/views_bridge_mac/bridged_native_widget_impl.h" // When event monitors process the events the full list of monitors is cached, // and if we unregister the event monitor that's at the end of the list while @@ -39,15 +42,20 @@ namespace views { -CocoaWindowMoveLoop::CocoaWindowMoveLoop( - BridgedNativeWidget* owner, - const NSPoint& initial_mouse_in_screen) +CocoaWindowMoveLoop::CocoaWindowMoveLoop(BridgedNativeWidgetImpl* owner, + const NSPoint& initial_mouse_in_screen) : owner_(owner), initial_mouse_in_screen_(initial_mouse_in_screen), - weak_factory_(this) { -} + weak_factory_(this) {} CocoaWindowMoveLoop::~CocoaWindowMoveLoop() { + // Record the address and stack to help catch https://crbug.com/876493. + static crash_reporter::CrashKeyString<19> address_key("move_loop_address"); + address_key.Set(base::StringPrintf("%p", this)); + + static crash_reporter::CrashKeyString<1024> stack_key("move_loop_stack"); + crash_reporter::SetCrashKeyStringToStackTrace(&stack_key, + base::debug::StackTrace()); // Handle the pathological case, where |this| is destroyed while running. if (exit_reason_ref_) { *exit_reason_ref_ = WINDOW_DESTROYED; @@ -57,7 +65,7 @@ CocoaWindowMoveLoop::~CocoaWindowMoveLoop() { owner_ = nullptr; } -Widget::MoveLoopResult CocoaWindowMoveLoop::Run() { +bool CocoaWindowMoveLoop::Run() { LoopExitReason exit_reason = ENDED_EXTERNALLY; exit_reason_ref_ = &exit_reason; NSWindow* window = owner_->ns_window(); @@ -75,6 +83,10 @@ Widget::MoveLoopResult CocoaWindowMoveLoop::Run() { // TabDragController. NSEventMask mask = NSLeftMouseUpMask | NSLeftMouseDraggedMask; auto handler = ^NSEvent*(NSEvent* event) { + // The docs say this always runs on the main thread, but if it didn't, + // it would explain https://crbug.com/876493, so let's make sure. + CHECK_EQ(CFRunLoopGetMain(), CFRunLoopGetCurrent()); + CocoaWindowMoveLoop* strong = [weak_cocoa_window_move_loop weak].get(); if (!strong || !strong->exit_reason_ref_) { // By this point CocoaWindowMoveLoop was deleted while processing this @@ -111,8 +123,7 @@ Widget::MoveLoopResult CocoaWindowMoveLoop::Run() { owner_->EndMoveLoop(); // Deletes |this|. } - return exit_reason != MOUSE_UP ? Widget::MOVE_LOOP_CANCELED - : Widget::MOVE_LOOP_SUCCESSFUL; + return exit_reason == MOUSE_UP; } void CocoaWindowMoveLoop::End() { diff --git a/chromium/ui/views_bridge_mac/drag_drop_client.h b/chromium/ui/views_bridge_mac/drag_drop_client.h new file mode 100644 index 00000000000..3efa842c4e6 --- /dev/null +++ b/chromium/ui/views_bridge_mac/drag_drop_client.h @@ -0,0 +1,37 @@ +// 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_VIEWS_BRIDGE_MAC_DRAG_DROP_CLIENT_H_ +#define UI_VIEWS_BRIDGE_MAC_DRAG_DROP_CLIENT_H_ + +#import <Cocoa/Cocoa.h> + +#include "ui/views_bridge_mac/views_bridge_mac_export.h" + +namespace views_bridge_mac { + +// Interface between the content view of a BridgedNativeWidgetImpl and a +// DragDropClientMac in the browser process. This interface should eventually +// become mojo-ified, but at the moment only passes raw pointers (consequently, +// drag-drop behavior does not work in RemoteMacViews). +class VIEWS_BRIDGE_MAC_EXPORT DragDropClient { + public: + virtual ~DragDropClient() {} + + // Called when mouse is dragged during a drag and drop. + virtual NSDragOperation DragUpdate(id<NSDraggingInfo>) = 0; + + // Called when mouse is released during a drag and drop. + virtual NSDragOperation Drop(id<NSDraggingInfo> sender) = 0; + + // Called when the drag and drop session has ended. + virtual void EndDrag() = 0; + + // Called when mouse leaves the drop area. + virtual void DragExit() = 0; +}; + +} // namespace views_bridge_mac + +#endif // UI_VIEWS_BRIDGE_MAC_DRAG_DROP_CLIENT_H_ diff --git a/chromium/ui/views_bridge_mac/mojo/bridge_factory.mojom b/chromium/ui/views_bridge_mac/mojo/bridge_factory.mojom new file mode 100644 index 00000000000..246ba5bb289 --- /dev/null +++ b/chromium/ui/views_bridge_mac/mojo/bridge_factory.mojom @@ -0,0 +1,20 @@ +// 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. + +module views_bridge_mac.mojom; + +import "ui/views_bridge_mac/mojo/bridged_native_widget.mojom"; +import "ui/views_bridge_mac/mojo/bridged_native_widget_host.mojom"; + +// The interface through which a bridge is created and connected to its host. +interface BridgeFactory { + // Create a bridge for a native widget. The resulting object will be owned by + // the connection for |host|. Closing that connection will result in deleting + // the bridge. + CreateBridgedNativeWidget( + uint64 bridge_id, + associated BridgedNativeWidget& bridge_request, + associated BridgedNativeWidgetHost host); +}; + diff --git a/chromium/ui/views_bridge_mac/mojo/bridged_native_widget.mojom b/chromium/ui/views_bridge_mac/mojo/bridged_native_widget.mojom index e120fc8268e..a256b186a6e 100644 --- a/chromium/ui/views_bridge_mac/mojo/bridged_native_widget.mojom +++ b/chromium/ui/views_bridge_mac/mojo/bridged_native_widget.mojom @@ -9,6 +9,13 @@ import "ui/base/mojo/ui_base_types.mojom"; import "ui/gfx/geometry/mojo/geometry.mojom"; import "ui/gfx/mojo/ca_layer_params.mojom"; +// Parameters used to described creation of an NSWindow. +// TODO(ccameron): Add additional members to support all window sub-classes. +struct CreateWindowParams { + // The NSWindowStyleMask for the window. + uint64 style_mask; +}; + // Ways of changing the visibility of the bridged NSWindow. enum WindowVisibilityState { // Hides with -[NSWindow orderOut:]. @@ -20,10 +27,23 @@ enum WindowVisibilityState { kShowInactive }; +// Window transitions to animate, mirrors views::Widget::VisibilityTransition. +enum VisibilityTransition { + kShow = 1, + kHide = 2, + kBoth = 3, + kNone = 4, +}; + struct BridgedNativeWidgetInitParams { ui.mojom.ModalType modal_type; // If true, the underlying window potentially be seen through. bool is_translucent; + // True if the widget is considered top level widget. + bool widget_is_top_level; + // True if the bounds specified to SetBounds should be treated as though they + // are in screen coordinates. + bool position_window_in_screen_coords; // If true, then the NSWindow is set to have a shadow using // -[NSWindow setHasShadow:YES]. bool has_window_server_shadow; @@ -36,6 +56,13 @@ struct BridgedNativeWidgetInitParams { // The interface through which a NativeWidgetMac may interact with an NSWindow // in another process. interface BridgedNativeWidget { + // Create and set the NSWindow for the bridge. + CreateWindow(CreateWindowParams params); + + // Set the BridgedNativeWidget indicated by |parent_id| to be the parent of + // this BridgedNativeWidget. + SetParent(uint64 parent_id); + // Initialize the window's style. InitWindow(BridgedNativeWidgetInitParams params); @@ -44,8 +71,9 @@ interface BridgedNativeWidget { // BridgedNativeWidgetHost. InitCompositorView(); - // Create the NSView to be the content view for the window. - CreateContentView(gfx.mojom.Rect bounds); + // Create the NSView to be the content view for the window. Use |ns_view_id| + // to look up this NSView in other functions (e.g, to specify a parent view). + CreateContentView(uint64 ns_view_id, gfx.mojom.Rect bounds); // Destroy the content NSView for this window. Note that the window will // become blank once this has been called. @@ -62,12 +90,9 @@ interface BridgedNativeWidget { // Specify initial bounds for the window via |new_bounds| in screen // coordinates. It is invalid for |new_bounds| to have an empty size and // non-zero position. The size of the window will be expanded so that the - // content size will be at least |minimum_content_size|. The bounds are offset - // by |parent_offset| (this isn't incorporated directly into |new_bounds| for - // the aforementioned checks of |new_bounds|' position). + // content size will be at least |minimum_content_size|. SetInitialBounds(gfx.mojom.Rect new_bounds, - gfx.mojom.Size minimum_content_size, - gfx.mojom.Vector2d parent_offset); + gfx.mojom.Size minimum_content_size); // Specify new bounds for the window via |new_bounds| in screen coordinates. // The size of the window will be expanded so that the content size will be @@ -75,10 +100,20 @@ interface BridgedNativeWidget { SetBounds(gfx.mojom.Rect new_bounds, gfx.mojom.Size minimum_content_size); + // Centers the window and sets its content size to |content_size|. The size of + // the window will be expanded so that the content size will be at least + // |minimum_content_size|. + SetSizeAndCenter(gfx.mojom.Size content_size, + gfx.mojom.Size minimum_content_size); + // Sets the desired visibility of the window and updates the visibility of // descendant windows where necessary. SetVisibilityState(WindowVisibilityState new_state); + // Sets which transitions will animate. Currently this only affects non-native + // animations. + SetTransitionsToAnimate(VisibilityTransition transitions); + // Sets the collection behavior so that the window will or will not be visible // on all spaces. SetVisibleOnAllSpaces(bool always_visible); @@ -114,6 +149,9 @@ interface BridgedNativeWidget { // Clear the touchbar. ClearTouchBar(); + // Update the tooltip text at the current mouse location. + UpdateTooltip(); + // Acquiring mouse capture first steals capture from any existing // CocoaMouseCaptureDelegate, then captures all mouse events until released. AcquireCapture(); diff --git a/chromium/ui/views_bridge_mac/mojo/bridged_native_widget_host.mojom b/chromium/ui/views_bridge_mac/mojo/bridged_native_widget_host.mojom new file mode 100644 index 00000000000..cba9a6b77eb --- /dev/null +++ b/chromium/ui/views_bridge_mac/mojo/bridged_native_widget_host.mojom @@ -0,0 +1,159 @@ +// 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. + +module views_bridge_mac.mojom; + +import "mojo/public/mojom/base/string16.mojom"; +import "ui/base/mojo/ui_base_types.mojom"; +import "ui/display/mojo/display.mojom"; +import "ui/events/mojo/event.mojom"; +import "ui/gfx/geometry/mojo/geometry.mojom"; +import "ui/gfx/mojo/ca_layer_params.mojom"; + +// The interface through which a NativeWidgetMac may interact with an NSWindow +// in another process. +interface BridgedNativeWidgetHost { + // Update the views::Widget, ui::Compositor and ui::Layer's visibility. + OnVisibilityChanged(bool visible); + + // Called when the window's native theme changes. + OnWindowNativeThemeChanged(); + + // Resize the underlying views::View to |new_size|. Note that this will not + // necessarily match the content bounds from OnWindowGeometryChanged. + SetViewSize(gfx.mojom.Size new_size); + + // Indicate if full keyboard accessibility is needed and update focus if + // needed. + SetKeyboardAccessible(bool enabled); + + // Indicate if the NSView for this widget is the first responder for the + // NSWindow for this widget. + SetIsFirstResponder(bool is_first_responder); + + // Indicate if mouse capture is active. + OnMouseCaptureActiveChanged(bool capture_is_active); + + // Handle events. Note that whether or not the event is actually handled is + // not returned. + OnScrollEvent(ui.mojom.Event event); + OnMouseEvent(ui.mojom.Event event); + OnGestureEvent(ui.mojom.Event event); + + // Synchronously dispatch a key event and return in |event_handled| whether + // or not the event was handled. This method is to be used only via the + // BridgedNativeWidgetHostHelper interface. + [Sync] + DispatchKeyEventRemote(ui.mojom.Event event) => (bool event_handled); + + // Synchronously dispatch a key event to the current menu controller (if one + // exists and is owned by the widget for this). Return in |event_swallowed| + // whether or not the event was swallowed (that is, if the menu's dispatch + // returned POST_DISPATCH_NONE). Return in |event_handled| whether or not the + // event was handled (that is, if the event in the caller's scope should be + // marked as handled). This method is to be used only via the + // BridgedNativeWidgetHostHelper interface. + [Sync] + DispatchKeyEventToMenuControllerRemote(ui.mojom.Event event) => + (bool event_swallowed, bool event_handled); + + // Synchronously return in |has_menu_controller| whether or not a menu + // controller exists for this widget. + [Sync] + GetHasMenuController() => (bool has_menu_controller); + + // Synchronously query if |location_in_content| is a draggable background. + [Sync] + GetIsDraggableBackgroundAt(gfx.mojom.Point location_in_content) => + (bool is_draggable_background); + + // Synchronously query the tooltip text for |location_in_content|. + [Sync] + GetTooltipTextAt(gfx.mojom.Point location_in_content) => + (mojo_base.mojom.String16 new_tooltip_text); + + // Synchronously query the value of IsModal for this widget and store it in + // |widget_is_modal|. + [Sync] + GetWidgetIsModal() => (bool widget_is_modal); + + // Synchronously return in |is_textual| whether or not the focused view + // contains text that can be selected and copied. + [Sync] + GetIsFocusedViewTextual() => (bool is_textual); + + // Called whenever the NSWindow's size or position changes. + OnWindowGeometryChanged( + gfx.mojom.Rect window_bounds_in_screen_dips, + gfx.mojom.Rect content_bounds_in_screen_dips); + + // Called when the window begins transitioning to or from being fullscreen. + OnWindowFullscreenTransitionStart( + bool target_fullscreen_state); + + // Called when the window has completed its transition to or from being + // fullscreen. Note that if there are multiple consecutive transitions + // (because a new transition was initiated before the previous one completed) + // then this will only be called when all transitions have competed. + OnWindowFullscreenTransitionComplete(bool is_fullscreen); + + // Called when the window is miniaturized or deminiaturized. + OnWindowMiniaturizedChanged(bool miniaturized); + + // Called when the current display or the properties of the current display + // change. + OnWindowDisplayChanged(display.mojom.Display display); + + // Called before the NSWindow is closed and destroyed. + OnWindowWillClose(); + + // Called after the NSWindow has been closed and destroyed. + OnWindowHasClosed(); + + // Called when the NSWindow becomes key or resigns from being key. Additional + // state required for the transition include whether or not the content NSView + // is the first responder for the NSWindow in |is_content_first_responder| and + // whether or not the NSApp's full keyboard access is enabled in + // |full_keyboard_access_enabled|. + OnWindowKeyStatusChanged(bool is_key, + bool is_content_first_responder, + bool full_keyboard_access_enabled); + + // Accept or cancel the current dialog window (depending on the value of + // |button|), if a current dialog exists. + DoDialogButtonAction(ui.mojom.DialogButton button); + + // Synchronously determine if the specified button exists in the current + // dialog (if any), along with its label, whether or not it is enabled, and + // whether or not it is the default button.. + [Sync] + GetDialogButtonInfo(ui.mojom.DialogButton button) => (bool button_exists, + mojo_base.mojom.String16 title, + bool is_button_enabled, + bool is_button_default); + + // Synchronously return in |buttons_exist| whether or not any buttons exist + // for the current dialog. + [Sync] + GetDoDialogButtonsExist() => (bool buttons_exist); + + // Synchronously query if the NSWindow should display its title. + [Sync] + GetShouldShowWindowTitle() => (bool should_show_window_title); + + // Synchronously query if the NSWindow can become key (activate, in views + // terminology). + [Sync] + GetCanWindowBecomeKey() => (bool can_window_become_key); + + // Synchronously query if the NSWindow should always render as if it is + // the key window (is active, in views terminology). + [Sync] + GetAlwaysRenderWindowAsKey() => (bool always_render_as_key); + + // Synchronously query if the NSWindow should always render as if it is + // the key window (is active, in views terminology). + [Sync] + GetCanWindowClose() => (bool can_window_close); +}; diff --git a/chromium/ui/views/cocoa/native_widget_mac_nswindow.h b/chromium/ui/views_bridge_mac/native_widget_mac_nswindow.h index 8a0f51b791b..ef8afe11aee 100644 --- a/chromium/ui/views/cocoa/native_widget_mac_nswindow.h +++ b/chromium/ui/views_bridge_mac/native_widget_mac_nswindow.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_VIEWS_COCOA_NATIVE_WIDGET_MAC_NSWINDOW_H_ -#define UI_VIEWS_COCOA_NATIVE_WIDGET_MAC_NSWINDOW_H_ +#ifndef UI_VIEWS_BRIDGE_MAC_NATIVE_WIDGET_MAC_NSWINDOW_H_ +#define UI_VIEWS_BRIDGE_MAC_NATIVE_WIDGET_MAC_NSWINDOW_H_ #import <Cocoa/Cocoa.h> @@ -47,6 +47,10 @@ VIEWS_EXPORT // create one. - (void)setWindowTouchBarDelegate:(id<WindowTouchBarDelegate>)delegate; +// Identifier for the NativeWidgetMac from which this window was created. This +// may be used to look up the BridgedNativeWidgetHostImpl in the browser process +// or the BridgedNativeWidgetImpl in a display process. +@property(assign, nonatomic) uint64_t bridgedNativeWidgetId; @end -#endif // UI_VIEWS_COCOA_NATIVE_WIDGET_MAC_NSWINDOW_H_ +#endif // UI_VIEWS_BRIDGE_MAC_NATIVE_WIDGET_MAC_NSWINDOW_H_ diff --git a/chromium/ui/views/cocoa/native_widget_mac_nswindow.mm b/chromium/ui/views_bridge_mac/native_widget_mac_nswindow.mm index 3e5775bc184..f0e7ac703ae 100644 --- a/chromium/ui/views/cocoa/native_widget_mac_nswindow.mm +++ b/chromium/ui/views_bridge_mac/native_widget_mac_nswindow.mm @@ -2,18 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "ui/views/cocoa/native_widget_mac_nswindow.h" +#import "ui/views_bridge_mac/native_widget_mac_nswindow.h" #include "base/mac/foundation_util.h" #import "base/mac/sdk_forward_declarations.h" #import "ui/base/cocoa/user_interface_item_command_handler.h" #import "ui/base/cocoa/window_size_constants.h" -#import "ui/views/cocoa/bridged_native_widget.h" -#import "ui/views/cocoa/views_nswindow_delegate.h" -#import "ui/views/cocoa/window_touch_bar_delegate.h" -#include "ui/views/controls/menu/menu_controller.h" -#include "ui/views/widget/native_widget_mac.h" -#include "ui/views/widget/widget_delegate.h" +#include "ui/views_bridge_mac/bridged_native_widget_host_helper.h" +#import "ui/views_bridge_mac/bridged_native_widget_impl.h" +#include "ui/views_bridge_mac/mojo/bridged_native_widget_host.mojom.h" +#import "ui/views_bridge_mac/views_nswindow_delegate.h" +#import "ui/views_bridge_mac/window_touch_bar_delegate.h" @interface NSWindow (Private) + (Class)frameViewClassForStyleMask:(NSWindowStyleMask)windowStyle; @@ -27,13 +26,15 @@ @interface NativeWidgetMacNSWindow () - (ViewsNSWindowDelegate*)viewsNSWindowDelegate; -- (views::Widget*)viewsWidget; - (BOOL)hasViewsMenuActive; - (id)rootAccessibilityObject; // Private API on NSWindow, determines whether the title is drawn on the title // bar. The title is still visible in menus, Expose, etc. - (BOOL)_isTitleHidden; + +// Retrieve the corresponding views::BridgedNativeWidgetImpl in this process. +- (views::BridgedNativeWidgetImpl*)bridgeImpl; @end // Use this category to implement mouseDown: on multiple frame view classes @@ -83,7 +84,9 @@ base::scoped_nsobject<CommandDispatcher> commandDispatcher_; base::scoped_nsprotocol<id<UserInterfaceItemCommandHandler>> commandHandler_; id<WindowTouchBarDelegate> touchBarDelegate_; // Weak. + uint64_t bridgedNativeWidgetId_; } +@synthesize bridgedNativeWidgetId = bridgedNativeWidgetId_; - (instancetype)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)windowStyle @@ -114,9 +117,9 @@ - (void)sheetDidEnd:(NSWindow*)sheet returnCode:(NSInteger)returnCode contextInfo:(void*)contextInfo { - // Note BridgedNativeWidget may have cleared [self delegate], in which case - // this will no-op. This indirection is necessary to handle AppKit invoking - // this selector via a posted task. See https://crbug.com/851376. + // Note BridgedNativeWidgetImpl may have cleared [self delegate], in which + // case this will no-op. This indirection is necessary to handle AppKit + // invoking this selector via a posted task. See https://crbug.com/851376. [[self viewsNSWindowDelegate] sheetDidEnd:sheet returnCode:returnCode contextInfo:contextInfo]; @@ -132,19 +135,18 @@ return base::mac::ObjCCastStrict<ViewsNSWindowDelegate>([self delegate]); } -- (views::Widget*)viewsWidget { - return [[self viewsNSWindowDelegate] nativeWidgetMac]->GetWidget(); +- (views::BridgedNativeWidgetImpl*)bridgeImpl { + return views::BridgedNativeWidgetImpl::GetFromId(bridgedNativeWidgetId_); } - (BOOL)hasViewsMenuActive { - views::MenuController* menuController = - views::MenuController::GetActiveInstance(); - return menuController && menuController->owner() == [self viewsWidget]; + bool hasMenuController = false; + [self bridgeImpl]->host()->GetHasMenuController(&hasMenuController); + return hasMenuController; } - (id)rootAccessibilityObject { - views::Widget* widget = [self viewsWidget]; - return widget ? widget->GetRootView()->GetNativeViewAccessible() : nil; + return [self bridgeImpl]->host_helper()->GetNativeViewAccessible(); } // NSWindow overrides. @@ -161,10 +163,10 @@ } - (BOOL)_isTitleHidden { - if (![self delegate]) - return NO; - - return ![self viewsWidget]->widget_delegate()->ShouldShowWindowTitle(); + bool shouldShowWindowTitle = YES; + if ([self bridgeImpl]) + [self bridgeImpl]->host()->GetShouldShowWindowTitle(&shouldShowWindowTitle); + return !shouldShowWindowTitle; } // The base implementation returns YES if the window's frame view is a custom @@ -179,23 +181,36 @@ // Note these can be called via -[NSWindow close] while the widget is being torn // down, so check for a delegate. - (BOOL)canBecomeKeyWindow { - return [self delegate] && [self viewsWidget]->CanActivate(); + bool canBecomeKey = NO; + if ([self bridgeImpl]) + [self bridgeImpl]->host()->GetCanWindowBecomeKey(&canBecomeKey); + return canBecomeKey; } - (BOOL)canBecomeMainWindow { - if (![self delegate]) + views::BridgedNativeWidgetImpl* bridgeImpl = [self bridgeImpl]; + if (!bridgeImpl) return NO; // Dialogs and bubbles shouldn't take large shadows away from their parent. - views::Widget* widget = [self viewsWidget]; - return widget->CanActivate() && - !views::NativeWidgetMac::GetBridgeForNativeWindow(self)->parent(); + if (bridgeImpl->parent()) + return NO; + + bool canBecomeKey = NO; + if (bridgeImpl) + bridgeImpl->host()->GetCanWindowBecomeKey(&canBecomeKey); + return canBecomeKey; } // Lets the traffic light buttons on the parent window keep their active state. - (BOOL)hasKeyAppearance { - if ([self delegate] && [self viewsWidget]->IsAlwaysRenderAsActive()) - return YES; + views::BridgedNativeWidgetImpl* bridgeImpl = [self bridgeImpl]; + if (bridgeImpl) { + bool isAlwaysRenderWindowAsKey = NO; + bridgeImpl->host()->GetAlwaysRenderWindowAsKey(&isAlwaysRenderWindowAsKey); + if (isAlwaysRenderWindowAsKey) + return YES; + } return [super hasKeyAppearance]; } @@ -312,12 +327,12 @@ // Additionally, if we don't do this, VoiceOver reads out the partial a11y // properties on the NSWindow and repeats them when focusing an item in the // RootView's a11y group. See http://crbug.com/748221. - views::Widget* widget = [self viewsWidget]; id superFocus = [super accessibilityFocusedUIElement]; - if (!widget || superFocus != self) + views::BridgedNativeWidgetImpl* bridgeImpl = [self bridgeImpl]; + if (!bridgeImpl || superFocus != self) return superFocus; - return widget->GetRootView()->GetNativeViewAccessible(); + return bridgeImpl->host_helper()->GetNativeViewAccessible(); } - (id)accessibilityAttributeValue:(NSString*)attribute { diff --git a/chromium/ui/views_bridge_mac/views_bridge_mac_export.h b/chromium/ui/views_bridge_mac/views_bridge_mac_export.h new file mode 100644 index 00000000000..6a44a0d32e0 --- /dev/null +++ b/chromium/ui/views_bridge_mac/views_bridge_mac_export.h @@ -0,0 +1,32 @@ +// 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_VIEWS_BRIDGE_MAC_VIEWS_BRIDGE_MAC_EXPORT_H_ +#define UI_VIEWS_BRIDGE_MAC_VIEWS_BRIDGE_MAC_EXPORT_H_ + +// Defines VIEWS_BRIDGE_MAC_EXPORT so that functionality implemented by the +// RemoteMacViews module can be exported to consumers. + +#if defined(COMPONENT_BUILD) +#if defined(WIN32) + +#if defined(VIEWS_BRIDGE_MAC_IMPLEMENTATION) +#define VIEWS_BRIDGE_MAC_EXPORT __declspec(dllexport) +#else +#define VIEWS_BRIDGE_MAC_EXPORT __declspec(dllimport) +#endif // defined(VIEWS_BRIDGE_MAC_IMPLEMENTATION) + +#else // defined(WIN32) +#if defined(VIEWS_BRIDGE_MAC_IMPLEMENTATION) +#define VIEWS_BRIDGE_MAC_EXPORT __attribute__((visibility("default"))) +#else +#define VIEWS_BRIDGE_MAC_EXPORT +#endif +#endif + +#else // defined(COMPONENT_BUILD) +#define VIEWS_BRIDGE_MAC_EXPORT +#endif + +#endif // UI_VIEWS_BRIDGE_MAC_VIEWS_BRIDGE_MAC_EXPORT_H_ diff --git a/chromium/ui/views/cocoa/views_nswindow_delegate.h b/chromium/ui/views_bridge_mac/views_nswindow_delegate.h index 00c2e13bbab..bc9f33afddf 100644 --- a/chromium/ui/views/cocoa/views_nswindow_delegate.h +++ b/chromium/ui/views_bridge_mac/views_nswindow_delegate.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_VIEWS_COCOA_VIEWS_NSWINDOW_DELEGATE_H_ -#define UI_VIEWS_COCOA_VIEWS_NSWINDOW_DELEGATE_H_ +#ifndef UI_VIEWS_BRIDGE_MAC_VIEWS_NSWINDOW_DELEGATE_H_ +#define UI_VIEWS_BRIDGE_MAC_VIEWS_NSWINDOW_DELEGATE_H_ #import <Cocoa/Cocoa.h> @@ -11,29 +11,24 @@ #include "ui/views/views_export.h" namespace views { -class NativeWidgetMac; -class BridgedNativeWidget; +class BridgedNativeWidgetImpl; } -// The delegate set on the NSWindow when a views::BridgedNativeWidget is +// The delegate set on the NSWindow when a views::BridgedNativeWidgetImpl is // initialized. VIEWS_EXPORT @interface ViewsNSWindowDelegate : NSObject<NSWindowDelegate> { @private - views::BridgedNativeWidget* parent_; // Weak. Owns this. + views::BridgedNativeWidgetImpl* parent_; // Weak. Owns this. base::scoped_nsobject<NSCursor> cursor_; } -// The NativeWidgetMac that created the window this is attached to. Returns -// NULL if not created by NativeWidgetMac. -@property(nonatomic, readonly) views::NativeWidgetMac* nativeWidgetMac; - // If set, the cursor set in -[NSResponder updateCursor:] when the window is // reached along the responder chain. @property(retain, nonatomic) NSCursor* cursor; // Initialize with the given |parent|. -- (id)initWithBridgedNativeWidget:(views::BridgedNativeWidget*)parent; +- (id)initWithBridgedNativeWidget:(views::BridgedNativeWidgetImpl*)parent; // Notify that the window has been reordered in (or removed from) the window // server's screen list. This is a substitute for -[NSWindowDelegate @@ -52,4 +47,4 @@ VIEWS_EXPORT @end -#endif // UI_VIEWS_COCOA_VIEWS_NSWINDOW_DELEGATE_H_ +#endif // UI_VIEWS_BRIDGE_MAC_VIEWS_NSWINDOW_DELEGATE_H_ diff --git a/chromium/ui/views/cocoa/views_nswindow_delegate.mm b/chromium/ui/views_bridge_mac/views_nswindow_delegate.mm index bce74956126..ca77131d78f 100644 --- a/chromium/ui/views/cocoa/views_nswindow_delegate.mm +++ b/chromium/ui/views_bridge_mac/views_nswindow_delegate.mm @@ -2,19 +2,19 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "ui/views/cocoa/views_nswindow_delegate.h" +#import "ui/views_bridge_mac/views_nswindow_delegate.h" #include "base/bind.h" #include "base/logging.h" #include "base/threading/thread_task_runner_handle.h" -#import "ui/views/cocoa/bridged_content_view.h" -#import "ui/views/cocoa/bridged_native_widget.h" -#include "ui/views/cocoa/bridged_native_widget_host.h" -#include "ui/views/widget/native_widget_mac.h" +#import "ui/views_bridge_mac/bridged_content_view.h" +#include "ui/views_bridge_mac/bridged_native_widget_host_helper.h" +#import "ui/views_bridge_mac/bridged_native_widget_impl.h" +#include "ui/views_bridge_mac/mojo/bridged_native_widget_host.mojom.h" @implementation ViewsNSWindowDelegate -- (id)initWithBridgedNativeWidget:(views::BridgedNativeWidget*)parent { +- (id)initWithBridgedNativeWidget:(views::BridgedNativeWidgetImpl*)parent { DCHECK(parent); if ((self = [super init])) { parent_ = parent; @@ -22,10 +22,6 @@ return self; } -- (views::NativeWidgetMac*)nativeWidgetMac { - return parent_->native_widget_mac(); -} - - (NSCursor*)cursor { return cursor_.get(); } @@ -64,7 +60,7 @@ break; currentWindow = parentWindow; if ([currentWindow isKeyWindow]) { - [(newCursor ? newCursor : [NSCursor arrowCursor]) set]; + [(newCursor ? newCursor : [NSCursor arrowCursor])set]; break; } } @@ -123,9 +119,9 @@ } - (BOOL)windowShouldClose:(id)sender { - views::NonClientView* nonClientView = - [self nativeWidgetMac]->GetWidget()->non_client_view(); - return !nonClientView || nonClientView->CanClose(); + bool canWindowClose = true; + parent_->host()->GetCanWindowClose(&canWindowClose); + return canWindowClose; } - (void)windowWillClose:(NSNotification*)notification { @@ -199,12 +195,15 @@ - (NSRect)window:(NSWindow*)window willPositionSheet:(NSWindow*)sheet usingRect:(NSRect)defaultSheetLocation { + // TODO(ccameron): This should go through the BridgedNativeWidgetHost + // interface. + CGFloat sheetPositionY = parent_->host_helper()->SheetPositionY(); + // As per NSWindowDelegate documentation, the origin indicates the top left // point of the host frame in window coordinates. The width changes the // animation from vertical to trapezoid if it is smaller than the width of the // dialog. The height is ignored but should be set to zero. - return NSMakeRect(0, [self nativeWidgetMac]->SheetPositionY(), - NSWidth(defaultSheetLocation), 0); + return NSMakeRect(0, sheetPositionY, NSWidth(defaultSheetLocation), 0); } @end diff --git a/chromium/ui/views/cocoa/views_scrollbar_bridge.h b/chromium/ui/views_bridge_mac/views_scrollbar_bridge.h index eb070a161c5..7b5809eba8f 100644 --- a/chromium/ui/views/cocoa/views_scrollbar_bridge.h +++ b/chromium/ui/views_bridge_mac/views_scrollbar_bridge.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_VIEWS_COCOA_VIEWS_SCROLLBAR_BRIDGE_DELEGATE_H_ -#define UI_VIEWS_COCOA_VIEWS_SCROLLBAR_BRIDGE_DELEGATE_H_ +#ifndef UI_VIEWS_BRIDGE_MAC_VIEWS_SCROLLBAR_BRIDGE_H_ +#define UI_VIEWS_BRIDGE_MAC_VIEWS_SCROLLBAR_BRIDGE_H_ #import <Cocoa/Cocoa.h> @@ -30,11 +30,11 @@ class ViewsScrollbarBridgeDelegate { - (id)initWithDelegate:(ViewsScrollbarBridgeDelegate*)delegate; // Sets |delegate_| to nullptr. --(void)clearDelegate; +- (void)clearDelegate; // Returns the style of scrollers that OSX is using. + (NSScrollerStyle)getPreferredScrollerStyle; @end -#endif
\ No newline at end of file +#endif diff --git a/chromium/ui/views/cocoa/views_scrollbar_bridge.mm b/chromium/ui/views_bridge_mac/views_scrollbar_bridge.mm index d80dece0e7b..c720483d4fe 100644 --- a/chromium/ui/views/cocoa/views_scrollbar_bridge.mm +++ b/chromium/ui/views_bridge_mac/views_scrollbar_bridge.mm @@ -2,7 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#import "ui/views/cocoa/views_scrollbar_bridge.h" +#import "ui/views_bridge_mac/views_scrollbar_bridge.h" #import "base/mac/sdk_forward_declarations.h" diff --git a/chromium/ui/views/cocoa/window_touch_bar_delegate.h b/chromium/ui/views_bridge_mac/window_touch_bar_delegate.h index daef5466ed6..9de867197fd 100644 --- a/chromium/ui/views/cocoa/window_touch_bar_delegate.h +++ b/chromium/ui/views_bridge_mac/window_touch_bar_delegate.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_VIEWS_COCOA_WINDOW_TOUCH_BAR_DELEGATE_H_ -#define UI_VIEWS_COCOA_WINDOW_TOUCH_BAR_DELEGATE_H_ +#ifndef UI_VIEWS_BRIDGE_MAC_WINDOW_TOUCH_BAR_DELEGATE_H_ +#define UI_VIEWS_BRIDGE_MAC_WINDOW_TOUCH_BAR_DELEGATE_H_ #import <Cocoa/Cocoa.h> @@ -18,4 +18,4 @@ @end -#endif // UI_VIEWS_COCOA_WINDOW_TOUCH_BAR_DELEGATE_H_
\ No newline at end of file +#endif // UI_VIEWS_BRIDGE_MAC_WINDOW_TOUCH_BAR_DELEGATE_H_ diff --git a/chromium/ui/views_content_client/views_content_client.h b/chromium/ui/views_content_client/views_content_client.h index 11f653a2476..d71a12fbe01 100644 --- a/chromium/ui/views_content_client/views_content_client.h +++ b/chromium/ui/views_content_client/views_content_client.h @@ -66,6 +66,13 @@ class VIEWS_CONTENT_CLIENT_EXPORT ViewsContentClient { void set_task(const Task& task) { task_ = task; } const Task& task() const { return task_; } + // Called by ViewsContentClientMainParts to supply the quit-closure to use + // to exit RunMain(). + void set_quit_closure(base::OnceClosure quit_closure) { + quit_closure_ = std::move(quit_closure); + } + base::OnceClosure& quit_closure() { return quit_closure_; } + private: #if defined(OS_WIN) HINSTANCE instance_; @@ -75,6 +82,7 @@ class VIEWS_CONTENT_CLIENT_EXPORT ViewsContentClient { const char** argv_; #endif Task task_; + base::OnceClosure quit_closure_; DISALLOW_COPY_AND_ASSIGN(ViewsContentClient); }; diff --git a/chromium/ui/views_content_client/views_content_client_main_parts.cc b/chromium/ui/views_content_client/views_content_client_main_parts.cc index 3f789391eaf..5a5abb34842 100644 --- a/chromium/ui/views_content_client/views_content_client_main_parts.cc +++ b/chromium/ui/views_content_client/views_content_client_main_parts.cc @@ -12,6 +12,7 @@ #include "ui/base/ime/input_method_initializer.h" #include "ui/base/material_design/material_design_controller.h" #include "ui/views/test/desktop_test_views_delegate.h" +#include "ui/views_content_client/views_content_client.h" namespace ui { @@ -44,6 +45,7 @@ void ViewsContentClientMainParts::PostMainMessageLoopRun() { bool ViewsContentClientMainParts::MainMessageLoopRun(int* result_code) { base::RunLoop run_loop; + views_content_client_->set_quit_closure(run_loop.QuitClosure()); run_loop.Run(); return true; } diff --git a/chromium/ui/web_dialogs/BUILD.gn b/chromium/ui/web_dialogs/BUILD.gn index d5d48e4970d..d09fa063ddd 100644 --- a/chromium/ui/web_dialogs/BUILD.gn +++ b/chromium/ui/web_dialogs/BUILD.gn @@ -33,7 +33,7 @@ jumbo_component("web_dialogs") { } } -static_library("test_support") { +jumbo_static_library("test_support") { sources = [ "test/test_web_contents_handler.cc", "test/test_web_contents_handler.h", diff --git a/chromium/ui/web_dialogs/web_dialog_delegate.cc b/chromium/ui/web_dialogs/web_dialog_delegate.cc index f5ae08226cf..b2ecbf67a84 100644 --- a/chromium/ui/web_dialogs/web_dialog_delegate.cc +++ b/chromium/ui/web_dialogs/web_dialog_delegate.cc @@ -8,6 +8,10 @@ namespace ui { +base::string16 WebDialogDelegate::GetAccessibleDialogTitle() const { + return GetDialogTitle(); +} + std::string WebDialogDelegate::GetDialogName() const { return std::string(); } diff --git a/chromium/ui/web_dialogs/web_dialog_delegate.h b/chromium/ui/web_dialogs/web_dialog_delegate.h index b966e1fa8d8..ee639fd025e 100644 --- a/chromium/ui/web_dialogs/web_dialog_delegate.h +++ b/chromium/ui/web_dialogs/web_dialog_delegate.h @@ -41,6 +41,9 @@ class WEB_DIALOGS_EXPORT WebDialogDelegate { // Returns the title of the dialog. virtual base::string16 GetDialogTitle() const = 0; + // Returns the title to be read with screen readers. + virtual base::string16 GetAccessibleDialogTitle() const; + // Returns the dialog's name identifier. Used to identify this dialog for // state restoration. virtual std::string GetDialogName() const; diff --git a/chromium/ui/webui/PLATFORM_OWNERS b/chromium/ui/webui/PLATFORM_OWNERS index 29e5a4ae960..a93051bd3f8 100644 --- a/chromium/ui/webui/PLATFORM_OWNERS +++ b/chromium/ui/webui/PLATFORM_OWNERS @@ -1,10 +1,8 @@ # Please use more specific OWNERS when possible. -bauerb@chromium.org calamity@chromium.org dpapad@chromium.org dschuyler@chromium.org michaelpg@chromium.org -pam@chromium.org scottchen@chromium.org stevenjb@chromium.org tommycli@chromium.org diff --git a/chromium/ui/webui/mojo_web_ui_controller.cc b/chromium/ui/webui/mojo_web_ui_controller.cc index e2de8d20f8a..769e194449c 100644 --- a/chromium/ui/webui/mojo_web_ui_controller.cc +++ b/chromium/ui/webui/mojo_web_ui_controller.cc @@ -24,6 +24,12 @@ void MojoWebUIController::OnInterfaceRequestFromFrame( content::RenderFrameHost* render_frame_host, const std::string& interface_name, mojo::ScopedMessagePipeHandle* interface_pipe) { + if (!registry_.CanBindInterface(interface_name)) { + LOG(WARNING) << "Cannot bind request to " << interface_name << "; ignoring " + << "request."; + return; + } + // Right now, this is expected to be called only for main frames. if (render_frame_host->GetParent()) { LOG(ERROR) << "Terminating renderer for requesting " << interface_name diff --git a/chromium/ui/webui/resources/cr_components/BUILD.gn b/chromium/ui/webui/resources/cr_components/BUILD.gn index f3d71a3a372..b39708e6607 100644 --- a/chromium/ui/webui/resources/cr_components/BUILD.gn +++ b/chromium/ui/webui/resources/cr_components/BUILD.gn @@ -7,6 +7,9 @@ import("//third_party/closure_compiler/compile_js.gni") group("closure_compile") { deps = [ "certificate_manager:closure_compile", - "chromeos:closure_compile", ] + + if (is_chromeos) { + deps += [ "chromeos:closure_compile" ] + } } diff --git a/chromium/ui/webui/resources/cr_components/chromeos/BUILD.gn b/chromium/ui/webui/resources/cr_components/chromeos/BUILD.gn index 0fd1a54821e..3c7e36300ab 100644 --- a/chromium/ui/webui/resources/cr_components/chromeos/BUILD.gn +++ b/chromium/ui/webui/resources/cr_components/chromeos/BUILD.gn @@ -4,9 +4,12 @@ import("//third_party/closure_compiler/compile_js.gni") +assert(is_chromeos, "Only ChromeOS components belong here.") + group("closure_compile") { deps = [ ":chromeos_resources", + "multidevice_setup:closure_compile", "network:closure_compile", "quick_unlock:closure_compile", ] diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/.eslintrc.js b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/.eslintrc.js new file mode 100644 index 00000000000..25e21f992eb --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/.eslintrc.js @@ -0,0 +1,13 @@ +// 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. + +module.exports = { + 'env': { + 'browser': true, + 'es6': true, + }, + 'rules': { + 'no-var': 'error', + }, +}; diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/BUILD.gn b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/BUILD.gn new file mode 100644 index 00000000000..616ad6e48f5 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/BUILD.gn @@ -0,0 +1,129 @@ +# 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. + +import("//third_party/closure_compiler/compile_js.gni") + +assert(is_chromeos, "MultiDevice UI is Chrome OS only.") + +js_type_check("closure_compile") { + deps = [ + ":button_bar", + ":fake_mojo_service", + ":mojo_api", + ":multidevice_setup", + ":multidevice_setup_browser_proxy", + ":multidevice_setup_delegate", + ":setup_failed_page", + ":setup_succeeded_page", + ":start_setup_page", + ":ui_page_container_behavior", + "//ui/webui/resources/js:web_ui_listener_behavior", + ] +} + +js_library("button_bar") { +} + +js_library("fake_mojo_service") { + deps = [ + "//ui/webui/resources/js:cr", + ] + + extra_deps = [ + "//chromeos/services/device_sync/public/mojom:mojom_js", + "//chromeos/services/multidevice_setup/public/mojom:mojom_js", + "//mojo/public/mojom/base:base_js", + ] + + externs_list = [ + "$root_gen_dir/chromeos/services/device_sync/public/mojom/device_sync.mojom.externs.js", + "$root_gen_dir/chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.externs.js", + "$root_gen_dir/mojo/public/mojom/base/time.mojom.externs.js", + "$externs_path/mojo.js", + ] +} + +js_library("mojo_api") { + deps = [ + "//ui/webui/resources/js:cr", + ] +} + +js_library("multidevice_setup") { + deps = [ + ":button_bar", + ":fake_mojo_service", + ":mojo_api", + ":multidevice_setup_delegate", + ":password_page", + ":setup_failed_page", + ":setup_succeeded_page", + ":start_setup_page", + "//ui/webui/resources/js:cr", + "//ui/webui/resources/js:web_ui_listener_behavior", + ] + + extra_deps = [ + "//chromeos/services/device_sync/public/mojom:mojom_js", + "//chromeos/services/multidevice_setup/public/mojom:mojom_js", + "//mojo/public/mojom/base:base_js", + ] + + externs_list = [ + "$root_gen_dir/chromeos/services/device_sync/public/mojom/device_sync.mojom.externs.js", + "$root_gen_dir/chromeos/services/multidevice_setup/public/mojom/multidevice_setup.mojom.externs.js", + "$root_gen_dir/mojo/public/mojom/base/time.mojom.externs.js", + "$externs_path/mojo.js", + ] +} + +js_library("multidevice_setup_browser_proxy") { + deps = [ + "//ui/webui/resources/js:cr", + ] +} + +js_library("multidevice_setup_delegate") { + deps = [ + "//ui/webui/resources/js:cr", + ] +} + +js_library("password_page") { + deps = [ + ":multidevice_setup_browser_proxy", + ":ui_page_container_behavior", + "//ui/webui/resources/cr_elements/cr_input:cr_input", + "//ui/webui/resources/js:cr", + ] + externs_list = [ "$externs_path/quick_unlock_private.js" ] + extra_sources = [ "$interfaces_path/quick_unlock_private_interface.js" ] +} + +js_library("setup_failed_page") { + deps = [ + ":ui_page_container_behavior", + ] +} + +js_library("setup_succeeded_page") { + deps = [ + ":multidevice_setup_browser_proxy", + ":ui_page_container_behavior", + ] +} + +js_library("start_setup_page") { + deps = [ + ":ui_page_container_behavior", + "//ui/webui/resources/js:web_ui_listener_behavior", + ] +} + +js_library("ui_page_container_behavior") { + deps = [ + "//ui/webui/resources/js:cr", + "//ui/webui/resources/js:i18n_behavior", + ] +} diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/OWNERS b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/OWNERS new file mode 100644 index 00000000000..aef4ecb9195 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/OWNERS @@ -0,0 +1 @@ +file://chromeos/services/multidevice_setup/OWNERS diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/button_bar.html b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/button_bar.html new file mode 100644 index 00000000000..7b6c70c4df6 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/button_bar.html @@ -0,0 +1,31 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/multidevice_setup_shared_css.html"> + +<dom-module id="button-bar"> + <template> + <style include="multidevice-setup-shared"> + :host { + display: flex; + } + </style> + <div id="backward" + on-click="onBackwardButtonClicked_" + hidden$="[[backwardButtonHidden]]"> + <slot name="backward-button"></slot> + </div> + <div class="flex"></div> + <div id="cancel" + on-click="onCancelButtonClicked_" + hidden$="[[cancelButtonHidden]]"> + <slot name="cancel-button"></slot> + </div> + <div id="forward" + on-click="onForwardButtonClicked_" + hidden$="[[forwardButtonHidden]]"> + <slot name="forward-button"></slot> + </div> + </template> + <script src="chrome://resources/cr_components/chromeos/multidevice_setup/button_bar.js"> + </script> +</dom-module> diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/button_bar.js b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/button_bar.js new file mode 100644 index 00000000000..f556598dce9 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/button_bar.js @@ -0,0 +1,46 @@ +// 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. + +/** + * DOM Element containing (page-dependent) navigation buttons for the + * MultiDevice Setup WebUI. + */ +Polymer({ + is: 'button-bar', + + properties: { + /** Whether the forward button should be hidden. */ + forwardButtonHidden: { + type: Boolean, + value: true, + }, + + /** Whether the cancel button should be hidden. */ + cancelButtonHidden: { + type: Boolean, + value: true, + }, + + /** Whether the backward button should be hidden. */ + backwardButtonHidden: { + type: Boolean, + value: true, + }, + }, + + /** @private */ + onForwardButtonClicked_: function() { + this.fire('forward-navigation-requested'); + }, + + /** @private */ + onCancelButtonClicked_: function() { + this.fire('cancel-requested'); + }, + + /** @private */ + onBackwardButtonClicked_: function() { + this.fire('backward-navigation-requested'); + }, +}); diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/fake_mojo_service.html b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/fake_mojo_service.html new file mode 100644 index 00000000000..12556f5782b --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/fake_mojo_service.html @@ -0,0 +1,4 @@ +<link rel="import" href="chrome://resources/html/cr.html"> + +<script src="chrome://resources/cr_components/chromeos/multidevice_setup/fake_mojo_service.js"> +</script> diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/fake_mojo_service.js b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/fake_mojo_service.js new file mode 100644 index 00000000000..48e3eaa4ef0 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/fake_mojo_service.js @@ -0,0 +1,108 @@ +// 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. + +/** + * @implements {chromeos.multideviceSetup.mojom.MultiDeviceSetupImpl} + */ +class FakeMojoService { + constructor() { + /** + * The number of devices to return in a getEligibleHostDevices() call. + * @type {number} + */ + this.deviceCount = 2; + + /** + * Whether calls to setHostDevice() should succeed. + * @type {boolean} + */ + this.shouldSetHostSucceed = true; + } + + /** @override */ + setAccountStatusChangeDelegate(delegate) { + // Unimplemented; never called from setup flow. + assertNotReached(); + } + + /** @override */ + addHostStatusObserver(observer) { + // Unimplemented; never called from setup flow. + assertNotReached(); + } + + /** @override */ + addFeatureStateObserver(observer) { + // Unimplemented; never called from setup flow. + assertNotReached(); + } + + /** @override */ + getEligibleHostDevices() { + const deviceNames = ['Pixel', 'Pixel XL', 'Nexus 5', 'Nexus 6P']; + let devices = []; + for (let i = 0; i < this.deviceCount; i++) { + const deviceName = deviceNames[i % 4]; + devices.push({deviceName: deviceName, deviceId: deviceName + '--' + i}); + } + return new Promise(function(resolve, reject) { + resolve({eligibleHostDevices: devices}); + }); + } + + /** @override */ + setHostDevice(deviceId) { + if (this.shouldSetHostSucceed) { + console.log( + 'setHostDevice(' + deviceId + ') called; simulating ' + + 'success.'); + } else { + console.warn('setHostDevice() called; simulating failure.'); + } + return new Promise((resolve, reject) => { + resolve({success: this.shouldSetHostSucceed}); + }); + } + + /** @override */ + removeHostDevice() { + // Unimplemented; never called from setup flow. + assertNotReached(); + } + + /** @override */ + getHostStatus() { + return new Promise((resolve, reject) => { + reject('Unimplemented; never called from setup flow.'); + }); + } + + /** @override */ + setFeatureEnabledState() { + return new Promise((resolve, reject) => { + reject('Unimplemented; never called from setup flow.'); + }); + } + + /** @override */ + getFeatureStates() { + return new Promise((resolve, reject) => { + reject('Unimplemented; never called from setup flow.'); + }); + } + + /** @override */ + retrySetHostNow() { + return new Promise((resolve, reject) => { + reject('Unimplemented; never called from setup flow.'); + }); + } + + /** @override */ + triggerEventForDebugging(type) { + return new Promise((resolve, reject) => { + reject('Unimplemented; never called from setup flow.'); + }); + } +} diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/icons.html b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/icons.html new file mode 100644 index 00000000000..0232cf476e4 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/icons.html @@ -0,0 +1,36 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/polymer/v1_0/iron-iconset-svg/iron-iconset-svg.html"> + +<iron-iconset-svg name="multidevice-setup-icons-32" size="32"> + <svg> + <defs> + <g id="google-g" fill="none" fill-rule="evenodd"> + <path d="M30.42 16.83a16.66 16.66 0 0 0-.264-2.966H16.5v5.609h7.804c-.336 1.812-1.358 3.348-2.894 4.376v3.638h4.686c2.742-2.524 4.324-6.242 4.324-10.657z" fill="#4285F4"></path> + <path d="M16.5 31c3.915 0 7.197-1.298 9.596-3.513l-4.686-3.638c-1.298.87-2.96 1.384-4.91 1.384-3.777 0-6.973-2.55-8.113-5.978H3.542v3.757C5.928 27.75 10.832 31 16.5 31z" fill="#34A853"></path> + <path d="M8.387 19.255c-.29-.87-.455-1.8-.455-2.755 0-.956.165-1.885.455-2.755V9.988H3.542A14.494 14.494 0 0 0 2 16.5c0 2.34.56 4.554 1.542 6.512l4.845-3.757z" fill="#FBBC05"></path> + <path d="M16.5 7.767c2.129 0 4.04.732 5.543 2.168l4.159-4.158C23.69 3.437 20.408 2 16.5 2 10.832 2 5.928 5.25 3.542 9.988l4.845 3.757c1.14-3.427 4.336-5.978 8.113-5.978z" fill="#EA4335"></path> + <path d="M2 2h29v29H2z"></path> + </g> + <g id="error-icon" fill="none" fill-rule="evenodd" transform="translate(-4 -4)"> + <path d="M20 4C11.168 4 4 11.168 4 20s7.168 16 16 16 16-7.168 16-16S28.832 4 20 4zm1.6 24h-3.2v-3.2h3.2V28zm0-6.4h-3.2V12h3.2v9.6z" fill="#D93025" fill-rule="nonzero"></path> + <path d="M.8.8h38.4v38.4H.8z"></path> + </g> + </defs> + </svg> +</iron-iconset-svg> +<iron-iconset-svg name="multidevice-setup-icons-20" size="20"> + <svg> + <defs> + <g id="messages" fill="none" fill-rule="evenodd"> + <path d="M16.3107503,3 L3.66666667,3 C2.75,3 2,3.75 2,4.66666667 L2,18.3161621 L5.33333333,15 L16.3107503,15 C17.227417,15 17.977417,14.2328288 17.977417,13.3161621 L17.977417,4.66666667 C17.977417,3.75 17.227417,3 16.3107503,3 Z M16,13 L4,13 L4,5 L16,5 L16,13 Z M6,8 L8,8 L8,10 L6,10 L6,8 Z M9,8 L11,8 L11,10 L9,10 L9,8 Z M12,8 L14,8 L14,10 L12,10 L12,8 Z" fill="#9AA0A6"></path> + </g> + <g id="downloads" fill="none" fill-rule="evenodd"> + <path d="M2,13 L4,13 L4,16 L16,16 L16,13 L18,13 L18,16 C18,17.1 17.1,18 16,18 L4,18 C2.9,18 2,17.1 2,16 L2,13 Z M13.59,7.59 L11,10.17 L11,2 L9,2 L9,10.17 L6.41,7.59 L5,9 L10,14 L15,9 L13.59,7.59 Z" fill="#9AA0A6"></path> + </g> + <g id="features" fill="none" fill-rule="evenodd"> + <path d="M5,5 L18,5 L18,3.5 L5.16080729,3.5 C4.24414063,3.5 3.49414062,4.23125 3.49414062,5.125 L3.49414062,14 L1,14 L1,17 L11,17 L11,14 L5,14 L5,5 Z M18.1666667,6.49829102 L13.3713582,6.49829102 C12.9130249,6.49829102 12.5,6.86391602 12.5,7.31079102 L12.5,16.171875 C12.5,16.61875 12.9130249,17 13.3713582,17 L18.1666667,17 C18.625,17 19,16.61875 19,16.171875 L19,7.31079102 C19,6.86391602 18.625,6.49829102 18.1666667,6.49829102 Z M17.5,14 L14,14 L14,8.5 L17.5,8.5 L17.5,14 Z" fill="#9AA0A6"></path> + </g> + </defs> + </svg> +</iron-iconset-svg> diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/mojo_api.html b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/mojo_api.html new file mode 100644 index 00000000000..2bcb1bbc67d --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/mojo_api.html @@ -0,0 +1,10 @@ +<link rel="import" href="chrome://resources/html/cr.html"> +<script src="chrome://resources/js/mojo_bindings.js"></script> +<script src="chrome://resources/js/time.mojom.js"></script> +<script src="chrome://resources/js/chromeos/device_sync.mojom.js"></script> +<script src="chrome://resources/js/chromeos/multidevice_setup.mojom.js"> +</script> +<script src="chrome://resources/js/chromeos/multidevice_setup_constants.mojom.js"> +</script> +<script src="chrome://resources/cr_components/chromeos/multidevice_setup/mojo_api.js"> +</script> diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/mojo_api.js b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/mojo_api.js new file mode 100644 index 00000000000..d2aba99703f --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/mojo_api.js @@ -0,0 +1,40 @@ +// 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. + +cr.define('multidevice_setup', function() { + /** @interface */ + class MojoInterfaceProvider { + /** + * @return {!chromeos.multideviceSetup.mojom.MultiDeviceSetupImpl} + */ + getInterfacePtr() {} + } + + /** @implements {multidevice_setup.MojoInterfaceProvider} */ + class MojoInterfaceProviderImpl { + constructor() { + /** @private {?chromeos.multideviceSetup.mojom.MultiDeviceSetupPtr} */ + this.ptr_ = null; + } + + /** @override */ + getInterfacePtr() { + if (!this.ptr_) { + this.ptr_ = new chromeos.multideviceSetup.mojom.MultiDeviceSetupPtr(); + Mojo.bindInterface( + chromeos.multideviceSetup.mojom.MultiDeviceSetup.name, + mojo.makeRequest(this.ptr_).handle); + } + + return this.ptr_; + } + } + + cr.addSingletonGetter(MojoInterfaceProviderImpl); + + return { + MojoInterfaceProvider: MojoInterfaceProvider, + MojoInterfaceProviderImpl: MojoInterfaceProviderImpl, + }; +}); diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html new file mode 100644 index 00000000000..a75218ee419 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.html @@ -0,0 +1,67 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/button_bar.html"> +<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/fake_mojo_service.html"> +<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/mojo_api.html"> +<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/multidevice_setup_delegate.html"> +<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/multidevice_setup_shared_css.html"> +<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/password_page.html"> +<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/setup_failed_page.html"> +<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/setup_succeeded_page.html"> +<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/start_setup_page.html"> +<link rel="import" href="chrome://resources/html/cr.html"> +<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-pages/iron-pages.html"> + +<dom-module id="multidevice-setup"> + <template> + <style include="multidevice-setup-shared"> + :host { + @apply --layout-vertical; + box-sizing: border-box; + color: var(--google-grey-700); + font-size: 13px; + height: 640px; + line-height: 16px; + margin: auto; + padding: 60px 32px 32px 32px; + width: 768px; + } + + iron-pages { + padding: 0 32px; + } + </style> + <iron-pages attr-for-selected="is" + selected="[[visiblePageName_]]" + selected-item="{{visiblePage_}}"> + <template is="dom-if" if="[[shouldPasswordPageBeIncluded_(delegate)]]" + restamp> + <password-page auth-token="{{authToken_}}" + forward-button-disabled="{{passwordPageForwardButtonDisabled_}}" + password-field-valid="{{passwordFieldValid}}" + on-user-submitted-password="onUserSubmittedPassword_"> + </password-page> + </template> + <setup-failed-page></setup-failed-page> + <template is="dom-if" + if="[[shouldSetupSucceededPageBeIncluded_(delegate)]]" restamp> + <setup-succeeded-page></setup-succeeded-page> + </template> + <start-setup-page devices="[[devices_]]" + selected-device-id="{{selectedDeviceId_}}" + delegate="[[delegate]]"> + </start-setup-page> + </iron-pages> + <div class="flex"></div> + <button-bar forward-button-hidden="[[!forwardButtonText]]" + backward-button-hidden="[[!backwardButtonText]]" + cancel-button-hidden="[[!cancelButtonText]]"> + <slot name="backward-button" slot="backward-button"></slot> + <slot name="cancel-button" slot="cancel-button"></slot> + <slot name="forward-button" slot="forward-button"></slot> + </button-bar> + </template> + <script src="chrome://resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js"> + </script> +</dom-module> diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js new file mode 100644 index 00000000000..3dc0dd457d8 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup.js @@ -0,0 +1,312 @@ +// 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. + +cr.exportPath('multidevice_setup'); + +/** @enum {string} */ +multidevice_setup.PageName = { + FAILURE: 'setup-failed-page', + PASSWORD: 'password-page', + SUCCESS: 'setup-succeeded-page', + START: 'start-setup-page', +}; + +cr.define('multidevice_setup', function() { + const PageName = multidevice_setup.PageName; + + const MultiDeviceSetup = Polymer({ + is: 'multidevice-setup', + + behaviors: [WebUIListenerBehavior], + + properties: { + /** + * Delegate object which performs differently in OOBE vs. non-OOBE mode. + * @type {!multidevice_setup.MultiDeviceSetupDelegate} + */ + delegate: Object, + + /** + * Text to be shown on the forward navigation button. + * @type {string|undefined} + */ + forwardButtonText: { + type: String, + computed: 'getForwardButtonText_(visiblePage_)', + notify: true, + }, + + /** Whether the forward button should be disabled. */ + forwardButtonDisabled: { + type: Boolean, + computed: 'shouldForwardButtonBeDisabled_(' + + 'passwordPageForwardButtonDisabled_, visiblePageName_)', + notify: true + }, + + /** + * Text to be shown on the cancel button. + * @type {string|undefined} + */ + cancelButtonText: { + type: String, + computed: 'getCancelButtonText_(visiblePage_)', + notify: true, + }, + + /** + * Text to be shown on the backward navigation button. + * @type {string|undefined} + */ + backwardButtonText: { + type: String, + computed: 'getBackwardButtonText_(visiblePage_)', + notify: true, + }, + + /** + * Element name of the currently visible page. + * + * @private {!multidevice_setup.PageName} + */ + visiblePageName_: { + type: String, + value: PageName.START, + notify: true, // For testing purposes only. + }, + + /** + * DOM Element corresponding to the visible page. + * + * @private {!PasswordPageElement|!StartSetupPageElement| + * !SetupSucceededPageElement|!SetupFailedPageElement} + */ + visiblePage_: Object, + + /** + * Authentication token, which is generated by the password page. + * @private {string} + */ + authToken_: { + type: String, + }, + + /** + * Array of objects representing all potential MultiDevice hosts. + * + * @private {!Array<!chromeos.deviceSync.mojom.RemoteDevice>} + */ + devices_: Array, + + /** + * Unique identifier for the currently selected host device. + * + * Undefined if the no list of potential hosts has been received from mojo + * service. + * + * @private {string|undefined} + */ + selectedDeviceId_: String, + + /** + * Whether the password page reports that the forward button should be + * disabled. This field is only relevant when the password page is + * visible. + * @private {boolean} + */ + passwordPageForwardButtonDisabled_: Boolean, + + /** + * Provider of an interface to the MultiDeviceSetup Mojo service. + * @private {!multidevice_setup.MojoInterfaceProvider} + */ + mojoInterfaceProvider_: Object + }, + + listeners: { + 'backward-navigation-requested': 'onBackwardNavigationRequested_', + 'cancel-requested': 'onCancelRequested_', + 'forward-navigation-requested': 'onForwardNavigationRequested_', + }, + + /** @override */ + created: function() { + this.mojoInterfaceProvider_ = + multidevice_setup.MojoInterfaceProviderImpl.getInstance(); + }, + + /** @override */ + ready: function() { + this.addWebUIListener( + 'multidevice_setup.initializeSetupFlow', + this.initializeSetupFlow.bind(this)); + }, + + initializeSetupFlow: function() { + this.mojoInterfaceProvider_.getInterfacePtr() + .getEligibleHostDevices() + .then((responseParams) => { + if (responseParams.eligibleHostDevices.length == 0) { + console.warn('Potential host list is empty.'); + return; + } + + this.devices_ = responseParams.eligibleHostDevices; + }) + .catch((error) => { + console.warn('Mojo service failure: ' + error); + }); + }, + + /** @private */ + onCancelRequested_: function() { + this.exitSetupFlow_(); + }, + + /** @private */ + onBackwardNavigationRequested_: function() { + // The back button is only visible on the password page. + assert(this.visiblePageName_ == PageName.PASSWORD); + + this.$$('password-page').clearPasswordTextInput(); + this.visiblePageName_ = PageName.START; + }, + + /** @private */ + onForwardNavigationRequested_: function() { + if (this.forwardButtonDisabled) + return; + + this.visiblePage_.getCanNavigateToNextPage().then((canNavigate) => { + if (!canNavigate) + return; + this.navigateForward_(); + }); + }, + + /** @private */ + navigateForward_: function() { + switch (this.visiblePageName_) { + case PageName.FAILURE: + this.visiblePageName_ = PageName.START; + return; + case PageName.PASSWORD: + this.$$('password-page').clearPasswordTextInput(); + this.setHostDevice_(); + return; + case PageName.SUCCESS: + this.exitSetupFlow_(); + return; + case PageName.START: + if (this.delegate.isPasswordRequiredToSetHost()) + this.visiblePageName_ = PageName.PASSWORD; + else + this.setHostDevice_(); + return; + } + }, + + /** @private */ + setHostDevice_: function() { + // An authentication token must be set if a password is required. + assert(this.delegate.isPasswordRequiredToSetHost() == !!this.authToken_); + + let deviceId = /** @type {string} */ (this.selectedDeviceId_); + this.delegate.setHostDevice(deviceId, this.authToken_) + .then((responseParams) => { + if (!responseParams.success) { + console.warn('Failure setting host with device ID: ' + deviceId); + return; + } + + if (this.delegate.shouldExitSetupFlowAfterSettingHost()) { + this.exitSetupFlow_(); + return; + } + + this.visiblePageName_ = PageName.SUCCESS; + this.fire('forward-button-focus-requested'); + }) + .catch((error) => { + console.warn('Mojo service failure: ' + error); + }); + }, + + /** @private */ + onUserSubmittedPassword_: function() { + this.onForwardNavigationRequested_(); + }, + + /** + * @return {string|undefined} The forward button text, which is undefined + * if no button should be displayed. + * @private + */ + getForwardButtonText_: function() { + if (!this.visiblePage_) + return undefined; + return this.visiblePage_.forwardButtonText; + }, + + /** + * @return {boolean} Whether the forward button should be disabled. + * @private + */ + shouldForwardButtonBeDisabled_: function() { + return (this.visiblePageName_ == PageName.PASSWORD) && + this.passwordPageForwardButtonDisabled_; + }, + + /** + * @return {string|undefined} The cancel button text, which is undefined + * if no button should be displayed. + * @private + */ + getCancelButtonText_: function() { + if (!this.visiblePage_) + return undefined; + return this.visiblePage_.cancelButtonText; + }, + + /** + * @return {string|undefined} The backward button text, which is undefined + * if no button should be displayed. + * @private + */ + getBackwardButtonText_: function() { + if (!this.visiblePage_) + return undefined; + return this.visiblePage_.backwardButtonText; + }, + + /** + * @return {boolean} + * @private + */ + shouldPasswordPageBeIncluded_: function() { + return this.delegate.isPasswordRequiredToSetHost(); + }, + + /** + * @return {boolean} + * @private + */ + shouldSetupSucceededPageBeIncluded_: function() { + return !this.delegate.shouldExitSetupFlowAfterSettingHost(); + }, + + /** + * Notifies observers that the setup flow has completed. + * + * @private + */ + exitSetupFlow_: function() { + this.fire('setup-exited'); + }, + }); + + return { + MultiDeviceSetup: MultiDeviceSetup, + }; +}); diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup_browser_proxy.html b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup_browser_proxy.html new file mode 100644 index 00000000000..82f8d961c25 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup_browser_proxy.html @@ -0,0 +1,3 @@ +<link rel="import" href="chrome://resources/html/cr.html"> +<script src="chrome://resources/cr_components/chromeos/multidevice_setup/multidevice_setup_browser_proxy.js"> +</script> diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup_browser_proxy.js b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup_browser_proxy.js new file mode 100644 index 00000000000..be88d746277 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup_browser_proxy.js @@ -0,0 +1,41 @@ +// 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. + +cr.define('multidevice_setup', function() { + /** @interface */ + class BrowserProxy { + /** + * Requests profile information; namely, a dictionary containing the user's + * e-mail address and profile photo. + * @return {!Promise<{profilePhotoUrl: string, email: string}>} + */ + getProfileInfo() {} + + /** + * Opens settings to the MultiDevice individual feature settings subpage. + * (a.k.a. Connected Devices). + */ + openMultiDeviceSettings() {} + } + + /** @implements {multidevice_setup.BrowserProxy} */ + class BrowserProxyImpl { + /** @override */ + getProfileInfo() { + return cr.sendWithPromise('getProfileInfo'); + } + + /** @override */ + openMultiDeviceSettings() { + chrome.send('openMultiDeviceSettings'); + } + } + + cr.addSingletonGetter(BrowserProxyImpl); + + return { + BrowserProxy: BrowserProxy, + BrowserProxyImpl: BrowserProxyImpl, + }; +}); diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup_delegate.html b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup_delegate.html new file mode 100644 index 00000000000..46ce0b9a518 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup_delegate.html @@ -0,0 +1,4 @@ +<link rel="import" href="chrome://resources/html/cr.html"> +<script src="chrome://resources/cr_components/chromeos/multidevice_setup/multidevice_setup_delegate.js"> +</script> + diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup_delegate.js b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup_delegate.js new file mode 100644 index 00000000000..8526f38216b --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup_delegate.js @@ -0,0 +1,33 @@ +// 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. + +cr.define('multidevice_setup', function() { + /** + * Interface which provides the ability to set the host device and perform + * related logic. + * @interface + */ + class MultiDeviceSetupDelegate { + /** @return {boolean} */ + isPasswordRequiredToSetHost() {} + + /** + * @param {string} hostDeviceId The ID of the host to set. + * @param {string=} opt_authToken An auth token to authenticate the request; + * only necessary if isPasswordRequiredToSetHost() returns true. + * @return {!Promise<{success: boolean}>} + */ + setHostDevice(hostDeviceId, opt_authToken) {} + + /** @return {boolean} */ + shouldExitSetupFlowAfterSettingHost() {} + + /** @return {string} */ + getStartSetupCancelButtonTextId() {} + } + + return { + MultiDeviceSetupDelegate: MultiDeviceSetupDelegate, + }; +}); diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup_shared_css.html b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup_shared_css.html new file mode 100644 index 00000000000..c9403122d45 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/multidevice_setup_shared_css.html @@ -0,0 +1,19 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_elements/paper_button_style_css.html"> +<link rel="import" href="chrome://resources/cr_elements/shared_style_css.html"> +<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> +<link rel="import" href="chrome://resources/html/md_select_css.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout-classes.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout.html"> + +<dom-module id="multidevice-setup-shared"> + <template> + <style include="iron-flex paper-button-style cr-shared-style md-select"> + a { + color: var(--google-blue-600); + text-decoration: none; + } + </style> + </template> +</dom-module> diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/password_page.html b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/password_page.html new file mode 100644 index 00000000000..b5c52c0f573 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/password_page.html @@ -0,0 +1,51 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/multidevice_setup_shared_css.html"> +<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/ui_page.html"> +<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/ui_page_container_behavior.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_input/cr_input.html"> +<link rel="import" href="chrome://resources/html/cr.html"> + +<dom-module id="password-page"> + <template> + <style include="multidevice-setup-shared"> + #user-info-container { + @apply --layout-horizontal; + align-items: center; + color: var(--paper-grey-600); + } + + #profile-photo { + border-radius: 50%; + height: 20px; + margin-right: 8px; + width: 20px; + } + + #passwordInput { + height: 32px; + margin-top: 64px; + width: 560px; + } + </style> + <ui-page header-text="[[headerText]]" icon-name="google-g"> + <div id="content-container" slot="additional-content"> + <div id="user-info-container"> + <img id="profile-photo" src="[[profilePhotoUrl_]]"></img> + <span id="email">[[email_]]</span> + </div> + <cr-input id="passwordInput" type="password" + placeholder="[[i18n('enterPassword')]]" + invalid="[[passwordInvalid_]]" + error-message="[[i18n('wrongPassword')]]" + value="{{inputValue_}}" + aria-disabled="false" + on-keypress="onInputKeypress_" + autofocus> + </cr-input> + </div> + </ui-page> + </template> + <script src="chrome://resources/cr_components/chromeos/multidevice_setup/password_page.js"> + </script> +</dom-module> diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/password_page.js b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/password_page.js new file mode 100644 index 00000000000..ba2abe0bf6f --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/password_page.js @@ -0,0 +1,167 @@ +// 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. + +Polymer({ + is: 'password-page', + + behaviors: [ + UiPageContainerBehavior, + ], + + properties: { + /** + * Whether forward button should be disabled. In this context, the forward + * button should be disabled if the user has not entered a password or if + * the user has submitted an incorrect password and has not yet edited it. + * @type {boolean} + */ + forwardButtonDisabled: { + type: Boolean, + computed: 'shouldForwardButtonBeDisabled_(' + + 'inputValue_, passwordInvalid_, waitingForPasswordCheck_)', + notify: true, + }, + + /** Overridden from UiPageContainerBehavior. */ + forwardButtonTextId: { + type: String, + value: 'done', + }, + + /** Overridden from UiPageContainerBehavior. */ + cancelButtonTextId: { + type: String, + value: 'cancel', + }, + + /** Overridden from UiPageContainerBehavior. */ + backwardButtonTextId: { + type: String, + value: 'back', + }, + + /** Overridden from UiPageContainerBehavior. */ + headerId: { + type: String, + value: 'passwordPageHeader', + }, + + /** + * Authentication token; retrieved using the quickUnlockPrivate API. + * @type {string} + */ + authToken: { + type: String, + value: '', + notify: true, + }, + + /** @private {string} */ + profilePhotoUrl_: { + type: String, + value: '', + }, + + /** @private {string} */ + email_: { + type: String, + value: '', + }, + + /** @private {!QuickUnlockPrivate} */ + quickUnlockPrivate_: { + type: Object, + value: chrome.quickUnlockPrivate, + }, + + /** @private {string} */ + inputValue_: { + type: String, + value: '', + observer: 'onInputValueChange_', + }, + + /** @private {boolean} */ + passwordInvalid_: { + type: Boolean, + value: false, + }, + + /** @private {boolean} */ + waitingForPasswordCheck_: { + type: Boolean, + value: false, + }, + }, + + /** @private {?multidevice_setup.BrowserProxy} */ + browserProxy_: null, + + clearPasswordTextInput: function() { + this.$.passwordInput.value = ''; + }, + + /** @override */ + created: function() { + this.browserProxy_ = multidevice_setup.BrowserProxyImpl.getInstance(); + }, + + /** @override */ + attached: function() { + this.browserProxy_.getProfileInfo().then((profileInfo) => { + this.profilePhotoUrl_ = profileInfo.profilePhotoUrl; + this.email_ = profileInfo.email; + }); + }, + + /** Overridden from UiPageContainerBehavior. */ + getCanNavigateToNextPage: function() { + return new Promise((resolve) => { + if (this.waitingForPasswordCheck_) { + resolve(false /* canNavigate */); + return; + } + this.waitingForPasswordCheck_ = true; + this.quickUnlockPrivate_.getAuthToken(this.inputValue_, (tokenInfo) => { + this.waitingForPasswordCheck_ = false; + if (chrome.runtime.lastError) { + this.passwordInvalid_ = true; + // Select the password text if the user entered an incorrect password. + this.$.passwordInput.select(); + resolve(false /* canNavigate */); + return; + } + this.authToken = tokenInfo.token; + this.passwordInvalid_ = false; + resolve(true /* canNavigate */); + }); + }); + }, + + /** @private */ + onInputValueChange_: function() { + this.passwordInvalid_ = false; + }, + + /** + * @param {!Event} e + * @private + */ + onInputKeypress_: function(e) { + // We are only listening for the user trying to enter their password. + if (e.key != 'Enter') + return; + + this.fire('user-submitted-password'); + }, + + /** + * @return {boolean} Whether the forward button should be disabled. + * @private + */ + shouldForwardButtonBeDisabled_: function() { + return this.passwordInvalid_ || !this.inputValue_ || + this.waitingForPasswordCheck_; + }, +}); diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_failed_page.html b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_failed_page.html new file mode 100644 index 00000000000..181687dffd7 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_failed_page.html @@ -0,0 +1,18 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/ui_page.html"> +<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/ui_page_container_behavior.html"> +<link rel="import" href="chrome://resources/html/cr.html"> + +<dom-module id="setup-failed-page"> + <template> + <ui-page header-text="[[headerText]]" icon-name="error-icon"> + <span slot="message" inner-h-t-m-l="[[messageHtml]]"></span> + <div slot="additional-content"> + This is empty... (PlAcEhOlDeR tExT!!) + </div> + </ui-page> + </template> + <script src="chrome://resources/cr_components/chromeos/multidevice_setup/setup_failed_page.js"> + </script> +</dom-module> diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_failed_page.js b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_failed_page.js new file mode 100644 index 00000000000..36f2d4ae117 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_failed_page.js @@ -0,0 +1,43 @@ +// 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. + +Polymer({ + is: 'setup-failed-page', + + properties: { + /** Overridden from UiPageContainerBehavior. */ + forwardButtonTextId: { + type: String, + value: 'tryAgain', + }, + + /** Overridden from UiPageContainerBehavior. */ + cancelButtonTextId: { + type: String, + value: 'cancel', + }, + + /** Overridden from UiPageContainerBehavior. */ + backwardButtonTextId: { + type: String, + value: 'back', + }, + + /** Overridden from UiPageContainerBehavior. */ + headerId: { + type: String, + value: 'setupFailedPageHeader', + }, + + /** Overridden from UiPageContainerBehavior. */ + messageId: { + type: String, + value: 'setupFailedPageMessage', + }, + }, + + behaviors: [ + UiPageContainerBehavior, + ], +}); diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_icon_1x.png b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_icon_1x.png Binary files differnew file mode 100644 index 00000000000..03074bd5735 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_icon_1x.png diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_icon_2x.png b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_icon_2x.png Binary files differnew file mode 100644 index 00000000000..271b0484e3e --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_icon_2x.png diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_page.html b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_page.html new file mode 100644 index 00000000000..a899610a5d0 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_page.html @@ -0,0 +1,35 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/multidevice_setup_browser_proxy.html"> +<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/multidevice_setup_shared_css.html"> +<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/ui_page.html"> +<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/ui_page_container_behavior.html"> +<link rel="import" href="chrome://resources/html/cr.html"> + +<dom-module id="setup-succeeded-page"> + <template> + <style include="multidevice-setup-shared"> + #page-icon-container { + @apply --layout-horizontal; + @apply --layout-center-justified; + } + + #page-icon { + background-image: -webkit-image-set( + url(setup_succeeded_icon_1x.png) 1x, + url(setup_succeeded_icon_2x.png) 2x); + height: 156px; + margin-top: 64px; + width: 416px; + } + </style> + <ui-page header-text="[[headerText]]" icon-name="google-g"> + <span slot="message" inner-h-t-m-l="[[messageHtml]]"></span> + <div id="page-icon-container" slot="additional-content"> + <div id="page-icon"></div> + </div> + </ui-page> + </template> + <script src="chrome://resources/cr_components/chromeos/multidevice_setup/setup_succeeded_page.js"> + </script> +</dom-module> diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_page.js b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_page.js new file mode 100644 index 00000000000..e9c4eac1bc1 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/setup_succeeded_page.js @@ -0,0 +1,59 @@ +// 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. + +cr.exportPath('multidevice_setup'); + +Polymer({ + is: 'setup-succeeded-page', + + properties: { + /** Overridden from UiPageContainerBehavior. */ + forwardButtonTextId: { + type: String, + value: 'done', + }, + + /** Overridden from UiPageContainerBehavior. */ + headerId: { + type: String, + value: 'setupSucceededPageHeader', + }, + + /** Overridden from UiPageContainerBehavior. */ + messageId: { + type: String, + value: 'setupSucceededPageMessage', + }, + }, + + behaviors: [ + UiPageContainerBehavior, + ], + + /** @private {?multidevice_setup.BrowserProxy} */ + browserProxy_: null, + + /** @override */ + created: function() { + this.browserProxy_ = multidevice_setup.BrowserProxyImpl.getInstance(); + }, + + /** @private */ + openSettings_: function() { + this.browserProxy_.openMultiDeviceSettings(); + }, + + /** @private */ + onSettingsLinkClicked_: function() { + this.openSettings_(); + this.fire('setup-exited'); + }, + + /** @override */ + ready: function() { + let linkElement = this.$$('#settings-link'); + linkElement.setAttribute('href', '#'); + linkElement.addEventListener('click', () => this.onSettingsLinkClicked_()); + }, +}); diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_icon_1x.png b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_icon_1x.png Binary files differnew file mode 100644 index 00000000000..b6a3a196cb8 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_icon_1x.png diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_icon_2x.png b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_icon_2x.png Binary files differnew file mode 100644 index 00000000000..c7234e8f191 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_icon_2x.png diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.html b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.html new file mode 100644 index 00000000000..a410816bbf7 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.html @@ -0,0 +1,135 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/icons.html"> +<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/multidevice_setup_shared_css.html"> +<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/ui_page.html"> +<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/ui_page_container_behavior.html"> +<link rel="import" href="chrome://resources/html/cr.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/html/web_ui_listener_behavior.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html"> + +<dom-module id="start-setup-page"> + <template> + <style include="multidevice-setup-shared"> + #selector-and-details-container { + @apply --layout-horizontal; + margin-top: 48px; + min-height: 246px; + } + + #singleDeviceName { + color: var(--google-grey-900); + margin-top: 16px; + } + + #deviceDropdown { + margin-top: 16px; + } + + #page-icon-container { + @apply --layout-horizontal; + @apply --layout-center-justified; + } + + #page-icon { + background-image: -webkit-image-set(url(start_setup_icon_1x.png) 1x, + url(start_setup_icon_2x.png) 2x); + height: 116px; + margin-top: 10px; + width: 320px; + } + + #deviceSelectionContainer { + color: var(--paper-grey-600); + } + + #feature-details-container { + @apply --layout-vertical; + @apply --layout-center-justified; + border-left: 1px solid rgb(218, 220, 224); + padding-left: 24px; + } + + #feature-details-container-header { + margin-bottom: 18px; + } + + .feature-detail { + @apply --layout-horizontal; + @apply --layout-center; + box-sizing: border-box; + min-height: 64px; + padding: 10px 0; + } + + .feature-detail iron-icon { + --iron-icon-height: 20px; + --iron-icon-width: 20px; + min-width: 20px; + } + + .feature-detail span { + margin-left: 8px; + } + + #footnote { + color: var(--paper-grey-600); + margin-top: 12px; + } + </style> + + <ui-page header-text="[[headerText]]" icon-name="google-g"> + <span slot="message" id="multidevice-summary-message" inner-h-t-m-l="[[messageHtml]]"></span> + <div slot="additional-content"> + <div id="selector-and-details-container"> + <div id="deviceSelectionContainer" class="flex"> + [[getDeviceSelectionHeader_(devices)]] + <div class="flex"></div> + <div id="singleDeviceName" + hidden$="[[!doesDeviceListHaveOneElement_(devices)]]"> + [[getFirstDeviceNameInList_(devices)]] + </div> + <div hidden$="[[!doesDeviceListHaveMultipleElements_(devices)]]"> + <select id="deviceDropdown" + class="md-select" + on-change="onDeviceDropdownSelectionChanged_"> + <template is="dom-repeat" items="[[devices]]"> + <option value$="[[item.deviceId]]"> + [[item.deviceName]] + </option> + </template> + </select> + </div> + <div id="page-icon-container"> + <div id="page-icon"></div> + </div> + </div> + <div id="feature-details-container" class="flex"> + <div id="feature-details-container-header"> + [[i18n('startSetupPageFeatureListHeader')]] + </div> + <div class="feature-detail"> + <iron-icon icon="multidevice-setup-icons-20:messages"></iron-icon> + <span id="awm-summary-message" inner-h-t-m-l=" + [[i18nAdvanced('startSetupPageFeatureListAwm')]]"> + </span> + </div> + <div class="feature-detail"> + <iron-icon icon="multidevice-setup-icons-20:downloads"> + </iron-icon> + <span>[[i18n('startSetupPageFeatureListInstallApps')]]</span> + </div> + <div class="feature-detail"> + <iron-icon icon="multidevice-setup-icons-20:features"></iron-icon> + <span>[[i18n('startSetupPageFeatureListAddFeatures')]]</span> + </div> + </div> + </div> + <div id="footnote">[[i18n('startSetupPageFootnote')]]</div> + </div> + </ui-page> + </template> + <script src="chrome://resources/cr_components/chromeos/multidevice_setup/start_setup_page.js"> + </script> +</dom-module> diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.js b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.js new file mode 100644 index 00000000000..d9f6db488e7 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/start_setup_page.js @@ -0,0 +1,155 @@ +// 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. + +Polymer({ + is: 'start-setup-page', + + properties: { + /** Overridden from UiPageContainerBehavior. */ + forwardButtonTextId: { + type: String, + value: 'accept', + }, + + /** Overridden from UiPageContainerBehavior. */ + cancelButtonTextId: { + type: String, + computed: 'getCancelButtonTextId_(delegate)', + }, + + /** Overridden from UiPageContainerBehavior. */ + headerId: { + type: String, + value: 'startSetupPageHeader', + }, + + /** Overridden from UiPageContainerBehavior. */ + messageId: { + type: String, + value: 'startSetupPageMessage', + }, + + /** + * Array of objects representing all potential MultiDevice hosts. + * + * @type {!Array<!chromeos.deviceSync.mojom.RemoteDevice>} + */ + devices: { + type: Array, + value: () => [], + observer: 'devicesChanged_', + }, + + /** + * Unique identifier for the currently selected host device. + * + * Undefined if the no list of potential hosts has been received from mojo + * service. + * + * @type {string|undefined} + */ + selectedDeviceId: { + type: String, + notify: true, + }, + + /** + * Delegate object which performs differently in OOBE vs. non-OOBE mode. + * @type {!multidevice_setup.MultiDeviceSetupDelegate} + */ + delegate: Object, + }, + + behaviors: [ + UiPageContainerBehavior, + I18nBehavior, + WebUIListenerBehavior, + ], + + /** @override */ + attached: function() { + this.addWebUIListener( + 'multidevice_setup.initializeSetupFlow', + this.initializeSetupFlow_.bind(this)); + }, + + /** @private */ + initializeSetupFlow_: function() { + // The "Learn More" links are inside a grdp string, so we cannot actually + // add an onclick handler directly to the html. Instead, grab the two and + // manaully add onclick handlers. + let helpArticleLinks = [ + this.$$('#multidevice-summary-message a'), + this.$$('#awm-summary-message a') + ]; + for (let i = 0; i < helpArticleLinks.length; i++) { + helpArticleLinks[i].onclick = this.fire.bind( + this, 'open-learn-more-webview-requested', helpArticleLinks[i].href); + } + }, + + /** + * @param {!multidevice_setup.MultiDeviceSetupDelegate} delegate + * @return {string} The cancel button text ID, dependent on OOBE vs. non-OOBE. + * @private + */ + getCancelButtonTextId_: function(delegate) { + return this.delegate.getStartSetupCancelButtonTextId(); + }, + + /** + * @param {!Array<!chromeos.deviceSync.mojom.RemoteDevice>} devices + * @return {string} Label for devices selection content. + * @private + */ + getDeviceSelectionHeader_(devices) { + switch (devices.length) { + case 0: + return ''; + case 1: + return this.i18n('startSetupPageSingleDeviceHeader'); + default: + return this.i18n('startSetupPageMultipleDeviceHeader'); + } + }, + + /** + * @param {!Array<!chromeos.deviceSync.mojom.RemoteDevice>} devices + * @return {boolean} True if there are more than one potential host devices. + * @private + */ + doesDeviceListHaveMultipleElements_: function(devices) { + return devices.length > 1; + }, + + /** + * @param {!Array<!chromeos.deviceSync.mojom.RemoteDevice>} devices + * @return {boolean} True if there is exactly one potential host device. + * @private + */ + doesDeviceListHaveOneElement_: function(devices) { + return devices.length == 1; + }, + + /** + * @param {!Array<!chromeos.deviceSync.mojom.RemoteDevice>} devices + * @return {string} Name of the first device in device list if there are any. + * Returns an empty string otherwise. + * @private + */ + getFirstDeviceNameInList_: function(devices) { + return devices[0] ? this.devices[0].deviceName : ''; + }, + + /** @private */ + devicesChanged_: function() { + if (this.devices.length > 0) + this.selectedDeviceId = this.devices[0].deviceId; + }, + + /** @private */ + onDeviceDropdownSelectionChanged_: function() { + this.selectedDeviceId = this.$.deviceDropdown.value; + }, +}); diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/ui_page.html b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/ui_page.html new file mode 100644 index 00000000000..289e08644b8 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/ui_page.html @@ -0,0 +1,42 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/polymer/v1_0/iron-icon/iron-icon.html"> +<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/icons.html"> +<link rel="import" href="chrome://resources/cr_components/chromeos/multidevice_setup/multidevice_setup_shared_css.html"> + +<dom-module id="ui-page"> + <template> + <style include="multidevice-setup-shared"> + iron-icon { + --iron-icon-width: 32px; + --iron-icon-height: 32px; + } + + h1 { + color: var(--google-grey-900); + font-family: 'Google Sans'; + font-size: 28px; + font-weight: normal; + line-height: 28px; + margin: 0; + padding-top: 36px; + } + + #message-container { + box-sizing: border-box; + min-height: 32px; + padding-top: 16px; + } + </style> + <iron-icon icon="[[computeIconIdentifier_(iconName)]]"></iron-icon> + <h1>[[headerText]]</h1> + <div id="message-container"> + <slot name="message"></slot> + </div> + <div id="additional-content-container"> + <slot name="additional-content"></slot> + </div> + </template> + <script src="chrome://resources/cr_components/chromeos/multidevice_setup/ui_page.js"> + </script> +</dom-module> diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/ui_page.js b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/ui_page.js new file mode 100644 index 00000000000..d29f7722d10 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/ui_page.js @@ -0,0 +1,34 @@ +// 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. + +/** + * An element that encapsulates the structure common to all pages in the WebUI. + */ +Polymer({ + is: 'ui-page', + + properties: { + /** + * Main heading for the page. + * + * @type {string} + */ + headerText: String, + + /** + * Name of icon within icon set. + * + * @type {string} + */ + iconName: String, + }, + + /** + * @return {string} + * @private + */ + computeIconIdentifier_: function() { + return 'multidevice-setup-icons-32:' + this.iconName; + }, +}); diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/ui_page_container_behavior.html b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/ui_page_container_behavior.html new file mode 100644 index 00000000000..501cc374471 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/ui_page_container_behavior.html @@ -0,0 +1,5 @@ +<link rel="import" href="chrome://resources/html/cr.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> + +<script src="chrome://resources/cr_components/chromeos/multidevice_setup/ui_page_container_behavior.js"> +</script> diff --git a/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/ui_page_container_behavior.js b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/ui_page_container_behavior.js new file mode 100644 index 00000000000..e9f16b9ee89 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/multidevice_setup/ui_page_container_behavior.js @@ -0,0 +1,141 @@ +// 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. + +/** @polymerBehavior */ +const UiPageContainerBehaviorImpl = { + properties: { + /** + * ID for forward button label, which must be translated for display. + * + * Undefined if the visible page has no forward-navigation button. + * + * @type {string|undefined} + */ + forwardButtonTextId: String, + + /** + * ID for cancel button label, which must be translated for display. + * + * Undefined if the visible page has no cancel button. + * + * @type {string|undefined} + */ + cancelButtonTextId: String, + + /** + * ID for backward button label, which must be translated for display. + * + * Undefined if the visible page has no backward-navigation button. + * + * @type {string|undefined} + */ + backwardButtonTextId: String, + + /** + * ID for text of main UI Page heading. + * + * @type {string} + */ + headerId: String, + + /** + * ID for text of main UI Page message body. + * + * @type {string} + */ + messageId: String, + + /** + * Translated text to display on the forward-naviation button. + * + * Undefined if the visible page has no forward-navigation button. + * + * @type {string|undefined} + */ + forwardButtonText: { + type: String, + computed: 'computeLocalizedText_(forwardButtonTextId)', + }, + + /** + * Translated text to display on the cancel button. + * + * Undefined if the visible page has no cancel button. + * + * @type {string|undefined} + */ + cancelButtonText: { + type: String, + computed: 'computeLocalizedText_(cancelButtonTextId)', + }, + + /** + * Translated text to display on the backward-naviation button. + * + * Undefined if the visible page has no backward-navigation button. + * + * @type {string|undefined} + */ + backwardButtonText: { + type: String, + computed: 'computeLocalizedText_(backwardButtonTextId)', + }, + + /** + * Translated text of main UI Page heading. + * + * @type {string|undefined} + */ + headerText: { + type: String, + computed: 'computeLocalizedText_(headerId)', + }, + + /** + * Translated text of main UI Page heading. In general this can include + * some markup. + * + * @type {string|undefined} + */ + messageHtml: { + type: String, + computed: 'computeLocalizedText_(messageId)', + }, + }, + + /** + * Returns a promise which always resolves and returns a boolean representing + * whether it should be possible to navigate forward. This function is called + * before forward navigation is requested; if false is returned, the active + * page does not change. + * @return {!Promise} + */ + getCanNavigateToNextPage: function() { + return new Promise((resolve) => { + resolve(true /* canNavigate */); + }); + }, + + /** + * @param {string} textId Key for the localized string to appear on a + * button. + * @return {string|undefined} The localized string corresponding to the key + * textId. Return value is undefined if textId is not a key + * for any localized string. Note: this includes the case in which + * textId is undefined. + * @private + */ + computeLocalizedText_: function(textId) { + if (!this.i18nExists(textId)) + return; + + return loadTimeData.getString(textId); + }, +}; + +/** @polymerBehavior */ +const UiPageContainerBehavior = [ + I18nBehavior, + UiPageContainerBehaviorImpl, +]; diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_config.html b/chromium/ui/webui/resources/cr_components/chromeos/network/network_config.html index 4c506f20691..f6053ce9367 100644 --- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_config.html +++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_config.html @@ -154,7 +154,7 @@ <cr-toggle id="share" checked="{{shareNetwork_}}" disabled="[[!shareIsEnabled_(guid, configProperties_.*, security_, eapProperties_.*, shareAllowEnable)]]" - aria-label="[[i18n('networkConfigShare')]]"> + aria-labeledby="shareLabel"> </cr-toggle> </div> </template> diff --git a/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy.js b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy.js index b5dc5c2bbc2..c827a0ddfe5 100644 --- a/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy.js +++ b/chromium/ui/webui/resources/cr_components/chromeos/network/network_proxy.js @@ -191,7 +191,8 @@ Polymer({ (CrOnc.proxyMatches(jsonHttp, proxy.Manual.SecureHTTPProxy) && CrOnc.proxyMatches(jsonHttp, proxy.Manual.FTPProxy) && CrOnc.proxyMatches(jsonHttp, proxy.Manual.SOCKS)) || - (!proxy.Manual.SecureHTTPProxy.Host && + (!proxy.Manual.HTTPProxy.Host && + !proxy.Manual.SecureHTTPProxy.Host && !proxy.Manual.FTPProxy.Host && !proxy.Manual.SOCKS.Host); } if (proxySettings.ExcludeDomains) { diff --git a/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/BUILD.gn b/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/BUILD.gn index a024915b51f..afe7eb05b32 100644 --- a/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/BUILD.gn +++ b/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/BUILD.gn @@ -7,6 +7,7 @@ import("//third_party/closure_compiler/compile_js.gni") js_type_check("closure_compile") { deps = [ ":pin_keyboard", + ":setup_pin_keyboard", ] } @@ -17,3 +18,21 @@ js_library("pin_keyboard") { "//ui/webui/resources/js:i18n_behavior", ] } + +js_library("lock_screen_constants") { + deps = [ + "//ui/webui/resources/cr_elements/cr_profile_avatar_selector:cr_profile_avatar_selector", + "//ui/webui/resources/js:cr", + ] +} + +js_library("setup_pin_keyboard") { + deps = [ + ":lock_screen_constants", + ":pin_keyboard", + "//ui/webui/resources/cr_components/chromeos/quick_unlock:lock_screen_constants", + "//ui/webui/resources/js:i18n_behavior", + ] + externs_list = [ "$externs_path/quick_unlock_private.js" ] + extra_sources = [ "$interfaces_path/quick_unlock_private_interface.js" ] +} diff --git a/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.html b/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.html new file mode 100644 index 00000000000..29c7f5cb64f --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.html @@ -0,0 +1 @@ +<script src="chrome://resources/cr_components/chromeos/quick_unlock/lock_screen_constants.js"></script> diff --git a/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.js b/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.js new file mode 100644 index 00000000000..e5ee46e6d1a --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/lock_screen_constants.js @@ -0,0 +1,47 @@ +// 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. + +/** + * @fileoverview Constants used for logging the pin unlock setup uma. + */ + +/** + * Name of the pin unlock setup uma histogram. + * @type {string} + */ +const PinUnlockUmaHistogramName = 'Settings.PinUnlockSetup'; + +/** + * Stages the user can enter while setting up pin unlock. + * @enum {number} + */ +const LockScreenProgress = { + START_SCREEN_LOCK: 0, + ENTER_PASSWORD_CORRECTLY: 1, + CHOOSE_PIN_OR_PASSWORD: 2, + ENTER_PIN: 3, + CONFIRM_PIN: 4, + MAX_BUCKET: 5 +}; + +cr.define('settings', function() { + /** + * Helper function to send the progress of the pin setup to be recorded in the + * histogram. + * @param {LockScreenProgress} currentProgress + */ + const recordLockScreenProgress = function(currentProgress) { + if (currentProgress >= LockScreenProgress.MAX_BUCKET) { + console.error( + 'Expected a enumeration value of ' + LockScreenProgress.MAX_BUCKET + + ' or lower: Received ' + currentProgress + '.'); + return; + } + chrome.send('metricsHandler:recordInHistogram', [ + PinUnlockUmaHistogramName, currentProgress, LockScreenProgress.MAX_BUCKET + ]); + }; + + return {recordLockScreenProgress: recordLockScreenProgress}; +}); diff --git a/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html b/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html index f28f89b9e06..080f3cc0328 100644 --- a/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html +++ b/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.html @@ -36,16 +36,41 @@ <template> <style include="cr-shared-style"> :host { + --backspace-button-ripple-left: calc((var(--backspace-button-width) - + var(--pin-button-ripple-width)) / 2); + --backspace-button-width: calc(var(--pin-button-width) + + var(--pin-button-horizontal-margin) * 2); + --pin-button-height: 40px; + --pin-button-horizontal-margin: 20px; + --pin-button-ripple-height: 48px; + --pin-button-ripple-left: calc((var(--pin-button-width) - + var(--pin-button-ripple-width)) / 2); + --pin-button-ripple-top: calc((var(--pin-button-height) - + var(--pin-button-ripple-height)) / 2); + --pin-button-ripple-width: 48px; + --pin-button-vertical-margin: 8px; + --pin-button-width: 40px; outline: none; } #root { + align-items: center; + display: flex; + flex-direction: column; + min-height: 0; + } + + #rowsContainer { direction: ltr; display: block; + width: calc((var(--pin-button-width) + + var(--pin-button-horizontal-margin) * 2) * 3); } .row { display: flex; + margin-bottom: calc(var(--pin-button-vertical-margin) * 2); + min-height: 0; } :host([enable-password]) #pinInputDiv { @@ -57,10 +82,10 @@ } #backspaceButton { - color: var(--pin-keyboard-backspace-color, #000); + color: var(--pin-keyboard-backspace-color, var(--google-grey-700)); left: 0; opacity: var(--pin-keyboard-backspace-opacity, --dark-primary-opacity); - padding: 14px; + padding: 12px; position: absolute; top: 0; } @@ -71,75 +96,93 @@ #backspaceButtonContainer { position: relative; + width: var(--backspace-button-width); } #backspaceButtonContainer paper-ripple { - left: var(--pin-keyboard-backspace-paper-ripple-offset, 0); - top: var(--pin-keyboard-backspace-paper-ripple-offset, 0); + left: var(--pin-keyboard-backspace-paper-ripple-offset, + var(--backspace-button-ripple-left)); + top: var(--pin-keyboard-backspace-paper-ripple-offset, + var(--pin-button-ripple-top)); } .digit-button { + --paper-button: { + min-width: 0; + }; align-items: center; background: none; border-radius: 0; box-sizing: border-box; - color: #000; + color: var(--google-grey-900); display: flex; flex-direction: column; - height: 48px; + height: var(--pin-button-height) justify-content: center; - margin: 0; - min-height: 48px; - min-width: 48px; + margin: 0 var(--pin-button-horizontal-margin); + min-height: 0; opacity: 0.87px; - width: 60px; + padding: 0; + width: var(--pin-button-width); @apply --pin-keyboard-digit-button; } .digit-button inner-text { - display: flex; - flex-direction: column; font-family: 'Roboto'; } - .letter { - color: var(--pin-keyboard-letter-color, --paper-blue-grey-700); - font-size: 9px; - margin-top: 4px; + inner-text.letter { + color: var(--pin-keyboard-letter-color, var(--google-grey-700)); + font-size: 12px; + margin-top: 8px; + + @apply --pin-keyboard-digit-button-letter; } .number { - color: var(--pin-keyboard-number-color, --paper-blue-grey-700); - font-size: 20px; - height: 52px; + color: var(--pin-keyboard-number-color, var(--paper-blue-grey-700)); + font-size: 18px; + height: 16px; } paper-ripple { color: var(--google-grey-700); - height: 48px; - left: 6px; - width: 48px; + height: var(--pin-button-ripple-height); + left: var(--pin-button-ripple-left); + top: var(--pin-button-ripple-top); + width: var(--pin-button-ripple-width); @apply --pin-keyboard-paper-ripple; } #pinInput { + --cr-input-error-display: none; + --cr-input-input: { + font-size: 28px; + letter-spacing: 28px; + }; + --cr-input-padding-bottom: 1px; + --cr-input-padding-end: 0; + --cr-input-padding-start: 0; + --cr-input-padding-top: 1px; background-color: white; border: 0; box-sizing: border-box; font-face: Roboto-Regular; font-size: 13px; - height: 43px; left: 0; opacity: var(--dark-secondary-opacity); outline: 0; position: relative; text-align: center; - width: 180px; + width: 200px; + + @apply --pin-keyboard-pin-input-style; } #pinInput[has-content] { + --cr-disabled-opacity: var(--dark-primary-opacity); opacity: var(--dark-primary-opacity); } @@ -155,93 +198,98 @@ </style> <div id="root" on-contextmenu="onContextMenu_" on-tap="focusInput_"> - <div id="pinInputDiv" class="row"> + <div id="pinInputDiv"> <cr-input id="pinInput" type="password" value="{{value}}" is-input-rtl$="[[isInputRtl_(value)]]" has-content$="[[hasInput_(value)]]" invalid="[[hasError]]" - placeholder="[[getInputPlaceholder_(enablePassword)]]" - on-keydown="onInputKeyDown_"> + placeholder="[[getInputPlaceholder_(enablePassword, + enablePlaceholder)]]" + on-keydown="onInputKeyDown_" force-underline$="[[forceUnderline_]]" + disabled="[[isIncognitoUi]]"> </cr-input> </div> <slot select="[problem]"></slot> - <div class="row"> - <paper-button class="digit-button" on-tap="onNumberTap_" value="1" - noink> - <inner-text class="number">[[i18n('pinKeyboard1')]]</inner-text> - <paper-ripple class="circle" center></paper-ripple> - </paper-button> - <paper-button class="digit-button" on-tap="onNumberTap_" value="2" - noink> - <inner-text class="number">[[i18n('pinKeyboard2')]]</inner-text> - <inner-text class="letter">ABC</inner-text> - <paper-ripple class="circle" center></paper-ripple> - </paper-button> - <paper-button class="digit-button" on-tap="onNumberTap_" value="3" - noink> - <inner-text class="number">[[i18n('pinKeyboard3')]]</inner-text> - <inner-text class="letter">DEF</inner-text> - <paper-ripple class="circle" center></paper-ripple> - </paper-button> - </div> - <div class="row"> - <paper-button class="digit-button" on-tap="onNumberTap_" value="4" - noink> - <inner-text class="number">[[i18n('pinKeyboard4')]]</inner-text> - <inner-text class="letter">GHI</inner-text> - <paper-ripple class="circle" center></paper-ripple> - </paper-button> - <paper-button class="digit-button" on-tap="onNumberTap_" value="5" - noink> - <inner-text class="number">[[i18n('pinKeyboard5')]]</inner-text> - <inner-text class="letter">JKL</inner-text> - <paper-ripple class="circle" center></paper-ripple> - </paper-button> - <paper-button class="digit-button" on-tap="onNumberTap_" value="6" - noink> - <inner-text class="number">[[i18n('pinKeyboard6')]]</inner-text> - <inner-text class="letter">MNO</inner-text> - <paper-ripple class="circle" center></paper-ripple> - </paper-button> - </div> - <div class="row"> - <paper-button class="digit-button" on-tap="onNumberTap_" value="7" - noink> - <inner-text class="number">[[i18n('pinKeyboard7')]]</inner-text> - <inner-text class="letter">PQRS</inner-text> - <paper-ripple class="circle" center></paper-ripple> - </paper-button> - <paper-button class="digit-button" on-tap="onNumberTap_" value="8" - noink> - <inner-text class="number">[[i18n('pinKeyboard8')]]</inner-text> - <inner-text class="letter">TUV</inner-text> - <paper-ripple class="circle" center></paper-ripple> - </paper-button> - <paper-button class="digit-button" on-tap="onNumberTap_" value="9" - noink> - <inner-text class="number">[[i18n('pinKeyboard9')]]</inner-text> - <inner-text class="letter">WXYZ</inner-text> - <paper-ripple class="circle" center></paper-ripple> - </paper-button> - </div> - <div class="row bottom-row"> - <div class="digit-button"></div> - <paper-button class="digit-button" on-tap="onNumberTap_" value="0" - noink> - <inner-text class="number">[[i18n('pinKeyboard0')]]</inner-text> - <inner-text class="letter">+</inner-text> - <paper-ripple class="circle" center></paper-ripple> - </paper-button> - <div id="backspaceButtonContainer"> - <paper-icon-button id="backspaceButton" class="digit-button" - disabled$="[[!hasInput_(value)]]" - icon="pin-keyboard:backspace" - on-pointerdown="onBackspacePointerDown_" - on-pointerout="clearAndReset_" - on-pointerup="onBackspacePointerUp_" - title="[[i18n('pinKeyboardDeleteAccessibleName')]]" + <div id="rowsContainer"> + <div class="row"> + <paper-button class="digit-button" on-tap="onNumberTap_" value="1" + noink> + <inner-text class="number">[[i18n('pinKeyboard1')]]</inner-text> + <inner-text class="letter"> </inner-text> + <paper-ripple class="circle" center></paper-ripple> + </paper-button> + <paper-button class="digit-button" on-tap="onNumberTap_" value="2" + noink> + <inner-text class="number">[[i18n('pinKeyboard2')]]</inner-text> + <inner-text class="letter">ABC</inner-text> + <paper-ripple class="circle" center></paper-ripple> + </paper-button> + <paper-button class="digit-button" on-tap="onNumberTap_" value="3" + noink> + <inner-text class="number">[[i18n('pinKeyboard3')]]</inner-text> + <inner-text class="letter">DEF</inner-text> + <paper-ripple class="circle" center></paper-ripple> + </paper-button> + </div> + <div class="row"> + <paper-button class="digit-button" on-tap="onNumberTap_" value="4" + noink> + <inner-text class="number">[[i18n('pinKeyboard4')]]</inner-text> + <inner-text class="letter">GHI</inner-text> + <paper-ripple class="circle" center></paper-ripple> + </paper-button> + <paper-button class="digit-button" on-tap="onNumberTap_" value="5" + noink> + <inner-text class="number">[[i18n('pinKeyboard5')]]</inner-text> + <inner-text class="letter">JKL</inner-text> + <paper-ripple class="circle" center></paper-ripple> + </paper-button> + <paper-button class="digit-button" on-tap="onNumberTap_" value="6" + noink> + <inner-text class="number">[[i18n('pinKeyboard6')]]</inner-text> + <inner-text class="letter">MNO</inner-text> + <paper-ripple class="circle" center></paper-ripple> + </paper-button> + </div> + <div class="row"> + <paper-button class="digit-button" on-tap="onNumberTap_" value="7" + noink> + <inner-text class="number">[[i18n('pinKeyboard7')]]</inner-text> + <inner-text class="letter">PQRS</inner-text> + <paper-ripple class="circle" center></paper-ripple> + </paper-button> + <paper-button class="digit-button" on-tap="onNumberTap_" value="8" + noink> + <inner-text class="number">[[i18n('pinKeyboard8')]]</inner-text> + <inner-text class="letter">TUV</inner-text> + <paper-ripple class="circle" center></paper-ripple> + </paper-button> + <paper-button class="digit-button" on-tap="onNumberTap_" value="9" + noink> + <inner-text class="number">[[i18n('pinKeyboard9')]]</inner-text> + <inner-text class="letter">WXYZ</inner-text> + <paper-ripple class="circle" center></paper-ripple> + </paper-button> + </div> + <div class="row bottom-row"> + <div class="digit-button"></div> + <paper-button class="digit-button" on-tap="onNumberTap_" value="0" noink> - </paper-icon-button> - <paper-ripple class="circle" center></paper-ripple> + <inner-text class="number">[[i18n('pinKeyboard0')]]</inner-text> + <inner-text class="letter">+</inner-text> + <paper-ripple class="circle" center></paper-ripple> + </paper-button> + <div id="backspaceButtonContainer"> + <paper-icon-button id="backspaceButton" class="digit-button" + disabled$="[[!hasInput_(value)]]" + icon="pin-keyboard:backspace" + on-pointerdown="onBackspacePointerDown_" + on-pointerout="clearAndReset_" + on-pointerup="onBackspacePointerUp_" + title="[[i18n('pinKeyboardDeleteAccessibleName')]]" + noink> + </paper-icon-button> + <paper-ripple class="circle" center></paper-ripple> + </div> </div> </div> </div> diff --git a/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.js b/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.js index 0f7e3da6463..ceaaae8adca 100644 --- a/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.js +++ b/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/pin_keyboard.js @@ -29,7 +29,7 @@ * @type {number} * @const */ -var REPEAT_BACKSPACE_DELAY_MS = 150; +const REPEAT_BACKSPACE_DELAY_MS = 150; /** * How long the backspace button must be held down before auto backspace @@ -37,7 +37,7 @@ var REPEAT_BACKSPACE_DELAY_MS = 150; * @type {number} * @const */ -var INITIAL_BACKSPACE_DELAY_MS = 500; +const INITIAL_BACKSPACE_DELAY_MS = 500; /** * The key codes of the keys allowed to be used on the pin input, in addition to @@ -45,7 +45,7 @@ var INITIAL_BACKSPACE_DELAY_MS = 500; * @type {Array<number>} * @const */ -var PIN_INPUT_ALLOWED_NON_NUMBER_KEY_CODES = [8, 9, 37, 39]; +const PIN_INPUT_ALLOWED_NON_NUMBER_KEY_CODES = [8, 9, 37, 39]; Polymer({ is: 'pin-keyboard', @@ -103,6 +103,36 @@ Polymer({ value: '', observer: 'onPinValueChange_', }, + + /** + * @private + */ + forceUnderline_: { + type: Boolean, + value: false, + }, + + /** + * Enables pin placeholder. + */ + enablePlaceholder: { + type: Boolean, + value: false, + }, + + /** + * Turns on "incognito mode". (FIXME after https://crbug.com/900351 is + * fixed). + */ + isIncognitoUi: { + type: Boolean, + value: false, + }, + }, + + listeners: { + 'blur': 'onBlur_', + 'focus': 'onFocus_', }, /** @@ -174,17 +204,27 @@ Polymer({ this.focus(this.selectionStart_, this.selectionEnd_); }, + /** @private */ + onFocus_: function() { + this.forceUnderline_ = true; + }, + + /** @private */ + onBlur_: function() { + this.forceUnderline_ = false; + }, + /** * Called when a keypad number has been tapped. * @param {Event} event The event object. * @private */ onNumberTap_: function(event) { - var numberValue = event.target.getAttribute('value'); + let numberValue = event.target.getAttribute('value'); // Add the number where the caret is, then update the selection range of the // input element. - var selectionStart = this.selectionStart_; + let selectionStart = this.selectionStart_; this.value = this.value.substring(0, this.selectionStart_) + numberValue + this.value.substring(this.selectionEnd_); @@ -223,8 +263,8 @@ Polymer({ // If the input is shown, clear the text based on the caret location or // selected region of the input element. If it is just a caret, remove the // character in front of the caret. - var selectionStart = this.selectionStart_; - var selectionEnd = this.selectionEnd_; + let selectionStart = this.selectionStart_; + let selectionEnd = this.selectionEnd_; if (selectionStart == selectionEnd && selectionStart) selectionStart--; @@ -355,9 +395,13 @@ Polymer({ /** * Computes the value of the pin input placeholder. * @param {boolean} enablePassword + * @param {boolean} enablePlaceholder * @private */ - getInputPlaceholder_: function(enablePassword) { + getInputPlaceholder_: function(enablePassword, enablePlaceholder) { + if (!enablePlaceholder) + return ''; + return enablePassword ? this.i18n('pinKeyboardPlaceholderPinPassword') : this.i18n('pinKeyboardPlaceholderPin'); }, diff --git a/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.html b/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.html new file mode 100644 index 00000000000..71e9a15fd8e --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.html @@ -0,0 +1,103 @@ +<link rel="import" href="chrome://resources/html/polymer.html"> + +<link rel="import" href="chrome://resources/cr_components/chromeos/quick_unlock/lock_screen_constants.html"> +<link rel="import" href="chrome://resources/cr_components/chromeos/quick_unlock/pin_keyboard.html"> +<link rel="import" href="chrome://resources/cr_elements/cr_dialog/cr_dialog.html"> +<link rel="import" href="chrome://resources/cr_elements/icons.html"> +<link rel="import" href="chrome://resources/cr_elements/shared_vars_css.html"> +<link rel="import" href="chrome://resources/html/assert.html"> +<link rel="import" href="chrome://resources/html/i18n_behavior.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-button/paper-button.html"> + +<!-- + +This module is a "pin setup" keyboard + pin display element. +It can be integrated into some UI container to set pin unlock. + +Usage: + <setup-pin-keyboard + enable-submit="{{enableSubmit_}}" + is-confirm-step="{{isConfirmStep_}}" + on-pin-submit="onPinSubmit_" + on-set-pin-done="onSetPinDone_" + set-modes="{{setModes}}"> + </setup-pin-keyboard> + +Where: + * enable-submit - Notification property for the container to enable/disable + submit button in the container (if it exists). True when pin can be + submitted. + * is-confirm-step - Notification property for the container to update UI + when pin confirmation is requested. False when initial PIN entry step + is active, true when pin confirmation is active. + * on-pin-submit - Event handler for the user requested pin submit by pressing + "Enter" key on the keyboard. setup-pin-keyboard will + not submit pin automatically, delegating this step to outer container. + Container must call setup-pin-keyboard.doSubmit() when + pin should be submitted. + * on-set-pin-done - Event handler for the "set pin done" event, which should + normally close the pin setup UI. This object state is reset before + sending this event. + * set-modes - Reflects property set in password_prompt_dialog.js. + +--> + +<dom-module id="setup-pin-keyboard"> + <template> + <style include="settings-shared"> + .error { + color: var(--google-red-600); + } + + .error > iron-icon { + --iron-icon-fill-color: var(--google-red-600); + } + + .warning { + color: var(--cr-secondary-text-color); + } + + .warning > iron-icon { + --iron-icon-fill-color: var(--google-grey-refresh-700); + } + + #problemDiv { + align-items: center; + display: flex; + flex-direction: row; + height: 32px; + min-height: 0; + } + + /* Hide this using visibility: hidden instead of hidden so that the + dialog does not resize when there are no problems to display. */ + #problemDiv[invisible] { + visibility: hidden; + } + + #problemMessage { + font-size: 10px; + } + </style> + <pin-keyboard id="pinKeyboard" on-pin-change="onPinChange_" + on-submit="onPinSubmit_" value="{{pinKeyboardValue_}}" + has-error="[[hasError_(problemMessageId_, problemClass_)]]" + enable-placeholder="[[enablePlaceholder]]" + is-incognito-ui="[[isIncognitoUi]]"> + <!-- Warning/error; only shown if title is hidden. --> + <div id="problemDiv" class$="[[problemClass_]]" + invisible$="[[!problemMessageId_]]" problem> + <div> + <iron-icon icon="cr:error-outline"></iron-icon> + <span id="problemMessage"> + [[formatProblemMessage_(locale, problemMessageId_, + problemMessageParameters_)]] + </span> + </div> + </div> + </pin-keyboard> + </template> + <script + src="chrome://resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js"> + </script> +</dom-module> diff --git a/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js b/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js new file mode 100644 index 00000000000..9dde5f4717f --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/chromeos/quick_unlock/setup_pin_keyboard.js @@ -0,0 +1,377 @@ +// 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. + +/** + * @fileoverview + * 'setup-pin-keyboard' is the keyboard/input field for choosing a PIN. + * + * See usage documentation in setup_pin_keyboard.html. + * + */ + +(function() { +'use strict'; + +/** + * Keep in sync with the string keys provided by settings. + * @enum {string} + */ +const MessageType = { + TOO_SHORT: 'configurePinTooShort', + TOO_LONG: 'configurePinTooLong', + TOO_WEAK: 'configurePinWeakPin', + MISMATCH: 'configurePinMismatched' +}; + +/** @enum {string} */ +const ProblemType = { + WARNING: 'warning', + ERROR: 'error' +}; + +Polymer({ + is: 'setup-pin-keyboard', + + behaviors: [I18nBehavior], + + properties: { + /** + * Reflects property set in password_prompt_dialog.js. + * @type {?Object} + */ + setModes: { + type: Object, + notify: true, + }, + + /** + * The current PIN keyboard value. + * @private + */ + pinKeyboardValue_: String, + + /** + * Stores the initial PIN value so it can be confirmed. + * @private + */ + initialPin_: String, + + /** + * The message ID of actual problem message to display. + * @private + */ + problemMessageId_: { + type: String, + value: '', + }, + + /** + * The additional parameters to format for the problem message string. + * @private + */ + problemMessageParameters_: { + type: String, + value: '', + }, + + /** + * The type of problem class to show (warning or error). + * @private + */ + problemClass_: String, + + /** + * Should the step-specific submit button be displayed? + * This has upward data flow only. + */ + enableSubmit: { + notify: true, + type: Boolean, + value: false, + }, + + /** + * writeUma is a function that handles writing uma stats. + * + * @type {function(LockScreenProgress)} + */ + writeUma: { + type: Object, + value: function() { + return function() {}; + } + }, + + /** + * The current step/subpage we are on. + * This is has upward data flow only. + */ + isConfirmStep: { + notify: true, + type: Boolean, + value: false, + }, + + /** + * Interface for chrome.quickUnlockPrivate calls. + * @type {QuickUnlockPrivate} + */ + quickUnlockPrivate: Object, + + /** + * |pinHasPassedMinimumLength_| tracks whether a user has passed the minimum + * length threshold at least once, and all subsequent PIN too short messages + * will be displayed as errors. They will be displayed as warnings prior to + * this. + * @private + */ + pinHasPassedMinimumLength_: {type: Boolean, value: false}, + + /** + * Enables pin placeholder. + */ + enablePlaceholder: { + type: Boolean, + value: false, + }, + + /** + * Turns on "incognito mode". (FIXME after https://crbug.com/900351 is + * fixed). + */ + isIncognitoUi: { + type: Boolean, + value: false, + }, + }, + + focus: function() { + this.$.pinKeyboard.focus(); + }, + + /** @override */ + attached: function() { + this.resetState(); + + // Show the pin is too short error when first displaying the PIN dialog. + this.problemClass_ = ProblemType.WARNING; + this.quickUnlockPrivate.getCredentialRequirements( + chrome.quickUnlockPrivate.QuickUnlockMode.PIN, + this.processPinRequirements_.bind(this, MessageType.TOO_SHORT)); + }, + + /** + * Resets the element to the initial state. + */ + resetState: function() { + this.initialPin_ = ''; + this.pinKeyboardValue_ = ''; + this.enableSubmit = false; + this.isConfirmStep = false; + this.hideProblem_(); + this.onPinChange_(); + }, + + /** + * Returns true if the PIN is ready to be changed to a new value. + * @private + * @return {boolean} + */ + canSubmit_: function() { + return this.initialPin_ == this.pinKeyboardValue_; + }, + + /** + * Handles writing the appropriate message to |problemMessageId_| && + * |problemMessageParameters_|. + * @private + * @param {string} messageId + * @param {chrome.quickUnlockPrivate.CredentialRequirements} requirements + * The requirements received from getCredentialRequirements. + */ + processPinRequirements_: function(messageId, requirements) { + let additionalInformation = ''; + switch (messageId) { + case MessageType.TOO_SHORT: + additionalInformation = requirements.minLength.toString(); + break; + case MessageType.TOO_LONG: + additionalInformation = (requirements.maxLength + 1).toString(); + break; + case MessageType.TOO_WEAK: + case MessageType.MISMATCH: + break; + default: + assertNotReached(); + break; + } + this.problemMessageId_ = messageId; + this.problemMessageParameters_ = additionalInformation; + }, + + /** + * Notify the user about a problem. + * @private + * @param {string} messageId + * @param {string} problemClass + */ + showProblem_: function(messageId, problemClass) { + this.quickUnlockPrivate.getCredentialRequirements( + chrome.quickUnlockPrivate.QuickUnlockMode.PIN, + this.processPinRequirements_.bind(this, messageId)); + this.problemClass_ = problemClass; + this.updateStyles(); + this.enableSubmit = + problemClass != ProblemType.ERROR && messageId != MessageType.TOO_SHORT; + }, + + /** @private */ + hideProblem_: function() { + this.problemMessageId_ = ''; + this.problemClass_ = ''; + }, + + /** + * Processes the message received from the quick unlock api and hides/shows + * the problem based on the message. + * @private + * @param {chrome.quickUnlockPrivate.CredentialCheck} message The message + * received from checkCredential. + */ + processPinProblems_: function(message) { + if (!message.errors.length && !message.warnings.length) { + this.hideProblem_(); + this.enableSubmit = true; + this.pinHasPassedMinimumLength_ = true; + return; + } + + if (!message.errors.length || + message.errors[0] != + chrome.quickUnlockPrivate.CredentialProblem.TOO_SHORT) { + this.pinHasPassedMinimumLength_ = true; + } + + if (message.warnings.length) { + assert( + message.warnings[0] == + chrome.quickUnlockPrivate.CredentialProblem.TOO_WEAK); + this.showProblem_(MessageType.TOO_WEAK, ProblemType.WARNING); + } + + if (message.errors.length) { + switch (message.errors[0]) { + case chrome.quickUnlockPrivate.CredentialProblem.TOO_SHORT: + this.showProblem_( + MessageType.TOO_SHORT, + this.pinHasPassedMinimumLength_ ? ProblemType.ERROR : + ProblemType.WARNING); + break; + case chrome.quickUnlockPrivate.CredentialProblem.TOO_LONG: + this.showProblem_(MessageType.TOO_LONG, ProblemType.ERROR); + break; + case chrome.quickUnlockPrivate.CredentialProblem.TOO_WEAK: + this.showProblem_(MessageType.TOO_WEAK, ProblemType.ERROR); + break; + default: + assertNotReached(); + break; + } + } + }, + + /** @private */ + onPinChange_: function() { + if (!this.isConfirmStep) { + if (this.pinKeyboardValue_) { + this.quickUnlockPrivate.checkCredential( + chrome.quickUnlockPrivate.QuickUnlockMode.PIN, + this.pinKeyboardValue_, this.processPinProblems_.bind(this)); + } else { + this.enableSubmit = false; + } + return; + } + + this.hideProblem_(); + this.enableSubmit = this.pinKeyboardValue_.length > 0; + }, + + /** @private */ + onPinSubmit_: function() { + // Notify container object. + this.fire('pin-submit'); + }, + + /** + * This is callback for quickUnlockPrivate.QuickUnlockMode.PIN API. + * + * @private + * @param {boolean} didSet + */ + onSetModesCompleted_: function(didSet) { + if (!didSet) { + console.error('Failed to update pin'); + return; + } + + this.resetState(); + this.fire('set-pin-done'); + }, + + /** This is called by container object when user initiated submit. */ + doSubmit: function() { + if (!this.isConfirmStep) { + if (!this.enableSubmit) + return; + this.initialPin_ = this.pinKeyboardValue_; + this.pinKeyboardValue_ = ''; + this.isConfirmStep = true; + this.onPinChange_(); + this.$.pinKeyboard.focus(); + this.writeUma(LockScreenProgress.ENTER_PIN); + return; + } + // onPinSubmit gets called if the user hits enter on the PIN keyboard. + // The PIN is not guaranteed to be valid in that case. + if (!this.canSubmit_()) { + this.showProblem_(MessageType.MISMATCH, ProblemType.ERROR); + this.enableSubmit = false; + // Focus the PIN keyboard and highlight the entire PIN. + this.$.pinKeyboard.focus(0, this.pinKeyboardValue_.length + 1); + return; + } + + assert(this.setModes); + this.setModes.call( + null, [chrome.quickUnlockPrivate.QuickUnlockMode.PIN], + [this.pinKeyboardValue_], this.onSetModesCompleted_.bind(this)); + this.writeUma(LockScreenProgress.CONFIRM_PIN); + }, + + /** + * @private + * @param {string} problemMessageId + * @param {string} problemClass + * @return {boolean} + */ + hasError_: function(problemMessageId, problemClass) { + return !!problemMessageId && problemClass == ProblemType.ERROR; + }, + + /** + * Formar problem message + * @private + * @param {string} locale i18n locale data + * @param {string} messageId + * @param {string} messageParameters + * @return {string} + */ + formatProblemMessage_: function(locale, messageId, messageParameters) { + return messageId ? this.i18nDynamic(locale, messageId, messageParameters) : + ''; + }, +}); + +})(); diff --git a/chromium/ui/webui/resources/cr_components/cr_components_images.grdp b/chromium/ui/webui/resources/cr_components/cr_components_images.grdp new file mode 100644 index 00000000000..9afe1f136f9 --- /dev/null +++ b/chromium/ui/webui/resources/cr_components/cr_components_images.grdp @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<grit-part> + <if expr="chromeos"> + <include name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_START_SETUP_ICON_1X_PNG" + file="cr_components/chromeos/multidevice_setup/start_setup_icon_1x.png" + type="BINDATA" + compress="gzip" /> + <include name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_START_SETUP_ICON_2X_PNG" + file="cr_components/chromeos/multidevice_setup/start_setup_icon_2x.png" + type="BINDATA" + compress="gzip" /> + <include name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_SETUP_SUCCEEDED_ICON_1X_PNG" + file="cr_components/chromeos/multidevice_setup/setup_succeeded_icon_1x.png" + type="BINDATA" + compress="gzip" /> + <include name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_SETUP_SUCCEEDED_ICON_2X_PNG" + file="cr_components/chromeos/multidevice_setup/setup_succeeded_icon_2x.png" + type="BINDATA" + compress="gzip" /> + </if> +</grit-part> diff --git a/chromium/ui/webui/resources/cr_components/cr_components_resources.grdp b/chromium/ui/webui/resources/cr_components/cr_components_resources.grdp index 2ff0de4098b..3bf1f1cdd56 100644 --- a/chromium/ui/webui/resources/cr_components/cr_components_resources.grdp +++ b/chromium/ui/webui/resources/cr_components/cr_components_resources.grdp @@ -123,19 +123,19 @@ file="cr_components/chromeos/network/network_choose_mobile.js" type="chrome_html" compress="gzip" /> - <structure name="IDR_WEBUI_CHROMEOS__NETWORK_CONFIG_HTML" + <structure name="IDR_WEBUI_CHROMEOS_NETWORK_CONFIG_HTML" file="cr_components/chromeos/network/network_config.html" type="chrome_html" compress="gzip" /> - <structure name="IDR_WEBUI_CHROMEOS__NETWORK_CONFIG_JS" + <structure name="IDR_WEBUI_CHROMEOS_NETWORK_CONFIG_JS" file="cr_components/chromeos/network/network_config.js" type="chrome_html" compress="gzip" /> - <structure name="IDR_WEBUI_CHROMEOS__NETWORK_CONFIG_SELECT_HTML" + <structure name="IDR_WEBUI_CHROMEOS_NETWORK_CONFIG_SELECT_HTML" file="cr_components/chromeos/network/network_config_select.html" type="chrome_html" compress="gzip" /> - <structure name="IDR_WEBUI_CHROMEOS__NETWORK_CONFIG_SELECT_JS" + <structure name="IDR_WEBUI_CHROMEOS_NETWORK_CONFIG_SELECT_JS" file="cr_components/chromeos/network/network_config_select.js" type="chrome_html" compress="gzip" /> @@ -155,11 +155,11 @@ file="cr_components/chromeos/network/network_nameservers.js" type="chrome_html" compress="gzip" /> - <structure name="IDR_WEBUI_CHROMEOS__NETWORK_PASSWORD_INPUT_HTML" + <structure name="IDR_WEBUI_CHROMEOS_NETWORK_PASSWORD_INPUT_HTML" file="cr_components/chromeos/network/network_password_input.html" type="chrome_html" compress="gzip" /> - <structure name="IDR_WEBUI_CHROMEOS__NETWORK_PASSWORD_INPUT_JS" + <structure name="IDR_WEBUI_CHROMEOS_NETWORK_PASSWORD_INPUT_JS" file="cr_components/chromeos/network/network_password_input.js" type="chrome_html" compress="gzip" /> @@ -218,5 +218,127 @@ file="cr_components/chromeos/quick_unlock/pin_keyboard.js" type="chrome_html" compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_QUICK_UNLOCK_SETUP_PIN_KEYBOARD_JS" + file="cr_components/chromeos/quick_unlock/setup_pin_keyboard.js" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_QUICK_UNLOCK_SETUP_PIN_KEYBOARD_HTML" + file="cr_components/chromeos/quick_unlock/setup_pin_keyboard.html" + type="chrome_html" + compress="gzip"/> + <structure name="IDR_WEBUI_CHROMEOS_QUICK_UNLOCK_LOCK_SCREEN_CONSTANTS_JS" + file="cr_components/chromeos/quick_unlock/lock_screen_constants.js" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_QUICK_UNLOCK_LOCK_SCREEN_CONSTANTS_HTML" + file="cr_components/chromeos/quick_unlock/lock_screen_constants.html" + type="chrome_html" + compress="gzip" /> + + <!-- Shared between MultiDevice setup flow and OOBE. --> + <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_MULTIDEVICE_SETUP_BROWSER_PROXY_HTML" + file="cr_components/chromeos/multidevice_setup/multidevice_setup_browser_proxy.html" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_MULTIDEVICE_SETUP_BROWSER_PROXY_JS" + file="cr_components/chromeos/multidevice_setup/multidevice_setup_browser_proxy.js" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_BUTTON_BAR_HTML" + file="cr_components/chromeos/multidevice_setup/button_bar.html" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_BUTTON_BAR_JS" + file="cr_components/chromeos/multidevice_setup/button_bar.js" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_FAKE_MOJO_SERVICE_HTML" + file="cr_components/chromeos/multidevice_setup/fake_mojo_service.html" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_FAKE_MOJO_SERVICE_JS" + file="cr_components/chromeos/multidevice_setup/fake_mojo_service.js" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_ICONS_HTML" + file="cr_components/chromeos/multidevice_setup/icons.html" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_MOJO_API_HTML" + file="cr_components/chromeos/multidevice_setup/mojo_api.html" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_MOJO_API_JS" + file="cr_components/chromeos/multidevice_setup/mojo_api.js" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_MULTIDEVICE_SETUP_HTML" + file="cr_components/chromeos/multidevice_setup/multidevice_setup.html" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_MULTIDEVICE_SETUP_JS" + file="cr_components/chromeos/multidevice_setup/multidevice_setup.js" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_MULTIDEVICE_SETUP_DELEGATE_HTML" + file="cr_components/chromeos/multidevice_setup/multidevice_setup_delegate.html" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_MULTIDEVICE_SETUP_DELEGATE_JS" + file="cr_components/chromeos/multidevice_setup/multidevice_setup_delegate.js" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_MULTIDEVICE_SETUP_SHARED_CSS_HTML" + file="cr_components/chromeos/multidevice_setup/multidevice_setup_shared_css.html" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_PASSWORD_PAGE_HTML" + file="cr_components/chromeos/multidevice_setup/password_page.html" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_PASSWORD_PAGE_JS" + file="cr_components/chromeos/multidevice_setup/password_page.js" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_SETUP_FAILED_PAGE_HTML" + file="cr_components/chromeos/multidevice_setup/setup_failed_page.html" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_SETUP_FAILED_PAGE_JS" + file="cr_components/chromeos/multidevice_setup/setup_failed_page.js" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_SETUP_SUCCEEDED_PAGE_HTML" + file="cr_components/chromeos/multidevice_setup/setup_succeeded_page.html" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_SETUP_SUCCEEDED_PAGE_JS" + file="cr_components/chromeos/multidevice_setup/setup_succeeded_page.js" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_START_SETUP_PAGE_HTML" + file="cr_components/chromeos/multidevice_setup/start_setup_page.html" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_START_SETUP_PAGE_JS" + file="cr_components/chromeos/multidevice_setup/start_setup_page.js" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_UI_PAGE_CONTAINER_BEHAVIOR_HTML" + file="cr_components/chromeos/multidevice_setup/ui_page_container_behavior.html" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_UI_PAGE_CONTAINER_BEHAVIOR_JS" + file="cr_components/chromeos/multidevice_setup/ui_page_container_behavior.js" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_UI_PAGE_HTML" + file="cr_components/chromeos/multidevice_setup/ui_page.html" + type="chrome_html" + compress="gzip" /> + <structure name="IDR_WEBUI_CHROMEOS_MULTIDEVICE_SETUP_UI_PAGE_JS" + file="cr_components/chromeos/multidevice_setup/ui_page.js" + type="chrome_html" + compress="gzip" /> </if> </grit-part> diff --git a/chromium/ui/webui/resources/cr_elements/BUILD.gn b/chromium/ui/webui/resources/cr_elements/BUILD.gn index 90954848f2e..9927b6e60e8 100644 --- a/chromium/ui/webui/resources/cr_elements/BUILD.gn +++ b/chromium/ui/webui/resources/cr_elements/BUILD.gn @@ -22,6 +22,7 @@ group("closure_compile") { "cr_slider:closure_compile", "cr_toast:closure_compile", "cr_toggle:closure_compile", + "cr_view_manager:closure_compile", "policy:closure_compile", ] } diff --git a/chromium/ui/webui/resources/cr_elements/READE.md b/chromium/ui/webui/resources/cr_elements/README.md index b8b47ef98b3..b8b47ef98b3 100644 --- a/chromium/ui/webui/resources/cr_elements/READE.md +++ b/chromium/ui/webui/resources/cr_elements/README.md diff --git a/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.js b/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.js index 32dfbba4732..66036c7283d 100644 --- a/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.js +++ b/chromium/ui/webui/resources/cr_elements/chromeos/network/cr_network_select.js @@ -159,6 +159,8 @@ Polymer({ if (this.cellularDeviceState_) this.ensureCellularNetwork_(networkStates); this.networkStateList_ = networkStates; + this.fire('network-list-changed', networkStates); + var defaultNetwork; for (var i = 0; i < networkStates.length; ++i) { var state = networkStates[i]; diff --git a/chromium/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.html b/chromium/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.html index 3bb931d9a4b..3c87967d159 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.html +++ b/chromium/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.html @@ -60,7 +60,7 @@ outline: none; } </style> - <dialog id="dialog" tabindex="0"> + <dialog id="dialog" tabindex="0" on-close="onNativeDialogClose_"> <div class="item-wrapper" tabindex="-1" role="menu"> <slot name="item" id="contentNode"></slot> </div> diff --git a/chromium/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.js b/chromium/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.js index ec1ac2d8fce..9c70ecede80 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.js +++ b/chromium/ui/webui/resources/cr_elements/cr_action_menu/cr_action_menu.js @@ -197,6 +197,24 @@ Polymer({ * @param {!Event} e * @private */ + onNativeDialogClose_: function(e) { + // Ignore any 'close' events not fired directly by the <dialog> element. + if (e.target !== this.$.dialog) + return; + + // TODO(dpapad): This is necessary to make the code work both for Polymer 1 + // and Polymer 2. Remove once migration to Polymer 2 is completed. + e.stopPropagation(); + + // Catch and re-fire the 'close' event such that it bubbles across Shadow + // DOM v1. + this.fire('close'); + }, + + /** + * @param {!Event} e + * @private + */ onTap_: function(e) { if (e.target == this) { this.close(); @@ -271,7 +289,7 @@ Polymer({ var options = this.querySelectorAll('.dropdown-item'); var numOptions = options.length; var focusedIndex = - Array.prototype.indexOf.call(options, this.root.activeElement); + Array.prototype.indexOf.call(options, getDeepActiveElement()); // Handle case where nothing is focused and up is pressed. if (focusedIndex === -1 && step === -1) diff --git a/chromium/ui/webui/resources/cr_elements/cr_container_shadow_behavior.js b/chromium/ui/webui/resources/cr_elements/cr_container_shadow_behavior.js index 45d8db171ec..3eb059ebfcf 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_container_shadow_behavior.js +++ b/chromium/ui/webui/resources/cr_elements/cr_container_shadow_behavior.js @@ -29,7 +29,7 @@ var CrContainerShadowBehavior = { var dropShadow = document.createElement('div'); // This ID should match the CSS rules in shared_styles_css.html. dropShadow.id = 'cr-container-shadow'; - this.shadowRoot.insertBefore(dropShadow, this.$.container); + this.$.container.parentNode.insertBefore(dropShadow, this.$.container); // Dummy element used to detect scrolling. Has a 0px height intentionally. var intersectionProbe = document.createElement('div'); diff --git a/chromium/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.js b/chromium/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.js index 864898edab5..ebf0ad52afa 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.js +++ b/chromium/ui/webui/resources/cr_elements/cr_dialog/cr_dialog.js @@ -52,6 +52,15 @@ Polymer({ }, /** + * True if the dialog should consume 'keydown' events. If ignoreEnterKey + * is true, 'Enter' key won't be consumed. + */ + consumeKeydownEvent: { + type: Boolean, + value: false, + }, + + /** * True if the dialog should not be able to be cancelled, which will prevent * 'Escape' key presses from closing the dialog. */ @@ -77,6 +86,9 @@ Polymer({ /** @private {?MutationObserver} */ mutationObserver_: null, + /** @private {?Function} */ + boundKeydown_: null, + /** @override */ ready: function() { // If the active history entry changes (i.e. user clicks back button), @@ -93,10 +105,13 @@ Polymer({ /** @override */ attached: function() { var mutationObserverCallback = function() { - if (this.$.dialog.open) + if (this.$.dialog.open) { this.addIntersectionObserver_(); - else + this.addKeydownListener_(); + } else { this.removeIntersectionObserver_(); + this.removeKeydownListener_(); + } }.bind(this); this.mutationObserver_ = new MutationObserver(mutationObserverCallback); @@ -113,6 +128,7 @@ Polymer({ /** @override */ detached: function() { this.removeIntersectionObserver_(); + this.removeKeydownListener_(); if (this.mutationObserver_) { this.mutationObserver_.disconnect(); this.mutationObserver_ = null; @@ -163,6 +179,31 @@ Polymer({ } }, + /** @private */ + addKeydownListener_: function() { + if (!this.consumeKeydownEvent) + return; + + this.boundKeydown_ = this.boundKeydown_ || this.onKeydown_.bind(this); + + this.addEventListener('keydown', this.boundKeydown_); + + // Sometimes <body> is key event's target and in that case the event + // will bypass cr-dialog. We should consume those events too in order to + // behave modally. This prevents accidentally triggering keyboard commands. + document.body.addEventListener('keydown', this.boundKeydown_); + }, + + /** @private */ + removeKeydownListener_: function() { + if (!this.boundKeydown_) + return; + + this.removeEventListener('keydown', this.boundKeydown_); + document.body.removeEventListener('keydown', this.boundKeydown_); + this.boundKeydown_ = null; + }, + showModal: function() { this.$.dialog.showModal(); assert(this.$.dialog.open); @@ -216,6 +257,10 @@ Polymer({ * @private */ onNativeDialogCancel_: function(e) { + // Ignore any 'cancel' events not fired directly by the <dialog> element. + if (e.target !== this.getNative()) + return; + if (this.noCancel) { e.preventDefault(); return; @@ -265,6 +310,23 @@ Polymer({ } }, + /** + * @param {!Event} e + * @private + */ + onKeydown_: function(e) { + assert(this.consumeKeydownEvent); + + if (!this.getNative().open) + return; + + if (this.ignoreEnterKey && e.key == 'Enter') + return; + + // Stop propagation to behave modally. + e.stopPropagation(); + }, + /** @param {!PointerEvent} e */ onPointerdown_: function(e) { // Only show pulse animation if user left-clicked outside of the dialog diff --git a/chromium/ui/webui/resources/cr_elements/cr_input/cr_input.html b/chromium/ui/webui/resources/cr_elements/cr_input/cr_input.html index 5be1ea9e8c9..b328ee4b72e 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_input/cr_input.html +++ b/chromium/ui/webui/resources/cr_elements/cr_input/cr_input.html @@ -10,18 +10,22 @@ <template> <style include="cr-hidden-style cr-input-style"> /* + A 'suffix' element will be outside the underlined space, while a + 'prefix' element will be inside the underlined space by default. + Regarding cr-input's width: - When there's no element in the 'suffix' slot, setting the width of - cr-input as follows will work as expected: + When there's no element in the 'prefix' or 'suffix' slot, setting + the width of cr-input as follows will work as expected: cr-input { width: 200px; } - However, when there's an element in the 'suffix' slot, setting the - 'width' will dictate the total with of the input field *plus* the - 'suffix' element. To set the width of the input field itself when - a 'suffix' is present, use --cr-input-width. + However, when there's an element in the 'suffix' and/or 'prefix' + slot, setting the 'width' will dictate the total width of the input + field *plus* the 'prefix' and 'suffix' elements. To set the width + of the input field + 'prefix' when a 'suffix' is present, use + --cr-input-width. cr-input { --cr-input-width: 200px; @@ -72,8 +76,8 @@ color: var(--cr-input-error-color); display: var(--cr-input-error-display, block); font-size: var(--cr-form-field-label-font-size); - height: var(--cr-form-field-label-font-size); - line-height: var(--cr-form-field-label-font-size); + height: var(--cr-form-field-label-height); + line-height: var(--cr-form-field-label-line-height); margin: 8px 0; visibility: hidden; } @@ -82,14 +86,17 @@ visibility: visible; } - #row-container { + #row-container, + #inner-input-container { align-items: center; display: flex; /* This will spread the input field and the suffix apart only if the host element width is intentionally set to something large. */ justify-content: space-between; position: relative; + } + #row-container { @apply --cr-input-row-container; } @@ -103,12 +110,17 @@ <!-- Only attributes that are named inconsistently between html and js need to use attr$="", such as |tabindex| vs .tabIndex and |readonly| vs .readOnly. --> - <input id="input" disabled="[[disabled]]" autofocus="[[autofocus]]" - value="{{value::input}}" tabindex$="[[tabindex]]" type="[[type]]" - readonly$="[[readonly]]" maxlength$="[[maxlength]]" - pattern="[[pattern]]" required="[[required]]" - incremental="[[incremental]]" minlength$="[[minlength]]" - max="[[max]]" min="[[min]]"> + <div id="inner-input-container"> + <slot name="prefix"></slot> + <input id="input" disabled="[[disabled]]" autofocus="[[autofocus]]" + value="{{value::input}}" tabindex$="[[tabindex]]" type="[[type]]" + readonly$="[[readonly]]" maxlength$="[[maxlength]]" + pattern$="[[pattern]]" required="[[required]]" + minlength$="[[minlength]]" + max="[[max]]" min="[[min]]" on-focus="onInputFocus_" + on-blur="onInputBlur_" on-change="onInputChange_" + on-keydown="onInputKeydown_"> + </div> <div id="underline"></div> </div> <slot name="suffix"></slot> diff --git a/chromium/ui/webui/resources/cr_elements/cr_input/cr_input.js b/chromium/ui/webui/resources/cr_elements/cr_input/cr_input.js index 2cbd52aa289..67b9484a99b 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_input/cr_input.js +++ b/chromium/ui/webui/resources/cr_elements/cr_input/cr_input.js @@ -8,7 +8,6 @@ * Native input attributes that are currently supported by cr-inputs are: * autofocus * disabled - * incremental (only applicable when type="search") * max (only applicable when type="number") * min (only applicable when type="number") * maxlength @@ -72,8 +71,6 @@ Polymer({ reflectToAttribute: true, }, - incremental: Boolean, - invalid: { type: Boolean, value: false, @@ -150,11 +147,7 @@ Polymer({ }, listeners: { - 'input.focus': 'onInputFocus_', - 'input.blur': 'onInputBlur_', - 'input.change': 'onInputChange_', - 'input.keydown': 'onInputKeydown_', - 'focus': 'focusInput_', + 'focus': 'onFocus_', 'pointerdown': 'onPointerDown_', }, @@ -219,9 +212,24 @@ Polymer({ }, /** @private */ + onFocus_: function() { + if (!this.focusInput_()) + return; + // Always select the <input> element on focus. TODO(stevenjb/scottchen): + // Native <input> elements only do this for keyboard focus, not when + // focus() is called directly. Fix this? https://crbug.com/882612. + this.inputElement.select(); + }, + + /** + * @return {boolean} Whether the <input> element was focused. + * @private + */ focusInput_: function() { - if (this.shadowRoot.activeElement != this.inputElement) - this.inputElement.focus(); + if (this.shadowRoot.activeElement == this.inputElement) + return false; + this.inputElement.focus(); + return true; }, /** @private */ @@ -328,4 +336,4 @@ Polymer({ this.invalid = !this.inputElement.checkValidity(); return !this.invalid; }, -});
\ No newline at end of file +}); diff --git a/chromium/ui/webui/resources/cr_elements/cr_input/cr_input_style_css.html b/chromium/ui/webui/resources/cr_elements/cr_input/cr_input_style_css.html index 2b3e104397f..a78334b5223 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_input/cr_input_style_css.html +++ b/chromium/ui/webui/resources/cr_elements/cr_input/cr_input_style_css.html @@ -35,6 +35,15 @@ @apply --cr-input-container; } + #inner-input-container { + background-color: var(--cr-input-background-color, + var(--google-grey-refresh-100)); + box-sizing: border-box; + padding: 0; + + @apply --cr-input-inner-container; + } + #input { -webkit-appearance: none; background-color: var(--cr-input-background-color, @@ -78,11 +87,12 @@ opacity: 0; position: absolute; right: 0; - transition: opacity 120ms ease-out, width 0 linear 180ms; + transition: opacity 120ms ease-out, width 0s linear 180ms; width: 0; } :host([invalid]) #underline, + :host([force-underline]) #underline, :host([focused_]:not([readonly])) #underline { opacity: 1; transition: width 180ms ease-out, opacity 120ms ease-in; diff --git a/chromium/ui/webui/resources/cr_elements/cr_search_field/cr_search_field_behavior.js b/chromium/ui/webui/resources/cr_elements/cr_search_field/cr_search_field_behavior.js index 1c41e4a776f..179a09753c2 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_search_field/cr_search_field_behavior.js +++ b/chromium/ui/webui/resources/cr_elements/cr_search_field/cr_search_field_behavior.js @@ -32,6 +32,9 @@ var CrSearchFieldBehavior = { }, }, + /** @private {number} */ + searchDelayTimer_: -1, + /** * @return {!HTMLInputElement} The input field element the behavior should * use. @@ -59,6 +62,26 @@ var CrSearchFieldBehavior = { this.onValueChanged_(value, !!opt_noEvent); }, + /** @private */ + scheduleSearch_: function() { + if (this.searchDelayTimer_ >= 0) + clearTimeout(this.searchDelayTimer_); + // Dispatch 'search' event after: + // 0ms if the value is empty + // 500ms if the value length is 1 + // 400ms if the value length is 2 + // 300ms if the value length is 3 + // 200ms if the value length is 4 or greater. + // The logic here was copied from WebKit's native 'search' event. + var length = this.getValue().length; + var timeoutMs = length > 0 ? (500 - 100 * (Math.min(length, 4) - 1)) : 0; + this.searchDelayTimer_ = setTimeout(() => { + this.getSearchInput().dispatchEvent( + new CustomEvent('search', {composed: true, detail: this.getValue()})); + this.searchDelayTimer_ = -1; + }, timeoutMs); + }, + onSearchTermSearch: function() { this.onValueChanged_(this.getValue(), false); }, @@ -70,6 +93,7 @@ var CrSearchFieldBehavior = { */ onSearchTermInput: function() { this.hasSearchText = this.$.searchInput.value != ''; + this.scheduleSearch_(); }, /** diff --git a/chromium/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.html b/chromium/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.html index 168a5fec0e7..209dc714d37 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.html +++ b/chromium/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.html @@ -49,7 +49,8 @@ immediately as the user types unless the update-value-on-input flag is explicitly used. --> <cr-input label="[[label]]" on-click="onClick_" value="[[value]]" - on-input="onInput_" id="search" autofocus="[[autofocus]]"> + on-input="onInput_" id="search" autofocus="[[autofocus]]" + placeholder="[[placeholder]]"> </cr-input> <iron-dropdown horizontal-align="left" vertical-align="top" vertical-offset="52"> diff --git a/chromium/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.js b/chromium/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.js index 3ce90c6fe13..e7e94edb832 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.js +++ b/chromium/ui/webui/resources/cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.js @@ -20,6 +20,8 @@ Polymer({ reflectToAttribute: true, }, + placeholder: String, + /** @type {!Array<string>} */ items: Array, diff --git a/chromium/ui/webui/resources/cr_elements/cr_slider/BUILD.gn b/chromium/ui/webui/resources/cr_elements/cr_slider/BUILD.gn index 78d6d47bac9..ad5d8f69b5e 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_slider/BUILD.gn +++ b/chromium/ui/webui/resources/cr_elements/cr_slider/BUILD.gn @@ -12,6 +12,8 @@ js_type_check("closure_compile") { js_library("cr_slider") { deps = [ - "//third_party/polymer/v1_0/components-chromium/paper-slider:paper-slider-extracted", + "//third_party/polymer/v1_0/components-chromium/paper-behaviors:paper-ripple-behavior-extracted", + "//ui/webui/resources/js:cr", + "//ui/webui/resources/js:event_tracker", ] } diff --git a/chromium/ui/webui/resources/cr_elements/cr_slider/cr_slider.html b/chromium/ui/webui/resources/cr_elements/cr_slider/cr_slider.html index 05adf450012..f6203a2f110 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_slider/cr_slider.html +++ b/chromium/ui/webui/resources/cr_elements/cr_slider/cr_slider.html @@ -1,52 +1,197 @@ <link rel="import" href="../../html/polymer.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/paper-behaviors/paper-ripple-behavior.html"> +<link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/iron-flex-layout.html"> +<link rel="import" href="../../html/cr.html"> +<link rel="import" href="../../html/event_tracker.html"> +<link rel="import" href="../hidden_style_css.html"> <link rel="import" href="../shared_vars_css.html"> -<link rel="import" href="chrome://resources/polymer/v1_0/paper-slider/paper-slider.html"> <dom-module id="cr-slider"> <template> - <style> - paper-slider { - --paper-slider-active-color: var(--google-blue-600); - --paper-slider-container-color: var(--google-blue-600-opacity-24); - --paper-slider-knob-color: var(--google-blue-600); - --paper-slider-knob-start-color: var(--google-blue-600); - --paper-slider-knob-start-border-color: var(--google-blue-600); - --paper-slider-pin-color: var(--google-blue-600); - --paper-slider-pin-start-color: var(--google-blue-600); - --paper-slider-markers-color: rgba(255, 255, 255, 0.54); - --paper-slider-disabled-active-color: var(--google-grey-600); - --paper-slider-disabled-knob-color: var(--google-grey-600); - width: 100%; - - --paper-slider-pin-text: { - font-family: Roboto; - font-size: 12px; - font-weight: 500; - line-height: 14px; - }; - } - - :host-context([dir=rtl]) paper-slider { - --paper-slider-pin-text: { - font-family: Roboto; - font-size: 12px; - font-weight: 500; - line-height: 14px; - transform: scale(-1, 1) translate(0, -17px); - }; - } - - paper-slider[disabled] { - --paper-slider-container-color: var(--google-grey-600-opacity-24); + <style include="cr-hidden-style"> + :host { + -webkit-tap-highlight-color: rgba(0, 0, 0, 0); + cursor: default; + user-select: none; + } + + :host([dragging]) { + touch-action: none; + } + + #container { + height: 32px; + position: relative; + } + + #barContainer { + background-color: var(--google-blue-600-opacity-24); + border-radius: 1px; + height: 2px; + margin: 0 16px; + position: absolute; + top: 15px; + width: calc(100% - 32px); + } + + #bar { + background-color: var(--google-blue-600); + border-radius: 1px; + height: 2px; + left: 0; + position: absolute; + transition: width 80ms ease; + width: 0; + } + + :host-context([dir=rtl]) #bar { + left: initial; + right: 0; + } + + #knobContainer { + margin-inline-start: 12px; + position: absolute; + top: 11px; + width: calc(100% - 32px); + } + + #knob { + background-color: var(--google-blue-600); + border: 0; + border-radius: 50%; + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.4); + height: 10px; + margin-inline-start: 0; + outline: none; + position: absolute; + transition: margin-inline-start 80ms ease; + width: 10px; + } + + paper-ripple { + color: var(--google-blue-600); + height: 32px; + left: -11px; + pointer-events: none; + top: -11px; + transition: color linear 80ms; + width: 32px; + } + + :host-context([dir=rtl]) paper-ripple { + left: auto; + right: -11px; + } + + #markers { + left: 0; + pointer-events: none; + position: absolute; + right: 0; + top: 0; + @apply --layout-horizontal; + } + + .active-marker, + .inactive-marker { + @apply --layout-flex; + } + #markers::before, + #markers::after, + .active-marker::after, + .inactive-marker::after { + border-radius: 50%; + content: ''; + display: block; + height: 2px; + margin-left: -1px; + width: 2px; + } + + #markers::before, + .active-marker::after { + background-color: rgba(255, 255, 255, 0.54); + } + + #markers::after, + .inactive-marker::after { + background-color: rgba(26, 115, 232, 0.54); + } + + #labelContainer { + cursor: default; + margin-inline-start: 1px; + opacity: 0; + transition: opacity 80ms ease-in-out; + user-select: none; + width: calc(100% - 32px); + } + + #container:hover #labelContainer, + .hover #labelContainer, + :host([hold-down_]) #labelContainer { + opacity: 1; + } + + #label { + background: var(--google-blue-600); + border-radius: 14px; + bottom: 28px; + color: white; + font-size: 12px; + line-height: 1.5em; + padding: 0 8px; + position: absolute; + transition: margin-inline-start 80ms ease; + white-space: nowrap; + } + + :host([disabled]) { + pointer-events: none; + } + + :host([disabled]) #barContainer { + background-color: var(--google-grey-600-opacity-24); + } + + :host([disabled]) #bar { + background-color: var(--google-grey-600); + } + + :host([disabled]) inactive-marker::after, + :host([disabled]) #markers::after { + background-color: rgba(255, 255, 255, 0.54); + } + + :host([disabled]) #knobContainer { + margin-inline-start: 9px; + top: 9px; + } + :host([disabled]) #knob { + background-color: var(--google-grey-600); + border: 2px solid white; + box-shadow: unset; } </style> - <paper-slider id="slider" - disabled$="[[disabled]]" snaps="[[snaps]]" on-change="onChange_" - max="[[max]]" min="[[min]]" on-up="resetTrackLock_" value="{{value}}" - max-markers="[[maxMarkers]]" immediate-value="{{immediateValue}}" - dragging="{{dragging}}"> - </paper-slider> + <div id="container"> + <div id="barContainer"> + <div id="bar"></div> + <div id="markers" hidden$="[[!markerCount]]"> + <template is="dom-repeat" items="[[getMarkers_(markerCount)]]"> + <div class$="[[getMarkerClass_(index, immediateValue_, min, max, + markerCount)]]"></div> + </template> + </div> + </div> + <div id="knobContainer"> + <div id="knob" tabindex="0"></div> + </div> + <div id="labelContainer" aria-label="[[label_]]"> + <div id="label">[[label_]]</div> + </div> + </div> </template> <script src="cr_slider.js"></script> </dom-module> diff --git a/chromium/ui/webui/resources/cr_elements/cr_slider/cr_slider.js b/chromium/ui/webui/resources/cr_elements/cr_slider/cr_slider.js index 16180c12ec0..97033f5ade8 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_slider/cr_slider.js +++ b/chromium/ui/webui/resources/cr_elements/cr_slider/cr_slider.js @@ -3,148 +3,420 @@ // found in the LICENSE file. /** - * @fileoverview 'cr-slider' is a wrapper around paper-slider to alter the - * styling. The behavior of the slider remains the same. + * @fileoverview 'cr-slider' is a slider component used to select a number from + * a continuous or discrete range of numbers. */ -Polymer({ - is: 'cr-slider', - properties: { - min: Number, +cr.exportPath('cr_slider'); - max: Number, +/** + * The |value| is the corresponding value that the current slider tick is + * associated with. The string |label| is shown in the UI as the label for the + * current slider value. The |ariaValue| number is used for aria-valuemin, + * aria-valuemax, and aria-valuenow, and is optional. If missing, |value| will + * be used instead. + * @typedef {{ + * value: number, + * label: string, + * ariaValue: (number|undefined), + * }} + */ +cr_slider.SliderTick; + +(() => { + /** + * @param {number} min + * @param {number} max + * @param {number} value + * @return {number} + */ + function clamp(min, max, value) { + return Math.min(max, Math.max(min, value)); + } + + Polymer({ + is: 'cr-slider', + + behaviors: [ + Polymer.PaperRippleBehavior, + ], + + properties: { + disabled: { + type: Boolean, + value: false, + reflectToAttribute: true, + }, + + dragging: { + type: Boolean, + value: false, + reflectToAttribute: true, + }, + + markerCount: { + type: Number, + value: 0, + }, + + max: { + type: Number, + value: 100, + }, + + min: { + type: Number, + value: 0, + }, + + snaps: { + type: Boolean, + value: false, + }, + + /** + * The data associated with each tick on the slider. Each element in the + * array contains a value and the label corresponding to that value. + * @type {!Array<cr_slider.SliderTick>|!Array<number>} + */ + ticks: { + type: Array, + value: () => [], + observer: 'onTicksChanged_', + }, + + value: { + type: Number, + value: 0, + notify: true, + observer: 'onValueChanged_', + }, - snaps: { - type: Boolean, - value: true, + /** + * If true, |value| is updated while dragging happens. If false, |value| + * is updated only once, when drag gesture finishes. + */ + updateValueInstantly: { + type: Boolean, + value: true, + }, + + /** + * |immediateValue_| has the most up-to-date value and is used to render + * the slider UI. When dragging, |immediateValue_| is always updated, and + * |value| is updated at least once when dragging is stopped. + * @private + */ + immediateValue_: { + type: Number, + value: 0, + }, + + /** @private */ + holdDown_: { + type: Boolean, + value: false, + observer: 'onHoldDownChanged_', + reflectToAttribute: true, + }, + + /** @private */ + label_: { + type: String, + value: '', + }, }, - disabled: { - type: Boolean, - observer: 'onDisabledChanged_', + hostAttributes: { + role: 'slider', }, - value: Number, - maxMarkers: Number, + observers: [ + 'updateLabelAndAria_(immediateValue_, min, max)', + 'updateKnobAndBar_(immediateValue_, min, max)', + ], - immediateValue: { - type: Number, - observer: 'onImmediateValueChanged_', + listeners: { + focus: 'onFocus_', + blur: 'onBlur_', + keydown: 'onKeyDown_', + pointerdown: 'onPointerDown_', }, - dragging: Boolean, - }, + /** @private {Map<string, number>} */ + deltaKeyMap_: null, - listeners: { - 'focus': 'onFocus_', - 'blur': 'onBlur_', - 'keydown': 'onKeyDown_', - 'pointerdown': 'onPointerDown_', - 'pointerup': 'onPointerUp_', - }, + /** @private {boolean} */ + isRtl_: false, - /** @private {boolean} */ - usedMouse_: false, + /** @private {EventTracker} */ + draggingEventTracker_: null, - /** @override */ - attached: function() { - this.onDisabledChanged_(); - }, + /** @override */ + attached: function() { + this.isRtl_ = this.matches(':host-context([dir=rtl]) cr-slider'); + this.deltaKeyMap_ = new Map([ + ['ArrowDown', -1], + ['ArrowUp', 1], + ['PageDown', -1], + ['PageUp', 1], + ['ArrowLeft', this.isRtl_ ? 1 : -1], + ['ArrowRight', this.isRtl_ ? -1 : 1], + ]); + this.draggingEventTracker_ = new EventTracker(); + }, - /** @private */ - onFocus_: function() { - this.$.slider.getRipple().holdDown = true; - this.$.slider._expandKnob(); - }, + /** + * When markers are displayed on the slider, they are evenly spaced across + * the entire slider bar container and are rendered on top of the bar and + * bar container. The location of the marks correspond to the discrete + * values that the slider can have. + * @return {!Array} The array items have no type since this is used to + * create |markerCount| number of markers. + * @private + */ + getMarkers_: function() { + return new Array(Math.max(0, this.markerCount - 1)); + }, - /** @private */ - onBlur_: function() { - this.$.slider.getRipple().holdDown = false; - this.$.slider._resetKnob(); - }, + /** + * @param {number} index + * @return {string} + * @private + */ + getMarkerClass_: function(index) { + const currentStep = (this.markerCount - 1) * this.getRatio_(); + return index < currentStep ? 'active-marker' : 'inactive-marker'; + }, - /** @private */ - onChange_: function() { - this.$.slider._setExpand(!this.usedMouse_); - this.$.slider.getRipple().holdDown = !this.usedMouse_; - this.usedMouse_ = false; - }, + /** + * The ratio is a value from 0 to 1.0 corresponding to a location along the + * slider bar where 0 is the minimum value and 1.0 is the maximum value. + * This is a helper function used to calculate the bar width, knob location + * and label location. + * @return {number} + * @private + */ + getRatio_: function() { + return (this.immediateValue_ - this.min) / (this.max - this.min); + }, - /** @private */ - onKeyDown_: function() { - this.usedMouse_ = false; - if (!this.disabled) - this.onFocus_(); - }, + /** @private */ + ensureValidValue_: function() { + if (this.immediateValue_ == undefined || this.value == undefined) + return; + let validValue = clamp(this.min, this.max, this.immediateValue_); + validValue = this.snaps ? Math.round(validValue) : validValue; + this.immediateValue_ = validValue; + if (!this.dragging || this.updateValueInstantly) + this.value = validValue; + }, - /** - * @param {!MouseEvent} event - * @private - */ - onPointerDown_: function(event) { - if (this.disabled || event.button != 0) { - event.preventDefault(); - return; - } - this.usedMouse_ = true; - setTimeout(() => { - this.$.slider.getRipple().holdDown = true; - }); - }, + /** + * Removes all event listeners related to dragging, and cancels ripple. + * @param {number} pointerId + * @private + */ + stopDragging_: function(pointerId) { + this.dragging = false; + this.draggingEventTracker_.removeAll(); + this.value = this.immediateValue_; + // If there is a ripple animation in progress, setTimeout will hold off + // on updating |holdDown_|. + setTimeout(() => { + this.holdDown_ = false; + }); + this.releasePointerCapture(pointerId); + }, - /** - * @param {!MouseEvent} event - * @private - */ - onPointerUp_: function(event) { - if (event.button != 0) - return; - this.$.slider.getRipple().holdDown = false; - }, + /** @private */ + onBlur_: function() { + this.holdDown_ = false; + }, - /** - * The style is being set in this way to keep the knob size the same - * regardless of the state or properties set in the paper-slider. paper-slider - * styles alter the size in multiple places making it difficult to introduce - * one or two mixins to override the existing paper-slider knob styling. - * @private - */ - onDisabledChanged_: function() { - const knob = this.$.slider.$$('.slider-knob-inner'); - knob.style.boxSizing = 'content-box'; - knob.style.height = '10px'; - knob.style.transform = 'unset'; - knob.style.transition = 'unset'; - knob.style.width = '10px'; - this.$.slider.$$('.bar-container').style.left = '0'; - if (this.disabled) { - knob.style.backgroundColor = 'var(--google-grey-600)'; - knob.style.border = '2px solid white'; - knob.style.boxShadow = 'unset'; - knob.style.margin = '9px'; - } else { - knob.style.backgroundColor = 'var(--google-blue-600)'; - knob.style.border = '0'; - knob.style.boxShadow = '0 1px 3px 0 rgba(0, 0, 0, 0.4)'; - knob.style.margin = '11px'; - } - }, - - /** @private */ - onImmediateValueChanged_: function() { - // TODO(dpapad): Need to catch and refire the property changed event in - // Polymer 2 only, since it does not bubble by default. Remove the - // condition when migration to Polymer 2 is completed. - if (Polymer.DomIf) - this.fire('immediate-value-changed', this.immediateValue); - }, + /** @private */ + onFocus_: function() { + this.holdDown_ = true; + }, - /** - * TODO(scottchen): temporary fix until polymer gesture bug resolved. See: - * https://github.com/PolymerElements/paper-slider/issues/186 - * @private - */ - resetTrackLock_: function() { - Polymer.Gestures.gestures.tap.reset(); - }, -}); + /** @private */ + onHoldDownChanged_: function() { + this.getRipple().holdDown = this.holdDown_; + }, + + /** + * @param {!Event} event + * @private + */ + onKeyDown_: function(event) { + if (this.disabled) + return; + + if (event.metaKey || event.shiftKey || event.altKey || event.ctrlKey) + return; + + let handled = true; + if (event.key == 'Home') + this.value = this.min; + else if (event.key == 'End') + this.value = this.max; + else if (this.deltaKeyMap_.has(event.key)) { + const newValue = this.value + this.deltaKeyMap_.get(event.key); + this.value = clamp(this.min, this.max, newValue); + } else + handled = false; + + if (handled) { + event.preventDefault(); + setTimeout(() => { + this.holdDown_ = true; + }); + } + }, + + /** + * When the left-mouse button is pressed, the knob location is updated and + * dragging starts. + * @param {!PointerEvent} event + * @private + */ + onPointerDown_: function(event) { + if (this.disabled || event.buttons != 1 && event.pointerType == 'mouse') + return; + + this.dragging = true; + // If there is a ripple animation in progress, setTimeout will hold off on + // updating |holdDown_|. + setTimeout(() => { + this.$.knob.focus(); + this.holdDown_ = true; + }); + this.updateValueFromClientX_(event.clientX); + + this.setPointerCapture(event.pointerId); + const stopDragging = this.stopDragging_.bind(this, event.pointerId); + + this.draggingEventTracker_.add(this, 'pointermove', e => { + // If the left-button on the mouse is pressed by itself, then update. + // Otherwise stop capturing the mouse events because the drag operation + // is complete. + if (e.buttons != 1 && e.pointerType == 'mouse') { + stopDragging(); + return; + } + this.updateValueFromClientX_(e.clientX); + }); + this.draggingEventTracker_.add(this, 'pointercancel', stopDragging); + this.draggingEventTracker_.add(this, 'pointerdown', stopDragging); + this.draggingEventTracker_.add(this, 'pointerup', stopDragging); + this.draggingEventTracker_.add(this, 'keydown', e => { + if (e.key == 'Escape' || e.key == 'Tab') + stopDragging(); + }); + }, + + /** @private */ + onTicksChanged_: function() { + if (this.ticks.length == 0) { + this.disabled = false; + this.snaps = false; + } else if (this.ticks.length == 1) { + this.disabled = true; + } else { + this.disabled = false; + this.snaps = true; + this.max = this.ticks.length - 1; + this.min = 0; + } + this.ensureValidValue_(); + this.updateLabelAndAria_(); + }, + + /** + * Update |immediateValue_| which is used for rendering when |value| is + * updated either programmatically or from a keyboard input or a mouse drag + * (when |updateValueInstantly| is true). + * @private + */ + onValueChanged_: function() { + if (this.immediateValue_ == this.value) + return; + + this.immediateValue_ = this.value; + this.ensureValidValue_(); + }, + + /** @private */ + updateKnobAndBar_: function() { + const percent = `${this.getRatio_() * 100}%`; + this.$.bar.style.width = percent; + this.$.knob.style.marginInlineStart = percent; + }, + + /** @private */ + updateLabelAndAria_: function() { + const ticks = this.ticks; + const index = this.immediateValue_; + if (!ticks || ticks.length == 0 || index >= ticks.length || + !Number.isInteger(index) || !this.snaps) { + this.setAttribute('aria-valuetext', index); + this.setAttribute('aria-valuemin', this.min); + this.setAttribute('aria-valuemax', this.max); + this.setAttribute('aria-valuenow', index); + return; + } + const tick = ticks[index]; + this.label_ = Number.isFinite(tick) ? '' : tick.label; + + // Update label location after it has been rendered. + this.async(() => { + const label = this.$.label; + const parentWidth = label.parentElement.offsetWidth; + const labelWidth = label.offsetWidth; + // The left and right margin are 16px. + const margin = 16; + const knobLocation = parentWidth * this.getRatio_() + margin; + const offsetStart = knobLocation - (labelWidth / 2); + // The label should be centered over the knob. Clamping the offset to a + // min and max value prevents the label from being cutoff. + const max = parentWidth + 2 * margin - labelWidth; + label.style.marginInlineStart = + `${Math.round(clamp(0, max, offsetStart))}px`; + }); + + const ariaValues = [tick, ticks[0], ticks[ticks.length - 1]].map(t => { + if (Number.isFinite(t)) + return t; + return Number.isFinite(t.ariaValue) ? t.ariaValue : t.value; + }); + this.setAttribute( + 'aria-valuetext', + this.label_.length > 0 ? this.label_ : ariaValues[0]); + this.setAttribute('aria-valuenow', ariaValues[0]); + this.setAttribute('aria-valuemin', ariaValues[1]); + this.setAttribute('aria-valuemax', ariaValues[2]); + }, + + /** + * @param {number} clientX + * @private + */ + updateValueFromClientX_: function(clientX) { + const rect = this.$.barContainer.getBoundingClientRect(); + let ratio = (clientX - rect.left) / rect.width; + if (this.isRtl_) + ratio = 1 - ratio; + this.immediateValue_ = ratio * (this.max - this.min) + this.min; + this.ensureValidValue_(); + }, + + _createRipple: function() { + this._rippleContainer = this.$.knob; + const ripple = Polymer.PaperRippleBehavior._createRipple(); + ripple.id = 'ink'; + ripple.setAttribute('recenters', ''); + ripple.classList.add('circle', 'toggle-ink'); + return ripple; + }, + }); +})(); diff --git a/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html b/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html index 635bfe77f64..0f03fdf8a69 100644 --- a/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html +++ b/chromium/ui/webui/resources/cr_elements/cr_toolbar/cr_toolbar_search_field.html @@ -150,7 +150,6 @@ on-keydown="onSearchTermKeydown_" on-focus="onInputFocus_" on-blur="onInputBlur_" - incremental autofocus spellcheck="false"> </div> diff --git a/chromium/ui/webui/resources/cr_elements/cr_view_manager/BUILD.gn b/chromium/ui/webui/resources/cr_elements/cr_view_manager/BUILD.gn new file mode 100644 index 00000000000..e1de4429a50 --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/cr_view_manager/BUILD.gn @@ -0,0 +1,19 @@ +# 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. + +import("//third_party/closure_compiler/compile_js.gni") + +js_type_check("closure_compile") { + deps = [ + ":cr_view_manager", + ] +} + +js_library("cr_view_manager") { + deps = [ + "//ui/webui/resources/js:assert", + "//ui/webui/resources/js:cr", + ] + externs_list = [ "$externs_path/web_animations.js" ] +} diff --git a/chromium/ui/webui/resources/cr_elements/cr_view_manager/cr_view_manager.html b/chromium/ui/webui/resources/cr_elements/cr_view_manager/cr_view_manager.html new file mode 100644 index 00000000000..45ff7d81938 --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/cr_view_manager/cr_view_manager.html @@ -0,0 +1,26 @@ +<link rel="import" href="../../html/polymer.html"> + +<link rel="import" href="../../html/assert.html"> +<link rel="import" href="../../html/cr.html"> + +<dom-module id="cr-view-manager"> + <template> + <style> + :host ::slotted([slot=view]) { + bottom: 0; + display: none; + left: 0; + position: absolute; + right: 0; + top: 0; + } + + :host ::slotted(.active), + :host ::slotted(.closing) { + display: block; + } + </style> + <slot name="view"></slot> + </template> + <script src="cr_view_manager.js"></script> +</dom-module> diff --git a/chromium/ui/webui/resources/cr_elements/cr_view_manager/cr_view_manager.js b/chromium/ui/webui/resources/cr_elements/cr_view_manager/cr_view_manager.js new file mode 100644 index 00000000000..44f67b9877d --- /dev/null +++ b/chromium/ui/webui/resources/cr_elements/cr_view_manager/cr_view_manager.js @@ -0,0 +1,115 @@ +// 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. + +(function() { +/** + * TODO(scottchen): shim for not having Animation.finished implemented. Can + * replace with Animation.finished if Chrome implements it (see: + * crbug.com/257235). + * @param {!Animation} animation + * @return {!Promise} + */ +function whenFinished(animation) { + return new Promise(function(resolve, reject) { + animation.addEventListener('finish', resolve); + }); +} + +/** @type {!Map<string, function(!Element): !Promise>} */ +const viewAnimations = new Map(); +viewAnimations.set('no-animation', () => Promise.resolve()); +viewAnimations.set('fade-in', element => { + const animation = element.animate( + { + opacity: [0, 1], + }, + /** @type {!KeyframeEffectOptions} */ ({ + duration: 180, + easing: 'ease-in-out', + iterations: 1, + })); + + return whenFinished(animation); +}); +viewAnimations.set('fade-out', element => { + const animation = element.animate( + { + opacity: [1, 0], + }, + /** @type {!KeyframeEffectOptions} */ ({ + duration: 180, + easing: 'ease-in-out', + iterations: 1, + })); + + return whenFinished(animation); +}); + +Polymer({ + is: 'cr-view-manager', + + /** + * @param {!Element} element + * @param {string} animation + * @return {!Promise} + * @private + */ + exit_: function(element, animation) { + const animationFunction = viewAnimations.get(animation); + assert(animationFunction); + + element.classList.remove('active'); + element.classList.add('closing'); + element.dispatchEvent( + new CustomEvent('view-exit-start', {bubbles: true, composed: true})); + return animationFunction(element).then(function() { + element.classList.remove('closing'); + element.dispatchEvent( + new CustomEvent('view-exit-finish', {bubbles: true, composed: true})); + }); + }, + + /** + * @param {!Element} view + * @param {string} animation + * @return {!Promise} + * @private + */ + enter_: function(view, animation) { + const animationFunction = viewAnimations.get(animation); + assert(animationFunction); + + let effectiveView = view.matches('cr-lazy-render') ? view.get() : view; + + effectiveView.classList.add('active'); + effectiveView.dispatchEvent( + new CustomEvent('view-enter-start', {bubbles: true, composed: true})); + return animationFunction(effectiveView).then(() => { + effectiveView.dispatchEvent(new CustomEvent( + 'view-enter-finish', {bubbles: true, composed: true})); + }); + }, + + /** + * @param {string} newViewId + * @param {string=} enterAnimation + * @param {string=} exitAnimation + * @return {!Promise} + */ + switchView: function(newViewId, enterAnimation, exitAnimation) { + const previousView = this.querySelector('.active'); + const newView = assert(this.querySelector('#' + newViewId)); + + const promises = []; + if (previousView) { + promises.push(this.exit_(previousView, exitAnimation || 'fade-out')); + promises.push(this.enter_(newView, enterAnimation || 'fade-in')); + } else { + promises.push(this.enter_(newView, 'no-animation')); + } + + return Promise.all(promises); + }, +}); +})();
\ No newline at end of file diff --git a/chromium/ui/webui/resources/cr_elements/policy/BUILD.gn b/chromium/ui/webui/resources/cr_elements/policy/BUILD.gn index 244f3a615eb..743f98969f3 100644 --- a/chromium/ui/webui/resources/cr_elements/policy/BUILD.gn +++ b/chromium/ui/webui/resources/cr_elements/policy/BUILD.gn @@ -54,6 +54,7 @@ js_library("cr_policy_network_indicator") { deps = [ ":cr_policy_indicator_behavior", ":cr_policy_network_behavior", + ":cr_tooltip_icon", "../chromeos/network:cr_onc_types", ] } diff --git a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_behavior.js b/chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_behavior.js index eb89b2b66db..c20b1ca4ec6 100644 --- a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_behavior.js +++ b/chromium/ui/webui/resources/cr_elements/policy/cr_policy_network_behavior.js @@ -53,22 +53,42 @@ var CrPolicyNetworkBehavior = { /** * @param {!CrOnc.ManagedProperty|undefined} property - * @return {boolean} True if the network property is enforced by a policy. + * @return {boolean} True if the network property is editable. */ - isNetworkPolicyEnforced: function(property) { - if (!this.isNetworkPolicyControlled(property)) + isEditable: function(property) { + // If the property is not a dictionary, then the property is not editable. + if (typeof property != 'object') return false; + // If the property has a UserEditable sub-property, that determines whether - // or not it is editable (not enforced). + // or not it is editable. if (typeof property.UserEditable != 'undefined') - return !property.UserEditable; + return property.UserEditable; // Otherwise if the property has a DeviceEditable sub-property, check that. if (typeof property.DeviceEditable != 'undefined') - return !property.DeviceEditable; + return property.DeviceEditable; + + // If no 'Editable' sub-property exists, the policy value is not editable. + return false; + }, + + /** + * @param {!CrOnc.ManagedProperty|undefined} property + * @return {boolean} True if the network property is enforced by a policy. + */ + isNetworkPolicyEnforced: function(property) { + return this.isNetworkPolicyControlled(property) && + !this.isEditable(property); + }, - // If no 'Editable' sub-property exists, the policy value is enforced. - return true; + /** + * @param {!CrOnc.ManagedProperty|undefined} property + * @return {boolean} True if the network property is recommended by a policy. + */ + isNetworkPolicyRecommended: function(property) { + return this.isNetworkPolicyControlled(property) && + this.isEditable(property); }, /** diff --git a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_pref_indicator.js b/chromium/ui/webui/resources/cr_elements/policy/cr_policy_pref_indicator.js index 447aa7fb810..b83f94971b0 100644 --- a/chromium/ui/webui/resources/cr_elements/policy/cr_policy_pref_indicator.js +++ b/chromium/ui/webui/resources/cr_elements/policy/cr_policy_pref_indicator.js @@ -78,4 +78,9 @@ Polymer({ return this.getIndicatorTooltip( indicatorType, this.pref.controlledByName || '', matches); }, + + /** @return {!Element} */ + getFocusableElement: function() { + return this.$$('cr-tooltip-icon').getFocusableElement(); + }, }); diff --git a/chromium/ui/webui/resources/cr_elements/policy/cr_tooltip_icon.js b/chromium/ui/webui/resources/cr_elements/policy/cr_tooltip_icon.js index ba918fb0ca7..cd8cd9cc2ab 100644 --- a/chromium/ui/webui/resources/cr_elements/policy/cr_tooltip_icon.js +++ b/chromium/ui/webui/resources/cr_elements/policy/cr_tooltip_icon.js @@ -9,4 +9,9 @@ Polymer({ iconClass: String, tooltipText: String, }, + + /** @return {!Element} */ + getFocusableElement: function() { + return this.$.indicator; + }, });
\ No newline at end of file diff --git a/chromium/ui/webui/resources/cr_elements/shared_vars_css.html b/chromium/ui/webui/resources/cr_elements/shared_vars_css.html index b98339dcb19..f774f420268 100644 --- a/chromium/ui/webui/resources/cr_elements/shared_vars_css.html +++ b/chromium/ui/webui/resources/cr_elements/shared_vars_css.html @@ -142,13 +142,15 @@ --cr-disabled-opacity: 0.38; --cr-form-field-bottom-spacing: 16px; --cr-form-field-label-font-size: 0.625rem; + --cr-form-field-label-height: 0.625rem; + --cr-form-field-label-line-height: 0.625rem; --cr-form-field-label: { color: var(--google-grey-refresh-700); display: block; font-size: var(--cr-form-field-label-font-size); font-weight: 500; letter-spacing: 0.4px; - line-height: var(--cr-form-field-label-font-size); + line-height: var(--cr-form-field-label-line-height); margin-bottom: 8px; } --google-blue-50: #E8F0FE; diff --git a/chromium/ui/webui/resources/cr_elements_resources.grdp b/chromium/ui/webui/resources/cr_elements_resources.grdp index 26c467315df..040b089fbef 100644 --- a/chromium/ui/webui/resources/cr_elements_resources.grdp +++ b/chromium/ui/webui/resources/cr_elements_resources.grdp @@ -126,6 +126,12 @@ file="cr_elements/cr_searchable_drop_down/cr_searchable_drop_down.js" type="chrome_html" compress="gzip" /> + <structure name="IDR_CR_ELEMENTS_CR_VIEW_MANAGER_HTML" + file="cr_elements/cr_view_manager/cr_view_manager.html" + type="chrome_html" /> + <structure name="IDR_CR_ELEMENTS_CR_VIEW_MANAGER_JS" + file="cr_elements/cr_view_manager/cr_view_manager.js" + type="chrome_html" /> <if expr="chromeos"> <structure name="IDR_CR_ELEMENTS_CHROMEOS_CR_PICTURE_CR_CAMERA_HTML" file="cr_elements/chromeos/cr_picture/cr_camera.html" diff --git a/chromium/ui/webui/resources/js/assert.js b/chromium/ui/webui/resources/js/assert.js index 42fb523fb07..e46a056778e 100644 --- a/chromium/ui/webui/resources/js/assert.js +++ b/chromium/ui/webui/resources/js/assert.js @@ -22,6 +22,8 @@ function assert(condition, opt_message) { message = message + ': ' + opt_message; var error = new Error(message); var global = function() { + /** @type {boolean} */ + this.traceAssertionsForTesting; return this; }(); if (global.traceAssertionsForTesting) diff --git a/chromium/ui/webui/resources/js/cr/ui/dialogs.js b/chromium/ui/webui/resources/js/cr/ui/dialogs.js index 40e86f6c368..a2aff076194 100644 --- a/chromium/ui/webui/resources/js/cr/ui/dialogs.js +++ b/chromium/ui/webui/resources/js/cr/ui/dialogs.js @@ -19,6 +19,9 @@ cr.define('cr.ui.dialogs', function() { this.previousActiveElement_ = null; this.initDom_(); + + /** @private{boolean} */ + this.showing_ = false; } /** @@ -214,6 +217,7 @@ cr.define('cr.ui.dialogs', function() { */ BaseDialog.prototype.show_ = function( title, opt_onOk, opt_onCancel, opt_onShow) { + this.showing_ = true; // Make all outside nodes unfocusable while the dialog is active. this.deactivatedNodes_ = this.findFocusableElements_(this.document_); this.tabIndexes_ = this.deactivatedNodes_.map(function(n) { @@ -239,10 +243,11 @@ cr.define('cr.ui.dialogs', function() { var self = this; setTimeout(function() { - // Note that we control the opacity of the *container*, but the top/left - // of the *frame*. - self.container_.classList.add('shown'); - self.initialFocusElement_.focus(); + // Check that hide() was not called in between. + if (self.showing_) { + self.container_.classList.add('shown'); + self.initialFocusElement_.focus(); + } setTimeout(function() { if (opt_onShow) opt_onShow(); @@ -252,6 +257,7 @@ cr.define('cr.ui.dialogs', function() { /** @param {Function=} opt_onHide */ BaseDialog.prototype.hide = function(opt_onHide) { + this.showing_ = false; // Restore focusability. for (var i = 0; i < this.deactivatedNodes_.length; i++) { var node = this.deactivatedNodes_[i]; @@ -263,8 +269,6 @@ cr.define('cr.ui.dialogs', function() { this.deactivatedNodes_ = null; this.tabIndexes_ = null; - // Note that we control the opacity of the *container*, but the top/left - // of the *frame*. this.container_.classList.remove('shown'); if (this.previousActiveElement_) { @@ -277,9 +281,10 @@ cr.define('cr.ui.dialogs', function() { var self = this; setTimeout(function() { // Wait until the transition is done before removing the dialog. - // It is possible to show/hide/show/hide and have hide called twice + // Check show() was not called in between. + // It is also possible to show/hide/show/hide and have hide called twice // and container_ already removed from parentNode_. - if (self.parentNode_ === self.container_.parentNode) + if (!self.showing_ && self.parentNode_ === self.container_.parentNode) self.parentNode_.removeChild(self.container_); if (opt_onHide) opt_onHide(); diff --git a/chromium/ui/webui/resources/js/cr/ui/focus_grid.js b/chromium/ui/webui/resources/js/cr/ui/focus_grid.js index 000fb051966..de710112a36 100644 --- a/chromium/ui/webui/resources/js/cr/ui/focus_grid.js +++ b/chromium/ui/webui/resources/js/cr/ui/focus_grid.js @@ -25,7 +25,7 @@ cr.define('cr.ui', function() { * focusable focusable focusable * * @constructor - * @implements {cr.ui.FocusRow.Delegate} + * @implements {cr.ui.FocusRowDelegate} */ function FocusGrid() { /** @type {!Array<!cr.ui.FocusRow>} */ diff --git a/chromium/ui/webui/resources/js/cr/ui/focus_row.js b/chromium/ui/webui/resources/js/cr/ui/focus_row.js index 91399fb5250..8915b114a4a 100644 --- a/chromium/ui/webui/resources/js/cr/ui/focus_row.js +++ b/chromium/ui/webui/resources/js/cr/ui/focus_row.js @@ -14,84 +14,76 @@ cr.define('cr.ui', function() { * If no items in this row are focused, the row can stay active until focus * changes to a node inside |this.boundary_|. If |boundary| isn't specified, * any focus change deactivates the row. - * - * @param {!Element} root The root of this focus row. Focus classes are - * applied to |root| and all added elements must live within |root|. - * @param {?Element} boundary Focus events are ignored outside of this - * element. - * @param {cr.ui.FocusRow.Delegate=} opt_delegate An optional event delegate. - * @constructor */ - function FocusRow(root, boundary, opt_delegate) { - /** @type {!Element} */ - this.root = root; - - /** @private {!Element} */ - this.boundary_ = boundary || document.documentElement; - - /** @type {cr.ui.FocusRow.Delegate|undefined} */ - this.delegate = opt_delegate; - - /** @protected {!EventTracker} */ - this.eventTracker = new EventTracker; - } - - /** @interface */ - FocusRow.Delegate = function() {}; - - FocusRow.Delegate.prototype = { + class FocusRow { /** - * Called when a key is pressed while on a FocusRow's item. If true is - * returned, further processing is skipped. - * @param {!cr.ui.FocusRow} row The row that detected a keydown. - * @param {!Event} e - * @return {boolean} Whether the event was handled. + * @param {!Element} root The root of this focus row. Focus classes are + * applied to |root| and all added elements must live within |root|. + * @param {?Element} boundary Focus events are ignored outside of this + * element. + * @param {cr.ui.FocusRowDelegate=} delegate An optional event + * delegate. */ - onKeydown: assertNotReached, + constructor(root, boundary, delegate) { + /** @type {!Element} */ + this.root = root; - /** - * @param {!cr.ui.FocusRow} row - * @param {!Event} e - */ - onFocus: assertNotReached, - }; + /** @private {!Element} */ + this.boundary_ = boundary || document.documentElement; - /** @const {string} */ - FocusRow.ACTIVE_CLASS = 'focus-row-active'; + /** @type {cr.ui.FocusRowDelegate|undefined} */ + this.delegate = delegate; - /** - * Whether it's possible that |element| can be focused. - * @param {Element} element - * @return {boolean} Whether the item is focusable. - */ - FocusRow.isFocusable = function(element) { - if (!element || element.disabled) - return false; + /** @protected {!EventTracker} */ + this.eventTracker = new EventTracker; + } - // We don't check that element.tabIndex >= 0 here because inactive rows set - // a tabIndex of -1. + /** + * Whether it's possible that |element| can be focused. + * @param {Element} element + * @return {boolean} Whether the item is focusable. + */ + static isFocusable(element) { + if (!element || element.disabled) + return false; - function isVisible(element) { - assertInstanceof(element, Element); + // We don't check that element.tabIndex >= 0 here because inactive rows + // set a tabIndex of -1. + let current = element; + while (true) { + assertInstanceof(current, Element); - var style = window.getComputedStyle(element); - if (style.visibility == 'hidden' || style.display == 'none') - return false; + var style = window.getComputedStyle(current); + if (style.visibility == 'hidden' || style.display == 'none') + return false; - var parent = element.parentNode; - if (!parent) - return false; + var parent = current.parentNode; + if (!parent) + return false; - if (parent == element.ownerDocument || parent instanceof DocumentFragment) - return true; + if (parent == current.ownerDocument || + parent instanceof DocumentFragment) + return true; - return isVisible(parent); + current = /** @type {Element} */ (parent); + } } - return isVisible(element); - }; + /** + * A focus override is a function that returns an element that should gain + * focus. The element may not be directly selectable for example the element + * that can gain focus is in a shadow DOM. Allowing an override via a + * function leaves the details of how the element is retrieved to the + * component. + * @param {!Element} element + * @return {!Element} + */ + static getFocusableElement(element) { + if (element.getFocusableElement) + return element.getFocusableElement(); + return element; + } - FocusRow.prototype = { /** * Register a new type of focusable element (or add to an existing one). * @@ -100,7 +92,7 @@ cr.define('cr.ui', function() { * When FocusRow is used within a FocusGrid, these types are used to * determine equivalent controls when Up/Down are pressed to change rows. * - * Another example: mutually exclusive controls that hide eachother on + * Another example: mutually exclusive controls that hide each other on * activation (i.e. Play/Pause) could use the same type (i.e. 'play-pause') * to indicate they're equivalent. * @@ -109,7 +101,7 @@ cr.define('cr.ui', function() { * from this row's root, or the element itself. * @return {boolean} Whether a new item was added. */ - addItem: function(type, selectorOrElement) { + addItem(type, selectorOrElement) { assert(type); var element; @@ -128,30 +120,30 @@ cr.define('cr.ui', function() { this.eventTracker.add(element, 'keydown', this.onKeydown_.bind(this)); this.eventTracker.add(element, 'mousedown', this.onMousedown_.bind(this)); return true; - }, + } /** Dereferences nodes and removes event handlers. */ - destroy: function() { + destroy() { this.eventTracker.removeAll(); - }, + } /** * @param {!Element} sampleElement An element for to find an equivalent for. * @return {!Element} An equivalent element to focus for |sampleElement|. * @protected */ - getCustomEquivalent: function(sampleElement) { + getCustomEquivalent(sampleElement) { return assert(this.getFirstFocusable()); - }, + } /** * @return {!Array<!Element>} All registered elements (regardless of * focusability). */ - getElements: function() { - var elements = this.root.querySelectorAll('[focus-type]'); - return Array.prototype.slice.call(elements); - }, + getElements() { + return Array.from(this.root.querySelectorAll('[focus-type]')) + .map(cr.ui.FocusRow.getFocusableElement); + } /** * Find the element that best matches |sampleElement|. @@ -159,7 +151,7 @@ cr.define('cr.ui', function() { * which previously held focus. * @return {!Element} The element that best matches sampleElement. */ - getEquivalentElement: function(sampleElement) { + getEquivalentElement(sampleElement) { if (this.getFocusableElements().indexOf(sampleElement) >= 0) return sampleElement; @@ -171,46 +163,42 @@ cr.define('cr.ui', function() { } return this.getCustomEquivalent(sampleElement); - }, + } /** * @param {string=} opt_type An optional type to search for. * @return {?Element} The first focusable element with |type|. */ - getFirstFocusable: function(opt_type) { - var filter = opt_type ? '="' + opt_type + '"' : ''; - var elements = this.root.querySelectorAll('[focus-type' + filter + ']'); - for (var i = 0; i < elements.length; ++i) { - if (cr.ui.FocusRow.isFocusable(elements[i])) - return elements[i]; - } - return null; - }, + getFirstFocusable(opt_type) { + const element = this.getFocusableElements().find( + el => !opt_type || el.getAttribute('focus-type') == opt_type); + return element || null; + } /** @return {!Array<!Element>} Registered, focusable elements. */ - getFocusableElements: function() { + getFocusableElements() { return this.getElements().filter(cr.ui.FocusRow.isFocusable); - }, + } /** * @param {!Element} element An element to determine a focus type for. * @return {string} The focus type for |element| or '' if none. */ - getTypeForElement: function(element) { + getTypeForElement(element) { return element.getAttribute('focus-type') || ''; - }, + } /** @return {boolean} Whether this row is currently active. */ - isActive: function() { + isActive() { return this.root.classList.contains(FocusRow.ACTIVE_CLASS); - }, + } /** * Enables/disables the tabIndex of the focusable elements in the FocusRow. * tabIndex can be set properly. * @param {boolean} active True if tab is allowed for this row. */ - makeActive: function(active) { + makeActive(active) { if (active == this.isActive()) return; @@ -219,35 +207,35 @@ cr.define('cr.ui', function() { }); this.root.classList.toggle(FocusRow.ACTIVE_CLASS, active); - }, + } /** * @param {!Event} e * @private */ - onBlur_: function(e) { + onBlur_(e) { if (!this.boundary_.contains(/** @type {Element} */ (e.relatedTarget))) return; var currentTarget = /** @type {!Element} */ (e.currentTarget); if (this.getFocusableElements().indexOf(currentTarget) >= 0) this.makeActive(false); - }, + } /** * @param {!Event} e * @private */ - onFocus_: function(e) { + onFocus_(e) { if (this.delegate) this.delegate.onFocus(this, e); - }, + } /** * @param {!Event} e A mousedown event. * @private */ - onMousedown_: function(e) { + onMousedown_(e) { // Only accept left mouse clicks. if (e.button) return; @@ -255,13 +243,13 @@ cr.define('cr.ui', function() { // Allow the element under the mouse cursor to be focusable. if (!e.currentTarget.disabled) e.currentTarget.tabIndex = 0; - }, + } /** * @param {!Event} e The keydown event. * @private */ - onKeydown_: function(e) { + onKeydown_(e) { var elements = this.getFocusableElements(); var currentElement = /** @type {!Element} */ (e.currentTarget); var elementIndex = elements.indexOf(currentElement); @@ -289,10 +277,33 @@ cr.define('cr.ui', function() { this.getEquivalentElement(elementToFocus).focus(); e.preventDefault(); } - }, - }; + } + } + + /** @const {string} */ + FocusRow.ACTIVE_CLASS = 'focus-row-active'; + + + /** @interface */ + class FocusRowDelegate { + /** + * Called when a key is pressed while on a FocusRow's item. If true is + * returned, further processing is skipped. + * @param {!cr.ui.FocusRow} row The row that detected a keydown. + * @param {!Event} e + * @return {boolean} Whether the event was handled. + */ + onKeydown(row, e) {} + + /** + * @param {!cr.ui.FocusRow} row + * @param {!Event} e + */ + onFocus(row, e) {} + } return { - FocusRow: FocusRow, + FocusRow, + FocusRowDelegate, }; }); diff --git a/chromium/ui/webui/resources/js/util.js b/chromium/ui/webui/resources/js/util.js index a5099bf9b56..7139d9baede 100644 --- a/chromium/ui/webui/resources/js/util.js +++ b/chromium/ui/webui/resources/js/util.js @@ -33,6 +33,18 @@ function getSVGElement(id) { } /** + * @return {?Element} The currently focused element (including elements that are + * behind a shadow root), or null if nothing is focused. + */ +function getDeepActiveElement() { + var a = document.activeElement; + while (a && a.shadowRoot && a.shadowRoot.activeElement) { + a = a.shadowRoot.activeElement; + } + return a; +} + +/** * Add an accessible message to the page that will be announced to * users who have spoken feedback on, but will be invisible to all * other users. It's removed right away so it doesn't clutter the DOM. diff --git a/chromium/ui/webui/resources/js/webui_resource_test.js b/chromium/ui/webui/resources/js/webui_resource_test.js index ed8d79e401c..ea7089b0bac 100644 --- a/chromium/ui/webui/resources/js/webui_resource_test.js +++ b/chromium/ui/webui/resources/js/webui_resource_test.js @@ -116,9 +116,28 @@ function assertDeepEquals(expected, observed, opt_message) { } /** - * Defines runTests. + * Decorates |window| with runTests() and endTests(). + * + * @param {{ + * runTests: (function(Object=):void|undefined), + * endTests: (function(boolean):void|undefined) + * }} exports */ (function(exports) { + +/** + * Optional setup and teardown hooks that can be defined in a test scope. + * |setUpPage| is invoked once. |setUp|/|tearDown| are invoked before/after each + * test*() declared in the test scope. + * + * @typedef {{ + * setUpPage: (function(): void|undefined), + * setUp: (function(): void|undefined), + * tearDown: (function(): void|undefined), + * }} + */ +var WebUiTestHarness; + /** * Scope containing testXXX functions. * @type {!Object} @@ -126,6 +145,12 @@ function assertDeepEquals(expected, observed, opt_message) { var testScope = {}; /** + * Test harness entrypoints on |testScope|. + * @type {!WebUiTestHarness} + */ +var testHarness = {}; + +/** * List of test cases. * @type {Array<string>} List of function names for tests to run. */ @@ -170,6 +195,7 @@ var runnerStartTime = 0; function runTests(opt_testScope) { runnerStartTime = performance.now(); testScope = opt_testScope || window; + testHarness = /** @type{!WebUiTestHarness} */ (testScope); for (var name in testScope) { // To avoid unnecessary getting properties, test name first. if (/^test/.test(name) && typeof testScope[name] == 'function') @@ -180,11 +206,22 @@ function runTests(opt_testScope) { cleanTestRun = false; } try { - if (testScope.setUpPage) - testScope.setUpPage(); + if (testHarness.setUpPage) + testHarness.setUpPage(); } catch (err) { cleanTestRun = false; } + startTesting(); +} + +/** + * @suppress {missingProperties} + */ +function startTesting() { + if (window.waitUser) { + setTimeout(startTesting, 1000); + return; + } continueTesting(); } @@ -216,9 +253,9 @@ function continueTesting(opt_asyncTestFailure) { var isAsyncTest = testScope[testName].length; var testError = false; try { - if (testScope.setUp) - testScope.setUp(); - pendingTearDown = testScope.tearDown || null; + if (testHarness.setUp) + testHarness.setUp(); + pendingTearDown = testHarness.tearDown || null; testScope[testName](continueTesting); } catch (err) { console.error('Failure in test ' + testName + '\n' + err); @@ -257,6 +294,16 @@ exports.runTests = runTests; exports.endTests = endTests; })(window); +/** + * @type {!function(Object=):void} + */ +window.runTests; + +/** + * @type {!function(boolean):void} + */ +window.endTests; + window.onerror = function() { window.endTests(false); }; diff --git a/chromium/ui/webui/resources/webui_resources.grd b/chromium/ui/webui/resources/webui_resources.grd index a24f4983bf4..a2b2e2d14bc 100644 --- a/chromium/ui/webui/resources/webui_resources.grd +++ b/chromium/ui/webui/resources/webui_resources.grd @@ -200,6 +200,7 @@ without changes to the corresponding grd file. --> file="images/trash.png" type="BINDATA" /> <if expr="not is_android"> + <part file="cr_components/cr_components_images.grdp" /> <part file="cr_elements_images.grdp" /> </if> </includes> diff --git a/chromium/ui/wm/BUILD.gn b/chromium/ui/wm/BUILD.gn index 3df69ca6fc4..38d53f54b50 100644 --- a/chromium/ui/wm/BUILD.gn +++ b/chromium/ui/wm/BUILD.gn @@ -98,7 +98,7 @@ jumbo_component("wm") { } } -static_library("test_support") { +jumbo_static_library("test_support") { testonly = true sources = [ "test/testing_cursor_client_observer.cc", @@ -113,6 +113,7 @@ static_library("test_support") { "//services/ws/public/cpp", "//services/ws/public/cpp/input_devices", "//services/ws/public/mojom", + "//services/ws/test_ws:mojom", "//skia", "//ui/aura", "//ui/aura:test_support", @@ -130,7 +131,6 @@ test("wm_unittests") { "core/compound_event_filter_unittest.cc", "core/coordinate_conversion_unittest.cc", "core/cursor_manager_unittest.cc", - "core/easy_resize_window_targeter_unittest.cc", "core/focus_controller_unittest.cc", "core/shadow_controller_unittest.cc", "core/transient_window_manager_unittest.cc", diff --git a/chromium/ui/wm/core/base_focus_rules.cc b/chromium/ui/wm/core/base_focus_rules.cc index 73e95142f12..eb446ee0fc9 100644 --- a/chromium/ui/wm/core/base_focus_rules.cc +++ b/chromium/ui/wm/core/base_focus_rules.cc @@ -178,9 +178,7 @@ aura::Window* BaseFocusRules::GetNextActivatableWindow( const aura::Window::Windows& siblings = ignore->parent()->children(); DCHECK(!siblings.empty()); - for (aura::Window::Windows::const_reverse_iterator rit = siblings.rbegin(); - rit != siblings.rend(); - ++rit) { + for (auto rit = siblings.rbegin(); rit != siblings.rend(); ++rit) { aura::Window* cur = *rit; if (cur == ignore) continue; diff --git a/chromium/ui/wm/core/easy_resize_window_targeter.cc b/chromium/ui/wm/core/easy_resize_window_targeter.cc index 2ada01797de..7707c2d2df7 100644 --- a/chromium/ui/wm/core/easy_resize_window_targeter.cc +++ b/chromium/ui/wm/core/easy_resize_window_targeter.cc @@ -6,104 +6,23 @@ #include <algorithm> -#include "services/ws/public/mojom/window_manager.mojom.h" +#include "services/ws/public/mojom/window_tree_constants.mojom.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/client/transient_window_client.h" -#include "ui/aura/env.h" -#include "ui/aura/mus/window_port_mus.h" -#include "ui/aura/mus/window_tree_client.h" #include "ui/aura/window.h" #include "ui/events/event.h" -#include "ui/gfx/geometry/insets_f.h" -#include "ui/gfx/geometry/rect.h" +#include "ui/gfx/geometry/insets.h" namespace wm { -namespace { - -gfx::Insets InsetsWithOnlyPositiveValues(const gfx::Insets& insets) { - return gfx::Insets(std::max(0, insets.top()), std::max(0, insets.left()), - std::max(0, insets.bottom()), std::max(0, insets.right())); -} - -} // namespace - -// HitMaskSetter is responsible for setting the hit-test mask on a Window. -class EasyResizeWindowTargeter::HitMaskSetter : public aura::WindowObserver { - public: - explicit HitMaskSetter(aura::Window* window) : window_(window) { - window_->AddObserver(this); - } - ~HitMaskSetter() override { - if (window_) { - aura::WindowPortMus::Get(window_)->SetHitTestMask(base::nullopt); - window_->RemoveObserver(this); - } - } - - void SetHitMaskInsets(const gfx::Insets& insets) { - if (insets == insets_) - return; - - insets_ = insets; - ApplyHitTestMask(); - } - - private: - void ApplyHitTestMask() { - base::Optional<gfx::Rect> hit_test_mask( - gfx::Rect(window_->bounds().size())); - hit_test_mask->Inset(insets_); - aura::WindowPortMus::Get(window_)->SetHitTestMask(hit_test_mask); - } - - // aura::WindowObserver: - void OnWindowDestroying(aura::Window* window) override { - window_->RemoveObserver(this); - window_ = nullptr; - } - void OnWindowBoundsChanged(aura::Window* window, - const gfx::Rect& old_bounds, - const gfx::Rect& new_bounds, - ui::PropertyChangeReason reason) override { - ApplyHitTestMask(); - } - - private: - aura::Window* window_; - gfx::Insets insets_; - - DISALLOW_COPY_AND_ASSIGN(HitMaskSetter); -}; EasyResizeWindowTargeter::EasyResizeWindowTargeter( - aura::Window* container, const gfx::Insets& mouse_extend, - const gfx::Insets& touch_extend) - : container_(container) { - DCHECK(container_); + const gfx::Insets& touch_extend) { SetInsets(mouse_extend, touch_extend); } EasyResizeWindowTargeter::~EasyResizeWindowTargeter() {} -void EasyResizeWindowTargeter::OnSetInsets( - const gfx::Insets& last_mouse_extend, - const gfx::Insets& last_touch_extend) { - if (container_->env()->mode() != aura::Env::Mode::MUS) - return; - - // Positive values equate to a hit test mask. - const gfx::Insets positive_mouse_insets = - InsetsWithOnlyPositiveValues(mouse_extend()); - if (positive_mouse_insets.IsEmpty()) { - hit_mask_setter_.reset(); - } else { - if (!hit_mask_setter_) - hit_mask_setter_ = std::make_unique<HitMaskSetter>(container_); - hit_mask_setter_->SetHitMaskInsets(positive_mouse_insets); - } -} - bool EasyResizeWindowTargeter::EventLocationInsideBounds( aura::Window* target, const ui::LocatedEvent& event) const { @@ -111,28 +30,28 @@ bool EasyResizeWindowTargeter::EventLocationInsideBounds( } bool EasyResizeWindowTargeter::ShouldUseExtendedBounds( - const aura::Window* window) const { - // Use the extended bounds only for immediate child windows of |container_|. + const aura::Window* w) const { + DCHECK(window()); + // Use the extended bounds only for immediate child windows of window(). // Use the default targeter otherwise. - if (window->parent() != container_) + if (w->parent() != window()) return false; // Only resizable windows benefit from the extended hit-test region. - if ((window->GetProperty(aura::client::kResizeBehaviorKey) & + if ((w->GetProperty(aura::client::kResizeBehaviorKey) & ws::mojom::kResizeBehaviorCanResize) == 0) { return false; } // For transient children use extended bounds if a transient parent or if - // transient parent's parent is a top level window in |container_|. + // transient parent's parent is a top level window in window(). aura::client::TransientWindowClient* transient_window_client = aura::client::GetTransientWindowClient(); const aura::Window* transient_parent = - transient_window_client - ? transient_window_client->GetTransientParent(window) - : nullptr; - return !transient_parent || transient_parent == container_ || - transient_parent->parent() == container_; + transient_window_client ? transient_window_client->GetTransientParent(w) + : nullptr; + return !transient_parent || transient_parent == window() || + transient_parent->parent() == window(); } } // namespace wm diff --git a/chromium/ui/wm/core/easy_resize_window_targeter.h b/chromium/ui/wm/core/easy_resize_window_targeter.h index d8ffa701b3b..560c4b26f60 100644 --- a/chromium/ui/wm/core/easy_resize_window_targeter.h +++ b/chromium/ui/wm/core/easy_resize_window_targeter.h @@ -7,44 +7,36 @@ #include "base/macros.h" #include "ui/aura/window_targeter.h" -#include "ui/gfx/geometry/insets.h" #include "ui/wm/core/wm_core_export.h" +namespace gfx { +class Insets; +} + namespace wm { // An EventTargeter for a container window that uses a slightly larger -// hit-target region for easier resize. -// TODO(sky): make this class final. +// hit-target region for easier resize. It extends the hit test region for child +// windows (top level Widgets that are resizable) to outside their bounds. For +// Ash, this correlates to ash::kResizeOutsideBoundsSize. For the interior +// resize area, see ash::wm::InstallResizeHandleWindowTargeterForWindow(). class WM_CORE_EXPORT EasyResizeWindowTargeter : public aura::WindowTargeter { public: - // |container| window is the owner of this targeter. // NOTE: the insets must be negative. - EasyResizeWindowTargeter(aura::Window* container, - const gfx::Insets& mouse_extend, + EasyResizeWindowTargeter(const gfx::Insets& mouse_extend, const gfx::Insets& touch_extend); ~EasyResizeWindowTargeter() override; - protected: - // aura::WindowTargeter: - void OnSetInsets(const gfx::Insets& last_mouse_extend, - const gfx::Insets& last_touch_extend) override; - private: - class HitMaskSetter; - // aura::WindowTargeter: // Delegates to WindowTargeter's impl and prevents overriding in subclasses. bool EventLocationInsideBounds(aura::Window* target, - const ui::LocatedEvent& event) const final; + const ui::LocatedEvent& event) const override; // Returns true if the hit testing (GetHitTestRects()) should use the // extended bounds. - bool ShouldUseExtendedBounds(const aura::Window* window) const override; - - aura::Window* container_; - - std::unique_ptr<HitMaskSetter> hit_mask_setter_; + bool ShouldUseExtendedBounds(const aura::Window* w) const override; DISALLOW_COPY_AND_ASSIGN(EasyResizeWindowTargeter); }; diff --git a/chromium/ui/wm/core/easy_resize_window_targeter_unittest.cc b/chromium/ui/wm/core/easy_resize_window_targeter_unittest.cc deleted file mode 100644 index a8cb4edf258..00000000000 --- a/chromium/ui/wm/core/easy_resize_window_targeter_unittest.cc +++ /dev/null @@ -1,66 +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. - -#include "ui/wm/core/easy_resize_window_targeter.h" - -#include "ui/aura/mus/window_port_mus.h" -#include "ui/aura/test/aura_mus_test_base.h" -#include "ui/aura/test/mus/test_window_tree.h" -#include "ui/aura/window.h" -#include "ui/compositor/layer_type.h" -#include "ui/gfx/geometry/insets.h" -#include "ui/gfx/geometry/rect.h" - -namespace wm { - -namespace { - -class TestEasyResizeWindowTargeter : public EasyResizeWindowTargeter { - public: - explicit TestEasyResizeWindowTargeter(aura::Window* window) - : EasyResizeWindowTargeter(window, gfx::Insets(), gfx::Insets()) {} -}; - -} // namespace - -using EasyResizeWindowTargeterTest = aura::test::AuraMusClientTestBase; - -TEST_F(EasyResizeWindowTargeterTest, SetHitTestMask) { - aura::Window window(nullptr); - window.Init(ui::LAYER_NOT_DRAWN); - TestEasyResizeWindowTargeter window_targeter(&window); - const gfx::Rect bounds1 = gfx::Rect(10, 20, 200, 300); - window.SetBounds(bounds1); - const gfx::Insets insets1(1, 2, 3, 4); - window_targeter.SetInsets(insets1, insets1); - ASSERT_TRUE(window_tree()->last_hit_test_mask().has_value()); - EXPECT_EQ(gfx::Rect(insets1.left(), insets1.top(), - bounds1.width() - insets1.width(), - bounds1.height() - insets1.height()), - *window_tree()->last_hit_test_mask()); - - // Adjusting the bounds should trigger resetting the mask. - const gfx::Rect bounds2 = gfx::Rect(10, 20, 300, 400); - window.SetBounds(bounds2); - ASSERT_TRUE(window_tree()->last_hit_test_mask().has_value()); - EXPECT_EQ(gfx::Rect(insets1.left(), insets1.top(), - bounds2.width() - insets1.width(), - bounds2.height() - insets1.height()), - *window_tree()->last_hit_test_mask()); - - // Empty insets should reset the mask. - window_targeter.SetInsets(gfx::Insets(), gfx::Insets()); - EXPECT_FALSE(window_tree()->last_hit_test_mask().has_value()); - - const gfx::Insets insets2(-1, 3, 4, 5); - const gfx::Insets effective_insets2(0, 3, 4, 5); - window_targeter.SetInsets(insets2, insets2); - ASSERT_TRUE(window_tree()->last_hit_test_mask().has_value()); - EXPECT_EQ(gfx::Rect(effective_insets2.left(), effective_insets2.top(), - bounds2.width() - effective_insets2.width(), - bounds2.height() - effective_insets2.height()), - *window_tree()->last_hit_test_mask()); -} - -} // namespace wm diff --git a/chromium/ui/wm/core/transient_window_manager.cc b/chromium/ui/wm/core/transient_window_manager.cc index dd65f45d603..80a57bab880 100644 --- a/chromium/ui/wm/core/transient_window_manager.cc +++ b/chromium/ui/wm/core/transient_window_manager.cc @@ -84,7 +84,7 @@ void TransientWindowManager::AddTransientChild(Window* child) { } void TransientWindowManager::RemoveTransientChild(Window* child) { - Windows::iterator i = + auto i = std::find(transient_children_.begin(), transient_children_.end(), child); DCHECK(i != transient_children_.end()); transient_children_.erase(i); @@ -131,8 +131,7 @@ void TransientWindowManager::RestackTransientDescendants() { // |window_|. The existing stacking order is preserved by iterating backwards // and always stacking on top. Window::Windows children(parent->children()); - for (Window::Windows::reverse_iterator it = children.rbegin(); - it != children.rend(); ++it) { + for (auto it = children.rbegin(); it != children.rend(); ++it) { if ((*it) != window_ && HasTransientAncestor(*it, window_)) { TransientWindowManager* descendant_manager = GetOrCreate(*it); base::AutoReset<Window*> resetter( @@ -205,10 +204,8 @@ void TransientWindowManager::OnWindowStackingChanged(Window* window) { // Do nothing if we initiated the stacking change. const TransientWindowManager* transient_manager = GetIfExists(window); if (transient_manager && transient_manager->stacking_target_) { - Windows::const_iterator window_i = std::find( - window->parent()->children().begin(), - window->parent()->children().end(), - window); + auto window_i = std::find(window->parent()->children().begin(), + window->parent()->children().end(), window); DCHECK(window_i != window->parent()->children().end()); if (window_i != window->parent()->children().begin() && (*(window_i - 1) == transient_manager->stacking_target_)) diff --git a/chromium/ui/wm/core/transient_window_stacking_client.cc b/chromium/ui/wm/core/transient_window_stacking_client.cc index 18110983413..ff3737626b6 100644 --- a/chromium/ui/wm/core/transient_window_stacking_client.cc +++ b/chromium/ui/wm/core/transient_window_stacking_client.cc @@ -45,8 +45,8 @@ void FindCommonTransientAncestor(Window** window1, Window** window2) { return; } // Walk the two chains backwards and look for the first difference. - Window::Windows::reverse_iterator it1 = ancestors1.rbegin(); - Window::Windows::reverse_iterator it2 = ancestors2.rbegin(); + auto it1 = ancestors1.rbegin(); + auto it2 = ancestors2.rbegin(); for (; it1 != ancestors1.rend() && it2 != ancestors2.rend(); ++it1, ++it2) { if (*it1 != *it2) { *window1 = *it1; diff --git a/chromium/ui/wm/core/window_animations.cc b/chromium/ui/wm/core/window_animations.cc index 7b18c37fbda..d61a35a8b6c 100644 --- a/chromium/ui/wm/core/window_animations.cc +++ b/chromium/ui/wm/core/window_animations.cc @@ -35,17 +35,13 @@ #include "ui/gfx/geometry/vector2d.h" #include "ui/gfx/geometry/vector3d_f.h" #include "ui/gfx/interpolated_transform.h" +#include "ui/wm/core/window_properties.h" #include "ui/wm/core/window_util.h" #include "ui/wm/core/wm_core_switches.h" #include "ui/wm/public/animation_host.h" -DEFINE_UI_CLASS_PROPERTY_TYPE(::wm::WindowVisibilityAnimationType) -DEFINE_UI_CLASS_PROPERTY_TYPE(::wm::WindowVisibilityAnimationTransition) -DEFINE_UI_CLASS_PROPERTY_TYPE(float) - namespace wm { namespace { -const float kWindowAnimation_Vertical_TranslateY = 15.f; // A base class for hiding animation observer which has two roles: // 1) Notifies AnimationHost at the end of hiding animation. @@ -89,10 +85,8 @@ class HidingWindowAnimationObserverBase : public aura::WindowObserver { if (window_->parent()) { const aura::Window::Windows& transient_children = GetTransientChildren(window_); - aura::Window::Windows::const_iterator iter = - std::find(window_->parent()->children().begin(), - window_->parent()->children().end(), - window_); + auto iter = std::find(window_->parent()->children().begin(), + window_->parent()->children().end(), window_); DCHECK(iter != window_->parent()->children().end()); aura::Window* topmost_transient_child = NULL; for (++iter; iter != window_->parent()->children().end(); ++iter) { @@ -161,19 +155,6 @@ base::LazyInstance<HidingWindowMetricsReporter>::Leaky g_reporter_hide = } // namespace -DEFINE_UI_CLASS_PROPERTY_KEY(int, - kWindowVisibilityAnimationTypeKey, - WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT); -DEFINE_UI_CLASS_PROPERTY_KEY(base::TimeDelta, - kWindowVisibilityAnimationDurationKey, - base::TimeDelta()); -DEFINE_UI_CLASS_PROPERTY_KEY(WindowVisibilityAnimationTransition, - kWindowVisibilityAnimationTransitionKey, - ANIMATE_BOTH); -DEFINE_UI_CLASS_PROPERTY_KEY(float, - kWindowVisibilityAnimationVerticalPositionKey, - kWindowAnimation_Vertical_TranslateY); - // A HidingWindowAnimationObserver that deletes observer and detached // layers upon the completion of the implicit animation. class ImplicitHidingWindowAnimationObserver diff --git a/chromium/ui/wm/core/window_animations.h b/chromium/ui/wm/core/window_animations.h index 598623482c8..2881b15fd52 100644 --- a/chromium/ui/wm/core/window_animations.h +++ b/chromium/ui/wm/core/window_animations.h @@ -9,6 +9,7 @@ #include "base/macros.h" #include "ui/compositor/scoped_layer_animation_settings.h" +#include "ui/wm/core/window_properties.h" #include "ui/wm/core/wm_core_export.h" namespace aura { @@ -40,15 +41,6 @@ enum WindowAnimationType { WINDOW_ANIMATION_TYPE_BOUNCE = 0, // Window scales up and down. }; -// Type of visibility change transition that a window should animate. -// Default behavior is to animate both show and hide. -enum WindowVisibilityAnimationTransition { - ANIMATE_SHOW = 0x1, - ANIMATE_HIDE = 0x2, - ANIMATE_BOTH = ANIMATE_SHOW | ANIMATE_HIDE, - ANIMATE_NONE = 0x4, -}; - // These two methods use int for type rather than WindowVisibilityAnimationType // since downstream libraries can extend the set of animations. WM_CORE_EXPORT void SetWindowVisibilityAnimationType(aura::Window* window, diff --git a/chromium/ui/wm/core/window_animations_unittest.cc b/chromium/ui/wm/core/window_animations_unittest.cc index 3b8a82470fb..f8e993a6814 100644 --- a/chromium/ui/wm/core/window_animations_unittest.cc +++ b/chromium/ui/wm/core/window_animations_unittest.cc @@ -31,8 +31,7 @@ namespace { template<typename T>int GetZPosition(const T* child) { const T* parent = child->parent(); const std::vector<T*> children = parent->children(); - typename std::vector<T*>::const_iterator iter = - std::find(children.begin(), children.end(), child); + auto iter = std::find(children.begin(), children.end(), child); DCHECK(iter != children.end()); return iter - children.begin(); } diff --git a/chromium/ui/wm/core/window_properties.cc b/chromium/ui/wm/core/window_properties.cc index bfa24b8ce82..3cc7b6859e7 100644 --- a/chromium/ui/wm/core/window_properties.cc +++ b/chromium/ui/wm/core/window_properties.cc @@ -4,9 +4,28 @@ #include "ui/wm/core/window_properties.h" +#include "ui/wm/core/window_animations.h" + +DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(WM_CORE_EXPORT, + wm::WindowVisibilityAnimationTransition) + +DEFINE_EXPORTED_UI_CLASS_PROPERTY_TYPE(WM_CORE_EXPORT, float) + namespace wm { DEFINE_UI_CLASS_PROPERTY_KEY(bool, kSnapChildrenToPixelBoundary, false); DEFINE_UI_CLASS_PROPERTY_KEY(bool, kUsesScreenCoordinatesKey, false); +DEFINE_UI_CLASS_PROPERTY_KEY(base::TimeDelta, + kWindowVisibilityAnimationDurationKey, + base::TimeDelta()); +DEFINE_UI_CLASS_PROPERTY_KEY(WindowVisibilityAnimationTransition, + kWindowVisibilityAnimationTransitionKey, + ANIMATE_BOTH); +DEFINE_UI_CLASS_PROPERTY_KEY(int, + kWindowVisibilityAnimationTypeKey, + WINDOW_VISIBILITY_ANIMATION_TYPE_DEFAULT); +DEFINE_UI_CLASS_PROPERTY_KEY(float, + kWindowVisibilityAnimationVerticalPositionKey, + 15.f); } // namespace wm diff --git a/chromium/ui/wm/core/window_properties.h b/chromium/ui/wm/core/window_properties.h index 1eb15dea40b..ec2268067ec 100644 --- a/chromium/ui/wm/core/window_properties.h +++ b/chromium/ui/wm/core/window_properties.h @@ -10,6 +10,15 @@ namespace wm { +// Type of visibility change transition that a window should animate. +// Default behavior is to animate both show and hide. +enum WindowVisibilityAnimationTransition { + ANIMATE_SHOW = 0x1, + ANIMATE_HIDE = 0x2, + ANIMATE_BOTH = ANIMATE_SHOW | ANIMATE_HIDE, + ANIMATE_NONE = 0x4, +}; + // Alphabetical sort. // Containers with this property (true) are aligned with physical pixel @@ -22,6 +31,25 @@ WM_CORE_EXPORT extern const ui::ClassProperty<bool>* const WM_CORE_EXPORT extern const ui::ClassProperty<bool>* const kUsesScreenCoordinatesKey; +WM_CORE_EXPORT extern const ui::ClassProperty<base::TimeDelta>* const + kWindowVisibilityAnimationDurationKey; + +WM_CORE_EXPORT extern const ui::ClassProperty< + WindowVisibilityAnimationTransition>* const + kWindowVisibilityAnimationTransitionKey; + +WM_CORE_EXPORT extern const ui::ClassProperty<int>* const + kWindowVisibilityAnimationTypeKey; + +// Used if the animation-type is WINDOW_VISIBILITY_ANIMATION_TYPE_VERTICAL. +WM_CORE_EXPORT extern const ui::ClassProperty<float>* const + kWindowVisibilityAnimationVerticalPositionKey; + } // namespace wm +// These need to be declared here for jumbo builds. +DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(WM_CORE_EXPORT, + wm::WindowVisibilityAnimationTransition) +DECLARE_EXPORTED_UI_CLASS_PROPERTY_TYPE(WM_CORE_EXPORT, float) + #endif // UI_WM_CORE_WINDOW_PROPERTIES_H_ diff --git a/chromium/ui/wm/public/BUILD.gn b/chromium/ui/wm/public/BUILD.gn index a60f7da5cb9..799fb90a7b7 100644 --- a/chromium/ui/wm/public/BUILD.gn +++ b/chromium/ui/wm/public/BUILD.gn @@ -12,7 +12,6 @@ jumbo_component("public") { "activation_client.h", "activation_delegate.h", "animation_host.h", - "scoped_drag_drop_disabler.h", "scoped_tooltip_disabler.h", "tooltip_client.h", "window_move_client.h", @@ -23,7 +22,6 @@ jumbo_component("public") { "activation_client.cc", "activation_delegate.cc", "animation_host.cc", - "scoped_drag_drop_disabler.cc", "scoped_tooltip_disabler.cc", "tooltip_client.cc", "window_move_client.cc", diff --git a/chromium/ui/wm/public/scoped_drag_drop_disabler.cc b/chromium/ui/wm/public/scoped_drag_drop_disabler.cc deleted file mode 100644 index 2f30f647510..00000000000 --- a/chromium/ui/wm/public/scoped_drag_drop_disabler.cc +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2015 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/wm/public/scoped_drag_drop_disabler.h" - -#include "ui/aura/client/drag_drop_client.h" -#include "ui/aura/window.h" - -namespace wm { - -class NopDragDropClient : public aura::client::DragDropClient { - public: - ~NopDragDropClient() override {} - int StartDragAndDrop(const ui::OSExchangeData& data, - aura::Window* root_window, - aura::Window* source_window, - const gfx::Point& screen_location, - int operation, - ui::DragDropTypes::DragEventSource source) override { - return 0; - } - void DragCancel() override {} - bool IsDragDropInProgress() override { - return false; - } - void AddObserver(aura::client::DragDropClientObserver* observer) override {} - void RemoveObserver(aura::client::DragDropClientObserver* observer) override { - } -}; - -ScopedDragDropDisabler::ScopedDragDropDisabler(aura::Window* window) - : window_(window), - old_client_(aura::client::GetDragDropClient(window)), - new_client_(new NopDragDropClient()) { - SetDragDropClient(window_, new_client_.get()); - window_->AddObserver(this); -} - -ScopedDragDropDisabler::~ScopedDragDropDisabler() { - if (window_) { - window_->RemoveObserver(this); - SetDragDropClient(window_, old_client_); - } -} - -void ScopedDragDropDisabler::OnWindowDestroyed(aura::Window* window) { - CHECK_EQ(window_, window); - window_ = NULL; - new_client_.reset(); -} - -} // namespace wm diff --git a/chromium/ui/wm/public/scoped_drag_drop_disabler.h b/chromium/ui/wm/public/scoped_drag_drop_disabler.h deleted file mode 100644 index 13207b1890d..00000000000 --- a/chromium/ui/wm/public/scoped_drag_drop_disabler.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2015 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_WM_PUBLIC_SCOPED_DRAG_DROP_DISABLER_H_ -#define UI_WM_PUBLIC_SCOPED_DRAG_DROP_DISABLER_H_ - -#include <memory> - -#include "base/macros.h" -#include "ui/aura/window_observer.h" -#include "ui/wm/public/wm_public_export.h" - -namespace aura { -class Window; - -namespace client { -class DragDropClient; -} -} - -namespace wm { - -// ScopedDragDropDisabler is used to temporarily replace the drag'n'drop client -// for a window with a "no-op" client. Upon construction, it installs a new -// client on the window, and upon destruction, it restores the previous one. -class WM_PUBLIC_EXPORT ScopedDragDropDisabler : public aura::WindowObserver { - public: - explicit ScopedDragDropDisabler(aura::Window* window); - ~ScopedDragDropDisabler() override; - - private: - // WindowObserver: - void OnWindowDestroyed(aura::Window* window) override; - - aura::Window* window_; - aura::client::DragDropClient* old_client_; - std::unique_ptr<aura::client::DragDropClient> new_client_; - - DISALLOW_COPY_AND_ASSIGN(ScopedDragDropDisabler); -}; - -} // namespace wm - -#endif // UI_WM_PUBLIC_SCOPED_DRAG_DROP_DISABLER_H_ |